ejabberd-18.01/0000755000232200023220000000000013225667423013641 5ustar debalancedebalanceejabberd-18.01/inetrc0000644000232200023220000000015713225664356015055 0ustar debalancedebalance{lookup,["file","native"]}. {host,{127,0,0,1}, ["localhost","hostalias"]}. {file, resolv, "/etc/resolv.conf"}. ejabberd-18.01/lib/0000755000232200023220000000000013225664356014411 5ustar debalancedebalanceejabberd-18.01/lib/ct_formatter.ex0000644000232200023220000001052413225664356017442 0ustar debalancedebalancedefmodule ExUnit.CTFormatter do @moduledoc false use GenEvent import ExUnit.Formatter, only: [format_time: 2, format_test_failure: 5, format_test_case_failure: 5] def init(opts) do file = File.open! "exunit.log", [:append] # We do not print filter in log file as exclusion of test with tag # pending: true is always done config = %{ file: file, seed: opts[:seed], trace: opts[:trace], colors: Keyword.put_new(opts[:colors], :enabled, false), width: 80, tests_counter: 0, failures_counter: 0, skipped_counter: 0, invalids_counter: 0 } {:ok, config} end def handle_event({:suite_started, _opts}, config) do {:ok, config} end def handle_event({:suite_finished, run_us, load_us}, config) do print_suite(config, run_us, load_us) File.close config[:file] :remove_handler end def handle_event({:test_started, %ExUnit.Test{} = test}, config) do if config.tests_counter == 0, do: IO.binwrite config[:file], "== Running #{test.case} ==\n\n" {:ok, config} end def handle_event({:test_finished, %ExUnit.Test{state: nil} = _test}, config) do IO.binwrite config[:file], "." {:ok, %{config | tests_counter: config.tests_counter + 1}} end def handle_event({:test_finished, %ExUnit.Test{state: {:skip, _}} = _test}, config) do {:ok, %{config | tests_counter: config.tests_counter + 1, skipped_counter: config.skipped_counter + 1}} end def handle_event({:test_finished, %ExUnit.Test{state: {:invalid, _}} = _test}, config) do IO.binwrite config[:file], "?" {:ok, %{config | tests_counter: config.tests_counter + 1, invalids_counter: config.invalids_counter + 1}} end def handle_event({:test_finished, %ExUnit.Test{state: {:failed, failures}} = test}, config) do formatted = format_test_failure(test, failures, config.failures_counter + 1, config.width, &formatter(&1, &2, config)) print_failure(formatted, config) print_logs(test.logs) {:ok, %{config | tests_counter: config.tests_counter + 1, failures_counter: config.failures_counter + 1}} end def handle_event({:case_started, %ExUnit.TestCase{}}, config) do {:ok, config} end def handle_event({:case_finished, %ExUnit.TestCase{state: nil}}, config) do {:ok, config} end def handle_event({:case_finished, %ExUnit.TestCase{state: {:failed, failures}} = test_case}, config) do formatted = format_test_case_failure(test_case, failures, config.failures_counter + 1, config.width, &formatter(&1, &2, config)) print_failure(formatted, config) {:ok, %{config | failures_counter: config.failures_counter + 1}} end ## Printing defp print_suite(config, run_us, load_us) do IO.binwrite config[:file], "\n\n" IO.binwrite config[:file], format_time(run_us, load_us) IO.binwrite config[:file], "\n\n" # singular/plural test_pl = pluralize(config.tests_counter, "test", "tests") failure_pl = pluralize(config.failures_counter, "failure", "failures") message = "#{config.tests_counter} #{test_pl}, #{config.failures_counter} #{failure_pl}" |> if_true(config.skipped_counter > 0, & &1 <> ", #{config.skipped_counter} skipped") |> if_true(config.invalids_counter > 0, & &1 <> ", #{config.invalids_counter} invalid") cond do config.failures_counter > 0 -> IO.binwrite config[:file], message config.invalids_counter > 0 -> IO.binwrite config[:file], message true -> IO.binwrite config[:file], message end IO.binwrite config[:file], "\nRandomized with seed #{config.seed}\n\n\n\n" end defp if_true(value, false, _fun), do: value defp if_true(value, true, fun), do: fun.(value) defp print_failure(formatted, config) do IO.binwrite config[:file], "\n" IO.binwrite config[:file], formatted IO.binwrite config[:file], "\n" end defp formatter(_, msg, _config), do: msg defp pluralize(1, singular, _plural), do: singular defp pluralize(_, _singular, plural), do: plural defp print_logs(""), do: nil defp print_logs(output) do indent = "\n " output = String.replace(output, "\n", indent) IO.puts([" The following output was logged:", indent | output]) end end ejabberd-18.01/lib/mix/0000755000232200023220000000000013225664356015206 5ustar debalancedebalanceejabberd-18.01/lib/mix/tasks/0000755000232200023220000000000013225664356016333 5ustar debalancedebalanceejabberd-18.01/lib/mix/tasks/deps.tree.ex0000644000232200023220000000574613225664356020576 0ustar debalancedebalancedefmodule Mix.Tasks.Ejabberd.Deps.Tree do use Mix.Task alias Ejabberd.Config.EjabberdModule @shortdoc "Lists all ejabberd modules and their dependencies" @moduledoc """ Lists all ejabberd modules and their dependencies. The project must have ejabberd as a dependency. """ def run(_argv) do # First we need to start manually the store to be available # during the compilation of the config file. Ejabberd.Config.Store.start_link Ejabberd.Config.init(:ejabberd_config.get_ejabberd_config_path()) Mix.shell.info "ejabberd modules" Ejabberd.Config.Store.get(:modules) |> Enum.reverse # Because of how mods are stored inside the store |> format_mods |> Mix.shell.info end defp format_mods(mods) when is_list(mods) do deps_tree = build_dependency_tree(mods) mods_used_as_dependency = get_mods_used_as_dependency(deps_tree) keep_only_mods_not_used_as_dep(deps_tree, mods_used_as_dependency) |> format_mods_into_string end defp build_dependency_tree(mods) do Enum.map mods, fn %EjabberdModule{module: mod, attrs: attrs} -> deps = attrs[:dependency] build_dependency_tree(mods, mod, deps) end end defp build_dependency_tree(mods, mod, []), do: %{module: mod, dependency: []} defp build_dependency_tree(mods, mod, deps) when is_list(deps) do dependencies = Enum.map deps, fn dep -> dep_deps = get_dependencies_of_mod(mods, dep) build_dependency_tree(mods, dep, dep_deps) end %{module: mod, dependency: dependencies} end defp get_mods_used_as_dependency(mods) when is_list(mods) do Enum.reduce mods, [], fn(mod, acc) -> case mod do %{dependency: []} -> acc %{dependency: deps} -> get_mod_names(deps) ++ acc end end end defp get_mod_names([]), do: [] defp get_mod_names(mods) when is_list(mods), do: Enum.map(mods, &get_mod_names/1) |> List.flatten defp get_mod_names(%{module: mod, dependency: deps}), do: [mod | get_mod_names(deps)] defp keep_only_mods_not_used_as_dep(mods, mods_used_as_dep) do Enum.filter mods, fn %{module: mod} -> not mod in mods_used_as_dep end end defp get_dependencies_of_mod(deps, mod_name) do Enum.find(deps, &(Map.get(&1, :module) == mod_name)) |> Map.get(:attrs) |> Keyword.get(:dependency) end defp format_mods_into_string(mods), do: format_mods_into_string(mods, 0) defp format_mods_into_string([], _indentation), do: "" defp format_mods_into_string(mods, indentation) when is_list(mods) do Enum.reduce mods, "", fn(mod, acc) -> acc <> format_mods_into_string(mod, indentation) end end defp format_mods_into_string(%{module: mod, dependency: deps}, 0) do "\n├── #{mod}" <> format_mods_into_string(deps, 2) end defp format_mods_into_string(%{module: mod, dependency: deps}, indentation) do spaces = Enum.reduce 0..indentation, "", fn(_, acc) -> " " <> acc end "\n│#{spaces}└── #{mod}" <> format_mods_into_string(deps, indentation + 4) end end ejabberd-18.01/lib/ejabberd/0000755000232200023220000000000013225664356016147 5ustar debalancedebalanceejabberd-18.01/lib/ejabberd/module.ex0000644000232200023220000000057213225664356017776 0ustar debalancedebalancedefmodule Ejabberd.Module do defmacro __using__(opts) do logger_enabled = Keyword.get(opts, :logger, true) quote do @behaviour :gen_mod import Ejabberd.Module unquote(if logger_enabled do quote do: import Ejabberd.Logger end) end end # gen_mod callbacks def depends(_host, _opts), do: [] def mod_opt_type(_), do: [] end ejabberd-18.01/lib/ejabberd/config_util.ex0000644000232200023220000000065213225664356021012 0ustar debalancedebalancedefmodule Ejabberd.ConfigUtil do @moduledoc """ Module containing utility functions for the config file. """ @doc """ Returns true when the config file is based on elixir. """ @spec is_elixir_config(list) :: boolean def is_elixir_config(filename) when is_list(filename) do is_elixir_config(to_string(filename)) end def is_elixir_config(filename) do String.ends_with?(filename, "exs") end end ejabberd-18.01/lib/ejabberd/config/0000755000232200023220000000000013225664356017414 5ustar debalancedebalanceejabberd-18.01/lib/ejabberd/config/validator/0000755000232200023220000000000013225664356021401 5ustar debalancedebalanceejabberd-18.01/lib/ejabberd/config/validator/validation.ex0000644000232200023220000000243713225664356024077 0ustar debalancedebalancedefmodule Ejabberd.Config.Validation do @moduledoc """ Module used to validate a list of modules. """ @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} @type mod_validation_result :: {:ok, EjabberdModule.t} | {:error, EjabberdModule.t, map} alias Ejabberd.Config.EjabberdModule alias Ejabberd.Config.Attr alias Ejabberd.Config.Validator alias Ejabberd.Config.ValidatorUtility @doc """ Given a module or a list of modules it runs validators on them and returns {:ok, mod} or {:error, mod, errors}, for each of them. """ @spec validate([EjabberdModule.t] | EjabberdModule.t) :: [mod_validation_result] def validate(modules) when is_list(modules), do: Enum.map(modules, &do_validate(modules, &1)) def validate(module), do: validate([module]) # Private API @spec do_validate([EjabberdModule.t], EjabberdModule.t) :: mod_validation_result defp do_validate(modules, mod) do {modules, mod, %{}} |> Validator.Attrs.validate |> Validator.Dependencies.validate |> resolve_validation_result end @spec resolve_validation_result(mod_validation) :: mod_validation_result defp resolve_validation_result({_modules, mod, errors}) do case errors do err when err == %{} -> {:ok, mod} err -> {:error, mod, err} end end end ejabberd-18.01/lib/ejabberd/config/validator/validator_dependencies.ex0000644000232200023220000000170413225664356026434 0ustar debalancedebalancedefmodule Ejabberd.Config.Validator.Dependencies do @moduledoc """ Validator module used to validate dependencies specified with the @dependency annotation. """ # TODO: Duplicated from validator.ex !!! @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} import Ejabberd.Config.ValidatorUtility @doc """ Given a module (with the form used for validation) it checks if the @dependency annotation is respected and returns the validation tuple with the errors updated, if found. """ @spec validate(mod_validation) :: mod_validation def validate({modules, mod, errors}) do module_names = extract_module_names(modules) dependencies = mod.attrs[:dependency] errors = Enum.reduce dependencies, errors, fn(req_module, err) -> case req_module in module_names do true -> err false -> put_error(err, :dependency, {req_module, :not_found}) end end {modules, mod, errors} end end ejabberd-18.01/lib/ejabberd/config/validator/validator_attrs.ex0000644000232200023220000000150113225664356025136 0ustar debalancedebalancedefmodule Ejabberd.Config.Validator.Attrs do @moduledoc """ Validator module used to validate attributes. """ # TODO: Duplicated from validator.ex !!! @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} import Ejabberd.Config.ValidatorUtility alias Ejabberd.Config.Attr @doc """ Given a module (with the form used for validation) it runs Attr.validate/1 on each attribute and returns the validation tuple with the errors updated, if found. """ @spec validate(mod_validation) :: mod_validation def validate({modules, mod, errors}) do errors = Enum.reduce mod.attrs, errors, fn(attr, err) -> case Attr.validate(attr) do {:ok, attr} -> err {:error, attr, cause} -> put_error(err, :attribute, {attr, cause}) end end {modules, mod, errors} end end ejabberd-18.01/lib/ejabberd/config/validator/validator_utility.ex0000644000232200023220000000155513225664356025515 0ustar debalancedebalancedefmodule Ejabberd.Config.ValidatorUtility do @moduledoc """ Module used as a base validator for validation modules. Imports utility functions for working with validation structures. """ alias Ejabberd.Config.EjabberdModule @doc """ Inserts an error inside the errors collection, for the given key. If the key doesn't exists then it creates an empty collection and inserts the value passed. """ @spec put_error(map, atom, any) :: map def put_error(errors, key, val) do Map.update errors, key, [val], fn coll -> [val | coll] end end @doc """ Given a list of modules it extracts and returns a list of the module names (which are Elixir.Module). """ @spec extract_module_names(EjabberdModule.t) :: [atom] def extract_module_names(modules) when is_list(modules) do modules |> Enum.map(&Map.get(&1, :module)) end end ejabberd-18.01/lib/ejabberd/config/ejabberd_hook.ex0000644000232200023220000000104313225664356022526 0ustar debalancedebalancedefmodule Ejabberd.Config.EjabberdHook do @moduledoc """ Module containing functions for manipulating ejabberd hooks. """ defstruct hook: nil, opts: [], fun: nil alias Ejabberd.Config.EjabberdHook @type t :: %EjabberdHook{} @doc """ Register a hook to ejabberd. """ @spec start(EjabberdHook.t) :: none def start(%EjabberdHook{hook: hook, opts: opts, fun: fun}) do host = Keyword.get(opts, :host, :global) priority = Keyword.get(opts, :priority, 50) :ejabberd_hooks.add(hook, host, fun, priority) end end ejabberd-18.01/lib/ejabberd/config/logger/0000755000232200023220000000000013225664356020673 5ustar debalancedebalanceejabberd-18.01/lib/ejabberd/config/logger/ejabberd_logger.ex0000644000232200023220000000247513225664356024336 0ustar debalancedebalancedefmodule Ejabberd.Config.EjabberdLogger do @moduledoc """ Module used to log validation errors given validated modules given validated modules. """ alias Ejabberd.Config.EjabberdModule @doc """ Given a list of modules validated, in the form of {:ok, mod} or {:error, mod, errors}, it logs to the user the errors found. """ @spec log_errors([EjabberdModule.t]) :: [EjabberdModule.t] def log_errors(modules_validated) when is_list(modules_validated) do Enum.each modules_validated, &do_log_errors/1 modules_validated end defp do_log_errors({:ok, _mod}), do: nil defp do_log_errors({:error, _mod, errors}), do: Enum.each errors, &do_log_errors/1 defp do_log_errors({:attribute, errors}), do: Enum.each errors, &log_attribute_error/1 defp do_log_errors({:dependency, errors}), do: Enum.each errors, &log_dependency_error/1 defp log_attribute_error({{attr_name, val}, :attr_not_supported}), do: IO.puts "[ WARN ] Annotation @#{attr_name} is not supported." defp log_attribute_error({{attr_name, val}, :type_not_supported}), do: IO.puts "[ WARN ] Annotation @#{attr_name} with value #{inspect val} is not supported (type mismatch)." defp log_dependency_error({module, :not_found}), do: IO.puts "[ WARN ] Module #{inspect module} was not found, but is required as a dependency." end ejabberd-18.01/lib/ejabberd/config/attr.ex0000644000232200023220000001005613225664356020726 0ustar debalancedebalancedefmodule Ejabberd.Config.Attr do @moduledoc """ Module used to work with the attributes parsed from an elixir block (do...end). Contains functions for extracting attrs from a block and validation. """ @type attr :: {atom(), any()} @attr_supported [ active: [type: :boolean, default: true], git: [type: :string, default: ""], name: [type: :string, default: ""], opts: [type: :list, default: []], dependency: [type: :list, default: []] ] @doc """ Takes a block with annotations and extracts the list of attributes. """ @spec extract_attrs_from_block_with_defaults(any()) :: [attr] def extract_attrs_from_block_with_defaults(block) do block |> extract_attrs_from_block |> put_into_list_if_not_already |> insert_default_attrs_if_missing end @doc """ Takes an attribute or a list of attrs and validate them. Returns a {:ok, attr} or {:error, attr, cause} for each of the attributes. """ @spec validate([attr]) :: [{:ok, attr}] | [{:error, attr, atom()}] def validate(attrs) when is_list(attrs), do: Enum.map(attrs, &valid_attr?/1) def validate(attr), do: validate([attr]) |> List.first @doc """ Returns the type of an attribute, given its name. """ @spec get_type_for_attr(atom()) :: atom() def get_type_for_attr(attr_name) do @attr_supported |> Keyword.get(attr_name) |> Keyword.get(:type) end @doc """ Returns the default value for an attribute, given its name. """ @spec get_default_for_attr(atom()) :: any() def get_default_for_attr(attr_name) do @attr_supported |> Keyword.get(attr_name) |> Keyword.get(:default) end # Private API # Given an elixir block (do...end) returns a list with the annotations # or a single annotation. @spec extract_attrs_from_block(any()) :: [attr] | attr defp extract_attrs_from_block({:__block__, [], attrs}), do: Enum.map(attrs, &extract_attrs_from_block/1) defp extract_attrs_from_block({:@, _, [attrs]}), do: extract_attrs_from_block(attrs) defp extract_attrs_from_block({attr_name, _, [value]}), do: {attr_name, value} defp extract_attrs_from_block(nil), do: [] # In case extract_attrs_from_block returns a single attribute, # then put it into a list. (Ensures attrs are always into a list). @spec put_into_list_if_not_already([attr] | attr) :: [attr] defp put_into_list_if_not_already(attrs) when is_list(attrs), do: attrs defp put_into_list_if_not_already(attr), do: [attr] # Given a list of attributes, it inserts the missing attribute with their # default value. @spec insert_default_attrs_if_missing([attr]) :: [attr] defp insert_default_attrs_if_missing(attrs) do Enum.reduce @attr_supported, attrs, fn({attr_name, _}, acc) -> case Keyword.has_key?(acc, attr_name) do true -> acc false -> Keyword.put(acc, attr_name, get_default_for_attr(attr_name)) end end end # Given an attribute, validates it and return a tuple with # {:ok, attr} or {:error, attr, cause} @spec valid_attr?(attr) :: {:ok, attr} | {:error, attr, atom()} defp valid_attr?({attr_name, param} = attr) do case Keyword.get(@attr_supported, attr_name) do nil -> {:error, attr, :attr_not_supported} [{:type, param_type} | _] -> case is_of_type?(param, param_type) do true -> {:ok, attr} false -> {:error, attr, :type_not_supported} end end end # Given an attribute value and a type, it returns a true # if the value its of the type specified, false otherwise. # Usefoul for checking if an attr value respects the type # specified for the annotation. @spec is_of_type?(any(), atom()) :: boolean() defp is_of_type?(param, type) when type == :boolean and is_boolean(param), do: true defp is_of_type?(param, type) when type == :string and is_bitstring(param), do: true defp is_of_type?(param, type) when type == :list and is_list(param), do: true defp is_of_type?(param, type) when type == :atom and is_atom(param), do: true defp is_of_type?(_param, type) when type == :any, do: true defp is_of_type?(_, _), do: false end ejabberd-18.01/lib/ejabberd/config/config.ex0000644000232200023220000000744513225664356021231 0ustar debalancedebalancedefmodule 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 |> Dict.put(:modules, get_modules_parsed_in_order()) |> Dict.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 ejabberd-18.01/lib/ejabberd/config/store.ex0000644000232200023220000000240613225664356021110 0ustar debalancedebalancedefmodule Ejabberd.Config.Store do @moduledoc """ Module used for storing the modules parsed from the configuration file. Example: - Store.put(:modules, mod1) - Store.put(:modules, mod2) - Store.get(:modules) :: [mod1, mod2] Be carefoul: when retrieving data you get them in the order inserted into the store, which normally is the reversed order of how the modules are specified inside the configuration file. To resolve this just use a Enum.reverse/1. """ @name __MODULE__ def start_link do Agent.start_link(fn -> %{} end, name: @name) end @doc """ Stores a value based on the key. If the key already exists, then it inserts the new element, maintaining all the others. It uses a list for this. """ @spec put(atom, any) :: :ok def put(key, val) do Agent.update @name, &Map.update(&1, key, [val], fn coll -> [val | coll] end) end @doc """ Gets a value based on the key passed. Returns always a list. """ @spec get(atom) :: [any] def get(key) do Agent.get @name, &Map.get(&1, key, []) end @doc """ Stops the store. It uses Agent.stop underneath, so be aware that exit could be called. """ @spec stop() :: :ok def stop do Agent.stop @name end end ejabberd-18.01/lib/ejabberd/config/opts_formatter.ex0000644000232200023220000000247513225664356023032 0ustar debalancedebalancedefmodule Ejabberd.Config.OptsFormatter do @moduledoc """ Module for formatting options parsed into the format ejabberd uses. """ alias Ejabberd.Config.EjabberdModule @doc """ Takes a keyword list with keys corresponding to the keys requested by the ejabberd config (ex: modules: mods) and formats them to be correctly evaluated by ejabberd. Look at how Config.get_ejabberd_opts/0 is constructed for more informations. """ @spec format_opts_for_ejabberd([{atom(), any()}]) :: list() def format_opts_for_ejabberd(opts) do opts |> format_attrs_for_ejabberd end defp format_attrs_for_ejabberd(opts) when is_list(opts), do: Enum.map opts, &format_attrs_for_ejabberd/1 defp format_attrs_for_ejabberd({:listeners, mods}), do: {:listen, format_listeners_for_ejabberd(mods)} defp format_attrs_for_ejabberd({:modules, mods}), do: {:modules, format_mods_for_ejabberd(mods)} defp format_attrs_for_ejabberd({key, opts}) when is_atom(key), do: {key, opts} defp format_mods_for_ejabberd(mods) do Enum.map mods, fn %EjabberdModule{module: mod, attrs: attrs} -> {mod, attrs[:opts]} end end defp format_listeners_for_ejabberd(mods) do Enum.map mods, fn %EjabberdModule{module: mod, attrs: attrs} -> Keyword.put(attrs[:opts], :module, mod) end end end ejabberd-18.01/lib/ejabberd/config/ejabberd_module.ex0000644000232200023220000000406713225664356023064 0ustar debalancedebalancedefmodule Ejabberd.Config.EjabberdModule do @moduledoc """ Module representing a module block in the configuration file. It offers functions for validation and for starting the modules. Warning: The name is EjabberdModule to not collide with the already existing Elixir.Module. """ @type t :: %{module: atom, attrs: [Attr.t]} defstruct [:module, :attrs] alias Ejabberd.Config.EjabberdModule alias Ejabberd.Config.Attr alias Ejabberd.Config.Validation @doc """ Given a list of modules / single module it runs different validators on them. For each module, returns a {:ok, mod} or {:error, mod, errors} """ def validate(modules) do Validation.validate(modules) end @doc """ Given a list of modules, it takes only the ones with a git attribute and tries to fetch the repo, then, it install them through :ext_mod.install/1 """ @spec fetch_git_repos([EjabberdModule.t]) :: none() def fetch_git_repos(modules) do modules |> Enum.filter(&is_git_module?/1) |> Enum.each(&fetch_and_install_git_module/1) end # Private API defp is_git_module?(%EjabberdModule{attrs: attrs}) do case Keyword.get(attrs, :git) do "" -> false repo -> String.match?(repo, ~r/((git|ssh|http(s)?)|(git@[\w\.]+))(:(\/\/)?)([\w\.@\:\/\-~]+)(\.git)(\/)?/) end end defp fetch_and_install_git_module(%EjabberdModule{attrs: attrs}) do repo = Keyword.get(attrs, :git) mod_name = case Keyword.get(attrs, :name) do "" -> infer_mod_name_from_git_url(repo) name -> name end path = "#{:ext_mod.modules_dir()}/sources/ejabberd-contrib\/#{mod_name}" fetch_and_store_repo_source_if_not_exists(path, repo) :ext_mod.install(mod_name) # Have to check if overwrites an already present mod end defp fetch_and_store_repo_source_if_not_exists(path, repo) do unless File.exists?(path) do IO.puts "[info] Fetching: #{repo}" :os.cmd('git clone #{repo} #{path}') end end defp infer_mod_name_from_git_url(repo), do: String.split(repo, "/") |> List.last |> String.replace(".git", "") end ejabberd-18.01/lib/ejabberd/logger.ex0000644000232200023220000000066213225664356017770 0ustar debalancedebalancedefmodule Ejabberd.Logger do def critical(message, args \\ []), do: :lager.log(:critical, [], message, args) def error(message, args \\ []), do: :lager.log(:error, [], message, args) def warning(message, args \\ []), do: :lager.log(:warning, [], message, args) def info(message, args \\ []), do: :lager.log(:info, [], message, args) def debug(message, args \\ []), do: :lager.log(:debug, [], message, args) end ejabberd-18.01/lib/ejabberd/hooks.ex0000644000232200023220000000056313225664356017634 0ustar debalancedebalancedefmodule Ejabberd.Hooks do # Generic hook setting features def add(hook_name, host, module, function, priority) do :ejabberd_hooks.add(hook_name, host, module, function, priority) end # Should be named 'removed' def delete(hook_name, host, module, function, priority) do :ejabberd_hooks.delete(hook_name, host, module, function, priority) end end ejabberd-18.01/lib/mod_presence_demo.ex0000644000232200023220000000077613225664356020430 0ustar debalancedebalancedefmodule ModPresenceDemo do use Ejabberd.Module def start(host, _opts) do info('Starting ejabberd module Presence Demo') Ejabberd.Hooks.add(:set_presence_hook, host, __MODULE__, :on_presence, 50) :ok end def stop(host) do info('Stopping ejabberd module Presence Demo') Ejabberd.Hooks.delete(:set_presence_hook, host, __MODULE__, :on_presence, 50) :ok end def on_presence(user, _server, _resource, _packet) do info('Receive presence for #{user}') :none end end ejabberd-18.01/rel/0000755000232200023220000000000013225664356014425 5ustar debalancedebalanceejabberd-18.01/rel/files/0000755000232200023220000000000013225664356015527 5ustar debalancedebalanceejabberd-18.01/rel/files/install_upgrade.escript0000644000232200023220000000325613225664356022305 0ustar debalancedebalance#!/usr/bin/env escript %%! -noshell -noinput %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et -define(TIMEOUT, 60000). -define(INFO(Fmt,Args), io:format(Fmt,Args)). main([NodeName, Cookie, ReleasePackage]) -> TargetNode = start_distribution(NodeName, Cookie), {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release, [ReleasePackage], ?TIMEOUT), ?INFO("Unpacked Release ~p~n", [Vsn]), {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, check_install_release, [Vsn], ?TIMEOUT), {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, install_release, [Vsn], ?TIMEOUT), ?INFO("Installed Release ~p~n", [Vsn]), ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT), ?INFO("Made Release ~p Permanent~n", [Vsn]); main(_) -> init:stop(1). start_distribution(NodeName, Cookie) -> MyNode = make_script_node(NodeName), {ok, _Pid} = net_kernel:start([MyNode, shortnames]), erlang:set_cookie(node(), list_to_atom(Cookie)), TargetNode = make_target_node(NodeName), case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of {true, pong} -> ok; {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), init:stop(1) end, TargetNode. make_target_node(Node) -> [_, Host] = string:tokens(atom_to_list(node()), "@"), list_to_atom(lists:concat([Node, "@", Host])). make_script_node(Node) -> list_to_atom(lists:concat([Node, "_upgrader_", os:getpid()])). ejabberd-18.01/rel/files/erl0000755000232200023220000000213613225664356016241 0ustar debalancedebalance#!/bin/sh ## This script replaces the default "erl" in erts-VSN/bin. This is necessary ## as escript depends on erl and in turn, erl depends on having access to a ## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect ## of running escript -- the embedded node bypasses erl and uses erlexec directly ## (as it should). ## ## Note that this script makes the assumption that there is a start_clean.boot ## file available in $ROOTDIR/release/VSN. # Determine the abspath of where this script is executing from. ERTS_BIN_DIR=$(cd ${0%/*} && pwd) # Now determine the root directory -- this script runs from erts-VSN/bin, # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR # path. ROOTDIR=${ERTS_BIN_DIR%/*/*} # Parse out release and erts info START_ERL=`cat $ROOTDIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` CMD="$BINDIR/erlexec" export EMU export ROOTDIR export BINDIR export PROGNAME exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"} ejabberd-18.01/rel/reltool.config.script0000644000232200023220000001017213225664356020600 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeniy Khramtsov %%% @copyright (C) 2013-2017, Evgeniy Khramtsov %%% @doc %%% %%% @end %%% Created : 8 May 2013 by Evgeniy Khramtsov %%%------------------------------------------------------------------- TopDir = filename:join(filename:dirname(SCRIPT), ".."), GetDeps = fun(Config, GetDepsFun) -> case catch rebar_config:consult_file(Config) of {ok, Data} -> case lists:keyfind(deps, 1, Data) of {deps, Deps} -> lists:map(fun({Dep, _, _}) -> [Dep, GetDepsFun(filename:join([TopDir, "deps", Dep, "rebar.config"]), GetDepsFun)] end, Deps); _ -> [] end; _ -> [] end end, Vars = case file:consult(filename:join([TopDir, "vars.config"])) of {ok, Terms} -> Terms; _Err -> [] end, RequiredOTPApps = [sasl, crypto, public_key, ssl, mnesia, inets, compiler, asn1, syntax_tools, os_mon, xmerl], ConfiguredOTPApps = lists:flatmap( fun({tools, true}) -> [tools, runtime_tools]; ({odbc, true}) -> [odbc]; (_) -> [] end, Vars), OTPApps = RequiredOTPApps ++ ConfiguredOTPApps, DepApps = lists:usort(lists:flatten(GetDeps(filename:join(TopDir, "rebar.config"), GetDeps))), Sys = [{lib_dirs, []}, {erts, [{mod_cond, derived}, {app_file, strip}]}, {app_file, strip}, {rel, "ejabberd", proplists:get_value(vsn, Vars), [ kernel, stdlib, ejabberd ] ++ OTPApps ++ DepApps}, {rel, "start_clean", "", [ kernel, stdlib ]}, {boot_rel, "ejabberd"}, {profile, embedded}, {incl_cond, exclude}, {excl_archive_filters, [".*"]}, %% Do not archive built libs {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)", "^erts.*/(doc|info|include|lib|man|src)"]}, {excl_app_filters, ["\.gitignore"]}, {app, stdlib, [{incl_cond, include}]}, {app, kernel, [{incl_cond, include}]}, {app, ejabberd, [{incl_cond, include}, {lib_dir, ".."}]}] ++ lists:map( fun(App) -> {app, App, [{incl_cond, include}, {lib_dir, "../deps/" ++ atom_to_list(App)}]} end, DepApps) ++ lists:map( fun(App) -> {app, App, [{incl_cond, include}]} end, OTPApps). Overlay = [ {mkdir, "var/log/ejabberd"}, {mkdir, "var/lock"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, {mkdir, "doc"}, {template, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, {template, "../ejabberdctl.template", "bin/ejabberdctl"}, {copy, "../ejabberdctl.cfg.example", "etc/ejabberd/ejabberdctl.cfg"}, {copy, "../ejabberd.yml.example", "etc/ejabberd/ejabberd.yml"}, {copy, "../inetrc", "etc/ejabberd/inetrc"}, {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"} ], Config = [{sys, Sys}, {overlay_vars, "../vars.config"}, {target_dir, "ejabberd"}, {overlay, Overlay}], %%io:format("ejabberd release:~n ~p~n", [Config]), Config. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: ejabberd-18.01/ejabberdctl.template0000755000232200023220000002451713225664356017655 0ustar debalancedebalance#!/bin/sh # define default configuration POLL=true SMP=auto ERL_MAX_PORTS=32000 ERL_PROCESSES=250000 ERL_MAX_ETS_TABLES=1400 FIREWALL_WINDOW="" INET_DIST_INTERFACE="" ERLANG_NODE=ejabberd@localhost # define default environment variables SCRIPT_DIR=$(cd "${0%/*}" && pwd) ERL="{{erl}}" IEX="{{bindir}}/iex" EPMD="{{epmd}}" INSTALLUSER="{{installuser}}" # check the proper system user is used case $(id -un) in "$INSTALLUSER") EXEC_CMD="as_current_user" ;; root) if [ -n "$INSTALLUSER" ] ; then EXEC_CMD="as_install_user" else EXEC_CMD="as_current_user" echo "WARNING: This is not recommended to run ejabberd as root" >&2 fi ;; *) if [ -n "$INSTALLUSER" ] ; then echo "ERROR: This command can only be run by root or the user $INSTALLUSER" >&2 exit 7 else EXEC_CMD="as_current_user" fi ;; esac # parse command line parameters for arg; do case $arg in -n|--node) ERLANG_NODE_ARG=$2; shift;; -s|--spool) SPOOL_DIR=$2; shift;; -l|--logs) LOGS_DIR=$2; shift;; -f|--config) EJABBERD_CONFIG_PATH=$2; shift;; -c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift;; -d|--config-dir) ETC_DIR=$2; shift;; -t|--no-timeout) NO_TIMEOUT="--no-timeout";; --) :;; *) break;; esac shift done # define ejabberd variables if not already defined from the command line : "${ETC_DIR:="{{sysconfdir}}/ejabberd"}" : "${LOGS_DIR:="{{localstatedir}}/log/ejabberd"}" : "${SPOOL_DIR:="{{localstatedir}}/lib/ejabberd"}" : "${EJABBERD_CONFIG_PATH:="$ETC_DIR/ejabberd.yml"}" : "${EJABBERDCTL_CONFIG_PATH:="$ETC_DIR/ejabberdctl.cfg"}" [ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" [ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" : "${EJABBERD_DOC_PATH:="{{docdir}}"}" : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" # define erl parameters ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS" if [ -n "$FIREWALL_WINDOW" ] ; then ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" fi if [ -n "$INET_DIST_INTERFACE" ] ; then INET_DIST_INTERFACE2=$("$ERL" -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" fi fi ERL_LIBS={{libdir}} ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump ERL_INETRC="$ETC_DIR"/inetrc # define ejabberd parameters EJABBERD_OPTS="$EJABBERD_OPTS\ $(sed '/^log_rate_limit/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_rotate_date/!d;s/:[ \t]*\(.[^ ]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" # export global variables export EJABBERD_CONFIG_PATH export EJABBERD_LOG_PATH export EJABBERD_DOC_PATH export EJABBERD_PID_PATH export ERL_CRASH_DUMP export ERL_EPMD_ADDRESS export ERL_INETRC export ERL_MAX_PORTS export ERL_MAX_ETS_TABLES export CONTRIB_MODULES_PATH export CONTRIB_MODULES_CONF_DIR export ERL_LIBS # run command either directly or via su $INSTALLUSER exec_cmd() { case $EXEC_CMD in as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;; as_current_user) "$@" ;; esac } exec_erl() { NODE=$1; shift exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" } exec_iex() { NODE=$1; shift exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" } # usage debugwarning() { if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then echo "--------------------------------------------------------------------" echo "" echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell" echo "to an already running ejabberd node." echo "If an ERROR is printed, it means the connection was not successful." echo "You can interact with the ejabberd node if you know how to use it." echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" echo "To detach this shell from ejabberd, press:" echo " control+c, control+c" echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" echo "Press return to continue" read -r input echo "" fi } livewarning() { if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then echo "--------------------------------------------------------------------" echo "" echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode." echo "All log messages will be shown in the command shell." echo "You can interact with the ejabberd node if you know how to use it." echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" echo "To exit this LIVE mode and stop ejabberd, press:" echo " q(). and press the Enter key" echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" echo "Press return to continue" read -r input echo "" fi } help() { echo "" echo "Commands to start an ejabberd node:" echo " start Start an ejabberd node in server mode" echo " debug Attach an interactive Erlang shell to a running ejabberd node" echo " iexdebug Attach an interactive Elixir shell to a running ejabberd node" echo " live Start an ejabberd node in live (interactive) mode" echo " iexlive Start an ejabberd node in live (interactive) mode, within an Elixir shell" echo " foreground Start an ejabberd node in server mode (attached)" echo "" echo "Optional parameters when starting an ejabberd node:" echo " --config-dir dir Config ejabberd: $ETC_DIR" echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH" echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH" echo " --logs dir Directory for logs: $LOGS_DIR" echo " --spool dir Database spool dir: $SPOOL_DIR" echo " --node nodename ejabberd node name: $ERLANG_NODE" echo "" } # dynamic node name helper uid() { uuid=$(uuidgen 2>/dev/null) [ -z "$uuid" ] && [ -f /proc/sys/kernel/random/uuid ] && uuid=$(cat /proc/sys/kernel/random/uuid) [ -z "$uuid" ] && uuid=$(printf "%X" "${RANDOM:-$$}$(date +%M%S)") uuid=${uuid%%-*} [ $# -eq 0 ] && echo "${uuid}-${ERLANG_NODE}" [ $# -eq 1 ] && echo "${uuid}-${1}-${ERLANG_NODE}" [ $# -eq 2 ] && echo "${uuid}-${1}@${2}" } # stop epmd if there is no other running node stop_epmd() { "$EPMD" -names 2>/dev/null | grep -q name || "$EPMD" -kill >/dev/null } # make sure node not already running and node name unregistered # if all ok, ensure runtime directory exists and make it current directory check_start() { "$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && { pgrep -f "$ERLANG_NODE" >/dev/null && { echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running." exit 4 } pgrep beam >/dev/null && { echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered," echo " but no related beam process has been found." echo "Shutdown all other erlang nodes, and call 'epmd -kill'." exit 5 } "$EPMD" -kill >/dev/null } } # allow sync calls wait_status() { # args: status try delay # return: 0 OK, 1 KO timeout="$2" status=4 while [ "$status" -ne "$1" ] ; do sleep "$3" timeout=$((timeout - 1)) if [ $timeout -eq 0 ] ; then status="$1" else exec_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null status="$?" fi done [ $timeout -gt 0 ] } # ensure we can change current directory to SPOOL_DIR [ -d "$SPOOL_DIR" ] || exec_cmd mkdir -p "$SPOOL_DIR" cd "$SPOOL_DIR" || { echo "ERROR: can not access directory $SPOOL_DIR" exit 6 } # main case $1 in start) check_start exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -detached ;; foreground) check_start exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput ;; live) livewarning check_start exec_erl "$ERLANG_NODE" $EJABBERD_OPTS ;; debug) debugwarning exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE" ;; etop) exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" -s etop \ -s erlang halt -output text ;; iexdebug) debugwarning exec_iex "$(uid debug)" --remsh "$ERLANG_NODE" ;; iexlive) livewarning exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" --app ejabberd ;; ping) PEER=${2:-$ERLANG_NODE} [ "$PEER" = "${PEER%.*}" ] && PS="-s" exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; started) wait_status 0 30 2 # wait 30x2s before timeout ;; stopped) wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout ;; *) exec_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" result=$? case $result in 2|3) help;; *) :;; esac exit $result ;; esac ejabberd-18.01/configure.bat0000644000232200023220000000045713225664356016322 0ustar debalancedebalance @if "x%1"=="x--help" goto usage @set arg=dynamic @if "x%1"=="x--static" set arg=static @echo Configuring for %arg% build... erlc configure.erl erl -s configure -env arg %arg% -noshell @goto end :usage @echo Usage: configure.bat @echo or configure.bat --static @echo or configure.bat --help :end ejabberd-18.01/mix.lock0000644000232200023220000001255413225664356015321 0ustar debalancedebalance%{"cache_tab": {:hex, :cache_tab, "1.0.12", "a06a4ffbd4da8469791ba941512a6a45ed8c11865b4606a368e21b332da3638a", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"}, "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, "eimp": {:git, "https://github.com/processone/eimp.git", "23796118176be98195db9f831f17dde74d1553e1", [tag: "1.0.1"]}, "epam": {:hex, :epam, "1.0.3", "3adcc148cdbaaa2bbe15dd661f0d74284e5749a815b4e480dbf94e8e023361b9", [:rebar3], [], "hexpm"}, "eredis": {:hex, :eredis, "1.1.0", "8d8d74496f35216679b97726b75fb1c8715e99dd7f3ef9f9824a2264c3e0aac0", [:rebar3], [], "hexpm"}, "esip": {:hex, :esip, "1.0.21", "711c704337d434db6d7c70bd0da868aaacd91b252c0bb63b4580e6c896164f1f", [:rebar3], [{:fast_tls, "1.0.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.20", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, "ezlib": {:git, "https://github.com/processone/ezlib.git", "ec6491d788436bb096022843e6ec7f58d2973ae3", [tag: "1.0.3"]}, "fast_tls": {:hex, :fast_tls, "1.0.20", "edd241961ab20b71ec1e9f75a2a2c043128ff117adf3efd42e6cec94f1937539", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "fast_xml": {:hex, :fast_xml, "1.1.28", "31ce5cf44d20e900e1a499009f886ff74b589324d532ed0ed7a432e4f498beb1", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "fast_yaml": {:hex, :fast_yaml, "1.0.12", "ee8527d388255cf7a24fc1e6cb2d09dca4e506966dd9d86e61d3d90f236a3e2e", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "fs": {:hex, :fs, "3.4.0", "6d18575c250b415b3cad559e6f97a4c822516c7bc2c10bfbb2493a8f230f5132", [:rebar3], [], "hexpm"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"}, "hamcrest": {:hex, :basho_hamcrest, "0.4.1", "fb7b2c92d252a1e9db936750b86089addaebeb8f87967fb4bbdda61e8863338e", [:make, :mix, :rebar3], [], "hexpm"}, "iconv": {:hex, :iconv, "1.0.6", "3b424a80039059767f1037dc6a49ff07c2f88df14068c16dc938c4f377a77b4c", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "jiffy": {:hex, :jiffy, "0.14.13", "225a9a35e26417832c611526567194b4d3adc4f0dfa5f2f7008f4684076f2a01", [:rebar3], [], "hexpm"}, "lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"}, "luerl": {:git, "https://github.com/rvirding/luerl.git", "f7b2cc0ab6fa4245ebeda0169fc994aff0628bf9", [tag: "v0.2"]}, "meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"}, "moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.4", "7b9d7957a9d031813a0e6bcea5a7f5e91b54db805a92709a445cf75cf934bc1d", [:rebar3], [], "hexpm"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.2", "cc381038920e3d34ef32aa10ba7eb637bdff38a946748c4fd99329ff484a3889", [:rebar3], [], "hexpm"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.4", "eadbbddee8d52145694bf86bdfe8c1ae8353a55e152410146b8c2711756d6041", [:rebar3], [], "hexpm"}, "p1_utils": {:hex, :p1_utils, "1.0.10", "a6d6927114bac79cf6468a10824125492034af7071adc6ed5ebc4ddb443845d4", [:rebar3], [], "hexpm"}, "riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"}, "riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"}, "samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]}, "sqlite3": {:hex, :sqlite3, "1.1.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], [], "hexpm"}, "stringprep": {:hex, :stringprep, "1.0.10", "552d784eb60652220fce9131f8bb0ebc62fdffd6482c4f08f2e7d61300227c28", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "stun": {:hex, :stun, "1.0.20", "6b156fa11606bebb6086d02cb2f6532c84effb59c95ba93d0e2d8e2510970253", [:rebar3], [{:fast_tls, "1.0.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "xmpp": {:hex, :xmpp, "1.1.19", "ca0a89c567e972d119204b1296ffe58ad5d3237738950ae2c61043fbaf5e150e", [:rebar3], [{:fast_xml, "1.1.28", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.10", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"}} ejabberd-18.01/README.md0000777000232200023220000000000013225664356015777 2READMEustar debalancedebalanceejabberd-18.01/vars.config.in0000644000232200023220000000414313225664356016414 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% %%% ejabberd, Copyright (C) 2002-2017 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %% Macros {roster_gateway_workaround, @roster_gateway_workaround@}. {full_xml, @full_xml@}. {db_type, @db_type@}. {debug, @debug@}. {hipe, @hipe@}. {erlang_deprecated_types, @erlang_deprecated_types@}. {new_sql_schema, @new_sql_schema@}. %% Ad-hoc directories with source files {tools, @tools@}. %% Dependencies {odbc, @odbc@}. {mysql, @mysql@}. {pgsql, @pgsql@}. {sqlite, @sqlite@}. {pam, @pam@}. {zlib, @zlib@}. {riak, @riak@}. {redis, @redis@}. {elixir, @elixir@}. {iconv, @iconv@}. {stun, @stun@}. {sip, @sip@}. {graphics, @graphics@}. %% Version {vsn, "@PACKAGE_VERSION@"}. %% Variables for overlay template files %% Platform-specific installation paths {release, true}. {release_dir, "${SCRIPT_DIR%/*}"}. {sysconfdir, "{{release_dir}}/etc"}. {installuser, "@INSTALLUSER@"}. {erl, "{{release_dir}}/{{erts_vsn}}/bin/erl"}. {epmd, "{{release_dir}}/{{erts_vsn}}/bin/epmd"}. {localstatedir, "{{release_dir}}/var"}. {libdir, "{{release_dir}}/lib"}. {docdir, "{{release_dir}}/doc"}. {latest_deps, @latest_deps@}. {system_deps, @system_deps@}. {ldflags, "@LDFLAGS@"}. {cflags, "@CFLAGS@"}. {cppflags, "@CPPFLAGS@"}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: ejabberd-18.01/ejabberdctl.cfg.example0000644000232200023220000001314413225664356020222 0ustar debalancedebalance# # In this file you can configure options that are passed by ejabberdctl # to the erlang runtime system when starting ejabberd # #' POLL: Kernel polling ([true|false]) # # The kernel polling option requires support in the kernel. # Additionally, you need to enable this feature while compiling Erlang. # # Default: true # #POLL=true #. #' SMP: SMP support ([enable|auto|disable]) # # Explanation in Erlang/OTP documentation: # enable: starts the Erlang runtime system with SMP support enabled. # This may fail if no runtime system with SMP support is available. # auto: starts the Erlang runtime system with SMP support enabled if it # is available and more than one logical processor are detected. # disable: starts a runtime system without SMP support. # # Default: auto # #SMP=auto #. #' ERL_MAX_PORTS: Maximum number of simultaneously open Erlang ports # # ejabberd consumes two or three ports for every connection, either # from a client or from another Jabber server. So take this into # account when setting this limit. # # Default: 32000 # Maximum: 268435456 # #ERL_MAX_PORTS=32000 #. #' FIREWALL_WINDOW: Range of allowed ports to pass through a firewall # # If Ejabberd is configured to run in cluster, and a firewall is blocking ports, # it's possible to make Erlang use a defined range of port (instead of dynamic # ports) for node communication. # # Default: not defined # Example: 4200-4210 # #FIREWALL_WINDOW= #. #' INET_DIST_INTERFACE: IP address where this Erlang node listens other nodes # # This communication is used by ejabberdctl command line tool, # and in a cluster of several ejabberd nodes. # # Default: 0.0.0.0 # #INET_DIST_INTERFACE=127.0.0.1 #. #' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections # # IMPORTANT: This option works only in Erlang/OTP R14B03 and newer. # # This environment variable may be set to a comma-separated # list of IP addresses, in which case the epmd daemon # will listen only on the specified address(es) and on the # loopback address (which is implicitly added to the list if it # has not been specified). The default behaviour is to listen on # all available IP addresses. # # Default: 0.0.0.0 # #ERL_EPMD_ADDRESS=127.0.0.1 #. #' ERL_PROCESSES: Maximum number of Erlang processes # # Erlang consumes a lot of lightweight processes. If there is a lot of activity # on ejabberd so that the maximum number of processes is reached, people will # experience greater latency times. As these processes are implemented in # Erlang, and therefore not related to the operating system processes, you do # not have to worry about allowing a huge number of them. # # Default: 250000 # Maximum: 268435456 # #ERL_PROCESSES=250000 #. #' ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables # # The number of concurrent ETS and Mnesia tables is limited. When the limit is # reached, errors will appear in the logs: # ** Too many db tables ** # You can safely increase this limit when starting ejabberd. It impacts memory # consumption but the difference will be quite small. # # Default: 1400 # #ERL_MAX_ETS_TABLES=1400 #. #' ERL_OPTIONS: Additional Erlang options # # The next variable allows to specify additional options passed to erlang while # starting ejabberd. Some useful options are -noshell, -detached, -heart. When # ejabberd is started from an init.d script options -noshell and -detached are # added implicitly. See erl(1) for more info. # # It might be useful to add "-pa /usr/local/lib/ejabberd/ebin" if you # want to add local modules in this path. # # Default: "" # #ERL_OPTIONS="" #. #' ERLANG_NODE: Erlang node name # # The next variable allows to explicitly specify erlang node for ejabberd # It can be given in different formats: # ERLANG_NODE=ejabberd # Lets erlang add hostname to the node (ejabberd uses short name in this case) # ERLANG_NODE=ejabberd@hostname # Erlang uses node name as is (so make sure that hostname is a real # machine hostname or you'll not be able to control ejabberd) # ERLANG_NODE=ejabberd@hostname.domainname # The same as previous, but erlang will use long hostname # (see erl (1) manual for details) # # Default: ejabberd@localhost # #ERLANG_NODE=ejabberd@localhost #. #' EJABBERD_PID_PATH: ejabberd PID file # # Indicate the full path to the ejabberd Process identifier (PID) file. # If this variable is defined, ejabberd writes the PID file when starts, # and deletes it when stops. # Remember to create the directory and grant write permission to ejabberd. # # Default: don't write PID file # #EJABBERD_PID_PATH=/var/run/ejabberd/ejabberd.pid #. #' EJABBERD_CONFIG_PATH: ejabberd configuration file # # Specify the full path to the ejabberd configuration file. If the file name has # yml or yaml extension, it is parsed as a YAML file; otherwise, Erlang syntax is # expected. # # Default: $ETC_DIR/ejabberd.yml # #EJABBERD_CONFIG_PATH=/etc/ejabberd/ejabberd.yml #. #' CONTRIB_MODULES_PATH: contributed ejabberd modules path # # Specify the full path to the contributed ejabberd modules. If the path is not # defined, ejabberd will use ~/.ejabberd-modules in home of user running ejabberd. # # Default: $HOME/.ejabberd-modules # #CONTRIB_MODULES_PATH=/opt/ejabberd-modules #. #' CONTRIB_MODULES_CONF_DIR: configuration directory for contributed modules # # Specify the full path to the configuration directory for contributed ejabberd # modules. In order to configure a module named mod_foo, a mod_foo.yml file can # be created in this directory. This file will then be used instead of the # default configuration file provided with the module. # # Default: $CONTRIB_MODULES_PATH/conf # #CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules #. #' # vim: foldmarker=#',#. foldmethod=marker: ejabberd-18.01/asn1/0000755000232200023220000000000013225664356014505 5ustar debalancedebalanceejabberd-18.01/asn1/XmppAddr.asn10000644000232200023220000000057713225664356017021 0ustar debalancedebalanceXmppAddr { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-on(8) id-on-xmppAddr(5) } DEFINITIONS EXPLICIT TAGS ::= BEGIN id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-on(8) 5 } XmppAddr ::= UTF8String END ejabberd-18.01/COPYING0000644000232200023220000004332413225664356014704 0ustar debalancedebalanceAs a special exception, the authors give permission to link this program with the OpenSSL library and distribute the resulting binary. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ejabberd-18.01/rebar.config.script0000644000232200023220000002441113225664356017432 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% %%% ejabberd, Copyright (C) 2002-2017 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of {ok, Terms} -> Terms; _Err -> [] end ++ [{cflags, "-g -O2 -Wall"}, {cppflags, "-g -O2 -Wall"}, {ldflags, ""}, {system_deps, false}], {cflags, CFlags} = lists:keyfind(cflags, 1, Vars), {cppflags, CPPFlags} = lists:keyfind(cppflags, 1, Vars), {ldflags, LDFlags} = lists:keyfind(ldflags, 1, Vars), {system_deps, SystemDeps} = lists:keyfind(system_deps, 1, Vars), GetCfg0 = fun(F, Cfg, [Key | Tail], Default) -> Val = case lists:keyfind(Key, 1, Cfg) of {Key, V1} -> V1; false -> Default end, case Tail of [] -> Val; _ -> F(F, Val, Tail, Default) end end, ModCfg0 = fun(F, Cfg, [Key | Tail], Op, Default) -> {OldVal, PartCfg} = case lists:keytake(Key, 1, Cfg) of {value, {_, V1}, V2} -> {V1, V2}; false -> {if Tail == [] -> Default; true -> [] end, Cfg} end, case Tail of [] -> [{Key, Op(OldVal)} | PartCfg]; _ -> [{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg] end end, FilterConfig = fun(F, Cfg, [{Path, true, ModFun, Default} | Tail]) -> F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun, Default), Tail); (F, Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) -> SourceVal = GetCfg0(GetCfg0, Cfg, SourcePath, SourceDefault), ModFun2 = fun(V) -> ModFun(V, SourceVal) end, F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun2, Default), Tail); (F, Cfg, [_ | Tail]) -> F(F, Cfg, Tail); (_, Cfg, []) -> Cfg end, IsRebar3 = case application:get_key(rebar, vsn) of {ok, VSN} -> [VSN1 | _] = string:tokens(VSN, "-"), [Maj, _Min, _Patch] = string:tokens(VSN1, "."), (list_to_integer(Maj) >= 3); undefined -> lists:keymember(mix, 1, application:loaded_applications()) end, SysVer = erlang:system_info(otp_release), ProcessSingleVar = fun(F, Var, Tail) -> case F(F, [Var], []) of [] -> Tail; [Val] -> [Val | Tail] end end, ProcessVars = fun(_F, [], Acc) -> lists:reverse(Acc); (F, [{Type, Ver, Value} | Tail], Acc) when Type == if_version_above orelse Type == if_version_below -> SysVer = erlang:system_info(otp_release), Include = if Type == if_version_above -> SysVer > Ver; true -> SysVer < Ver end, if Include -> F(F, Tail, ProcessSingleVar(F, Value, Acc)); true -> F(F, Tail, Acc) end; (F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when Type == if_version_above orelse Type == if_version_below -> Include = if Type == if_version_above -> SysVer > Ver; true -> SysVer < Ver end, if Include -> F(F, Tail, ProcessSingleVar(F, Value, Acc)); true -> F(F, Tail, ProcessSingleVar(F, ElseValue, Acc)) end; (F, [{Type, Var, Value} | Tail], Acc) when Type == if_var_true orelse Type == if_var_false -> Flag = Type == if_var_true, case proplists:get_bool(Var, Vars) of V when V == Flag -> F(F, Tail, ProcessSingleVar(F, Value, Acc)); _ -> F(F, Tail, Acc) end; (F, [{Type, Value} | Tail], Acc) when Type == if_rebar3 orelse Type == if_not_rebar3 -> Flag = Type == if_rebar3, case IsRebar3 == Flag of true -> F(F, Tail, ProcessSingleVar(F, Value, Acc)); _ -> F(F, Tail, Acc) end; (F, [{Type, Var, Match, Value} | Tail], Acc) when Type == if_var_match orelse Type == if_var_no_match -> case proplists:get_value(Var, Vars) of V when V == Match -> F(F, Tail, ProcessSingleVar(F, Value, Acc)); _ -> F(F, Tail, Acc) end; (F, [{if_have_fun, MFA, Value} | Tail], Acc) -> {Mod, Fun, Arity} = MFA, code:ensure_loaded(Mod), case erlang:function_exported(Mod, Fun, Arity) of true -> F(F, Tail, ProcessSingleVar(F, Value, Acc)); false -> F(F, Tail, Acc) end; (F, [Other1 | Tail1], Acc) -> F(F, Tail1, [F(F, Other1, []) | Acc]); (F, Val, Acc) when is_tuple(Val) -> list_to_tuple(F(F, tuple_to_list(Val), Acc)); (_F, Other2, _Acc) -> Other2 end, MaybeApply = fun(Val) when is_function(Val) -> Val(); (Val) -> Val end, MaybeApply2 = fun(Val, Arg) when is_function(Val) -> Val(Arg); (Val, _) -> Val end, AppendStr = fun(Append) -> fun("") -> lists:flatten(MaybeApply(Append)); (Val) -> lists:flatten([Val, " ", MaybeApply(Append)]) end end, AppendList = fun(Append) -> fun(Val) -> Val ++ MaybeApply(Append) end end, AppendStr2 = fun(Append) -> fun("", Arg) -> lists:flatten(MaybeApply2(Append, Arg)); (Val, Arg) -> lists:flatten([Val, " ", MaybeApply2(Append, Arg)]) end end, AppendList2 = fun(Append) -> fun(Val, Arg) -> Val ++ MaybeApply2(Append, Arg) end end, Rebar3DepsFilter = fun(DepsList) -> lists:map(fun({DepName, _, {git, _, {tag, Version}}}) -> {DepName, Version}; (Dep) -> Dep end, DepsList) end, GlobalDepsFilter = fun(Deps) -> DepNames = lists:map(fun({DepName, _, _}) -> DepName; ({DepName, _}) -> DepName end, Deps), lists:filtermap(fun(Dep) -> case code:lib_dir(Dep) of {error, _} -> {true, "Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps."}; _ -> false end end, DepNames) end, {ok, Cwd} = file:get_cwd(), TestConfigFile = filename:join([Cwd, "test", "config.ctc"]), TestConfig = case file:read_file_info(TestConfigFile) of {ok, _} -> [" -userconfig ct_config_plain ", TestConfigFile, " "]; _ -> "" end, ResolveDepPath = case {SystemDeps, IsRebar3} of {true, _} -> fun("deps/" ++ Rest) -> Slash = string:str(Rest, "/"), case code:lib_dir(string:sub_string(Rest, 1, Slash -1)) of {error, _} -> Rest; V -> V ++ string:sub_string(Rest, Slash) end; (Path) -> Path end; {_, true} -> fun("deps/" ++ Rest) -> Slash = string:str(Rest, "/"), "_build/default/lib/" ++ string:sub_string(Rest, 1, Slash - 1) ++ string:sub_string(Rest, Slash); (Path) -> Path end; _ -> fun(P) -> P end end, CtParams = fun(CompileOpts) -> ["-ct_hooks cth_surefire ", lists:map(fun({i, IncPath}) -> [" -include ", filename:join([Cwd, ResolveDepPath(IncPath)])] end, CompileOpts), TestConfig] end, GenDepConfigureLine = fun(DepPath, Flags) -> ["sh -c 'if test ! -f config.status -o ", "../../config.status -nt config.status; ", "then (", "CFLAGS=\"", CFlags,"\" ", "CPPFLAGS=\"", CPPFlags, "\" " "LDFLAGS=\"", LDFlags, "\"", " ./configure ", string:join(Flags, " "), "); fi'"] end, GenDepsConfigure = fun(Hooks) -> lists:map(fun({Pkg, Flags}) -> DepPath = ResolveDepPath("deps/" ++ Pkg ++ "/"), {add, list_to_atom(Pkg), [{pre_hooks, {'compile', lists:flatten(GenDepConfigureLine(DepPath, Flags))}}]} end, Hooks) end, ProcessErlOpt = fun(Vals) -> lists:map( fun({i, Path}) -> {i, ResolveDepPath(Path)}; (ErlOpt) -> ErlOpt end, Vals) end, ProcssXrefExclusions = fun(Items) -> [{lists:flatten(["(XC - UC) || (XU - X - B ", [[" - ", V] || V <- Items], ")"]), []}] end, ProcessFloatingDeps = fun(Deps, FDeps) -> lists:map(fun({DepName, _Ver, {git, Repo, _Commit}} = Dep) -> case lists:member(DepName, FDeps) of true -> {DepName, ".*", {git, Repo}}; _ -> Dep end; (Dep2) -> Dep2 end, Deps) end, TravisPostHooks = fun(true) -> [{eunit, "echo '\n%%! -pa .eunit/ deps/coveralls/ebin\n" ++ "main(_)->{ok,F}=file:open(\"erlang.json\",[write])," ++ "io:fwrite(F,\"~s\",[coveralls:convert_file(" ++ "\".eunit/cover.coverdata\", \"" ++ os:getenv("TRAVIS_JOB_ID") ++ "\", \"travis-ci\",\"\")]).' > getcover.erl"}, {eunit, "escript ./getcover.erl"}]; (_) -> [] end, Rules = [ {[provider_hooks], IsRebar3, AppendList([{pre, [ {compile, {asn, compile}}, {clean, {asn, clean}} ]}]), []}, {[deps], os:getenv("TRAVIS") == "true", AppendList([{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}]), []}, {[post_hooks], [cover_enabled], os:getenv("TRAVIS") == "true", AppendList2(TravisPostHooks), [], false}, {[overrides], [post_hook_configure], SystemDeps == false, AppendList2(GenDepsConfigure), [], []}, {[ct_extra_params], [eunit_compile_opts], true, AppendStr2(CtParams), "", []}, {[erl_opts], true, ProcessErlOpt, []}, {[xref_queries], [xref_exclusions], true, AppendList2(ProcssXrefExclusions), [], []}, {[deps], [floating_deps], true, ProcessFloatingDeps, [], []}, {[deps], IsRebar3, Rebar3DepsFilter, []}, {[deps], SystemDeps /= false, GlobalDepsFilter, []} ], Config = [{plugin_dir, filename:join([filename:dirname(SCRIPT),"plugins"])}]++ FilterConfig(FilterConfig, ProcessVars(ProcessVars, CONFIG, []), Rules), %io:format("ejabberd configuration:~n ~p~n", [Config]), Config. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: ejabberd-18.01/tools/0000755000232200023220000000000013225664356015003 5ustar debalancedebalanceejabberd-18.01/tools/ejabberdctl.bc0000644000232200023220000000516013225664356017554 0ustar debalancedebalance# # bash completion for ejabberdctl # get_help() { local COMMANDCACHE=/var/log/ejabberd/bash_completion_$RANDOM ejabberdctl $CTLARGS help >$COMMANDCACHE if [[ $? == 2 ]] ; then ISRUNNING=1 runningcommands=`cat $COMMANDCACHE | grep "^ [a-z]" | awk '{print $1}' | xargs` fi rm $COMMANDCACHE } _ejabberdctl() { local cur prev local ISRUNNING=0 local runningcommands COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" local startcoms="start debug live" local startpars="--config-dir --config --ctl-config --logs --spool" local i=1 local CTLARGS="" while [ $i -lt $COMP_CWORD ] ; do local PARAM="${COMP_WORDS[i]}" i=$((i+1)) case $PARAM in --*) CTLARGS="--node ${COMP_WORDS[i]}" i=$((i+1)) ;; *) break ;; esac done case "${prev##*/}" in ejabberdctl) # This clause matches even when calling `/sbin/ejabberdctl` thanks to the ##*/ in the case get_help COMPREPLY=($(compgen -W "--node ${startpars} ${startcoms} ${runningcommands}" -- $cur)) return 0 ;; start|live) COMPREPLY=($(compgen -W "--node ${startpars}" -- $cur)) return 0 ;; debug) COMPREPLY=($(compgen -W "--node" -- $cur)) return 0 ;; help) get_help COMPREPLY=($(compgen -W "${runningcommands}" -- $cur)) return 0 ;; --node) RUNNINGNODES=`epmd -names | grep name | awk '{print $2"@localhost"}' | xargs` COMPREPLY=($(compgen -W "$RUNNINGNODES" -- $cur)) return 0 ;; --config|--ctl-config) _filedir '?(u)cfg' return 0 ;; --config-dir|--logs|--spool) _filedir return 0 ;; *) prev2="${COMP_WORDS[COMP_CWORD-2]}" get_help if [[ "$prev2" == --* ]]; then COMPREPLY=($(compgen -W "--node ${startpars} ${startcoms} ${runningcommands}" -- $cur)) else if [[ $ISRUNNING == 1 ]]; then echo "" ejabberdctl $CTLARGS help ${PARAM} echo -n "${COMP_LINE}" fi fi return 0 ;; esac } complete -F _ejabberdctl ejabberdctl # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=sh ejabberd-18.01/tools/prepare-tr.sh0000755000232200023220000000460313225664356017426 0ustar debalancedebalance#!/bin/bash # Frontend for ejabberd's extract-tr.sh # How to create template files for a new language: # NEWLANG=zh # cp priv/msgs/ejabberd.pot priv/msgs/$NEWLANG.po # echo \{\"\",\"\"\}. > priv/msgs/$NEWLANG.msg # make translations extract_lang_src2pot () { ./tools/extract-tr.sh ebin/ > priv/msgs/ejabberd.pot } extract_lang_popot2po () { LANG_CODE=$1 PO_PATH=$MSGS_DIR/$LANG_CODE.po POT_PATH=$MSGS_DIR/$PROJECT.pot msgmerge $PO_PATH $POT_PATH >$PO_PATH.translate 2>/dev/null mv $PO_PATH.translate $PO_PATH } extract_lang_po2msg () { LANG_CODE=$1 PO_PATH=$LANG_CODE.po MS_PATH=$PO_PATH.ms MSGID_PATH=$PO_PATH.msgid MSGSTR_PATH=$PO_PATH.msgstr MSGS_PATH=$LANG_CODE.msg cd $MSGS_DIR # Check PO has correct ~ # Let's convert to C format so we can use msgfmt PO_TEMP=$LANG_CODE.po.temp cat $PO_PATH | sed 's/%/perc/g' | sed 's/~/%/g' | sed 's/#:.*/#, c-format/g' >$PO_TEMP msgfmt $PO_TEMP --check-format result=$? rm $PO_TEMP if [ $result -ne 0 ] ; then exit 1 fi msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH echo "%% -*- coding: latin-1 -*-" >$MSGS_PATH paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH rm $MS_PATH rm $MSGID_PATH rm $MSGSTR_PATH } extract_lang_updateall () { echo "" echo "Generating POT..." extract_lang_src2pot cd $MSGS_DIR echo "" echo -e "File Missing (fuzzy) Language Last translator" echo -e "---- ------- ------- -------- ---------------" for i in $( ls *.msg ) ; do LANG_CODE=${i%.msg} echo -n $LANG_CODE | awk '{printf "%-6s", $1 }' PO=$LANG_CODE.po extract_lang_popot2po $LANG_CODE extract_lang_po2msg $LANG_CODE MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4+$7 }'` echo -n " $MISSING" FUZZY=`msgfmt --statistics $PO 2>&1 | awk '{printf "%7s", $4 }'` echo -n " $FUZZY" LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'` echo -n " $LANGUAGE" LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'` echo " $LASTAUTH" done echo "" rm messages.mo cd .. } EJA_DIR=`pwd` PROJECT=ejabberd MSGS_DIR=$EJA_DIR/priv/msgs extract_lang_updateall ejabberd-18.01/tools/jhbtest.pl0000755000232200023220000002755213225664356017021 0ustar debalancedebalance#!/usr/bin/perl -w use strict; # har har use constant ERR => 0; use constant WARN => 1; use constant INFO => 2; use constant DEBUG => 3; use constant RID => 31974; ### ### ### conf ### ### ### my $BASE_ADDRESS = "http://localhost:5280/http-bind/"; my $JABBER_SERVER = "localhost"; my $RID = RID; my $WAIT = 60; my $USER = "tester"; my $UPW = "mysecret"; my $DEBUG = INFO; ### ### ### END conf ### ### ### # create an agent we can use for our requests use LWP::UserAgent; my $ua = new LWP::UserAgent(); # create a tree parser to parse response content use XML::Parser; my $p = new XML::Parser(Style => 'Tree'); ### ### ### subs ### ### ### sub doSend() { my $content = shift; # create a request my $req = new HTTP::Request(POST => $BASE_ADDRESS); $req->content_type('text/xml; charset=utf-8'); $req->content($content); debug(DEBUG,"<< Request\n".$req->as_string."<< END Request"); # send request my $res = $ua->request($req); debug(DEBUG,">> Response\n" . $res->as_string .">> END Response"); return $res; } # getChildEls # used to strip enclosing body element # PARAMS: @tree - tree style array from XML::Parser # RETURN: @children - child elements of top level element sub getChildEls { my $t = $_[0]; shift @{$t->[1]}; return @{$t->[1]}; } sub debug { my $lvl = shift; my $msg = shift; return if ($DEBUG < $lvl); my $prefix = "["; $prefix .= "ERROR" if ($lvl == ERR); $prefix .= "WARNING" if ($lvl == WARN); $prefix .= "INFO" if ($lvl == INFO); $prefix .= "DEBUG" if ($lvl == DEBUG); $prefix .= "] "; $msg =~ s/\n/\n$prefix/g; print STDERR $prefix . $msg . "\n"; } ### ### ### main ### ### ### $| = 1; # set streaming output # no body print "Sending some 'foo': "; my $res = &doSend("foo"); if ($res->code == 400) { print "OK.\n"; } else { print "Failed!\n"; print $res->as_string, "\n"; } # no body print "Sending some '': "; $res = &doSend(""); if ($res->code == 400) { print "OK.\n"; } else { print "Failed!\n"; print $res->as_string, "\n"; } # empty body print "Sending empty body: "; $res = &doSend(""); if ($res->code == 400) { print "OK.\n"; } else { print "Failed!\n"; print $res->as_string, "\n"; } # fake a sid print "Sending wrong sid: "; $res = &doSend(""); if ($res->code == 404) { print "OK.\n"; } else { print "Failed!\n"; print $res->as_string, "\n"; } # forget to send 'to' print "Missing 'to' attribute at session creation request: "; $res = &doSend(""); if ($res->is_success && $res->content =~ /content =~/as_string, "\n"; } # sending empty 'to' attribute print "Empty 'to' attribute at session creation request: "; $res = &doSend(""); if ($res->is_success && $res->content =~ /content =~/as_string, "\n"; } # forget to send a rid print "Missing 'rid' attribute at session creation request: "; $res = &doSend(""); if ($res->code == 404) { print "OK.\n"; } else { print "Failed!\n"; print $res->as_string, "\n"; } # trying to connect to non-existent domain print "Connecting to non-existent domain: "; $res = &doSend(""); if ($res->is_success && $res->content =~ /content =~/as_string, "\n"; } # trying to connect to non-existent jabber server print "Connecting to non-existent jabber server: "; $res = &doSend(""); if ($res->is_success && $res->content =~ /content =~/content =~/as_string, "\n"; } # connection to foreign server #print "Connecting to foreign jabber server: "; #$res = &doSend(""); #if ($res->is_success && $res->content =~ /content =~/as_string, "\n"; #} my %sess; sub getSess { $sess{rid} = RID; # a rid to start $res = &doSend(""); if ($res->is_success) { my $t = $p->parse($res->content); %sess = %{$t->[1]->[0]}; $sess{rid} = RID; # a rid to start if (defined($sess{sid}) && $sess{sid} ne "" && defined($sess{wait}) && $sess{wait} ne '' && $sess{wait} <= $WAIT) { debug(INFO,"sid: $sess{sid}"); debug(INFO, "authid: $sess{authid}") if (defined($sess{authid})); debug(INFO, "wait: $sess{wait}"); debug(INFO, "inactivity: $sess{inactivity}") if (defined($sess{inactivity})); debug(INFO, "polling: $sess{polling}") if (defined($sess{polling})); debug(INFO, "requests: $sess{requests}") if (defined($sess{requests})); debug(INFO, "accept: $sess{accept}") if (defined($sess{accept})); debug(INFO, "charsets: $sess{charsets}") if (defined($sess{charsets})); debug (WARN, "authid missing") unless (defined($sess{authid}) && $sess{authid} ne ''); debug (WARN, "server indicates polling mode") if (defined($sess{requests}) && $sess{requests} == 1); return 1; } } debug(ERR, "sid missing") unless (defined($sess{sid}) && $sess{sid} ne ""); debug(ERR, "wait missing") unless (defined($sess{wait}) && $sess{wait} ne ''); debug(ERR, "wait bigger then requested") unless (defined($sess{wait}) && $sess{wait} ne '' && $sess{wait} <= $WAIT); debug(DEBUG, $res->as_string); return 0; } # try to get a real sid print "Create a new session: "; if (&getSess()) { print "OK.\n"; } else { debug(ERR, "Aborting."); exit(1); } # checking wait attribute print "Creating another session with smaller 'wait' then before: "; $WAIT = $sess{wait} - 1; if (&getSess()) { print "OK.\n"; } else { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } sub doAuth { # query auth $sess{rid}++; $res = &doSend("$USER"); my @els = (&getChildEls($p->parse($res->content))); unless ($els[0] eq 'iq' && $els[1]->[0]->{'type'} eq 'result') { debug(ERR, $res->content); return 0; } # send auth $sess{rid}++; $res = &doSend("$USERtest$UPW"); @els = (&getChildEls($p->parse($res->content))); unless ($els[0] eq 'iq' && $els[1]->[0]->{'type'} eq 'result') { debug(ERR, $res->content); return 0; } return 1; } print "Authenticating: "; if (&doAuth()) { print "OK.\n"; } else { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } sub doPoll { $sess{rid}++; return &doSend(""); } print "Polling with wrong 'rid': "; $sess{rid}--; $res = &doPoll(); if ($res->code != 404) { print "FAILED!\n"; debug(ERR, "Aborting."); # exit(1); } print "OK.\n"; print "Checking if session terminated: "; $res = &doPoll(); if ($res->code != 404) { print "FAILED!\n"; debug(ERR, "Aborting."); # exit(1); } print "OK.\n"; print "Create a new session: "; if (&getSess()) { print "OK.\n"; } else { debug(ERR, "Aborting."); exit(1); } print "Authenticating: "; if (&doAuth()) { print "OK.\n"; } else { print "FAILED!\n"; debug(ERR, "Aborting."); # exit(1); } print "Polling too frequently: "; $res = &doPoll(); if ($res->code != 200) { print "First poll failed unexpectedly!\n"; debug(ERR, "Aborting."); #exit(1); } $res = &doPoll(); if ($res->code != 403) { print "FAILED!\n"; debug(ERR, "Aborting."); #exit(1); } print "OK.\n"; print "Checking if session terminated: "; $res = &doPoll(); if ($res->code != 404) { print "FAILED!\n"; debug(ERR, "Aborting."); #exit(1); } print "OK.\n"; print "Create a new session: "; if (&getSess()) { print "OK.\n"; } else { debug(ERR, "Aborting."); exit(1); } print "Authenticating: "; if (&doAuth()) { print "OK.\n"; } else { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } print "Test if proper polling is allowed: "; $res = &doPoll(); if ($res->code != 200) { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } sleep $sess{polling}; $res = &doPoll(); if ($res->code != 200) { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } print "OK.\n"; print "Waiting for session to timeout: "; my $STEP=10; for (my $i=0; $i<$sess{inactivity}; $i+=$STEP) { print "."; sleep $STEP; } sleep 1; # take another nap $res = &doPoll(); if ($res->code != 404) { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } print "OK.\n"; print "Create a new session: "; if (&getSess()) { print "OK.\n"; } else { debug(ERR, "Aborting."); exit(1); } print "Authenticating: "; if (&doAuth()) { print "OK.\n"; } else { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } # [TODO] # Check for # * KEY Sequence Algorithm Compliance # * Too Many Simultaneous Connections (probably hard to achieve for polling mode) # * request custom content-type/-encoding # get roster print "getting roster\n"; $sess{rid}++; $res = &doSend(""); debug(INFO, $res->content); # send presence print "sending presence\n"; $sess{rid}++; $res = &doSend(""); debug(INFO, $res->content); # sending bullshit print "sending bullshit\n"; $sess{rid}++; $res = &doSend("sending bullshit"); debug(INFO, $res->content); # send presence print "sending xa presence\n"; $sess{rid}++; $res = &doSend("xa"); debug(INFO, $res->content); # disconnect sleep 3; print "logout\n"; $sess{rid}++; $res = &doSend(""); debug(INFO, $res->content); print "Checking if session terminated: "; $res = &doPoll(); if ($res->code != 404) { print "FAILED!\n"; debug(ERR, "Aborting."); exit(1); } print "OK.\n"; ejabberd-18.01/tools/hook_deps.sh0000755000232200023220000002704613225664356017326 0ustar debalancedebalance#!/usr/bin/env escript %% -*- erlang -*- %%! -pa ebin -record(state, {run_hooks = dict:new(), run_fold_hooks = dict:new(), hooked_funs = dict:new(), mfas = dict:new(), specs = dict:new(), module :: module(), file :: filename:filename()}). main([Dir]) -> State = filelib:fold_files( Dir, ".+\.[eh]rl\$", false, fun(FileIn, Res) -> case get_forms(FileIn) of {ok, Forms} -> Tree = erl_syntax:form_list(Forms), Mod = list_to_atom(filename:rootname(filename:basename(FileIn))), Acc0 = analyze_form(Tree, Res#state{module = Mod, file = FileIn}), erl_syntax_lib:fold( fun(Form, Acc) -> case erl_syntax:type(Form) of application -> case erl_syntax_lib:analyze_application(Form) of {ejabberd_hooks, {run, N}} when N == 2; N == 3 -> analyze_run_hook(Form, Acc); {ejabberd_hooks, {run_fold, N}} when N == 3; N == 4 -> analyze_run_fold_hook(Form, Acc); {ejabberd_hooks, {add, N}} when N == 4; N == 5 -> analyze_run_fun(Form, Acc); {gen_iq_handler, {add_iq_handler, 6}} -> analyze_iq_handler(Form, Acc); _ -> Acc end; attribute -> case catch erl_syntax_lib:analyze_attribute(Form) of {spec, _} -> analyze_type_spec(Form, Acc); _ -> Acc end; _ -> Acc end end, Acc0, Tree); _Err -> Res end end, #state{}), report_orphaned_funs(State), RunDeps = build_deps(State#state.run_hooks, State#state.hooked_funs), RunFoldDeps = build_deps(State#state.run_fold_hooks, State#state.hooked_funs), emit_module(RunDeps, RunFoldDeps, State#state.specs, Dir, hooks_type_test). analyze_form(_Form, State) -> %% case catch erl_syntax_lib:analyze_forms(Form) of %% Props when is_list(Props) -> %% M = State#state.module, %% MFAs = lists:foldl( %% fun({F, A}, Acc) -> %% dict:append({M, F}, A, Acc) %% end, State#state.mfas, %% proplists:get_value(functions, Props, [])), %% State#state{mfas = MFAs}; %% _ -> %% State %% end. State. analyze_run_hook(Form, State) -> [Hook|Tail] = erl_syntax:application_arguments(Form), case atom_value(Hook, State) of undefined -> State; HookName -> Args = case Tail of [_Host, Args0] -> Args0; [Args0] -> Args0 end, Arity = erl_syntax:list_length(Args), Hooks = dict:store({HookName, Arity}, {State#state.file, erl_syntax:get_pos(Hook)}, State#state.run_hooks), State#state{run_hooks = Hooks} end. analyze_run_fold_hook(Form, State) -> [Hook|Tail] = erl_syntax:application_arguments(Form), case atom_value(Hook, State) of undefined -> State; HookName -> Args = case Tail of [_Host, _Val, Args0] -> Args0; [_Val, Args0] -> Args0 end, Arity = erl_syntax:list_length(Args) + 1, Hooks = dict:store({HookName, Arity}, {State#state.file, erl_syntax:get_pos(Form)}, State#state.run_fold_hooks), State#state{run_fold_hooks = Hooks} end. analyze_run_fun(Form, State) -> [Hook|Tail] = erl_syntax:application_arguments(Form), case atom_value(Hook, State) of undefined -> State; HookName -> {Module, Fun, Seq} = case Tail of [_Host, M, F, S] -> {M, F, S}; [M, F, S] -> {M, F, S} end, ModName = module_name(Module, State), FunName = atom_value(Fun, State), if ModName /= undefined, FunName /= undefined -> Funs = dict:append( HookName, {ModName, FunName, integer_value(Seq, State), {State#state.file, erl_syntax:get_pos(Form)}}, State#state.hooked_funs), State#state{hooked_funs = Funs}; true -> State end end. analyze_iq_handler(Form, State) -> [_Component, _Host, _NS, Module, Function, _IQDisc] = erl_syntax:application_arguments(Form), Mod = module_name(Module, State), Fun = atom_value(Function, State), if Mod /= undefined, Fun /= undefined -> code:ensure_loaded(Mod), case erlang:function_exported(Mod, Fun, 1) of false -> log("~s:~p: Error: function ~s:~s/1 is registered " "as iq handler, but is not exported~n", [State#state.file, erl_syntax:get_pos(Form), Mod, Fun]); true -> ok end; true -> ok end, State. analyze_type_spec(Form, State) -> case catch erl_syntax:revert(Form) of {attribute, _, spec, {{F, A}, _}} -> Specs = dict:store({State#state.module, F, A}, {Form, State#state.file}, State#state.specs), State#state{specs = Specs}; _ -> State end. build_deps(Hooks, Hooked) -> dict:fold( fun({Hook, Arity}, {_File, _LineNo} = Meta, Deps) -> case dict:find(Hook, Hooked) of {ok, Funs} -> ExportedFuns = lists:flatmap( fun({M, F, Seq, {FunFile, FunLineNo} = FunMeta}) -> code:ensure_loaded(M), case erlang:function_exported(M, F, Arity) of false -> log("~s:~p: Error: function ~s:~s/~p " "is hooked on ~s/~p, but is not " "exported~n", [FunFile, FunLineNo, M, F, Arity, Hook, Arity]), []; true -> [{{M, F, Arity}, Seq, FunMeta}] end end, Funs), dict:append_list({Hook, Arity, Meta}, ExportedFuns, Deps); error -> %% log("~s:~p: Warning: hook ~p/~p is unused~n", %% [_File, _LineNo, Hook, Arity]), dict:append_list({Hook, Arity, Meta}, [], Deps) end end, dict:new(), Hooks). report_orphaned_funs(State) -> dict:map( fun(Hook, Funs) -> lists:foreach( fun({M, F, _, {File, Line}}) -> case get_fun_arities(M, F, State) of [] -> log("~s:~p: Error: function ~s:~s is " "hooked on hook ~s, but is not exported~n", [File, Line, M, F, Hook]); Arities -> case lists:any( fun(Arity) -> dict:is_key({Hook, Arity}, State#state.run_hooks) orelse dict:is_key({Hook, Arity}, State#state.run_fold_hooks); (_) -> false end, Arities) of false -> Arity = hd(Arities), log("~s:~p: Error: function ~s:~s/~p is hooked" " on non-existent hook ~s/~p~n", [File, Line, M, F, Arity, Hook, Arity]); true -> ok end end end, Funs) end, State#state.hooked_funs). get_fun_arities(Mod, Fun, _State) -> proplists:get_all_values(Fun, Mod:module_info(exports)). module_name(Form, State) -> try Name = erl_syntax:macro_name(Form), 'MODULE' = erl_syntax:variable_name(Name), State#state.module catch _:_ -> atom_value(Form, State) end. atom_value(Form, State) -> case erl_syntax:type(Form) of atom -> erl_syntax:atom_value(Form); _ -> log("~s:~p: Warning: not an atom: ~s~n", [State#state.file, erl_syntax:get_pos(Form), erl_prettypr:format(Form)]), undefined end. integer_value(Form, State) -> case erl_syntax:type(Form) of integer -> erl_syntax:integer_value(Form); _ -> log("~s:~p: Warning: not an integer: ~s~n", [State#state.file, erl_syntax:get_pos(Form), erl_prettypr:format(Form)]), 0 end. emit_module(RunDeps, RunFoldDeps, Specs, Dir, Module) -> File = filename:join([Dir, Module]) ++ ".erl", try {ok, Fd} = file:open(File, [write]), write(Fd, "-module(~s).~n~n", [Module]), emit_export(Fd, RunDeps, "run hooks"), emit_export(Fd, RunFoldDeps, "run_fold hooks"), emit_run_hooks(Fd, RunDeps, Specs), emit_run_fold_hooks(Fd, RunFoldDeps, Specs), file:close(Fd), log("Module written to file ~s~n", [File]) catch _:{badmatch, {error, Reason}} -> log("writing to ~s failed: ~s", [File, file:format_error(Reason)]) end. emit_run_hooks(Fd, Deps, Specs) -> DepsList = lists:sort(dict:to_list(Deps)), lists:foreach( fun({{Hook, Arity, {File, LineNo}}, []}) -> Args = lists:duplicate(Arity, "_"), write(Fd, "%% called at ~s:~p~n", [File, LineNo]), write(Fd, "~s(~s) -> ok.~n~n", [Hook, string:join(Args, ", ")]); ({{Hook, Arity, {File, LineNo}}, Funs}) -> emit_specs(Fd, Funs, Specs), write(Fd, "%% called at ~s:~p~n", [File, LineNo]), Args = string:join( [[N] || N <- lists:sublist(lists:seq($A, $Z), Arity)], ", "), write(Fd, "~s(~s) ->~n ", [Hook, Args]), Calls = [io_lib:format("~s:~s(~s)", [Mod, Fun, Args]) || {{Mod, Fun, _}, _Seq, _} <- lists:keysort(2, Funs)], write(Fd, "~s.~n~n", [string:join(Calls, ",\n ")]) end, DepsList). emit_run_fold_hooks(Fd, Deps, Specs) -> DepsList = lists:sort(dict:to_list(Deps)), lists:foreach( fun({{Hook, Arity, {File, LineNo}}, []}) -> write(Fd, "%% called at ~s:~p~n", [File, LineNo]), Args = ["Acc"|lists:duplicate(Arity - 1, "_")], write(Fd, "~s(~s) -> Acc.~n~n", [Hook, string:join(Args, ", ")]); ({{Hook, Arity, {File, LineNo}}, Funs}) -> emit_specs(Fd, Funs, Specs), write(Fd, "%% called at ~s:~p~n", [File, LineNo]), Args = [[N] || N <- lists:sublist(lists:seq($A, $Z), Arity - 1)], write(Fd, "~s(~s) ->~n ", [Hook, string:join(["Acc0"|Args], ", ")]), {Calls, _} = lists:mapfoldl( fun({{Mod, Fun, _}, _Seq, _}, N) -> Args1 = ["Acc" ++ integer_to_list(N)|Args], {io_lib:format("Acc~p = ~s:~s(~s)", [N+1, Mod, Fun, string:join(Args1, ", ")]), N + 1} end, 0, lists:keysort(2, Funs)), write(Fd, "~s,~n", [string:join(Calls, ",\n ")]), write(Fd, " Acc~p.~n~n", [length(Funs)]) end, DepsList). emit_export(Fd, Deps, Comment) -> DepsList = lists:sort(dict:to_list(Deps)), Exports = lists:map( fun({{Hook, Arity, _}, _}) -> io_lib:format("~s/~p", [Hook, Arity]) end, DepsList), write(Fd, "%% ~s~n-export([~s]).~n~n", [Comment, string:join(Exports, ",\n ")]). emit_specs(Fd, Funs, Specs) -> lists:foreach( fun({{M, _, _} = MFA, _, _}) -> case dict:find(MFA, Specs) of {ok, {Form, _File}} -> Lines = string:tokens(erl_syntax:get_ann(Form), "\n"), lists:foreach( fun("%" ++ _) -> ok; ("-spec" ++ Spec) -> write(Fd, "%% -spec ~p:~s~n", [M, string:strip(Spec, left)]); (Line) -> write(Fd, "%% ~s~n", [Line]) end, Lines); error -> ok end end, lists:keysort(2, Funs)). get_forms(Path) -> case file:open(Path, [read]) of {ok, Fd} -> parse(Path, Fd, 1, []); Err -> Err end. parse(Path, Fd, Line, Acc) -> {ok, Pos} = file:position(Fd, cur), case epp_dodger:parse_form(Fd, Line) of {ok, Form, NewLine} -> {ok, NewPos} = file:position(Fd, cur), {ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), file:position(Fd, {bof, NewPos}), AnnForm = erl_syntax:set_ann(Form, RawForm), parse(Path, Fd, NewLine, [AnnForm|Acc]); {eof, _} -> {ok, NewPos} = file:position(Fd, cur), if NewPos > Pos -> {ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), Form = erl_syntax:text(""), AnnForm = erl_syntax:set_ann(Form, RawForm), {ok, lists:reverse([AnnForm|Acc])}; true -> {ok, lists:reverse(Acc)} end; {error, {_, _, ErrDesc}, LineNo} = Err -> log("~s:~p: Error: ~s~n", [Path, LineNo, erl_parse:format_error(ErrDesc)]), Err end. log(Format, Args) -> io:format(standard_io, Format, Args). write(Fd, Format, Args) -> file:write(Fd, io_lib:format(Format, Args)). ejabberd-18.01/tools/extract-tr.sh0000755000232200023220000001717113225664356017446 0ustar debalancedebalance#!/usr/bin/env escript %% -*- erlang -*- %%! -pa ebin main([Dir]) -> Txts = filelib:fold_files( Dir, ".+\.beam\$", false, fun(BeamFile, Res) -> Mod = mod(BeamFile), ErlFile = filename:join("src", Mod ++ ".erl"), case get_forms(BeamFile, ErlFile) of {ok, BeamForms, ErlForms} -> process_forms(BeamForms, Mod, application) ++ process_forms(ErlForms, Mod, macro) ++ Res; _Err -> Res end end, []), Dict = lists:foldl( fun({B, Meta}, Acc) -> dict:update( binary_to_list(B), fun(OldMeta) -> lists:usort([Meta|OldMeta]) end, [Meta], Acc) end, dict:new(), Txts), generate_pot(Dict). process_forms(Forms, Mod, Type) -> Tree = erl_syntax:form_list(Forms), erl_syntax_lib:fold_subtrees( fun(Form, Acc) -> case erl_syntax:type(Form) of function -> case map(Form, Mod, Type) of [] -> Acc; Vars -> Vars ++ Acc end; _ -> Acc end end, [], Tree). map(Tree, Mod, Type) -> Vars = erl_syntax_lib:fold( fun(Form, Acc) -> case erl_syntax:type(Form) of Type when Type == application -> analyze_app(Form, Mod) ++ Acc; Type when Type == macro -> analyze_macro(Form, Mod) ++ Acc; _ -> Acc end end, [], Tree), Bins = lists:flatmap( fun({Var, Pos}) when is_atom(Var) -> Res = erl_syntax_lib:fold( fun(Form, Acc) -> case process_match_expr( Form, Var, Mod) of {ok, Binary, NewPos} -> [{Binary, NewPos}|Acc]; error -> Acc end end, [], Tree), case Res of [] -> log("~s:~p: unresolved variable: ~s~n", [Mod, Pos, Var]); _ -> ok end, Res; ({Var, Pos}) when is_binary(Var) -> [{Var, Pos}] end, lists:usort(Vars)), [{B, {Mod, Pos}} || {B, Pos} <- Bins, B /= <<"">>]. process_match_expr(Form, Var, Mod) -> case erl_syntax:type(Form) of match_expr -> Pattern = erl_syntax:match_expr_pattern(Form), Body = erl_syntax:match_expr_body(Form), {V, Expr} = case {erl_syntax:type(Pattern), erl_syntax:type(Body)} of {variable, _} -> {erl_syntax:variable_name(Pattern), Body}; {_, variable} -> {erl_syntax:variable_name(Body), Pattern}; _ -> {'', none} end, Text = maybe_extract_tuple(Expr), if V == Var -> Pos = erl_syntax:get_pos(Text), try {ok, erl_syntax:concrete(Text), Pos} catch _:_ -> case catch erl_syntax_lib:analyze_application(Text) of {_M, {Fn, 1}} when Fn == format_error; Fn == io_format_error -> error; _ -> log("~s:~p: not a binary: ~s~n", [Mod, Pos, erl_prettypr:format(Text)]), {ok, <<>>, Pos} end end; true -> error end; _ -> error end. maybe_extract_tuple(none) -> none; maybe_extract_tuple(Form) -> try tuple = erl_syntax:type(Form), [Text, _] = erl_syntax:tuple_elements(Form), Text catch _:{badmatch, _} -> Form end. analyze_app(Form, Mod) -> try {M, {F, A}} = erl_syntax_lib:analyze_application(Form), Args = erl_syntax:application_arguments(Form), Txt = case {M, atom_to_list(F), A, Args} of {xmpp, "err_" ++ _, 2, [T|_]} -> T; {xmpp, "serr_" ++ _, 2, [T|_]} -> T; {xmpp, "mk_text", 2, [T|_]} -> T; {translate, "translate", 2, [_,T|_]} -> T end, Pos = erl_syntax:get_pos(Txt), case erl_syntax:type(Txt) of binary -> try [{erl_syntax:concrete(Txt), Pos}] catch _:_ -> Pos = erl_syntax:get_pos(Txt), log("~s:~p: not a binary: ~s~n", [Mod, Pos, erl_prettypr:format(Txt)]), [] end; variable -> [{erl_syntax:variable_name(Txt), Pos}]; application -> Vars = sets:to_list(erl_syntax_lib:variables(Txt)), case Vars of [Var] -> [{Var, Pos}]; [_|_] -> log("Too many variables: ~p~n", [Vars]), []; [] -> [] end; _ -> [] end catch _:{badmatch, _} -> []; _:{case_clause, _} -> [] end. analyze_macro(Form, Mod) -> try Name = erl_syntax:macro_name(Form), variable = erl_syntax:type(Name), 'T' = erl_syntax:variable_name(Name), [Txt] = erl_syntax:macro_arguments(Form), string = erl_syntax:type(Txt), Pos = erl_syntax:get_pos(Txt), try [{list_to_binary(erl_syntax:string_value(Txt)), Pos}] catch _:_ -> log("~s:~p: not a binary: ~s~n", [Mod, Pos, erl_prettypr:format(Txt)]), [] end catch _:{badmatch, _} -> [] end. generate_pot(Dict) -> io:format("~s~n~n", [pot_header()]), lists:foreach( fun({Msg, Location}) -> S1 = format_location(Location), S2 = format_msg(Msg), io:format("~smsgstr \"\"~n~n", [S1 ++ S2]) end, lists:keysort(1, dict:to_list(Dict))). format_location([A, B, C|T]) -> format_location_list([A,B,C]) ++ format_location(T); format_location([A, B|T]) -> format_location_list([A,B]) ++ format_location(T); format_location([A|T]) -> format_location_list([A]) ++ format_location(T); format_location([]) -> "". format_location_list(L) -> "#: " ++ string:join( lists:map( fun({File, Pos}) -> io_lib:format("~s:~B", [File, Pos]) end, L), " ") ++ io_lib:nl(). format_msg(Bin) -> io_lib:format("msgid \"~s\"~n", [escape(Bin)]). escape(Bin) -> lists:map( fun($") -> "\\\""; (C) -> C end, binary_to_list(iolist_to_binary(Bin))). pot_header() -> string:join( ["msgid \"\"", "msgstr \"\"", "\"Project-Id-Version: 15.11.127\\n\"", "\"X-Language: Language Name\\n\"", "\"Last-Translator: Translator name and contact method\\n\"", "\"MIME-Version: 1.0\\n\"", "\"Content-Type: text/plain; charset=UTF-8\\n\"", "\"Content-Transfer-Encoding: 8bit\\n\""], io_lib:nl()). mod(Path) -> filename:rootname(filename:basename(Path)). log(Format, Args) -> io:format(standard_error, Format, Args). get_forms(BeamFile, ErlFile) -> try {ok, BeamForms} = get_beam_forms(BeamFile), {ok, ErlForms} = get_erl_forms(ErlFile), {ok, BeamForms, ErlForms} catch _:{badmatch, error} -> error end. get_beam_forms(File) -> case beam_lib:chunks(File, [abstract_code]) of {ok, {_, List}} -> case lists:keyfind(abstract_code, 1, List) of {abstract_code, {raw_abstract_v1, Abstr}} -> {ok, Abstr}; _Err -> log("failed to get abstract code from ~s~n", [File]), error end; Err -> log("failed to read chunks from ~s: ~p~n", [File, Err]), error end. get_erl_forms(Path) -> case file:open(Path, [read]) of {ok, Fd} -> parse(Path, Fd, 1, []); {error, Why} -> log("failed to read ~s: ~s~n", [Path, file:format_error(Why)]), error end. parse(Path, Fd, Line, Acc) -> {ok, Pos} = file:position(Fd, cur), case epp_dodger:parse_form(Fd, Line) of {ok, Form, NewLine} -> {ok, NewPos} = file:position(Fd, cur), {ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), file:position(Fd, {bof, NewPos}), AnnForm = erl_syntax:set_ann(Form, RawForm), parse(Path, Fd, NewLine, [AnnForm|Acc]); {eof, _} -> {ok, NewPos} = file:position(Fd, cur), if NewPos > Pos -> {ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), Form = erl_syntax:text(""), AnnForm = erl_syntax:set_ann(Form, RawForm), {ok, lists:reverse([AnnForm|Acc])}; true -> {ok, lists:reverse(Acc)} end; Err -> log("failed to parse ~s: ~p~n", [Path, Err]), error end. ejabberd-18.01/tools/captcha.sh0000755000232200023220000000413513225664356016750 0ustar debalancedebalance#!/bin/sh INPUT=$1 if test -n ${BASH_VERSION:-''} ; then get_random () { R=$RANDOM } else for n in `od -A n -t u2 -N 48 /dev/urandom`; do RL="$RL$n "; done get_random () { R=${RL%% *} RL=${RL#* } } fi get_random WAVE1_AMPLITUDE=$((2 + $R % 5)) get_random WAVE1_LENGTH=$((50 + $R % 25)) get_random WAVE2_AMPLITUDE=$((2 + $R % 5)) get_random WAVE2_LENGTH=$((50 + $R % 25)) get_random WAVE3_AMPLITUDE=$((2 + $R % 5)) get_random WAVE3_LENGTH=$((50 + $R % 25)) get_random W1_LINE_START_Y=$((10 + $R % 40)) get_random W1_LINE_STOP_Y=$((10 + $R % 40)) get_random W2_LINE_START_Y=$((10 + $R % 40)) get_random W2_LINE_STOP_Y=$((10 + $R % 40)) get_random W3_LINE_START_Y=$((10 + $R % 40)) get_random W3_LINE_STOP_Y=$((10 + $R % 40)) get_random B1_LINE_START_Y=$(($R % 40)) get_random B1_LINE_STOP_Y=$(($R % 40)) get_random B2_LINE_START_Y=$(($R % 40)) get_random B2_LINE_STOP_Y=$(($R % 40)) #B3_LINE_START_Y=$(($R % 40)) #B3_LINE_STOP_Y=$(($R % 40)) get_random B1_LINE_START_X=$(($R % 20)) get_random B1_LINE_STOP_X=$((100 + $R % 40)) get_random B2_LINE_START_X=$(($R % 20)) get_random B2_LINE_STOP_X=$((100 + $R % 40)) #B3_LINE_START_X=$(($R % 20)) #B3_LINE_STOP_X=$((100 + $R % 40)) get_random ROLL_X=$(($R % 40)) convert -size 180x60 xc:none -pointsize 40 \ \( -clone 0 -fill white \ -stroke black -strokewidth 4 -annotate +0+40 "$INPUT" \ -stroke white -strokewidth 2 -annotate +0+40 "$INPUT" \ -roll +$ROLL_X+0 \ -wave "$WAVE1_AMPLITUDE"x"$WAVE1_LENGTH" \ -roll -$ROLL_X+0 \) \ \( -clone 0 -stroke black \ -strokewidth 1 -draw \ "line $B1_LINE_START_X,$B1_LINE_START_Y $B1_LINE_STOP_X,$B1_LINE_STOP_Y" \ -strokewidth 1 -draw \ "line $B2_LINE_START_X,$B2_LINE_START_Y $B2_LINE_STOP_X,$B2_LINE_STOP_Y" \ -wave "$WAVE2_AMPLITUDE"x"$WAVE2_LENGTH" \) \ \( -clone 0 -stroke white \ -strokewidth 2 -draw "line 0,$W1_LINE_START_Y 140,$W1_LINE_STOP_Y" \ -strokewidth 2 -draw "line 0,$W2_LINE_START_Y 140,$W2_LINE_STOP_Y" \ -strokewidth 2 -draw "line 0,$W3_LINE_START_Y 140,$W3_LINE_STOP_Y" \ -wave "$WAVE3_AMPLITUDE"x"$WAVE3_LENGTH" \) \ -flatten -crop 140x60 +repage -quality 90 -depth 8 png:- ejabberd-18.01/tools/update-deps-releases.pl0000755000232200023220000003572113225664356021367 0ustar debalancedebalance#!/usr/bin/perl use v5.10; use strict; use warnings; use File::Slurp qw(slurp write_file); use File::stat; use File::Touch; use Data::Dumper qw(Dumper); use Carp; use Term::ANSIColor; use Term::ReadKey; use List::Util qw(first); use Clone qw(clone); sub get_deps { my ($config, %fdeps) = @_; my %deps; return { } unless $config =~ /\{\s*deps\s*,\s*\[(.*?)\]/s; my $sdeps = $1; while ($sdeps =~ /\{\s* (\w+) \s*,\s* ".*?" \s*,\s* \{\s*git \s*,\s* "(.*?)" \s*,\s* (?: (?:{\s*tag \s*,\s* "(.*?)") | "(.*?)" | ( \{ (?: (?-1) | [^{}]+ )+ \} ) )/sgx) { next unless not %fdeps or exists $fdeps{$1}; $deps{$1} = { repo => $2, commit => $3 || $4 }; } return \%deps; } my (%info_updates, %top_deps_updates, %sub_deps_updates, @operations); my $epoch = 1; sub top_deps { state %deps; state $my_epoch = $epoch; if (not %deps or $my_epoch != $epoch) { $my_epoch = $epoch; my $config = slurp "rebar.config"; croak "Unable to extract floating_deps" unless $config =~ /\{floating_deps, \[(.*?)\]/s; my $fdeps = $1; $fdeps =~ s/\s*//g; my %fdeps = map { $_ => 1 } split /,/, $fdeps; %deps = %{get_deps($config, %fdeps)}; } return {%deps, %top_deps_updates}; } sub update_deps_repos { my ($force) = @_; my $deps = top_deps(); $epoch++; mkdir(".deps-update") unless -d ".deps-update"; for my $dep (keys %{$deps}) { my $dd = ".deps-update/$dep"; if (not -d $dd) { say "Downloading $dep..."; my $repo = $deps->{$dep}->{repo}; $repo =~ s!^https?://github.com/!git\@github.com:!; system("git", "-C", ".deps-update", "clone", $repo); } elsif (time() - stat($dd)->mtime > 24 * 60 * 60 or $force) { say "Updating $dep..."; system("git", "-C", $dd, "pull"); touch($dd) } } } sub sub_deps { state %sub_deps; state $my_epoch = $epoch; if (not %sub_deps or $my_epoch != $epoch) { $my_epoch = $epoch; my $deps = top_deps(); for my $dep (keys %{$deps}) { my $rc = ".deps-update/$dep/rebar.config"; $sub_deps{$dep} = { }; next unless -f $rc; $sub_deps{$dep} = get_deps(scalar(slurp($rc))); } } return {%sub_deps, %sub_deps_updates}; } sub rev_deps_helper { my ($rev_deps, $dep) = @_; if (not exists $rev_deps->{$dep}->{indirect}) { my %deps = %{$rev_deps->{$dep}->{direct} || {}}; for (keys %{$rev_deps->{$dep}->{direct}}) { %deps = (%deps, %{rev_deps_helper($rev_deps, $_)}); } $rev_deps->{$dep}->{indirect} = \%deps; } return $rev_deps->{$dep}->{indirect}; } sub rev_deps { state %rev_deps; state $deps_epoch = $epoch; if (not %rev_deps or $deps_epoch != $epoch) { $deps_epoch = $epoch; my $sub_deps = sub_deps(); for my $dep (keys %$sub_deps) { $rev_deps{$_}->{direct}->{$dep} = 1 for keys %{$sub_deps->{$dep}}; } for my $dep (keys %$sub_deps) { $rev_deps{$dep}->{indirect} = rev_deps_helper(\%rev_deps, $dep); } } return \%rev_deps; } sub update_changelog { my ($dep, $version, @reasons) = @_; my $cl = ".deps-update/$dep/CHANGELOG.md"; return if not -f $cl; my $reason = join "\n", map {"* $_"} @reasons; my $content = slurp($cl); if (not $content =~ /^# Version $version/) { $content = "# Version $version\n\n$reason\n\n$content"; } else { $content =~ s/(# Version $version\n\n)/$1$reason\n/; } write_file($cl, $content); } sub edit_changelog { my ($dep, $version) = @_; my $cl = ".deps-update/$dep/CHANGELOG.md"; return if not -f $cl; my $top_deps = top_deps(); my $git_info = deps_git_info(); say color("red"), "$dep", color("reset"), " ($top_deps->{$dep}->{commit}):"; say " $_" for @{$git_info->{$dep}->{new_commits}}; say ""; my $content = slurp($cl); my $old_content = $content; if (not $content =~ /^# Version $version/) { $content = "# Version $version\n\n* \n\n$content"; } else { $content =~ s/(# Version $version\n\n)/$1* \n/; } write_file($cl, $content); system("$ENV{EDITOR} $cl"); my $new_content = slurp($cl); if ($new_content eq $content) { write_file($cl, $old_content); } else { system("git", "-C", ".deps-update/$dep", "commit", "-a", "-m", "Update changelog"); } } sub update_app_src { my ($dep, $version) = @_; my $app = ".deps-update/$dep/src/$dep.app.src"; return if not -f $app; my $content = slurp($app); $content =~ s/({\s*vsn\s*,\s*)".*"/$1"$version"/; write_file($app, $content); } sub update_deps_versions { my ($config_path, %deps) = @_; my $config = slurp $config_path; for (keys %deps) { $config =~ s/(\{\s*$_\s*,\s*".*?"\s*,\s*\{\s*git\s*,\s*".*?"\s*,\s*)(?:{\s*tag\s*,\s*"(.*?)"\s*}|"(.*?)")/$1\{tag, "$deps{$_}"}/s; } write_file($config_path, $config); } sub cmp_ver { my @a = split /(\d+)/, $a; my @b = split /(\d+)/, $b; my $is_num = 1; return - 1 if $#a == 0; return 1 if $#b == 0; while (1) { my $ap = shift @a; my $bp = shift @b; $is_num = 1 - $is_num; if (defined $ap) { if (defined $bp) { if ($is_num) { next if $ap == $bp; return 1 if $ap > $bp; return - 1; } else { next if $ap eq $bp; return 1 if $ap gt $bp; return - 1; } } else { return 1; } } elsif (defined $bp) { return - 1; } else { return 0; } } } sub deps_git_info { state %info; state $my_epoch = $epoch; if (not %info or $my_epoch != $epoch) { $my_epoch = $epoch; my $deps = top_deps(); for my $dep (keys %{$deps}) { my $dir = ".deps-update/$dep"; my @tags = `git -C "$dir" tag`; chomp(@tags); @tags = sort cmp_ver @tags; my $last_tag = $tags[$#tags]; my @new = `git -C $dir log --oneline $last_tag..origin/master`; my $new_tag = $last_tag; $new_tag =~ s/(\d+)$/$1+1/e; chomp(@new); $info{$dep} = { last_tag => $last_tag, new_commits => \@new, new_tag => $new_tag }; } } return { %info, %info_updates }; } sub show_commands { my %commands = @_; my @keys; while (@_) { push @keys, shift; shift; } for (@keys) { say color("red"), $_, color("reset"), ") $commands{$_}"; } ReadMode(4); while (1) { my $key = ReadKey(0); if (defined $commands{uc($key)}) { ReadMode(0); say ""; return uc($key); } } } sub schedule_operation { my ($type, $dep, $tag, $reason, $op) = @_; my $idx = first { $operations[$_]->{dep} eq $dep } 0..$#operations; if (defined $idx) { my $mop = $operations[$idx]; if (defined $op) { my $oidx = first { $mop->{operations}->[$_]->[0] eq $op->[0] } 0..$#{$mop->{operations}}; if (defined $oidx) { $mop->{reasons}->[$oidx] = $reason; $mop->{operations}->[$oidx] = $op; } else { push @{$mop->{reasons}}, $reason; push @{$mop->{operations}}, $op; } } return if $type eq "update"; $mop->{type} = $type; $info_updates{$dep}->{new_commits} = []; return; } my $info = deps_git_info(); $top_deps_updates{$dep} = {commit => $tag}; $info_updates{$dep} = {last_tag => $tag, new_tag => $tag, new_commits => $type eq "tupdate" ? [] : $info->{$dep}->{new_commits}}; my $rev_deps = rev_deps(); @operations = sort { exists $rev_deps->{$a->{dep}}->{indirect}->{$b->{dep}} ? -1 : exists $rev_deps->{$b->{dep}}->{indirect}->{$a->{dep}} ? 1 : $a->{dep} cmp $b->{dep} } (@operations, { type => $type, dep => $dep, version => $tag, reasons => ($reason ? [$reason] : []), operations => ($op ? [$op] : [])} ); my $sub_deps = sub_deps(); for (keys %{$rev_deps->{$dep}->{direct}}) { schedule_operation("update", $_, $info->{$_}->{new_tag}, "Updating $dep to version $tag.", [$dep, $tag]); $sub_deps_updates{$_} = $sub_deps_updates{$_} || clone($sub_deps->{$_}); $sub_deps_updates{$_}->{$dep}->{commit} = $tag; } } sub git_tag { my ($dep, $ver, $msg) = @_; system("git", "-C", ".deps-update/$dep", "commit", "-a", "-m", $msg); system("git", "-C", ".deps-update/$dep", "tag", $ver); } sub git_push { my ($dep) = @_; system("git", "-C", ".deps-update/$dep", "push"); system("git", "-C", ".deps-update/$dep", "push", "--tags"); } update_deps_repos(); MAIN: while (1) { my $top_deps = top_deps(); my $git_info = deps_git_info(); print color("bold blue"), "Dependences with newer tags:\n", color("reset"); my $old_deps = 0; for my $dep (sort keys %$top_deps) { next unless $git_info->{$dep}->{last_tag} ne $top_deps->{$dep}->{commit}; say color("red"), "$dep", color("reset"), ": $top_deps->{$dep}->{commit} -> $git_info->{$dep}->{last_tag}"; $old_deps = 1; } say "(none)" if not $old_deps; say ""; print color("bold blue"), "Dependences that have commits after last tags:\n", color("reset"); my $changed_deps = 0; for my $dep (sort keys %$top_deps) { next unless @{$git_info->{$dep}->{new_commits}}; say color("red"), "$dep", color("reset"), " ($top_deps->{$dep}->{commit}):"; say " $_" for @{$git_info->{$dep}->{new_commits}}; $changed_deps = 1; } say "(none)" if not $changed_deps; say ""; my $cmd = show_commands($old_deps ? (U => "Update dependency") : (), $changed_deps ? (T => "Tag new release") : (), @operations ? (A => "Apply changes") : (), R => "Refresh repositiories", E => "Exit"); last if $cmd eq "E"; if ($cmd eq "U") { while (1) { my @deps_to_update; my @od; my $idx = 1; for my $dep (sort keys %$top_deps) { next unless $git_info->{$dep}->{last_tag} ne $top_deps->{$dep}->{commit}; $od[$idx] = $dep; push @deps_to_update, $idx++, "Update $dep to $git_info->{$dep}->{last_tag}"; } last if $idx == 1; my $cmd = show_commands(@deps_to_update, E => "Exit"); last if $cmd eq "E"; my $dep = $od[$cmd]; schedule_operation("update", $dep, $git_info->{$dep}->{last_tag}); $top_deps = top_deps(); $git_info = deps_git_info(); } } if ($cmd eq "R") { update_deps_repos(1); } if ($cmd eq "T") { while (1) { my @deps_to_tag; my @od; my $idx = 1; for my $dep (sort keys %$top_deps) { next unless @{$git_info->{$dep}->{new_commits}}; $od[$idx] = $dep; push @deps_to_tag, $idx++, "Tag $dep with version $git_info->{$dep}->{new_tag}"; } last if $idx == 1; my $cmd = show_commands(@deps_to_tag, E => "Exit"); last if $cmd eq "E"; my $dep = $od[$cmd]; my $d = $git_info->{$dep}; schedule_operation("tupdate", $dep, $d->{new_tag}); $top_deps = top_deps(); $git_info = deps_git_info(); } } my $changelog_updated = 0; if ($cmd eq "A") { APPLY: { $top_deps = top_deps(); $git_info = deps_git_info(); my $sub_deps = sub_deps(); for my $dep (keys %$top_deps) { for my $sdep (keys %{$sub_deps->{$dep}}) { next if not defined $top_deps->{$sdep} or $sub_deps->{$dep}->{$sdep}->{commit} eq $top_deps->{$sdep}->{commit}; say "$dep $sdep ", $sub_deps->{$dep}->{$sdep}->{commit}, " <=> $sdep ", $top_deps->{$sdep}->{commit}; schedule_operation("update", $dep, $git_info->{$dep}->{new_tag}, "Updating $sdep to version $top_deps->{$sdep}->{commit}.", [ $sdep, $top_deps->{$sdep}->{commit} ]); } } %info_updates = (); %top_deps_updates = (); %sub_deps_updates = (); $top_deps = top_deps(); $git_info = deps_git_info(); $sub_deps = sub_deps(); print color("bold blue"), "List of operations:\n", color("reset"); for my $op (@operations) { print color("red"), $op->{dep}, color("reset"), " ($top_deps->{$op->{dep}}->{commit} -> $op->{version})"; if (@{$op->{operations}}) { say ":"; say " $_->[0] -> $_->[1]" for @{$op->{operations}}; } else { say ""; } } say ""; my %to_tag; if (not $changelog_updated) { for my $op (@operations) { if ($git_info->{$op->{dep}}->{last_tag} ne $op->{version}) { $to_tag{$op->{dep}} = $op->{version}; } } } my $cmd = show_commands(A => "Apply", (%to_tag ? (U => "Update Changelogs") : ()), E => "Exit"); if ($cmd eq "U") { for my $dep (keys %to_tag) { edit_changelog($dep, $to_tag{$dep}); } redo APPLY; } elsif ($cmd eq "A") { my %top_changes; for my $op (@operations) { update_changelog($op->{dep}, $op->{version}, @{$op->{reasons}}) if @{$op->{reasons}}; update_deps_versions(".deps-update/$op->{dep}/rebar.config", map {@{$_}[0,1] } @{$op->{operations}}) if @{$op->{operations}}; if ($git_info->{$op->{dep}}->{last_tag} ne $op->{version}) { update_app_src($op->{dep}, $op->{version}); git_tag($op->{dep}, $op->{version}, "Release $op->{version}"); } $top_changes{$op->{dep}} = $op->{version}; } update_deps_versions("rebar.config", %top_changes); for my $op (@operations) { if ($git_info->{$op->{dep}}->{last_tag} ne $op->{version}) { git_push($op->{dep}); } } last MAIN; } } } } ejabberd-18.01/sql/0000755000232200023220000000000013225664356014442 5ustar debalancedebalanceejabberd-18.01/sql/lite.sql0000644000232200023220000002634013225664356016125 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- CREATE TABLE users ( username text PRIMARY KEY, password text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE last ( username text PRIMARY KEY, seconds text NOT NULL, state text NOT NULL ); CREATE TABLE rosterusers ( username text NOT NULL, jid text NOT NULL, nick text NOT NULL, subscription character(1) NOT NULL, ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, subscribe text NOT NULL, type text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers (username, jid); CREATE INDEX i_rosteru_username ON rosterusers (username); CREATE INDEX i_rosteru_jid ON rosterusers (jid); CREATE TABLE rostergroups ( username text NOT NULL, jid text NOT NULL, grp text NOT NULL ); CREATE INDEX pk_rosterg_user_jid ON rostergroups (username, jid); CREATE TABLE sr_group ( name text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE sr_user ( jid text NOT NULL, grp text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user (jid, grp); CREATE INDEX i_sr_user_jid ON sr_user (jid); CREATE INDEX i_sr_user_grp ON sr_user (grp); CREATE TABLE spool ( username text NOT NULL, xml text NOT NULL, seq INTEGER PRIMARY KEY AUTOINCREMENT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_despool ON spool (username); CREATE TABLE archive ( username text NOT NULL, timestamp BIGINT UNSIGNED NOT NULL, peer text NOT NULL, bare_peer text NOT NULL, xml text NOT NULL, txt text, id INTEGER PRIMARY KEY AUTOINCREMENT, kind text, nick text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_username_timestamp ON archive(username, timestamp); CREATE INDEX i_timestamp ON archive(timestamp); CREATE INDEX i_peer ON archive(peer); CREATE INDEX i_bare_peer ON archive(bare_peer); CREATE TABLE archive_prefs ( username text NOT NULL PRIMARY KEY, def text NOT NULL, always text NOT NULL, never text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE vcard ( username text PRIMARY KEY, vcard text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE vcard_search ( username text NOT NULL, lusername text PRIMARY KEY, fn text NOT NULL, lfn text NOT NULL, family text NOT NULL, lfamily text NOT NULL, given text NOT NULL, lgiven text NOT NULL, middle text NOT NULL, lmiddle text NOT NULL, nickname text NOT NULL, lnickname text NOT NULL, bday text NOT NULL, lbday text NOT NULL, ctry text NOT NULL, lctry text NOT NULL, locality text NOT NULL, llocality text NOT NULL, email text NOT NULL, lemail text NOT NULL, orgname text NOT NULL, lorgname text NOT NULL, orgunit text NOT NULL, lorgunit text NOT NULL ); CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn); CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily); CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven); CREATE INDEX i_vcard_search_lmiddle ON vcard_search(lmiddle); CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname); CREATE INDEX i_vcard_search_lbday ON vcard_search(lbday); CREATE INDEX i_vcard_search_lctry ON vcard_search(lctry); CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality); CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail); CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname); CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit); CREATE TABLE privacy_default_list ( username text PRIMARY KEY, name text NOT NULL ); CREATE TABLE privacy_list ( username text NOT NULL, name text NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_privacy_list_username ON privacy_list (username); CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list (username, name); CREATE TABLE privacy_list_data ( id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, t character(1) NOT NULL, value text NOT NULL, action character(1) NOT NULL, ord NUMERIC NOT NULL, match_all boolean NOT NULL, match_iq boolean NOT NULL, match_message boolean NOT NULL, match_presence_in boolean NOT NULL, match_presence_out boolean NOT NULL ); CREATE TABLE private_storage ( username text NOT NULL, namespace text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_private_storage_username ON private_storage (username); CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage (username, namespace); CREATE TABLE roster_version ( username text PRIMARY KEY, version text NOT NULL ); CREATE TABLE pubsub_node ( host text NOT NULL, node text NOT NULL, parent text NOT NULL DEFAULT '', plugin text NOT NULL, nodeid INTEGER PRIMARY KEY AUTOINCREMENT ); CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node); CREATE TABLE pubsub_node_option ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, name text NOT NULL, val text NOT NULL ); CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid); CREATE TABLE pubsub_node_owner ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, owner text NOT NULL ); CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid); CREATE TABLE pubsub_state ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, jid text NOT NULL, affiliation character(1), subscriptions text NOT NULL DEFAULT '', stateid INTEGER PRIMARY KEY AUTOINCREMENT ); CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid); CREATE TABLE pubsub_item ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, itemid text NOT NULL, publisher text NOT NULL, creation text NOT NULL, modification text NOT NULL, payload text NOT NULL DEFAULT '' ); CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid); CREATE TABLE pubsub_subscription_opt ( subid text NOT NULL, opt_name varchar(32), opt_value text NOT NULL ); CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt (subid, opt_name); CREATE TABLE muc_room ( name text NOT NULL, host text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host); CREATE TABLE muc_registered ( jid text NOT NULL, host text NOT NULL, nick text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_muc_registered_nick ON muc_registered (nick); CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host); CREATE TABLE muc_online_room ( name text NOT NULL, host text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name, host); CREATE TABLE muc_online_users ( username text NOT NULL, server text NOT NULL, resource text NOT NULL, name text NOT NULL, host text NOT NULL, node text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host); CREATE INDEX i_muc_online_users_us ON muc_online_users (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, host text NOT NULL, jid text NOT NULL, nick text NOT NULL, nodes text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); CREATE TABLE irc_custom ( jid text NOT NULL, host text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host); CREATE TABLE motd ( username text PRIMARY KEY, xml text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE caps_features ( node text NOT NULL, subnode text NOT NULL, feature text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode); CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, node text NOT NULL, username text NOT NULL, resource text NOT NULL, priority text NOT NULL, info text NOT NULL ); CREATE UNIQUE INDEX i_sm_sid ON sm(usec, pid); CREATE INDEX i_sm_node ON sm(node); CREATE INDEX i_sm_username ON sm(username); CREATE TABLE oauth_token ( token text NOT NULL PRIMARY KEY, jid text NOT NULL, scope text NOT NULL, expire bigint NOT NULL ); CREATE TABLE route ( domain text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL, local_hint text NOT NULL ); CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid); CREATE INDEX i_route_domain ON route(domain); CREATE TABLE bosh ( sid text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid); CREATE TABLE carboncopy ( username text NOT NULL, resource text NOT NULL, namespace text NOT NULL, node text NOT NULL ); CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username, resource); CREATE INDEX i_carboncopy_user ON carboncopy (username); CREATE TABLE proxy65 ( sid text NOT NULL, pid_t text NOT NULL, pid_i text NOT NULL, node_t text NOT NULL, node_i text NOT NULL, jid_i text NOT NULL ); CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid); CREATE INDEX i_proxy65_jid ON proxy65 (jid_i); CREATE TABLE push_session ( username text NOT NULL, timestamp bigint NOT NULL, service text NOT NULL, node text NOT NULL, xml text NOT NULL ); CREATE UNIQUE INDEX i_push_usn ON push_session (username, service, node); CREATE UNIQUE INDEX i_push_ut ON push_session (username, timestamp); ejabberd-18.01/sql/pg.new.sql0000644000232200023220000005334013225664356016366 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- -- To update from the old schema, replace with the host's domain: -- ALTER TABLE users ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE users DROP CONSTRAINT users_pkey; -- ALTER TABLE users ADD PRIMARY KEY (server_host, username); -- ALTER TABLE users ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE last ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE last DROP CONSTRAINT last_pkey; -- ALTER TABLE last ADD PRIMARY KEY (server_host, username); -- ALTER TABLE last ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE rosterusers ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_rosteru_user_jid; -- DROP INDEX i_rosteru_username; -- DROP INDEX i_rosteru_jid; -- CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (server_host, username, jid); -- CREATE INDEX i_rosteru_sh_username ON rosterusers USING btree (server_host, username); -- CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid); -- ALTER TABLE rosterusers ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE rostergroups ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX pk_rosterg_user_jid; -- CREATE INDEX i_rosterg_sh_user_jid ON rostergroups USING btree (server_host, username, jid); -- ALTER TABLE rostergroups ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE sr_group ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE sr_group ADD PRIMARY KEY (server_host, name); -- ALTER TABLE sr_group ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE sr_user ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_sr_user_jid_grp; -- DROP INDEX i_sr_user_jid; -- DROP INDEX i_sr_user_grp; -- ALTER TABLE sr_user ADD PRIMARY KEY (server_host, jid, grp); -- CREATE INDEX i_sr_user_sh_jid ON sr_user USING btree (server_host, jid); -- CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp); -- ALTER TABLE sr_user ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE spool ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_despool; -- CREATE INDEX i_spool_sh_username ON spool USING btree (server_host, username); -- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_username; -- DROP INDEX i_username_timestamp; -- DROP INDEX i_timestamp; -- DROP INDEX i_peer; -- DROP INDEX i_bare_peer; -- CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp); -- CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp); -- CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer); -- CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer); -- ALTER TABLE archive ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE archive_prefs ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE archive_prefs DROP CONSTRAINT archive_prefs_pkey; -- ALTER TABLE archive_prefs ADD PRIMARY KEY (server_host, username); -- ALTER TABLE archive_prefs ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE vcard ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE vcard DROP CONSTRAINT vcard_pkey; -- ALTER TABLE vcard ADD PRIMARY KEY (server_host, username); -- ALTER TABLE vcard ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE vcard_search ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE vcard_search DROP CONSTRAINT vcard_search_pkey; -- DROP INDEX i_vcard_search_lfn; -- DROP INDEX i_vcard_search_lfamily; -- DROP INDEX i_vcard_search_lgiven; -- DROP INDEX i_vcard_search_lmiddle; -- DROP INDEX i_vcard_search_lnickname; -- DROP INDEX i_vcard_search_lbday; -- DROP INDEX i_vcard_search_lctry; -- DROP INDEX i_vcard_search_llocality; -- DROP INDEX i_vcard_search_lemail; -- DROP INDEX i_vcard_search_lorgname; -- DROP INDEX i_vcard_search_lorgunit; -- ALTER TABLE vcard_search ADD PRIMARY KEY (server_host, username); -- CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); -- CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); -- CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); -- CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host, lmiddle); -- CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, lnickname); -- CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host, lbday); -- CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host, lctry); -- CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, llocality); -- CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host, lemail); -- CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host, lorgname); -- CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host, lorgunit); -- ALTER TABLE vcard_search ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE privacy_default_list ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE privacy_default_list DROP CONSTRAINT privacy_default_list_pkey; -- ALTER TABLE privacy_default_list ADD PRIMARY KEY (server_host, username); -- ALTER TABLE privacy_default_list ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE privacy_list ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_privacy_list_username; -- DROP INDEX i_privacy_list_username_name; -- CREATE INDEX i_privacy_list_sh_username ON privacy_list USING btree (server_host, username); -- CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USING btree (server_host, username, name); -- ALTER TABLE privacy_list ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE private_storage ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_private_storage_username; -- DROP INDEX i_private_storage_username_namespace; -- ALTER TABLE private_storage ADD PRIMARY KEY (server_host, username, namespace); -- CREATE INDEX i_private_storage_sh_username ON private_storage USING btree (server_host, username); -- ALTER TABLE private_storage ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE roster_version ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE roster_version DROP CONSTRAINT roster_version_pkey; -- ALTER TABLE roster_version ADD PRIMARY KEY (server_host, username); -- ALTER TABLE roster_version ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE muc_room ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE muc_room ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE muc_registered ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE muc_registered ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE muc_online_room ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE muc_online_room ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE muc_online_users ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE muc_online_users ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE irc_custom ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE irc_custom ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE motd ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE motd DROP CONSTRAINT motd_pkey; -- ALTER TABLE motd ADD PRIMARY KEY (server_host, username); -- ALTER TABLE motd ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE sm ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_sm_sid; -- DROP INDEX i_sm_username; -- ALTER TABLE sm ADD PRIMARY KEY (usec, pid); -- CREATE INDEX i_sm_sh_username ON sm USING btree (server_host, username); -- ALTER TABLE sm ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE carboncopy ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_carboncopy_ur; -- DROP INDEX i_carboncopy_user; -- ALTER TABLE carboncopy ADD PRIMARY KEY (server_host, username, resource); -- CREATE INDEX i_carboncopy_sh_user ON carboncopy USING btree (server_host, username); -- ALTER TABLE carboncopy ALTER COLUMN server_host DROP DEFAULT; CREATE TABLE users ( username text NOT NULL, server_host text NOT NULL, "password" text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, username) ); -- Add support for SCRAM auth to a database created before ejabberd 16.03: -- ALTER TABLE users ADD COLUMN serverkey text NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN salt text NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0; CREATE TABLE last ( username text NOT NULL, server_host text NOT NULL, seconds text NOT NULL, state text NOT NULL, PRIMARY KEY (server_host, username) ); CREATE TABLE rosterusers ( username text NOT NULL, server_host text NOT NULL, jid text NOT NULL, nick text NOT NULL, subscription character(1) NOT NULL, ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, subscribe text NOT NULL, "type" text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (server_host, username, jid); CREATE INDEX i_rosteru_sh_username ON rosterusers USING btree (server_host, username); CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid); CREATE TABLE rostergroups ( username text NOT NULL, server_host text NOT NULL, jid text NOT NULL, grp text NOT NULL ); CREATE INDEX i_rosterg_sh_user_jid ON rostergroups USING btree (server_host, username, jid); CREATE TABLE sr_group ( name text NOT NULL, server_host text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, name) ); CREATE TABLE sr_user ( jid text NOT NULL, server_host text NOT NULL, grp text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, jid, grp) ); CREATE INDEX i_sr_user_sh_jid ON sr_user USING btree (server_host, jid); CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp); CREATE TABLE spool ( username text NOT NULL, server_host text NOT NULL, xml text NOT NULL, seq SERIAL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_spool_sh_username ON spool USING btree (server_host, username); CREATE TABLE archive ( username text NOT NULL, server_host text NOT NULL, timestamp BIGINT NOT NULL, peer text NOT NULL, bare_peer text NOT NULL, xml text NOT NULL, txt text, id SERIAL, kind text, nick text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp); CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp); CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer); CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer); CREATE TABLE archive_prefs ( username text NOT NULL, server_host text NOT NULL, def text NOT NULL, always text NOT NULL, never text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, username) ); CREATE TABLE vcard ( username text NOT NULL, server_host text NOT NULL, vcard text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, username) ); CREATE TABLE vcard_search ( username text NOT NULL, lusername text NOT NULL, server_host text NOT NULL, fn text NOT NULL, lfn text NOT NULL, family text NOT NULL, lfamily text NOT NULL, given text NOT NULL, lgiven text NOT NULL, middle text NOT NULL, lmiddle text NOT NULL, nickname text NOT NULL, lnickname text NOT NULL, bday text NOT NULL, lbday text NOT NULL, ctry text NOT NULL, lctry text NOT NULL, locality text NOT NULL, llocality text NOT NULL, email text NOT NULL, lemail text NOT NULL, orgname text NOT NULL, lorgname text NOT NULL, orgunit text NOT NULL, lorgunit text NOT NULL, PRIMARY KEY (server_host, username) ); CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host, lmiddle); CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, lnickname); CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host, lbday); CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host, lctry); CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, llocality); CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host, lemail); CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host, lorgname); CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host, lorgunit); CREATE TABLE privacy_default_list ( username text NOT NULL, server_host text NOT NULL, name text NOT NULL, PRIMARY KEY (server_host, username) ); CREATE TABLE privacy_list ( username text NOT NULL, server_host text NOT NULL, name text NOT NULL, id SERIAL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_privacy_list_sh_username ON privacy_list USING btree (server_host, username); CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USING btree (server_host, username, name); CREATE TABLE privacy_list_data ( id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, t character(1) NOT NULL, value text NOT NULL, action character(1) NOT NULL, ord NUMERIC NOT NULL, match_all boolean NOT NULL, match_iq boolean NOT NULL, match_message boolean NOT NULL, match_presence_in boolean NOT NULL, match_presence_out boolean NOT NULL ); CREATE INDEX i_privacy_list_data_id ON privacy_list_data USING btree (id); CREATE TABLE private_storage ( username text NOT NULL, server_host text NOT NULL, namespace text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, username, namespace) ); CREATE INDEX i_private_storage_sh_username ON private_storage USING btree (server_host, username); CREATE TABLE roster_version ( username text NOT NULL, server_host text NOT NULL, version text NOT NULL, PRIMARY KEY (server_host, username) ); -- To update from 0.9.8: -- CREATE SEQUENCE spool_seq_seq; -- ALTER TABLE spool ADD COLUMN seq integer; -- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq'); -- UPDATE spool SET seq = DEFAULT; -- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL; -- To update from 1.x: -- ALTER TABLE rosterusers ADD COLUMN askmessage text; -- UPDATE rosterusers SET askmessage = ''; -- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; CREATE TABLE pubsub_node ( host text NOT NULL, node text NOT NULL, parent text NOT NULL DEFAULT '', plugin text NOT NULL, nodeid SERIAL UNIQUE ); CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node); CREATE TABLE pubsub_node_option ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, name text NOT NULL, val text NOT NULL ); CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btree (nodeid); CREATE TABLE pubsub_node_owner ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, owner text NOT NULL ); CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree (nodeid); CREATE TABLE pubsub_state ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, jid text NOT NULL, affiliation character(1), subscriptions text NOT NULL DEFAULT '', stateid SERIAL UNIQUE ); CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, jid); CREATE TABLE pubsub_item ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, itemid text NOT NULL, publisher text NOT NULL, creation text NOT NULL, modification text NOT NULL, payload text NOT NULL DEFAULT '' ); CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (nodeid, itemid); CREATE TABLE pubsub_subscription_opt ( subid text NOT NULL, opt_name varchar(32), opt_value text NOT NULL ); CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt USING btree (subid, opt_name); CREATE TABLE muc_room ( name text NOT NULL, host text NOT NULL, server_host text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room USING btree (name, host); CREATE TABLE muc_registered ( jid text NOT NULL, host text NOT NULL, server_host text NOT NULL, nick text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick); CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING btree (jid, host); CREATE TABLE muc_online_room ( name text NOT NULL, host text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING btree (name, host); CREATE TABLE muc_online_users ( username text NOT NULL, server text NOT NULL, resource text NOT NULL, name text NOT NULL, host text NOT NULL, server_host text NOT NULL, node text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host); CREATE INDEX i_muc_online_users_us ON muc_online_users USING btree (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, host text NOT NULL, jid text NOT NULL, nick text NOT NULL, nodes text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); CREATE TABLE irc_custom ( jid text NOT NULL, host text NOT NULL, server_host text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host); CREATE TABLE motd ( username text NOT NULL, server_host text NOT NULL, xml text, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (server_host, username) ); CREATE TABLE caps_features ( node text NOT NULL, subnode text NOT NULL, feature text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_caps_features_node_subnode ON caps_features USING btree (node, subnode); CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, node text NOT NULL, username text NOT NULL, server_host text NOT NULL, resource text NOT NULL, priority text NOT NULL, info text NOT NULL, PRIMARY KEY (usec, pid) ); CREATE INDEX i_sm_node ON sm USING btree (node); CREATE INDEX i_sm_sh_username ON sm USING btree (server_host, username); CREATE TABLE oauth_token ( token text NOT NULL, jid text NOT NULL, scope text NOT NULL, expire bigint NOT NULL ); CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token); CREATE TABLE route ( domain text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL, local_hint text NOT NULL ); CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); CREATE INDEX i_route_domain ON route USING btree (domain); CREATE TABLE bosh ( sid text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid); CREATE TABLE carboncopy ( username text NOT NULL, server_host text NOT NULL, resource text NOT NULL, namespace text NOT NULL, node text NOT NULL, PRIMARY KEY (server_host, username, resource) ); CREATE INDEX i_carboncopy_sh_user ON carboncopy USING btree (server_host, username); CREATE TABLE proxy65 ( sid text NOT NULL, pid_t text NOT NULL, pid_i text NOT NULL, node_t text NOT NULL, node_i text NOT NULL, jid_i text NOT NULL ); CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid); CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i); CREATE TABLE push_session ( username text NOT NULL, server_host text NOT NULL, timestamp bigint NOT NULL, service text NOT NULL, node text NOT NULL, xml text NOT NULL, PRIMARY KEY (server_host, username, timestamp) ); CREATE UNIQUE INDEX i_push_session_susn ON push_session USING btree (server_host, username, service, node); ejabberd-18.01/sql/mysql.sql0000644000232200023220000003612713225664356016341 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- CREATE TABLE users ( username varchar(191) PRIMARY KEY, password text NOT NULL, serverkey varchar(64) NOT NULL DEFAULT '', salt varchar(64) NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Add support for SCRAM auth to a database created before ejabberd 16.03: -- ALTER TABLE users ADD COLUMN serverkey varchar(64) NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN salt varchar(64) NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0; CREATE TABLE last ( username varchar(191) PRIMARY KEY, seconds text NOT NULL, state text NOT NULl ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE rosterusers ( username varchar(191) NOT NULL, jid varchar(191) NOT NULL, nick text NOT NULL, subscription character(1) NOT NULL, ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, subscribe text NOT NULL, type text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers(username(75), jid(75)); CREATE INDEX i_rosteru_username ON rosterusers(username); CREATE INDEX i_rosteru_jid ON rosterusers(jid); CREATE TABLE rostergroups ( username varchar(191) NOT NULL, jid varchar(191) NOT NULL, grp text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX pk_rosterg_user_jid ON rostergroups(username(75), jid(75)); CREATE TABLE sr_group ( name varchar(191) NOT NULL, opts text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE sr_user ( jid varchar(191) NOT NULL, grp varchar(191) NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_sr_user_jid_group ON sr_user(jid(75), grp(75)); CREATE INDEX i_sr_user_jid ON sr_user(jid); CREATE INDEX i_sr_user_grp ON sr_user(grp); CREATE TABLE spool ( username varchar(191) NOT NULL, xml BLOB NOT NULL, seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_despool USING BTREE ON spool(username); CREATE INDEX i_spool_created_at USING BTREE ON spool(created_at); CREATE TABLE archive ( username varchar(191) NOT NULL, timestamp BIGINT UNSIGNED NOT NULL, peer varchar(191) NOT NULL, bare_peer varchar(191) NOT NULL, xml text NOT NULL, txt text, id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, kind varchar(10), nick varchar(191), created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE FULLTEXT INDEX i_text ON archive(txt); CREATE INDEX i_username_timestamp USING BTREE ON archive(username,timestamp); CREATE INDEX i_timestamp USING BTREE ON archive(timestamp); CREATE INDEX i_peer USING BTREE ON archive(peer); CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer); CREATE TABLE archive_prefs ( username varchar(191) NOT NULL PRIMARY KEY, def text NOT NULL, always text NOT NULL, never text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE vcard ( username varchar(191) PRIMARY KEY, vcard mediumtext NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE vcard_search ( username varchar(191) NOT NULL, lusername varchar(191) PRIMARY KEY, fn text NOT NULL, lfn varchar(191) NOT NULL, family text NOT NULL, lfamily varchar(191) NOT NULL, given text NOT NULL, lgiven varchar(191) NOT NULL, middle text NOT NULL, lmiddle varchar(191) NOT NULL, nickname text NOT NULL, lnickname varchar(191) NOT NULL, bday text NOT NULL, lbday varchar(191) NOT NULL, ctry text NOT NULL, lctry varchar(191) NOT NULL, locality text NOT NULL, llocality varchar(191) NOT NULL, email text NOT NULL, lemail varchar(191) NOT NULL, orgname text NOT NULL, lorgname varchar(191) NOT NULL, orgunit text NOT NULL, lorgunit varchar(191) NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn); CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily); CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven); CREATE INDEX i_vcard_search_lmiddle ON vcard_search(lmiddle); CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname); CREATE INDEX i_vcard_search_lbday ON vcard_search(lbday); CREATE INDEX i_vcard_search_lctry ON vcard_search(lctry); CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality); CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail); CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname); CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit); CREATE TABLE privacy_default_list ( username varchar(191) PRIMARY KEY, name varchar(191) NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE privacy_list ( username varchar(191) NOT NULL, name varchar(191) NOT NULL, id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_privacy_list_username USING BTREE ON privacy_list(username); CREATE UNIQUE INDEX i_privacy_list_username_name USING BTREE ON privacy_list (username(75), name(75)); CREATE TABLE privacy_list_data ( id bigint, t character(1) NOT NULL, value text NOT NULL, action character(1) NOT NULL, ord NUMERIC NOT NULL, match_all boolean NOT NULL, match_iq boolean NOT NULL, match_message boolean NOT NULL, match_presence_in boolean NOT NULL, match_presence_out boolean NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_privacy_list_data_id ON privacy_list_data(id); CREATE TABLE private_storage ( username varchar(191) NOT NULL, namespace varchar(191) NOT NULL, data text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username); CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_storage(username(75), namespace(75)); -- Not tested in mysql CREATE TABLE roster_version ( username varchar(191) PRIMARY KEY, version text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- To update from 1.x: -- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask; -- UPDATE rosterusers SET askmessage = ''; -- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; CREATE TABLE pubsub_node ( host text NOT NULL, node text NOT NULL, parent VARCHAR(191) NOT NULL DEFAULT '', plugin text NOT NULL, nodeid bigint auto_increment primary key ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120)); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(20), node(120)); CREATE TABLE pubsub_node_option ( nodeid bigint, name text NOT NULL, val text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid); ALTER TABLE `pubsub_node_option` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_node_owner ( nodeid bigint, owner text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid); ALTER TABLE `pubsub_node_owner` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_state ( nodeid bigint, jid text NOT NULL, affiliation character(1), subscriptions VARCHAR(191) NOT NULL DEFAULT '', stateid bigint auto_increment primary key ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60)); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state(nodeid, jid(60)); ALTER TABLE `pubsub_state` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_item ( nodeid bigint, itemid text NOT NULL, publisher text NOT NULL, creation text NOT NULL, modification text NOT NULL, payload text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36)); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36)); ALTER TABLE `pubsub_item` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_subscription_opt ( subid text NOT NULL, opt_name varchar(32), opt_value text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt(subid(32), opt_name(32)); CREATE TABLE muc_room ( name text NOT NULL, host text NOT NULL, opts mediumtext NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75), host(75)); CREATE TABLE muc_registered ( jid text NOT NULL, host text NOT NULL, nick text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75)); CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registered(jid(75), host(75)); CREATE TABLE muc_online_room ( name text NOT NULL, host text NOT NULL, node text NOT NULL, pid text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_online_room(name(75), host(75)); CREATE TABLE muc_online_users ( username text NOT NULL, server text NOT NULL, resource text NOT NULL, name text NOT NULL, host text NOT NULL, node text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75)); CREATE INDEX i_muc_online_users_us USING BTREE ON muc_online_users(username(75), server(75)); CREATE TABLE muc_room_subscribers ( room varchar(191) NOT NULL, host varchar(191) NOT NULL, jid varchar(191) NOT NULL, nick text NOT NULL, nodes text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY i_muc_room_subscribers_host_room_jid (host, room, jid) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); CREATE TABLE irc_custom ( jid text NOT NULL, host text NOT NULL, data text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75)); CREATE TABLE motd ( username varchar(191) PRIMARY KEY, xml text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE caps_features ( node varchar(191) NOT NULL, subnode varchar(191) NOT NULL, feature text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_caps_features_node_subnode ON caps_features(node(75), subnode(75)); CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, node text NOT NULL, username varchar(191) NOT NULL, resource varchar(191) NOT NULL, priority text NOT NULL, info text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_sid ON sm(usec, pid(75)); CREATE INDEX i_node ON sm(node(75)); CREATE INDEX i_username ON sm(username); CREATE TABLE oauth_token ( token varchar(191) NOT NULL PRIMARY KEY, jid text NOT NULL, scope text NOT NULL, expire bigint NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE route ( domain text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL, local_hint text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75)); CREATE INDEX i_route_domain ON route(domain(75)); CREATE TABLE bosh ( sid text NOT NULL, node text NOT NULL, pid text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75)); CREATE TABLE carboncopy ( username text NOT NULL, resource text NOT NULL, namespace text NOT NULL, node text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username(75), resource(75)); CREATE INDEX i_carboncopy_user ON carboncopy (username(75)); CREATE TABLE proxy65 ( sid text NOT NULL, pid_t text NOT NULL, pid_i text NOT NULL, node_t text NOT NULL, node_i text NOT NULL, jid_i text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191)); CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191)); CREATE TABLE push_session ( username text NOT NULL, timestamp bigint NOT NULL, service text NOT NULL, node text NOT NULL, xml text NOT NULL ); CREATE UNIQUE INDEX i_push_usn ON push_session (username(191), service(191), node(191)); CREATE UNIQUE INDEX i_push_ut ON push_session (username(191), timestamp); ejabberd-18.01/sql/mysql.new.sql0000644000232200023220000004125713225664356017131 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- CREATE TABLE users ( username varchar(191) NOT NULL, server_host text NOT NULL, password text NOT NULL, serverkey varchar(64) NOT NULL DEFAULT '', salt varchar(64) NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Add support for SCRAM auth to a database created before ejabberd 16.03: -- ALTER TABLE users ADD COLUMN serverkey varchar(64) NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN salt varchar(64) NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0; CREATE TABLE last ( username varchar(191) NOT NULL, server_host text NOT NULL, seconds text NOT NULL, state text NOT NULL, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE rosterusers ( username varchar(191) NOT NULL, server_host text NOT NULL, jid varchar(191) NOT NULL, nick text NOT NULL, subscription character(1) NOT NULL, ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, subscribe text NOT NULL, type text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers(server_host(191), username(75), jid(75)); CREATE INDEX i_rosteru_sh_username ON rosterusers(server_host(191), username); CREATE INDEX i_rosteru_sh_jid ON rosterusers(server_host(191), jid); CREATE TABLE rostergroups ( username varchar(191) NOT NULL, server_host text NOT NULL, jid varchar(191) NOT NULL, grp text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_rosterg_sh_user_jid ON rostergroups(server_host(191), username(75), jid(75)); CREATE TABLE sr_group ( name varchar(191) NOT NULL, server_host text NOT NULL, opts text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), name) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE sr_user ( jid varchar(191) NOT NULL, server_host text NOT NULL, grp varchar(191) NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), jid, grp) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_sr_user_sh_jid ON sr_user(server_host(191), jid); CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp); CREATE TABLE spool ( username varchar(191) NOT NULL, server_host text NOT NULL, xml BLOB NOT NULL, seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_spool_sh_username USING BTREE ON spool(server_host(191), username); CREATE INDEX i_spool_created_at USING BTREE ON spool(created_at); CREATE TABLE archive ( username varchar(191) NOT NULL, server_host text NOT NULL, timestamp BIGINT UNSIGNED NOT NULL, peer varchar(191) NOT NULL, bare_peer varchar(191) NOT NULL, xml text NOT NULL, txt text, id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, kind varchar(10), nick varchar(191), created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE FULLTEXT INDEX i_text ON archive(txt); CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username,timestamp); CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp); CREATE INDEX i_archive_sh_peer USING BTREE ON archive(server_host(191), peer); CREATE INDEX i_archive_sh_bare_peer USING BTREE ON archive(server_host(191), bare_peer); CREATE TABLE archive_prefs ( username varchar(191) NOT NULL, server_host text NOT NULL, def text NOT NULL, always text NOT NULL, never text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE vcard ( username varchar(191) NOT NULL, server_host text NOT NULL, vcard mediumtext NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE vcard_search ( username varchar(191) NOT NULL, lusername varchar(191) NOT NULL, server_host text NOT NULL, fn text NOT NULL, lfn varchar(191) NOT NULL, family text NOT NULL, lfamily varchar(191) NOT NULL, given text NOT NULL, lgiven varchar(191) NOT NULL, middle text NOT NULL, lmiddle varchar(191) NOT NULL, nickname text NOT NULL, lnickname varchar(191) NOT NULL, bday text NOT NULL, lbday varchar(191) NOT NULL, ctry text NOT NULL, lctry varchar(191) NOT NULL, locality text NOT NULL, llocality varchar(191) NOT NULL, email text NOT NULL, lemail varchar(191) NOT NULL, orgname text NOT NULL, lorgname varchar(191) NOT NULL, orgunit text NOT NULL, lorgunit varchar(191) NOT NULL, PRIMARY KEY (server_host(191), lusername) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host(191), lfn); CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host(191), lfamily); CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host(191), lgiven); CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host(191), lmiddle); CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host(191), lnickname); CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host(191), lbday); CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host(191), lctry); CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host(191), llocality); CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host(191), lemail); CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host(191), lorgname); CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host(191), lorgunit); CREATE TABLE privacy_default_list ( username varchar(191) NOT NULL, server_host text NOT NULL, name varchar(191) NOT NULL, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE privacy_list ( username varchar(191) NOT NULL, server_host text NOT NULL, name varchar(191) NOT NULL, id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_privacy_list_sh_username USING BTREE ON privacy_list(server_host(191), username); CREATE UNIQUE INDEX i_privacy_list_sh_username_name USING BTREE ON privacy_list (server_host(191), username(75), name(75)); CREATE TABLE privacy_list_data ( id bigint, t character(1) NOT NULL, value text NOT NULL, action character(1) NOT NULL, ord NUMERIC NOT NULL, match_all boolean NOT NULL, match_iq boolean NOT NULL, match_message boolean NOT NULL, match_presence_in boolean NOT NULL, match_presence_out boolean NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_privacy_list_data_id ON privacy_list_data(id); CREATE TABLE private_storage ( username varchar(191) NOT NULL, server_host text NOT NULL, namespace varchar(191) NOT NULL, data text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), username, namespace) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_private_storage_sh_username USING BTREE ON private_storage(server_host(191), username); -- Not tested in mysql CREATE TABLE roster_version ( username varchar(191) NOT NULL, server_host text NOT NULL, version text NOT NULL, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- To update from 1.x: -- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask; -- UPDATE rosterusers SET askmessage = ''; -- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; CREATE TABLE pubsub_node ( host text NOT NULL, node text NOT NULL, parent VARCHAR(191) NOT NULL DEFAULT '', plugin text NOT NULL, nodeid bigint auto_increment primary key ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120)); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(20), node(120)); CREATE TABLE pubsub_node_option ( nodeid bigint, name text NOT NULL, val text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid); ALTER TABLE `pubsub_node_option` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_node_owner ( nodeid bigint, owner text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid); ALTER TABLE `pubsub_node_owner` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_state ( nodeid bigint, jid text NOT NULL, affiliation character(1), subscriptions VARCHAR(191) NOT NULL DEFAULT '', stateid bigint auto_increment primary key ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60)); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state(nodeid, jid(60)); ALTER TABLE `pubsub_state` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_item ( nodeid bigint, itemid text NOT NULL, publisher text NOT NULL, creation text NOT NULL, modification text NOT NULL, payload text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36)); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36)); ALTER TABLE `pubsub_item` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_subscription_opt ( subid text NOT NULL, opt_name varchar(32), opt_value text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt(subid(32), opt_name(32)); CREATE TABLE muc_room ( name text NOT NULL, host text NOT NULL, server_host text NOT NULL, opts mediumtext NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75), host(75)); CREATE TABLE muc_registered ( jid text NOT NULL, host text NOT NULL, server_host text NOT NULL, nick text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75)); CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registered(jid(75), host(75)); CREATE TABLE muc_online_room ( name text NOT NULL, host text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_online_room(name(75), host(75)); CREATE TABLE muc_online_users ( username text NOT NULL, server text NOT NULL, resource text NOT NULL, name text NOT NULL, host text NOT NULL, server_host text NOT NULL, node text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75)); CREATE INDEX i_muc_online_users_us USING BTREE ON muc_online_users(username(75), server(75)); CREATE TABLE muc_room_subscribers ( room varchar(191) NOT NULL, host varchar(191) NOT NULL, jid varchar(191) NOT NULL, nick text NOT NULL, nodes text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY i_muc_room_subscribers_host_room_jid (host, room, jid) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); CREATE TABLE irc_custom ( jid text NOT NULL, host text NOT NULL, server_host text NOT NULL, data text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75)); CREATE TABLE motd ( username varchar(191) NOT NULL, server_host text NOT NULL, xml text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host(191), username) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE caps_features ( node varchar(191) NOT NULL, subnode varchar(191) NOT NULL, feature text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_caps_features_node_subnode ON caps_features(node(75), subnode(75)); CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, node text NOT NULL, username varchar(191) NOT NULL, server_host text NOT NULL, resource varchar(191) NOT NULL, priority text NOT NULL, info text NOT NULL, PRIMARY KEY (usec, pid(75)) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_sm_node ON sm(node(75)); CREATE INDEX i_sm_sh_username ON sm(server_host(191), username); CREATE TABLE oauth_token ( token varchar(191) NOT NULL PRIMARY KEY, jid text NOT NULL, scope text NOT NULL, expire bigint NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE route ( domain text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL, local_hint text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75)); CREATE INDEX i_route_domain ON route(domain(75)); CREATE TABLE bosh ( sid text NOT NULL, node text NOT NULL, pid text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75)); CREATE TABLE carboncopy ( username text NOT NULL, server_host text NOT NULL, resource text NOT NULL, namespace text NOT NULL, node text NOT NULL, PRIMARY KEY (server_host(191), username(191), resource(191)) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host, username(75)); CREATE TABLE proxy65 ( sid text NOT NULL, pid_t text NOT NULL, pid_i text NOT NULL, node_t text NOT NULL, node_i text NOT NULL, jid_i text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191)); CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191)); CREATE TABLE push_session ( username text NOT NULL, server_host text NOT NULL, timestamp bigint NOT NULL, service text NOT NULL, node text NOT NULL, xml text NOT NULL, PRIMARY KEY (server_host(191), username(191), timestamp) ); CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, username(191), service(191), node(191)); ejabberd-18.01/sql/mssql.sql0000644000232200023220000006015413225664356016330 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- SET ANSI_PADDING OFF; SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON; SET ANSI_PADDING ON; CREATE TABLE [dbo].[archive] ( [username] [varchar] (250) NOT NULL, [timestamp] [bigint] NOT NULL, [peer] [varchar] (250) NOT NULL, [bare_peer] [varchar] (250) NOT NULL, [xml] [text] NOT NULL, [txt] [text] NULL, [id] [bigint] IDENTITY(1,1) NOT NULL, [kind] [varchar] (10) NULL, [nick] [varchar] (250) NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [archive_timestamp] ON [archive] (timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [archive_peer] ON [archive] (peer) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [archive_bare_peer] ON [archive] (bare_peer) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[archive_prefs] ( [username] [varchar] (250) NOT NULL, [def] [text] NOT NULL, [always] [text] NOT NULL, [never] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [archive_prefs_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[caps_features] ( [node] [varchar] (250) NOT NULL, [subnode] [varchar] (250) NOT NULL, [feature] [text] NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; CREATE CLUSTERED INDEX [caps_features_node_subnode] ON [caps_features] (node, subnode) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[irc_custom] ( [jid] [varchar] (255) NOT NULL, [host] [varchar] (255) NOT NULL, [data] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; CREATE UNIQUE CLUSTERED INDEX [irc_custom_jid_host] ON [irc_custom] (jid, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[last] ( [username] [varchar] (250) NOT NULL, [seconds] [text] NOT NULL, [state] [text] NOT NULL, CONSTRAINT [last_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[motd] ( [username] [varchar] (250) NOT NULL, [xml] [text] NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [motd_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[muc_registered] ( [jid] [varchar] (255) NOT NULL, [host] [varchar] (255) NOT NULL, [nick] [varchar] (255) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ); CREATE INDEX [muc_registered_nick] ON [muc_registered] (nick) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE CLUSTERED INDEX [muc_registered_jid_host] ON [muc_registered] (jid, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[muc_room] ( [name] [varchar] (250) NOT NULL, [host] [varchar] (250) NOT NULL, [opts] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; CREATE UNIQUE CLUSTERED INDEX [muc_room_name_host] ON [muc_room] (name, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[muc_online_room] ( [name] [varchar] (250) NOT NULL, [host] [varchar] (250) NOT NULL, [node] [text] NOT NULL, [pid] [text] NOT NULL ); CREATE UNIQUE CLUSTERED INDEX [muc_online_room_name_host] ON [muc_online_room] (name, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[muc_online_users] ( [username] [varchar] (250) NOT NULL, [server] [varchar] (250) NOT NULL, [resource] [varchar] (250) NOT NULL, [name] [varchar] (250) NOT NULL, [host] [varchar] (250) NOT NULL, node text NOT NULL ); CREATE UNIQUE CLUSTERED INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server); WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[muc_room_subscribers] ( [room] [varchar] (191) NOT NULL, [host] [varchar] (191) NOT NULL, [jid] [varchar] (191) NOT NULL, [nick] [text] NOT NULL, [nodes] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ); CREATE UNIQUE CLUSTERED INDEX [muc_room_subscribers_host_room_jid] ON [muc_room_subscribers] (host, room, jid); CREATE INDEX [muc_room_subscribers_host_jid] ON [muc_room_subscribers] (host, jid); CREATE TABLE [dbo].[privacy_default_list] ( [username] [varchar] (250) NOT NULL, [name] [varchar] (250) NOT NULL, CONSTRAINT [privacy_default_list_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ); CREATE TABLE [dbo].[privacy_list] ( [username] [varchar] (250) NOT NULL, [name] [varchar] (250) NOT NULL, [id] [bigint] IDENTITY(1,1) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [privacy_list_PK] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ); CREATE INDEX [privacy_list_username] ON [privacy_list] (username) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE INDEX [privacy_list_username_name] ON [privacy_list] (username, name) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[privacy_list_data] ( [id] [bigint] NULL, [t] [char] (1) NOT NULL, [value] [text] NOT NULL, [action] [char] (1) NOT NULL, [ord] [smallint] NOT NULL, [match_all] [smallint] NOT NULL, [match_iq] [smallint] NOT NULL, [match_message] [smallint] NOT NULL, [match_presence_in] [smallint] NOT NULL, [match_presence_out] [smallint] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE CLUSTERED INDEX [privacy_list_data_id] ON [privacy_list_data] (id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[private_storage] ( [username] [varchar] (250) NOT NULL, [namespace] [varchar] (250) NOT NULL, [data] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [private_storage_username] ON [private_storage] (username) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE CLUSTERED INDEX [private_storage_username_namespace] ON [private_storage] (username, namespace) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_item] ( [nodeid] [bigint] NULL, [itemid] [varchar] (255) NOT NULL, [publisher] [text] NOT NULL, [creation] [text] NOT NULL, [modification] [varchar] (255) NOT NULL, [payload] [text] NOT NULL DEFAULT '' ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [pubsub_item_itemid] ON [pubsub_item] (itemid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE CLUSTERED INDEX [pubsub_item_nodeid_itemid] ON [pubsub_item] (nodeid, itemid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_node_option] ( [nodeid] [bigint] NULL, [name] [text] NOT NULL, [val] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE CLUSTERED INDEX [pubsub_node_option_nodeid] ON [pubsub_node_option] (nodeid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_node_owner] ( [nodeid] [bigint] NULL, [owner] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE CLUSTERED INDEX [pubsub_node_owner_nodeid] ON [pubsub_node_owner] (nodeid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_state] ( [nodeid] [bigint] NULL, [jid] [varchar] (255) NOT NULL, [affiliation] [char] (1) NOT NULL, [subscriptions] [text] NOT NULL DEFAULT '', [stateid] [bigint] IDENTITY(1,1) NOT NULL, CONSTRAINT [pubsub_state_PRIMARY] PRIMARY KEY CLUSTERED ( [stateid] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [pubsub_state_jid] ON [pubsub_state] (jid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE INDEX [pubsub_state_nodeid_jid] ON [pubsub_state] (nodeid, jid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_subscription_opt] ( [subid] [varchar] (255) NOT NULL, [opt_name] [varchar] (32) NOT NULL, [opt_value] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE UNIQUE CLUSTERED INDEX [pubsub_subscription_opt_subid_opt_name] ON [pubsub_subscription_opt] (subid, opt_name) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_node] ( [host] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL, [parent] [varchar] (255) NOT NULL DEFAULT '', [plugin] [text] NOT NULL, [nodeid] [bigint] IDENTITY(1,1) NOT NULL, CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED ( [nodeid] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [pubsub_node_parent] ON [pubsub_node] (parent) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE INDEX [pubsub_node_host_node] ON [pubsub_node] (host, node) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[roster_version] ( [username] [varchar] (250) NOT NULL, [version] [text] NOT NULL, CONSTRAINT [roster_version_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[rostergroups] ( [username] [varchar] (250) NOT NULL, [jid] [varchar] (250) NOT NULL, [grp] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE CLUSTERED INDEX [rostergroups_username_jid] ON [rostergroups] ([username], [jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[rosterusers] ( [username] [varchar] (250) NOT NULL, [jid] [varchar] (250) NOT NULL, [nick] [text] NOT NULL, [subscription] [char] (1) NOT NULL, [ask] [char] (1) NOT NULL, [askmessage] [text] NOT NULL, [server] [char] (1) NOT NULL, [subscribe] [text] NOT NULL, [type] [text] NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; CREATE UNIQUE CLUSTERED INDEX [rosterusers_username_jid] ON [rosterusers] ([username], [jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [rosterusers_username] ON [rosterusers] ([username]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [rosterusers_jid] ON [rosterusers] ([jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[sm] ( [usec] [bigint] NOT NULL, [pid] [varchar] (100) NOT NULL, [node] [varchar] (255) NOT NULL, [username] [varchar] (255) NOT NULL, [resource] [varchar] (255) NOT NULL, [priority] [text] NOT NULL, [info] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE UNIQUE CLUSTERED INDEX [sm_sid] ON [sm] (usec, pid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [sm_node] ON [sm] (node) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [sm_username] ON [sm] (username) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[spool] ( [username] [varchar] (250) NOT NULL, [xml] [text] NOT NULL, [seq] [bigint] IDENTITY(1,1) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [spool_PK] PRIMARY KEY CLUSTERED ( [seq] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [spool_username] ON [spool] (username) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [spool_created_at] ON [spool] (created_at) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ; CREATE TABLE [dbo].[sr_group] ( [name] [varchar] (250) NOT NULL, [opts] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [sr_group_PRIMARY] PRIMARY KEY CLUSTERED ( [name] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[sr_user] ( [jid] [varchar] (250) NOT NULL, [grp] [varchar] (250) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE() ); CREATE UNIQUE CLUSTERED INDEX [sr_user_jid_group] ON [sr_user] ([jid], [grp]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [sr_user_jid] ON [sr_user] ([jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [sr_user_grp] ON [sr_user] ([grp]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[users] ( [username] [varchar] (250) NOT NULL, [password] [text] NOT NULL, [serverkey] [text] NOT NULL DEFAULT '', [salt] [text] NOT NULL DEFAULT '', [iterationcount] [smallint] NOT NULL DEFAULT 0, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[vcard] ( [username] [varchar] (250) NOT NULL, [vcard] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [vcard_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[vcard_search] ( [username] [varchar] (250) NOT NULL, [lusername] [varchar] (250) NOT NULL, [fn] [text] NOT NULL, [lfn] [varchar] (250) NOT NULL, [family] [text] NOT NULL, [lfamily] [varchar] (250) NOT NULL, [given] [text] NOT NULL, [lgiven] [varchar] (250) NOT NULL, [middle] [text] NOT NULL, [lmiddle] [varchar] (250) NOT NULL, [nickname] [text] NOT NULL, [lnickname] [varchar] (250) NOT NULL, [bday] [text] NOT NULL, [lbday] [varchar] (250) NOT NULL, [ctry] [text] NOT NULL, [lctry] [varchar] (250) NOT NULL, [locality] [text] NOT NULL, [llocality] [varchar] (250) NOT NULL, [email] [text] NOT NULL, [lemail] [varchar] (250) NOT NULL, [orgname] [text] NOT NULL, [lorgname] [varchar] (250) NOT NULL, [orgunit] [text] NOT NULL, [lorgunit] [varchar] (250) NOT NULL, CONSTRAINT [vcard_search_PRIMARY] PRIMARY KEY CLUSTERED ( [lusername] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [vcard_search_lfn] ON [vcard_search] (lfn) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lfamily] ON [vcard_search] (lfamily) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lgiven] ON [vcard_search] (lgiven) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lmiddle] ON [vcard_search] (lmiddle) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lnickname] ON [vcard_search] (lnickname) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lbday] ON [vcard_search] (lbday) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lctry] ON [vcard_search] (lctry) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_llocality] ON [vcard_search] (llocality) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lemail] ON [vcard_search] (lemail) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lorgname] ON [vcard_search] (lorgname) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [vcard_search_lorgunit] ON [vcard_search] (lorgunit) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); ALTER TABLE [dbo].[pubsub_item] WITH CHECK ADD CONSTRAINT [pubsub_item_ibfk_1] FOREIGN KEY([nodeid]) REFERENCES [dbo].[pubsub_node] ([nodeid]) ON DELETE CASCADE; ALTER TABLE [dbo].[pubsub_item] CHECK CONSTRAINT [pubsub_item_ibfk_1]; ALTER TABLE [dbo].[pubsub_node_option] WITH CHECK ADD CONSTRAINT [pubsub_node_option_ibfk_1] FOREIGN KEY([nodeid]) REFERENCES [dbo].[pubsub_node] ([nodeid]) ON DELETE CASCADE; ALTER TABLE [dbo].[pubsub_node_option] CHECK CONSTRAINT [pubsub_node_option_ibfk_1]; ALTER TABLE [dbo].[pubsub_node_owner] WITH CHECK ADD CONSTRAINT [pubsub_node_owner_ibfk_1] FOREIGN KEY([nodeid]) REFERENCES [dbo].[pubsub_node] ([nodeid]) ON DELETE CASCADE; ALTER TABLE [dbo].[pubsub_node_owner] CHECK CONSTRAINT [pubsub_node_owner_ibfk_1]; ALTER TABLE [dbo].[pubsub_state] WITH CHECK ADD CONSTRAINT [pubsub_state_ibfk_1] FOREIGN KEY([nodeid]) REFERENCES [dbo].[pubsub_node] ([nodeid]) ON DELETE CASCADE; ALTER TABLE [dbo].[pubsub_state] CHECK CONSTRAINT [pubsub_state_ibfk_1]; CREATE TABLE [dbo].[oauth_token] ( [token] [varchar] (250) NOT NULL, [jid] [text] NOT NULL, [scope] [text] NOT NULL, [expire] [bigint] NOT NULL, CONSTRAINT [oauth_token_PRIMARY] PRIMARY KEY CLUSTERED ( [token] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[route] ( [domain] [varchar] (255) NOT NULL, [server_host] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL, [pid] [varchar](100) NOT NULL, [local_hint] [text] NOT NULL ); CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [route_domain] ON [route] (domain) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[bosh] ( [sid] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL, [pid] [varchar](100) NOT NULL CONSTRAINT [bosh_PRIMARY] PRIMARY KEY CLUSTERED ( [sid] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; CREATE TABLE [dbo].[carboncopy] ( [username] [varchar] (255) NOT NULL, [resource] [varchar] (255) NOT NULL, [namespace] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL ); CREATE UNIQUE CLUSTERED INDEX [carboncopy_ur] ON [carboncopy] (username, resource) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [carboncopy_user] ON [carboncopy] (username) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[push_session] ( [username] [varchar] (255) NOT NULL, [timestamp] [bigint] NOT NULL, [service] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL, [xml] [varchar] (255) NOT NULL ); CREATE UNIQUE CLUSTERED INDEX [i_push_usn] ON [push_session] (username, service, node) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE UNIQUE CLUSTERED INDEX [i_push_ut] ON [push_session] (username, timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); ejabberd-18.01/sql/pg.sql0000644000232200023220000003052513225664356015576 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- CREATE TABLE users ( username text PRIMARY KEY, "password" text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT now() ); -- Add support for SCRAM auth to a database created before ejabberd 16.03: -- ALTER TABLE users ADD COLUMN serverkey text NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN salt text NOT NULL DEFAULT ''; -- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0; CREATE TABLE last ( username text PRIMARY KEY, seconds text NOT NULL, state text NOT NULL ); CREATE TABLE rosterusers ( username text NOT NULL, jid text NOT NULL, nick text NOT NULL, subscription character(1) NOT NULL, ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, subscribe text NOT NULL, "type" text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers USING btree (username, jid); CREATE INDEX i_rosteru_username ON rosterusers USING btree (username); CREATE INDEX i_rosteru_jid ON rosterusers USING btree (jid); CREATE TABLE rostergroups ( username text NOT NULL, jid text NOT NULL, grp text NOT NULL ); CREATE INDEX pk_rosterg_user_jid ON rostergroups USING btree (username, jid); CREATE TABLE sr_group ( name text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE TABLE sr_user ( jid text NOT NULL, grp text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user USING btree (jid, grp); CREATE INDEX i_sr_user_jid ON sr_user USING btree (jid); CREATE INDEX i_sr_user_grp ON sr_user USING btree (grp); CREATE TABLE spool ( username text NOT NULL, xml text NOT NULL, seq SERIAL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_despool ON spool USING btree (username); CREATE TABLE archive ( username text NOT NULL, timestamp BIGINT NOT NULL, peer text NOT NULL, bare_peer text NOT NULL, xml text NOT NULL, txt text, id SERIAL, kind text, nick text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp); CREATE INDEX i_timestamp ON archive USING btree (timestamp); CREATE INDEX i_peer ON archive USING btree (peer); CREATE INDEX i_bare_peer ON archive USING btree (bare_peer); CREATE TABLE archive_prefs ( username text NOT NULL PRIMARY KEY, def text NOT NULL, always text NOT NULL, never text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE TABLE vcard ( username text PRIMARY KEY, vcard text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE TABLE vcard_search ( username text NOT NULL, lusername text PRIMARY KEY, fn text NOT NULL, lfn text NOT NULL, family text NOT NULL, lfamily text NOT NULL, given text NOT NULL, lgiven text NOT NULL, middle text NOT NULL, lmiddle text NOT NULL, nickname text NOT NULL, lnickname text NOT NULL, bday text NOT NULL, lbday text NOT NULL, ctry text NOT NULL, lctry text NOT NULL, locality text NOT NULL, llocality text NOT NULL, email text NOT NULL, lemail text NOT NULL, orgname text NOT NULL, lorgname text NOT NULL, orgunit text NOT NULL, lorgunit text NOT NULL ); CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn); CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily); CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven); CREATE INDEX i_vcard_search_lmiddle ON vcard_search(lmiddle); CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname); CREATE INDEX i_vcard_search_lbday ON vcard_search(lbday); CREATE INDEX i_vcard_search_lctry ON vcard_search(lctry); CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality); CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail); CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname); CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit); CREATE TABLE privacy_default_list ( username text PRIMARY KEY, name text NOT NULL ); CREATE TABLE privacy_list ( username text NOT NULL, name text NOT NULL, id SERIAL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_privacy_list_username ON privacy_list USING btree (username); CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list USING btree (username, name); CREATE TABLE privacy_list_data ( id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, t character(1) NOT NULL, value text NOT NULL, action character(1) NOT NULL, ord NUMERIC NOT NULL, match_all boolean NOT NULL, match_iq boolean NOT NULL, match_message boolean NOT NULL, match_presence_in boolean NOT NULL, match_presence_out boolean NOT NULL ); CREATE INDEX i_privacy_list_data_id ON privacy_list_data USING btree (id); CREATE TABLE private_storage ( username text NOT NULL, namespace text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_private_storage_username ON private_storage USING btree (username); CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage USING btree (username, namespace); CREATE TABLE roster_version ( username text PRIMARY KEY, version text NOT NULL ); -- To update from 0.9.8: -- CREATE SEQUENCE spool_seq_seq; -- ALTER TABLE spool ADD COLUMN seq integer; -- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq'); -- UPDATE spool SET seq = DEFAULT; -- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL; -- To update from 1.x: -- ALTER TABLE rosterusers ADD COLUMN askmessage text; -- UPDATE rosterusers SET askmessage = ''; -- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; CREATE TABLE pubsub_node ( host text NOT NULL, node text NOT NULL, parent text NOT NULL DEFAULT '', plugin text NOT NULL, nodeid SERIAL UNIQUE ); CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node); CREATE TABLE pubsub_node_option ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, name text NOT NULL, val text NOT NULL ); CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btree (nodeid); CREATE TABLE pubsub_node_owner ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, owner text NOT NULL ); CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree (nodeid); CREATE TABLE pubsub_state ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, jid text NOT NULL, affiliation character(1), subscriptions text NOT NULL DEFAULT '', stateid SERIAL UNIQUE ); CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, jid); CREATE TABLE pubsub_item ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, itemid text NOT NULL, publisher text NOT NULL, creation text NOT NULL, modification text NOT NULL, payload text NOT NULL DEFAULT '' ); CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (nodeid, itemid); CREATE TABLE pubsub_subscription_opt ( subid text NOT NULL, opt_name varchar(32), opt_value text NOT NULL ); CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt USING btree (subid, opt_name); CREATE TABLE muc_room ( name text NOT NULL, host text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room USING btree (name, host); CREATE TABLE muc_registered ( jid text NOT NULL, host text NOT NULL, nick text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick); CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING btree (jid, host); CREATE TABLE muc_online_room ( name text NOT NULL, host text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING btree (name, host); CREATE TABLE muc_online_users ( username text NOT NULL, server text NOT NULL, resource text NOT NULL, name text NOT NULL, host text NOT NULL, node text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host); CREATE INDEX i_muc_online_users_us ON muc_online_users USING btree (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, host text NOT NULL, jid text NOT NULL, nick text NOT NULL, nodes text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); CREATE TABLE irc_custom ( jid text NOT NULL, host text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host); CREATE TABLE motd ( username text PRIMARY KEY, xml text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE TABLE caps_features ( node text NOT NULL, subnode text NOT NULL, feature text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE INDEX i_caps_features_node_subnode ON caps_features USING btree (node, subnode); CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, node text NOT NULL, username text NOT NULL, resource text NOT NULL, priority text NOT NULL, info text NOT NULL ); CREATE UNIQUE INDEX i_sm_sid ON sm USING btree (usec, pid); CREATE INDEX i_sm_node ON sm USING btree (node); CREATE INDEX i_sm_username ON sm USING btree (username); CREATE TABLE oauth_token ( token text NOT NULL, jid text NOT NULL, scope text NOT NULL, expire bigint NOT NULL ); CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token); CREATE TABLE route ( domain text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL, local_hint text NOT NULL ); CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); CREATE INDEX i_route_domain ON route USING btree (domain); CREATE TABLE bosh ( sid text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid); CREATE TABLE carboncopy ( username text NOT NULL, resource text NOT NULL, namespace text NOT NULL, node text NOT NULL ); CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy USING btree (username, resource); CREATE INDEX i_carboncopy_user ON carboncopy USING btree (username); CREATE TABLE proxy65 ( sid text NOT NULL, pid_t text NOT NULL, pid_i text NOT NULL, node_t text NOT NULL, node_i text NOT NULL, jid_i text NOT NULL ); CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid); CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i); CREATE TABLE push_session ( username text NOT NULL, timestamp bigint NOT NULL, service text NOT NULL, node text NOT NULL, xml text NOT NULL ); CREATE UNIQUE INDEX i_push_usn ON push_session USING btree (username, service, node); CREATE UNIQUE INDEX i_push_ut ON push_session USING btree (username, timestamp); ejabberd-18.01/sql/lite.new.sql0000644000232200023220000003121313225664356016710 0ustar debalancedebalance-- -- ejabberd, Copyright (C) 2002-2017 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License along -- with this program; if not, write to the Free Software Foundation, Inc., -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -- CREATE TABLE users ( username text NOT NULL, server_host text NOT NULL, password text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, username) ); CREATE TABLE last ( username text NOT NULL, server_host text NOT NULL, seconds text NOT NULL, state text NOT NULL, PRIMARY KEY (server_host, username) ); CREATE TABLE rosterusers ( username text NOT NULL, server_host text NOT NULL, jid text NOT NULL, nick text NOT NULL, subscription character(1) NOT NULL, ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, subscribe text NOT NULL, type text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers (server_host, username, jid); CREATE INDEX i_rosteru_sh_username ON rosterusers (server_host, username); CREATE INDEX i_rosteru_sh_jid ON rosterusers (server_host, jid); CREATE TABLE rostergroups ( username text NOT NULL, server_host text NOT NULL, jid text NOT NULL, grp text NOT NULL ); CREATE INDEX i_rosterg_sh_user_jid ON rostergroups (server_host, username, jid); CREATE TABLE sr_group ( name text NOT NULL, server_host text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, name) ); CREATE TABLE sr_user ( jid text NOT NULL, server_host text NOT NULL, grp text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, jid, grp) ); CREATE INDEX i_sr_user_sh_jid ON sr_user (server_host, jid); CREATE INDEX i_sr_user_sh_grp ON sr_user (server_host, grp); CREATE TABLE spool ( username text NOT NULL, server_host text NOT NULL, xml text NOT NULL, seq INTEGER PRIMARY KEY AUTOINCREMENT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_spool_sh_username ON spool (server_host, username); CREATE TABLE archive ( username text NOT NULL, server_host text NOT NULL, timestamp BIGINT UNSIGNED NOT NULL, peer text NOT NULL, bare_peer text NOT NULL, xml text NOT NULL, txt text, id INTEGER PRIMARY KEY AUTOINCREMENT, kind text, nick text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, timestamp); CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp); CREATE INDEX i_archive_sh_peer ON archive (server_host, peer); CREATE INDEX i_archive_sh_bare_peer ON archive (server_host, bare_peer); CREATE TABLE archive_prefs ( username text NOT NULL, server_host text NOT NULL, def text NOT NULL, always text NOT NULL, never text NOT NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, username) ); CREATE TABLE vcard ( username text NOT NULL, server_host text NOT NULL, vcard text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, username) ); CREATE TABLE vcard_search ( username text NOT NULL, lusername text NOT NULL, server_host text NOT NULL, fn text NOT NULL, lfn text NOT NULL, family text NOT NULL, lfamily text NOT NULL, given text NOT NULL, lgiven text NOT NULL, middle text NOT NULL, lmiddle text NOT NULL, nickname text NOT NULL, lnickname text NOT NULL, bday text NOT NULL, lbday text NOT NULL, ctry text NOT NULL, lctry text NOT NULL, locality text NOT NULL, llocality text NOT NULL, email text NOT NULL, lemail text NOT NULL, orgname text NOT NULL, lorgname text NOT NULL, orgunit text NOT NULL, lorgunit text NOT NULL, PRIMARY KEY (server_host, lusername) ); CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host, lmiddle); CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, lnickname); CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host, lbday); CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host, lctry); CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, llocality); CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host, lemail); CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host, lorgname); CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host, lorgunit); CREATE TABLE privacy_default_list ( username text NOT NULL, server_host text NOT NULL, name text NOT NULL, PRIMARY KEY (server_host, username) ); CREATE TABLE privacy_list ( username text NOT NULL, server_host text NOT NULL, name text NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_privacy_list_sh_username ON privacy_list (server_host, username); CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list (server_host, username, name); CREATE TABLE privacy_list_data ( id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, t character(1) NOT NULL, value text NOT NULL, action character(1) NOT NULL, ord NUMERIC NOT NULL, match_all boolean NOT NULL, match_iq boolean NOT NULL, match_message boolean NOT NULL, match_presence_in boolean NOT NULL, match_presence_out boolean NOT NULL ); CREATE TABLE private_storage ( username text NOT NULL, server_host text NOT NULL, namespace text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, username, namespace) ); CREATE INDEX i_private_storage_sh_username ON private_storage (server_host, username); CREATE TABLE roster_version ( username text NOT NULL, server_host text NOT NULL, version text NOT NULL, PRIMARY KEY (server_host, username) ); CREATE TABLE pubsub_node ( host text NOT NULL, node text NOT NULL, parent text NOT NULL DEFAULT '', plugin text NOT NULL, nodeid INTEGER PRIMARY KEY AUTOINCREMENT ); CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node); CREATE TABLE pubsub_node_option ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, name text NOT NULL, val text NOT NULL ); CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid); CREATE TABLE pubsub_node_owner ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, owner text NOT NULL ); CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid); CREATE TABLE pubsub_state ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, jid text NOT NULL, affiliation character(1), subscriptions text NOT NULL DEFAULT '', stateid INTEGER PRIMARY KEY AUTOINCREMENT ); CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid); CREATE TABLE pubsub_item ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, itemid text NOT NULL, publisher text NOT NULL, creation text NOT NULL, modification text NOT NULL, payload text NOT NULL DEFAULT '' ); CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid); CREATE TABLE pubsub_subscription_opt ( subid text NOT NULL, opt_name varchar(32), opt_value text NOT NULL ); CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt (subid, opt_name); CREATE TABLE muc_room ( name text NOT NULL, server_host text NOT NULL, host text NOT NULL, opts text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host); CREATE TABLE muc_registered ( jid text NOT NULL, host text NOT NULL, server_host text NOT NULL, nick text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_muc_registered_nick ON muc_registered (nick); CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host); CREATE TABLE muc_online_room ( name text NOT NULL, host text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name, host); CREATE TABLE muc_online_users ( username text NOT NULL, server text NOT NULL, resource text NOT NULL, name text NOT NULL, host text NOT NULL, server_host text NOT NULL, node text NOT NULL ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host); CREATE INDEX i_muc_online_users_us ON muc_online_users (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, host text NOT NULL, jid text NOT NULL, nick text NOT NULL, nodes text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); CREATE TABLE irc_custom ( jid text NOT NULL, host text NOT NULL, server_host text NOT NULL, data text NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host); CREATE TABLE motd ( username text NOT NULL, server_host text NOT NULL, xml text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (server_host, username) ); CREATE TABLE caps_features ( node text NOT NULL, subnode text NOT NULL, feature text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode); CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, node text NOT NULL, username text NOT NULL, server_host text NOT NULL, resource text NOT NULL, priority text NOT NULL, info text NOT NULL, PRIMARY KEY (usec, pid) ); CREATE INDEX i_sm_node ON sm(node); CREATE INDEX i_sm_sh_username ON sm (server_host, username); CREATE TABLE oauth_token ( token text NOT NULL PRIMARY KEY, jid text NOT NULL, scope text NOT NULL, expire bigint NOT NULL ); CREATE TABLE route ( domain text NOT NULL, server_host text NOT NULL, node text NOT NULL, pid text NOT NULL, local_hint text NOT NULL ); CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid); CREATE INDEX i_route_domain ON route(domain); CREATE TABLE bosh ( sid text NOT NULL, node text NOT NULL, pid text NOT NULL ); CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid); CREATE TABLE carboncopy ( username text NOT NULL, server_host text NOT NULL, resource text NOT NULL, namespace text NOT NULL, node text NOT NULL, PRIMARY KEY (server_host, username, resource) ); CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host, username); CREATE TABLE proxy65 ( sid text NOT NULL, pid_t text NOT NULL, pid_i text NOT NULL, node_t text NOT NULL, node_i text NOT NULL, jid_i text NOT NULL ); CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid); CREATE INDEX i_proxy65_jid ON proxy65 (jid_i); CREATE TABLE push_session ( username text NOT NULL, server_host text NOT NULL, timestamp bigint NOT NULL, service text NOT NULL, node text NOT NULL, xml text NOT NULL, PRIMARY KEY (server_host, username, timestamp) ); CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, username, service, node); ejabberd-18.01/configure.ac0000644000232200023220000002400613225667421016127 0ustar debalancedebalance# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.53) AC_INIT(ejabberd, 18.01, ejabberd@process-one.net, ejabberd) REQUIRE_ERLANG_MIN="6.4 (Erlang/OTP 17.5)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PROG_SED if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" fi # Checks Erlang runtime and compiler AC_ARG_WITH(erlang, AC_HELP_STRING([--with-erlang=dir], [search for erlang in dir]), [if test "$withval" = "yes" -o "$withval" = "no" -o "X$with_erlang" = "X"; then extra_erl_path="" else extra_erl_path="$with_erlang:$with_erlang/bin:" fi ]) AC_PATH_TOOL(ERL, erl, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(ERLC, erlc, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(EPMD, epmd, , [${extra_erl_path}$PATH]) AC_ERLANG_NEED_ERL AC_ERLANG_NEED_ERLC AC_ARG_ENABLE(erlang-version-check, [AC_HELP_STRING([--enable-erlang-version-check], [Check Erlang/OTP version @<:@default=yes@:>@])]) case "$enable_erlang_version_check" in yes|'') ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX]) ;; no) ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX],[warn]) ;; esac # Checks and sets ERLANG_ROOT_DIR and ERLANG_LIB_DIR variable AC_ERLANG_SUBST_ROOT_DIR # AC_ERLANG_SUBST_LIB_DIR #locating escript AC_PATH_PROG([ESCRIPT], [escript], [], [$ERLANG_ROOT_DIR/bin]) #locating make AC_CHECK_PROG([MAKE], [make], [make], []) if test "x$ESCRIPT" = "x"; then AC_MSG_ERROR(['escript' was not found]) fi if test "x$MAKE" = "x"; then AC_MSG_ERROR(['make' was not found]) fi # Change default prefix AC_PREFIX_DEFAULT(/usr/local) AC_ARG_ENABLE(hipe, [AC_HELP_STRING([--enable-hipe], [compile natively with HiPE, not recommended (default: no)])], [case "${enableval}" in yes) hipe=true ;; no) hipe=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-hipe) ;; esac],[hipe=false]) AC_ARG_ENABLE(roster_gateway_workaround, [AC_HELP_STRING([--enable-roster-gateway-workaround], [turn on workaround for processing gateway subscriptions (default: no)])], [case "${enableval}" in yes) roster_gateway_workaround=true ;; no) roster_gateway_workaround=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-roster-gateway-workaround) ;; esac],[roster_gateway_workaround=false]) AC_ARG_ENABLE(new_sql_schema, [AC_HELP_STRING([--enable-new-sql-schema], [use new SQL schema (default: no)])], [case "${enableval}" in yes) new_sql_schema=true ;; no) new_sql_schema=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-new-sql-schema) ;; esac],[new_sql_schema=false]) AC_ARG_ENABLE(full_xml, [AC_HELP_STRING([--enable-full-xml], [use XML features in XMPP stream (ex: CDATA) (default: no, requires XML compliant clients)])], [case "${enableval}" in yes) full_xml=true ;; no) full_xml=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;; esac],[full_xml=false]) AC_ARG_ENABLE(mssql, [AC_HELP_STRING([--enable-mssql], [use Microsoft SQL Server database (default: no, requires --enable-odbc)])], [case "${enableval}" in yes) db_type=mssql ;; no) db_type=generic ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;; esac],[db_type=generic]) AC_ARG_ENABLE(all, [AC_HELP_STRING([--enable-all], [same as --enable-odbc --enable-mysql --enable-pgsql --enable-sqlite --enable-pam --enable-zlib --enable-riak --enable-redis --enable-elixir --enable-iconv --enable-stun --enable-sip --enable-debug --enable-tools (useful for Dialyzer checks, default: no)])], [case "${enableval}" in yes) odbc=true mysql=true pgsql=true sqlite=true pam=true zlib=true riak=true redis=true elixir=true iconv=true stun=true sip=true debug=true tools=true ;; no) odbc=false mysql=false pgsql=false sqlite=false pam=false zlib=false riak=false redis=false elixir=false iconv=false stun=false sip=false debug=false tools=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;; esac],[]) AC_ARG_ENABLE(tools, [AC_HELP_STRING([--enable-tools], [build development tools (default: no)])], [case "${enableval}" in yes) tools=true ;; no) tools=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;; esac],[if test "x$tools" = "x"; then tools=false; fi]) AC_ARG_ENABLE(odbc, [AC_HELP_STRING([--enable-odbc], [enable pure ODBC support (default: no)])], [case "${enableval}" in yes) odbc=true ;; no) odbc=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-odbc) ;; esac],[if test "x$odbc" = "x"; then odbc=false; fi]) AC_ARG_ENABLE(mysql, [AC_HELP_STRING([--enable-mysql], [enable MySQL support (default: no)])], [case "${enableval}" in yes) mysql=true ;; no) mysql=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-mysql) ;; esac],[if test "x$mysql" = "x"; then mysql=false; fi]) AC_ARG_ENABLE(pgsql, [AC_HELP_STRING([--enable-pgsql], [enable PostgreSQL support (default: no)])], [case "${enableval}" in yes) pgsql=true ;; no) pgsql=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;; esac],[if test "x$pgsql" = "x"; then pgsql=false; fi]) AC_ARG_ENABLE(sqlite, [AC_HELP_STRING([--enable-sqlite], [enable SQLite support (default: no)])], [case "${enableval}" in yes) sqlite=true ;; no) sqlite=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-sqlite) ;; esac],[if test "x$sqlite" = "x"; then sqlite=false; fi]) AC_ARG_ENABLE(pam, [AC_HELP_STRING([--enable-pam], [enable PAM support (default: no)])], [case "${enableval}" in yes) pam=true ;; no) pam=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-pam) ;; esac],[if test "x$pam" = "x"; then pam=false; fi]) AC_ARG_ENABLE(zlib, [AC_HELP_STRING([--enable-zlib], [enable Stream Compression (XEP-0138) using zlib (default: yes)])], [case "${enableval}" in yes) zlib=true ;; no) zlib=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-zlib) ;; esac],[if test "x$zlib" = "x"; then zlib=true; fi]) AC_ARG_ENABLE(riak, [AC_HELP_STRING([--enable-riak], [enable Riak support (default: no)])], [case "${enableval}" in yes) riak=true ;; no) riak=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-riak) ;; esac],[if test "x$riak" = "x"; then riak=false; fi]) AC_ARG_ENABLE(redis, [AC_HELP_STRING([--enable-redis], [enable Redis support (default: no)])], [case "${enableval}" in yes) redis=true ;; no) redis=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-redis) ;; esac],[if test "x$redis" = "x"; then redis=false; fi]) AC_ARG_ENABLE(elixir, [AC_HELP_STRING([--enable-elixir], [enable Elixir support (default: no)])], [case "${enableval}" in yes) elixir=true ;; no) elixir=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-elixir) ;; esac],[if test "x$elixir" = "x"; then elixir=false; fi]) AC_ARG_ENABLE(iconv, [AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])], [case "${enableval}" in yes) iconv=true ;; no) iconv=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-iconv) ;; esac],[if test "x$iconv" = "x"; then iconv=true; fi]) AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug], [enable debug information (default: yes)])], [case "${enableval}" in yes) debug=true ;; no) debug=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; esac],[if test "x$debug" = "x"; then debug=true; fi]) AC_ARG_ENABLE(latest_deps, [AC_HELP_STRING([--enable-latest-deps], [makes rebar use latest commits for dependences instead of tagged versions (default: no)])], [case "${enableval}" in yes) latest_deps=true ;; no) latest_deps=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-latest-deps) ;; esac],[if test "x$latest_deps" = "x"; then latest_deps=false; fi]) AC_ARG_ENABLE(system_deps, [AC_HELP_STRING([--enable-system-deps], [makes rebar use localy installed dependences instead of downloading them (default: no)])], [case "${enableval}" in yes) system_deps=true ;; no) system_deps=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-system-deps) ;; esac],[if test "x$system_deps" = "x"; then system_deps=false; fi]) AC_ARG_ENABLE(stun, [AC_HELP_STRING([--enable-stun], [enable STUN/TURN support (default: no)])], [case "${enableval}" in yes) stun=true ;; no) stun=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-stun) ;; esac],[if test "x$stun" = "x"; then stun=false; fi]) AC_ARG_ENABLE(sip, [AC_HELP_STRING([--enable-sip], [enable SIP support (default: no)])], [case "${enableval}" in yes) sip=true ;; no) sip=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-sip) ;; esac],[if test "x$sip" = "x"; then sip=false; fi]) AC_ARG_ENABLE(graphics, [AC_HELP_STRING([--enable-graphics], [enable support for graphic images manipulation (default: yes)])], [case "${enableval}" in yes) graphics=true ;; no) graphics=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-graphics) ;; esac],[if test "x$graphics" = "x"; then graphics=true; fi]) AC_CONFIG_FILES([Makefile vars.config src/ejabberd.app.src]) ENABLEUSER="" AC_ARG_ENABLE(user, [AS_HELP_STRING([--enable-user[[[[=USER]]]]], [allow this system user to start ejabberd (default: no)])], [case "${enableval}" in yes) ENABLEUSER=`whoami` ;; no) ENABLEUSER="" ;; *) ENABLEUSER=$enableval esac], []) if test "$ENABLEUSER" != ""; then echo "allow this system user to start ejabberd: $ENABLEUSER" AC_SUBST([INSTALLUSER], [$ENABLEUSER]) fi ERLANG_DEPRECATED_TYPES_CHECK if test "$sqlite" = "true"; then AX_LIB_SQLITE3([3.6.19]) if test "x$SQLITE3_VERSION" = "x"; then AC_MSG_ERROR(SQLite3 library >= 3.6.19 was not found) fi fi AC_SUBST(hipe) AC_SUBST(roster_gateway_workaround) AC_SUBST(new_sql_schema) AC_SUBST(full_xml) AC_SUBST(db_type) AC_SUBST(odbc) AC_SUBST(mysql) AC_SUBST(pgsql) AC_SUBST(sqlite) AC_SUBST(pam) AC_SUBST(zlib) AC_SUBST(riak) AC_SUBST(redis) AC_SUBST(elixir) AC_SUBST(iconv) AC_SUBST(stun) AC_SUBST(sip) AC_SUBST(debug) AC_SUBST(graphics) AC_SUBST(tools) AC_SUBST(latest_deps) AC_SUBST(system_deps) AC_SUBST(CFLAGS) AC_SUBST(CPPFLAGS) AC_SUBST(LDFLAGS) AC_OUTPUT ejabberd-18.01/autogen.sh0000755000232200023220000000006413225664356015644 0ustar debalancedebalance# generate a new autoconf aclocal -I m4 autoconf -f ejabberd-18.01/priv/0000755000232200023220000000000013225664356014623 5ustar debalancedebalanceejabberd-18.01/priv/img/0000755000232200023220000000000013225664356015377 5ustar debalancedebalanceejabberd-18.01/priv/img/valid-xhtml10.png0000644000232200023220000000455613225664356020511 0ustar debalancedebalancePNG  IHDRX#DPLTE{9ތBBcZZބB{{ޥ{޵RssޭRZs{Rc19Jc猭RZ)J!{{{ZZJs9JZRJsssJk1!c1kkkBs9ƜJccck1c1kcRk99J֜kkƥZε9999R){9{c1s9111J!1{Jk1){)))ZֵBƽZ9))csZΥZkcRcR)cJ)9c1{9Zs1Z{skZB{BJJsBsBBJJ֌kB9skR)!sZ1kB1scRkZRB!ΥRkR)ZcsRcZZZZ9BJZ)ZJRRR!ZJ1kk)BJ{JJJ{ֵccZR֭cBBBޭc91c޵Z{sR1){J9{kR1!{ZJ!֭R)!sZ)ZZB!c֥R{RcckR!sZsZRJ!޽9sJRRJ9kRJJ!R޵cB?tRNSlIt y]Dq>`IDATxڕV_E˻aFڊQY DTrDiPP"|94B":$4G):[F~3w~=fs@Ŵ{"Ք5CYeF$WPVFt'1|NcDc>1Y Õ-{~BI`[%[#I$H%aD|D cO(Loau( )y*aҺ)}*`&lf0wvӿfXmM>Dزn9kZ2ps!acf&Ia:Tqsr=4On XL|Ԥjar|c9G+@yuJ\#1twA#G4H_5A =xb9*t5o{x5I,M*: AN(Y$Im9Hbmi feNBK~nfk~[pcD16)@hi)M?R&ȅHY)m4ZdTt#4~"T%.eVv%_ȥXd^.ЪhlzfRJdds*tYF\8LhX:֝U" _7e[ʆigJ;ܽqg7:AQOzr`7PaaaL፞g4@> n;4h[l``'N !Ҩ ύWO*oхG`f3 ;y_+'H6ssGAoѪh9A q>g!]]]$\UH\UHOOOݲjT|?<5ΧQK$s]K$^KNK@NK@̸OOQD<,$^n Ep__>lz^}e1޴X 6dmX+&YϒL>!U<1LLLWSP<<<9$ D//,,,ME͝>fE)s r8 LctRNS0 00zIDATx}S@j RDEAROĈQ'p.eL+rl/_{vf?pMrx://ǼbDQ`Rg FDۊEsT89@&W~ѧ4tR̨|D^NV3h|{/oc@TGliYd-O`ۓ,X)Ddj !# TS Pukbe b]䄄KdSЍToYK惵[%筇DRlvVdʛQ ?,i(V TP+V9rE62ܓ&62,DEK] R0X76A`SQ˂쨂 ݏz31` y{rAW0W`PFrsw`P)n웸gSEAh. gcXKE7YF!cCM1[ID(C8Ep`ŗ\(/`aa??E4npXOWw(Ȇ>Oj>Lopje`Pkk+,?ˉHLeuSJM` X1rjZN&&^܌sfkx'@UhQ `= 5O3(R 4| -8GL3'fQ'rqU~؁-sghGo 5:G_89}㬍lJ :Xт iהGf@+`9Zvm:#XUAg)|Ly肵%w\Y h)8 5%MODo:>w:~mnvY*,f6W,lY&z}D`uSTlƙo?UG؞?6SQ~ҎU/i%O߿!X|GEAW8۠nbOnE9p({( B/6p}qK傕Z [(8 CN5i﮸y2=ЦLJ;XaA7(yjU6A-XGU(Wrh`徧 P CK\JC3QzJpDP{a%vUU=hI\0 kP_ªag V?"N ;BGeCv֮mЬg үò΃U5mue!DH qf,6̑ž UvZ\d2Z?UL)tlIDee7is`QhyFe`f2ahcw=Tdv^;V֛ 3aZ޽J&:Hst5J|Ngȗ 4GY)^fr.^ߥ RlfP<`Ou_A>'Hw@dSI:ݜ+$f^8g 2?Ǟe*`BU :).g* D^2m Ka?|{K`{RU~m!9Rj(!(+pG/s6X+?8jw0_{O*L6SpbF*,@6bi+beh{p9ME:X[`m V/U2)*:ڝ&*L10fxR&X:"~(s,%$O --A-UQCˑEU$u6).Qi/*(XÞ'ՀiU7 -}JЏxk2 D6!}O *Yg-4ڋ",4mZ!kB#J*~؃ðZ/?Zh=E+AZ*6CY(U ]m(Rz{6x;RfO˙ o#<-8"9IcġbxXVؿ&Q.X"N`Pcp_Wlk:8D|A\(fHT@{ ?GıS.mM\>k/CL o(Sq'ɇ*@>wwqJ5eb~]MNpS&3,{J sUyg 8cWùT$~ 57"bJ+[9gvLvyUy`>X5!₿^⽺ `yX-;) h@ qV q#i(;f@*1l9Abރk(o&dEL/[xXOs}f̣U!}b,[(z#Xb?tqQUĄjG!؍gOr]Ȥ V&6O{* Wۉ >~%x=G^1 V31,s2&Q=r#&{3D hO\eU0 (VX]oqqᡳ( B%[%=쀌nJ0,|@e1;Je" c#>h[kfnkloox$ ,& ub$>rg= fچ?P loо¦(vj_VbaPATX[!'k. rF*oS.y:ٯñr!Cpb\qt;l%̦*}ByĂsCpv( ڪ4 lWaQ'A,3=F ܳ3&× *o`fX Y`-_oj*5b~Lq%X tTq$[p/0M\ı@yrX v6)>y4 ~Lߓ%OׅjŅԲ`8_A o[Nx[. .e.YJQ;ȀVsNQ.vU9xv1LʼnR "~~yzqXB O;UN@ljwհpxJ b3y jpVھb- JH,RNRP`CBXHJYYOQNa`[[Fb`)\[Zhh%pmih8sacljHngeklV|wwvwxwo ǛRtRNS@fbKGDH pHYs  tIME @pIDATHǽr0AHbZ]ZEV׆?UAHv:;;J䞛 q-Jp8dv?A(=%@ȄGg_quc붴Djga6;lpd~nOuN&TiGБzB/!(%emP /O?DZJ&}}J]y_q8^ Twcq܊.ZL$|v>`sT N˲{ D/3) qa^~AƱ1w eThFeQc O2ͨx{RJ^Rnk(ث‹9>t,Q}oSP0+a)%,VrռX4h6ߧ 1mkeI5#kKD rx72{ Y)IO) &ɽ񣬾~;Aţ(E9se*98//}BT5O$ľ0pχKoɥ4D?՘U2IDBLZl]a!@w 8{+;ZcDM E»eh6#%?b/L6y\La1׶|4Gr=q/.FwIENDB`ejabberd-18.01/priv/img/admin-logo.png0000644000232200023220000006403013225664356020136 0ustar debalancedebalancePNG  IHDRC AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~@IDATx} UnL A"B>-*k|v3{l!|5>[|v+A[ABV 4ބ d sn׹;sn {ڻ߷j^{+ XDQ4 & 1 AiT3C;F]: j"@ʌv+v4E{I&4M_@ D"/Z WpPΏWSJ' T7>[ "7l{=U/!TޛL_{B@ 9# L27̲K{Ԗ+ 7 4=T7-.bZL_;jN  }Mf/RŨMƲ FH2_.IT"@E ZV?]#hrgj7͍8@GZqA=<~D"/%_Rՠo%3AHPR"05h)oWJ it*Vle/휖rTпǘ Dl4ֳjm. ZoJ T4Lxٶ'UVuVy= Dsz_eǕ{@{]٫2f(=s5GM_CLj DClkXM א৫( \yS/7XwORSϨ&׉!D"@z!5:`˘ӱ5v$#"`J6.ZMb|.ME@`WgǮKf=: Z[&0!z] O'k}_zrB`#U<,1CC!Anfઃ0A gO.00B#t f>#@Gq G@lNI˔@F}vy\G@,``)1U`v=rslAWN P5kkPSPl0ʒU/6[QNOGm̆ڐ(T"@ G,ٯyVK`-Kz*>*A|"]'YpA׆& \mLkLN^H #`oH6CP "П`~be -  %@X Sr0Be:(_r%D"Z@g2+)g[[[H64"@"/": E?KD"@"u*kA,̸ͯA(Ѵe'vcdžd_}eՖ0n'"@A?߂MS+@/AqJ;*C@ff Tî ĬHҦX Dx3AA_> 4DGib?àtĐ+ `jqK0-$k=ŃD"@FA؇@O@EaWy4G@6z= :yU%Du!WA >3+aꙙِ D ȟN((Cρ(F"P6&c{w]΄؝% ("@]dM eWz!~LM/4vnMg%30|F'RD=;x~hzVo=Iʼn CA̲>bК? ξJ&M`$D"@BЗQ4)+_ ǪD)mLD"@I3@z" Hv*N@OD"@ D@DgBXg`?W|_/%D  \ IVD"@h_4sN p(x>{̀`#M D>naAv5;ćMM@x>ED"@C u~ZT%󏫾Lv%k"`F`$"@ DG8,x/Mu@!`Z7"@ D5LNn'5ŕ÷ HT""@ D4F18$H>YaΓ;e!n#mP;"@ D3̴rV3%PWfӗLp "B쎆lQ~0H^- fրvv6VVVag!@ Z jb^MT9]Nm6}p9HֶS\QlSi+I#Է'#Wt5+6@] PE{q%s/觠G ڀ8t 8npwK kq韄aPCM? n_>R/=zPpgàr6UVPo`p;<Z 55Ж"> }44=4m]ѠQ /;IɒSܘBm0t =(I>Q_> Q` q É ~+5=Wspz/D8O|gt.VYAׂN2K-`8.2hu8bP+HT !^΅$h3&q2zPցߏ>8"~ZbTd 뺖:QG@2XX"j/[# \`"p'w<CKBVdU ^{.1!&U!!`#f_Xf33c{f.j 0 '銢@ %ɍŒ~T-aP%"D i#ce f`R:lphag3)"> Eo }ZIh苠wT&9w4ApPvV4Jlc>`G@yK.՚Hl@kbl\ʼn~ ' cwu<]EvE-W/Ɔƥ`X­^&@];[_k4NN[: cr},rW ;EA UjHps/W$?V:33pf*БuB)JN(MFh4̝:=/ 0Ea8qC$,ʠCf ϓUȒKef (2/\M_ka@+*H,lbi@ˠA'@Io|L$K -p*gi:(0]US|0N\Ğs\Ƭ1<>nU>CdQ fDfNv:tcJkmho ɇґp|@{1.% `RՕAu׹lUUS&d\(^Ľz ] H|[ 4p@g@2X'}3SDflO+˵}Q$iA7U!:@7uCV2 $`M=] ȪW8r,eeڗR77*Ss<69՗2Wj 딻SII}=rS5!=&t*eZ7S@_#Lw0EٮAPx1׮k wdc@AxQM܉6rVvϒ%M`jPH6NeQamjpA{"̵gM[:䛆z7Zge,(҆NZ#GVóW<휇ޅ=kʬs7.\8 G  &؉u9ꫭwZ]{GyI8ޞL~Cxȷ:˻)ehĮ^AWۊNMJj&;_6&`P{k_+CQU6l{V9+`ƈ#veϣ?kཕ#e1jŔUմ{cyg1ȳ]N^@ҧǾٝBP%/> pF~J7}]f5uVDǠ=зaۨt@ry  R؏VZe2ܩNm A'굒ђ-E1]>j>f-p6tӰw])#ܤ$,%|!)ՇzB:R~MS@F0M|DRWcoʠiy}]4_<t_@ɞ-æNWal/r6C(C؏.Wki Z=;ߖ EKVzQ\DZ2~ztJ=h cS=.g;jŨʋzJ6 8KK_/s ZMW@_cf .Z~D`ub*H!68jimaN-̲by1.~ڏT=B`V$HxK=+~D|-NU[v)kͧTASf>M>[22|6:[Zg?gYBwtTIY*(nX 4I2kXꃠ:U-pޝ~%Sg4v6 =<^Mciu40d(.7l܃5}QF^biR EleuِPц>ÆPͿ=ki2`0kH&sOf00"u` |{ز\+û%ejZ._:#t(sހX@HDY<@FєkKTf]s?(kd-oW%_ A+BWoD[Q{jɦ7o>8Gzt6[]hu&rMqj laSBWEˬ `NѣԚ<5F(yT@ٕ(k䝯e~f{ /"P䫊[ҁ#]g,uO_}=G~fz'Wac! bMd"Z$u̽vQҫXwdbB߭qp~;,8X#.]ez% جgrL/Mz\/ﱞu/"c2ڣ,345.4QBM1 #lڴsAq$hit(^%kZnQZfX .);<fF*v@sA:%/"߶% > ,ņ!O"Vu0h#׼ݠ$u3pT/y-n}O3٤rl@tp/ S`%-_';m3O' Jx"K ߍ?$!)pE Ҩ0x<7ϋYFOն:=/:T 2h0zFGL3 rפ \f9Bh:g( RFS+(xY+ya3s/S?w_=j~!U9Ąb S}t=**{+7!$d& ZB sd90~\9@@qx6||~ѥ0j#+0K6*2ˈǯ KӀEvNcFI$˷o}t4m/5B?{38 th2T 3#nFHc&LP;Ҋu}r {>༣E8hQ9u}\(xG_#t9̢}leUR,(gf~cqv I$I_,#`49 ߓ XmU/u Wvr^bˡjA6 9VCAb4VZ *d;Rװ#/گ4 j{]ojrGpm h6ncњ})@=m|hTlpDЂ(ڌONJ] sK8xƁƎhL}mցNAgdmwA?=z' Br^; H~GMذ :( [ɐL1%uT4ZͤԳ1P- *H?xo8om仺u$\[A4MPiqmlvუ)rȃ==3gS}he28dzۃ!U¡6U~! b{tHR=mq] U%8Ue56XghyZ+@wAֈT<7vI8i<k[[xҙzc$`v@V;XY[rJJpMu#RI3*w^7 d ME({}ke_#x#96hl#u~Z W*k30췖.)\gǴR.j6!P:NIi&J>\ebU DB_b*W8z~:ІtJ]%YPԘ.».KG.1 y'D.KM22+G̰܈"교=5י2h!~˱꾖z~KdRfm.A]L@ -O@y͂f<-8צ, ^{2v-<Ǿv d[֓iT{o#'.lJz >؃>4HR7 \svp2MAv)RA/h OZG`c͟j];/p9 !;vFM=yx]oУz$ژLuo`}}Qoyֿ݈ 5DY*vKlZh;` Q8 { '-|+dAN#V-<; G{jӑ<U?eg4pš]Wᤙh*I};nMY{ "pnߘJ"Ͳ6:F]K>i'rkts# r:AKc5Vϱ `&ySaGz}q=?=]h䜵8xT` L#} P9Y0$A #CY*V֜8 jH68I+obs &i'XwZLiTOƬA=Qu5;_ ;@cʦ26S]F_Cz}E-ww+aծ[7ϰ+349dftCM,1Q^:ю,Ji+.q{e=a8՛^ aO}9BwF)@qb*VnxT0.zx[\+#wF+t)rֵ,oumJVNТyFE Ztje"x]WCd*)'Vur] 8}IA TH(CsUMvɾ ^<5Nj̴ZhOYIõ=t \9T&X'c&NY_bg+cw;tbt~Q= g87Z|Â'.}}jROGzp>Kv  id5s!:st0r̶"+?Փbt~D["d<-mrыU[P'.sëtfZ1or{ՎC)ۗњ2%x=2yWֺ\Jx{rL3}ͨBo1f,tjw6jUf]zp#|61jdzA=Qxv_#vR\USAL@a 7̤l7$Ƽd0nдsX{wƟg:[0BH}]7Tg[uO e";KSEQy|0ϘmH5e4ius_z-҇S_O"~mf]uG [lpQBǽ 5岓4)#ܗ=z$yJEֵ+fߌtj-"z慖Zv-dPx+FeB_gr_?9FP'ujܿPf^;g)mekx"w~!I's2U҇;}س$t^+!mR$/85ƮOc;P]FR;"]C-c}9H7;)F:Aa1ֲi7&]̾uI%I_?kxt)fٌ'̝ fd2߂Y"y; c֋7F_{ gV/'t|6q`t9a}k\fo\~oHĽ0y=ٞcYkhspF iCдL&󒢥E ɣ]hAv}L0t/YUO%}ץ5I~OVͶ.Ɇ7!+p5r̫/x.+q3<65п02pp&5-9պЇ( FԚ71)9kv3z,%W+_gEǗxsOJ5HpGOv`Iez&9 mg6Mvkt&_a^d>E琢]d-k]TTLmΘ ̾qX~\4x MdG$]"]np\v>b=zP7486"qOi~~N}v-Mph E-_v<=9M4k~fuIa,| da}o~!0YZY>¯Y6c#JuV \'P7λJ??@S%ݔL?s]5hSrhD@}M|͓r%Wn0y\ɐ9qiͪ- tو{0U WakY˟leFi!8~І0B@/ĂS8 .wBVG5I]JŬِ C78wZ!,=}rhSx?luV&uϡ]! g[TWeM y{W6~P!L}Z1ZԢpM+F]y;&)N_;%r/cEN9 dntWf$ߝE"fR]02:X&_Z`9JP09iw9V=Ze5Ap@u_!49Vd驥L-9d7|?k}z+c[}yynCUC̡ņXa#![iQd5tƆ\0x /Rmt:1yr`}4ewje;}="z ?@kt*vDӏ9$r;rouQol<^ZpN PA`w )iBNE} \0nߐʶLAsx'L_ Z: lK,Y7 /w74&m[tg;S[dBd͍T /i0 Z^(=)&vuS `)m:^"}qk֣u}6b,h%%@XʢF䬴If)*,,)3t3X7ۣV=kbs0J )5Zc#E,# oYh6Ӗ<TZaCkF@yx6MwjXXzKQLC{"`k>7%݋;Mk|>Zpz#zMkY&´G#lU3gw"*M/r%`)>>fc.B~#qb]b;I# $@@B@+k,8ւ+cd/pL򬵡X r m߀6*ҙG .8RwWaqp p=1S%jQI FK@ t׸mϨC5zYs (xգ=er.\)l, 8c⻔aiqI&^<:?};/SbPn @9XKɩ̧ >F~?K2@O(2VKk%k H-)U#]SqʓVE!rsI[}dm~δƱR:.:8Ɉ_?2`eG\FMdA;Xs?Z3;!* (YF 1rC\ ~wʩu;eź(wr?\]gXiv傯XP SB͉)׌c1Y4<`9M7[xGܮlA=7wlCqyMŮ9:qu)j=sTqׂ3]OF4_eBu ]񵾥M%`A_@9g7=#_ܙEGzP#лkhA!HR;OI֩!Dt'=S5dZEK]۬hɟ*00g֢*=RG_tZźL  hc9)XL>S6S 8\TMQIfE?}A@WO‚;h[O5v7`$!RL$٣LO Ϻh,pv 7d}˟Tz.:FLִ>"V6cQ%!y8Iu;>\1<>kj+ Q?}@`2]4AM#7 9 6y˯m_GFѨApbHAٸ]qM)cZ3H!uSxtٌ|љ95N0 zN& θ$L>(O2B_5iďk++v.# q)mZ*o~?Z78ʨnʴ Fc&XyvڮF˒ۚkֳ4g`ʿVtW99k-gދGFusQ4v0xRVy c)-KB0^)a8(0Zyj ٠I-7"]vͱ*HE<`FY,*J%'a}9a8U}}, 6}N_h :!f.djG.V$ST$g%,JhX,%& 3U{lB{ ǐeNR7a8E5d\|.,%N=khLQ0zKg{a/hΜLoﺣ߅,4(j]D׉[I_U^{tnk/0_$\#A[$ iB7>j\>:196XvTQGO?p?/IҶ'3o uMFbm\xJ{Q#w:]T ]=`vmjʤ N]8HQh_ Glb*"^&b链]O>+k\ѪΔ[D h(:ojƎk$'.x$I +ZQ4[-M+w6`f8,EYv`+n$0n]I"ּ˾nY:9U'<x%Eu:WAl/~nԽZPl+c@T<+|elՕA"ѹׂאboWlj SsTh1 : jq׍rFPjoqf7[A=sʃ2\qVW,_g[+M{zx p=ŔrY߆(#{e^^̸#0zS'MRF[phA%}/Ȳ#ec͝WّfP RH{ rljEK #‚evU&msS^z`s4kdYWσh!ou, ጀAAy;ʌNRgW]gVONU* Yol_Xʊދ:/Í>#r;:<|k#/]~&Hslh)mq%v?BouRĺtz@vydIfx!xPw|=s 3݅ L=g쵳Gt ^iճ_k`}8q`; sNaEQ@F{=ȣbNog^.B ~=O~[ ^Dplo Mホo񚲃%-qPG I50*ϫf>ܛxN w5?pEMlaf1d28E;QuA u4I0|<ta5j(4jֲ3ݐ0\5Fcλ<d@N>\C'}":*3=b@*ka>l+hWW^ ZÍS 0Un*tm'*Iib KC 30R${Ef[gީ!oYWhic'h?x1mE~x9Q#f yi lL#zְ|fX$_7TUq0G~zӖEF p"LyKDMlu~,sivѵ<]mV zuWNǟԻ@ Wua6ܽ#lǠ]{$k[϶$5$uKm~"k+}DCa_5Ldzi8Y*BVfK7Srö(rOZ[\PXsB͐N̬ދֵiB?.Ui=Hz00е9/R4(y8Zɽ#}S]ge Ԛ<~ Bv[ $EnI\+Bie0(j7Mk9u\;c1i:8_!.ߑm+{>pw'6t`zV  ;c&wZYSU`ApHf%Fgsv{7+iۖʫo;޶f#ڊ󹭷m[9 \ױ¤XZ6݆s.iD_kyCמdT _7lv-g 7UhuXWa:'bJnweJ2%+wf]>7\akvN8C+!X \ErJJ+Fuf UMCkW3f4EN],rVO74׌hb3$,-),Xw&kSTW>8R,̕iBDS Ad!bٵe^绩D); WgldMm_4o# ;5AVE']٤U.8zb1%YV"] ذ/LRtNkt!- Khvcgʰ0,\暑E°  Cf\lP YkC~׆,MFKXVCßFA.fL=aK6$hzF6W\a M(R|;&m#~b._& PApUkX 3 ֜% Leu#_򺭟7A~*sQ x]/N.U9>?fV13S* Ыx/6Q5tpF8Z32 k_TJqŋK`AmA:ȋf5}]C"ᧇNhꇁ޽ސq٨^W^Dg@E"╠ݜ6s*Ly-Xukm;:b/25<]ZaM Ù\:yW³籯Zj;aHbi]=|-"wBZzO6`/]%/ CS3I#R|DxN{*O{x=.ԙz5fPoQGwnfMxpb:~K0̷(&xd+>-K!m/=86 ytg"r۝N_ l57?%셙8 :3#7C79QʿuSM-9y>Uf`Yz#_v9e8 IȢ«r 3h3xb4p 18QbF@eΔ}s+87WBhSM>]3knȪ+̂u;e/X>,AzqHy'<# /2=~x0CeUboK{뚥?_Z( }O:[u_uaU?,'qk0atF \8!@HsnV>An“ 0 /7gInkz\_ _n_^Mll']E_.=A*dVFm?,5q255U4f=Z ୘UcF7- 濡Ll$wcU6wXb͕m-;6LܘbKW!̾,k凮pKj(@|>k]xx{)ͪng=6&N,5F0S5O&̍O >2æ:ꁫKZ̦qw F./.v sm.;a5Ddy }LbzA P~: &^,du~ڝ5ryӰfYХVYBZUF :,֏_x`3StdmG\vٙV8K-gjf:叏` lLijv>gtmKJm7| 6.}`c$>?"Y2{Xr7)ۺEmh5,[tpƵhn]ڬ?:12neƵS`vLcWam&Lt0h<1ҏio^QyA*6 Z5u` HI`=d4p,2vսepdŹ=S=_ % {cwee<Ld"P`)i/A*?i 8*`g}2X8Lw<|lc~uQtw>xw=@=-( }*lC̺5n\<\#`Һ/3=m<'BvrSc@e*;a CUgԇte/.;U&VDtj5ޮȒ 94ߣV+',8)N]o<@u.9z%(vR}[iY`( /E@:QٌM濅Z1.@ *#/.Z+lܟRMT=tu8Cio:@tPIuMOi>KٗK{Rڳ<(e=#.P>X)_X9nu:S=$^̼*U2'/HgQDO]i,tE9 #;Hf]<= ?r )YkL_0 <G:͆SH 8 A{ yY~׭gc(^|6lЭ=`<׀bk΄Ҕ$XF I ?lZXzaѸ'J<%h)%@ V)TT@߁p\D+ٕ= ؕ=W(_93 ]}$\^34UKgE*i? "+m/˸]əQ~sJ ٠@9m20BdRl¯a+hA Я@OԯNUouq:hлA@ǀdWC^E,:7߁~Z wJҡ$;A}{{[WݍpTɭ<vл+Q%kNj:&P=2*X3]^)sc<ݏO_% Ay@:̃"ӝ (3( 8cV7o?{P~p|l|Ҡ^-zNٛoS3%ؒ I0P큇mJ}#ݪk@ z ,}Vu(uk "@PAs \Fv,I, }L\OąAjJYV9_d{'HvݥBC9$ D"< \SHWA+@ :BC@%/K&/XIl"&ڔE D\/{`zYt'>(bzޠD"@ >" Aξ OQO"@ D>5` NDED D"9Zes#"@`.Y"@ Dn,48WAU D"@@?Z%xe!D \-LD"@ AIXrVy=  `j h!D"@`\`è. D"@@.Eк+JJג7O D"0 OA D 0X+"@ Dտ@MDo?jO D"K`{uؓ+ I`pX"@ D |FȠ W_@X& D"@@xlIfWE*/"P4"@ " Qc!Dd0p-i. D"@F@v~$AO P݅O"@Jג7O D܇^B*,D^0p"@ D@ ,_5»I6|_ `u>Y ` &V"D"@@,2ЯA]BԶ"@R 5h"@ Ud)A"@݃O"@0W` D"P;An(4"@ D"@th (mن tN D"@ EAgu'h|iD"@ D"`f("@ D"@J@%="@ D"@@Qo]("@ D"@ p݅("@ D"@ !$p]sM1 &D"@ D" 4\W,"D D"@ D wqD"@ D"@>|Md D"@ D8@u?3Q"@ D"@ @ D"@ DOssQ!"@ D"@ XG%D"@ D"@ DcxnIENDB`ejabberd-18.01/priv/img/powered-by-erlang.png0000644000232200023220000000156013225664356021432 0ustar debalancedebalancePNG  IHDReh7IDAThZ?LQ1ES$Bbbr ڤ0i"MHRVցX u6ݨƄ.EkIS:8o:~.w߻q ( I`ARV1$1 :8==nQ*#PQ* zўo(ɺa1 otԄw8JĢ evs60&}q@b+D?Jm6RkF)yIQ~:. v"mse]+3qb чĢE<# Q kxهu(gL,XQ/m-rp'.e%sJ}]˫*e ."9e[}7pvA)+yRV-fu(*rH\Io)8Fh/ o iwӚ]z nZ`DXLV+9N4K&݂]/M~㓦`0 !,&t+kd5YJL)$[-Qu;%+kpOB wjǠS SԮPogF}Jֺ|#,[|?,.* R/w0T:eOa7b1F!ͷJ~u(_p9uIENDB`ejabberd-18.01/priv/img/admin-logo-fill.png0000644000232200023220000000026113225664356021056 0ustar debalancedebalancePNG  IHDR7&ssRGBPLTEv*Jjʌ֦AIDATeK A>$G,` Xp <]]P+Jl %DyPP:RzfIENDB`ejabberd-18.01/priv/img/oauth-logo.png0000644000232200023220000001275713225664356020177 0ustar debalancedebalancePNG  IHDR,@d-O`ۓ,X)Ddj !# TS Pukbe b]䄄KdSЍToYK惵[%筇DRlvVdʛQ ?,i(V TP+V9rE62ܓ&62,DEK] R0X76A`SQ˂쨂 ݏz31` y{rAW0W`PFrsw`P)n웸gSEAh. gcXKE7YF!cCM1[ID(C8Ep`ŗ\(/`aa??E4npXOWw(Ȇ>Oj>Lopje`Pkk+,?ˉHLeuSJM` X1rjZN&&^܌sfkx'@UhQ `= 5O3(R 4| -8GL3'fQ'rqU~؁-sghGo 5:G_89}㬍lJ :Xт iהGf@+`9Zvm:#XUAg)|Ly肵%w\Y h)8 5%MODo:>w:~mnvY*,f6W,lY&z}D`uSTlƙo?UG؞?6SQ~ҎU/i%O߿!X|GEAW8۠nbOnE9p({( B/6p}qK傕Z [(8 CN5i﮸y2=ЦLJ;XaA7(yjU6A-XGU(Wrh`徧 P CK\JC3QzJpDP{a%vUU=hI\0 kP_ªag V?"N ;BGeCv֮mЬg үò΃U5mue!DH qf,6̑ž UvZ\d2Z?UL)tlIDee7is`QhyFe`f2ahcw=Tdv^;V֛ 3aZ޽J&:Hst5J|Ngȗ 4GY)^fr.^ߥ RlfP<`Ou_A>'Hw@dSI:ݜ+$f^8g 2?Ǟe*`BU :).g* D^2m Ka?|{K`{RU~m!9Rj(!(+pG/s6X+?8jw0_{O*L6SpbF*,@6bi+beh{p9ME:X[`m V/U2)*:ڝ&*L10fxR&X:"~(s,%$O --A-UQCˑEU$u6).Qi/*(XÞ'ՀiU7 -}JЏxk2 D6!}O *Yg-4ڋ",4mZ!kB#J*~؃ðZ/?Zh=E+AZ*6CY(U ]m(Rz{6x;RfO˙ o#<-8"9IcġbxXVؿ&Q.X"N`Pcp_Wlk:8D|A\(fHT@{ ?GıS.mM\>k/CL o(Sq'ɇ*@>wwqJ5eb~]MNpS&3,{J sUyg 8cWùT$~ 57"bJ+[9gvLvyUy`>X5!₿^⽺ `yX-;) h@ qV q#i(;f@*1l9Abރk(o&dEL/[xXOs}f̣U!}b,[(z#Xb?tqQUĄjG!؍gOr]Ȥ V&6O{* Wۉ >~%x=G^1 V31,s2&Q=r#&{3D hO\eU0 (VX]oqqᡳ( B%[%=쀌nJ0,|@e1;Je" c#>h[kfnkloox$ ,& ub$>rg= fچ?P loо¦(vj_VbaPATX[!'k. rF*oS.y:ٯñr!Cpb\qt;l%̦*}ByĂsCpv( ڪ4 lWaQ'A,3=F ܳ3&× *o`fX Y`-_oj*5b~Lq%X tTq$[p/0M\ı@yrX v6)>y4 ~Lߓ%OׅjŅԲ`8_A o[Nx[. .e.YJQ;ȀVsNQ.vU9xv1LʼnR "~~yzqXB O;UN@ljwհpxJ b3y jpVھb- .section { background: #424A55; } .block { margin: 0 auto; max-width: 900px; width: 100%; } ejabberd-18.01/priv/css/register.css0000644000232200023220000000164613225664356017760 0ustar debalancedebalance@viewport { width: device-width; zoom: 1.0; } html,body { font-family: sans-serif; background: white; padding: 0.5em; margin: auto; max-width: 800px; height: 100%; } form { padding: 0.5em 0; } ul { list-style: none; } ul > li { margin-bottom: 2em; } ol { list-style: none; padding: 0; } ol > li { margin-bottom: 2em; font-weight: bold; font-size: 0.75em; } ol > li > ul { list-style: decimal; font-weight: normal; font-style: italic; } ol > li > ul > li { margin-bottom: auto; } input { display: block; padding: 0.25em; font-size: 1.5em; border: 1px solid #ccc; border-radius: 0; -webkit-appearance: none; -moz-appearance: none; } input:focus { border-color: #428bca; } input[type=submit] { padding: 0.33em 1em; background-color: #428bca; border-radius: 2px; cursor: pointer; border: none; color: #fff; } ejabberd-18.01/priv/css/bosh.css0000644000232200023220000000155713225664356017070 0ustar debalancedebalancebody { margin: 0; padding: 0; font-family: sans-serif; color: #fff; } h1 { font-size: 3em; color: #444; } p { line-height: 1.5em; color: #888; } a { color: #fff; } a:hover, a:active { text-decoration: underline; } .container { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #424A55; background-image: -webkit-linear-gradient(270deg, rgba(48,52,62,0) 24%, #30353e 100%); background-image: linear-gradient(-180deg, rgba(48,52,62,0) 24%, #30353e 100%); } .section { padding: 3em; } .white.section { background: #fff; border-bottom: 4px solid #41AFCA; } .white.section a { text-decoration: none; color: #41AFCA; } .white.section a:hover, .white.section a:active { text-decoration: underline; } .block { margin: 0 auto; max-width: 900px; width: 100%; } ejabberd-18.01/priv/css/admin.css0000644000232200023220000001047313225664356017222 0ustar debalancedebalancehtml,body { margin: 0; padding: 0; height: 100%; background: #f9f9f9; font-family: sans-serif; } body { min-width: 990px; } a { text-decoration: none; color: #3eaffa; } a:hover, a:active { text-decoration: underline; } #container { position: relative; padding: 0; margin: 0 auto; max-width: 1280px; min-height: 100%; height: 100%; margin-bottom: -30px; z-index: 1; } html>body #container { height: auto; } #header h1 { width: 100%; height: 50px; padding: 0; margin: 0; background-color: #49cbc1; } #header h1 a { position: absolute; top: 0; left: 0; width: 100%; height: 50px; padding: 0; margin: 0; background: url('@BASE@logo.png') 10px center no-repeat transparent; background-size: auto 25px; display: block; text-indent: -9999px; } #clearcopyright { display: block; width: 100%; height: 30px; } #copyrightouter { position: relative; display: table; width: 100%; height: 30px; z-index: 2; } #copyright { display: table-cell; vertical-align: bottom; width: 100%; height: 30px; } #copyright a { font-weight: bold; color: #fff; } #copyright p { margin-left: 0; margin-right: 0; margin-top: 5px; margin-bottom: 0; padding-left: 0; padding-right: 0; padding-top: 5px; padding-bottom: 5px; width: 100%; color: #fff; background-color: #30353E; font-size: 0.75em; text-align: center; } #navigation { display: inline-block; vertical-align: top; width: 30%; } #navigation ul { padding: 0; margin: 0; width: 90%; background: #fff; } #navigation ul li { list-style: none; margin: 0; border-bottom: 1px solid #f9f9f9; text-align: left; } #navigation ul li a { margin: 0; display: inline-block; padding: 10px; color: #333; } ul li #navhead a, ul li #navheadsub a, ul li #navheadsubsub a { font-size: 1.5em; color: inherit; } #navitemsub { border-left: 0.5em solid #424a55; } #navitemsubsub { border-left: 2em solid #424a55; } #navheadsub, #navheadsubsub { padding-left: 0.5em; } #navhead, #navheadsub, #navheadsubsub { border-top: 3px solid #49cbc1; background: #424a55; color: #fff; } #lastactivity li { padding: 2px; margin-bottom: -1px; } thead tr td { background: #3eaffa; color: #fff; } thead tr td a { color: #fff; } td.copy { text-align: center; } tr.head { color: #fff; background-color: #3b547a; text-align: center; } tr.oddraw { color: #412c75; background-color: #ccd4df; text-align: center; } tr.evenraw { color: #412c75; background-color: #dbe0e8; text-align: center; } td.leftheader { color: #412c75; background-color: #ccccc1; padding-left: 5px; padding-top: 2px; padding-bottom: 2px; margin-top: 0px; margin-bottom: 0px; } td.leftcontent { color: #000044; background-color: #e6e6df; padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; margin-top: 0px; margin-bottom: 0px; } td.rightcontent { color: #000044; text-align: justify; padding-left: 10px; padding-right: 10px; padding-bottom: 5px; } h1 { color: #000044; padding-top: 2px; padding-bottom: 2px; margin-top: 0px; margin-bottom: 0px; } h2 { color: #000044; text-align: center; padding-top: 2px; padding-bottom: 2px; margin-top: 0px; margin-bottom: 0px; } h3 { color: #000044; text-align: left; padding-top: 20px; padding-bottom: 2px; margin-top: 0px; margin-bottom: 0px; } #content ul { padding-left: 1.1em; margin-top: 1em; } #content ul li { list-style-type: disc; padding: 5px; } #content ul.nolistyle>li { list-style-type: none; } #content { display: inline-block; vertical-align: top; padding-top: 25px; width: 70%; } div.guidelink, p[dir=ltr] { display: inline-block; float: right; margin: 0; margin-right: 1em; } div.guidelink a, p[dir=ltr] a { display: inline-block; border-radius: 3px; padding: 3px; background: #3eaffa; text-transform: uppercase; font-size: 0.75em; color: #fff; } table { margin-top: 1em; } table tr td { padding: 0.5em; } table tr:nth-child(odd) { background: #fff; } table.withtextareas>tbody>tr>td { vertical-align: top; } textarea { margin-bottom: 1em; } input, select { font-size: 1em; } p.result { border: 1px; border-style: dashed; border-color: #FE8A02; padding: 1em; margin-right: 1em; background: #FFE3C9; } *.alignright { text-align: right; } ejabberd-18.01/priv/js/0000755000232200023220000000000013225664356015237 5ustar debalancedebalanceejabberd-18.01/priv/js/admin.js0000644000232200023220000000055013225664356016665 0ustar debalancedebalance function selectAll() { for(i=0;i element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Không được phép gửi những thư riêng đến phòng họp" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 #, fuzzy msgid "It is not allowed to send private messages" msgstr "Không được phép gửi những thư riêng đến phòng họp" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Không được phép gửi những thư riêng loại \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Không được phép gửi những thư riêng đến phòng họp" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Tháng Một" #: mod_irc:665 msgid "Join IRC channel" msgstr "" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "" #: mod_muc_log:479 msgid "July" msgstr "Tháng Bảy" #: mod_muc_log:478 msgid "June" msgstr "Tháng Sáu" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Hoạt Động Cuối Cùng" #: mod_configure:1646 msgid "Last login" msgstr "Đăng nhập lần cuối" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Tháng trước" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Năm trước" #: mod_configure:941 msgid "List of modules to start" msgstr "Danh sách các môđun khởi động" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Cổng Kết Nối" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Cổng Liên Lạc tại" #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Lệnh cập nhật mức độ thấp" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Tạo danh sách người tham dự công khai" #: mod_muc_log:1062 #, fuzzy msgid "Make room CAPTCHA protected" msgstr "Tạo phòng được bảo vệ bằng mật khẩu" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Tạo phòng chỉ cho phép tư cách thành viên tham gia" #: mod_muc_log:1042 #, fuzzy msgid "Make room moderated" msgstr "Tạo phòng bền vững" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Tạo phòng được bảo vệ bằng mật khẩu" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Tạo phòng bền vững" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Tạo phòng có thể tìm kiếm công khai" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Tên truy cập IRC" #: mod_muc_log:475 msgid "March" msgstr "Tháng Ba" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Số Lượng Người Tham Dự Tối Đa" #: mod_muc_log:477 msgid "May" msgstr "Tháng Năm" #: mod_shared_roster:907 msgid "Members:" msgstr "Thành viên:" #: mod_muc_room:1833 #, fuzzy msgid "Membership is required to enter this room" msgstr "Yêu cầu tư cách thành viên khi tham gia vào phòng này" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Bộ Nhớ" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Thân thư" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Họ Đệm" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Yêu cầu đặc quyền của nhà điều phối" #: ejabberd_web_admin:2279 #, fuzzy msgid "Modified modules" msgstr "Môđun cập nhật" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Môđun" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Môđun" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Môđun tại " #: mod_muc_log:463 msgid "Monday" msgstr "Thứ Hai" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Tên" #: mod_shared_roster:896 msgid "Name:" msgstr "Tên:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Không bao giờ" #: mod_register_web:377 #, fuzzy msgid "New Password:" msgstr "Mật Khẩu:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Bí danh" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Đăng Ký Bí Danh tại" #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Bí danh ~s không tồn tại trong phòng này" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Nút không tìm thấy" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Không Dữ Liệu" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Không có nội dung trong thư thông báo" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Nút không tìm thấy" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Nút không tìm thấy" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Nút không tìm thấy" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nút không tìm thấy" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Nút " #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nút" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Không có" #: ejabberd_web_admin:1033 #, fuzzy msgid "Not Found" msgstr "Nút không tìm thấy" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "Tháng Mười Một" #: mod_configure:1212 msgid "Number of online users" msgstr "Số người sử dụng trực tuyến" #: mod_configure:1202 msgid "Number of registered users" msgstr "Số người sử dụng đã đăng ký" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Tháng Mười" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Thư Ngoại Tuyến" #: mod_offline:761 msgid "Offline Messages:" msgstr "Thư Ngoại Tuyến:" #: mod_register_web:373 #, fuzzy msgid "Old Password:" msgstr "Mật Khẩu:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Trực tuyến" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Người Sử Dụng Trực Tuyến" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Người Sử Dụng Trực Tuyến:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Chỉ có những người điều phối được phép thay đổi chủ đề trong phòng này" #: mod_muc_room:773 #, fuzzy msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Chỉ có những người điều phối và những người tham gia được phép thay đổi chủ " "đề trong phòng này" #: mod_muc_room:778 #, fuzzy msgid "Only moderators are allowed to change the subject in this room" msgstr "Chỉ có những người điều phối được phép thay đổi chủ đề trong phòng này" #: mod_muc_room:917 #, fuzzy msgid "Only moderators can approve voice requests" msgstr "Cho phép người sử dụng gửi lời mời" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Chỉ có những đối tượng tham gia mới được phép gửi thư đến phòng họp" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "" "Chỉ có những đối tượng tham gia mới được phép gửi yêu cầu đến phòng họp" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Chỉ có người quản trị dịch vụ mới được phép gửi những thư dịch vụ" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Tùy chọn" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Tên Tổ Chức" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Bộ Phận" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Kết Nối Bên Ngoài s2s" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Kết Nối Bên Ngoài s2s:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Yêu cầu đặc quyền của người sở hữu" #: mod_offline:693 msgid "Packet" msgstr "Gói thông tin" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Mật Khẩu" #: mod_configure:1130 msgid "Password Verification" msgstr "Kiểm Tra Mật Khẩu" #: mod_register_web:268 mod_register_web:381 #, fuzzy msgid "Password Verification:" msgstr "Kiểm Tra Mật Khẩu" #: mod_irc:822 #, fuzzy msgid "Password ~b" msgstr "Mật Khẩu" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Mật Khẩu:" #: mod_configure:998 msgid "Path to Dir" msgstr "Đường Dẫn đến Thư Mục" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Đường dẫn đến Tập Tin" #: mod_roster:915 msgid "Pending" msgstr "Chờ" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Giai đoạn: " #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "rời khỏi phòng này" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 #, fuzzy msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Lưu ý rằng những tùy chọn này sẽ chỉ được sao lưu cơ sở dữ liệu bên trong " "Mnesia. Nếu bạn đang sử dụng môđun ODBC, bạn cũng cần sao lưu cơ sở dữ liệu " "SQL của bạn riêng biệt." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Cổng" #: mod_irc:828 #, fuzzy msgid "Port ~b" msgstr "Cổng" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 #, fuzzy msgid "Protocol" msgstr "Cổng" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Yêu cầu người đăng ký môđun Xuất Bản Đăng Ký" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Xuất Bản-Đăng Ký" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Không được phép gửi các yêu cầu gửi đến các thành viên trong phòng họp này" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Sao chép vào RAM và đĩa" #: mod_configure:889 msgid "RAM copy" msgstr "Sao chép vào RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Lỗi Gọi RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Thô" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Có thực sự xóa thư trong ngày này không?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Người nhận không có trong phòng họp" #: mod_register_web:275 #, fuzzy msgid "Register" msgstr "Bảng phân công" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Người Sử Dụng Đã Đăng Ký" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Người Sử Dụng Đã Đăng Ký:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Người Sử Dụng Đã Đăng Ký" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Đăng ký trong mod_irc cho " #: mod_configure:889 msgid "Remote copy" msgstr "Sao chép từ xa" #: mod_roster:963 msgid "Remove" msgstr "Gỡ bỏ" #: mod_offline:765 #, fuzzy msgid "Remove All Offline Messages" msgstr "Thư Ngoại Tuyến" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Gỡ Bỏ Người Sử Dụng" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Được thay thế bởi kết nối mới" #: mod_configure:1675 msgid "Resources" msgstr "Nguồn tài nguyên" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Khởi động lại" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Khởi Động Lại Dịch Vụ" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Khôi phục" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Phục hồi Sao Lưu từ Tập Tin tại " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Khôi phục bản sao lưu dự phòng dạng nhị phân sau lần khởi động ejabberd kế " "tiếp (yêu cầu ít bộ nhớ hơn):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Khôi phục bản sao lưu dự phòng dạng nhị phận ngay lập tức:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Khôi phục bản sao lưu dự phòng thuần văn bản ngay lập tức:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Cấu Hình Phòng" #: mod_muc_log:892 #, fuzzy msgid "Room Occupants" msgstr "Số người tham dự" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Việc tạo phòng bị ngăn lại theo chính sách dịch vụ" #: mod_muc_log:1064 #, fuzzy msgid "Room description" msgstr "Miêu tả:" #: mod_muc_log:1027 msgid "Room title" msgstr "Tên phòng" #: mod_roster:1082 msgid "Roster" msgstr "Bảng phân công" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Bảng phân công của " #: mod_configure:1671 msgid "Roster size" msgstr "Kích thước bảng phân công" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nút Hoạt Động" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Thứ Bảy" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Lệnh kiểm tra" #: mod_vcard:453 msgid "Search Results for " msgstr "Kết Quả Tìm Kiếm cho " #: mod_vcard:427 msgid "Search users in " msgstr "Tìm kiếm người sử dụng trong" #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Gửi thông báo đến tất cả người sử dụng trực tuyến" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" "Gửi thông báo đến tất cả người sử dụng trực tuyến trên tất cả các máy chủ" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Gửi thông báo đến tất cả người sử dụng" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Gửi thông báo đến tất cả người sử dụng trên tất cả các máy chủ" #: mod_muc_log:481 msgid "September" msgstr "Tháng Chín" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 #, fuzzy msgid "Server:" msgstr "Không bao giờ" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Tạo lập thư trong ngày và gửi đến những người sử dụng trực tuyến" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Tạo lập thư trong ngày trên tất cả các máy chủ và gửi đến những người sử " "dụng trực tuyến" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Nhóm Phân Công Chia Sẻ" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Hiển Thị Bảng Đầy Đủ" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Hiển Thị Bảng Thường" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Tắt Dịch Vụ" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Khởi động" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Môđun Khởi Động" #: mod_configure:936 msgid "Start Modules at " msgstr "Môđun Khởi Động tại " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Số liệu thống kê" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Thống kê về ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Dừng" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Môđun Dừng" #: mod_configure:918 msgid "Stop Modules at " msgstr "Môđun Dừng tại" #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nút Dừng" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Loại Lưu Trữ" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Lưu dữ liệu sao lưu dạng nhị phân:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Khôi phục bản sao lưu dự phòng thuần văn bản" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Tiêu đề" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Gửi" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Đã gửi" #: mod_roster:914 msgid "Subscription" msgstr "Đăng ký" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Chủ Nhật" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 #, fuzzy msgid "That nickname is already in use by another occupant" msgstr "Bí danh đang do một người tham dự khác sử dụng" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 #, fuzzy msgid "That nickname is registered by another person" msgstr "Một người khác đã đăng ký bí danh này rồi" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 #, fuzzy msgid "The password is too weak" msgstr "mật khẩu là" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "" #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Phòng này không nặc danh" #: mod_muc_log:466 msgid "Thursday" msgstr "Thứ Năm" #: mod_offline:690 msgid "Time" msgstr "Thời Gian" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Thời gian trì hoãn" #: mod_offline:692 msgid "To" msgstr "Đến" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Gửi đến ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Phòng trò chuyện" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Quá giới hạn tỷ lệ lưu lượng truyền tải" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Giao Dịch Hủy Bỏ:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Giao Dịch Được Cam Kết:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Giao Dịch Được Ghi Nhận:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Giao Dịch Khởi Động Lại:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Thứ Ba" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Cập Nhật" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Cập nhật thư trong ngày (không gửi)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Cập nhật thư trong ngày trên tất cả các máy chủ (không gửi)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Kế hoạch cập nhật" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Cập nhận lệnh" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Cập Nhật " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Thời gian tải lên:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Yêu cầu sử dụng STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Yêu cầu sử dụng STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Người sử dụng" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Quản Lý Người Sử Dụng" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Nút không tìm thấy" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Người sử dụng " #: mod_register_web:230 mod_register_web:366 mod_register_web:476 #, fuzzy msgid "Username:" msgstr "Tên truy cập IRC" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Người sử dụng" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Hoạt Động Cuối Cùng Của Người Sử Dụng" #: mod_register:375 #, fuzzy msgid "Users are not allowed to register accounts so quickly" msgstr "Người ghé thăm không được phép gửi thư đến tất cả các người tham dự" #: mod_roster:954 msgid "Validate" msgstr "Xác nhận hợp lệ" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Máy Chủ Ảo" #: mod_muc_room:992 #, fuzzy msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Chỉ có những người điều phối được phép thay đổi chủ đề trong phòng này" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Người ghé thăm không được phép gửi thư đến tất cả các người tham dự" #: mod_muc_room:3879 msgid "Voice request" msgstr "" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "" #: mod_muc_log:465 msgid "Wednesday" msgstr "Thứ Tư" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Bạn bị cấm tham gia phòng này" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Bạn phải điền thông tin vào ô \"Nickname\" trong biểu mẫu này" #: mod_register:222 #, fuzzy msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để đăng ký " "bí danh" #: mod_muc:731 #, fuzzy msgid "You need a client that supports x:data to register the nickname" msgstr "" "Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để đăng ký " "bí danh" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác định " "các thiết lập mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "" "Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để tìm kiếm" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Không được phép gửi những thư riêng đến phòng họp" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "" #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Danh sách chờ thư liên lạc ngoại tuyến của bạn đã đầy. Thư này đã bị loại bỏ." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Môdun ejabberd IRC Bản quyền" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Môdun ejabberd MUC Bản quyền" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Môdun ejabberd Xuất Bản-Đăng Ký Bản quyền" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Môdun SOCKS5 Bytestreams Bản quyền" #: ejabberd_web_admin:311 ejabberd_web_admin:343 #, fuzzy msgid "ejabberd Web Admin" msgstr "Giao diện Web ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Môdun ejabberd vCard Bản quyền" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "đã bị cấm" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "đã bị đẩy ra khỏi" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" #: mod_muc_log:410 msgid "is now known as" msgstr "bây giờ được biết như" #: mod_muc_log:370 msgid "joins the room" msgstr "tham gia phòng này" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "rời khỏi phòng này" #: mod_muc_room:3856 msgid "private, " msgstr "riêng," #: mod_muc_room:3955 msgid "the password is" msgstr "mật khẩu là" #: mod_vcard:292 msgid "vCard User Search" msgstr "Tìm Kiếm Người Sử Dụng vCard" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s cấu hình quy tắc truy cập" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s mời bạn vào phòng ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s's Danh Sách Chờ Thư Ngoại Tuyến" #~ msgid "No resource provided" #~ msgstr "Không có nguồn lực cung cấp" #, fuzzy #~ msgid "Server" #~ msgstr "Không bao giờ" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s không hợp lệ" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Tư cách không hợp lệ: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Vai trò không hợp lệ: ~s" #~ msgid "No limit" #~ msgstr "Không giới hạn" #~ msgid "Present real Jabber IDs to" #~ msgstr "Jabber ID thực tế hiện hành đến" #~ msgid "moderators only" #~ msgstr "nhà điều phối duy nhất" #~ msgid "anyone" #~ msgstr "bất kỳ ai" #, fuzzy #~ msgid "Moderator" #~ msgstr "nhà điều phối duy nhất" #, fuzzy #~ msgid "Allow visitors to send voice requests" #~ msgstr "Cho phép người sử dụng gửi lời mời" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác " #~ "định cấu hình phòng họp" #~ msgid "Number of occupants" #~ msgstr "Số người tham dự" #, fuzzy #~ msgid "User JID" #~ msgstr "Người sử dụng " #~ msgid "Node ID" #~ msgstr "ID Nút" #~ msgid "Subscriber Address" #~ msgstr "Địa Chỉ Người Đăng Ký" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Cho phép Jabber ID đăng ký nút môđun xuất bản đăng ký này không?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Đưa ra thông tin dung lượng với các thông báo sự kiện" #~ msgid "Deliver event notifications" #~ msgstr "Đưa ra các thông báo sự kiện" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Thông báo cho người đăng ký khi nào cấu hình nút thay đổi" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Thông báo cho người đăng ký khi nào nút bị xóa bỏ" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Thông báo cho người đăng ký khi nào các mục chọn bị gỡ bỏ khỏi nút" #~ msgid "Persist items to storage" #~ msgstr "Những mục cần để lưu trữ" #~ msgid "Max # of items to persist" #~ msgstr "Số mục tối đa để lưu trữ" #~ msgid "Whether to allow subscriptions" #~ msgstr "Xác định nên cho phép đăng ký không" #~ msgid "Specify the access model" #~ msgstr "Xác định mô hình truy cập" #~ msgid "Specify the publisher model" #~ msgstr "Xác định mô hình nhà xuất bản" #, fuzzy #~ msgid "Specify the event message type" #~ msgstr "Xác định mô hình truy cập" #~ msgid "Max payload size in bytes" #~ msgstr "Kích thước dung lượng byte tối đa" #~ msgid "When to send the last published item" #~ msgstr "Khi cần gửi mục được xuất bản cuối cùng" #~ msgid "Only deliver notifications to available users" #~ msgstr "Chỉ gửi thông báo đến những người sử dụng hiện có" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "" #~ "Điền vào các ô để tìm kiếm bất kỳ các thông tin nào khớp với Người sử " #~ "dụng Jabber" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Máy chủ Bên Ngoài s2s:" #~ msgid "Delete" #~ msgstr "Xóa" #~ msgid "Encodings" #~ msgstr "Mã hóa" #~ msgid "(Raw)" #~ msgstr "(Thô)" #~ msgid "Specified nickname is already registered" #~ msgstr "Bí danh xác định đã đăng ký rồi" #~ msgid "Size" #~ msgstr "Kích thước" #~ msgid "Roster groups that may subscribe (if access model is roster)" #~ msgstr "" #~ "Các nhóm phân công có thể đăng ký (nếu mô hình truy cập là dạng phân công)" ejabberd-18.01/priv/msgs/vi.msg0000644000232200023220000004021213225664356016721 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Cấu Hình Truy Cập"}. {"Access Control List Configuration","Cấu Hình Danh Sách Kiểm Soát Truy Cập"}. {"Access control lists","Danh sách kiểm soát truy cập"}. {"Access Control Lists","Danh Sách Kiểm Soát Truy Cập"}. {"Access denied by service policy","Sự truy cập bị chặn theo chính sách phục vụ"}. {"Access rules","Quy tắc Truy Cập"}. {"Access Rules","Quy Tắc Truy Cập"}. {"Action on user","Hành động đối với người sử dụng"}. {"Add Jabber ID","Thêm Jabber ID"}. {"Add New","Thêm Mới"}. {"Add User","Thêm Người Sử Dụng"}. {"Administration of ","Quản trị về "}. {"Administration","Quản trị"}. {"Administrator privileges required","Yêu cầu đặc quyền của nhà quản trị"}. {"All activity","Tất cả hoạt động"}. {"Allow users to query other users","Cho phép người sử dụng hỏi người sử dụng khác"}. {"Allow users to send invites","Cho phép người sử dụng gửi lời mời"}. {"Allow users to send private messages","Cho phép người sử dụng gửi thư riêng"}. {"All Users","Tất Cả Người Sử Dụng"}. {"Announcements","Thông báo"}. {"April","Tháng Tư"}. {"August","Tháng Tám"}. {"Backup Management","Quản lý Sao Lưu Dự Phòng"}. {"Backup","Sao lưu dự phòng"}. {"Backup to File at ","Sao lưu dự phòng ra Tập Tin tại"}. {"Bad format","Định dạng hỏng"}. {"Birthday","Ngày sinh"}. {"Change Password","Thay Đổi Mật Khẩu"}. {"Change User Password","Thay Đổi Mật Khẩu Người Sử Dụng"}. {"Chatroom configuration modified","Cấu hình phòng trò chuyện được chỉnh sửa"}. {"Chatrooms","Phòng trò chuyện"}. {"Choose a username and password to register with this server","Chọn một tên truy cập và mật khẩu để đăng ký với máy chủ này"}. {"Choose modules to stop","Chọn môđun để dừng"}. {"Choose storage type of tables","Chọn loại bảng lưu trữ"}. {"Choose whether to approve this entity's subscription.","Chọn có nên chấp nhận sự đăng ký của đối tượng này không"}. {"City","Thành phố"}. {"Commands","Lệnh"}. {"Conference room does not exist","Phòng họp không tồn tại"}. {"Configuration","Cấu hình"}. {"Connected Resources:","Tài Nguyên Được Kết Nối:"}. {"Country","Quốc gia"}. {"CPU Time:","Thời Gian CPU:"}. {"Database","Cơ sở dữ liệu"}. {"Database Tables Configuration at ","Cấu Hình Bảng Cơ Sở Dữ Liệu tại"}. {"December","Tháng Mười Hai"}. {"Default users as participants","Người sử dụng mặc định là người tham dự"}. {"Delete message of the day on all hosts","Xóa thư trong ngày trên tất cả các máy chủ"}. {"Delete message of the day","Xóa thư trong ngày"}. {"Delete Selected","Tùy chọn Xóa được Chọn"}. {"Delete User","Xóa Người Sử Dụng"}. {"Description:","Miêu tả:"}. {"Disc only copy","Chỉ sao chép vào đĩa"}. {"Displayed Groups:","Nhóm được hiển thị:"}. {"Dump Backup to Text File at ","Kết Xuất Sao Lưu ra Tập Tin Văn Bản tại"}. {"Dump to Text File","Kết xuất ra Tập Tin Văn Bản"}. {"Edit Properties","Chỉnh Sửa Thuộc Tính"}. {"ejabberd IRC module","Môdun ejabberd IRC Bản quyền"}. {"ejabberd MUC module","Môdun ejabberd MUC Bản quyền"}. {"ejabberd Publish-Subscribe module","Môdun ejabberd Xuất Bản-Đăng Ký Bản quyền"}. {"ejabberd SOCKS5 Bytestreams module","Môdun SOCKS5 Bytestreams Bản quyền"}. {"ejabberd vCard module","Môdun ejabberd vCard Bản quyền"}. {"Email","Email"}. {"Enable logging","Cho phép ghi nhật ký"}. {"End User Session","Kết Thúc Phiên Giao Dịch Người Sử Dụng"}. {"Enter list of {Module, [Options]}","Nhập danh sách {Môđun, [Các Tùy Chọn]}"}. {"Enter nickname you want to register","Nhập bí danh bạn muốn đăng ký"}. {"Enter path to backup file","Nhập đường dẫn đến tập tin sao lưu dự phòng"}. {"Enter path to jabberd14 spool dir","Nhập đường dẫn đến thư mục spool jabberd14"}. {"Enter path to jabberd14 spool file","Nhập đường dẫn đến tập tin spool jabberd14"}. {"Enter path to text file","Nhập đường dẫn đến tập tin văn bản"}. {"Erlang Jabber Server","Erlang Jabber Server Bản quyền"}. {"Family Name","Họ"}. {"February","Tháng Hai"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Điền vào mẫu này để tìm kiếm bất kỳ thông tin nào khớp với Người sử dụng Jabber (Thêm dấu * vào cuối ô để thông tin khớp với chuỗi bên trong)"}. {"Friday","Thứ Sáu"}. {"From ~s","Nhận từ ~s"}. {"From","Từ"}. {"Full Name","Tên Đầy Đủ"}. {"Get Number of Online Users","Nhận Số Người Sử Dụng Trực Tuyến"}. {"Get Number of Registered Users","Nhận Số Người Sử Dụng Đã Đăng Ký"}. {"Get User Last Login Time","Nhận Thời Gian Đăng Nhập Cuối Cùng Của Người Sử Dụng"}. {"Get User Password","Nhận Mật Khẩu Người Sử Dụng"}. {"Get User Statistics","Nhận Thông Tin Thống Kê Người Sử Dụng"}. {"Group ","Nhóm "}. {"Groups","Nhóm"}. {"has been banned","đã bị cấm"}. {"has been kicked","đã bị đẩy ra khỏi"}. {" has set the subject to: "," đã đặt chủ đề thành: "}. {"Host","Máy chủ"}. {"Import Directory","Nhập Thư Mục"}. {"Import File","Nhập Tập Tin"}. {"Import User from File at ","Nhập Người Sử Dụng từ Tập Tin tại"}. {"Import Users from Dir at ","Nhập Người Sử Dụng từ Thư Mục tại"}. {"Import Users From jabberd14 Spool Files","Nhập Người Sử Dụng Từ Các Tập Tin Spool jabberd14"}. {"Improper message type","Loại thư không phù hợp"}. {"Incorrect password","Mật khẩu sai"}. {"IP addresses","Địa chỉ IP"}. {"IRC Transport","Truyền tải IRC"}. {"IRC Username","Tên truy cập IRC"}. {"is now known as","bây giờ được biết như"}. {"It is not allowed to send private messages of type \"groupchat\"","Không được phép gửi những thư riêng loại \"groupchat\""}. {"It is not allowed to send private messages to the conference","Không được phép gửi những thư riêng đến phòng họp"}. {"Jabber ID","Jabber ID"}. {"January","Tháng Một"}. {"joins the room","tham gia phòng này"}. {"July","Tháng Bảy"}. {"June","Tháng Sáu"}. {"Last Activity","Hoạt Động Cuối Cùng"}. {"Last login","Đăng nhập lần cuối"}. {"Last month","Tháng trước"}. {"Last year","Năm trước"}. {"leaves the room","rời khỏi phòng này"}. {"Listened Ports at ","Cổng Liên Lạc tại"}. {"Listened Ports","Cổng Kết Nối"}. {"List of modules to start","Danh sách các môđun khởi động"}. {"Low level update script","Lệnh cập nhật mức độ thấp"}. {"Make participants list public","Tạo danh sách người tham dự công khai"}. {"Make room members-only","Tạo phòng chỉ cho phép tư cách thành viên tham gia"}. {"Make room password protected","Tạo phòng được bảo vệ bằng mật khẩu"}. {"Make room persistent","Tạo phòng bền vững"}. {"Make room public searchable","Tạo phòng có thể tìm kiếm công khai"}. {"March","Tháng Ba"}. {"Maximum Number of Occupants","Số Lượng Người Tham Dự Tối Đa"}. {"May","Tháng Năm"}. {"Members:","Thành viên:"}. {"Memory","Bộ Nhớ"}. {"Message body","Thân thư"}. {"Middle Name","Họ Đệm"}. {"Moderator privileges required","Yêu cầu đặc quyền của nhà điều phối"}. {"Module","Môđun"}. {"Modules","Môđun"}. {"Monday","Thứ Hai"}. {"Name:","Tên:"}. {"Name","Tên"}. {"Never","Không bao giờ"}. {"Nickname","Bí danh"}. {"Nickname Registration at ","Đăng Ký Bí Danh tại"}. {"Nickname ~s does not exist in the room","Bí danh ~s không tồn tại trong phòng này"}. {"No body provided for announce message","Không có nội dung trong thư thông báo"}. {"No Data","Không Dữ Liệu"}. {"Node not found","Nút không tìm thấy"}. {"Nodes","Nút"}. {"None","Không có"}. {"November","Tháng Mười Một"}. {"Number of online users","Số người sử dụng trực tuyến"}. {"Number of registered users","Số người sử dụng đã đăng ký"}. {"October","Tháng Mười"}. {"Offline Messages:","Thư Ngoại Tuyến:"}. {"Offline Messages","Thư Ngoại Tuyến"}. {"OK","OK"}. {"Online","Trực tuyến"}. {"Online Users:","Người Sử Dụng Trực Tuyến:"}. {"Online Users","Người Sử Dụng Trực Tuyến"}. {"Only occupants are allowed to send messages to the conference","Chỉ có những đối tượng tham gia mới được phép gửi thư đến phòng họp"}. {"Only occupants are allowed to send queries to the conference","Chỉ có những đối tượng tham gia mới được phép gửi yêu cầu đến phòng họp"}. {"Only service administrators are allowed to send service messages","Chỉ có người quản trị dịch vụ mới được phép gửi những thư dịch vụ"}. {"Options","Tùy chọn"}. {"Organization Name","Tên Tổ Chức"}. {"Organization Unit","Bộ Phận"}. {"Outgoing s2s Connections:","Kết Nối Bên Ngoài s2s:"}. {"Outgoing s2s Connections","Kết Nối Bên Ngoài s2s"}. {"Owner privileges required","Yêu cầu đặc quyền của người sở hữu"}. {"Packet","Gói thông tin"}. {"Password:","Mật Khẩu:"}. {"Password","Mật Khẩu"}. {"Password Verification","Kiểm Tra Mật Khẩu"}. {"Path to Dir","Đường Dẫn đến Thư Mục"}. {"Path to File","Đường dẫn đến Tập Tin"}. {"Pending","Chờ"}. {"Period: ","Giai đoạn: "}. {"Ping","Ping"}. {"Pong","Pong"}. {"Port","Cổng"}. {"private, ","riêng,"}. {"Publish-Subscribe","Xuất Bản-Đăng Ký"}. {"PubSub subscriber request","Yêu cầu người đăng ký môđun Xuất Bản Đăng Ký"}. {"Queries to the conference members are not allowed in this room","Không được phép gửi các yêu cầu gửi đến các thành viên trong phòng họp này"}. {"RAM and disc copy","Sao chép vào RAM và đĩa"}. {"RAM copy","Sao chép vào RAM"}. {"Raw","Thô"}. {"Really delete message of the day?","Có thực sự xóa thư trong ngày này không?"}. {"Recipient is not in the conference room","Người nhận không có trong phòng họp"}. {"Registered Users:","Người Sử Dụng Đã Đăng Ký:"}. {"Registered Users","Người Sử Dụng Đã Đăng Ký"}. {"Registration in mod_irc for ","Đăng ký trong mod_irc cho "}. {"Remote copy","Sao chép từ xa"}. {"Remove","Gỡ bỏ"}. {"Remove User","Gỡ Bỏ Người Sử Dụng"}. {"Replaced by new connection","Được thay thế bởi kết nối mới"}. {"Resources","Nguồn tài nguyên"}. {"Restart","Khởi động lại"}. {"Restart Service","Khởi Động Lại Dịch Vụ"}. {"Restore Backup from File at ","Phục hồi Sao Lưu từ Tập Tin tại "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Khôi phục bản sao lưu dự phòng dạng nhị phân sau lần khởi động ejabberd kế tiếp (yêu cầu ít bộ nhớ hơn):"}. {"Restore binary backup immediately:","Khôi phục bản sao lưu dự phòng dạng nhị phận ngay lập tức:"}. {"Restore","Khôi phục"}. {"Restore plain text backup immediately:","Khôi phục bản sao lưu dự phòng thuần văn bản ngay lập tức:"}. {"Room Configuration","Cấu Hình Phòng"}. {"Room creation is denied by service policy","Việc tạo phòng bị ngăn lại theo chính sách dịch vụ"}. {"Room title","Tên phòng"}. {"Roster","Bảng phân công"}. {"Roster of ","Bảng phân công của "}. {"Roster size","Kích thước bảng phân công"}. {"RPC Call Error","Lỗi Gọi RPC"}. {"Running Nodes","Nút Hoạt Động"}. {"~s access rule configuration","~s cấu hình quy tắc truy cập"}. {"Saturday","Thứ Bảy"}. {"Script check","Lệnh kiểm tra"}. {"Search Results for ","Kết Quả Tìm Kiếm cho "}. {"Search users in ","Tìm kiếm người sử dụng trong"}. {"Send announcement to all online users","Gửi thông báo đến tất cả người sử dụng trực tuyến"}. {"Send announcement to all online users on all hosts","Gửi thông báo đến tất cả người sử dụng trực tuyến trên tất cả các máy chủ"}. {"Send announcement to all users","Gửi thông báo đến tất cả người sử dụng"}. {"Send announcement to all users on all hosts","Gửi thông báo đến tất cả người sử dụng trên tất cả các máy chủ"}. {"September","Tháng Chín"}. {"Set message of the day and send to online users","Tạo lập thư trong ngày và gửi đến những người sử dụng trực tuyến"}. {"Set message of the day on all hosts and send to online users","Tạo lập thư trong ngày trên tất cả các máy chủ và gửi đến những người sử dụng trực tuyến"}. {"Shared Roster Groups","Nhóm Phân Công Chia Sẻ"}. {"Show Integral Table","Hiển Thị Bảng Đầy Đủ"}. {"Show Ordinary Table","Hiển Thị Bảng Thường"}. {"Shut Down Service","Tắt Dịch Vụ"}. {"~s invites you to the room ~s","~s mời bạn vào phòng ~s"}. {"~s's Offline Messages Queue","~s's Danh Sách Chờ Thư Ngoại Tuyến"}. {"Start","Khởi động"}. {"Start Modules at ","Môđun Khởi Động tại "}. {"Start Modules","Môđun Khởi Động"}. {"Statistics of ~p","Thống kê về ~p"}. {"Statistics","Số liệu thống kê"}. {"Stop","Dừng"}. {"Stop Modules at ","Môđun Dừng tại"}. {"Stop Modules","Môđun Dừng"}. {"Stopped Nodes","Nút Dừng"}. {"Storage Type","Loại Lưu Trữ"}. {"Store binary backup:","Lưu dữ liệu sao lưu dạng nhị phân:"}. {"Store plain text backup:","Khôi phục bản sao lưu dự phòng thuần văn bản"}. {"Subject","Tiêu đề"}. {"Submit","Gửi"}. {"Submitted","Đã gửi"}. {"Subscription","Đăng ký"}. {"Sunday","Chủ Nhật"}. {"the password is","mật khẩu là"}. {"This room is not anonymous","Phòng này không nặc danh"}. {"Thursday","Thứ Năm"}. {"Time delay","Thời gian trì hoãn"}. {"Time","Thời Gian"}. {"To","Đến"}. {"To ~s","Gửi đến ~s"}. {"Traffic rate limit is exceeded","Quá giới hạn tỷ lệ lưu lượng truyền tải"}. {"Transactions Aborted:","Giao Dịch Hủy Bỏ:"}. {"Transactions Committed:","Giao Dịch Được Cam Kết:"}. {"Transactions Logged:","Giao Dịch Được Ghi Nhận:"}. {"Transactions Restarted:","Giao Dịch Khởi Động Lại:"}. {"Tuesday","Thứ Ba"}. {"Update","Cập Nhật"}. {"Update message of the day (don't send)","Cập nhật thư trong ngày (không gửi)"}. {"Update message of the day on all hosts (don't send)","Cập nhật thư trong ngày trên tất cả các máy chủ (không gửi)"}. {"Update plan","Kế hoạch cập nhật"}. {"Update script","Cập nhận lệnh"}. {"Uptime:","Thời gian tải lên:"}. {"Use of STARTTLS required","Yêu cầu sử dụng STARTTLS"}. {"User Management","Quản Lý Người Sử Dụng"}. {"User","Người sử dụng"}. {"Users Last Activity","Hoạt Động Cuối Cùng Của Người Sử Dụng"}. {"Users","Người sử dụng"}. {"Validate","Xác nhận hợp lệ"}. {"vCard User Search","Tìm Kiếm Người Sử Dụng vCard"}. {"Virtual Hosts","Máy Chủ Ảo"}. {"Visitors are not allowed to send messages to all occupants","Người ghé thăm không được phép gửi thư đến tất cả các người tham dự"}. {"Wednesday","Thứ Tư"}. {"You have been banned from this room","Bạn bị cấm tham gia phòng này"}. {"You must fill in field \"Nickname\" in the form","Bạn phải điền thông tin vào ô \"Nickname\" trong biểu mẫu này"}. {"You need an x:data capable client to configure mod_irc settings","Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác định các thiết lập mod_irc"}. {"You need an x:data capable client to search","Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để tìm kiếm"}. {"Your contact offline message queue is full. The message has been discarded.","Danh sách chờ thư liên lạc ngoại tuyến của bạn đã đầy. Thư này đã bị loại bỏ."}. ejabberd-18.01/priv/msgs/ejabberd.pot0000644000232200023220000012740013225664356020062 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 15.11.127\n" "X-Language: Language Name\n" "Last-Translator: Translator name and contact method\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr "" #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "" #: mod_configure:1095 msgid "Access control lists" msgstr "" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 #: ejabberd_service:202 mod_announce:255 mod_announce:273 #: mod_announce:275 mod_announce:277 mod_announce:279 #: mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 #: mod_announce:351 mod_announce:353 mod_announce:355 #: mod_announce:357 mod_announce:359 mod_announce:361 #: mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 #: mod_configure:206 mod_configure:219 mod_configure:220 #: mod_configure:221 mod_configure:222 mod_configure:224 #: mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 #: mod_configure:235 mod_configure:237 mod_configure:239 #: mod_configure:241 mod_configure:243 mod_configure:245 #: mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 #: mod_configure:259 mod_configure:261 mod_configure:263 #: mod_configure:265 mod_configure:267 mod_configure:313 #: mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 #: mod_configure:790 mod_configure:792 mod_configure:794 #: mod_configure:1740 mod_http_upload:545 mod_irc:265 #: mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 #: mod_register:325 mod_register:326 mod_roster:187 #: mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "" #: mod_configure:1113 msgid "Access rules" msgstr "" #: mod_configure:1772 msgid "Action on user" msgstr "" #: mod_roster:982 msgid "Add Jabber ID" msgstr "" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "" #: mod_configure:1767 msgid "Administration of " msgstr "" #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "" #: mod_configure:507 msgid "All Users" msgstr "" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" #: mod_announce:611 msgid "Announcements" msgstr "" #: mod_muc_log:476 msgid "April" msgstr "" #: mod_muc_log:480 msgid "August" msgstr "" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "" #: mod_configure:584 msgid "Backup Management" msgstr "" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "" #: mod_configure:948 msgid "Backup to File at " msgstr "" #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "" #: mod_register:295 msgid "Changing password is not allowed" msgstr "" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" #: mod_configure:920 msgid "Choose modules to stop" msgstr "" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 #: mod_adhoc:179 msgid "Commands" msgstr "" #: mod_muc:461 msgid "Conference room does not exist" msgstr "" #: mod_configure:129 mod_configure:286 mod_configure:306 #: mod_configure:504 msgid "Configuration" msgstr "" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "" #: mod_irc:526 msgid "Connections parameters" msgstr "" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "" #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 #: mod_irc:574 mod_irc:739 mod_last:215 #: mod_mam:501 mod_muc:749 mod_offline:291 #: mod_offline:582 mod_privacy:186 mod_privacy:204 #: mod_privacy:298 mod_privacy:314 mod_privacy:347 #: mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 #: mod_pubsub:3573 mod_pubsub:3598 mod_pubsub:3604 #: mod_pubsub:3607 mod_vcard:226 node_flat_sql:801 #: nodetree_tree_sql:128 nodetree_tree_sql:142 nodetree_tree_sql:257 msgid "Database failure" msgstr "" #: mod_muc_log:484 msgid "December" msgstr "" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "" #: mod_announce:629 msgid "Delete message of the day" msgstr "" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "" #: mod_shared_roster:900 msgid "Description:" msgstr "" #: mod_configure:889 msgid "Disc only copy" msgstr "" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "" #: mod_register_web:251 msgid "Don't tell your password to anybody, not even the administrators of the Jabber server." msgstr "" #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "" #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "" #: mod_register:292 msgid "Empty password" msgstr "" #: mod_muc_log:1055 msgid "Enable logging" msgstr "" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "" #: mod_configure:974 msgid "Enter path to text file" msgstr "" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "" #: mod_irc:759 msgid "Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings." msgstr "" #: mod_irc:540 msgid "Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers" msgstr "" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "" #: mod_irc:520 msgid "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "" #: mod_muc_log:474 msgid "February" msgstr "" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)" msgstr "" #: mod_muc_log:467 msgid "Friday" msgstr "" #: mod_offline:691 msgid "From" msgstr "" #: mod_configure:723 msgid "From ~s" msgstr "" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "" #: mod_configure:172 mod_configure:526 mod_configure:1155 #: mod_configure:1165 msgid "Get User Password" msgstr "" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "" #: mod_shared_roster:923 msgid "Group " msgstr "" #: mod_roster:916 msgid "Groups" msgstr "" #: ejabberd_web_admin:1365 msgid "Host" msgstr "" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "" #: mod_configure:1673 msgid "IP addresses" msgstr "" #: mod_irc:439 msgid "IRC Transport" msgstr "" #: mod_irc:496 msgid "IRC Username" msgstr "" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "" #: mod_irc:417 msgid "IRC connection not found" msgstr "" #: mod_irc:673 msgid "IRC server" msgstr "" #: mod_irc:756 msgid "IRC settings" msgstr "" #: mod_irc:766 msgid "IRC username" msgstr "" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" #: mod_irc:503 msgid "If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password." msgstr "" #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "" #: mod_configure:982 msgid "Import User from File at " msgstr "" #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "" #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 #: mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 #: mod_configure:1419 mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 #: mod_configure:1159 mod_configure:1169 mod_configure:1183 #: mod_configure:1192 mod_configure:1600 mod_configure:1644 #: mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "" #: mod_muc_log:473 msgid "January" msgstr "" #: mod_irc:665 msgid "Join IRC channel" msgstr "" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "" #: mod_muc_log:479 msgid "July" msgstr "" #: mod_muc_log:478 msgid "June" msgstr "" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "" #: mod_configure:1646 msgid "Last login" msgstr "" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "" #: mod_configure:941 msgid "List of modules to start" msgstr "" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "" #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "" #: mod_register:317 msgid "Malformed username" msgstr "" #: mod_muc_log:475 msgid "March" msgstr "" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "" #: mod_muc_log:477 msgid "May" msgstr "" #: mod_shared_roster:907 msgid "Members:" msgstr "" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "" #: mod_register_web:262 msgid "Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it." msgstr "" #: ejabberd_web_admin:1954 msgid "Memory" msgstr "" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 #: mod_muc_room:3798 msgid "Moderator privileges required" msgstr "" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "" #: mod_muc_log:463 msgid "Monday" msgstr "" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 #: mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "" #: mod_shared_roster:896 msgid "Name:" msgstr "" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "" #: mod_register_web:377 msgid "New Password:" msgstr "" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 #: mod_vcard_mnesia:104 mod_vcard_mnesia:118 mod_vcard_sql:159 #: mod_vcard_sql:173 msgid "Nickname" msgstr "" #: mod_muc:722 msgid "Nickname Registration at " msgstr "" #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 #: mod_configure:1413 mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 #: mod_blocking:110 mod_http_upload:513 mod_muc:482 #: mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 #: mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 #: mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 #: mod_configure:1254 mod_configure:1275 mod_configure:1314 #: mod_configure:1345 mod_configure:1377 mod_configure:1408 #: mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 #: mod_muc:532 mod_muc:680 nodetree_dag:78 #: nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 #: nodetree_tree_sql:130 nodetree_tree_sql:144 msgid "Node not found" msgstr "" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "" #: mod_configure:1212 msgid "Number of online users" msgstr "" #: mod_configure:1202 msgid "Number of registered users" msgstr "" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "" #: mod_muc_log:482 msgid "October" msgstr "" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "" #: mod_offline:761 msgid "Offline Messages:" msgstr "" #: mod_register_web:373 msgid "Old Password:" msgstr "" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "" #: mod_muc_room:773 msgid "Only moderators and participants are allowed to change the subject in this room" msgstr "" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 #: mod_pubsub:1302 mod_pubsub:1394 mod_pubsub:1553 #: mod_pubsub:2118 mod_pubsub:2184 mod_pubsub:2383 #: mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "" #: mod_offline:693 msgid "Packet" msgstr "" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 #: mod_muc_log:1036 mod_register:229 msgid "Password" msgstr "" #: mod_configure:1130 msgid "Password Verification" msgstr "" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "" #: mod_irc:822 msgid "Password ~b" msgstr "" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "" #: mod_configure:998 msgid "Path to Dir" msgstr "" #: mod_configure:952 mod_configure:964 mod_configure:976 #: mod_configure:987 msgid "Path to File" msgstr "" #: mod_roster:915 msgid "Pending" msgstr "" #: ejabberd_web_admin:994 msgid "Period: " msgstr "" #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately." msgstr "" #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" #: mod_adhoc:274 msgid "Pong" msgstr "" #: ejabberd_web_admin:2463 msgid "Port" msgstr "" #: mod_irc:828 msgid "Port ~b" msgstr "" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" #: mod_blocking:85 mod_disco:325 mod_disco:393 #: mod_offline:270 mod_privacy:146 mod_private:118 #: mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "" #: mod_configure:889 msgid "RAM copy" msgstr "" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "" #: mod_register_web:275 msgid "Register" msgstr "" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "" #: mod_configure:889 msgid "Remote copy" msgstr "" #: mod_roster:963 msgid "Remove" msgstr "" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "" #: mod_configure:1675 msgid "Resources" msgstr "" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "" #: ejabberd_web_admin:2013 msgid "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "" #: mod_muc_log:872 msgid "Room Configuration" msgstr "" #: mod_muc_log:892 msgid "Room Occupants" msgstr "" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "" #: mod_muc_log:1064 msgid "Room description" msgstr "" #: mod_muc_log:1027 msgid "Room title" msgstr "" #: mod_roster:1082 msgid "Roster" msgstr "" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "" #: mod_configure:1671 msgid "Roster size" msgstr "" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "" #: mod_vcard:453 msgid "Search Results for " msgstr "" #: mod_vcard:427 msgid "Search users in " msgstr "" #: mod_announce:617 msgid "Send announcement to all online users" msgstr "" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" #: mod_announce:613 msgid "Send announcement to all users" msgstr "" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "" #: mod_muc_log:481 msgid "September" msgstr "" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "" #: mod_register_web:258 msgid "Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons." msgstr "" #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "" #: mod_configure:936 msgid "Start Modules at " msgstr "" #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "" #: mod_configure:918 msgid "Stop Modules at " msgstr "" #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 #: mod_shared_roster:830 mod_shared_roster:925 msgid "Submitted" msgstr "" #: mod_roster:914 msgid "Subscription" msgstr "" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 #: mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "" #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "The stanza MUST contain only one element, one element, or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" #: mod_register_web:220 msgid "This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields." msgstr "" #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "" #: mod_muc_log:466 msgid "Thursday" msgstr "" #: mod_offline:690 msgid "Time" msgstr "" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "" #: mod_offline:692 msgid "To" msgstr "" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "" #: mod_muc_log:464 msgid "Tuesday" msgstr "" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 #: mod_sic:106 msgid "User session not found" msgstr "" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "" #: mod_roster:954 msgid "Validate" msgstr "" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 #: mod_muc_room:3802 mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 #: mod_disco:346 mod_irc:270 mod_irc:285 #: mod_irc:339 mod_last:118 mod_last:140 #: mod_muc:479 mod_muc:504 mod_muc:539 #: mod_muc:561 mod_muc:571 mod_muc_room:3597 #: mod_muc_room:3641 mod_proxy65_service:142 mod_proxy65_service:160 #: mod_proxy65_service:167 mod_pubsub:821 mod_pubsub:839 #: mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 #: mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "" #: mod_muc_room:3879 msgid "Voice request" msgstr "" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "" #: mod_muc_log:465 msgid "Wednesday" msgstr "" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "" #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" #: mod_offline:576 msgid "Your contact offline message queue is full. The message has been discarded." msgstr "" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 #: mod_muc_log:390 msgid "has been kicked" msgstr "" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" #: mod_muc_log:410 msgid "is now known as" msgstr "" #: mod_muc_log:370 msgid "joins the room" msgstr "" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "" #: mod_muc_room:3856 msgid "private, " msgstr "" #: mod_muc_room:3955 msgid "the password is" msgstr "" #: mod_vcard:292 msgid "vCard User Search" msgstr "" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "" ejabberd-18.01/priv/msgs/pl.po0000644000232200023220000020163613225664356016557 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Paweł Chmielowski \n" "Language-Team: \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Polish (polski)\n" "X-Additional-Translator: Janusz B. Wiśniewski\n" "X-Additional-Translator: Marcin Owsiany\n" "X-Additional-Translator: Andrzej Smyk\n" "X-Additional-Translator: Mateusz Gajewski\n" "X-Generator: Poedit 2.0.4\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " zmienił temat na: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Aby wejść do pokoju wymagane jest hasło" #: ejabberd_oauth:448 msgid "Accept" msgstr "Zaakceptuj" #: mod_configure:1109 msgid "Access Configuration" msgstr "Konfiguracja dostępu" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfiguracja listy dostępowej" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Lista dostępowa" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Zasady dostępu" #: mod_configure:1095 msgid "Access control lists" msgstr "Listy dostępowe" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Dostęp zabroniony zgodnie z zasadami usługi" #: mod_configure:1113 msgid "Access rules" msgstr "Reguły dostępu" #: mod_configure:1772 msgid "Action on user" msgstr "Wykonaj na użytkowniku" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Dodaj Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Dodaj nowe" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Dodaj użytkownika" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administracja" #: mod_configure:1767 msgid "Administration of " msgstr "Zarządzanie " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Wymagane uprawnienia administratora" #: mod_configure:507 msgid "All Users" msgstr "Wszyscy użytkownicy" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Cała aktywność" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Pozwól użytkownikom zmieniać temat" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Pozwól użytkownikom pobierać informacje o innych użytkownikach" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Pozwól użytkownikom wysyłać zaproszenia" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Pozwól użytkownikom wysyłać prywatne wiadomości" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Pozwól uczestnikom na zmianę nicka" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Pozwól użytkownikom wysyłać prywatne wiadomości" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Pozwól uczestnikom na wysyłanie statusów opisowych" #: mod_announce:611 msgid "Announcements" msgstr "Powiadomienia" #: mod_muc_log:476 msgid "April" msgstr "Kwiecień" #: mod_muc_log:480 msgid "August" msgstr "Sierpień" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "Automatyczne tworzenie węzłów nie zostało włączone" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Kopia zapasowa" #: mod_configure:584 msgid "Backup Management" msgstr "Zarządzanie kopiami zapasowymi" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Kopia zapasowa ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Zapisz kopię w pliku na " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Błędny format" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Data urodzenia" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Wymagana jest zarówno nazwa użytkownika jak i zasób" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Strumień danych został już aktywowany" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Strona internetowa CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Czas CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "Nie można usunąć aktywnej listy" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "Nie można usunąć domyślnej listy" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Zmień hasło" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Zmień hasło użytkownika" #: mod_register:295 msgid "Changing password is not allowed" msgstr "Zmiana hasła jest niedopuszczalna" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "Zmiana roli jest niedopuszczalna" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Te znaki są niedozwolone:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Konfiguracja pokoju zmodyfikowana" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Pokój został stworzony" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Pokój został usunięty" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Pokój został uruchomiony" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Pokój został zatrzymany" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Pokoje rozmów" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Wybierz nazwę użytkownika i hasło aby zarejestrować się na tym serwerze" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Wybierz moduły do zatrzymania" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Wybierz typ bazy dla tablel" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Wybierz, czy akceptować subskrypcję tej jednostki" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Miasto" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Polecenia" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Pokój konferencyjny nie istnieje" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Konfiguracja" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Konfiguracja pokoju ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Zasoby zalogowane:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parametry połączeń" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Państwo" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Baza danych" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Konfiguracja tabel bazy na " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Tabele bazy na ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Błąd bazy danych" #: mod_muc_log:484 msgid "December" msgstr "Grudzień" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Domyślni użytkownicy jako uczestnicy" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Usuń zaznaczone" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Usuń użytkownika" #: mod_announce:629 msgid "Delete message of the day" msgstr "Usuń wiadomość dnia" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Usuń wiadomość dnia ze wszystkich hostów" #: mod_shared_roster:900 msgid "Description:" msgstr "Opis:" #: mod_configure:889 msgid "Disc only copy" msgstr "Kopia tylko na dysku" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Wyświetlane grupy:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Nie podawaj swojego hasła nikomu, nawet administratorowi serwera Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Zapisz kopię zapasową w pliku tekstowym na " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Wykonaj kopie do pliku tekstowego" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Duplikaty grup nie są dozwolone" #: mod_configure:1776 msgid "Edit Properties" msgstr "Edytuj właściwości" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Zatwierdź lub odrzuć żądanie głosowe." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementy" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 msgid "Empty password" msgstr "Puste hasło" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Włącz logowanie" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "Aktywacja 'push' bez węzła jest nie dostępna" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Kodowanie znaków dla serwera ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Zakończ sesję uzytkownika" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Wprowadź listę {Moduł, [Opcje]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Wprowadz nazwę użytkownika którego chcesz zarejestrować" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Wprowadź scieżkę do pliku kopii zapasowej" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Wprowadź ścieżkę do roboczego katalogu serwera jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Wprowadź ścieżkę do roboczego pliku serwera jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Wprowadź scieżkę do pliku tekstowego" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Przepisz tekst z obrazka" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Wprowadź nazwę użytkownika i kodowania których chcesz używać do łączenia z " "serwerami IRC. Wciśnij \"Dalej\" aby ustawić więcej parametrów połączenia. " "Wciśnij \"Zakończ\" aby zapisać ustawienia." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Wprowadź nazwę użytkownika, port i kodowanie, których chcesz używać do " "łączenia z serwerami IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Błąd" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Przykład: [{\"wroclaw.irc.pl\",\"utf-8\"}, {\"warszawa.irc.pl\", " "\"iso8859-2\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Wyeksportuj wszystkie tabele jako zapytania SQL do pliku:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Eksportuj dane wszystkich użytkowników serwera do plików w formacie PIEFXIS " "(XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Eksportuj dane użytkowników z hosta do plików w formacie PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Błąd zewnętrznego komponentu" #: mod_delegation:283 msgid "External component timeout" msgstr "Upłynął limit czasu zewnętrznego komponentu" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Nie udało się aktywować strumienia danych" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Nie udało się wydobyć JID-u z twojego żądania" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Nie udało się znaleźć zewnętrznego komponentu na podstawie nazwy" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Nie udało się zanalizować odpowiedzi HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Nie udało się zanalizować odpowiedzi serwera" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Nie udało się przetworzyć opcji '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Nazwisko" #: mod_muc_log:474 msgid "February" msgstr "Luty" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "Plik jest większy niż ~w bajtów" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Wypełnij formularz aby wyszukać użytkowników Jabbera (dodaj * na koniec " "zapytania aby wyszukać po fragmencie)" #: mod_muc_log:467 msgid "Friday" msgstr "Piątek" #: mod_offline:691 msgid "From" msgstr "Od" #: mod_configure:723 msgid "From ~s" msgstr "Od ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Pełna nazwa" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Pokaż liczbę zalogowanych użytkowników" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Pokaż liczbę zarejestrowanych użytkowników" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Pokaż czas ostatniego zalogowania uzytkownika" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Pobierz hasło użytkownika" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Pobierz statystyki użytkownika" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Imię" #: mod_shared_roster:923 msgid "Group " msgstr "Grupa " #: mod_roster:916 msgid "Groups" msgstr "Grupy" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Nieznany host" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Adresy IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transport IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Nazwa użytkownika IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Kanał IRC (nie używaj #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "Połączenie IRC nie zostało znalezione" #: mod_irc:673 msgid "IRC server" msgstr "Serwer IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Ustawienia IRC" #: mod_irc:766 msgid "IRC username" msgstr "Nazwa użytkownika IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Jeśli nie widzisz obrazka CAPTCHA, odwiedź stronę internetową." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Jeśli chcesz ustawić inne hasła, porty lub kodowania dla poszczególnych " "serwerów IRC, wypełnij tą listę wartościami w formacie '{\"irc server\"," "\"encoding\", port, \"password\"}'. Domyślne ta usługa używa kodowania \"~s" "\", portu ~p, bez hasła." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importuj katalog" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importuj plik" #: mod_configure:982 msgid "Import User from File at " msgstr "Importuj użytkownika z pliku na " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importuj użytkowników z plików roboczych serwera jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importuj użytkowników z katalogu na " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importuj dane użytkownika z pliku roboczego serwera jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importuj dane użytkowników z pliku w formacie PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importuj użytkowników z katalogu roboczego serwera jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Nieprawidłowy atrybut 'from'" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Nieprawidłowy atrybut 'to'" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Nieprawidłowa domena atrybutu 'from'" #: mod_muc_room:260 msgid "Improper message type" msgstr "Nieprawidłowy typ wiadomości" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Przychodzące połączenia s2s:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "Nieprawidłowa odpowiedz dla CAPTCHA" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Nieprawidłowe dane w formatce" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Nieprawidłowe hasło" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Nieprawidłowe dane w formatce" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Nieprawidłowe dane atrybutu 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Nieprawidłowe dane atrybutu 'action'" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Nieprawidłowe dane atrybutu 'path'" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Nieprawidłowe dane atrybutu 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Niewystarczające uprawnienia" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Nieprawidłowy atrybut 'from' w przesyłanej dalej wiadomości" #: mod_privilege:300 msgid "Invalid element" msgstr "Nieprawidłowy element " #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "Zaproszenia są wyłączone w tym pokoju" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Użytkownik nie może wysyłać wiadomości o błędach do pokoju. Użytkownik (~s) " "wysłał błąd (~s) i został wyrzucony z pokoju" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Wysyłanie prywatnych wiadomości jest zabronione" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Nie można wysyłać prywatnych wiadomości typu \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Nie wolno wysyłac prywatnych wiadomości na konferencję" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Zakładanie konta Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Styczeń" #: mod_irc:665 msgid "Join IRC channel" msgstr "Dołącz do kanału IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Dołącz do kanału IRC." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Dołącz do kanału IRC pod tym Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Lipiec" #: mod_muc_log:478 msgid "June" msgstr "Czerwiec" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Ostatnia aktywność" #: mod_configure:1646 msgid "Last login" msgstr "Ostatnie logowanie" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Miniony miesiąc" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Miniony rok" #: mod_configure:941 msgid "List of modules to start" msgstr "Lista modułów do uruchomienia" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Lista pokoi" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Porty nasłuchujące" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Porty nasłuchujące na " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Skrypt aktualizacji niskiego poziomu" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Upublicznij listę uczestników" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Pokój zabezpieczony captchą" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Pokój tylko dla członków" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Pokój moderowany" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Pokój zabezpieczony hasłem" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Utwórz pokój na stałe" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Pozwól wyszukiwać pokój" #: mod_register:317 msgid "Malformed username" msgstr "Nieprawidłowa nazwa użytkownika" #: mod_muc_log:475 msgid "March" msgstr "Marzec" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Maksymalna liczba uczestników" #: mod_muc_log:477 msgid "May" msgstr "Maj" #: mod_shared_roster:907 msgid "Members:" msgstr "Członkowie:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Musisz być na liście członków tego pokoju aby do niego wejść" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Zapamiętaj swoje hasło lub zapisz je na kartce i zachowaj w bezpiecznym " "miejscu. Na Jabberze nie ma zautomatyzowanego systemu odzyskiwania haseł." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Pamięć" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Treść wiadomości" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Nie znaleziona wiadomości w przesyłanych dalej danych" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Drugie imię" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Brakujące dane dla 'channel' lub 'server'" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Brakujący atrybut 'from'" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Brakujący atrybut 'to'" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Wymagane uprawnienia moderatora" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Zmodyfikowane moduły" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Moduł" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "Moduł nie był wstanie przetworzyć zapytania" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduły" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Moduły na ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Poniedziałek" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Wieloosobowa rozmowa" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Dopuszczalny jest wyłącznie pojedynczy " #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Imię" #: mod_shared_roster:896 msgid "Name:" msgstr "Nazwa:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Brak zarówno atrybutu 'jid' jak i 'nick'" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Brak zarówno atrybutu 'role' jak i 'affiliation'" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nigdy" #: mod_register_web:377 msgid "New Password:" msgstr "Nowe hasło:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Nazwa użytkownika" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Rejestracja nazwy użytkownika na " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Nie ma nicka ~s w tym pokoju" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Brak wartości dla 'access'" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Brak wartości dla 'acls'" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "Brak wartości dla 'access'" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "Brak wartości dla 'item'" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Brak wartości dla 'modules'" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Brak wartości dla 'password'" #: mod_register:148 msgid "No 'password' found in this query" msgstr "Brak wartości dla 'password'" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Brak wartości dla 'path'" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "Brak wartości dla 'to' w zaproszeniu" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Brak danych" #: ejabberd_local:181 msgid "No available resource found" msgstr "Brak dostępnych zasobów" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Brak treści powiadomienia" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "Brak danych dla formatki" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "Brak dostępnych funkcji" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Żadna funkcja nie przetworzyła tej komendy" #: mod_last:218 msgid "No info about last activity found" msgstr "Nie znaleziono informacji o ostatniej aktywności" #: mod_blocking:99 msgid "No items found in this query" msgstr "Nie znaleziono żadnych pozycji w tym zapytaniu" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Żaden moduł nie obsługuje tego zapytania" #: mod_pubsub:1541 msgid "No node specified" msgstr "Nie podano węzła" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "Nie ma żadnych oczekujących subskrypcji" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "Nie znaleziona żadnych list prywatności z tą nazwą" #: mod_private:96 msgid "No private data found in this query" msgstr "Nie znaleziono danych prywatnych w tym zapytaniu" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "Brak uruchomionych węzłów" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "Usługa nie jest dostępna" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Nie znaleziono statystyk dla tego elementu" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "Węzeł już istnieje" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Indeks węzła już istnieje" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Węzeł nie został znaleziony" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Węzeł ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Weryfikacja nazwy nie powiodła się" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Węzły" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Brak" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Nie znaleziono" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "Nie zasubskrybowano" #: mod_muc_log:483 msgid "November" msgstr "Listopad" #: mod_configure:1212 msgid "Number of online users" msgstr "Liczba zalogowanych użytkowników" #: mod_configure:1202 msgid "Number of registered users" msgstr "Liczba zarejestrowanych użytkowników" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Październik" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Wiadomości offline" #: mod_offline:761 msgid "Offline Messages:" msgstr "Wiadomości offline:" #: mod_register_web:373 msgid "Old Password:" msgstr "Stare hasło:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Dostępny" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Użytkownicy zalogowani" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Użytkownicy zalogowani:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Dozwolone są wyłącznie elementy lub " #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Wyłącznie elementy są dozwolone w tym zapytaniu" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Tylko moderatorzy mogą przeglądać archiwa tego pokoju" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Tylko moderatorzy i uczestnicy mogą zmienić temat tego pokoju" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Tylko moderatorzy mogą zmienić temat tego pokoju" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Tylko moderatorzy mogą zatwierdzać żądania głosowe" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Tylko uczestnicy mogą wysyłać wiadomości na konferencję" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Tylko uczestnicy mogą wysyłać zapytania do konferencji" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Tylko administratorzy mogą wysyłać wiadomości" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opcje" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nazwa organizacji" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Dział" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Wychodzące połączenia s2s" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Wychodzące połączenia s2s:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Wymagane uprawnienia właściciela" #: mod_offline:693 msgid "Packet" msgstr "Pakiet" #: mod_irc:578 msgid "Parse error" msgstr "Only or tags are allowed" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "Błąd parsowania" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Hasło" #: mod_configure:1130 msgid "Password Verification" msgstr "Weryfikacja hasła" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Weryfikacja hasła:" #: mod_irc:822 msgid "Password ~b" msgstr "Hasło ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Hasło:" #: mod_configure:998 msgid "Path to Dir" msgstr "Ścieżka do katalogu" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Scieżka do pliku" #: mod_roster:915 msgid "Pending" msgstr "Oczekuje" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Przedział czasu: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Stałych pokoi" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Żądanie 'ping' nie jest prawidłowe" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Te opcje kopii zapasowych dotyczą tylko wbudowanej bazy danych typu Mnesia. " "Jeśli korzystasz z modułu ODBC, musisz wykonać kopie bazy we własnym " "zakresie." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Proszę poczekać chwile, zanim wyślesz nowe żądanie głosowe" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Atrybut 'ask' nie jest dozwolony" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokół" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Żądanie subskrybcji PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "PubSub" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Publikacja danych w węzłach kolekcji nie jest dozwolona" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Informacje o członkach konferencji nie są dostępne w tym pokoju" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Zapytanie do innych użytkowników nie są dozwolone" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Kopia na dysku i w pamięci RAM" #: mod_configure:889 msgid "RAM copy" msgstr "Kopia w pamięci RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Błąd żądania RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Żródło" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Na pewno usunąć wiadomość dnia?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Odbiorcy nie ma w pokoju" #: mod_register_web:275 msgid "Register" msgstr "Zarejestruj" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Załóż konto Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Użytkownicy zarejestrowani" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Użytkownicy zarejestrowani:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Zarejestrowanych nicków" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Rejestracja w mod_irc dla " #: mod_configure:889 msgid "Remote copy" msgstr "Kopia zdalna" #: mod_roster:963 msgid "Remove" msgstr "Usuń" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Usuń wszystkie wiadomości typu 'Offline'" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Usuń użytkownika" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Połączenie zostało zastąpione" #: mod_configure:1675 msgid "Resources" msgstr "Zasoby" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Uruchom ponownie" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Restart usługi" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Przywróć z kopii" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Odtwórz bazę danych z kopii zapasowej na " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Odtwórz kopię binarną podczas następnego uruchomienia ejabberd (wymaga mniej " "zasobów):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Natychmiast odtwórz kopię binarną:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Natychmiast odtwórz kopię z postaci tekstowej:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Konfiguracja pokoju" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Lista uczestników" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Zasady serwera zabraniają tworzyć nowe pokoje" #: mod_muc_log:1064 msgid "Room description" msgstr "Opis pokoju" #: mod_muc_log:1027 msgid "Room title" msgstr "Tytuł pokoju" #: mod_roster:1082 msgid "Roster" msgstr "Lista kontaktów" #: mod_roster:334 msgid "Roster module has failed" msgstr "Moduł list kontaktów zgłosił błąd" #: mod_roster:968 msgid "Roster of " msgstr "Lista kontaktów " #: mod_configure:1671 msgid "Roster size" msgstr "Rozmiar listy kontaktów" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Uruchomione węzły" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "Negocjacja SASL nie jest dopuszczalna w typ stanie" #: mod_muc_log:468 msgid "Saturday" msgstr "Sobota" #: mod_irc:582 msgid "Scan error" msgstr "Błąd skanowania" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "Błąd skanowania" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Sprawdź skrypt" #: mod_vcard:453 msgid "Search Results for " msgstr "Wyniki wyszukiwania dla " #: mod_vcard:427 msgid "Search users in " msgstr "Wyszukaj użytkowników w " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Wyślij powiadomienie do wszystkich zalogowanych użytkowników" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" "Wyślij powiadomienie do wszystkich zalogowanych użytkowników na wszystkich " "hostach" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Wyślij powiadomienie do wszystkich użytkowników" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Wyślij powiadomienie do wszystkich użytkowników na wszystkich hostach" #: mod_muc_log:481 msgid "September" msgstr "Wrzesień" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Połączenie z serwerem nie było możliwe" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Połączenie serwerowe do lokalnej domeny nie jest dopuszczalne" #: mod_irc:842 msgid "Server ~b" msgstr "Serwer ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Serwer:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Wyślij wiadomość dnia do wszystkich zalogowanych użytkowników" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Ustaw wiadomość dnia dla wszystkich hostów i wyślij do zalogowanych " "uzytkowników" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Wspólne grupy kontaktów" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Pokaż tabelę całkowitą" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Pokaż zwykłą tabelę" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Wyłącz usługę" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Niektóre klienty Jabber mogą zapisywać Twoje hasło na komputerze. Używaj tej " "opcji tylko jeśli ufasz komputerowi na którym pracujesz." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Uruchom" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Uruchom moduły" #: mod_configure:936 msgid "Start Modules at " msgstr "Uruchom moduły na " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statystyki" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statystyki ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Zatrzymaj" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Zatrzymaj moduły" #: mod_configure:918 msgid "Stop Modules at " msgstr "Zatrzymaj moduły na " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Zatrzymane węzły" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Typ bazy" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Zachowaj kopię binarną:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Zachowaj kopię w postaci tekstowej:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Temat" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Wyślij" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Wprowadzone" #: mod_roster:914 msgid "Subscription" msgstr "Subskrypcja" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Subskrypcje nie są dozwolone" #: mod_muc_log:469 msgid "Sunday" msgstr "Niedziela" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Ta nazwa użytkownika jest używana przez kogoś innego" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Ta nazwa użytkownika jest już zarejestrowana przez inną osobę" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Captcha jest poprawna." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Weryfikacja CAPTCHA nie powiodła się" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "Żądana czynność nie jest obsługiwana przez konferencje" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "Hasło zawiera niedopuszczalne znaki" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Hasło nie jest wystarczająco trudne" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Hasło do Twojego konta zostało zmienione." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "To żądanie jest dopuszczalne wyłącznie dla lokalnych użytkowników" #: mod_roster:203 msgid "The query must not contain elements" msgstr "Żądanie nie może zawierać elementów " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "Żądanie może zawierać wyłącznie jeden z elementów , lub " "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Podczas próby zmiany hasła wystąpił błąd: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Wystąpił błąd podczas tworzenia konta: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Podczas usuwania konta wystąpił błąd: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Pole nie rozróżnia wielkości liter: słowo Hanna jest takie samo jak hAnna " "lub haNNa." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Niniejsza strona pozwala na założenie konta Jabber na tym serwerze. Twój JID " "(Jabber IDentyfikator) będzie miał postać: nazwa_użytkownika@serwer. " "Przeczytaj dokładnie instrukcję i wypełnij pola." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Ta strona pozwala usunąć konto Jabber z tego serwera." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Ten pokój nie jest anonimowy" #: mod_muc_log:466 msgid "Thursday" msgstr "Czwartek" #: mod_offline:690 msgid "Time" msgstr "Czas" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Opóźnienie" #: mod_offline:692 msgid "To" msgstr "Do" #: mod_register:215 msgid "To register, visit ~s" msgstr "Żeby się zarejestrować odwiedź ~s" #: mod_configure:709 msgid "To ~s" msgstr "Do ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Limit czasu tokenu" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Zbyt wiele atrybutów 'xml:lang'" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "Zbyt wiele elementów " #: mod_privacy:164 msgid "Too many elements" msgstr "Zbyt wiele elementów " #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Za dużo żądań CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Zbyt wiele strumieni danych" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Zbyt wiele niepotwierdzonych pakietów" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Zbyt wielu użytkowników konferencji" #: mod_register:355 msgid "Too many users registered" msgstr "Zbyt wielu zarejestrowanych użytkowników" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Wszystkich pokoi" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Limit transferu przekroczony" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transakcje anulowane:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transakcje zakończone:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transakcje zalogowane:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transakcje uruchomione ponownie:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Wtorek" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Nie można wygenerować CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Nie można zarejestrować trasy dla lokalnej domeny" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Nie autoryzowano" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Nieoczekiwana akcja" #: mod_register_web:488 msgid "Unregister" msgstr "Wyrejestruj" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Usuń konto Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "Nieobsługiwany element " #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Nieobsługiwane żądanie MIX" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Aktualizuj" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Aktualizuj wiadomość dnia (bez wysyłania)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Aktualizuj wiadomość dnia na wszystkich hostach (bez wysyłania)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plan aktualizacji" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Skrypt aktualizacji" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Uaktualnij ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Czas pracy:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "Użycie STARTTLS jest zabronione" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Wymagane jest użycie STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Użytkownik" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Użytkownik (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Zarządzanie użytkownikami" #: mod_register:345 msgid "User already exists" msgstr "Użytkownik już istnieje" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "Część użytkownika w 'from' jest pusta" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Sesja użytkownika nie została znaleziona" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Sesja użytkownika została zakończona" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Użytkownik ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Nazwa użytkownika:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Użytkownicy" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Ostatnia aktywność użytkowników" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Użytkowncy nie mogą tak szybko rejestrować nowych kont" #: mod_roster:954 msgid "Validate" msgstr "Potwierdź" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "Wartość 'get' dla atrybutu 'type' jest niedozwolona" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Wartość 'set' dla atrybutu 'type' jest niedozwolona" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "Wartość '~s' powinna być typu logicznego" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "Wartość '~s' powinna być typu daty" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "Wartość '~s' powinna być liczbą" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Wirtualne Hosty" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Uczestnicy tego pokoju nie mogą zmieniać swoich nicków" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Odwiedzający nie mogą wysyłać wiadomości do wszystkich obecnych" #: mod_muc_room:3879 msgid "Voice request" msgstr "Żądanie głosowe" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Głosowe żądania są wyłączone w tym pokoju" #: mod_muc_log:465 msgid "Wednesday" msgstr "Środa" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Możesz później zmienić swoje hasło za pomocą dowolnego klienta Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Zostałeś wykluczony z tego pokoju" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Dołączyłeś do zbyt wielu konferencji" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Musisz wypełnić pole \"Nazwa użytkownika\" w formularzu" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Potrzebujesz klienta obsługującego x:data aby zarejestrować nick" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Potrzebujesz klienta obsługującego x:data aby zarejestrować nick" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Potrzebujesz klienta obsługującego x:data aby skonfigurować mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Potrzebujesz klienta obsługującego x:data aby wyszukiwać" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "Nie masz uprawnień do tworzenia węzłów" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Twoje konto zostało stworzone." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Twoje konto zostało usunięte." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Aktualna lista prywatności zabrania przesyłania tej stanzy." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Kolejka wiadomości offline adresata jest pełna. Wiadomość została odrzucona." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Moduł IRC ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Moduł MUC" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Serwis multicast ejabbera" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Moduł Publish-Subscribe" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Moduł SOCKS5 Bytestreams" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd: Panel Administracyjny" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Moduł vCard ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "został wykluczony" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "został wyrzucony" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "został wyrzucony z powodu wyłączenia systemu" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "został wyrzucony z powodu zmiany przynależności" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "został wyrzucony z powodu zmiany pokoju na \"Tylko dla Członków\"" #: mod_muc_log:410 msgid "is now known as" msgstr "jest teraz znany jako" #: mod_muc_log:370 msgid "joins the room" msgstr "dołącza do pokoju" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "opuszcza pokój" #: mod_muc_room:3856 msgid "private, " msgstr "prywatny, " #: mod_muc_room:3955 msgid "the password is" msgstr "hasło to:" #: mod_vcard:292 msgid "vCard User Search" msgstr "Wyszukiwanie vCard użytkowników" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s konfiguracja zasad dostępu" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s zaprasza Cię do pokoju ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Kolejka wiadomości offline użytkownika ~s" #~ msgid "No resource provided" #~ msgstr "Nie podano zasobu" #~ msgid "Server" #~ msgstr "Serwer" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Zbyt wiele (~p) nieudanych prób logowanie z tego adresu IP (~s). Ten " #~ "adres zostanie odblokowany o ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Proszę podać rozmiar pliku." #~ msgid "Please specify file name." #~ msgstr "Proszę podać nazwę pliku." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Ten adres IP został zablokowany w ~s" #~ msgid "Empty Rooms" #~ msgstr "Puste pokoje" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s jest niepoprawny" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Nieprawidłowa przynależność: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Nieprawidłowa rola: ~s" #~ msgid "No limit" #~ msgstr "Bez limitu" #~ msgid "Present real Jabber IDs to" #~ msgstr "Prawdziwe Jabber ID widoczne dla" #~ msgid "moderators only" #~ msgstr "tylko moderatorzy" #~ msgid "anyone" #~ msgstr "wszystkich" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Role dla których wysyłane są statusy" #~ msgid "Moderator" #~ msgstr "Moderatorzy" #~ msgid "Participant" #~ msgstr "Uczestnicy" #~ msgid "Visitor" #~ msgstr "Odwiedzający" #~ msgid "nobody" #~ msgstr "nikt" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Pozwól użytkownikom wysyłać zaproszenia" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Minimalny odstęp między żądaniami głosowymi (w sekundach)" #~ msgid "Enable message archiving" #~ msgstr "Włącz archiwizowanie rozmów" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Pomiń Jabber ID z żądania CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Potrzebujesz klienta obsługującego x:data aby skonfigurować pokój" #~ msgid "Number of occupants" #~ msgstr "Liczba uczestników" #~ msgid "User JID" #~ msgstr "Użytkownik " #~ msgid "Grant voice to this person?" #~ msgstr "Udzielić głosu tej osobie?" #~ msgid "Node ID" #~ msgstr "ID węzła" #~ msgid "Subscriber Address" #~ msgstr "Adres subskrybenta" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Pozwól temu Jabber ID na zapisanie się do tego węzła PubSub" #~ msgid "Deliver payloads with event notifications" #~ msgstr "" #~ "Dostarczaj zawartość publikacji wraz z powiadomieniami o zdarzeniach" #~ msgid "Deliver event notifications" #~ msgstr "Dostarczaj powiadomienia o zdarzeniach" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Informuj subskrybentów o zmianach konfiguracji węzła" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Informuj subskrybentów o usunięciu węzła" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Informuj subskrybentów o usunięciu elementów węzła" #~ msgid "Persist items to storage" #~ msgstr "Przechowuj na stałe dane PubSub" #~ msgid "A friendly name for the node" #~ msgstr "Przyjazna nazwa węzła" #~ msgid "Max # of items to persist" #~ msgstr "Maksymalna liczba przechowywanych przedmiotów" #~ msgid "Whether to allow subscriptions" #~ msgstr "Czy pozwolić na subskrypcje" #~ msgid "Specify the access model" #~ msgstr "Określ model dostępu" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Grupy kontaktów uprawnione do subskrypcji" #~ msgid "Specify the publisher model" #~ msgstr "Określ model publikującego" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Usuń wszystkie elementy w momencie kiedy publikujący rozłączy się" #~ msgid "Specify the event message type" #~ msgstr "Określ typ wiadomości" #~ msgid "Max payload size in bytes" #~ msgstr "Maksymalna wielkość powiadomienia w bajtach" #~ msgid "When to send the last published item" #~ msgstr "Kiedy wysłać ostatnio opublikowaną rzecz" #~ msgid "Only deliver notifications to available users" #~ msgstr "Dostarczaj powiadomienia tylko dostępnym użytkownikom" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Grupy, do których należy węzeł" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Wypełnij pola aby znaleźć pasujących użytkowników Jabbera" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Serwery zewnętrzne s2s:" #~ msgid "Delete" #~ msgstr "Usuń" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Ten uczestnik został wyrzucony z pokoju ponieważ wysłał komunikat błędu" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Ten uczestnik został wyrzucony z pokoju ponieważ wysłał komunikat błędu " #~ "do innego uczestnika" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Ten uczestnik został wyrzucony z pokoju ponieważ jego informacja o " #~ "statusie zawierała błędy" ejabberd-18.01/priv/msgs/zh.msg0000644000232200023220000005423113225664356016732 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","接受"}. {"Access Configuration","访问配置"}. {"Access Control List Configuration","访问控制列表(ACL)配置"}. {"Access control lists","访问控制列表(ACL)"}. {"Access Control Lists","访问控制列表(ACL)"}. {"Access denied by service policy","访问被服务策略拒绝"}. {"Access rules","访问规则"}. {"Access Rules","访问规则"}. {"Action on user","对用户的动作"}. {"Add Jabber ID","添加Jabber ID"}. {"Add New","添加新用户"}. {"Add User","添加用户"}. {"Administration of ","管理"}. {"Administration","管理"}. {"Administrator privileges required","需要管理员权限"}. {"All activity","所有活动"}. {"Allow users to change the subject","允许用户更改主题"}. {"Allow users to query other users","允许用户查询其它用户"}. {"Allow users to send invites","允许用户发送邀请"}. {"Allow users to send private messages","允许用户发送私聊消息"}. {"Allow visitors to change nickname","允许用户更改昵称"}. {"Allow visitors to send private messages to","允许访客发送私聊消息至"}. {"Allow visitors to send status text in presence updates","更新在线状态时允许用户发送状态文本"}. {"All Users","所有用户"}. {"Announcements","通知"}. {"A password is required to enter this room","进入此房间需要密码"}. {"April","四月"}. {"August","八月"}. {"Backup Management","备份管理"}. {"Backup of ~p","~p的备份"}. {"Backup to File at ","备份文件位于"}. {"Backup","备份"}. {"Bad format","格式错误"}. {"Birthday","出生日期"}. {"CAPTCHA web page","验证码网页"}. {"Change Password","更改密码"}. {"Change User Password","更改用户密码"}. {"Characters not allowed:","禁用字符:"}. {"Chatroom configuration modified","聊天室配置已修改"}. {"Chatroom is created","聊天室已被创建"}. {"Chatroom is destroyed","聊天室已被销毁"}. {"Chatroom is started","聊天室已被启动"}. {"Chatroom is stopped","聊天室已被停用"}. {"Chatrooms","聊天室"}. {"Choose a username and password to register with this server","请选择在此服务器上注册所需的用户名和密码"}. {"Choose modules to stop","请选择要停止的模块"}. {"Choose storage type of tables","请选择表格的存储类型"}. {"Choose whether to approve this entity's subscription.","选择是否允许该实体的订阅"}. {"City","城市"}. {"Commands","命令"}. {"Conference room does not exist","会议室不存在"}. {"Configuration of room ~s","房间~s的配置 "}. {"Configuration","配置"}. {"Connected Resources:","已连接资源:"}. {"Connections parameters","连接参数"}. {"Country","国家"}. {"CPU Time:","CPU时间:"}. {"Database Tables at ~p","位于~p的数据库表"}. {"Database Tables Configuration at ","数据库表格配置位于"}. {"Database","数据库"}. {"December","十二月"}. {"Default users as participants","用户默认被视为参与人"}. {"Delete message of the day on all hosts","删除所有主机上的每日消息"}. {"Delete message of the day","删除每日消息"}. {"Delete Selected","删除已选内容"}. {"Delete User","删除用户"}. {"Description:","描述:"}. {"Disc only copy","仅磁盘复制"}. {"Displayed Groups:","已显示的组:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","不要将密码告诉任何人, 就算是Jabber服务器的管理员也不可以."}. {"Dump Backup to Text File at ","转储备份到文本文件于"}. {"Dump to Text File","转储到文本文件"}. {"Edit Properties","编辑属性"}. {"Either approve or decline the voice request.","接受或拒绝声音请求"}. {"ejabberd IRC module","ejabberd IRC 模块"}. {"ejabberd MUC module","ejabberd MUC 模块"}. {"ejabberd Multicast service","ejabberd多重映射服务"}. {"ejabberd Publish-Subscribe module","ejabberd 发行-订阅模块"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}. {"ejabberd vCard module","ejabberd vCard模块"}. {"ejabberd Web Admin","ejabberd网页管理"}. {"Elements","元素"}. {"Email","电子邮件"}. {"Enable logging","启用服务器端聊天记录"}. {"Encoding for server ~b","服务器~b的编码"}. {"End User Session","结束用户会话"}. {"Enter list of {Module, [Options]}","请输入{模块, [选项]}列表"}. {"Enter nickname you want to register","请输入您想要注册的昵称"}. {"Enter path to backup file","请输入备份文件的路径"}. {"Enter path to jabberd14 spool dir","请输入jabberd14 spool目录的路径"}. {"Enter path to jabberd14 spool file","请输入 jabberd14 spool 文件的路径"}. {"Enter path to text file","请输入文本文件的路径"}. {"Enter the text you see","请输入您所看到的文本"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","请输入您想使用的用来连接到 IRC 服务器的用户名和编码. 按 '下一步' 获取更多待填字段. 按 '完成' 保存设置."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","请输入您想使用的用来连接到IRC服务器的用户名, 编码, 端口和密码."}. {"Erlang Jabber Server","Erlang Jabber服务器"}. {"Error","错误"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","例如: [{\"irc.lucky.net\", \"koi8-r\"}, 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","将所有表以SQL查询语句导出到文件:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器上所有用户的数据导出到 PIEFXIS 文件 (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","将某主机的用户数据导出到 PIEFXIS 文件 (XEP-0227):"}. {"Failed to extract JID from your voice request approval","无法从你的声音请求确认信息中提取JID"}. {"Family Name","姓氏"}. {"February","二月"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","填充表单以搜索任何匹配的Jabber用户(在字段末添加*来匹配子串)"}. {"Friday","星期五"}. {"From ~s","来自~s"}. {"From","从"}. {"Full Name","全名"}. {"Get Number of Online Users","获取在线用户数"}. {"Get Number of Registered Users","获取注册用户数"}. {"Get User Last Login Time","获取用户上次登陆时间"}. {"Get User Password","获取用户密码"}. {"Get User Statistics","获取用户统计"}. {"Groups","组"}. {"Group ","组"}. {"has been banned","已被禁止"}. {"has been kicked because of an affiliation change","因联属关系改变而被踢出"}. {"has been kicked because of a system shutdown","因系统关机而被踢出"}. {"has been kicked because the room has been changed to members-only","因该房间改为只对会员开放而被踢出"}. {"has been kicked","已被踢出"}. {" has set the subject to: ","已将标题设置为: "}. {"Host","主机"}. {"If you don't see the CAPTCHA image here, visit the web page.","如果您在这里没有看到验证码图片, 请访问网页."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","如果您想为 IRC 服务器指定不同的端口, 密码, 编码, 请用 '{\"irc 服务器\", \"编码\", 端口, \"密码\"}' 格式的值填充此表单. 默认情况下此服务使用\"~s\"编码, ~p 端口, 密码为空."}. {"Import Directory","导入目录"}. {"Import File","导入文件"}. {"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. {"Import User from File at ","导入用户的文件位于"}. {"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件 (XEP-0227) 导入用户数据:"}. {"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. {"Import Users from Dir at ","导入用户的目录位于"}. {"Import Users From jabberd14 Spool Files","从 jabberd14 Spool 文件导入用户"}. {"Improper message type","不恰当的消息类型"}. {"Incoming s2s Connections:","入站 s2s 连接:"}. {"Incorrect password","密码不正确"}. {"IP addresses","IP地址"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC频道 (不要输入第一个#号)"}. {"IRC server","IRC服务器"}. {"IRC settings","IRC设置"}. {"IRC Transport","IRC传输"}. {"IRC username","IRC用户名"}. {"IRC Username","IRC用户名"}. {"is now known as","现在称呼为"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许将错误消息发送到该房间. 参与者(~s)已发送过一条消息(~s)并已被踢出房间"}. {"It is not allowed to send private messages of type \"groupchat\"","\"群组聊天\"类型不允许发送私聊消息"}. {"It is not allowed to send private messages to the conference","不允许向会议发送私聊消息"}. {"It is not allowed to send private messages","不可以发送私聊消息"}. {"Jabber Account Registration","Jabber帐户注册"}. {"Jabber ID","Jabber ID"}. {"January","一月"}. {"Join IRC channel","加入IRC频道"}. {"joins the room","加入房间"}. {"Join the IRC channel here.","在这里加入IRC频道."}. {"Join the IRC channel in this Jabber ID: ~s","用此Jabber ID ~s加入IRC频道"}. {"July","七月"}. {"June","六月"}. {"Last Activity","上次活动"}. {"Last login","上次登陆"}. {"Last month","上个月"}. {"Last year","上一年"}. {"leaves the room","离开房间"}. {"Listened Ports at ","监听的端口位于"}. {"Listened Ports","被监听的端口"}. {"List of modules to start","要启动的模块列表"}. {"List of rooms","房间列表"}. {"Low level update script","低级别更新脚本"}. {"Make participants list public","公开参与人列表"}. {"Make room CAPTCHA protected","保护房间验证码"}. {"Make room members-only","设置房间只接收会员"}. {"Make room moderated","设置房间只接收主持人"}. {"Make room password protected","进入此房间需要密码"}. {"Make room persistent","永久保存该房间"}. {"Make room public searchable","使房间可被公开搜索"}. {"March","三月"}. {"Maximum Number of Occupants","允许的与会人最大数"}. {"May","五月"}. {"Membership is required to enter this room","进入此房间需要会员身份"}. {"Members:","会员:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","记住你的密码, 或将其记到纸上并放于安全位置. 如果你忘记了密码, Jabber也没有自动恢复密码的方式."}. {"Memory","内存"}. {"Message body","消息主体"}. {"Middle Name","中间名"}. {"Moderator privileges required","需要主持人权限"}. {"Modified modules","被修改模块"}. {"Modules at ~p","位于~p的模块"}. {"Modules","模块"}. {"Module","模块"}. {"Monday","星期一"}. {"Multicast","多重映射"}. {"Multi-User Chat","多用户聊天"}. {"Name:","姓名:"}. {"Name","姓名"}. {"Never","从未"}. {"New Password:","新密码:"}. {"Nickname Registration at ","昵称注册于"}. {"Nickname ~s does not exist in the room","昵称~s不在该房间"}. {"Nickname","昵称"}. {"No body provided for announce message","通知消息无正文内容"}. {"No Data","没有数据"}. {"Node not found","没有找到节点"}. {"Node ~p","节点~p"}. {"Nodes","节点"}. {"None","无"}. {"Not Found","没有找到"}. {"November","十一月"}. {"Number of online users","在线用户数"}. {"Number of registered users","注册用户数"}. {"October","十月"}. {"Offline Messages:","离线消息:"}. {"Offline Messages","离线消息"}. {"OK","确定"}. {"Old Password:","旧密码: "}. {"Online Users:","在线用户:"}. {"Online Users","在线用户"}. {"Online","在线"}. {"Only members may query archives of this room","只有会员可以查询本房间的存档"}. {"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与人可以在此房间里更改主题"}. {"Only moderators are allowed to change the subject in this room","只有主持人可以在此房间里更改主题"}. {"Only moderators can approve voice requests","仅主持人能确认声音请求"}. {"Only occupants are allowed to send messages to the conference","只有与会人可以向大会发送消息"}. {"Only occupants are allowed to send queries to the conference","只有与会人可以向大会发出查询请求"}. {"Only service administrators are allowed to send service messages","只有服务管理员可以发送服务消息"}. {"Options","选项"}. {"Organization Name","组织名称"}. {"Organization Unit","组织单位"}. {"Outgoing s2s Connections:","出站 s2s 连接:"}. {"Outgoing s2s Connections","出站 s2s 连接"}. {"Owner privileges required","需要持有人权限"}. {"Packet","数据包"}. {"Password ~b","~b的密码"}. {"Password Verification:","密码确认:"}. {"Password Verification","确认密码"}. {"Password:","密码:"}. {"Password","密码"}. {"Path to Dir","目录的路径"}. {"Path to File","文件路径"}. {"Pending","挂起"}. {"Period: ","持续时间: "}. {"Permanent rooms","永久房间"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项仅将备份内置的 Mnesia 数据库. 如果您正在使用 ODBC 模块, 您还需要分别备份您的数据库."}. {"Please, wait for a while before sending new voice request","请稍后再发送新的声音请求"}. {"Pong","Pong"}. {"Port ~b","~b的端口"}. {"Port","端口"}. {"private, ","保密, "}. {"Protocol","协议"}. {"Publish-Subscribe","发行-订阅"}. {"PubSub subscriber request","PubSub订阅人请求"}. {"Queries to the conference members are not allowed in this room","本房间不可以查询会议成员信息"}. {"RAM and disc copy","内存与磁盘复制"}. {"RAM copy","内存(RAM)复制"}. {"Raw","原始格式"}. {"Really delete message of the day?","确实要删除每日消息吗?"}. {"Recipient is not in the conference room","接收人不在会议室"}. {"Register a Jabber account","注册Jabber帐户"}. {"Registered nicknames","注册的昵称"}. {"Registered Users:","注册用户:"}. {"Registered Users","注册用户"}. {"Register","注册"}. {"Registration in mod_irc for ","mod_irc 中的注册是为 "}. {"Remote copy","远程复制"}. {"Remove All Offline Messages","移除所有离线消息"}. {"Remove User","删除用户"}. {"Remove","移除"}. {"Replaced by new connection","被新的连接替换"}. {"Resources","资源"}. {"Restart Service","重启服务"}. {"Restart","重启"}. {"Restore Backup from File at ","要恢复的备份文件位于"}. {"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):"}. {"Restore binary backup immediately:","立即恢复二进制备份:"}. {"Restore plain text backup immediately:","立即恢复普通文本备份:"}. {"Restore","恢复"}. {"Room Configuration","房间配置"}. {"Room creation is denied by service policy","创建房间被服务策略拒绝"}. {"Room description","房间描述"}. {"Room Occupants","房间人数"}. {"Room title","房间标题"}. {"Roster of ","花名册属于"}. {"Roster size","花名册大小"}. {"Roster","花名册"}. {"RPC Call Error","RPC 调用错误"}. {"Running Nodes","运行中的节点"}. {"~s access rule configuration","~s访问规则配置"}. {"Saturday","星期六"}. {"Script check","脚本检查"}. {"Search Results for ","搜索结果属于关键词 "}. {"Search users in ","搜索用户于"}. {"Send announcement to all online users on all hosts","发送通知给所有主机的在线用户"}. {"Send announcement to all online users","发送通知给所有在线用户"}. {"Send announcement to all users on all hosts","发送通知给所有主机上的所有用户"}. {"Send announcement to all users","发送通知给所有用户"}. {"September","九月"}. {"Server ~b","服务器~b"}. {"Server:","服务器:"}. {"Set message of the day and send to online users","设定每日消息并发送给所有在线用户"}. {"Set message of the day on all hosts and send to online users","设置所有主机上的每日消息并发送给在线用户"}. {"Shared Roster Groups","共享的花名册组群"}. {"Show Integral Table","显示完整列表"}. {"Show Ordinary Table","显示普通列表"}. {"Shut Down Service","关闭服务"}. {"~s invites you to the room ~s","~s邀请你到房间~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 Jabber 客户端可以在你的计算机里存储密码. 请仅在你确认你的计算机安全的情况下使用该功能."}. {"~s's Offline Messages Queue","~s的离线消息队列"}. {"Start Modules at ","要启动的模块位于 "}. {"Start Modules","启动模块"}. {"Start","开始"}. {"Statistics of ~p","~p的统计"}. {"Statistics","统计"}. {"Stop Modules at ","要停止的模块位于 "}. {"Stop Modules","停止模块"}. {"Stopped Nodes","已经停止的节点"}. {"Stop","停止"}. {"Storage Type","存储类型"}. {"Store binary backup:","存储为二进制备份:"}. {"Store plain text backup:","存储为普通文本备份:"}. {"Subject","标题"}. {"Submitted","已提交"}. {"Submit","提交"}. {"Subscription","订阅"}. {"Sunday","星期天"}. {"That nickname is already in use by another occupant","该昵称已被另一用户使用"}. {"That nickname is registered by another person","该昵称已被另一个人注册了"}. {"The CAPTCHA is valid.","验证码有效."}. {"The CAPTCHA verification has failed","验证码检查失败"}. {"The password is too weak","密码强度太弱"}. {"the password is","密码是"}. {"The password of your Jabber account was successfully changed.","你的Jabber帐户密码已成功更新."}. {"There was an error changing the password: ","修改密码出错: "}. {"There was an error creating the account: ","帐户创建出错: "}. {"There was an error deleting the account: ","帐户删除失败: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写: macbeth 与 MacBeth 和 Macbeth 是一样的."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器上创建Jabber帐户. 你的JID (Jabber ID) 的形式如下: 用户名@服务器. 请仔细阅读说明并正确填写相应字段."}. {"This page allows to unregister a Jabber account in this Jabber server.","此页面允许在此Jabber服务器上注销Jabber帐户"}. {"This room is not anonymous","此房间不是匿名房间"}. {"Thursday","星期四"}. {"Time delay","时间延迟"}. {"Time","时间"}. {"Too many CAPTCHA requests","验证码请求太多"}. {"Too many unacked stanzas","未被确认的节太多"}. {"To ~s","发送给~s"}. {"Total rooms","所有房间"}. {"To","到"}. {"Traffic rate limit is exceeded","已经超过传输率限制"}. {"Transactions Aborted:","取消的事务:"}. {"Transactions Committed:","提交的事务:"}. {"Transactions Logged:","记入日志的事务:"}. {"Transactions Restarted:","重启的事务:"}. {"Tuesday","星期二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. {"Unauthorized","未认证的"}. {"Unregister a Jabber account","注销Jabber帐户"}. {"Unregister","取消注册"}. {"Update message of the day (don't send)","更新每日消息(不发送)"}. {"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. {"Update plan","更新计划"}. {"Update ~p","更新~p"}. {"Update script","更新脚本"}. {"Update","更新"}. {"Uptime:","正常运行时间:"}. {"Use of STARTTLS required","要求使用 STARTTLS"}. {"User Management","用户管理"}. {"Username:","用户名:"}. {"Users are not allowed to register accounts so quickly","不允许用户太频繁地注册帐户"}. {"Users Last Activity","用户上次活动"}. {"Users","用户"}. {"User ~s","用户~s"}. {"User","用户"}. {"Validate","确认"}. {"vCard User Search","vCard用户搜索"}. {"Virtual Hosts","虚拟主机"}. {"Visitors are not allowed to change their nicknames in this room","此房间不允许用户更改昵称"}. {"Visitors are not allowed to send messages to all occupants","不允许访客给所有占有者发送消息"}. {"Voice requests are disabled in this conference","该会议的声音请求以被禁用"}. {"Voice request","声音请求"}. {"Wednesday","星期三"}. {"You can later change your password using a Jabber client.","你可以稍后用Jabber客户端修改你的密码."}. {"You have been banned from this room","您已被禁止进入该房间"}. {"You must fill in field \"Nickname\" in the form","您必须填充表单中\"昵称\"项"}. {"You need a client that supports x:data and CAPTCHA to register","您需要一个支持 x:data 和验证码的客户端进行注册"}. {"You need a client that supports x:data to register the nickname","您需要一个支持 x:data 的客户端来注册昵称"}. {"You need an x:data capable client to configure mod_irc settings","您需要一个兼容 x:data 的客户端来配置mod_irc设置"}. {"You need an x:data capable client to search","您需要一个兼容 x:data 的客户端来搜索"}. {"Your active privacy list has denied the routing of this stanza.","你的活跃私聊列表拒绝了在此房间进行路由分发."}. {"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满. 消息已被丢弃"}. {"Your Jabber account was successfully created.","你的Jabber帐户已成功创建."}. {"Your Jabber account was successfully deleted.","你的 Jabber 帐户已成功删除."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s"}. ejabberd-18.01/priv/msgs/ru.po0000644000232200023220000023645513225664356016601 0ustar debalancedebalance# , 2010. msgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: 2017-10-06 23:43+0300\n" "Last-Translator: Evgeniy Khramtsov \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Russian (русский)\n" "X-Additional-Translator: Konstantin Khomoutov\n" "X-Additional-Translator: Sergei Golovan\n" "X-Generator: Lokalize 1.0\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Poedit-Basepath: .\n" "X-Poedit-SearchPath-0: /home/xram/git/ejabberd/src\n" #: mod_muc_log:413 #: mod_muc_log:752 msgid " has set the subject to: " msgstr " установил(а) тему: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Чтобы войти в эту конференцию, нужен пароль" #: ejabberd_oauth:448 msgid "Accept" msgstr "Принять" #: mod_configure:1109 msgid "Access Configuration" msgstr "Конфигурация доступа" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Конфигурация списков управления доступом" #: ejabberd_web_admin:758 #: ejabberd_web_admin:797 #: mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Списки управления доступом" #: ejabberd_web_admin:863 #: ejabberd_web_admin:896 #: mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Правила доступа" #: mod_configure:1095 msgid "Access control lists" msgstr "Списки управления доступом" #: ejabberd_c2s:414 #: ejabberd_c2s:661 #: ejabberd_s2s:372 #: ejabberd_service:202 #: mod_announce:255 #: mod_announce:273 #: mod_announce:275 #: mod_announce:277 #: mod_announce:279 #: mod_announce:281 #: mod_announce:283 #: mod_announce:285 #: mod_announce:287 #: mod_announce:289 #: mod_announce:291 #: mod_announce:351 #: mod_announce:353 #: mod_announce:355 #: mod_announce:357 #: mod_announce:359 #: mod_announce:361 #: mod_announce:363 #: mod_announce:365 #: mod_announce:367 #: mod_announce:369 #: mod_announce:420 #: mod_announce:846 #: mod_configure:206 #: mod_configure:219 #: mod_configure:220 #: mod_configure:221 #: mod_configure:222 #: mod_configure:224 #: mod_configure:225 #: mod_configure:226 #: mod_configure:227 #: mod_configure:229 #: mod_configure:231 #: mod_configure:233 #: mod_configure:235 #: mod_configure:237 #: mod_configure:239 #: mod_configure:241 #: mod_configure:243 #: mod_configure:245 #: mod_configure:247 #: mod_configure:249 #: mod_configure:251 #: mod_configure:253 #: mod_configure:255 #: mod_configure:257 #: mod_configure:259 #: mod_configure:261 #: mod_configure:263 #: mod_configure:265 #: mod_configure:267 #: mod_configure:313 #: mod_configure:435 #: mod_configure:780 #: mod_configure:782 #: mod_configure:784 #: mod_configure:786 #: mod_configure:788 #: mod_configure:790 #: mod_configure:792 #: mod_configure:794 #: mod_configure:1740 #: mod_http_upload:545 #: mod_irc:265 #: mod_legacy_auth:136 #: mod_muc:401 #: mod_proxy65_service:186 #: mod_proxy65_service:235 #: mod_register:130 #: mod_register:273 #: mod_register:325 #: mod_register:326 #: mod_roster:187 #: mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Доступ запрещён политикой службы" #: mod_configure:1113 msgid "Access rules" msgstr "Правила доступа" #: mod_configure:1772 msgid "Action on user" msgstr "Действие над пользователем" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Добавить Jabber ID" #: ejabberd_web_admin:1277 #: mod_shared_roster:825 msgid "Add New" msgstr "Добавить" #: ejabberd_web_admin:1444 #: mod_configure:166 #: mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Добавить пользователя" #: ejabberd_web_admin:687 #: ejabberd_web_admin:698 msgid "Administration" msgstr "Администрирование" #: mod_configure:1767 msgid "Administration of " msgstr "Администрирование " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Требуются права администратора" #: mod_configure:507 msgid "All Users" msgstr "Все пользователи" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Вся статистика" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Разрешить пользователям изменять тему" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Разрешить iq-запросы к пользователям" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Разрешить пользователям посылать приглашения" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Разрешить приватные сообщения" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Разрешить посетителям изменять псевдоним" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Разрешить посетителям посылать приватные сообщения" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Разрешить посетителям вставлять текcт статуса в сообщения о присутствии" #: mod_announce:611 msgid "Announcements" msgstr "Объявления" #: mod_muc_log:476 msgid "April" msgstr "апреля" #: mod_muc_log:480 msgid "August" msgstr "августа" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "Автоматическое создание узлов недоступно" #: ejabberd_web_admin:1867 #: mod_configure:148 #: mod_configure:617 msgid "Backup" msgstr "Резервное копирование" #: mod_configure:584 msgid "Backup Management" msgstr "Управление резервным копированием" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Резервное копирование ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Резервное копирование в файл на " #: ejabberd_web_admin:763 #: ejabberd_web_admin:802 #: ejabberd_web_admin:868 #: ejabberd_web_admin:901 #: ejabberd_web_admin:937 #: ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 #: ejabberd_web_admin:1861 #: ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 #: mod_roster:972 #: mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Неправильный формат" #: mod_vcard_ldap:334 #: mod_vcard_ldap:347 #: mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 #: mod_vcard_sql:160 #: mod_vcard_sql:174 msgid "Birthday" msgstr "День рождения" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Требуются имя пользователя и ресурс" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Поток данных уже активирован" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Ссылка на капчу" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Процессорное время:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "Невозможно удалить активный список" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "Невозможно удалить список по умолчанию" #: ejabberd_web_admin:1679 #: mod_register_web:194 #: mod_register_web:353 #: mod_register_web:361 #: mod_register_web:386 msgid "Change Password" msgstr "Сменить пароль" #: mod_configure:174 #: mod_configure:528 msgid "Change User Password" msgstr "Изменить пароль пользователя" #: mod_register:295 msgid "Changing password is not allowed" msgstr "Изменение пароля не разрешено" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "Изменение роли/ранга не разрешено" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Недопустимые символы:" #: mod_muc_log:358 #: mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Конфигурация комнаты изменилась" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Комната создана" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Комната уничтожена" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Комната запущена" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Комната остановлена" #: mod_muc:525 #: mod_muc_admin:440 msgid "Chatrooms" msgstr "Комнаты" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Выберите имя пользователя и пароль для регистрации на этом сервере" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Выберите модули, которые следует остановить" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Выберите тип хранения таблиц" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Решите: предоставить ли подписку этому объекту." #: mod_vcard_ldap:336 #: mod_vcard_ldap:349 #: mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 #: mod_vcard_sql:162 #: mod_vcard_sql:176 msgid "City" msgstr "Город" #: mod_adhoc:118 #: mod_adhoc:147 #: mod_adhoc:163 #: mod_adhoc:179 msgid "Commands" msgstr "Команды" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Конференция не существует" #: mod_configure:129 #: mod_configure:286 #: mod_configure:306 #: mod_configure:504 msgid "Configuration" msgstr "Конфигурация" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Конфигурация комнаты ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Подключённые ресурсы:" #: mod_irc:526 msgid "Connections parameters" msgstr "Параметры соединения" #: mod_vcard_ldap:335 #: mod_vcard_ldap:348 #: mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 #: mod_vcard_sql:161 #: mod_vcard_sql:175 msgid "Country" msgstr "Страна" #: ejabberd_web_admin:1866 #: mod_configure:139 #: mod_configure:580 msgid "Database" msgstr "База данных" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Конфигурация таблиц базы данных на " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Таблицы базы данных на ~p" #: mod_blocking:267 #: mod_carboncopy:137 #: mod_irc:486 #: mod_irc:574 #: mod_irc:739 #: mod_last:215 #: mod_mam:501 #: mod_muc:749 #: mod_offline:291 #: mod_offline:582 #: mod_privacy:186 #: mod_privacy:204 #: mod_privacy:298 #: mod_privacy:314 #: mod_privacy:347 #: mod_privacy:364 #: mod_private:103 #: mod_private:110 #: mod_proxy65_service:231 #: mod_pubsub:3506 #: mod_pubsub:3513 #: mod_pubsub:3573 #: mod_pubsub:3598 #: mod_pubsub:3604 #: mod_pubsub:3607 #: mod_vcard:226 #: node_flat_sql:801 #: nodetree_tree_sql:128 #: nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Ошибка базы данных" #: mod_muc_log:484 msgid "December" msgstr "декабря" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Сделать пользователей участниками по умолчанию" #: ejabberd_web_admin:811 #: ejabberd_web_admin:911 #: mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Удалить выделенные" #: mod_configure:168 #: mod_configure:522 #: mod_configure:1135 msgid "Delete User" msgstr "Удалить пользователя" #: mod_announce:629 msgid "Delete message of the day" msgstr "Удалить сообщение дня" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Удалить сообщение дня со всех виртуальных серверов" #: mod_shared_roster:900 msgid "Description:" msgstr "Описание:" #: mod_configure:889 msgid "Disc only copy" msgstr "только диск" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Видимые группы:" #: mod_register_web:251 msgid "Don't tell your password to anybody, not even the administrators of the Jabber server." msgstr "Не говорите никому свой пароль, даже администраторам сервера." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Копирование в текстовый файл на " #: mod_configure:154 #: mod_configure:621 msgid "Dump to Text File" msgstr "Копирование в текстовый файл" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Группы с одинаковыми названиями запрещены стандартом RFC6121" #: mod_configure:1776 msgid "Edit Properties" msgstr "Изменить параметры" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Подтвердите или отклоните право голоса." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Элементы" #: mod_vcard_ldap:337 #: mod_vcard_ldap:350 #: mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 #: mod_vcard_sql:163 #: mod_vcard_sql:177 msgid "Email" msgstr "Электронная почта" #: mod_register:292 msgid "Empty password" msgstr "Пустой пароль" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Включить журналирование" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "Включение push-режима без атрибута 'node' не поддерживается" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Кодировка сервера ~b" #: mod_configure:170 #: mod_configure:524 #: mod_configure:1145 msgid "End User Session" msgstr "Завершить сеанс пользователя" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Введите список вида {Module, [Options]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Введите псевдоним, который Вы хотели бы зарегистрировать" #: mod_configure:950 #: mod_configure:962 msgid "Enter path to backup file" msgstr "Введите путь к резервному файлу" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Введите путь к директории спула jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Введите путь к файлу из спула jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Введите путь к текстовому файлу" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Введите увиденный текст" #: mod_irc:759 msgid "Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings." msgstr "Введите имя пользователя и кодировки, которые будут использоваться при подключении к IRC-серверам. Нажмите 'Далее' для получения дополнительных полей для заполнения. Нажмите 'Завершить' для сохранения настроек." #: mod_irc:540 msgid "Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers" msgstr "Введите имя пользователя, кодировки, порты и пароли, которые будут использоваться при подключении к IRC-серверам" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 #: ejabberd_web_admin:2147 msgid "Error" msgstr "Ошибка" #: mod_irc:520 msgid "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "Пример: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Экспортировать все таблицы в виде SQL запросов в файл:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "Экспорт данных всех пользователей сервера в файлы формата PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Экспорт пользовательских данных домена в файлы формата PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Ошибка внешнего сервиса" #: mod_delegation:283 msgid "External component timeout" msgstr "Таймаут внешнего сервиса" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Ошибка при активировании потока данных" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Ошибка обработки JID из вашего запроса на право голоса" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Не получилось найти внешний сервис, делегирующий это пространство имён" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Ошибка разбора HTTP ответа" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Ошибка разбора ChanServ" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Ошибка обработки опции '~s'" #: mod_vcard_ldap:332 #: mod_vcard_ldap:345 #: mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 #: mod_vcard_sql:158 #: mod_vcard_sql:172 msgid "Family Name" msgstr "Фамилия" #: mod_muc_log:474 msgid "February" msgstr "февраля" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "Файл больше ~w байт" #: mod_vcard:437 msgid "Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)" msgstr "Заполните форму для поиска пользователя Jabber (Если добавить * в конец поля, то происходит поиск подстроки)" #: mod_muc_log:467 msgid "Friday" msgstr "Пятница" #: mod_offline:691 msgid "From" msgstr "От кого" #: mod_configure:723 msgid "From ~s" msgstr "От ~s" #: mod_vcard_ldap:329 #: mod_vcard_ldap:342 #: mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 #: mod_vcard_sql:155 #: mod_vcard_sql:169 msgid "Full Name" msgstr "Полное имя" #: mod_configure:183 #: mod_configure:536 msgid "Get Number of Online Users" msgstr "Получить количество подключённых пользователей" #: mod_configure:180 #: mod_configure:534 msgid "Get Number of Registered Users" msgstr "Получить количество зарегистрированных пользователей" #: mod_configure:176 #: mod_configure:530 #: mod_configure:1179 msgid "Get User Last Login Time" msgstr "Получить время последнего подключения пользователя" #: mod_configure:172 #: mod_configure:526 #: mod_configure:1155 #: mod_configure:1165 msgid "Get User Password" msgstr "Получить пароль пользователя" #: mod_configure:178 #: mod_configure:532 #: mod_configure:1188 msgid "Get User Statistics" msgstr "Получить статистику по пользователю" #: mod_vcard_ldap:330 #: mod_vcard_ldap:343 msgid "Given Name" msgstr "Имя" #: mod_shared_roster:923 msgid "Group " msgstr "Группа " #: mod_roster:916 msgid "Groups" msgstr "Группы" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Хост" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Неизвестное имя сервера" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP адреса" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Транспорт" #: mod_irc:496 msgid "IRC Username" msgstr "Имя пользователя IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Канал IRC (без символа #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "IRC соединение не найдено" #: mod_irc:673 msgid "IRC server" msgstr "Сервер IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Настройки IRC" #: mod_irc:766 msgid "IRC username" msgstr "Имя пользователя IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Если вы не видите изображение капчи, перейдите по ссылке." #: mod_irc:503 msgid "If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password." msgstr "Чтобы указать различные порты, пароли, кодировки для разных серверов IRC, заполните список значениями в формате '{\"сервер IRC\", \"кодировка\", порт, \"пароль\"}'. По умолчанию сервис использует кодировку \"~s\", порт ~p, пустой пароль." #: mod_configure:160 #: mod_configure:634 msgid "Import Directory" msgstr "Импорт из директории" #: mod_configure:157 #: mod_configure:632 msgid "Import File" msgstr "Импорт из файла" #: mod_configure:982 msgid "Import User from File at " msgstr "Импорт пользователя из файла на " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Импорт пользователей из спула jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Импорт пользователей из директории на " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Импорт пользовательских данных из буферного файла jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Импорт пользовательских данных из файла формата PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Импорт пользовательских данных из буферной директории jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Неправильный атрибут 'from'" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Неправильный атрибут 'to'" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Неправильная доменная часть атрибута 'from'" #: mod_muc_room:260 msgid "Improper message type" msgstr "Неправильный тип сообщения" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Входящие s2s соединения:" #: mod_muc_room:3669 #: mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "Неверный ввод капчи" #: mod_muc:772 #: mod_muc_room:3052 #: mod_pubsub:1194 #: mod_register:183 #: mod_vcard:256 msgid "Incorrect data form" msgstr "Некорректная форма данных" #: mod_muc_room:1943 #: mod_register:300 #: mod_register:350 msgid "Incorrect password" msgstr "Неправильный пароль" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Некорректное значение в форме данных" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Некорректное значение атрибута 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Некорректное значение 'action' в форме данных" #: mod_configure:1335 #: mod_configure:1367 #: mod_configure:1399 #: mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Некорректное значение 'path' в форме данных" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Некорректное значение атрибута 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Недостаточно прав" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Некорректный атрибут 'from' в пересланном сообщении" #: mod_privilege:300 msgid "Invalid element" msgstr "Неверный элемент " #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "Рассылка приглашений отключена в этой конференции" #: mod_muc_room:233 #: mod_muc_room:375 #: mod_muc_room:1046 msgid "It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room" msgstr "Запрещено посылать сообщения об ошибках в эту комнату. Участник (~s) послал сообщение об ошибке (~s) и был выкинут из комнаты" #: mod_muc_room:418 #: mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Запрещено посылать приватные сообщения" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Нельзя посылать частные сообщения типа \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Не разрешается посылать частные сообщения прямо в конференцию" #: mod_register_web:181 #: mod_register_web:189 msgid "Jabber Account Registration" msgstr "Регистрация Jabber-аккаунта" #: mod_configure:1122 #: mod_configure:1139 #: mod_configure:1149 #: mod_configure:1159 #: mod_configure:1169 #: mod_configure:1183 #: mod_configure:1192 #: mod_configure:1600 #: mod_configure:1644 #: mod_configure:1669 #: mod_roster:912 #: mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "января" #: mod_irc:665 msgid "Join IRC channel" msgstr "Присоединиться к каналу IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Присоединяйтесь к каналу IRC" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Присоединиться к каналу IRC с Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "июля" #: mod_muc_log:478 msgid "June" msgstr "июня" #: ejabberd_web_admin:1488 #: ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Последнее подключение" #: mod_configure:1646 msgid "Last login" msgstr "Время последнего подключения" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "За последний месяц" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "За последний год" #: mod_configure:941 msgid "List of modules to start" msgstr "Список запускаемых модулей" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Список комнат" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Прослушиваемые порты" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Прослушиваемые порты на " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Низкоуровневый сценарий обновления" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Сделать список участников видимым всем" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Сделать комнату защищённой капчей" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Комната только для зарегистрированных участников" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Сделать комнату модерируемой" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Сделать комнату защищённой паролем" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Сделать комнату постоянной" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Сделать комнату видимой всем" #: mod_register:317 msgid "Malformed username" msgstr "Недопустимое имя пользователя" #: mod_muc_log:475 msgid "March" msgstr "марта" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Максимальное количество участников" #: mod_muc_log:477 msgid "May" msgstr "мая" #: mod_shared_roster:907 msgid "Members:" msgstr "Члены:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "В эту конференцию могут входить только её члены" #: mod_register_web:262 msgid "Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it." msgstr "Запомните пароль или запишите его на бумаге, которую сохраните в безопасном месте. В Jabber'е нет автоматизированного средства восстановления пароля в том случае, если Вы его забудете." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Память" #: mod_announce:520 #: mod_configure:1039 #: mod_configure:1079 msgid "Message body" msgstr "Тело сообщения" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Сообщение не найдено в пересылаемом вложении" #: mod_vcard_ldap:331 #: mod_vcard_ldap:344 #: mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 #: mod_vcard_sql:157 #: mod_vcard_sql:171 msgid "Middle Name" msgstr "Отчество" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Отсутствует 'channel' или 'server' в форме данных" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Отсутствует атрибут 'from'" #: xmpp_stream_in:472 #: xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Отсутствует атрибут 'to'" #: mod_muc_room:2536 #: mod_muc_room:3721 #: mod_muc_room:3765 #: mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Требуются права модератора" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Изменённые модули" #: ejabberd_web_admin:2465 #: ejabberd_web_admin:2620 msgid "Module" msgstr "Модуль" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "Ошибка модуля при обработке запроса" #: ejabberd_web_admin:1885 #: mod_configure:582 #: mod_configure:595 msgid "Modules" msgstr "Модули" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Модули на ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Понедельник" #: mod_muc_admin:346 #: mod_muc_admin:349 #: mod_muc_admin:365 #: mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Конференция" #: mod_multicast:267 msgid "Multicast" msgstr "Мультикаст" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Множественные элементы запрещены стандартом RFC6121" #: ejabberd_web_admin:1951 #: mod_vcard_mnesia:101 #: mod_vcard_mnesia:115 #: mod_vcard_sql:156 #: mod_vcard_sql:170 msgid "Name" msgstr "Название" #: mod_shared_roster:896 msgid "Name:" msgstr "Название:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Не найден атрибут 'jid' или 'nick'" #: mod_muc_room:2518 #: mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Не найден атрибут 'role' или 'affiliation'" #: ejabberd_web_admin:1506 #: ejabberd_web_admin:1687 #: mod_configure:1629 msgid "Never" msgstr "Никогда" #: mod_register_web:377 msgid "New Password:" msgstr "Новый пароль:" #: mod_roster:913 #: mod_vcard_ldap:333 #: mod_vcard_ldap:346 #: mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 #: mod_vcard_sql:159 #: mod_vcard_sql:173 msgid "Nickname" msgstr "Псевдоним" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Регистрация псевдонима на " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Псевдоним ~s в комнате отсутствует" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Не найден 'access' в форме данных" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Не найден 'acls' в форме данных" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "Не найден атрибут 'affiliation'" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "Элемент 'item' не найден" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Не найден 'modules' в форме данных" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Не найден 'password' в форме данных" #: mod_register:148 msgid "No 'password' found in this query" msgstr "Не найден 'password' в этом запросе" #: mod_configure:1319 #: mod_configure:1350 #: mod_configure:1382 #: mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Не найден 'path' в форме данных" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "Не найден атрибут 'to' в этом приглашении" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Нет данных" #: ejabberd_local:181 msgid "No available resource found" msgstr "Нет доступных ресурсов" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Тело объявления не должно быть пустым" #: mod_irc:335 #: mod_pubsub:1183 #: mod_pubsub:3289 msgid "No data form found" msgstr "Форма данных не найдена" #: mod_disco:224 #: mod_vcard:282 msgid "No features available" msgstr "Свойства недоступны" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Ни один из хуков не выполнил эту команду" #: mod_last:218 msgid "No info about last activity found" msgstr "Не найдено информации о последней активности" #: mod_blocking:99 msgid "No items found in this query" msgstr "Не найдены элементы в этом запросе" #: ejabberd_local:90 #: ejabberd_sm:863 #: mod_blocking:92 #: mod_blocking:110 #: mod_http_upload:513 #: mod_muc:482 #: mod_muc:534 #: mod_muc:556 #: mod_muc:580 #: mod_offline:303 #: mod_privacy:168 #: mod_privacy:285 #: mod_roster:207 msgid "No module is handling this query" msgstr "Нет модуля который бы обработал этот запрос" #: mod_pubsub:1541 msgid "No node specified" msgstr "Узел не указан" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "Не найдено ожидающих решения подписок" #: mod_privacy:201 #: mod_privacy:295 #: mod_privacy:311 #: mod_privacy:344 msgid "No privacy list with this name found" msgstr "Списка приватности с таким именем не найдено" #: mod_private:96 msgid "No private data found in this query" msgstr "Приватные данные не найдены в этом запросе" #: mod_configure:869 #: mod_configure:908 #: mod_configure:1222 #: mod_configure:1254 #: mod_configure:1275 #: mod_configure:1314 #: mod_configure:1345 #: mod_configure:1377 #: mod_configure:1408 #: mod_configure:1428 #: mod_stats:93 msgid "No running node found" msgstr "Нет работающих узлов" #: mod_disco:252 #: mod_vcard:265 msgid "No services available" msgstr "Нет доступных сервисов" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Не найдено статистики для этого элемента" #: nodetree_dag:72 #: nodetree_tree:181 #: nodetree_tree_sql:255 msgid "Node already exists" msgstr "Узел уже существует" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Индекс узла не найден" #: ejabberd_web_admin:1046 #: mod_irc:303 #: mod_irc:366 #: mod_muc:532 #: mod_muc:680 #: nodetree_dag:78 #: nodetree_dag:102 #: nodetree_dag:118 #: nodetree_dag:142 #: nodetree_dag:229 #: nodetree_tree:74 #: nodetree_tree:80 #: nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Узел не найден" #: ejabberd_web_admin:1857 #: ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Узел ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Ошибка применения профиля Nodeprep" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Узлы" #: ejabberd_web_admin:1622 #: ejabberd_web_admin:1818 #: ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 #: mod_roster:907 msgid "None" msgstr "Нет" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Не Найдено" #: mod_disco:296 #: mod_disco:370 #: mod_last:159 msgid "Not subscribed" msgstr "Нет подписки" #: mod_muc_log:483 msgid "November" msgstr "ноября" #: mod_configure:1212 msgid "Number of online users" msgstr "Количество подключённых пользователей" #: mod_configure:1202 msgid "Number of registered users" msgstr "Количество зарегистрированных пользователей" #: ejabberd_web_admin:2000 #: ejabberd_web_admin:2010 #: ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 #: ejabberd_web_admin:2040 #: ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 #: ejabberd_web_admin:2081 #: ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 #: ejabberd_web_admin:2118 msgid "OK" msgstr "Продолжить" #: mod_muc_log:482 msgid "October" msgstr "октября" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Офлайновые сообщения" #: mod_offline:761 msgid "Offline Messages:" msgstr "Офлайновые сообщения:" #: mod_register_web:373 msgid "Old Password:" msgstr "Старый пароль:" #: ejabberd_web_admin:1524 #: ejabberd_web_admin:1698 #: mod_configure:1639 msgid "Online" msgstr "Подключён" #: ejabberd_web_admin:974 #: ejabberd_web_admin:1367 #: mod_configure:506 msgid "Online Users" msgstr "Подключённые пользователи" #: ejabberd_web_admin:1580 #: ejabberd_web_admin:1599 #: ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Подключённые пользователи:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Допустимы только тэги или " #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Только элемент допустим в этом запросе" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Только члены могут запрашивать архивы этой комнаты" #: mod_muc_room:773 msgid "Only moderators and participants are allowed to change the subject in this room" msgstr "Только модераторы и участники могут изменять тему в этой комнате" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Только модераторы могут изменять тему в этой комнате" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Только модераторы могут утверждать запросы на право голоса" #: mod_muc_room:424 #: mod_muc_room:792 #: mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Только присутствующим разрешается посылать сообщения в конференцию" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Только присутствующим разрешается посылать запросы в конференцию" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Только администратор службы может посылать служебные сообщения" #: ejabberd_web_admin:2466 #: ejabberd_web_admin:2621 msgid "Options" msgstr "Параметры" #: mod_vcard_ldap:338 #: mod_vcard_ldap:351 #: mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 #: mod_vcard_sql:164 #: mod_vcard_sql:178 msgid "Organization Name" msgstr "Название организации" #: mod_vcard_ldap:339 #: mod_vcard_ldap:352 #: mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 #: mod_vcard_sql:165 #: mod_vcard_sql:179 msgid "Organization Unit" msgstr "Отдел организации" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Исходящие s2s-соединения" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Исходящие s2s-серверы:" #: mod_muc_room:3023 #: mod_muc_room:3067 #: mod_muc_room:3697 #: mod_pubsub:1302 #: mod_pubsub:1394 #: mod_pubsub:1553 #: mod_pubsub:2118 #: mod_pubsub:2184 #: mod_pubsub:2383 #: mod_pubsub:2464 #: mod_pubsub:3063 #: mod_pubsub:3206 msgid "Owner privileges required" msgstr "Требуются права владельца" #: mod_offline:693 msgid "Packet" msgstr "Пакет" #: mod_irc:578 msgid "Parse error" msgstr "Ошибка разбора" #: mod_configure:1299 #: mod_configure:1468 #: mod_configure:1513 msgid "Parse failed" msgstr "Ошибка разбора" #: ejabberd_oauth:431 #: ejabberd_web_admin:1435 #: mod_configure:1126 #: mod_configure:1173 #: mod_configure:1602 #: mod_configure:1781 #: mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Пароль" #: mod_configure:1130 msgid "Password Verification" msgstr "Проверка пароля" #: mod_register_web:268 #: mod_register_web:381 msgid "Password Verification:" msgstr "Проверка пароля:" #: mod_irc:822 msgid "Password ~b" msgstr "Пароль ~b" #: ejabberd_web_admin:1713 #: mod_register_web:245 #: mod_register_web:483 msgid "Password:" msgstr "Пароль:" #: mod_configure:998 msgid "Path to Dir" msgstr "Путь к директории" #: mod_configure:952 #: mod_configure:964 #: mod_configure:976 #: mod_configure:987 msgid "Path to File" msgstr "Путь к файлу" #: mod_roster:915 msgid "Pending" msgstr "Ожидание" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Период" #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Постоянные комнаты" #: mod_adhoc:168 #: mod_adhoc:259 msgid "Ping" msgstr "Пинг" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Некорректный пинг-запрос" #: ejabberd_web_admin:1983 msgid "Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately." msgstr "Заметьте, что здесь производится резервное копирование только встроенной базы данных Mnesia. Если Вы также используете другое хранилище данных (например с помощью модуля ODBC), то его резервное копирование следует осуществлять отдельно." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Пожалуйста, подождите перед тем как подать новый запрос на право голоса" #: mod_adhoc:274 msgid "Pong" msgstr "Понг" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Порт" #: mod_irc:828 msgid "Port ~b" msgstr "Порт ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Включение атрибута 'ask' запрещено стандартом RFC6121" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Протокол" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Запрос подписчика PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Публикация-Подписка" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Недоступна публикация элементов в коллекцию узлов" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Запросы к пользователям в этой конференции запрещены" #: mod_blocking:85 #: mod_disco:325 #: mod_disco:393 #: mod_offline:270 #: mod_privacy:146 #: mod_private:118 #: mod_roster:163 #: mod_sic:90 msgid "Query to another users is forbidden" msgstr "Запрос к другим пользователям запрещён" #: mod_configure:889 msgid "RAM and disc copy" msgstr "ОЗУ и диск" #: mod_configure:889 msgid "RAM copy" msgstr "ОЗУ" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Ошибка вызова RPC" #: ejabberd_web_admin:806 #: ejabberd_web_admin:905 msgid "Raw" msgstr "Необработанный формат" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Действительно удалить сообщение дня?" #: mod_muc_room:395 #: mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Адресата нет в конференции" #: mod_register_web:275 msgid "Register" msgstr "Зарегистрировать" #: mod_register_web:192 #: mod_register_web:210 #: mod_register_web:218 msgid "Register a Jabber account" msgstr "Зарегистрировать Jabber-аккаунт" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Зарегистрированные пользователи" #: ejabberd_web_admin:1577 #: ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Зарегистрированные пользователи:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Зарегистрированные псевдонимы" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Регистрация в mod_irc для " #: mod_configure:889 msgid "Remote copy" msgstr "не хранится локально" #: mod_roster:963 msgid "Remove" msgstr "Удалить" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Удалить все офлайновые сообщения" #: ejabberd_web_admin:1720 #: mod_configure:1779 msgid "Remove User" msgstr "Удалить пользователя" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Заменено новым соединением" #: mod_configure:1675 msgid "Resources" msgstr "Ресурсы" #: ejabberd_web_admin:1876 #: ejabberd_web_admin:2494 #: ejabberd_web_admin:2638 msgid "Restart" msgstr "Перезапустить" #: mod_configure:162 #: mod_configure:588 #: mod_configure:1009 msgid "Restart Service" msgstr "Перезапустить службу" #: mod_configure:151 #: mod_configure:619 msgid "Restore" msgstr "Восстановление из резервной копии" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Восстановление из резервной копии на " #: ejabberd_web_admin:2013 msgid "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "Восстановить из бинарной резервной копии при следующем запуске (требует меньше памяти):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Восстановить из бинарной резервной копии немедленно:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Восстановить из текстовой резервной копии немедленно:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Конфигурация комнаты" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Участники комнаты" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Cоздавать конференцию запрещено политикой службы" #: mod_muc_log:1064 msgid "Room description" msgstr "Описание комнаты" #: mod_muc_log:1027 msgid "Room title" msgstr "Название комнаты" #: mod_roster:1082 msgid "Roster" msgstr "Ростер" #: mod_roster:334 msgid "Roster module has failed" msgstr "Ошибка модуля roster" #: mod_roster:968 msgid "Roster of " msgstr "Ростер пользователя " #: mod_configure:1671 msgid "Roster size" msgstr "Размер списка контактов" #: ejabberd_web_admin:1838 #: mod_configure:510 msgid "Running Nodes" msgstr "Работающие узлы" #: xmpp_stream_in:541 #: xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "SASL переговоры на этой стадии недоступны" #: mod_muc_log:468 msgid "Saturday" msgstr "Суббота" #: mod_irc:582 msgid "Scan error" msgstr "Ошибка сканирования" #: mod_configure:1303 #: mod_configure:1472 #: mod_configure:1517 msgid "Scan failed" msgstr "Ошибка сканирования" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Проверка сценария" #: mod_vcard:453 msgid "Search Results for " msgstr "Результаты поиска в " #: mod_vcard:427 msgid "Search users in " msgstr "Поиск пользователей в " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Разослать объявление всем подключённым пользователям" #: mod_announce:619 #: mod_configure:1032 #: mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Разослать объявление всем подключённым пользователям на всех виртуальных серверах" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Разослать объявление всем пользователям" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Разослать объявление всем пользователям на всех виртуальных серверах" #: mod_muc_log:481 msgid "September" msgstr "сентября" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Ошибка подключения к серверу" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Серверные соединения с локальными поддоменами запрещены" #: mod_irc:842 msgid "Server ~b" msgstr "Сервер ~b" #: mod_register_web:242 #: mod_register_web:370 #: mod_register_web:480 msgid "Server:" msgstr "Сервер:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Установить сообщение дня и разослать его подключённым пользователям" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "Установить сообщение дня на всех виртуальных серверах и разослать его подключённым пользователям" #: mod_shared_roster:784 #: mod_shared_roster:826 #: mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Группы общих контактов" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Показать интегральную таблицу" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Показать обычную таблицу" #: mod_configure:164 #: mod_configure:590 #: mod_configure:1049 msgid "Shut Down Service" msgstr "Остановить службу" #: mod_register_web:258 msgid "Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons." msgstr "Некоторые Jabber-клиенты могут сохранять пароль на Вашем компьютере. Используйте эту функцию только в том случае, если считаете это безопасным." #: ejabberd_web_admin:2518 #: ejabberd_web_admin:2654 msgid "Start" msgstr "Запустить" #: mod_configure:142 #: mod_configure:605 msgid "Start Modules" msgstr "Запуск модулей" #: mod_configure:936 msgid "Start Modules at " msgstr "Запуск модулей на " #: ejabberd_web_admin:1023 #: ejabberd_web_admin:1871 #: mod_muc_admin:366 msgid "Statistics" msgstr "Статистика" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "статистика узла ~p" #: ejabberd_web_admin:1878 #: ejabberd_web_admin:2498 #: ejabberd_web_admin:2642 msgid "Stop" msgstr "Остановить" #: mod_configure:145 #: mod_configure:607 msgid "Stop Modules" msgstr "Остановка модулей" #: mod_configure:918 msgid "Stop Modules at " msgstr "Остановка модулей на " #: ejabberd_web_admin:1839 #: mod_configure:511 msgid "Stopped Nodes" msgstr "Остановленные узлы" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Тип таблицы" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Сохранить бинарную резервную копию:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Сохранить текстовую резервную копию:" #: mod_announce:516 #: mod_configure:1036 #: mod_configure:1076 msgid "Subject" msgstr "Тема" #: ejabberd_web_admin:774 #: ejabberd_web_admin:814 #: ejabberd_web_admin:879 #: ejabberd_web_admin:944 #: ejabberd_web_admin:1963 #: mod_shared_roster:933 msgid "Submit" msgstr "Отправить" #: ejabberd_web_admin:762 #: ejabberd_web_admin:801 #: ejabberd_web_admin:867 #: ejabberd_web_admin:900 #: ejabberd_web_admin:936 #: ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 #: ejabberd_web_admin:1860 #: ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 #: ejabberd_web_admin:2144 #: ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 #: mod_offline:681 #: mod_roster:971 #: mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Отправлено" #: mod_roster:914 msgid "Subscription" msgstr "Подписка" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Подписки недопустимы" #: mod_muc_log:469 msgid "Sunday" msgstr "Воскресенье" #: mod_muc_room:998 #: mod_muc_room:1843 #: mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Этот псевдоним уже занят другим участником" #: mod_muc:745 #: mod_muc_room:1004 #: mod_muc_room:1852 #: mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Этот псевдоним зарегистрирован кем-то другим" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Проверка капчи прошла успешно." #: mod_muc_room:653 #: mod_muc_room:3672 #: mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Проверка капчи не пройдена" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "Запрашиваемое свойство не поддерживается этой конференцией" #: mod_register:308 #: mod_register:366 msgid "The password contains unacceptable characters" msgstr "Пароль содержит недопустимые символы" #: mod_register:311 #: mod_register:370 msgid "The password is too weak" msgstr "Слишком слабый пароль" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Пароль Вашего Jabber-аккаунта был успешно изменен." #: mod_register:160 #: mod_vcard:219 msgid "The query is only allowed from local users" msgstr "Запрос доступен только для локальных пользователей" #: mod_roster:203 msgid "The query must not contain elements" msgstr "Запрос не должен содержать элементов " #: mod_privacy:280 msgid "The stanza MUST contain only one element, one element, or one element" msgstr "Строфа может содержать только один элемент , один элемент или один элемент " #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Ошибка при смене пароля:" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Ошибка при создании аккаунта:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Ошибка при удалении аккаунта:" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "Регистр не имеет значения: \"маша\" и \"МАША\" будет считаться одним и тем же именем." #: mod_register_web:220 msgid "This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields." msgstr "Здесь Вы можете создать Jabber-аккаунт на этом Jabber-сервере. Ваш JID (Jabber-идентификатор) будет в виде: \"пользователь@сервер\". Пожалуйста, внимательно читайте инструкции для правильного заполнения полей." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Здесь Вы можете удалить Jabber-аккаунт с этого сервера." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Эта комната не анонимная" #: mod_muc_log:466 msgid "Thursday" msgstr "Четверг" #: mod_offline:690 msgid "Time" msgstr "Время" #: mod_configure:1014 #: mod_configure:1054 msgid "Time delay" msgstr "По истечение" #: mod_offline:692 msgid "To" msgstr "Кому" #: mod_register:215 msgid "To register, visit ~s" msgstr "Для регистрации посетите ~s" #: mod_configure:709 msgid "To ~s" msgstr "К ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Токен TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Слишком длинное значение атрибута 'xml:lang'" #: mod_muc_room:2541 #: mod_muc_room:3081 msgid "Too many elements" msgstr "Слишком много элементов " #: mod_privacy:164 msgid "Too many elements" msgstr "Слишком много элементов " #: mod_muc_room:1924 #: mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Слишком много запросов капчи" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Слишком много активных потоков данных" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Слишком много неподтверждённых пакетов" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Слишком много пользователей в этой конференции" #: mod_register:355 msgid "Too many users registered" msgstr "Зарегистрировано слишком много пользователей" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Все комнаты" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Превышен лимит скорости посылки информации" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Транзакции отмененные:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Транзакции завершенные:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Транзакции запротоколированные:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Транзакции перезапущенные:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Вторник" #: mod_muc_room:1933 #: mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Не получилось создать капчу" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Нельзя регистрировать маршруты на существующие локальные домены" #: ejabberd_web_admin:209 #: ejabberd_web_admin:221 #: ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Не авторизован" #: mod_announce:485 #: mod_configure:830 #: mod_configure:1758 msgid "Unexpected action" msgstr "Неожиданное действие" #: mod_register_web:488 msgid "Unregister" msgstr "Удалить" #: mod_register_web:197 #: mod_register_web:460 #: mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Удалить Jabber-аккаунт" #: mod_mam:526 msgid "Unsupported element" msgstr "Элемент не поддерживается" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Неподдерживаемый запрос MIX" #: ejabberd_web_admin:1872 #: ejabberd_web_admin:2285 msgid "Update" msgstr "Обновить" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Обновить сообщение дня (не рассылать)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Обновить сообщение дня на всех виртуальных серверах (не рассылать)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "План обновления" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Сценарий обновления" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Обновление ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Время работы:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "Использование STARTTLS запрещено" #: xmpp_stream_in:573 #: xmpp_stream_out:527 #: xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Вы обязаны использовать STARTTLS" #: ejabberd_web_admin:1430 #: ejabberd_web_admin:1486 #: mod_register:225 #: mod_vcard_ldap:328 #: mod_vcard_mnesia:99 #: mod_vcard_sql:154 msgid "User" msgstr "Пользователь" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Пользователь (XMPP адрес)" #: mod_configure:308 #: mod_configure:505 msgid "User Management" msgstr "Управление пользователями" #: mod_register:345 msgid "User already exists" msgstr "Пользователь уже существует" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "Пустое имя пользователя в XMPP-адресе" #: ejabberd_sm:193 #: ejabberd_sm:662 #: ejabberd_sm:687 #: mod_sic:106 msgid "User session not found" msgstr "Сессия пользователя не найдена" #: mod_stream_mgmt:561 #: mod_stream_mgmt:583 msgid "User session terminated" msgstr "Сессия пользователя завершена" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Пользователь ~s" #: mod_register_web:230 #: mod_register_web:366 #: mod_register_web:476 msgid "Username:" msgstr "Имя пользователя:" #: ejabberd_web_admin:959 #: ejabberd_web_admin:967 msgid "Users" msgstr "Пользователи" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Статистика последнего подключения пользователей" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Пользователи не могут регистрировать учётные записи так быстро" #: mod_roster:954 msgid "Validate" msgstr "Утвердить" #: mod_carboncopy:144 #: mod_irc:345 #: mod_muc_room:3662 #: mod_muc_room:3802 #: mod_pubsub:895 #: mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "Значение 'get' атрибута 'type' недопустимо" #: mod_disco:159 #: mod_disco:175 #: mod_disco:279 #: mod_disco:346 #: mod_irc:270 #: mod_irc:285 #: mod_irc:339 #: mod_last:118 #: mod_last:140 #: mod_muc:479 #: mod_muc:504 #: mod_muc:539 #: mod_muc:561 #: mod_muc:571 #: mod_muc_room:3597 #: mod_muc_room:3641 #: mod_proxy65_service:142 #: mod_proxy65_service:160 #: mod_proxy65_service:167 #: mod_pubsub:821 #: mod_pubsub:839 #: mod_pubsub:877 #: mod_sic:81 #: mod_sic:93 #: mod_stats:55 #: mod_time:62 #: mod_vcard:198 #: mod_vcard:236 #: mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Значение 'set' атрибута 'type' недопустимо" #: pubsub_subscription:237 #: pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "Значение '~s' должно быть булевым" #: pubsub_subscription:215 #: pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "Значение '~s' должно быть датой" #: pubsub_subscription:209 #: pubsub_subscription:227 #: pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "Значение '~s' должно быть целочисленным" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Виртуальные хосты" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Посетителям запрещено изменять свои псевдонимы в этой комнате" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Посетителям не разрешается посылать сообщения всем присутствующим" #: mod_muc_room:3879 msgid "Voice request" msgstr "Запрос на право голоса" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Запросы на право голоса отключены в этой конференции" #: mod_muc_log:465 msgid "Wednesday" msgstr "Среда" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Позже Вы можете изменить пароль через Jabber-клиент." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Вам запрещено входить в эту конференцию" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Вы присоединены к слишком большому количеству конференций" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Вы должны заполнить поле \"Псевдоним\" в форме" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Чтобы зарегистрироваться, требуется x:data-совместимый клиент" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Чтобы зарегистрировать псевдоним, требуется x:data-совместимый клиент" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Чтобы настроить параметры mod_irc, требуется x:data-совместимый клиент" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Чтобы воспользоваться поиском, требуется x:data-совместимый клиент" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "Вам не разрешается создавать узлы" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Ваш Jabber-аккаунт был успешно создан." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Ваш Jabber-аккаунт был успешно удален." #: ejabberd_c2s:651 #: ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Маршрутизация этой строфы запрещена вашим активным списком приватности." #: mod_offline:576 msgid "Your contact offline message queue is full. The message has been discarded." msgstr "Очередь недоставленных сообщений Вашего адресата переполнена. Сообщение не было сохранено." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Ваши сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC модуль" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC модуль" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberd Multicast сервис" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Модуль ejabberd Публикации-Подписки" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams модуль" #: ejabberd_web_admin:311 #: ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Web-интерфейс администрирования ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard модуль" #: mod_muc_log:380 #: mod_muc_log:383 msgid "has been banned" msgstr "запретили входить в комнату" #: ejabberd_sm:407 #: mod_configure:1559 #: mod_muc_log:387 #: mod_muc_log:390 msgid "has been kicked" msgstr "выгнали из комнаты" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "выгнали из комнаты из-за останова системы" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "выгнали из комнаты вследствие смены ранга" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "выгнали из комнаты потому что она стала только для членов" #: mod_muc_log:410 msgid "is now known as" msgstr "изменил(а) имя на" #: mod_muc_log:370 msgid "joins the room" msgstr "вошёл(а) в комнату" #: mod_muc_log:373 #: mod_muc_log:376 msgid "leaves the room" msgstr "вышел(а) из комнаты" #: mod_muc_room:3856 msgid "private, " msgstr "приватная, " #: mod_muc_room:3955 msgid "the password is" msgstr "пароль:" #: mod_vcard:292 msgid "vCard User Search" msgstr "Поиск пользователей по vCard" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Конфигурация правила доступа ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s приглашает вас в комнату ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Oчередь офлайновых сообщений ~s" #~ msgid "No resource provided" #~ msgstr "Не указан ресурс" #, fuzzy #~ msgid "Server" #~ msgstr "Сервер:" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Слишком много (~p) неудачных попыток аутентификации с этого IP-адреса " #~ "(~s). Адрес будет разблокирован в ~s UTC" #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Этот IP адрес находится в чёрном списке ~s" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s недопустимый" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Недопустимый ранг: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Недопустимая роль: ~s" #~ msgid "No limit" #~ msgstr "Не ограничено" #~ msgid "Present real Jabber IDs to" #~ msgstr "Сделать реальные Jabber ID участников видимыми" #~ msgid "moderators only" #~ msgstr "только модераторам" #~ msgid "anyone" #~ msgstr "всем участникам" #, fuzzy #~ msgid "Moderator" #~ msgstr "только модераторам" #~ msgid "nobody" #~ msgstr "никто" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Разрешить посетителям запрашивать право голоса" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Минимальный интервал между запросами на право голоса" #~ msgid "Enable message archiving" #~ msgstr "Включить хранение сообщений" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Исключить показ капчи для списка Jabber ID" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Чтобы сконфигурировать комнату, требуется x:data-совместимый клиент" #~ msgid "Number of occupants" #~ msgstr "Число присутствующих" #~ msgid "User JID" #~ msgstr "JID пользователя" #~ msgid "Grant voice to this person?" #~ msgstr "Предоставить голос?" #~ msgid "Node ID" #~ msgstr "ID узла" #~ msgid "Subscriber Address" #~ msgstr "Адрес подписчика" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Разрешить этому Jabber ID подписаться на данный узел?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Доставлять вместе с уведомлениями o публикациях сами публикации" #~ msgid "Deliver event notifications" #~ msgstr "Доставлять уведомления о событиях" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Уведомлять подписчиков об изменении конфигурации сборника" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Уведомлять подписчиков об удалении сборника" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Уведомлять подписчиков об удалении публикаций из сборника" #~ msgid "Persist items to storage" #~ msgstr "Сохранять публикации в хранилище" #~ msgid "A friendly name for the node" #~ msgstr "Легко запоминаемое имя для узла" #~ msgid "Max # of items to persist" #~ msgstr "Максимальное число сохраняемых публикаций" #~ msgid "Whether to allow subscriptions" #~ msgstr "Разрешить подписку" #~ msgid "Specify the access model" #~ msgstr "Укажите механизм управления доступом" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Группы списка контактов, которым разрешена подписка" #~ msgid "Specify the publisher model" #~ msgstr "Условия публикации" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Очищать все записи автора публикации когда он отключается" #~ msgid "Specify the event message type" #~ msgstr "Укажите тип сообщения о событии" #~ msgid "Max payload size in bytes" #~ msgstr "Максимальный размер полезной нагрузки в байтах" #~ msgid "When to send the last published item" #~ msgstr "Когда посылать последний опубликованный элемент" #~ msgid "Only deliver notifications to available users" #~ msgstr "Доставлять уведомления только доступным пользователям" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Имя коллекции, в которую входит узел" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Заполните форму для поиска пользователя Jabber" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Исходящие s2s-серверы:" #~ msgid "Delete" #~ msgstr "Удалить" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Этого участника выгнали из комнаты за то, что он послал сообщение об " #~ "ошибке" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Этого участника выгнали из комнаты за то, что он послал сообщение об " #~ "ошибке другому участнику" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Этого участника выгнали из комнаты за то, что он послал присутствие с " #~ "ошибкой" ejabberd-18.01/priv/msgs/ca.msg0000644000232200023220000006231113225664356016672 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Acceptar"}. {"Access Configuration","Configuració d'accesos"}. {"Access Control List Configuration","Configuració de la Llista de Control d'Accés"}. {"Access Control Lists","Llista de Control d'Accés"}. {"Access control lists","Llistes de Control de Accés"}. {"Access denied by service policy","Accés denegat per la política del servei"}. {"Access rules","Regles d'accés"}. {"Access Rules","Regles d'Accés"}. {"Action on user","Acció en l'usuari"}. {"Add Jabber ID","Afegir Jabber ID"}. {"Add New","Afegir nou"}. {"Add User","Afegir usuari"}. {"Administration","Administració"}. {"Administration of ","Administració de "}. {"Administrator privileges required","Es necessita tenir privilegis d'administrador"}. {"All activity","Tota l'activitat"}. {"Allow users to change the subject","Permetre que els usuaris canviin el tema"}. {"Allow users to query other users","Permetre que els usuaris fagen peticions a altres usuaris"}. {"Allow users to send invites","Permetre que els usuaris envien invitacions"}. {"Allow users to send private messages","Permetre que els usuaris envien missatges privats"}. {"Allow visitors to change nickname","Permetre als visitants canviar el sobrenom"}. {"Allow visitors to send private messages to","Permetre als visitants enviar missatges privats a"}. {"Allow visitors to send status text in presence updates","Permetre als visitants enviar text d'estat en les actualitzacions de presència"}. {"All Users","Tots els usuaris"}. {"Announcements","Anuncis"}. {"A password is required to enter this room","Es necessita contrasenya per a entrar en aquesta sala"}. {"April","Abril"}. {"August","Agost"}. {"Backup","Guardar còpia de seguretat"}. {"Backup Management","Gestió de còpia de seguretat"}. {"Backup of ~p","Còpia de seguretat de ~p"}. {"Backup to File at ","Desar còpia de seguretat a fitxer en "}. {"Bad format","Format erroni"}. {"Birthday","Aniversari"}. {"CAPTCHA web page","Pàgina web del CAPTCHA"}. {"Change Password","Canviar Contrasenya"}. {"Change User Password","Canviar Contrasenya d'Usuari"}. {"Characters not allowed:","Caràcters no permesos:"}. {"Chatroom configuration modified","Configuració de la sala de xat modificada"}. {"Chatroom is created","La sala s'ha creat"}. {"Chatroom is destroyed","La sala s'ha destruït"}. {"Chatroom is started","La sala s'ha iniciat"}. {"Chatroom is stopped","La sala s'ha aturat"}. {"Chatrooms","Sales de xat"}. {"Choose a username and password to register with this server","Tria nom d'usuari i contrasenya per a registrar-te en aquest servidor"}. {"Choose modules to stop","Selecciona mòduls a detindre"}. {"Choose storage type of tables","Selecciona el tipus d'almacenament de les taules"}. {"Choose whether to approve this entity's subscription.","Tria si aprova aquesta entitat de subscripció"}. {"City","Ciutat"}. {"Commands","Comandaments"}. {"Conference room does not exist","La sala de conferències no existeix"}. {"Configuration","Configuració"}. {"Configuration of room ~s","Configuració de la sala ~s"}. {"Connected Resources:","Recursos connectats:"}. {"Connections parameters","Paràmetres de connexió"}. {"Country","Pais"}. {"CPU Time:","Temps de CPU"}. {"Database","Base de dades"}. {"Database Tables at ~p","Taules de la base de dades en ~p"}. {"Database Tables Configuration at ","Configuració de la base de dades en "}. {"December","Decembre"}. {"Default users as participants","Els usuaris són participants per defecte"}. {"Delete message of the day","Eliminar el missatge del dia"}. {"Delete message of the day on all hosts","Elimina el missatge del dis de tots els hosts"}. {"Delete Selected","Eliminar els seleccionats"}. {"Delete User","Eliminar Usuari"}. {"Description:","Descripció:"}. {"Disc only copy","Còpia sols en disc"}. {"Displayed Groups:","Mostrar grups:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","No li donis la teva contrasenya a ningú, ni tan sols als administradors del servidor Jabber."}. {"Dump Backup to Text File at ","Exporta còpia de seguretat a fitxer de text en "}. {"Dump to Text File","Exportar a fitxer de text"}. {"Edit Properties","Editar propietats"}. {"Either approve or decline the voice request.","Aprova o denega la petició de veu"}. {"ejabberd IRC module","mòdul ejabberd IRC"}. {"ejabberd MUC module","mòdul ejabberd MUC"}. {"ejabberd Multicast service","Servei de Multicast d'ejabberd"}. {"ejabberd Publish-Subscribe module","Mòdul ejabberd Publicar-Subscriure"}. {"ejabberd SOCKS5 Bytestreams module","mòdul ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","Mòdul ejabberd vCard"}. {"ejabberd Web Admin","Web d'administració del ejabberd"}. {"Elements","Elements"}. {"Email","Email"}. {"Enable logging","Habilitar el registre de la conversa"}. {"Encoding for server ~b","Codificació pel servidor ~b"}. {"End User Session","Finalitzar Sesió d'Usuari"}. {"Enter list of {Module, [Options]}","Introdueix llista de {mòdul, [opcions]}"}. {"Enter nickname you want to register","Introdueix el sobrenom que vols registrar"}. {"Enter path to backup file","Introdueix ruta al fitxer de còpia de seguretat"}. {"Enter path to jabberd14 spool dir","Introdueix la ruta al directori de jabberd14 spools"}. {"Enter path to jabberd14 spool file","Introdueix ruta al fitxer jabberd14 spool"}. {"Enter path to text file","Introdueix ruta al fitxer de text"}. {"Enter the text you see","Introdueix el text que veus"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Introdueix el nom d'usuari i les codificacions de caràcters per a utilitzar als servidors de IRC. Apreta \"Seguent\" per veure més caps per omplir. Apreta \"Completar\" per guardar la configuració. "}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Introdueix el nom d'usuari, les codificacions de caràcters, els ports i contrasenyes per a utilitzar al connectar als servidors de IRC"}. {"Erlang Jabber Server","Servidor Erlang Jabber"}. {"Error","Error"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Exporta totes les taules a un fitxer SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar dades de tots els usuaris del servidor a arxius PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar dades d'usuaris d'un host a arxius PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","No s'ha pogut extraure el JID de la teva aprovació de petició de veu"}. {"Family Name","Cognom"}. {"February","Febrer"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Emplena el formulari per a buscar usuaris Jabber. Afegix * al final d'un camp per a buscar subcadenes."}. {"Friday","Divendres"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Nom complet"}. {"Get Number of Online Users","Obtenir Número d'Usuaris Connectats"}. {"Get Number of Registered Users","Obtenir Número d'Usuaris Registrats"}. {"Get User Last Login Time","Obtenir la última connexió d'Usuari"}. {"Get User Password","Obtenir Contrasenya d'usuari"}. {"Get User Statistics","Obtenir Estadístiques d'Usuari"}. {"Group ","Grup "}. {"Groups","Grups"}. {"has been banned","Has sigut banejat"}. {"has been kicked because of an affiliation change","Has sigut expulsat a causa d'un canvi d'afiliació"}. {"has been kicked because of a system shutdown","Has sigut expulsat perquè el sistema s'ha apagat"}. {"has been kicked because the room has been changed to members-only","Has sigut expulsat perquè la sala ha canviat a sols membres"}. {"has been kicked","Has sigut expulsat"}. {" has set the subject to: "," ha posat l'assumpte: "}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si no veus la imatge CAPTCHA açí, visita la pàgina web."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si vols especificar codificacions de caràcters diferents per a cada servidor IRC emplena aquesta llista amb els valors amb el format '{\"servidor irc\", \"codificació\", port, \"contrasenya\"}'. Aquest servei utilitza per defecte la codificació \"~s\", port ~p, no contrasenya."}. {"Import Directory","Importar directori"}. {"Import File","Importar fitxer"}. {"Import user data from jabberd14 spool file:","Importar dades d'usuaris de l'arxiu de spool de jabberd14"}. {"Import User from File at ","Importa usuari des de fitxer en "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importar dades d'usuaris des d'un arxiu PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importar dades d'usuaris del directori de spool de jabberd14:"}. {"Import Users from Dir at ","Importar usuaris des del directori en "}. {"Import Users From jabberd14 Spool Files","Importar usuaris de jabberd14"}. {"Improper message type","Tipus de missatge incorrecte"}. {"Incoming s2s Connections:","Connexions s2s d'entrada"}. {"Incorrect password","Contrasenya incorrecta"}. {"IP addresses","Adreça IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canal d'IRC (no posis la primera #)"}. {"IRC server","Servidor d'IRC"}. {"IRC settings","Configuració d'IRC."}. {"IRC Transport","Transport a IRC"}. {"IRC username","Nom d'usuari al IRC"}. {"IRC Username","Nom d'usuari al IRC"}. {"is now known as","ara es conegut com"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","No està permés enviar missatges d'error a la sala. El participant (~s) ha enviat un missatge d'error (~s) i ha sigut expulsat de la sala"}. {"It is not allowed to send private messages","No està permés enviar missatges privats"}. {"It is not allowed to send private messages of type \"groupchat\"","No està permés enviar missatges del tipus \"groupchat\""}. {"It is not allowed to send private messages to the conference","No està permès l'enviament de missatges privats a la sala"}. {"Jabber Account Registration","Registre de compte Jabber"}. {"Jabber ID","ID Jabber"}. {"January","Gener"}. {"Join IRC channel","Entra a canal d'IRC"}. {"joins the room","Entrar a la sala"}. {"Join the IRC channel here.","Entra al canal d'IRC aquí."}. {"Join the IRC channel in this Jabber ID: ~s","Entra al canal d'IRC en aquesta Jabber ID: ~s"}. {"July","Juliol"}. {"June","Juny"}. {"Last Activity","Última activitat"}. {"Last login","Últim login"}. {"Last month","Últim mes"}. {"Last year","Últim any"}. {"leaves the room","Deixar la sala"}. {"Listened Ports at ","Ports a la escolta en "}. {"Listened Ports","Ports a l'escolta"}. {"List of modules to start","Llista de mòduls a iniciar"}. {"List of rooms","Llista de sales"}. {"Low level update script","Script d'actualització de baix nivell"}. {"Make participants list public","Crear una llista de participants pública"}. {"Make room CAPTCHA protected","Crear una sala protegida per CAPTCHA"}. {"Make room members-only","Crear una sala de \"només membres\""}. {"Make room moderated","Crear una sala moderada"}. {"Make room password protected","Crear una sala amb contrasenya"}. {"Make room persistent","Crear una sala persistent"}. {"Make room public searchable","Crear una sala pública"}. {"March","Març"}. {"Maximum Number of Occupants","Número màxim d'ocupants"}. {"May","Maig"}. {"Membership is required to enter this room","Necessites ser membre d'aquesta sala per a poder entrar"}. {"Members:","Membre:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memoritza la teva contrasenya, o escriu-la en un paper guardat a un lloc segur.A Jabber no hi ha una forma automatitzada de recuperar la teva contrasenya si la oblides."}. {"Memory","Memòria"}. {"Message body","Missatge"}. {"Middle Name","Segon nom"}. {"Moderator privileges required","Es necessita tenir privilegis de moderador"}. {"Modified modules","Mòduls modificats"}. {"Module","Mòdul"}. {"Modules at ~p","Mòduls en ~p"}. {"Modules","Mòduls"}. {"Monday","Dilluns"}. {"Multicast","Multicast"}. {"Multi-User Chat","Multi-Usuari Converses"}. {"Name:","Nom:"}. {"Name","Nom"}. {"Never","Mai"}. {"New Password:","Nova Contrasenya:"}. {"Nickname Registration at ","Registre del sobrenom en "}. {"Nickname ~s does not exist in the room","El sobrenom ~s no existeix a la sala"}. {"Nickname","Sobrenom"}. {"No body provided for announce message","No hi ha proveedor per al missatge anunci"}. {"No Data","No hi ha dades"}. {"Node not found","Node no trobat"}. {"Node ~p","Node ~p"}. {"Nodes","Nodes"}. {"None","Cap"}. {"Not Found","No Trobat"}. {"November","Novembre"}. {"Number of online users","Número d'usuaris connectats"}. {"Number of registered users","Número d'Usuaris Registrats"}. {"October","Octubre"}. {"Offline Messages:","Missatges fora de línia:"}. {"Offline Messages","Missatges offline"}. {"OK","Acceptar"}. {"Old Password:","Antiga contrasenya:"}. {"Online","Connectat"}. {"Online Users","Usuaris conectats"}. {"Online Users:","Usuaris en línia:"}. {"Only members may query archives of this room","Només membres poden consultar l'arxiu de missatges d'aquesta sala"}. {"Only moderators and participants are allowed to change the subject in this room","Només els moderadors i participants poden canviar l'assumpte d'aquesta sala"}. {"Only moderators are allowed to change the subject in this room","Només els moderadors poden canviar l'assumpte d'aquesta sala"}. {"Only moderators can approve voice requests","Només els moderadors poden aprovar les peticions de veu"}. {"Only occupants are allowed to send messages to the conference","Sols els ocupants poden enviar missatges a la sala"}. {"Only occupants are allowed to send queries to the conference","Sols els ocupants poden enviar sol·licituds a la sala"}. {"Only service administrators are allowed to send service messages","Sols els administradors del servei tenen permís per a enviar missatges de servei"}. {"Options","Opcions"}. {"Organization Name","Nom de la organizació"}. {"Organization Unit","Unitat de la organizació"}. {"Outgoing s2s Connections:","Connexions d'eixida s2s"}. {"Outgoing s2s Connections","Connexions s2s d'eixida"}. {"Owner privileges required","Es requerixen privilegis de propietari de la sala"}. {"Packet","Paquet"}. {"Password ~b","Contrasenya ~b"}. {"Password:","Contrasenya:"}. {"Password","Contrasenya"}. {"Password Verification:","Verificació de la Contrasenya:"}. {"Password Verification","Verificació de la Contrasenya"}. {"Path to Dir","Ruta al directori"}. {"Path to File","Ruta al fitxer"}. {"Pending","Pendent"}. {"Period: ","Període: "}. {"Permanent rooms","Sales permanents"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Recorda que aquestes opcions només fan còpia de seguretat de la base de dades Mnesia. Si estàs utilitzant el mòdul d'ODBC també deus de fer una còpia de seguretat de la base de dades de SQL a part."}. {"Please, wait for a while before sending new voice request","Si us plau, espera una mica abans d'enviar una nova petició de veu"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","privat"}. {"Protocol","Protocol"}. {"Publish-Subscribe","Publicar-subscriure't"}. {"PubSub subscriber request","Petició de subscriptor PubSub"}. {"Queries to the conference members are not allowed in this room"," En aquesta sala no es permeten sol·licituds als membres de la conferència"}. {"RAM and disc copy","Còpia en RAM i disc"}. {"RAM copy","Còpia en RAM"}. {"Raw","en format text"}. {"Really delete message of the day?","Segur que vols eliminar el missatge del dia?"}. {"Recipient is not in the conference room","El receptor no està en la sala de conferència"}. {"Register a Jabber account","Registrar un compte Jabber"}. {"Registered nicknames","Sobrenoms registrats"}. {"Registered Users:","Usuaris registrats:"}. {"Registered Users","Usuaris registrats"}. {"Register","Registrar"}. {"Registration in mod_irc for ","Registre en mod_irc per a"}. {"Remote copy","Còpia remota"}. {"Remove All Offline Messages","Eliminar tots els missatges offline"}. {"Remove","Borrar"}. {"Remove User","Eliminar usuari"}. {"Replaced by new connection","Reemplaçat per una nova connexió"}. {"Resources","Recursos"}. {"Restart","Reiniciar"}. {"Restart Service","Reiniciar el Servei"}. {"Restore Backup from File at ","Restaura còpia de seguretat des del fitxer en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar una còpia de seguretat binària després de reiniciar el ejabberd (requereix menys memòria:"}. {"Restore binary backup immediately:","Restaurar una còpia de seguretat binària ara mateix."}. {"Restore plain text backup immediately:","Restaurar una còpia de seguretat en format de text pla ara mateix:"}. {"Restore","Restaurar"}. {"Room Configuration","Configuració de la sala"}. {"Room creation is denied by service policy","Se t'ha denegat el crear la sala per política del servei"}. {"Room description","Descripció de la sala:"}. {"Room Occupants","Nombre d'ocupants"}. {"Room title","Títol de la sala"}. {"Roster","Llista de contactes"}. {"Roster of ","Llista de contactes de "}. {"Roster size","Tamany de la llista"}. {"RPC Call Error","Error de cridada RPC"}. {"Running Nodes","Nodes funcionant"}. {"~s access rule configuration","Configuració de les Regles d'Accés ~s"}. {"Saturday","Dissabte"}. {"Script check","Comprovar script"}. {"Search Results for ","Resultat de la búsqueda"}. {"Search users in ","Cerca usuaris en "}. {"Send announcement to all online users","Enviar anunci a tots els usuaris connectats"}. {"Send announcement to all online users on all hosts","Enviar anunci a tots els usuaris connectats a tots els hosts"}. {"Send announcement to all users","Enviar anunci a tots els usuaris"}. {"Send announcement to all users on all hosts","Enviar anunci a tots els usuaris de tots els hosts"}. {"September","Setembre"}. {"Server ~b","Servidor ~b"}. {"Server:","Servidor:"}. {"Set message of the day and send to online users","Configurar el missatge del dia i enviar a tots els usuaris"}. {"Set message of the day on all hosts and send to online users","Escriure missatge del dia en tots els hosts i enviar-ho als usuaris connectats"}. {"Shared Roster Groups","Grups de contactes compartits"}. {"Show Integral Table","Mostrar Taula Integral"}. {"Show Ordinary Table","Mostrar Taula Ordinaria"}. {"Shut Down Service","Apager el Servei"}. {"~s invites you to the room ~s","~s et convida a la sala ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clients Jabber poden emmagatzemar la teva contrasenya al teu ordinador. Fes servir aquesta característica només si saps que el teu ordinador és segur."}. {"~s's Offline Messages Queue","~s's cua de missatges offline"}. {"Start","Iniciar"}. {"Start Modules at ","Iniciar mòduls en "}. {"Start Modules","Iniciar mòduls"}. {"Statistics","Estadístiques"}. {"Statistics of ~p","Estadístiques de ~p"}. {"Stop","Detindre"}. {"Stop Modules at ","Detindre mòduls en "}. {"Stop Modules","Parar mòduls"}. {"Stopped Nodes","Nodes parats"}. {"Storage Type","Tipus d'emmagatzematge"}. {"Store binary backup:","Guardar una còpia de seguretat binària:"}. {"Store plain text backup:","Guardar una còpia de seguretat en format de text pla:"}. {"Subject","Assumpte"}. {"Submit","Enviar"}. {"Submitted","Enviat"}. {"Subscription","Subscripció"}. {"Sunday","Diumenge"}. {"That nickname is already in use by another occupant","El Nickname està siguent utilitzat per una altra persona"}. {"That nickname is registered by another person","El nickname ja està registrat per una altra persona"}. {"The CAPTCHA is valid.","El CAPTCHA es vàlid."}. {"The CAPTCHA verification has failed","La verificació CAPTCHA ha fallat"}. {"the password is","la contrasenya és"}. {"The password is too weak","La contrasenya és massa simple"}. {"The password of your Jabber account was successfully changed.","La contrasenya del teu compte Jabber s'ha canviat correctament."}. {"There was an error changing the password: ","Hi ha hagut un error canviant la contrasenya: "}. {"There was an error creating the account: ","Hi ha hagut un error creant el compte: "}. {"There was an error deleting the account: ","Hi ha hagut un error esborrant el compte: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Això no distingeix majúscules de minúscules: macbeth es el mateix que MacBeth i Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Aquesta pàgina permet crear un compte Jabber en aquest servidor Jabber. El teu JID (Jabber IDentifier; Identificador Jabber) tindrà aquesta forma: usuari@servidor. Si us plau, llegeix amb cura les instruccions per emplenar correctament els camps."}. {"This page allows to unregister a Jabber account in this Jabber server.","Aquesta pàgina permet anul·lar el registre d'un compte Jabber en aquest servidor Jabber."}. {"This room is not anonymous","Aquesta sala no és anònima"}. {"Thursday","Dijous"}. {"Time","Data"}. {"Time delay","Temps de retard"}. {"Too many CAPTCHA requests","Massa peticions de CAPTCHA"}. {"Too many unacked stanzas","Massa missatges sense haver reconegut la seva recepció"}. {"To","Per a"}. {"To ~s","A ~s"}. {"Total rooms","Nombre total de sales"}. {"Traffic rate limit is exceeded","El llímit de tràfic ha sigut sobrepassat"}. {"Transactions Aborted:","Transaccions Avortades"}. {"Transactions Committed:","Transaccions Realitzades:"}. {"Transactions Logged:","Transaccions registrades"}. {"Transactions Restarted:","Transaccions reiniciades"}. {"Tuesday","Dimarts"}. {"Unable to generate a CAPTCHA","No s'ha pogut generar un CAPTCHA"}. {"Unauthorized","No autoritzat"}. {"Unregister a Jabber account","Anul·lar el registre d'un compte Jabber"}. {"Unregister","Anul·lar el registre"}. {"Update","Actualitzar"}. {"Update message of the day (don't send)","Actualitzar el missatge del dia (no enviar)"}. {"Update message of the day on all hosts (don't send)","Actualitza el missatge del dia en tots els hosts (no enviar)"}. {"Update ~p","Actualitzar ~p"}. {"Update plan","Pla d'actualització"}. {"Update script","Script d'actualització"}. {"Uptime:","Temps en marxa"}. {"Use of STARTTLS required","És obligatori utilitzar STARTTLS"}. {"User Management","Gestió d'Usuaris"}. {"Username:","Nom d'usuari:"}. {"Users are not allowed to register accounts so quickly","Els usuaris no tenen permís per a crear comptes tan depresa"}. {"Users Last Activity","Última activitat d'usuari"}. {"User ~s","Usuari ~s"}. {"Users","Usuaris"}. {"User","Usuari"}. {"Validate","Validar"}. {"vCard User Search","Recerca de vCard d'usuari"}. {"Virtual Hosts","Hosts virtuals"}. {"Visitors are not allowed to change their nicknames in this room","Els visitants no tenen permés canviar el seus Nicknames en esta sala"}. {"Visitors are not allowed to send messages to all occupants","Els visitants no poden enviar missatges a tots els ocupants"}. {"Voice request","Petició de veu"}. {"Voice requests are disabled in this conference","Les peticions de veu es troben desactivades en aquesta conferència"}. {"Wednesday","Dimecres"}. {"You can later change your password using a Jabber client.","Podràs canviar la teva contrasenya més endavant utilitzant un client Jabber."}. {"You have been banned from this room","Has sigut bloquejat en aquesta sala"}. {"You must fill in field \"Nickname\" in the form","Deus d'omplir el camp \"Nickname\" al formulari"}. {"You need a client that supports x:data and CAPTCHA to register","Necessites un client amb suport x:data i de CAPTCHA para poder registrar-te"}. {"You need a client that supports x:data to register the nickname","Necessites un client amb suport x:data per a poder registrar el sobrenom"}. {"You need an x:data capable client to configure mod_irc settings","Necessites un client amb suport x:data per a configurar les opcions de mod_irc"}. {"You need an x:data capable client to search","Necessites un client amb suport x:data per a poder buscar"}. {"Your active privacy list has denied the routing of this stanza.","La teva llista de privacitat activa ha denegat l'encaminament d'aquesta stanza."}. {"Your contact offline message queue is full. The message has been discarded.","La cua de missatges offline és plena. El missatge ha sigut descartat"}. {"Your Jabber account was successfully created.","El teu compte Jabber ha sigut creat correctament."}. {"Your Jabber account was successfully deleted.","El teu compte Jabber ha sigut esborrat correctament."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Els teus missatges per ~s s'estan bloquejant. Per desbloquejar-los, visita ~s"}. ejabberd-18.01/priv/msgs/es.msg0000644000232200023220000006243513225664356016725 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Aceptar"}. {"Access Configuration","Configuración de accesos"}. {"Access Control List Configuration","Configuración de la Lista de Control de Acceso"}. {"Access control lists","Listas de Control de Acceso"}. {"Access Control Lists","Listas de Control de Acceso"}. {"Access denied by service policy","Acceso denegado por la política del servicio"}. {"Access rules","Reglas de acceso"}. {"Access Rules","Reglas de Acceso"}. {"Action on user","Acción en el usuario"}. {"Add Jabber ID","Añadir Jabber ID"}. {"Add New","Añadir nuevo"}. {"Add User","Añadir usuario"}. {"Administration","Administración"}. {"Administration of ","Administración de "}. {"Administrator privileges required","Se necesita privilegios de administrador"}. {"All activity","Toda la actividad"}. {"Allow users to change the subject","Permitir a los usuarios cambiar el asunto"}. {"Allow users to query other users","Permitir a los usuarios consultar a otros usuarios"}. {"Allow users to send invites","Permitir a los usuarios enviar invitaciones"}. {"Allow users to send private messages","Permitir a los usuarios enviar mensajes privados"}. {"Allow visitors to change nickname","Permitir a los visitantes cambiarse el apodo"}. {"Allow visitors to send private messages to","Permitir a los visitantes enviar mensajes privados a"}. {"Allow visitors to send status text in presence updates","Permitir a los visitantes enviar texto de estado en las actualizaciones de presencia"}. {"All Users","Todos los usuarios"}. {"Announcements","Anuncios"}. {"A password is required to enter this room","Se necesita contraseña para entrar en esta sala"}. {"April","abril"}. {"August","agosto"}. {"Backup","Guardar copia de seguridad"}. {"Backup Management","Gestión de copia de seguridad"}. {"Backup of ~p","Copia de seguridad de ~p"}. {"Backup to File at ","Guardar copia de seguridad en fichero en "}. {"Bad format","Mal formato"}. {"Birthday","Cumpleaños"}. {"CAPTCHA web page","Página web de CAPTCHA"}. {"Change Password","Cambiar contraseña"}. {"Change User Password","Cambiar contraseña de usuario"}. {"Characters not allowed:","Caracteres no permitidos:"}. {"Chatroom configuration modified","Configuración de la sala modificada"}. {"Chatroom is created","Se ha creado la sala"}. {"Chatroom is destroyed","Se ha destruido la sala"}. {"Chatroom is started","Se ha iniciado la sala"}. {"Chatroom is stopped","Se ha detenido la sala"}. {"Chatrooms","Salas de charla"}. {"Choose a username and password to register with this server","Escoge un nombre de usuario y contraseña para registrarte en este servidor"}. {"Choose modules to stop","Selecciona módulos a detener"}. {"Choose storage type of tables","Selecciona tipo de almacenamiento de las tablas"}. {"Choose whether to approve this entity's subscription.","Decidir si aprobar la subscripción de esta entidad."}. {"City","Ciudad"}. {"Commands","Comandos"}. {"Conference room does not exist","La sala de conferencias no existe"}. {"Configuration","Configuración"}. {"Configuration of room ~s","Configuración para la sala ~s"}. {"Connected Resources:","Recursos conectados:"}. {"Connections parameters","Parámetros de conexiones"}. {"Country","País"}. {"CPU Time:","Tiempo consumido de CPU:"}. {"Database","Base de datos"}. {"Database Tables at ~p","Tablas de la base de datos en ~p"}. {"Database Tables Configuration at ","Configuración de tablas de la base de datos en "}. {"December","diciembre"}. {"Default users as participants","Los usuarios son participantes por defecto"}. {"Delete message of the day","Borrar mensaje del dia"}. {"Delete message of the day on all hosts","Borrar el mensaje del día en todos los dominios"}. {"Delete Selected","Eliminar los seleccionados"}. {"Delete User","Borrar usuario"}. {"Description:","Descripción:"}. {"Disc only copy","Copia en disco solamente"}. {"Displayed Groups:","Mostrar grupos:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","No le digas tu contraseña a nadie, ni siquiera a los administradores del servidor Jabber."}. {"Dump Backup to Text File at ","Exporta copia de seguridad a fichero de texto en "}. {"Dump to Text File","Exportar a fichero de texto"}. {"Edit Properties","Editar propiedades"}. {"Either approve or decline the voice request.","Aprueba o rechaza la petición de voz."}. {"ejabberd IRC module","Módulo de IRC para ejabberd"}. {"ejabberd MUC module","Módulo de MUC para ejabberd"}. {"ejabberd Multicast service","Servicio Multicast de ejabberd"}. {"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"Elements","Elementos"}. {"Email","correo"}. {"Enable logging","Guardar históricos"}. {"Encoding for server ~b","Codificación del servidor ~b"}. {"End User Session","Cerrar sesión de usuario"}. {"Enter list of {Module, [Options]}","Introduce lista de {módulo, [opciones]}"}. {"Enter nickname you want to register","Introduce el apodo que quieras registrar"}. {"Enter path to backup file","Introduce ruta al fichero de copia de seguridad"}. {"Enter path to jabberd14 spool dir","Introduce la ruta al directorio de jabberd14 spools"}. {"Enter path to jabberd14 spool file","Introduce ruta al fichero jabberd14 spool"}. {"Enter path to text file","Introduce ruta al fichero de texto"}. {"Enter the text you see","Teclea el texto que ves"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Introduce el nombre de usuario y codificaciones de carácteres que quieras usar al conectar en los servidores de IRC. Pulsa Siguiente para conseguir más campos en el formulario. Pulsa Completar para guardar las opciones."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Introduce el nombre de usuario, codificaciones de carácteres, puertos y contraseñas que quieras usar al conectar en los servidores de IRC"}. {"Erlang Jabber Server","Servidor Jabber en Erlang"}. {"Error","Error"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Ejemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Exportar todas las tablas a un fichero SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar datos de todos los usuarios del servidor a ficheros PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar datos de los usuarios de un dominio a ficheros PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Fallo al extraer el Jabber ID de tu aprobación de petición de voz"}. {"Family Name","Apellido"}. {"February","febrero"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Rellena el formulario para buscar usuarios Jabber. Añade * al final de un campo para buscar subcadenas."}. {"Friday","viernes"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Nombre completo"}. {"Get Number of Online Users","Ver número de usuarios conectados"}. {"Get Number of Registered Users","Ver número de usuarios registrados"}. {"Get User Last Login Time","Ver fecha de la última conexión de usuario"}. {"Get User Password","Ver contraseña de usuario"}. {"Get User Statistics","Ver estadísticas de usuario"}. {"Group ","Grupo "}. {"Groups","Grupos"}. {"has been banned","ha sido bloqueado"}. {"has been kicked because of an affiliation change","ha sido expulsado por un cambio de su afiliación"}. {"has been kicked because of a system shutdown","ha sido expulsado porque el sistema se va a detener"}. {"has been kicked because the room has been changed to members-only","ha sido expulsado porque la sala es ahora solo para miembros"}. {"has been kicked","ha sido expulsado"}. {" has set the subject to: "," ha puesto el asunto: "}. {"Host","Dominio"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si no ves la imagen CAPTCHA aquí, visita la página web."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si quieres especificar distintos codificaciones de carácteres, contraseñas o puertos para cada servidor IRC rellena esta lista con valores en el formato '{\"servidor irc\", \"codificación\", \"puerto\", \"contrasela\"}'. Este servicio usa por defecto la codificación \"~s\", puerto ~p, sin contraseña."}. {"Import Directory","Importar directorio"}. {"Import File","Importar fichero"}. {"Import user data from jabberd14 spool file:","Importar usuario de fichero spool de jabberd14:"}. {"Import User from File at ","Importa usuario desde fichero en "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importar usuarios desde un fichero PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importar usuarios del directorio spool de jabberd14:"}. {"Import Users from Dir at ","Importar usuarios desde el directorio en "}. {"Import Users From jabberd14 Spool Files","Importar usuarios de ficheros spool de jabberd-1.4"}. {"Improper message type","Tipo de mensaje incorrecto"}. {"Incoming s2s Connections:","Conexiones S2S entrantes:"}. {"Incorrect password","Contraseña incorrecta"}. {"IP addresses","Direcciones IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canal IRC (no pongas el # del principio)"}. {"IRC server","Servidor IRC"}. {"IRC settings","Opciones de IRC"}. {"IRC Transport","Transporte de IRC"}. {"IRC username","Nombre de usuario en IRC"}. {"IRC Username","Nombre de usuario en IRC"}. {"is now known as","se cambia el nombre a"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","No está permitido enviar mensajes de error a la sala. Este participante (~s) ha enviado un mensaje de error (~s) y fue expulsado de la sala"}. {"It is not allowed to send private messages","No está permitido enviar mensajes privados"}. {"It is not allowed to send private messages of type \"groupchat\"","No está permitido enviar mensajes privados del tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir el envio de mensajes privados a la sala"}. {"Jabber Account Registration","Registro de Cuenta Jabber"}. {"Jabber ID","Jabber ID"}. {"January","enero"}. {"Join IRC channel","Entrar en canal IRC"}. {"joins the room","entra en la sala"}. {"Join the IRC channel here.","Entrar en el canal de IRC aquí"}. {"Join the IRC channel in this Jabber ID: ~s","Entra en el canal de IRC en esta dirección Jabber: ~s"}. {"July","julio"}. {"June","junio"}. {"Last Activity","Última actividad"}. {"Last login","Última conexión"}. {"Last month","Último mes"}. {"Last year","Último año"}. {"leaves the room","sale de la sala"}. {"Listened Ports at ","Puertos de escucha en "}. {"Listened Ports","Puertos de escucha"}. {"List of modules to start","Lista de módulos a iniciar"}. {"List of rooms","Lista de salas"}. {"Low level update script","Script de actualización a bajo nivel"}. {"Make participants list public","La lista de participantes es pública"}. {"Make room CAPTCHA protected","Proteger la sala con CAPTCHA"}. {"Make room members-only","Sala sólo para miembros"}. {"Make room moderated","Sala moderada"}. {"Make room password protected","Proteger la sala con contraseña"}. {"Make room persistent","Sala permanente"}. {"Make room public searchable","Sala públicamente visible"}. {"March","marzo"}. {"Maximum Number of Occupants","Número máximo de ocupantes"}. {"May","mayo"}. {"Membership is required to enter this room","Necesitas ser miembro de esta sala para poder entrar"}. {"Members:","Miembros:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memoriza tu contraseña, o apúntala en un papel en un lugar seguro. En Jabber no hay un método automatizado para recuperar la contraseña si la olvidas."}. {"Memory","Memoria"}. {"Message body","Cuerpo del mensaje"}. {"Middle Name","Segundo nombre"}. {"Moderator privileges required","Se necesita privilegios de moderador"}. {"Modified modules","Módulos modificados"}. {"Module","Módulo"}. {"Modules at ~p","Módulos en ~p"}. {"Modules","Módulos"}. {"Monday","lunes"}. {"Multicast","Multicast"}. {"Multi-User Chat","Salas de Charla"}. {"Name:","Nombre:"}. {"Name","Nombre"}. {"Never","Nunca"}. {"New Password:","Nueva contraseña:"}. {"Nickname","Apodo"}. {"Nickname Registration at ","Registro del apodo en "}. {"Nickname ~s does not exist in the room","El apodo ~s no existe en la sala"}. {"No body provided for announce message","No se ha proporcionado cuerpo de mensaje para el anuncio"}. {"No Data","Sin datos"}. {"Node not found","Nodo no encontrado"}. {"Node ~p","Nodo ~p"}. {"Nodes","Nodos"}. {"None","Ninguno"}. {"Not Found","No encontrado"}. {"November","noviembre"}. {"Number of online users","Número de usuarios conectados"}. {"Number of registered users","Número de usuarios registrados"}. {"October","octubre"}. {"Offline Messages:","Mensajes diferidos:"}. {"Offline Messages","Mensajes diferidos"}. {"OK","Aceptar"}. {"Old Password:","Contraseña antigua:"}. {"Online","Conectado"}. {"Online Users:","Usuarios conectados:"}. {"Online Users","Usuarios conectados"}. {"Only members may query archives of this room","Solo miembros pueden consultar el archivo de mensajes de la sala"}. {"Only moderators and participants are allowed to change the subject in this room","Solo los moderadores y participantes pueden cambiar el asunto de esta sala"}. {"Only moderators are allowed to change the subject in this room","Solo los moderadores pueden cambiar el asunto de esta sala"}. {"Only moderators can approve voice requests","Solo los moderadores pueden aprobar peticiones de voz"}. {"Only occupants are allowed to send messages to the conference","Solo los ocupantes pueden enviar mensajes a la sala"}. {"Only occupants are allowed to send queries to the conference","Solo los ocupantes pueden enviar solicitudes a la sala"}. {"Only service administrators are allowed to send service messages","Solo los administradores del servicio tienen permiso para enviar mensajes de servicio"}. {"Options","Opciones"}. {"Organization Name","Nombre de la organización"}. {"Organization Unit","Unidad de la organización"}. {"Outgoing s2s Connections:","Conexiones S2S salientes:"}. {"Outgoing s2s Connections","Conexiones S2S salientes"}. {"Owner privileges required","Se requieren privilegios de propietario de la sala"}. {"Packet","Paquete"}. {"Password ~b","Contraseña ~b"}. {"Password:","Contraseña:"}. {"Password","Contraseña"}. {"Password Verification:","Verificación de la contraseña:"}. {"Password Verification","Verificación de la contraseña"}. {"Path to Dir","Ruta al directorio"}. {"Path to File","Ruta al fichero"}. {"Pending","Pendiente"}. {"Period: ","Periodo: "}. {"Permanent rooms","Salas permanentes"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ten en cuenta que estas opciones solo harán copia de seguridad de la base de datos Mnesia embebida. Si estás usando ODBC tendrás que hacer también copia de seguridad de tu base de datos SQL."}. {"Please, wait for a while before sending new voice request","Por favor, espera un poco antes de enviar otra petición de voz"}. {"Pong","Pong"}. {"Port ~b","Puerto ~b"}. {"Port","Puerto"}. {"private, ","privado"}. {"Protocol","Protocolo"}. {"Publish-Subscribe","Servicio de Publicar-Subscribir"}. {"PubSub subscriber request","Petición de subscriptor de PubSub"}. {"Queries to the conference members are not allowed in this room","En esta sala no se permiten solicitudes a los miembros de la sala"}. {"RAM and disc copy","Copia en RAM y disco"}. {"RAM copy","Copia en RAM"}. {"Raw","Crudo"}. {"Really delete message of the day?","¿Está seguro de quere borrar el mensaje del dia?"}. {"Recipient is not in the conference room","El receptor no está en la sala de conferencia"}. {"Register a Jabber account","Registrar una cuenta Jabber"}. {"Registered nicknames","Apodos registrados"}. {"Registered Users:","Usuarios registrados:"}. {"Registered Users","Usuarios registrados"}. {"Register","Registrar"}. {"Registration in mod_irc for ","Registro en mod_irc para"}. {"Remote copy","Copia remota"}. {"Remove All Offline Messages","Borrar todos los mensajes diferidos"}. {"Remove","Borrar"}. {"Remove User","Eliminar usuario"}. {"Replaced by new connection","Reemplazado por una nueva conexión"}. {"Resources","Recursos"}. {"Restart","Reiniciar"}. {"Restart Service","Reiniciar el servicio"}. {"Restore Backup from File at ","Restaura copia de seguridad desde el fichero en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridad binaria en el siguiente reinicio de ejabberd (requiere menos memoria que si instantánea):"}. {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridad binaria:"}. {"Restore plain text backup immediately:","Restaurar copias de seguridad de texto plano inmediatamente:"}. {"Restore","Restaurar"}. {"Room Configuration","Configuración de la sala"}. {"Room creation is denied by service policy","Se te ha denegado crear la sala por política del servicio"}. {"Room description","Descripción de la sala"}. {"Room Occupants","Ocupantes de la sala"}. {"Room title","Título de la sala"}. {"Roster","Lista de contactos"}. {"Roster of ","Lista de contactos de "}. {"Roster size","Tamaño de la lista de contactos"}. {"RPC Call Error","Error en la llamada RPC"}. {"Running Nodes","Nodos funcionando"}. {"~s access rule configuration","Configuración de las Regla de Acceso ~s"}. {"Saturday","sábado"}. {"Script check","Comprobación de script"}. {"Search Results for ","Buscar resultados por "}. {"Search users in ","Buscar usuarios en "}. {"Send announcement to all online users","Enviar anuncio a todos los usuarios conectados"}. {"Send announcement to all online users on all hosts","Enviar anuncio a todos los usuarios conectados en todos los dominios"}. {"Send announcement to all users","Enviar anuncio a todos los usuarios"}. {"Send announcement to all users on all hosts","Enviar anuncio a todos los usuarios en todos los dominios"}. {"September","septiembre"}. {"Server ~b","Servidor ~b"}. {"Server:","Servidor:"}. {"Set message of the day and send to online users","Poner mensaje del dia y enviar a todos los usuarios conectados"}. {"Set message of the day on all hosts and send to online users","Poner mensaje del día en todos los dominios y enviar a los usuarios conectados"}. {"Shared Roster Groups","Grupos Compartidos"}. {"Show Integral Table","Mostrar Tabla Integral"}. {"Show Ordinary Table","Mostrar Tabla Ordinaria"}. {"Shut Down Service","Detener el servicio"}. {"~s invites you to the room ~s","~s te invita a la sala ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Algunos clientes Jabber pueden recordar tu contraseña en la máquina. Usa esa opción solo si confías en que la máquina que usas es segura."}. {"~s's Offline Messages Queue","Cola de mensajes diferidos de ~s"}. {"Start","Iniciar"}. {"Start Modules at ","Iniciar módulos en "}. {"Start Modules","Iniciar módulos"}. {"Statistics","Estadísticas"}. {"Statistics of ~p","Estadísticas de ~p"}. {"Stop","Detener"}. {"Stop Modules at ","Detener módulos en "}. {"Stop Modules","Detener módulos"}. {"Stopped Nodes","Nodos detenidos"}. {"Storage Type","Tipo de almacenamiento"}. {"Store binary backup:","Guardar copia de seguridad binaria:"}. {"Store plain text backup:","Guardar copia de seguridad en texto plano:"}. {"Subject","Asunto"}. {"Submit","Enviar"}. {"Submitted","Enviado"}. {"Subscription","Subscripción"}. {"Sunday","domingo"}. {"That nickname is already in use by another occupant","Ese apodo ya está siendo usado por otro ocupante"}. {"That nickname is registered by another person","El apodo ya está registrado por otra persona"}. {"The CAPTCHA is valid.","El CAPTCHA es válido."}. {"The CAPTCHA verification has failed","La verificación de CAPTCHA ha fallado"}. {"the password is","la contraseña es"}. {"The password is too weak","La contraseña es demasiado débil"}. {"The password of your Jabber account was successfully changed.","La contraseña de tu cuenta Jabber se ha cambiado correctamente."}. {"There was an error changing the password: ","Hubo un error cambiando la contraseña."}. {"There was an error creating the account: ","Hubo uno error al crear la cuenta:"}. {"There was an error deleting the account: ","Hubo un error borrando la cuenta."}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","No importa si usas mayúsculas: macbeth es lo mismo que MacBeth y Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta página te permite crear una cuenta Jabber este servidor Jabber. Tu JID (Jabber IDentificador) será de la forma: nombredeusuario@servidor. Por favor lee detenidamente las instrucciones para rellenar correctamente los campos."}. {"This page allows to unregister a Jabber account in this Jabber server.","Esta página te permite borrar tu cuenta Jabber en este servidor Jabber."}. {"This room is not anonymous","Sala no anónima"}. {"Thursday","jueves"}. {"Time delay","Retraso temporal"}. {"Time","Fecha"}. {"Too many CAPTCHA requests","Demasiadas peticiones de CAPTCHA"}. {"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}. {"To","Para"}. {"To ~s","A ~s"}. {"Total rooms","Salas totales"}. {"Traffic rate limit is exceeded","Se ha exedido el límite de tráfico"}. {"Transactions Aborted:","Transacciones abortadas:"}. {"Transactions Committed:","Transacciones finalizadas:"}. {"Transactions Logged:","Transacciones registradas:"}. {"Transactions Restarted:","Transacciones reiniciadas:"}. {"Tuesday","martes"}. {"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. {"Unauthorized","No autorizado"}. {"Unregister a Jabber account","Borrar una cuenta Jabber"}. {"Unregister","Borrar"}. {"Update","Actualizar"}. {"Update message of the day (don't send)","Actualizar mensaje del dia, pero no enviarlo"}. {"Update message of the day on all hosts (don't send)","Actualizar el mensaje del día en todos los dominos (pero no enviarlo)"}. {"Update ~p","Actualizar ~p"}. {"Update plan","Plan de actualización"}. {"Update script","Script de actualización"}. {"Uptime:","Tiempo desde el inicio:"}. {"Use of STARTTLS required","Es obligatorio usar STARTTLS"}. {"User Management","Administración de usuarios"}. {"Username:","Nombre de usuario:"}. {"Users are not allowed to register accounts so quickly","Los usuarios no tienen permitido crear cuentas con tanta rapidez"}. {"Users Last Activity","Última actividad de los usuarios"}. {"User ~s","Usuario ~s"}. {"Users","Usuarios"}. {"User","Usuario"}. {"Validate","Validar"}. {"vCard User Search","Buscar vCard de usuario"}. {"Virtual Hosts","Dominios Virtuales"}. {"Visitors are not allowed to change their nicknames in this room","Los visitantes no tienen permitido cambiar sus apodos en esta sala"}. {"Visitors are not allowed to send messages to all occupants","Los visitantes no pueden enviar mensajes a todos los ocupantes"}. {"Voice request","Petición de voz"}. {"Voice requests are disabled in this conference","Las peticiones de voz están desactivadas en esta sala"}. {"Wednesday","miércoles"}. {"You can later change your password using a Jabber client.","Puedes cambiar tu contraseña después, usando un cliente Jabber."}. {"You have been banned from this room","Has sido bloqueado en esta sala"}. {"You must fill in field \"Nickname\" in the form","Debes rellenar el campo \"Apodo\" en el formulario"}. {"You need a client that supports x:data and CAPTCHA to register","Necesitas un cliente con soporte de x:data y CAPTCHA para registrarte"}. {"You need a client that supports x:data to register the nickname","Necesitas un cliente con soporte de x:data para poder registrar el apodo"}. {"You need an x:data capable client to configure mod_irc settings","Necesitas un cliente con soporte de x:data para configurar las opciones de mod_irc"}. {"You need an x:data capable client to search","Necesitas un cliente con soporte de x:data para poder buscar"}. {"Your active privacy list has denied the routing of this stanza.","Tu lista de privacidad activa ha denegado el encío de este paquete."}. {"Your contact offline message queue is full. The message has been discarded.","Tu cola de mensajes diferidos de contactos está llena. El mensaje se ha descartado."}. {"Your Jabber account was successfully created.","Tu cuenta Jabber se ha creado correctamente."}. {"Your Jabber account was successfully deleted.","Tu cuenta Jabber se ha borrado correctamente."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Tus mensajes a ~s están siendo bloqueados. Para desbloquearlos, visita ~s"}. ejabberd-18.01/priv/msgs/el.po0000644000232200023220000024247513225664356016552 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: el\n" "POT-Creation-Date: \n" "PO-Revision-Date: 2017-10-27 12:33+0100\n" "Last-Translator: James Iakovos Mandelis \n" "Language-Team: \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Greek (ελληνικά)\n" "X-Generator: Poedit 2.0.4\n" #: mod_muc_log:413 mod_muc_log:588 msgid " has set the subject to: " msgstr " έχει θέσει το θέμα σε: " #: mod_muc_room:1904 msgid "A password is required to enter this room" msgstr "Απαιτείται κωδικός πρόσβασης για είσοδο σε αυτή την αίθουσα" #: ejabberd_oauth:448 msgid "Accept" msgstr "Αποδοχή" #: mod_configure:1109 msgid "Access Configuration" msgstr "Διαμόρφωση Πρόσβασης" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Διαχείριση στις Λίστες Ελέγχου Πρόσβασης" #: ejabberd_web_admin:484 ejabberd_web_admin:523 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Λίστες Ελέγχου Πρόσβασης" #: ejabberd_web_admin:589 ejabberd_web_admin:622 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Κανόνες Πρόσβασης" #: mod_configure:1095 msgid "Access control lists" msgstr "Λίστες Ελέγχου Πρόσβασης" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Άρνηση πρόσβασης, λόγω τακτικής παροχής υπηρεσιών" #: mod_configure:1113 msgid "Access rules" msgstr "Κανόνες Πρόσβασης" #: mod_configure:1772 msgid "Action on user" msgstr "Eνέργεια για το χρήστη" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Προσθήκη Jabber Ταυτότητας" #: ejabberd_web_admin:1003 mod_shared_roster:825 msgid "Add New" msgstr "Προσθήκη νέου" #: ejabberd_web_admin:1170 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Προσθήκη Χρήστη" #: ejabberd_web_admin:413 ejabberd_web_admin:424 msgid "Administration" msgstr "Διαχείριση" #: mod_configure:1767 msgid "Administration of " msgstr "Διαχείριση του " #: mod_muc_room:2548 msgid "Administrator privileges required" msgstr "Aπαιτούνται προνόμια διαχειριστή" #: mod_configure:507 msgid "All Users" msgstr "Όλοι οι χρήστες" #: ejabberd_web_admin:736 msgid "All activity" msgstr "Όλες οι δραστηριότητες" #: mod_muc_log:809 msgid "Allow users to change the subject" msgstr "Επιτρέψετε στους χρήστες να αλλάζουν το θέμα" #: mod_muc_log:815 msgid "Allow users to query other users" msgstr "Επιτρέπστε στους χρήστες να ερωτούν άλλους χρήστες" #: mod_muc_log:817 msgid "Allow users to send invites" msgstr "Επιτρέψετε στους χρήστες να αποστέλλουν προσκλήσεις" #: mod_muc_log:811 msgid "Allow users to send private messages" msgstr "Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα" #: mod_muc_log:820 msgid "Allow visitors to change nickname" msgstr "Επιτρέψετε στους επισκέπτες να αλλάζου ψευδώνυμο" #: mod_muc_log:813 msgid "Allow visitors to send private messages to" msgstr "Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα σε" #: mod_muc_log:822 msgid "Allow visitors to send status text in presence updates" msgstr "" "Επιτρέψτε στους επισκέπτες να αποστέλλουν κατάσταση στις ενημερώσεις " "παρουσίας" #: mod_announce:611 msgid "Announcements" msgstr "Ανακοινώσεις" #: mod_muc_log:476 msgid "April" msgstr "Απρίλιος" #: mod_muc_log:480 msgid "August" msgstr "Αύγουστος" #: mod_pubsub:1848 msgid "Automatic node creation is not enabled" msgstr "Η αυτόματη δημιουργία κόμβων δεν είναι ενεργοποιημένη" #: ejabberd_web_admin:1593 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Αποθήκευση Αντιγράφου Ασφαλείας" #: mod_configure:584 msgid "Backup Management" msgstr "Διαχείριση Αντιγράφου Ασφαλείας" #: ejabberd_web_admin:1705 msgid "Backup of ~p" msgstr "Αντιγράφο Ασφαλείας του ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Αποθήκευση Αντιγράφου Ασφαλείας σε Αρχείο στο " #: ejabberd_web_admin:489 ejabberd_web_admin:528 ejabberd_web_admin:594 #: ejabberd_web_admin:627 ejabberd_web_admin:663 ejabberd_web_admin:1148 #: ejabberd_web_admin:1431 ejabberd_web_admin:1587 ejabberd_web_admin:1871 #: ejabberd_web_admin:1900 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Ακατάλληλη μορφή" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Γενέθλια" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Τόσο το όνομα χρήστη όσο και ο πόρος είναι απαραίτητα" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Το Bytestream έχει ήδη ενεργοποιηθε" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Ιστοσελίδα CAPTCHA" #: ejabberd_web_admin:1933 msgid "CPU Time:" msgstr "Ώρα CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "Δεν είναι δυνατή η κατάργηση της ενεργής λίστας" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "Δεν μπορείτε να καταργήσετε την προεπιλεγμένη λίστα" #: ejabberd_web_admin:1405 mod_register_web:212 mod_register_web:373 #: mod_register_web:381 mod_register_web:406 msgid "Change Password" msgstr "Αλλαγή κωδικού" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Αλλαγή Κωδικού Πρόσβασης Χρήστη" #: mod_register:295 msgid "Changing password is not allowed" msgstr "Η αλλαγή του κωδικού πρόσβασης δεν επιτρέπεται" #: mod_muc_room:2784 msgid "Changing role/affiliation is not allowed" msgstr "Η αλλαγή ρόλου/ομάδας δεν επιτρέπεται" #: mod_register_web:258 msgid "Characters not allowed:" msgstr "Χαρακτήρες δεν επιτρέπονται:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Η αίθουσα σύνεδριασης δημιουργήθηκε" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Η αίθουσα σύνεδριασης διαγράφηκε" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Η αίθουσα σύνεδριασης έχει ξεκινήσει" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Η αίθουσα σύνεδριασης έχει σταματήσει" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Αίθουσες σύνεδριασης" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον " "διακομιστή" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Επιλέξτε modules για να σταματήσουν" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Επιλέξτε τύπο αποθήκευσης των πινάκων" #: mod_pubsub:1344 msgid "Choose whether to approve this entity's subscription." msgstr "Επιλέξτε αν θα εγκρίθεί η εγγραφή αυτής της οντότητας." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Πόλη" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Εντολές" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Αίθουσα σύνεδριασης δεν υπάρχει" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Διαμόρφωση" #: mod_muc_room:3183 msgid "Configuration of room ~s" msgstr "Διαμόρφωση Αίθουσας σύνεδριασης ~s" #: ejabberd_web_admin:1437 msgid "Connected Resources:" msgstr "Συνδεδεμένοι Πόροι:" #: mod_irc:526 msgid "Connections parameters" msgstr "Παράμετροι Συνδέσης" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Χώρα" #: ejabberd_web_admin:1592 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Βάση δεδομένων" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Διαμόρφωση Πίνακων βάσης δεδομένων στο " #: ejabberd_web_admin:1667 msgid "Database Tables at ~p" msgstr "Πίνακες βάσης δεδομένων στο ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3512 mod_pubsub:3519 mod_pubsub:3579 #: mod_pubsub:3604 mod_pubsub:3610 mod_pubsub:3613 mod_push:265 mod_push:280 #: mod_vcard:226 node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Αποτυχία βάσης δεδομένων" #: mod_muc_log:484 msgid "December" msgstr "Δεκέμβριος" #: mod_muc_log:807 msgid "Default users as participants" msgstr "Προεπιλογη χρήστων ως συμμετέχοντες" #: ejabberd_web_admin:537 ejabberd_web_admin:637 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Διαγραφή επιλεγμένων" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Διαγραφή Χρήστη" #: mod_announce:629 msgid "Delete message of the day" msgstr "Διαγράψτε το μήνυμα της ημέρας" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Διαγράψτε το μήνυμα της ημέρας σε όλους τους κεντρικούς υπολογιστές" #: mod_shared_roster:900 msgid "Description:" msgstr "Περιγραφή:" #: mod_configure:889 msgid "Disc only copy" msgstr "Αντίγραφο μόνο σε δίσκο" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Εμφανίσμενες Ομάδες:" #: mod_register_web:270 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Μην πείτε τον κωδικό πρόσβασής σας σε κανέναν, ακόμη και στους διαχειριστές " "του διακομιστή Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Αποθήκευση Αντιγράφου Ασφαλείας σε αρχείο κειμένου στο " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Αποθήκευση σε αρχείο κειμένου" # Should be "Duplicate" not "Duplicated" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Δεν επιτρέπονται διπλότυπες ομάδες από το RFC6121" #: mod_configure:1776 msgid "Edit Properties" msgstr "Επεξεργασία ιδιοτήτων" #: mod_muc_room:3901 msgid "Either approve or decline the voice request." msgstr "Είτε εγκρίνετε ή απορρίψτε το αίτημα φωνής." #: ejabberd_web_admin:1679 msgid "Elements" msgstr "Στοιχεία" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 msgid "Empty password" msgstr "Ο κωδικός πρόσβασης είναι κενός" #: mod_muc_log:818 msgid "Enable logging" msgstr "Ενεργοποίηση καταγραφής" #: mod_push:253 msgid "Enabling push without 'node' attribute is not supported" msgstr "" "Η ενεργοποίηση της ώθησης χωρίς το χαρακτηριστικό 'κόμβος' δεν υποστηρίζεται" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Κωδικοποίηση για διακομιστή ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Τερματισμός Συνεδρίας Χρήστη" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Εισάγετε κατάλογο των (Module, [Options])" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Πληκτρολογήστε το ψευδώνυμο που θέλετε να εγγραφείτε" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Εισάγετε τοποθεσία αρχείου αντιγράφου ασφαλείας" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Εισάγετε κατάλογο αρχείων σειράς jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Εισάγετε τοποθεσία αρχείου σειράς jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Εισάγετε Τοποθεσία Αρχείου Κειμένου" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Πληκτρολογήστε το κείμενο που βλέπετε" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Πληκτρολογήστε το όνομα χρήστη και κωδικοποιήσεις που θέλετε να " "χρησιμοποιήσετε για τη σύνδεση με διακομιστές IRC. Πατήστε 'Next' για να " "πάρετε περισσότερα πεδία να συμπληρώσετε. Πατήστε 'Complete' για να " "αποθηκεύσετε ρυθμίσεις." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Εισάγετε το όνομα χρήστη, κωδικοποιήσεις, τις θύρες και τους κωδικούς " "πρόσβασης που θέλετε να χρησιμοποιήσετε για σύνδεση με IRC διακομιστή" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Διακομιστής" #: ejabberd_web_admin:1702 ejabberd_web_admin:1873 msgid "Error" msgstr "Σφάλμα" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Παράδειγμα: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." "fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:1810 msgid "Export all tables as SQL queries to a file:" msgstr "Εξαγωγή όλων των πινάκων ως ερωτημάτων SQL σε ένα αρχείο:" #: ejabberd_web_admin:1782 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Εξαγωγή δεδομένων όλων των χρηστών του διακομιστή σε PIEFXIS αρχεία " "(XEP-0227):" #: ejabberd_web_admin:1794 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Εξαγωγή δεδομένων των χρηστών κεντρικού υπολογιστή σε PIEFXIS αρχεία " "(XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Βλάβη εξωτερικού στοιχείου" #: mod_delegation:283 msgid "External component timeout" msgstr "Τέλος χρονικού όριου εξωτερικού στοιχείου" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Απέτυχε η ενεργοποίηση του bytestream" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Απέτυχε η εξαγωγή JID από την έγκριση του αιτήματος φωνής σας" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" "Αποτυχία ταξιθέτησης μεταγεγραμμένου χώρου ονομάτων σε εξωτερικό στοιχείο" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Αποτυχία ανάλυσης της απόκρισης HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Δεν ήταν δυνατή η ανάλυση του chanserv" #: mod_muc_room:3317 msgid "Failed to process option '~s'" msgstr "Αποτυχία επεξεργασίας της επιλογής '~ s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Επώνυμο" #: mod_muc_log:474 msgid "February" msgstr "Φεβρουάριος" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "Αρχείο μεγαλύτερο από ~w bytes" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Συμπληρώστε τη φόρμα για να αναζητήσετε οποιαδήποτε Jabber χρήστη που " "ταιριάζει (Προσθέστε * στο τέλος τού πεδίου για να ταιριάξει σε μεγαλύτερες " "γραμματοσηρές)" #: mod_muc_log:467 msgid "Friday" msgstr "Παρασκευή" #: mod_offline:691 msgid "From" msgstr "Από" #: mod_configure:723 msgid "From ~s" msgstr "Από ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Ονοματεπώνυμο" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Έκθεση αριθμού συνδεδεμένων χρηστών" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Έκθεση αριθμού εγγεγραμμένων χρηστών" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Έκθεση Τελευταίας Ώρας Σύνδεσης Χρήστη" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Έκθεση Κωδικού Πρόσβασης Χρήστη" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Έκθεση Στατιστικών Χρήστη" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Ονομα" #: mod_shared_roster:923 msgid "Group " msgstr "Ομάδα " #: mod_roster:916 msgid "Groups" msgstr "Ομάδες" #: ejabberd_web_admin:1091 msgid "Host" msgstr "Κεντρικός Υπολογιστής" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Ο κεντρικός διακομιστής είναι άγνωστος" #: ejabberd_web_admin:2189 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Διευθύνσεις IP" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Διαβιβάσεις" #: mod_irc:496 msgid "IRC Username" msgstr "IRC Όνομα χρήστη" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC δίαυλος (μη βάλεται το πρώτο #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "Δεν βρέθηκε σύνδεση IRC" #: mod_irc:673 msgid "IRC server" msgstr "Διακομιστής IRC" #: mod_irc:756 msgid "IRC settings" msgstr "IRC Ρυθμίσεις" #: mod_irc:766 msgid "IRC username" msgstr "IRC όνομα χρήστη" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Εάν δεν βλέπετε την εικόνα CAPTCHA εδώ, επισκεφθείτε την ιστοσελίδα." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Εάν θέλετε να καθορίσετε διαφορετικές θύρες, κωδικούς πρόσβασης, " "κωδικοποιήσεις για IRC διακομιστές, εισάγετε πληροφορίες στη μορφή '{\"irc " "διακομιστής\", \"κωδικοποιήσεις\", θύρα, \"κωδικός πρόσβασης\"}'. " "Προεπιλεγμενα αυτή η υπηρεσία χρησιμοποιεί \"~s\" κωδικοποιήση, θύρα ~p, " "κενό κωδικό πρόσβασης." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Εισαγωγή κατάλογου αρχείων" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Εισαγωγή αρχείων" #: mod_configure:982 msgid "Import User from File at " msgstr "Εισαγωγή χρηστών από αρχείο στο " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Εισαγωγή Χρηστών από αρχεία σειράς jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Εισαγωγή χρηστών από κατάλογο αρχείων στο " #: ejabberd_web_admin:1826 msgid "Import user data from jabberd14 spool file:" msgstr "Εισαγωγή δεδομένων χρήστη από το αρχείο σειράς jabberd14:" #: ejabberd_web_admin:1769 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Εισαγωγή δεδομένων χρηστών από ένα αρχείο PIEFXIS (XEP-0227):" #: ejabberd_web_admin:1837 msgid "Import users data from jabberd14 spool directory:" msgstr "Εισαγωγή δεδομένων χρηστών από κατάλογο αρχείων σειράς jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Ακατάλληλο χαρακτηριστικό 'from'" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Ακατάλληλο χαρακτηριστικό 'to'" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Ανάρμοστο τμήμα τομέα του χαρακτηριστικού 'from'" #: mod_muc_room:260 msgid "Improper message type" msgstr "Ακατάλληλο είδος μηνύματος" #: ejabberd_web_admin:1312 msgid "Incoming s2s Connections:" msgstr "Εισερχόμενες συνδέσεις s2s:" #: mod_muc_room:3689 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "Λάθος υποβολή CAPTCHA" #: mod_muc:772 mod_muc_room:3072 mod_pubsub:1200 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Εσφαλμένη φόρμα δεδομένων" #: mod_muc_room:1954 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Εσφαλμένος κωδικός πρόσβασης" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Λανθασμένη τιμή στη φόρμα δεδομένων" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Λανθασμένη τιμή του χαρακτηριστικού 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Λανθασμένη τιμή 'action' στη φόρμα δεδομένων" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Λανθασμένη τιμή 'path' στη φόρμα δεδομένων" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Λανθασμένη τιμή του χαρακτηριστικού 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Ανεπαρκές προνόμια" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Μη έγκυρο χαρακτηριστικό 'από' στο προωθούμενο μήνυμα" #: mod_privilege:300 msgid "Invalid element" msgstr "Μη έγκυρο στοιχείο " #: mod_muc_room:3946 msgid "Invitations are not allowed in this conference" msgstr "Οι προσκλήσεις δεν επιτρέπονται σε αυτή τη διάσκεψη" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1052 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Δεν επιτρέπεται η αποστολή μηνυμάτων σφάλματος στο δωμάτιο. Ο συμμετέχων (~ " "s) έχει στείλει ένα μήνυμα σφάλματος (~ s) και έχει πέταχτεί έξω από την " "αίθουσα" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Δεν επιτρέπεται να στείλει προσωπικά μηνύματα του τύπου \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Δεν επιτρέπεται να στείλει προσωπικά μηνύματα για τη διάσκεψη" #: mod_register_web:199 mod_register_web:207 msgid "Jabber Account Registration" msgstr "Εγγραφή λογαριασμού Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Ταυτότητα Jabber" #: mod_muc_log:473 msgid "January" msgstr "Ιανουάριος" #: mod_irc:665 msgid "Join IRC channel" msgstr "Είσοδος στον IRC δίαυλος" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Είσοδος στον δίαυλο IRC εδώ." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Είσοδος στο δίαυλο IRC αυτής της Jabber Ταυτότητας: ~s" #: mod_muc_log:479 msgid "July" msgstr "Ιούλιος" #: mod_muc_log:478 msgid "June" msgstr "Ιούνιος" #: ejabberd_web_admin:1214 ejabberd_web_admin:1441 msgid "Last Activity" msgstr "Τελευταία Δραστηριότητα" #: mod_configure:1646 msgid "Last login" msgstr "Τελευταία σύνδεση" #: ejabberd_web_admin:733 msgid "Last month" msgstr "Περασμένο μήνα" #: ejabberd_web_admin:734 msgid "Last year" msgstr "Πέρυσι" #: mod_configure:941 msgid "List of modules to start" msgstr "Λίστα των Module για Εκκίνηση" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Κατάλογος αιθουσών" #: ejabberd_web_admin:1595 msgid "Listened Ports" msgstr "Παρακολουθούμενες Θύρες" #: ejabberd_web_admin:1865 msgid "Listened Ports at " msgstr "Παρακολουθούμενες Θύρες στο " #: ejabberd_web_admin:2007 msgid "Low level update script" msgstr "Προγράμα ενημέρωσης χαμηλού επίπεδου" #: mod_muc_log:796 msgid "Make participants list public" msgstr "Κάντε κοινό τον κατάλογο συμμετεχόντων" #: mod_muc_log:825 msgid "Make room CAPTCHA protected" msgstr "Κάντε την αίθουσα CAPTCHA προστατεύονομενη" #: mod_muc_log:803 msgid "Make room members-only" msgstr "Κάντε την αίθουσα μόνο για μέλη" #: mod_muc_log:805 msgid "Make room moderated" msgstr "Κάντε την αίθουσα εποπτεύονομενη" #: mod_muc_log:798 msgid "Make room password protected" msgstr "Κάντε την αίθουσα προστατεύομενη με κωδικό πρόσβασης" #: mod_muc_log:792 msgid "Make room persistent" msgstr "Κάντε αίθουσα μόνιμη" #: mod_muc_log:794 msgid "Make room public searchable" msgstr "Κάντε την δημόσια αναζήτηση δυνατή για αυτή την αίθουσα" #: mod_register:317 msgid "Malformed username" msgstr "Λανθασμένη μορφή ονόματος χρήστη" #: mod_muc_log:475 msgid "March" msgstr "Μάρτιος" #: mod_muc_log:831 msgid "Maximum Number of Occupants" msgstr "Μέγιστος αριθμός συμετεχόντων" #: mod_muc_log:477 msgid "May" msgstr "Μάιος" #: mod_shared_roster:907 msgid "Members:" msgstr "Μέλη:" #: mod_muc_room:1847 msgid "Membership is required to enter this room" msgstr "Απαιτείται αίτηση συμετοχής για είσοδο σε αυτή την αίθουσα" #: mod_register_web:281 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Απομνημονεύστε τον κωδικό πρόσβασής σας, ή γράψετε τον σε ένα χαρτί που είχε " "τοποθετηθεί σε ασφαλές μέρος. Στο Jabber δεν υπάρχει αυτοματοποιημένος " "τρόπος για να ανακτήσετε τον κωδικό σας αν τον ξεχάσετε." #: ejabberd_web_admin:1680 msgid "Memory" msgstr "Μνήμη" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Περιεχόμενο μηνυμάτως" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Δεν βρέθηκε μήνυμα στο προωθημένο ωφέλιμο φορτίο" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Πατρώνυμο" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Δεν υπάρχει \"δίαυλος\" ή \"διακομιστής\" στη φόρμα δεδομένων" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Λείπει χαρακτηριστικό 'from'" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Λείπει χαρακτηριστικό 'to'" #: mod_muc_room:2556 mod_muc_room:3741 mod_muc_room:3785 mod_muc_room:3818 msgid "Moderator privileges required" msgstr "Aπαιτούνται προνόμια συντονιστή" #: ejabberd_web_admin:2005 msgid "Modified modules" msgstr "Τροποποιημένα modules" #: ejabberd_web_admin:2191 ejabberd_web_admin:2346 msgid "Module" msgstr "Module" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "Το μodule απέτυχε να χειριστεί το ερώτημα" #: ejabberd_web_admin:1611 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Modules" #: ejabberd_web_admin:1894 msgid "Modules at ~p" msgstr "Modules στο ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Δευτέρα" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Συνομιλία με πολλούς χρήστες" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Πολλαπλά στοιχεία δεν επιτρέπονται από το RFC6121" #: ejabberd_web_admin:1677 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Όνομα" #: mod_shared_roster:896 msgid "Name:" msgstr "Όνομα:" #: mod_muc_room:2716 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Δεν βρέθηκε κανένα χαρακτηριστικό 'jid' ούτε 'nick'" #: mod_muc_room:2538 mod_muc_room:2721 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Δεν βρέθηκε ούτε χαρακτηριστικό 'role' ούτε 'affiliation'" #: ejabberd_web_admin:1232 ejabberd_web_admin:1413 mod_configure:1629 msgid "Never" msgstr "Ποτέ" #: mod_register_web:397 msgid "New Password:" msgstr "Νέος κωδικός πρόσβασης:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Ψευδώνυμο" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Εγγραφή με Ψευδώνυμο στο " #: mod_muc_room:2733 msgid "Nickname ~s does not exist in the room" msgstr "Ψευδώνυμο ~s δεν υπάρχει σε αυτή την αίθουσα" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Δεν υπάρχει 'access' στη φόρμα δεδομένων" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Δεν υπάρχει 'acls' στη φόρμα δεδομένων" #: mod_muc_room:3095 msgid "No 'affiliation' attribute found" msgstr "Δεν βρέθηκε χαρακτηριστικό 'affiliation'" #: mod_muc_room:2525 msgid "No 'item' element found" msgstr "Δεν βρέθηκε στοιχείο 'item'" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Δεν υπάρχει 'modules' στη φόρμα δεδομένων" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Δεν υπάρχει 'password' στη φόρμα δεδομένων" #: mod_register:148 msgid "No 'password' found in this query" msgstr "Δεν βρέθηκε \"password\" σε αυτό το ερώτημα" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Δεν υπάρχει 'path' στη φόρμα δεδομένων" #: mod_muc_room:3942 msgid "No 'to' attribute found in the invitation" msgstr "Δεν υπάρχει χαρακτηριστικό 'to' που βρέθηκε στην πρόσκληση" #: ejabberd_web_admin:1493 msgid "No Data" msgstr "Κανένα στοιχείο" #: ejabberd_local:181 msgid "No available resource found" msgstr "Δεν βρέθηκε διαθέσιμος πόρος" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Δεν προμηθεύτικε περιεχόμενο ανακοινώσης" #: mod_irc:335 mod_pubsub:1189 mod_pubsub:3295 msgid "No data form found" msgstr "Δεν βρέθηκε φόρμα δεδομένων" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "Δεν υπάρχουν διαθέσιμες λειτουργίες" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Κανένα άγκιστρο δεν έχει επεξεργαστεί αυτήν την εντολή" #: mod_last:218 msgid "No info about last activity found" msgstr "Δεν βρέθηκαν πληροφορίες για την τελευταία δραστηριότητα" #: mod_blocking:99 msgid "No items found in this query" msgstr "Δεν βρέθηκαν στοιχεία σε αυτό το ερώτημα" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Καμνένα module δεν χειρίζεται αυτό το ερώτημα" #: mod_pubsub:1547 msgid "No node specified" msgstr "Δεν καθορίστηκε κόμβος" #: mod_pubsub:1432 msgid "No pending subscriptions found" msgstr "Δεν βρέθηκαν εκκρεμείς συνδρομές" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "Δεν βρέθηκε κατάλογος απορρήτου με αυτό το όνομα" #: mod_private:96 msgid "No private data found in this query" msgstr "Δεν βρέθηκαν ιδιωτικά δεδομένα σε αυτό το ερώτημα" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "Δεν βρέθηκε ενεργός κόμβος" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "Δεν υπάρχουν διαθέσιμες υπηρεσίες" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Δεν βρέθηκαν στατιστικά στοιχεία για αυτό το στοιχείο" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "Ο κόμβος υπάρχει ήδη" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Ο δείκτης κόμβου δεν βρέθηκε" #: ejabberd_web_admin:772 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Κόμβος δεν βρέθηκε" #: ejabberd_web_admin:1583 ejabberd_web_admin:1608 msgid "Node ~p" msgstr "Κόμβος ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Το Nodeprep απέτυχε" #: ejabberd_web_admin:1563 msgid "Nodes" msgstr "Κόμβοι" #: ejabberd_web_admin:1348 ejabberd_web_admin:1544 ejabberd_web_admin:1554 #: ejabberd_web_admin:1964 mod_roster:907 msgid "None" msgstr "Κανένα" #: ejabberd_web_admin:759 msgid "Not Found" msgstr "Δεν Βρέθηκε" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "Δεν έχετε εγγραφεί" #: mod_muc_log:483 msgid "November" msgstr "Νοέμβριος" #: mod_configure:1212 msgid "Number of online users" msgstr "Αριθμός συνδεδεμένων χρηστών" #: mod_configure:1202 msgid "Number of registered users" msgstr "Αριθμός εγγεγραμμένων χρηστών" #: ejabberd_web_admin:1726 ejabberd_web_admin:1736 ejabberd_web_admin:1747 #: ejabberd_web_admin:1756 ejabberd_web_admin:1766 ejabberd_web_admin:1779 #: ejabberd_web_admin:1791 ejabberd_web_admin:1807 ejabberd_web_admin:1823 #: ejabberd_web_admin:1834 ejabberd_web_admin:1844 msgid "OK" msgstr "Όλλα Καλά" #: mod_muc_log:482 msgid "October" msgstr "Οκτώβριος" #: ejabberd_web_admin:1213 msgid "Offline Messages" msgstr "Χωρίς Σύνδεση Μηνύματα" #: mod_offline:761 msgid "Offline Messages:" msgstr "Χωρίς Σύνδεση Μηνύματα:" #: mod_register_web:393 msgid "Old Password:" msgstr "Παλαιός κωδικός πρόσβασης:" #: ejabberd_web_admin:1250 ejabberd_web_admin:1424 mod_configure:1639 msgid "Online" msgstr "Συνδεδεμένο" #: ejabberd_web_admin:700 ejabberd_web_admin:1093 mod_configure:506 msgid "Online Users" msgstr "Συνδεμένοι χρήστες" #: ejabberd_web_admin:1306 ejabberd_web_admin:1325 ejabberd_web_admin:1937 msgid "Online Users:" msgstr "Online Χρήστες:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Επιτρέπονται μόνο tags ή " #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Στο ερώτημα αυτό επιτρέπεται μόνο το στοιχείο " #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Μόνο μέλη μπορούν να δούνε τα αρχεία αυτής της αίθουσας" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Μόνο οι συντονιστές και οι συμμετέχοντες μπορούν να αλλάξουν το θέμα αυτής " "της αίθουσας" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Μόνο οι συντονιστές μπορούν να αλλάξουν το θέμα αυτής της αίθουσας" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Μόνο οι συντονιστές μπορούν να εγκρίνουν τις αιτήσεις φωνής" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:4009 msgid "Only occupants are allowed to send messages to the conference" msgstr "Μόνο οι συμμετέχωντες μπορούν να στέλνουν μηνύματα στο συνέδριο" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Μόνο οι συμετεχόντες μπορούν να στείλουν ερωτήματα στη διάσκεψη" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Μόνο οι διαχειριστές των υπηρεσιών επιτρέπεται να στείλουν υπηρεσιακά " "μηνύματα" #: ejabberd_web_admin:2192 ejabberd_web_admin:2347 msgid "Options" msgstr "Επιλογές" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Όνομα Οργανισμού" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Μονάδα Οργανισμού" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Εξερχόμενες S2S Συνδέσεις" #: ejabberd_web_admin:1309 msgid "Outgoing s2s Connections:" msgstr "Εξερχόμενες S2S Συνδέσεις:" #: mod_muc_room:3043 mod_muc_room:3087 mod_muc_room:3717 mod_pubsub:1308 #: mod_pubsub:1400 mod_pubsub:1559 mod_pubsub:2124 mod_pubsub:2190 #: mod_pubsub:2389 mod_pubsub:2470 mod_pubsub:3069 mod_pubsub:3212 msgid "Owner privileges required" msgstr "Aπαιτούνται προνόμια ιδιοκτήτη" #: mod_offline:693 msgid "Packet" msgstr "Πακέτο" #: mod_irc:578 msgid "Parse error" msgstr "Σφάλμα ανάλυσης" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "Η ανάλυση απέτυχε" #: ejabberd_oauth:431 ejabberd_web_admin:1161 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:799 #: mod_register:229 msgid "Password" msgstr "Κωδικός Πρόσβασης" #: mod_configure:1130 msgid "Password Verification" msgstr "Επαλήθευση κωδικού πρόσβασης" #: mod_register_web:287 mod_register_web:401 msgid "Password Verification:" msgstr "Επαλήθευση κωδικού πρόσβασης:" #: mod_irc:822 msgid "Password ~b" msgstr "Κωδικός πρόσβασης ~b" #: ejabberd_web_admin:1439 mod_register_web:264 mod_register_web:504 msgid "Password:" msgstr "Κωδικός πρόσβασης:" #: mod_configure:998 msgid "Path to Dir" msgstr "Τοποθεσία κατάλογου αρχείων" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Τοποθεσία Αρχείου" #: mod_roster:915 msgid "Pending" msgstr "Εκκρεμεί" #: ejabberd_web_admin:720 msgid "Period: " msgstr "Περίοδος: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Μόνιμες αίθουσες" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Πινγκ" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Το Ping είναι λανθασμένο" #: ejabberd_web_admin:1709 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Παρακαλώ σημειώστε ότι οι επιλογές αυτές θα αποθήκευσουν Αντιγράφο Ασφαλείας " "μόνο της ενσωματωμένης βάσης δεδομένων Mnesia. Εάν χρησιμοποιείτε το module " "ODBC, θα πρέπει επίσης να κάνετε χωριστά Αντιγράφο Ασφαλείας της SQL βάση " "δεδομένων σας ." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Παρακαλώ, περιμένετε για λίγο πριν την αποστολή νέου αιτήματος φωνής" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2189 msgid "Port" msgstr "Θύρα" #: mod_irc:828 msgid "Port ~b" msgstr "Θύρα ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Η ιδιότητα 'ask' δεν επιτρέπεται από το RFC6121" #: ejabberd_web_admin:2190 msgid "Protocol" msgstr "Πρωτόκολλο" #: mod_pubsub:1341 msgid "PubSub subscriber request" msgstr "Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής" #: mod_pubsub:976 msgid "Publish-Subscribe" msgstr "Δημοσίευση-Εγγραφή" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Η δημοσίευση στοιχείων σε κόμβους συλλογής δεν επιτρέπεται" #: mod_push:283 #, fuzzy msgid "Push record not found" msgstr "Κόμβος δεν βρέθηκε" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Το ερώτημα σε άλλους χρήστες είναι απαγορευμένο" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Αντίγραφο μόνο σε RAM καί δίσκο" #: mod_configure:889 msgid "RAM copy" msgstr "Αντίγραφο σε RAM" #: ejabberd_web_admin:1616 msgid "RPC Call Error" msgstr "Σφάλμα RPC Κλήσης" #: ejabberd_web_admin:532 ejabberd_web_admin:631 msgid "Raw" msgstr "Ακατέργαστο" # ; is question mark in Greek #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Πραγματικά να διαγράψετε το μήνυμα της ημέρας;" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Παραλήπτης δεν είναι στην αίθουσα συνεδριάσεων" #: mod_register_web:294 msgid "Register" msgstr "Καταχωρήστε" #: mod_register_web:210 mod_register_web:229 mod_register_web:237 msgid "Register a Jabber account" msgstr "Καταχωρήστε έναν λογαριασμό Jabber" #: ejabberd_web_admin:1092 msgid "Registered Users" msgstr "Εγγεγραμμένοι Χρήστες" #: ejabberd_web_admin:1303 ejabberd_web_admin:1322 msgid "Registered Users:" msgstr "Εγγεγραμμένοι Χρήστες:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Καταχωρημένα ψευδώνυμα" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Εγγραφή στο mod_irc για " #: mod_configure:889 msgid "Remote copy" msgstr "Απομεμακρυσμένο αντίγραφο" #: mod_roster:963 msgid "Remove" msgstr "Αφαίρεστε" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Αφαίρεση Όλων των Χωρίς Σύνδεση Μηνύματων" #: ejabberd_web_admin:1446 mod_configure:1779 msgid "Remove User" msgstr "Αφαίρεση χρήστη" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Αντικαταστάθικε από νέα σύνδεση" #: mod_configure:1675 msgid "Resources" msgstr "Πόροι" #: ejabberd_web_admin:1602 ejabberd_web_admin:2220 ejabberd_web_admin:2364 msgid "Restart" msgstr "Επανεκκίνηση" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Επανεκκίνηση Υπηρεσίας" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Επαναφορά Αντιγράφου Ασφαλείας" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Επαναφορά Αντιγράφου Ασφαλείας από αρχείο στο " #: ejabberd_web_admin:1739 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Επαναφορά δυαδικού αντιγράφου ασφαλείας μετά την επόμενη επανεκκίνηση του " "ejabberd (απαιτεί λιγότερη μνήμη):" #: ejabberd_web_admin:1729 msgid "Restore binary backup immediately:" msgstr "Επαναφορά δυαδικού αντιγράφου ασφαλείας αμέσως:" #: ejabberd_web_admin:1759 msgid "Restore plain text backup immediately:" msgstr "Επαναφορά αντιγράφου ασφαλείας από αρχείο κειμένου αμέσως:" #: mod_muc_log:635 msgid "Room Configuration" msgstr "Διαμόρφωση Αίθουσας σύνεδριασης" #: mod_muc_log:655 msgid "Room Occupants" msgstr "Συμετεχόντες Αίθουσας σύνεδριασης" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Άρνηση δημιουργίας αίθουσας , λόγω τακτικής παροχής υπηρεσιών" #: mod_muc_log:827 msgid "Room description" msgstr "Περιγραφή Αίθουσας" #: mod_muc_log:790 msgid "Room title" msgstr "Τίτλος Αίθουσας" #: mod_roster:1082 msgid "Roster" msgstr "Καταλόγος Επαφών" #: mod_roster:334 msgid "Roster module has failed" msgstr "Το Roster module απέτυχε" #: mod_roster:968 msgid "Roster of " msgstr "Καταλόγος Επαφών τού " #: mod_configure:1671 msgid "Roster size" msgstr "Μέγεθος Καταλόγου Επαφών" #: ejabberd_web_admin:1564 mod_configure:510 msgid "Running Nodes" msgstr "Ενεργοί Κόμβοι" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "Η διαπραγμάτευση SASL δεν επιτρέπεται σε αυτή την κατάσταση" #: mod_muc_log:468 msgid "Saturday" msgstr "Σάββατο" #: mod_irc:582 msgid "Scan error" msgstr "Σφάλμα σάρωσης" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "Η σάρωση απέτυχε" #: ejabberd_web_admin:2008 msgid "Script check" msgstr "Script ελέγχου" #: mod_vcard:453 msgid "Search Results for " msgstr "Αποτελέσματα αναζήτησης για " #: mod_vcard:427 msgid "Search users in " msgstr "Αναζήτηση χρηστών στο " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" "Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες σε όλους τους " "κεντρικούς υπολογιστές" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Αποστολή ανακοίνωσης σε όλους τους χρήστες" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "" "Αποστολή ανακοίνωσης σε όλους τους χρήστες σε όλους τους κεντρικούς " "υπολογιστές" #: mod_muc_log:481 msgid "September" msgstr "Σεπτέμβριος" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Η σύνδεση διακομιστή απέτυχε" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Οι συνδέσεις διακομιστή με τοπικούς υποτομείς απαγορεύονται" #: mod_irc:842 msgid "Server ~b" msgstr "Διακομιστής ~b" #: mod_register_web:261 mod_register_web:390 mod_register_web:501 msgid "Server:" msgstr "Διακομιστής:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Ορίστε μήνυμα ημέρας και αποστολή στους συνδεδεμένους χρήστες" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Ορίστε μήνυμα ημέρας και άμεση αποστολή στους συνδεδεμένους χρήστες σε όλους " "τους κεντρικούς υπολογιστές" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Κοινές Ομάδες Καταλόγων Επαφών" #: ejabberd_web_admin:742 msgid "Show Integral Table" msgstr "Δείτε Ολοκληρωτικό Πίνακα" #: ejabberd_web_admin:739 msgid "Show Ordinary Table" msgstr "Δείτε Κοινό Πίνακα" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Κλείσιμο Υπηρεσίας" #: mod_register_web:277 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Μερικοί πελάτες Jabber μπορεί να αποθηκεύσουν τον κωδικό πρόσβασής σας στον " "υπολογιστή σας. Χρησιμοποιήστε αυτό το χαρακτηριστικό μόνο εάν εμπιστεύεστε " "την ασφάλεια του υπολογιστή σας." #: ejabberd_web_admin:2244 ejabberd_web_admin:2380 msgid "Start" msgstr "Εκκίνηση" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Εκκίνηση Modules" #: mod_configure:936 msgid "Start Modules at " msgstr "Εκκίνηση Modules στο " #: ejabberd_web_admin:749 ejabberd_web_admin:1597 mod_muc_admin:366 msgid "Statistics" msgstr "Στατιστικές" #: ejabberd_web_admin:1925 msgid "Statistics of ~p" msgstr "Στατιστικές του ~p" #: ejabberd_web_admin:1604 ejabberd_web_admin:2224 ejabberd_web_admin:2368 msgid "Stop" msgstr "Σταμάτημα" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "ΠαύσηModules" #: mod_configure:918 msgid "Stop Modules at " msgstr "Παύση Modules στο " #: ejabberd_web_admin:1565 mod_configure:511 msgid "Stopped Nodes" msgstr "Σταματημένοι Κόμβοι" #: ejabberd_web_admin:1678 msgid "Storage Type" msgstr "Τύπος Αποθήκευσης" #: ejabberd_web_admin:1719 msgid "Store binary backup:" msgstr "Αποθηκεύση δυαδικού αντιγράφου ασφαλείας:" #: ejabberd_web_admin:1749 msgid "Store plain text backup:" msgstr "Αποθηκεύση αντιγράφου ασφαλείας σε αρχείο κειμένου:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Θέμα" #: ejabberd_web_admin:500 ejabberd_web_admin:540 ejabberd_web_admin:605 #: ejabberd_web_admin:670 ejabberd_web_admin:1689 mod_shared_roster:933 msgid "Submit" msgstr "Υποβοβολή" #: ejabberd_web_admin:488 ejabberd_web_admin:527 ejabberd_web_admin:593 #: ejabberd_web_admin:626 ejabberd_web_admin:662 ejabberd_web_admin:1147 #: ejabberd_web_admin:1430 ejabberd_web_admin:1586 ejabberd_web_admin:1620 #: ejabberd_web_admin:1700 ejabberd_web_admin:1870 ejabberd_web_admin:1899 #: ejabberd_web_admin:1996 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Υποβλήθηκε" #: mod_roster:914 msgid "Subscription" msgstr "Συνδρομή" #: mod_muc_room:3728 msgid "Subscriptions are not allowed" msgstr "Οι συνδρομές δεν επιτρέπονται" #: mod_muc_log:469 msgid "Sunday" msgstr "Κυριακή" #: mod_muc_room:998 mod_muc_room:1857 mod_muc_room:3757 msgid "That nickname is already in use by another occupant" msgstr "Αυτό το ψευδώνυμο είναι ήδη σε χρήση από άλλον συμμετέχων" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1866 mod_muc_room:3760 msgid "That nickname is registered by another person" msgstr "Αυτό το ψευδώνυμο είναι καταχωρημένο από άλλο πρόσωπο" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Το CAPTCHA είναι έγκυρο." #: mod_muc_room:653 mod_muc_room:3692 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Η επαλήθευση της εικόνας CAPTCHA απέτυχε" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "Η λειτουργία που ζητήθηκε δεν υποστηρίζεται από τη διάσκεψη" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "Ο κωδικός πρόσβασης περιέχει μη αποδεκτούς χαρακτήρες" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Ο κωδικός πρόσβασης είναι πολύ ασθενές" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Ο κωδικός πρόσβασης του Jabber λογαριασμού σας έχει αλλάξει επιτυχώς." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "Το ερώτημα επιτρέπεται μόνο από τοπικούς χρήστες" #: mod_roster:203 msgid "The query must not contain elements" msgstr "Το ερώτημα δεν πρέπει να περιέχει στοιχείο " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "Η stanza ΠΡΕΠΕΙ να περιέχει μόνο ένα στοιχείο , ένα στοιχείο " " ή ένα στοιχείο " #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Υπήρξε ένα σφάλμα κατά την αλλαγή του κωδικού πρόσβασης: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Υπήρξε ένα σφάλμα κατά τη δημιουργία του λογαριασμού: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Υπήρξε ένα σφάλμα κατά τη διαγραφή του λογαριασμού: " #: mod_register_web:255 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Ανεξαρτήτως με πεζά ή κεφαλαία: 'μιαλεξη' είναι το ίδιο με 'ΜιαΛέξη' και " "'Μιαλέξη'." #: mod_register_web:239 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Αυτή η σελίδα σας επιτρέπει να δημιουργήσετε ένα λογαριασμό Jabber σε αυτόν " "το διακομιστή Jabber. JID σας (Jabber Identifier) θα είναι της μορφής: " "όνομα_χρήστη@διακομιστής_Jabber. Παρακαλώ διαβάστε προσεκτικά τις οδηγίες " "για να συμπληρώσετε σωστά τα πεδία." #: mod_register_web:491 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Η σελίδα αυτή δίνει τη δυνατότητα να καταργήσετε την καταχώρηση ενός " "λογαριασμό Jabber σε αυτόν το διακομιστή Jabber." #: mod_muc_log:801 msgid "This room is not anonymous" msgstr "Η αίθουσα αυτή δεν είναι ανώνυμη" #: mod_muc_log:466 msgid "Thursday" msgstr "Πέμπτη" #: mod_offline:690 msgid "Time" msgstr "Χρόνος" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Χρόνος καθυστέρησης" #: mod_offline:692 msgid "To" msgstr "Πρώς" #: mod_register:215 msgid "To register, visit ~s" msgstr "Για να εγγραφείτε, επισκεφθείτε το ~ s" #: mod_configure:709 msgid "To ~s" msgstr "Πρώς ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Token TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Πολύ μακριά η τιμή του χαρακτηριστικού 'xml: lang'" #: mod_muc_room:2561 mod_muc_room:3101 msgid "Too many elements" msgstr "Πάρα πολλά στοιχεία " #: mod_privacy:164 msgid "Too many elements" msgstr "Πάρα πολλά στοιχεία " #: mod_muc_room:1935 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Πάρα πολλά αιτήματα CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Πάρα πολλά ενεργά bytestreams" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Πάρα πολλές μη αναγνωρισμένες stanzas" #: mod_muc_room:1816 msgid "Too many users in this conference" msgstr "Πάρα πολλοί χρήστες σε αυτή τη διάσκεψη" #: mod_register:355 msgid "Too many users registered" msgstr "Πάρα πολλοί εγγεγραμένοι χρήστες" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Συνολικές Αίθουσες σύνεδριασης" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Υπέρφορτωση" #: ejabberd_web_admin:1945 msgid "Transactions Aborted:" msgstr "Αποτυχημένες συναλλαγές:" #: ejabberd_web_admin:1941 msgid "Transactions Committed:" msgstr "Παραδοθείς συναλλαγές:" #: ejabberd_web_admin:1953 msgid "Transactions Logged:" msgstr "Καταγραμμένες συναλλαγές:" #: ejabberd_web_admin:1949 msgid "Transactions Restarted:" msgstr "Επανειλημμένες συναλλαγές:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Τρίτη" #: mod_muc_room:1944 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Αδήνατο να δημιουργηθεί CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Δεν είναι δυνατή η καταχώρηση της διαδρομής σε υπάρχοντα τοπικό τομέα" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Χορίς Εξουσιοδότηση" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Απροσδόκητη ενέργεια" #: mod_register_web:509 msgid "Unregister" msgstr "Καταργήση εγγραφής" #: mod_register_web:215 mod_register_web:481 mod_register_web:489 msgid "Unregister a Jabber account" msgstr "Καταργήστε την εγγραφή ενός λογαριασμού Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "Μη υποστηριζόμενο στοιχείο " #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Μη υποστηριζόμενο ερώτημα MIX" #: ejabberd_web_admin:1598 ejabberd_web_admin:2011 msgid "Update" msgstr "Ενημέρωση" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Ενημέρωση μηνύματως ημέρας (χωρίς άμεση αποστολή)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "" "Ενημέρωση μηνύματως ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση " "αποστολή)" #: ejabberd_web_admin:2004 msgid "Update plan" msgstr "Σχέδιο ενημέρωσης" #: ejabberd_web_admin:2006 msgid "Update script" msgstr "Προγράμα ενημέρωσης" #: ejabberd_web_admin:1993 msgid "Update ~p" msgstr "Ενημέρωση ~p" #: ejabberd_web_admin:1929 msgid "Uptime:" msgstr "Uptime:" #: xmpp_stream_out:535 msgid "Use of STARTTLS forbidden" msgstr "Η χρήση του STARTTLS είναι απαγορευμένη" #: xmpp_stream_in:573 xmpp_stream_out:529 xmpp_stream_out:605 msgid "Use of STARTTLS required" msgstr "Απαιτείται χρήση STARTTLS" #: ejabberd_web_admin:1156 ejabberd_web_admin:1212 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Χρήστης" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Χρήστη (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Διαχείριση χρηστών" #: mod_register:345 msgid "User already exists" msgstr "Ο χρήστης υπάρχει ήδη" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "Το τμήμα χρήστη του JID στο 'from' είναι άδειο" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_push:268 mod_sic:106 msgid "User session not found" msgstr "Η συνάντηση χρήστη δεν βρέθηκε" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Η σύνδεση χρήστη τερματίστηκε" #: ejabberd_web_admin:1426 msgid "User ~s" msgstr "Ο Χρήστης ~s" #: mod_register_web:249 mod_register_web:386 mod_register_web:497 msgid "Username:" msgstr "Όνομα χρήστη:" #: ejabberd_web_admin:685 ejabberd_web_admin:693 msgid "Users" msgstr "Χρήστες" #: ejabberd_web_admin:716 msgid "Users Last Activity" msgstr "Τελευταία Δραστηριότητα Χρήστη" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Οι χρήστες δεν επιτρέπεται να εγγραφούν λογαριασμούς τόσο γρήγορα" #: mod_roster:954 msgid "Validate" msgstr "Επαληθεύστε" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3682 mod_muc_room:3822 #: mod_pubsub:890 mod_push:250 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "Η τιμή 'get' του 'type' δεν επιτρέπεται" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3617 mod_muc_room:3661 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:815 mod_pubsub:834 mod_pubsub:872 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Δεν επιτρέπεται η παράμετρος 'set' του 'type'" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "Η τιμή του '~ s' πρέπει να είναι boolean" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "Η τιμή του '~ s' θα πρέπει να είναι χρονοσειρά" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "Η τιμή του '~ s' θα πρέπει να είναι ακέραιος" #: ejabberd_web_admin:676 msgid "Virtual Hosts" msgstr "Eεικονικοί κεντρικοί υπολογιστές" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "" "Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την " "αίθουσα" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "" "Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους " "συμμετέχωντες" #: mod_muc_room:3899 msgid "Voice request" msgstr "Αίτημα φωνής" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Τα αιτήματα φωνής είναι απενεργοποιημένα, σε αυτό το συνέδριο" #: mod_muc_log:465 msgid "Wednesday" msgstr "Τετάρτη" #: mod_register_web:274 msgid "You can later change your password using a Jabber client." msgstr "" "Μπορείτε αργότερα να αλλάξετε τον κωδικό πρόσβασής σας χρησιμοποιώντας έναν " "πελάτη Jabber." #: mod_muc_room:1844 msgid "You have been banned from this room" msgstr "Σας έχει απαγορευθεί η είσοδος σε αυτή την αίθουσα" #: mod_muc_room:1825 msgid "You have joined too many conferences" msgstr "Είσθε σε πάρα πολλά συνέδρια" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Θα πρέπει να συμπληρώσετε το πεδίο \"Nickname\" στη φόρμα" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Χρειάζεστε ένα x:data και CAPTCHA ικανό πελάτη για εγγραφή" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για εγγραφή με ψευδώνυμο" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε το mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για αναζήτηση" #: mod_pubsub:1510 msgid "You're not allowed to create nodes" msgstr "Δεν σου επιτρέπεται η δημιουργία κόμβων" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Ο Jabber λογαριασμός σας δημιουργήθηκε με επιτυχία." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Ο Jabber λογαριασμός σας διαγράφηκε με επιτυχία." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" "Ο ενεργός κατάλογος απορρήτου, έχει αρνηθεί τη δρομολόγηση αυτής της στροφής " "(stanza)." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Η μνήμη χωρίς σύνδεση μήνυματών είναι πλήρης. Το μήνυμα έχει απορριφθεί." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Τα μηνύματά σας πρως ~s είναι αποκλεισμένα. Για αποδεσμεύση, επισκεφθείτε ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC module" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC module" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "υπηρεσία ejabberd Multicast" #: mod_pubsub:1073 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd module Δημοσίευσης-Εγγραφής" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams module" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard module" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "έχει απαγορευθεί" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "αποβλήθηκε" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "αποβλήθηκε λόγω τερματισμού συστήματος" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "έχει αποβληθεί λόγω αλλαγής υπαγωγής" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "αποβλήθηκε επειδή η αίθουσα αλλάξε γιά μέλη μόνο" #: mod_muc_log:410 msgid "is now known as" msgstr "είναι τώρα γνωστή ως" #: mod_muc_log:370 msgid "joins the room" msgstr "συνδέετε στην αίθουσα" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "εγκαταλείπει την αίθουσα" #: mod_muc_room:3876 msgid "private, " msgstr "ιδιωτικό, " #: mod_muc_room:3975 msgid "the password is" msgstr "ο κωδικός πρόσβασης είναι" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard Αναζήτηση χρηστών" #: ejabberd_web_admin:658 msgid "~s access rule configuration" msgstr "~s διαμόρφωση κανόνα πρόσβασης" #: mod_muc_room:3968 msgid "~s invites you to the room ~s" msgstr "~s σας προσκαλεί στην αίθουσα ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s" #~ msgid "No resource provided" #~ msgstr "Δεν προμηθεύτικε πόρος" #, fuzzy #~ msgid "Server" #~ msgstr "Διακομιστής:" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Η Jabber Ταυτότητα ~s είναι άκυρη" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Άκυρη υπαγωγή: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Άκυρος ρόλο: ~s" #~ msgid "No limit" #~ msgstr "Χωρίς όριο" #~ msgid "Present real Jabber IDs to" #~ msgstr "Παρούσιαση πραγματικών ταυτοτήτων Jabber σε" #~ msgid "moderators only" #~ msgstr "συντονιστές μόνο" #~ msgid "anyone" #~ msgstr "οποιοσδήποτε" #, fuzzy #~ msgid "Moderator" #~ msgstr "συντονιστές μόνο" #~ msgid "nobody" #~ msgstr "κανείς" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Επιτρέψτε στους επισκέπτες να στέλνουν αιτήματα φωνής" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Ελάχιστο χρονικό διάστημα μεταξύ αιτημάτων φωνής (σε δευτερόλεπτα)" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Εξαίρεση από τις ταυτότητες Jabber, ή CAPTCHA πρόκληση" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε την αίθουσα " #~ msgid "Number of occupants" #~ msgstr "Αριθμός συμετεχόντων" #~ msgid "User JID" #~ msgstr "JID Χρήστη" #~ msgid "Grant voice to this person?" #~ msgstr "Παραχώρηση φωνής σε αυτό το άτομο;" #~ msgid "Node ID" #~ msgstr "Ταυτότητα Κόμβου" #~ msgid "Subscriber Address" #~ msgstr "Διεύθυνση Συνδρομητή" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "" #~ "Επιτρέπετε σε αυτή την Jabber Ταυτότητα να εγγραφεί σε αυτό τον κόμβο " #~ "Δημοσίευσης-Εγγραφής;" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Κοινοποιήσεις με την παράδοση φορτίων" #~ msgid "Deliver event notifications" #~ msgstr "Κοινοποιήσεις παράδοσης" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Ειδοποιηση στους συνδρομητές όταν αλλάζει η διαμόρφωση κόμβου" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Ειδοποιηση στους συνδρομητές όταν ο κόμβος διαγράφεται" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "" #~ "Ειδοποιηση στους συνδρομητές όταν αφαίρούντε στοιχεία από τον κόμβο" #~ msgid "Persist items to storage" #~ msgstr "Μονιμη αποθήκευση στοιχείων" #~ msgid "A friendly name for the node" #~ msgstr "Ένα φιλικό όνομα για τον κόμβο" #~ msgid "Max # of items to persist" #~ msgstr "Μέγιστος αριθμός μόνιμων στοιχείων" #~ msgid "Whether to allow subscriptions" #~ msgstr "Εάν επιτρέποντε συνδρομές" #~ msgid "Specify the access model" #~ msgstr "Καθορίστε το μοντέλο πρόσβασης" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Ομάδες Καταλόγου Επαφών μπορούν να εγγραφούν" #~ msgid "Specify the publisher model" #~ msgstr "Καθορίστε το μοντέλο εκδότη" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Διαγραφή όλων των στοιχείων όταν ο σχετικός εκδότης αποσυνδέεται" #~ msgid "Specify the event message type" #~ msgstr "Καθορίστε τον τύπο μηνύματος συμβάντος" #~ msgid "Max payload size in bytes" #~ msgstr "Μέγιστο μέγεθος φορτίου σε bytes" #~ msgid "When to send the last published item" #~ msgstr "Πότε να αποσταλθεί το τελευταίο στοιχείο που δημοσιεύθηκε" #~ msgid "Only deliver notifications to available users" #~ msgstr "Παράδωση κοινοποιήσεων μόνο σε διαθέσιμους χρήστες" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Οι συλλογές με την οποία είναι ένας κόμβος συνδέεται" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "" #~ "Συμπληρώστε τα πεδία για να αναζητήσετε οποιαδήποτε ταιριάζοντα Jabber " #~ "χρήστη" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Εξερχόμενοι S2S διακομιστές:" #~ msgid "Delete" #~ msgstr "Διαγραφή" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Αυτός ο συμμετέχων αποβλήθηκε από την αίθουσα, επειδή έστειλε ένα μήνυμα " #~ "σφάλματος" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Αυτός ο συμμετέχων αποβλήθηκε από την αίθουσα, επειδή έστειλε ένα μήνυμα " #~ "σφάλματος σε άλλον συμμετέχων" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Αυτός ο συμμετέχων αποβλήθηκε από την αίθουσα, επειδή έστειλε σφάλμα " #~ "παρουσίας " ejabberd-18.01/priv/msgs/id.msg0000644000232200023220000005534613225664356016715 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Akses Konfigurasi"}. {"Access Control List Configuration","Konfigurasi Daftar Akses Pengendalian"}. {"Access Control Lists","Akses Daftar Pengendalian"}. {"Access control lists","Daftar Pengendalian Akses"}. {"Access denied by service policy","Akses ditolak oleh kebijakan layanan"}. {"Access rules","Akses peraturan"}. {"Access Rules","Aturan Akses"}. {"Action on user","Tindakan pada pengguna"}. {"Add Jabber ID","Tambah Jabber ID"}. {"Add New","Tambah Baru"}. {"Add User","Tambah Pengguna"}. {"Administration","Administrasi"}. {"Administration of ","Administrasi"}. {"Administrator privileges required","Hak istimewa Administrator dibutuhkan"}. {"All activity","Semua aktifitas"}. {"Allow users to change the subject","Perbolehkan pengguna untuk mengganti topik"}. {"Allow users to query other users","Perbolehkan pengguna untuk mengetahui pengguna lain"}. {"Allow users to send invites","Perbolehkan pengguna mengirimkan undangan"}. {"Allow users to send private messages","perbolehkan pengguna mengirimkan pesan ke pengguna lain secara pribadi"}. {"Allow visitors to change nickname","Perbolehkan visitor mengganti nama julukan"}. {"Allow visitors to send status text in presence updates","Izinkan pengunjung untuk mengirim teks status terbaru"}. {"All Users","Semua Pengguna"}. {"Announcements","Pengumuman"}. {"A password is required to enter this room","Diperlukan kata sandi untuk masuk ruangan ini"}. {"April","April"}. {"August","Agustus"}. {"Backup","Backup"}. {"Backup Management","Manajemen Backup"}. {"Backup to File at ","Backup ke File pada"}. {"Bad format","Format yang buruk"}. {"Birthday","Hari Lahir"}. {"CAPTCHA web page","CAPTCHA laman web"}. {"Change Password","Ubah Kata Sandi"}. {"Change User Password","Ubah User Password"}. {"Characters not allowed:","Karakter tidak diperbolehkan:"}. {"Chatroom configuration modified","Konfigurasi ruang chat diubah"}. {"Chatroom is created","Ruang chat telah dibuat"}. {"Chatroom is destroyed","Ruang chat dilenyapkan"}. {"Chatroom is started","Ruang chat dimulai"}. {"Chatroom is stopped","Ruang chat dihentikan"}. {"Chatrooms","Ruangan Chat"}. {"Choose a username and password to register with this server","Pilih nama pengguna dan kata sandi untuk mendaftar dengan layanan ini"}. {"Choose modules to stop","Pilih Modul untuk berhenti"}. {"Choose storage type of tables","Pilih jenis penyimpanan tabel"}. {"Choose whether to approve this entity's subscription.","Pilih apakah akan menyetujui hubungan pertemanan ini."}. {"City","Kota"}. {"Commands","Perintah"}. {"Conference room does not exist","Ruang Konferensi tidak ada"}. {"Configuration of room ~s","Pengaturan ruangan ~s"}. {"Configuration","Pengaturan"}. {"Connected Resources:","Sumber Daya Terhubung:"}. {"Connections parameters","Parameter Koneksi"}. {"Country","Negara"}. {"CPU Time:","Waktu CPU:"}. {"Database","Database"}. {"Database Tables Configuration at ","Database Tabel Konfigurasi pada"}. {"December","Desember"}. {"Default users as participants","pengguna pertama kali masuk sebagai participant"}. {"Delete message of the day","Hapus pesan harian"}. {"Delete message of the day on all hosts","Hapus pesan harian pada semua host"}. {"Delete Selected","Hapus Yang Terpilih"}. {"Delete User","Hapus Pengguna"}. {"Description:","Keterangan:"}. {"Disc only copy","Hanya salinan dari disc"}. {"Displayed Groups:","Tampilkan Grup:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Jangan memberitahukan kata sandi Anda ke siapapun, bahkan para administrator dari layanan Jabber."}. {"Dump Backup to Text File at ","Dump Backup ke File Teks di"}. {"Dump to Text File","Dump menjadi File Teks"}. {"Edit Properties","Ganti Properti"}. {"ejabberd IRC module","ejabberd IRC modul"}. {"ejabberd MUC module","ejabberd MUC Module"}. {"ejabberd Publish-Subscribe module","Modul ejabberd Setujui-Pertemanan"}. {"ejabberd SOCKS5 Bytestreams module","modul ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","Modul ejabberd vCard"}. {"ejabberd Web Admin","Admin Web ejabberd"}. {"Elements","Elemen-elemen"}. {"Email","Email"}. {"Enable logging","Aktifkan catatan"}. {"Encoding for server ~b","Pengkodean untuk layanan ~b"}. {"End User Session","Akhir Sesi Pengguna"}. {"Enter list of {Module, [Options]}","Masukkan daftar {Modul, [Options]}"}. {"Enter nickname you want to register","Masukkan nama julukan Anda jika ingin mendaftar"}. {"Enter path to backup file","Masukkan path untuk file cadangan"}. {"Enter path to jabberd14 spool dir","Masukkan path ke direktori spool jabberd14"}. {"Enter path to jabberd14 spool file","Masukkan path ke file jabberd14 spool"}. {"Enter path to text file","Masukkan path ke file teks"}. {"Enter the text you see","Masukkan teks yang Anda lihat"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Masukkan username dan pengkodean yang ingin Anda gunakan untuk menghubungkan ke layanan IRC. Tekan 'Selanjutnya' untuk mendapatkan lagi formulir kemudian Tekan 'Lengkap' untuk menyimpan pengaturan."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Masukkan username, pengkodean, port dan sandi yang ingin Anda gunakan untuk menghubungkan ke layanan IRC"}. {"Erlang Jabber Server","Layanan Erlang Jabber"}. {"Error","Kesalahan"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Contoh: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Ekspor data dari semua pengguna pada layanan ke berkas PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Ekspor data pengguna pada sebuah host ke berkas PIEFXIS (XEP-0227):"}. {"Family Name","Nama Keluarga (marga)"}. {"February","Februari"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Isi formulir untuk pencarian pengguna Jabber yang cocok (Tambahkan * ke mengakhiri pengisian untuk menyamakan kata)"}. {"Friday","Jumat"}. {"From","Dari"}. {"From ~s","Dari ~s"}. {"Full Name","Nama Lengkap"}. {"Get Number of Online Users","Dapatkan Jumlah User Yang Online"}. {"Get Number of Registered Users","Dapatkan Jumlah Pengguna Yang Terdaftar"}. {"Get User Last Login Time","Dapatkan Waktu Login Terakhir Pengguna "}. {"Get User Password","Dapatkan User Password"}. {"Get User Statistics","Dapatkan Statistik Pengguna"}. {"Group ","Grup"}. {"Groups","Grup"}. {"has been banned","telah dibanned"}. {"has been kicked because of an affiliation change","telah dikick karena perubahan afiliasi"}. {"has been kicked because of a system shutdown","telah dikick karena sistem shutdown"}. {"has been kicked because the room has been changed to members-only","telah dikick karena ruangan telah diubah menjadi hanya untuk member"}. {"has been kicked","telah dikick"}. {" has set the subject to: ","telah menetapkan topik yaitu:"}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Jika Anda tidak melihat gambar CAPTCHA disini, silahkan kunjungi halaman web."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Jika Anda ingin menentukan port yang berbeda, sandi, pengkodean untuk layanan IRC, isi daftar ini dengan nilai-nilai dalam format '{\"server irc \", \"encoding \", port, \"sandi \"}'. Secara default ini menggunakan layanan \"~s \" pengkodean, port ~p, kata sandi kosong."}. {"Import Directory","Impor Direktori"}. {"Import File","Impor File"}. {"Import user data from jabberd14 spool file:","Impor data pengguna dari sekumpulan berkas jabberd14:"}. {"Import User from File at ","Impor Pengguna dari File pada"}. {"Import users data from a PIEFXIS file (XEP-0227):","impor data-data pengguna dari sebuah PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Импорт пользовательских данных из буферной директории jabberd14:"}. {"Import Users from Dir at ","Impor Pengguna dari Dir di"}. {"Import Users From jabberd14 Spool Files","Impor Pengguna Dari jabberd14 Spool File"}. {"Improper message type","Jenis pesan yang tidak benar"}. {"Incorrect password","Kata sandi salah"}. {"IP addresses","Alamat IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Channel IRC (tidak perlu menempatkan # sebelumnya)"}. {"IRC server","Layanan IRC"}. {"IRC settings","Pengaturan IRC"}. {"IRC Transport","IRC Transport"}. {"IRC username","Nama Pengguna IRC"}. {"IRC Username","Nama Pengguna IRC"}. {"is now known as","sekarang dikenal sebagai"}. {"It is not allowed to send private messages","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi"}. {"It is not allowed to send private messages of type \"groupchat\"","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi jenis \"groupchat \""}. {"It is not allowed to send private messages to the conference","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi ke konferensi"}. {"Jabber Account Registration","Pendaftaran Akun Jabber"}. {"Jabber ID","Jabber ID"}. {"January","Januari"}. {"Join IRC channel","Gabung channel IRC"}. {"joins the room","bergabung ke ruangan"}. {"Join the IRC channel here.","Gabung ke channel IRC disini"}. {"Join the IRC channel in this Jabber ID: ~s","Gabung ke channel IRC dengan Jabber ID: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Aktifitas Terakhir"}. {"Last login","Terakhir Login"}. {"Last month","Akhir bulan"}. {"Last year","Akhir tahun"}. {"leaves the room","meninggalkan ruangan"}. {"Listened Ports at ","Mendeteksi Port-port di"}. {"Listened Ports","Port Terdeteksi"}. {"List of modules to start","Daftar modul untuk memulai"}. {"Low level update script","Perbaruan naskah tingkat rendah"}. {"Make participants list public","Buat daftar participant diketahui oleh public"}. {"Make room CAPTCHA protected","Buat ruangan dilindungi dengan CAPTCHA"}. {"Make room members-only","Buat ruangan hanya untuk member saja"}. {"Make room moderated","Buat ruangan hanya untuk moderator saja"}. {"Make room password protected","Buat ruangan yang dilindungi dengan kata sandi"}. {"Make room persistent","Buat ruangan menjadi permanent"}. {"Make room public searchable","Buat ruangan dapat dicari"}. {"March","Maret"}. {"Maximum Number of Occupants","Maksimum Jumlah Penghuni"}. {"May","Mei"}. {"Members:","Anggota:"}. {"Membership is required to enter this room","Hanya Member yang dapat masuk ruangan ini"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Hafalkan kata sandi Anda, atau dicatat dan letakkan di tempat yang aman. Didalam Jabber tidak ada cara otomatis untuk mendapatkan kembali password Anda jika Anda lupa."}. {"Memory","Memori"}. {"Message body","Isi Pesan"}. {"Middle Name","Nama Tengah"}. {"Moderator privileges required","Hak istimewa moderator dibutuhkan"}. {"Modified modules","Modifikasi modul-modul"}. {"Module","Modul"}. {"Modules","Modul"}. {"Monday","Senin"}. {"Name:","Nama:"}. {"Name","Nama"}. {"Never","Tidak Pernah"}. {"New Password:","Password Baru:"}. {"Nickname","Nama Julukan"}. {"Nickname Registration at ","Pendaftaran Julukan pada"}. {"Nickname ~s does not exist in the room","Nama Julukan ~s tidak berada di dalam ruangan"}. {"No body provided for announce message","Tidak ada isi pesan yang disediakan untuk mengirimkan pesan"}. {"No Data","Tidak Ada Data"}. {"Node not found","Node tidak ditemukan"}. {"Nodes","Node-node"}. {"None","Tak satupun"}. {"Not Found","Tidak Ditemukan"}. {"November","Nopember"}. {"Number of online users","Jumlah pengguna online"}. {"Number of registered users","Jumlah pengguna terdaftar"}. {"October","Oktober"}. {"Offline Messages:","Pesan Offline:"}. {"Offline Messages","Pesan Offline"}. {"OK","YA"}. {"Old Password:","Password Lama:"}. {"Online","Online"}. {"Online Users:","Pengguna Online:"}. {"Online Users","Pengguna Yang Online"}. {"Only moderators and participants are allowed to change the subject in this room","Hanya moderator dan peserta yang diizinkan untuk mengganti topik pembicaraan di ruangan ini"}. {"Only moderators are allowed to change the subject in this room","Hanya moderator yang diperbolehkan untuk mengubah topik dalam ruangan ini"}. {"Only occupants are allowed to send messages to the conference","Hanya penghuni yang diizinkan untuk mengirim pesan ke konferensi"}. {"Only occupants are allowed to send queries to the conference","Hanya penghuni diizinkan untuk mengirim permintaan ke konferensi"}. {"Only service administrators are allowed to send service messages","Layanan hanya diperuntukan kepada administrator yang diizinkan untuk mengirim layanan pesan"}. {"Options","Pilihan-pilihan"}. {"Organization Name","Nama Organisasi"}. {"Organization Unit","Unit Organisasi"}. {"Outgoing s2s Connections","Koneksi Keluar s2s"}. {"Outgoing s2s Connections:","Koneksi s2s yang keluar:"}. {"Owner privileges required","Hak istimewa owner dibutuhkan"}. {"Packet","Paket"}. {"Password ~b","Kata Sandi ~b"}. {"Password:","Kata Sandi:"}. {"Password","Sandi"}. {"Password Verification:","Verifikasi Kata Sandi:"}. {"Password Verification","Verifikasi Sandi"}. {"Path to Dir","Jalur ke Dir"}. {"Path to File","Jalur ke File"}. {"Pending","Tertunda"}. {"Period: ","Periode:"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Harap dicatat bahwa pilihan ini hanya akan membuat cadangan builtin Mnesia database. Jika Anda menggunakan modul ODBC, anda juga perlu untuk membuat cadangan database SQL Anda secara terpisah."}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","pribadi, "}. {"Protocol","Protocol"}. {"Publish-Subscribe","Setujui-Pertemanan"}. {"PubSub subscriber request","Permintaan pertemanan PubSub"}. {"Queries to the conference members are not allowed in this room","Permintaan untuk para anggota konferensi tidak diperbolehkan di ruangan ini"}. {"RAM and disc copy","RAM dan disc salinan"}. {"RAM copy","Salinan RAM"}. {"Raw","mentah"}. {"Really delete message of the day?","Benar-benar ingin menghapus pesan harian?"}. {"Recipient is not in the conference room","Penerima tidak berada di ruangan konferensi"}. {"Register a Jabber account","Daftarkan sebuah akun jabber"}. {"Registered Users:","Pengguna Terdaftar:"}. {"Registered Users","Pengguna Terdaftar"}. {"Register","Mendaftar"}. {"Registration in mod_irc for ","Pendaftaran di mod_irc untuk"}. {"Remote copy","Salinan Remote"}. {"Remove All Offline Messages","Hapus Semua Pesan Offline"}. {"Remove","Menghapus"}. {"Remove User","Hapus Pengguna"}. {"Replaced by new connection","Diganti dengan koneksi baru"}. {"Resources","Sumber daya"}. {"Restart","Jalankan Ulang"}. {"Restart Service","Restart Layanan"}. {"Restore Backup from File at ","Kembalikan Backup dari File pada"}. {"Restore binary backup after next ejabberd restart (requires less memory):","Mengembalikan cadangan yang berpasanagn setelah ejabberd berikutnya dijalankan ulang (memerlukan memori lebih sedikit):"}. {"Restore binary backup immediately:","Segera mengembalikan cadangan yang berpasangan:"}. {"Restore","Mengembalikan"}. {"Restore plain text backup immediately:","Segera mengembalikan cadangan teks biasa:"}. {"Room Configuration","Konfigurasi Ruangan"}. {"Room creation is denied by service policy","Pembuatan Ruangan ditolak oleh kebijakan layanan"}. {"Room description","Keterangan ruangan"}. {"Room Occupants","Penghuni Ruangan"}. {"Room title","Nama Ruangan"}. {"Roster","Kontak"}. {"Roster of ","Kontak dari"}. {"Roster size","Ukuran Daftar Kontak"}. {"RPC Call Error","Panggilan Kesalahan RPC"}. {"Running Nodes","Menjalankan Node"}. {"~s access rule configuration","~s aturan akses konfigurasi"}. {"Saturday","Sabtu"}. {"Script check","Periksa naskah"}. {"Search Results for ","Hasil Pencarian untuk"}. {"Search users in ","Pencarian pengguna dalam"}. {"Send announcement to all online users","Kirim pengumuman untuk semua pengguna yang online"}. {"Send announcement to all online users on all hosts","Kirim pengumuman untuk semua pengguna yang online pada semua host"}. {"Send announcement to all users","Kirim pengumuman untuk semua pengguna"}. {"Send announcement to all users on all hosts","Kirim pengumuman untuk semua pengguna pada semua host"}. {"September","September"}. {"Server ~b","Layanan ~b"}. {"Server:","Layanan:"}. {"Set message of the day and send to online users","Mengatur pesan harian dan mengirimkan ke pengguna yang online"}. {"Set message of the day on all hosts and send to online users","Mengatur pesan harian pada semua host dan kirimkan ke pengguna yang online"}. {"Shared Roster Groups","Berbagi grup kontak"}. {"Show Integral Table","Tampilkan Tabel Terpisah"}. {"Show Ordinary Table","Tampilkan Tabel Normal"}. {"Shut Down Service","Shut Down Layanan"}. {"~s invites you to the room ~s","~s mengundang anda ke ruangan ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Beberapa klien Jabber dapat menyimpan password di komputer Anda. Gunakan fitur itu hanya jika Anda mempercayai komputer Anda aman."}. {"~s's Offline Messages Queue","Antrian Pesan Offline ~s"}. {"Start Modules at ","Mulai Modul pada"}. {"Start Modules","Memulai Modul"}. {"Start","Mulai"}. {"Statistics of ~p","statistik dari ~p"}. {"Statistics","Statistik"}. {"Stop","Hentikan"}. {"Stop Modules at ","Hentikan Modul pada"}. {"Stop Modules","Hentikan Modul"}. {"Stopped Nodes","Menghentikan node"}. {"Storage Type","Jenis Penyimpanan"}. {"Store binary backup:","Penyimpanan cadangan yang berpasangan:"}. {"Store plain text backup:","Simpan cadangan teks biasa:"}. {"Subject","Subyek"}. {"Submit","Serahkan"}. {"Submitted","Ulangi masukan"}. {"Subscription","Berlangganan"}. {"Sunday","Minggu"}. {"That nickname is already in use by another occupant","Julukan itu sudah digunakan oleh penghuni lain"}. {"That nickname is registered by another person","Julukan tersebut telah didaftarkan oleh orang lain"}. {"The CAPTCHA is valid.","Captcha ini benar."}. {"The CAPTCHA verification has failed","Verifikasi CAPTCHA telah gagal"}. {"the password is","kata sandi yaitu:"}. {"The password is too weak","Kata sandi terlalu lemah"}. {"The password of your Jabber account was successfully changed.","Kata sandi pada akun Jabber Anda telah berhasil diubah."}. {"There was an error changing the password: ","Ada kesalahan dalam mengubah password:"}. {"There was an error creating the account: ","Ada kesalahan saat membuat akun:"}. {"There was an error deleting the account: ","Ada kesalahan saat menghapus akun:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Pada bagian ini huruf besar dan kecil tidak dibedakan: Misalnya macbeth adalah sama dengan MacBeth juga Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Halaman ini memungkinkan untuk membuat akun Jabber di layanan Jabber ini. JID Anda (Jabber Pengenal) akan berbentuk: namapengguna@layanan. Harap baca dengan seksama petunjuk-petunjuk untuk mengisi kolom dengan benar."}. {"This page allows to unregister a Jabber account in this Jabber server.","Pada bagian ini memungkinkan Anda untuk membatalkan pendaftaran akun Jabber pada layanan Jabber ini."}. {"This room is not anonymous","Ruangan ini tidak dikenal"}. {"Thursday","Kamis"}. {"Time delay","Waktu tunda"}. {"Time","Waktu"}. {"To","Kepada"}. {"To ~s","Kepada ~s"}. {"Traffic rate limit is exceeded","Lalu lintas melebihi batas"}. {"Transactions Aborted:","Transaksi yang dibatalkan:"}. {"Transactions Committed:","Transaksi yang dilakukan:"}. {"Transactions Logged:","Transaksi yang ditempuh:"}. {"Transactions Restarted:","Transaksi yang dijalankan ulang:"}. {"Tuesday","Selasa"}. {"Unable to generate a CAPTCHA","Tidak dapat menghasilkan CAPTCHA"}. {"Unauthorized","Ditolak"}. {"Unregister a Jabber account","Nonaktifkan akun jabber"}. {"Unregister","Nonaktifkan"}. {"Update","Memperbarui"}. {"Update message of the day (don't send)","Rubah pesan harian (tidak dikirim)"}. {"Update message of the day on all hosts (don't send)","Rubah pesan harian pada semua host (tidak dikirim)"}. {"Update plan","Rencana Perubahan"}. {"Update script","Perbarui naskah"}. {"Uptime:","Sampai saat:"}. {"Use of STARTTLS required","Penggunaan STARTTLS diperlukan"}. {"User Management","Manajemen Pengguna"}. {"Username:","Nama Pengguna:"}. {"User","Pengguna"}. {"Users are not allowed to register accounts so quickly","Pengguna tidak diperkenankan untuk mendaftar akun begitu cepat"}. {"Users Last Activity","Aktifitas terakhir para pengguna"}. {"Users","Pengguna"}. {"Validate","Mengesahkan"}. {"vCard User Search","vCard Pencarian Pengguna"}. {"Virtual Hosts","Virtual Hosts"}. {"Visitors are not allowed to change their nicknames in this room","Visitor tidak diperbolehkan untuk mengubah nama julukan di ruangan ini"}. {"Visitors are not allowed to send messages to all occupants","Visitor tidak diperbolehkan untuk mengirim pesan ke semua penghuni"}. {"Wednesday","Rabu"}. {"You can later change your password using a Jabber client.","Anda dapat mengubah kata sandi anda dilain waktu dengan menggunakan klien Jabber."}. {"You have been banned from this room","Anda telah diblokir dari ruangan ini"}. {"You must fill in field \"Nickname\" in the form","Anda harus mengisi kolom \"Julukan\" dalam formulir"}. {"You need a client that supports x:data and CAPTCHA to register","Anda memerlukan klien yang mendukung x:data dan CAPTCHA untuk mendaftar"}. {"You need a client that supports x:data to register the nickname","Anda memerlukan klien yang mendukung x:data untuk mendaftar julukan"}. {"You need an x:data capable client to configure mod_irc settings","Anda memerlukan x:data klien untuk mampu mengkonfigurasi pengaturan mod_irc"}. {"You need an x:data capable client to search","Anda memerlukan x:data klien untuk melakukan pencarian"}. {"Your active privacy list has denied the routing of this stanza.","Daftar privasi aktif Anda telah menolak routing ztanza ini"}. {"Your contact offline message queue is full. The message has been discarded.","Kontak offline Anda pada antrian pesan sudah penuh. Pesan telah dibuang."}. {"Your Jabber account was successfully created.","Jabber akun Anda telah sukses dibuat"}. {"Your Jabber account was successfully deleted.","Jabber akun Anda berhasil dihapus."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Pesan Anda untuk ~s sedang diblokir. Untuk membuka blokir tersebut, kunjungi ~s"}. ejabberd-18.01/priv/msgs/tr.msg0000644000232200023220000006026413225664356016741 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Erişim Ayarları"}. {"Access Control List Configuration","Erişim Kontrol Listelerinin Ayarlanması (ACL)"}. {"Access control lists","Erişim kontrol listeleri (ACL)"}. {"Access Control Lists","Erişim Kontrol Listeleri (ACL)"}. {"Access denied by service policy","Servis politikası gereği erişim engellendi"}. {"Access rules","Erişim kuralları"}. {"Access Rules","Erişim Kuralları"}. {"Action on user","Kullanıcıya uygulanacak eylem"}. {"Add Jabber ID","Jabber ID'si Ekle"}. {"Add New","Yeni Ekle"}. {"Add User","Kullanıcı Ekle"}. {"Administration of ","Yönetim : "}. {"Administration","Yönetim"}. {"Administrator privileges required","Yönetim yetkileri gerekli"}. {"All activity","Tüm aktivite"}. {"Allow users to change the subject","Kullanıcıların konu değiştirmesine izin ver"}. {"Allow users to query other users","Kullanıcıların diğer kullanıcıları sorgulamalarına izin ver"}. {"Allow users to send invites","Kullanıcıların davetiye göndermelerine izin ver"}. {"Allow users to send private messages","Kullanıcıların özel mesaj göndermelerine izin ver"}. {"Allow visitors to change nickname","Ziyaretçilerin takma isim değiştirmelerine izin ver"}. {"Allow visitors to send private messages to","Ziyaretçilerin özel mesaj göndermelerine izin ver"}. {"Allow visitors to send status text in presence updates","Ziyaretçilerin varlık (presence) güncellemelerinde durum metni göndermelerine izin ver"}. {"All Users","Tüm Kullanıcılar"}. {"Announcements","Duyurular"}. {"A password is required to enter this room","Bu odaya girmek için parola gerekiyor"}. {"April","Nisan"}. {"August","Ağustos"}. {"Backup Management","Yedek Yönetimi"}. {"Backup to File at ","Dosyaya Yedekle : "}. {"Backup","Yedekle"}. {"Bad format","Kötü biçem"}. {"Birthday","Doğumgünü"}. {"CAPTCHA web page","CAPTCHA web sayfası"}. {"Change Password","Parola Değiştir"}. {"Change User Password","Kullanıcı Parolasını Değiştir"}. {"Characters not allowed:","İzin verilmeyen karakterler:"}. {"Chatroom configuration modified","Sohbet odası ayarı değiştirildi"}. {"Chatroom is created","Sohbet odası oluşturuldu"}. {"Chatroom is destroyed","Sohbet odası kaldırıldı"}. {"Chatroom is started","Sohbet odası başlatıldı"}. {"Chatroom is stopped","Sohbet odası durduruldu"}. {"Chatrooms","Sohbet Odaları"}. {"Choose a username and password to register with this server","Bu sunucuya kayıt olmak için bir kullanıcı ismi ve parola seçiniz"}. {"Choose modules to stop","Durdurulacak modülleri seçiniz"}. {"Choose storage type of tables","Tabloların veri depolama tipini seçiniz"}. {"Choose whether to approve this entity's subscription.","Bu varlığın üyeliğini onaylayıp onaylamamayı seçiniz."}. {"City","İl"}. {"Commands","Komutlar"}. {"Conference room does not exist","Konferans odası bulunamadı"}. {"Configuration","Ayarlar"}. {"Configuration of room ~s","~s odasının ayarları"}. {"Connected Resources:","Bağlı Kaynaklar:"}. {"Connections parameters","Bağlantı parametreleri"}. {"Country","Ülke"}. {"CPU Time:","İşlemci Zamanı:"}. {"Database Tables Configuration at ","Veritabanı Tablo Ayarları : "}. {"Database","Veritabanı"}. {"December","Aralık"}. {"Default users as participants","Kullanıcılar öntanımlı olarak katılımcı olsun"}. {"Delete message of the day","Günün mesajını sil"}. {"Delete message of the day on all hosts","Tüm sunuculardaki günün mesajını sil"}. {"Delete Selected","Seçilenleri Sil"}. {"Delete User","Kullanıcıyı Sil"}. {"Description:","Tanım:"}. {"Disc only copy","Sadece disk kopyala"}. {"Displayed Groups:","Gösterilen Gruplar:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Parolanızı kimseye söylemeyin, Jabber sunucusunun yöneticilerine bile."}. {"Dump Backup to Text File at ","Metin Dosyasına Döküm Alarak Yedekle : "}. {"Dump to Text File","Metin Dosyasına Döküm Al"}. {"Edit Properties","Özellikleri Düzenle"}. {"Either approve or decline the voice request.","Ses isteğini kabul edin ya da reddedin"}. {"ejabberd IRC module","ejabberd IRC modülü"}. {"ejabberd MUC module","ejabberd MUC modülü"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modülü"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modülü"}. {"ejabberd vCard module","ejabberd vCard modülü"}. {"ejabberd Web Admin","ejabberd Web Yöneticisi"}. {"Elements","Elementler"}. {"Email","E-posta"}. {"Enable logging","Kayıt tutma özelliğini aç"}. {"Encoding for server ~b","Sunucu için kodlama ~b"}. {"End User Session","Kullanıcı Oturumunu Kapat"}. {"Enter list of {Module, [Options]}","{Module, [Options]} listesi giriniz"}. {"Enter nickname you want to register","Kaydettirmek istediğiniz takma ismi giriniz"}. {"Enter path to backup file","Yedek dosyasının yolunu giriniz"}. {"Enter path to jabberd14 spool dir","jabberd14 spool dosyası için yol giriniz"}. {"Enter path to jabberd14 spool file","jabberd14 spool dosyası için yol giriniz"}. {"Enter path to text file","Metin dosyasının yolunu giriniz"}. {"Enter the text you see","Gördüğünüz metni giriniz"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı isimleri ve kodlamaları giriniz. 'İleri' tuşuna basınca karşınıza dolduracak daha fazla alan çıkacak. 'Tamamla' tuşuna basarak ayarları kaydedin."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı ismi, kodlamalar, kapılar (portlar) ve parolaları giriniz"}. {"Erlang Jabber Server","Erlang Jabber Sunucusu"}. {"Error","Hata"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Örnek: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"gizli\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}], {\"irc.sometestserver.net\", \"utf-8\"}]"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Sunucudaki tüm kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa aktar:"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Bir sunucudaki kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa aktar:"}. {"Failed to extract JID from your voice request approval","Ses isteği onayınızdan JID bilginize ulaşılamadı"}. {"Family Name","Soyisim"}. {"February","Şubat"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Eşleşen jabber kullanıcılarını aramak için formu doldurunuz (Alt dizgi eşlemek için alanın sonuna * ekleyin)"}. {"Friday","Cuma"}. {"From","Kimden"}. {"From ~s","Kimden ~s"}. {"Full Name","Tam İsim"}. {"Get Number of Online Users","Bağlı Kullanıcı Sayısını Al"}. {"Get Number of Registered Users","Kayıtlı Kullanıcı Sayısını Al"}. {"Get User Last Login Time","Kullanıcı Son Giriş Zamanınlarını Al"}. {"Get User Password","Kullanıcı Parolasını Al"}. {"Get User Statistics","Kullanıcı İstatistiklerini Al"}. {"Group ","Group "}. {"Groups","Gruplar"}. {"has been banned","odaya girmesi yasaklandı"}. {"has been kicked because of an affiliation change","ilişki değişikliğinden dolayı atıldı"}. {"has been kicked because of a system shutdown","sistem kapandığından dolayı atıldı"}. {"has been kicked because the room has been changed to members-only","oda üyelere-özel hale getirildiğinden dolayı atıldı"}. {"has been kicked","odadan atıldı"}. {" has set the subject to: "," konuyu değiştirdi: "}. {"Host","Sunucu"}. {"If you don't see the CAPTCHA image here, visit the web page.","Eğer burada CAPTCHA resmini göremiyorsanız, web sayfasını ziyaret edin."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","IRC sunucuları için farklı kapılar (portlar), parolalar, kodlamalar belirtmek istiyorsanız, '{\"irc sunucusu\", \"kodlama\",\"kapı\",\"parola\"}' biçeminde değerlerle bu listeyi doldurunuz. Öntanımlı olarak bu servis \"~s\" kodlamasını, ~p \"kapısını\", \"boş\" parolasını kullanıyor."}. {"Import Directory","Dizini İçe Aktar"}. {"Import File","Dosyayı İçe Aktar"}. {"Import user data from jabberd14 spool file:","Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçe Aktar:"}. {"Import User from File at ","Dosyadan Kullanıcıları İçe Aktar : "}. {"Import users data from a PIEFXIS file (XEP-0227):","Kullanıcıları bir PIEFXIS dosyasından (XEP-0227) içe aktar:"}. {"Import users data from jabberd14 spool directory:","Jabberd 1.4 Spool Dizininden Kullanıcıları İçe Aktar:"}. {"Import Users from Dir at ","Dizinden Kullanıcıları İçe Aktar : "}. {"Import Users From jabberd14 Spool Files","Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçeri Aktar"}. {"Improper message type","Uygunsuz mesaj tipi"}. {"Incorrect password","Yanlış parola"}. {"IP addresses","IP adresleri"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC kanalı (ilk # işaretini koymayın)"}. {"IRC server","IRC sunucusu"}. {"IRC settings","IRC ayarları"}. {"IRC Transport","IRC Nakli (Transport)"}. {"IRC username","IRC kullanıcı ismi"}. {"IRC Username","IRC Kullanıcı İsmi"}. {"is now known as","isim değiştirdi :"}. {"It is not allowed to send private messages of type \"groupchat\"","\"groupchat\" tipinde özel mesajlar gönderilmesine izin verilmiyor"}. {"It is not allowed to send private messages","Özel mesaj gönderilmesine izin verilmiyor"}. {"It is not allowed to send private messages to the conference","Konferansa özel mesajlar gönderilmesine izin verilmiyor"}. {"Jabber Account Registration","Jabber Hesap Kaydı"}. {"Jabber ID","Jabber ID"}. {"January","Ocak"}. {"Join IRC channel","IRC kanalına katıl"}. {"joins the room","odaya katıldı"}. {"Join the IRC channel here.","Buradaki IRC kanalına katıl."}. {"Join the IRC channel in this Jabber ID: ~s","IRC kanalına bu Jabber ID'si ile katıl: ~s"}. {"July","Temmuz"}. {"June","Haziran"}. {"Last Activity","Son Aktivite"}. {"Last login","Son giriş"}. {"Last month","Geçen ay"}. {"Last year","Geçen yıl"}. {"leaves the room","odadan ayrıldı"}. {"Listened Ports at ","Dinlenen Kapılar (Portlar) : "}. {"Listened Ports","Dinlenen Kapılar (Portlar)"}. {"List of modules to start","Başlatılacak modüllerin listesi"}. {"Low level update script","Düşük seviye güncelleme betiği"}. {"Make participants list public","Katılımcı listesini herkese açık hale getir"}. {"Make room CAPTCHA protected","Odayı insan doğrulaması (captcha) korumalı hale getir"}. {"Make room members-only","Odayı sadece üyelere açık hale getir"}. {"Make room moderated","Odayı moderasyonlu hale getir"}. {"Make room password protected","Odayı parola korumalı hale getir"}. {"Make room persistent","Odayı kalıcı hale getir"}. {"Make room public searchable","Odayı herkes tarafından aranabilir hale getir"}. {"March","Mart"}. {"Maximum Number of Occupants","Odada En Fazla Bulunabilecek Kişi Sayısı"}. {"May","Mayıs"}. {"Membership is required to enter this room","Bu odaya girmek için üyelik gerekiyor"}. {"Members:","Üyeler:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Parolanızı ezberleyin ya da bir kağıda yazarak güvenli bir yerde saklayın. Jabber'da parolanızı unutursanız, otomatik kurtarmak için bir yöntem bulunmuyor."}. {"Memory","Bellek"}. {"Message body","Mesajın gövdesi"}. {"Middle Name","Ortanca İsim"}. {"Moderator privileges required","Moderatör yetkileri gerekli"}. {"Modified modules","Değişen modüller"}. {"Module","Modül"}. {"Modules","Modüller"}. {"Monday","Pazartesi"}. {"Name:","İsim:"}. {"Name","İsim"}. {"Never","Asla"}. {"New Password:","Yeni Parola:"}. {"Nickname Registration at ","Takma İsim Kaydı : "}. {"Nickname ~s does not exist in the room","~s takma ismi odada yok"}. {"Nickname","Takma isim"}. {"No body provided for announce message","Duyuru mesajının gövdesi yok"}. {"No Data","Veri Yok"}. {"Node not found","Düğüm bulunamadı"}. {"Nodes","Düğümler"}. {"None","Hiçbiri"}. {"Not Found","Bulunamadı"}. {"November","Kasım"}. {"Number of online users","Bağlı kullanıcı sayısı"}. {"Number of registered users","Kayıtlı kullanıcı sayısı"}. {"October","Ekim"}. {"Offline Messages:","Çevirim-dışı Mesajlar:"}. {"Offline Messages","Çevirim-dışı Mesajlar"}. {"OK","Tamam"}. {"Old Password:","Eski Parola:"}. {"Online","Bağlı"}. {"Online Users:","Bağlı Kullanıcılar:"}. {"Online Users","Bağlı Kullanıcılar"}. {"Only moderators and participants are allowed to change the subject in this room","Sadece moderatörlerin ve katılımcıların bu odanın konusunu değiştirmesine izin veriliyor"}. {"Only moderators are allowed to change the subject in this room","Sadece moderatörlerin bu odanın konusunu değiştirmesine izin veriliyor"}. {"Only moderators can approve voice requests","Yalnız moderatörler ses isteklerini onaylayabilir"}. {"Only occupants are allowed to send messages to the conference","Sadece oda sakinlerinin konferansa mesaj göndermesine izin veriliyor"}. {"Only occupants are allowed to send queries to the conference","Sadece oda sakinlerinin konferansa sorgu göndermesine izin veriliyor"}. {"Only service administrators are allowed to send service messages","Sadece servis yöneticileri servis mesajı gönderebilirler"}. {"Options","Seçenekler"}. {"Organization Name","Kurum İsmi"}. {"Organization Unit","Kurumun İlgili Birimi"}. {"Outgoing s2s Connections:","Giden s2s Bağlantıları:"}. {"Outgoing s2s Connections","Giden s2s Bağlantıları"}. {"Owner privileges required","Sahip yetkileri gerekli"}. {"Packet","Paket"}. {"Password ~b","Parola ~b"}. {"Password:","Parola:"}. {"Password","Parola"}. {"Password Verification:","Parola Doğrulaması:"}. {"Password Verification","Parola Doğrulaması"}. {"Path to Dir","Dizinin Yolu"}. {"Path to File","Dosyanın Yolu"}. {"Pending","Sıra Bekleyen"}. {"Period: ","Periyot:"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Bu seçeneklerin sadece gömülü Mnesia veritabanını yedekleyeceğine dikkat edin. Eğer ODBC modülünü kullanıyorsanız, SQL veritabanınızı da ayrıca yedeklemeniz gerekiyor."}. {"Please, wait for a while before sending new voice request","Lütfen yeni bir ses isteği göndermeden önce biraz bekleyin"}. {"Pong","Pong"}. {"Port ~b","Kapı (Port) ~b"}. {"Port","Kapı (Port)"}. {"private, ","özel"}. {"Protocol","Protokol"}. {"Publish-Subscribe","Yayınla-Üye Ol"}. {"PubSub subscriber request","PubSub üye isteği"}. {"Queries to the conference members are not allowed in this room","Bu odada konferans üyelerine sorgu yapılmasına izin verilmiyor"}. {"RAM and disc copy","RAM ve disk kopyala"}. {"RAM copy","RAM kopyala"}. {"Raw","Ham"}. {"Really delete message of the day?","Günün mesajını silmek istediğinize emin misiniz?"}. {"Recipient is not in the conference room","Alıcı konferans odasında değil"}. {"Register a Jabber account","Bir Jabber hesabı kaydet"}. {"Registered Users:","Kayıtlı Kullanıcılar:"}. {"Registered Users","Kayıtlı Kullanıcılar"}. {"Register","Kayıt Ol"}. {"Registration in mod_irc for ","mod_irc'ye kayıt : "}. {"Remote copy","Uzak kopyala"}. {"Remove All Offline Messages","Tüm Çevirim-dışı Mesajları Kaldır"}. {"Remove","Kaldır"}. {"Remove User","Kullanıcıyı Kaldır"}. {"Replaced by new connection","Eski bağlantı yenisi ile değiştirildi"}. {"Resources","Kaynaklar"}. {"Restart Service","Servisi Tekrar Başlat"}. {"Restart","Tekrar Başlat"}. {"Restore Backup from File at ","Dosyadaki Yedekten Geri Al : "}. {"Restore binary backup after next ejabberd restart (requires less memory):","ejabberd'nin bir sonraki tekrar başlatılışında ikili yedekten geri al (daha az bellek gerektirir)"}. {"Restore binary backup immediately:","Hemen ikili yedekten geri al:"}. {"Restore plain text backup immediately:","Hemen düz metin yedekten geri al"}. {"Restore","Yedekten Geri Al"}. {"Room Configuration","Oda Ayarları"}. {"Room creation is denied by service policy","Odanın oluşturulması servis politikası gereği reddedildi"}. {"Room description","Oda tanımı"}. {"Room Occupants","Oda Sakini Sayısı"}. {"Room title","Oda başlığı"}. {"Roster","Kontak Listesi"}. {"Roster of ","Kontak Listesi : "}. {"Roster size","İsim listesi boyutu"}. {"RPC Call Error","RPC Çağrı Hatası"}. {"Running Nodes","Çalışan Düğümler"}. {"~s access rule configuration","~s erişim kuralları ayarları"}. {"Saturday","Cumartesi"}. {"Script check","Betik kontrolü"}. {"Search Results for ","Arama sonuçları : "}. {"Search users in ","Kullanıcılarda arama yap : "}. {"Send announcement to all online users","Duyuruyu tüm bağlı kullanıcılara yolla"}. {"Send announcement to all online users on all hosts","Duyuruyu tüm sunuculardaki tüm bağlı kullanıcılara yolla"}. {"Send announcement to all users","Duyuruyu tüm kullanıcılara yolla"}. {"Send announcement to all users on all hosts","Tüm sunuculardaki tüm kullanıcılara duyuru yolla"}. {"September","Eylül"}. {"Server ~b","Sunucu ~b"}. {"Server:","Sunucu:"}. {"Set message of the day and send to online users","Günün mesajını belirle"}. {"Set message of the day on all hosts and send to online users","Tüm sunucularda günün mesajını belirle ve bağlı tüm kullanıcılara gönder"}. {"Shared Roster Groups","Paylaşımlı Kontak Listesi Grupları"}. {"Show Integral Table","Önemli Tabloyu Göster"}. {"Show Ordinary Table","Sıradan Tabloyu Göster"}. {"Shut Down Service","Servisi Kapat"}. {"~s invites you to the room ~s","~s sizi ~s odasına davet ediyor"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Bazı Jabber istemcileri parolanızı bilgisayarınızda saklayabilir. Bu özelliği ancak bilgisayarın güvenli olduğuna güveniyorsanız kullanın."}. {"~s's Offline Messages Queue","~s Kullanıcısının Mesaj Kuyruğu"}. {"Start","Başlat"}. {"Start Modules at ","Modülleri Başlat : "}. {"Start Modules","Modülleri Başlat"}. {"Statistics","İstatistikler"}. {"Statistics of ~p","~p istatistikleri"}. {"Stop","Durdur"}. {"Stop Modules at ","Modülleri Durdur : "}. {"Stop Modules","Modülleri Durdur"}. {"Stopped Nodes","Durdurulmuş Düğümler"}. {"Storage Type","Depolama Tipi"}. {"Store binary backup:","İkili yedeği sakla:"}. {"Store plain text backup:","Düz metin yedeği sakla:"}. {"Subject","Konu"}. {"Submit","Gönder"}. {"Submitted","Gönderilenler"}. {"Subscription","Üyelik"}. {"Sunday","Pazar"}. {"That nickname is already in use by another occupant","Takma isim odanın başka bir sakini tarafından halihazırda kullanımda"}. {"That nickname is registered by another person","O takma isim başka biri tarafından kaydettirilmiş"}. {"The CAPTCHA is valid.","İnsan doğrulaması (captcha) geçerli."}. {"The CAPTCHA verification has failed","CAPTCHA doğrulaması başarısız oldu"}. {"the password is","parola :"}. {"The password is too weak","Parola çok zayıf"}. {"The password of your Jabber account was successfully changed.","Jabber hesabınızın parolası başarıyla değiştirildi."}. {"There was an error changing the password: ","Parolanın değiştirilmesi sırasında bir hata oluştu:"}. {"There was an error creating the account: ","Hesap oluşturulurken bir hata oluştu:"}. {"There was an error deleting the account: ","Hesabın silinmesi sırasında bir hata oluştu:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Burada büyük küçük harfi yapılmaz: macbeth ile MacBeth ve Macbeth aynıdır."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Bu sayfa bu Jabber sunucusunda bir Jabber hesabı oluşturulmasına olanak tanıyor. Sizin JID'iniz (Jabber Tanımlayıcısı) şu biçemde olacaktır: kullanici_adi@sunucu. Lütfen alanları doğru doldurabilmek için yönergeleri dikkatle okuyunuz."}. {"This page allows to unregister a Jabber account in this Jabber server.","Bu sayfa bu Jabber sunucusundan bir Jabber hesabının kaydını silmeye olanak tanıyor."}. {"This room is not anonymous","Bu oda anonim değil"}. {"Thursday","Perşembe"}. {"Time delay","Zaman gecikmesi"}. {"Time","Zaman"}. {"To","Kime"}. {"Too many CAPTCHA requests","Çok fazla CAPTCHA isteği"}. {"To ~s","Kime ~s"}. {"Traffic rate limit is exceeded","Trafik oran sınırı aşıldı"}. {"Transactions Aborted:","İptal Edilen Hareketler (Transactions):"}. {"Transactions Committed:","Tamamlanan Hareketler (Transactions Committed):"}. {"Transactions Logged:","Kaydı Tutulan Hareketler (Transactions):"}. {"Transactions Restarted:","Tekrar Başlatılan Hareketler (Transactions):"}. {"Tuesday","Salı"}. {"Unable to generate a CAPTCHA","İnsan doğrulaması (CAPTCHA) oluşturulamadı"}. {"Unauthorized","Yetkisiz"}. {"Unregister a Jabber account","Bir Jabber hesabı kaydı sil"}. {"Unregister","Kaydı Sil"}. {"Update","GÜncelle"}. {"Update message of the day (don't send)","Günün mesajını güncelle (gönderme)"}. {"Update message of the day on all hosts (don't send)","Tüm sunuculardaki günün mesajını güncelle (gönderme)"}. {"Update plan","Planı güncelle"}. {"Update script","Betiği Güncelle"}. {"Uptime:","Hizmet Süresi:"}. {"Use of STARTTLS required","STARTTLS kullanımı gereklidir"}. {"User","Kullanıcı"}. {"User Management","Kullanıcı Yönetimi"}. {"Username:","Kullanıcı adı:"}. {"Users are not allowed to register accounts so quickly","Kullanıcıların bu kadar hızlı hesap açmalarına izin verilmiyor"}. {"Users","Kullanıcılar"}. {"Users Last Activity","Kullanıcıların Son Aktiviteleri"}. {"Validate","Geçerli"}. {"vCard User Search","vCard Kullanıcı Araması"}. {"Virtual Hosts","Sanal Sunucuları"}. {"Visitors are not allowed to change their nicknames in this room","Bu odada ziyaretçilerin takma isimlerini değiştirmesine izin verilmiyor"}. {"Visitors are not allowed to send messages to all occupants","Ziyaretçilerin odadaki tüm sakinlere mesaj göndermesine izin verilmiyor"}. {"Voice requests are disabled in this conference","Bu konferansta ses istekleri etkisizleştirilmiş durumda."}. {"Voice request","Ses isteği"}. {"Wednesday","Çarşamba"}. {"You can later change your password using a Jabber client.","Parolanızı daha sonra bir Jabber istemci kullanarak değiştirebilirsiniz."}. {"You have been banned from this room","Bu odaya girmeniz yasaklandı"}. {"You must fill in field \"Nickname\" in the form","Formda \"Takma isim\" alanını doldurmanız gerekiyor"}. {"You need a client that supports x:data and CAPTCHA to register","Takma isminizi kaydettirmek için x:data ve CAPTCHA destekleyen bir istemciye gereksinimiz var"}. {"You need a client that supports x:data to register the nickname","Takma isminizi kaydettirmek için x:data destekleyen bir istemciye gereksinimiz var"}. {"You need an x:data capable client to configure mod_irc settings","mod_irc ayarlarını düzenlemek için x:data becerisine sahip bir istemciye gereksinimiz var"}. {"You need an x:data capable client to search","Arama yapabilmek için x:data becerisine sahip bir istemciye gereksinimiz var"}. {"Your active privacy list has denied the routing of this stanza.","Etkin mahremiyet listeniz bu bölümün yönlendirilmesini engelledi."}. {"Your contact offline message queue is full. The message has been discarded.","Çevirim-dışı mesaj kuyruğunuz dolu. Mesajını dikkate alınmadı."}. {"Your Jabber account was successfully created.","Jabber hesabınız başarıyla oluşturuldu."}. {"Your Jabber account was successfully deleted.","Jabber hesabınız başarıyla silindi."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","~s kullanıcısına mesajlarınız engelleniyor. Durumu düzeltmek için ~s adresini ziyaret ediniz."}. ejabberd-18.01/priv/msgs/he.po0000644000232200023220000020740113225664356016534 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: ejabberd 2.1.x\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Isratine Citizen \n" "Language-Team: Rahut \n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Hebrew\n" "X-Poedit-Language: Hebrew (עברית)\n" "X-Generator: Poedit 1.5.4\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Source-Language: en\n" #: mod_muc_log:413 mod_muc_log:588 msgid " has set the subject to: " msgstr " הגדיר/ה את הנושא אל: " # מילת־מעבר #: mod_muc_room:1904 msgid "A password is required to enter this room" msgstr "נדרשת סיסמה כדי להיכנס אל חדר זה" #: ejabberd_oauth:448 msgid "Accept" msgstr "קבל" #: mod_configure:1109 msgid "Access Configuration" msgstr "תצורת גישה" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "תצורת רשימת בקרת גישה" #: ejabberd_web_admin:484 ejabberd_web_admin:523 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "רשימות בקרת גישה" # חוקי #: ejabberd_web_admin:589 ejabberd_web_admin:622 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "כללי גישה" #: mod_configure:1095 msgid "Access control lists" msgstr "רשימות בקרת גישה" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "גישה נדחתה על ידי פוליסת שירות" #: mod_configure:1113 msgid "Access rules" msgstr "כללי גישה" #: mod_configure:1772 msgid "Action on user" msgstr "פעולה על משתמש" #: mod_roster:982 msgid "Add Jabber ID" msgstr "הוסף מזהה Jabber" #: ejabberd_web_admin:1003 mod_shared_roster:825 msgid "Add New" msgstr "הוסף חדש" #: ejabberd_web_admin:1170 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "הוסף משתמש" #: ejabberd_web_admin:413 ejabberd_web_admin:424 msgid "Administration" msgstr "הנהלה" #: mod_configure:1767 msgid "Administration of " msgstr "ניהול של " #: mod_muc_room:2548 msgid "Administrator privileges required" msgstr "נדרשות הרשאות מנהל" #: mod_configure:507 msgid "All Users" msgstr "כל המשתמשים" #: ejabberd_web_admin:736 msgid "All activity" msgstr "כל פעילות" #: mod_muc_log:809 msgid "Allow users to change the subject" msgstr "התר למשתמשים לשנות את הנושא" #: mod_muc_log:815 msgid "Allow users to query other users" msgstr "התר למשתמשים לתשאל משתמשים אחרים" #: mod_muc_log:817 msgid "Allow users to send invites" msgstr "התר למשתמשים לשלוח הזמנות" #: mod_muc_log:811 msgid "Allow users to send private messages" msgstr "התר למשתמשים לשלוח הודעות פרטיות" #: mod_muc_log:820 msgid "Allow visitors to change nickname" msgstr "התר למבקרים לשנות שם כינוי" #: mod_muc_log:813 msgid "Allow visitors to send private messages to" msgstr "התר למבקרים לשלוח הודעות פרטיות אל" #: mod_muc_log:822 msgid "Allow visitors to send status text in presence updates" msgstr "התר למבקרים לשלוח טקסט מצב בתוך עדכוני נוכחות" #: mod_announce:611 msgid "Announcements" msgstr "בשורות" #: mod_muc_log:476 msgid "April" msgstr "אפריל" #: mod_muc_log:480 msgid "August" msgstr "אוגוסט" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "יצירה אוטומטית של צומת אינה מאופשרת" #: ejabberd_web_admin:1593 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "גיבוי" #: mod_configure:584 msgid "Backup Management" msgstr "ניהול גיבוי" #: ejabberd_web_admin:1705 msgid "Backup of ~p" msgstr "גיבוי של ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "גבה לקובץ אצל " # פגום #: ejabberd_web_admin:489 ejabberd_web_admin:528 ejabberd_web_admin:594 #: ejabberd_web_admin:627 ejabberd_web_admin:663 ejabberd_web_admin:1148 #: ejabberd_web_admin:1431 ejabberd_web_admin:1587 ejabberd_web_admin:1871 #: ejabberd_web_admin:1900 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "פורמט רע" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "יום הולדת" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "עמוד רשת CAPTCHA" #: ejabberd_web_admin:1933 msgid "CPU Time:" msgstr "זמן מחשב (CPU):" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "לא ניתן להסיר רשימה פעילה" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "לא ניתן להסיר רשימה שגרתית" #: ejabberd_web_admin:1405 mod_register_web:212 mod_register_web:373 #: mod_register_web:381 mod_register_web:406 msgid "Change Password" msgstr "שנה סיסמה" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "שנה סיסמת משתמש" #: mod_register:295 msgid "Changing password is not allowed" msgstr "שינוי סיסמה אינו מותר" #: mod_muc_room:2784 msgid "Changing role/affiliation is not allowed" msgstr "שינוי תפקיד/שיוך אינו מותר" #: mod_register_web:258 msgid "Characters not allowed:" msgstr "תווים לא מורשים:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "תצורת חדר שיחה שונתה" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "חדר שיחה נוצר כעת" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "חדר שיחה הינו הרוס" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "חדר שיחה מותחל כעת" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "חדר שיחה הינו מופסק" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "חדרי שיחה" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "בחר שם משתמש וסיסמה כדי להירשם בעזרת שרת זה" #: mod_configure:920 msgid "Choose modules to stop" msgstr "בחר מודולים להפסקה" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "בחר טיפוס אחסון של טבלאות" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "בחר האם לאשר את ההרשמה של ישות זו." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "עיר" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "פקודות" #: mod_muc:461 msgid "Conference room does not exist" msgstr "חדר ועידה לא קיים" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "תצורה" # תצורה של חדר #: mod_muc_room:3183 msgid "Configuration of room ~s" msgstr "תצורת חדר ~s" #: ejabberd_web_admin:1437 msgid "Connected Resources:" msgstr "משאבים מחוברים:" # פרמטרי חיבור #: mod_irc:526 msgid "Connections parameters" msgstr "פרמטרים של חיבור" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "ארץ" #: ejabberd_web_admin:1592 mod_configure:139 mod_configure:580 msgid "Database" msgstr "מסד נתונים" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "תצורת טבלאות מסד נתונים אצל " #: ejabberd_web_admin:1667 msgid "Database Tables at ~p" msgstr "טבלאות מסד נתונים אצל ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "כשל מסד נתונים" #: mod_muc_log:484 msgid "December" msgstr "דצמבר" #: mod_muc_log:807 msgid "Default users as participants" msgstr "משתמשים שגרתיים כמשתתפים" # נבחרים #: ejabberd_web_admin:537 ejabberd_web_admin:637 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "מחק נבחרות" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "מחק משתמש" #: mod_announce:629 msgid "Delete message of the day" msgstr "מחק את בשורת היום" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "מחק את בשורת היום בכל המארחים" #: mod_shared_roster:900 msgid "Description:" msgstr "תיאור:" #: mod_configure:889 msgid "Disc only copy" msgstr "העתק של תקליטור בלבד" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "קבוצות מוצגות:" #: mod_register_web:270 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "אל תגלה את הסיסמה שלך לאף אחד, אפילו לא למנהלים של שרת Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "השלך גיבוי לקובץ טקסט אצל " # הטל אל קובץ תמליל #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "השלך לקובץ טקסט" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "ערוך מאפיינים" #: mod_muc_room:3901 msgid "Either approve or decline the voice request." msgstr "אשר או דחה בקשת ביטוי." #: ejabberd_web_admin:1679 msgid "Elements" msgstr "אלמנטים" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "דוא״ל" #: mod_register:292 msgid "Empty password" msgstr "סיסמה ריקה" #: mod_muc_log:818 msgid "Enable logging" msgstr "אפשר רישום פעילות" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "קידוד עבור שרת ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "סיים סשן משתמש" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "הזן רשימה של {מודול, [אפשרויות]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "הזן שם כינוי אשר ברצונך לרשום" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "הזן נתיב לקובץ גיבוי" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "הזן נתיב למדור סליל (spool dir) של jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "הזן נתיב לקובץ סליל (spool file) של jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "הזן נתיב לקובץ טקסט" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "הזן את הכיתוב שאתה רואה" # השלם #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "הזן שם משתמש וקידודים בהם ברצונך להשתמש לצורך התחברות לשרתי IRC. לחץ 'הבא' " "כדי להשיג עוד שדות למילוי. לחץ 'סיים' כדי לשמור הגדרות." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "הזן שם משתמש, קידודים, פורטים וסיסמאות בהם ברצונך להשתמש לצורך התחברות לשרתי " "IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "שרת ג׳אבּר Erlang" #: ejabberd_web_admin:1702 ejabberd_web_admin:1873 msgid "Error" msgstr "שגיאה" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "דוגמא: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:1810 msgid "Export all tables as SQL queries to a file:" msgstr "יצא את כל הטבלאות בתור שאילתות SQL לתוך קובץ:" #: ejabberd_web_admin:1782 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "יצא מידע של כל המשתמשים שבתוך שרת זה לתוך קבצי PIEFXIS ‏(XEP-0227):" #: ejabberd_web_admin:1794 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "יצא מידע של כל המשתמשים שבתוך מארח לתוך קבצי PIEFXIS ‏(XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "נכשל להפעיל bytestream" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "נכשל לחלץ JID מתוך אישור בקשת הביטוי שלך" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "נכשל לפענח תגובת HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "נכשל לפענח chanserv" #: mod_muc_room:3317 msgid "Failed to process option '~s'" msgstr "נכשל לעבד אפשרות '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "שם משפחה" #: mod_muc_log:474 msgid "February" msgstr "פברואר" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "קובץ גדול יותר משיעור של ~w בייטים" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "מלא את הטופס כדי לחפש אחר כל משתמש Jabber מבוקש (באפשרותך להוסיף * בסוף שדה " "כדי להתאים למחרוזת-משנה)" #: mod_muc_log:467 msgid "Friday" msgstr "יום שישי" #: mod_offline:691 msgid "From" msgstr "מאת" #: mod_configure:723 msgid "From ~s" msgstr "מאת ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "שם מלא" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "השג מספר של משתמשים מקוונים" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "השג מספר של משתמשים רשומים" # התחברות #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "השג זמן כניסה אחרון של משתמש" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "השג סיסמת משתמש" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "השג סטטיסטיקת משתמש" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "שם פרטי" #: mod_shared_roster:923 msgid "Group " msgstr "קבוצה " #: mod_roster:916 msgid "Groups" msgstr "קבוצות" #: ejabberd_web_admin:1091 msgid "Host" msgstr "מארח" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "מארח לא ידוע" #: ejabberd_web_admin:2189 msgid "IP" msgstr "‫IP" #: mod_configure:1673 msgid "IP addresses" msgstr "כתובות IP" #: mod_irc:439 msgid "IRC Transport" msgstr "טרנספורט IRC" #: mod_irc:496 msgid "IRC Username" msgstr "שם משתמש IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "ערוץ IRC (אל תשים סימן # ראשון)" #: mod_irc:417 msgid "IRC connection not found" msgstr "חיבור IRC לא נמצא" #: mod_irc:673 msgid "IRC server" msgstr "שרת IRC" #: mod_irc:756 msgid "IRC settings" msgstr "הגדרות IRC" #: mod_irc:766 msgid "IRC username" msgstr "שם משתמש IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "אם אינך רואה תמונת CAPTCHA כאן, בקר בעמוד רשת." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "אם ברצונך לציין פורטים, סיסמאות, קידודים אחרים עבור שרתים של IRC, מלא את " "רשימה זו עם ערכים בפורמט '{\"irc server\", \"encoding\", port, \"password" "\"}'. באופן שגרתי שירות זה משתמש בקידוד \"~s\", פורט ~p, סיסמה ריקה." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "ייבוא מדור" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "ייבוא קובץ" #: mod_configure:982 msgid "Import User from File at " msgstr "ייבוא משתמש מתוך קובץ אצל " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "יבא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "ייבוא משתמשים מתוך מדור אצל " #: ejabberd_web_admin:1826 msgid "Import user data from jabberd14 spool file:" msgstr "יבא נתוני משתמש מתוך קובץ סליל (spool file) של jabberd14:" #: ejabberd_web_admin:1769 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "יבא מידע משתמשים מתוך קובץ PIEFXIS ‏(XEP-0227):" #: ejabberd_web_admin:1837 msgid "Import users data from jabberd14 spool directory:" msgstr "יבא נתוני משתמשים מתוך מדור סליל (spool directory) של jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" # הולם #: mod_muc_room:260 msgid "Improper message type" msgstr "טיפוס הודעה לא מתאים" #: ejabberd_web_admin:1312 msgid "Incoming s2s Connections:" msgstr "חיבורי s2s נכנסים:" #: mod_muc_room:3689 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "נשלחה CAPTCHA שגויה" #: mod_muc:772 mod_muc_room:3072 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "טופס מידע לא תקין" #: mod_muc_room:1954 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "מילת מעבר שגויה" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "נשלח ערך שגוי בטופס מידע" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "הרשאה לא מספיקה" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3946 msgid "Invitations are not allowed in this conference" msgstr "הזמנות אינן מותרות בועידה זו" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1052 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "אין זה מותר לשלוח הודעות שגיאה לחדר. משתתף זה (~s) שלח הודעת שגיאה (~s) " "ונבעט מתוך החדר" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "אין זה מותר לשלוח הודעות פרטיות" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "אין זה מותר לשלוח הודעות פרטיות מן טיפוס \"groupchat\"" # חל איסור #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "אין זה מותר לשלוח הודעות פרטיות לועידה" #: mod_register_web:199 mod_register_web:207 msgid "Jabber Account Registration" msgstr "רישום חשבון Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "מזהה Jabber" #: mod_muc_log:473 msgid "January" msgstr "ינואר" #: mod_irc:665 msgid "Join IRC channel" msgstr "הצטרף לערוץ IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "הצטרף לערוץ IRC כאן." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "הצטרף לערוץ IRC במזהה Jabber זה: ~s" #: mod_muc_log:479 msgid "July" msgstr "יולי" #: mod_muc_log:478 msgid "June" msgstr "יוני" #: ejabberd_web_admin:1214 ejabberd_web_admin:1441 msgid "Last Activity" msgstr "פעילות אחרונה" #: mod_configure:1646 msgid "Last login" msgstr "כניסה אחרונה" #: ejabberd_web_admin:733 msgid "Last month" msgstr "חודש אחרון" #: ejabberd_web_admin:734 msgid "Last year" msgstr "שנה אחרונה" #: mod_configure:941 msgid "List of modules to start" msgstr "רשימה של מודולים להפעלה" #: mod_muc_admin:373 msgid "List of rooms" msgstr "רשימה של חדרים" #: ejabberd_web_admin:1595 msgid "Listened Ports" msgstr "פורטים מואזנים" #: ejabberd_web_admin:1865 msgid "Listened Ports at " msgstr "פורטים מואזנים אצל " #: ejabberd_web_admin:2007 msgid "Low level update script" msgstr "תסריט עדכון Low level" #: mod_muc_log:796 msgid "Make participants list public" msgstr "הפוך רשימת משתתפים לפומבית" #: mod_muc_log:825 msgid "Make room CAPTCHA protected" msgstr "הפוך חדר לחדר מוגן CAPTCHA" #: mod_muc_log:803 msgid "Make room members-only" msgstr "הפוך חדר לחדר עבור חברים-בלבד" #: mod_muc_log:805 msgid "Make room moderated" msgstr "הפוך חדר לחדר מבוקר" #: mod_muc_log:798 msgid "Make room password protected" msgstr "הפוך חדר לחדר מוגן במילת מעבר" #: mod_muc_log:792 msgid "Make room persistent" msgstr "הפוך חדר לחדר קבוע" #: mod_muc_log:794 msgid "Make room public searchable" msgstr "הפוך חדר לחדר שנתון לחיפוש פומבי" #: mod_register:317 msgid "Malformed username" msgstr "שם משתמש פגום" #: mod_muc_log:475 msgid "March" msgstr "מרץ" #: mod_muc_log:831 msgid "Maximum Number of Occupants" msgstr "מספר מרבי של נוכחים" #: mod_muc_log:477 msgid "May" msgstr "מאי" #: mod_shared_roster:907 msgid "Members:" msgstr "חברים:" #: mod_muc_room:1847 msgid "Membership is required to enter this room" msgstr "נדרשת חברות כדי להיכנס אל חדר זה" # תישכח #: mod_register_web:281 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "שנן את הסיסמה שלך, או רשום אותה בנייר שמור במקום בטוח. אצל Jabber אין דרך " "אוטומטית לשחזר את הסיסמה שלך במידה וזו תישמט מתוך זיכרונך." #: ejabberd_web_admin:1680 msgid "Memory" msgstr "זיכרון" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "גוף הודעה" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "שם אמצעי" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2556 mod_muc_room:3741 mod_muc_room:3785 mod_muc_room:3818 msgid "Moderator privileges required" msgstr "נדרשות הרשאות אחראי" # adjusted #: ejabberd_web_admin:2005 msgid "Modified modules" msgstr "מודולים שהותאמו" #: ejabberd_web_admin:2191 ejabberd_web_admin:2346 msgid "Module" msgstr "מודול" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "מודול נכשל לטפל בשאילתא" #: ejabberd_web_admin:1611 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "מודולים" #: ejabberd_web_admin:1894 msgid "Modules at ~p" msgstr "מודולים אצל ~p" #: mod_muc_log:463 msgid "Monday" msgstr "יום שני" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "שיחה מרובת משתמשים" #: mod_multicast:267 msgid "Multicast" msgstr "שידור מרובב" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1677 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "שם" #: mod_shared_roster:896 msgid "Name:" msgstr "שם:" #: mod_muc_room:2716 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2538 mod_muc_room:2721 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1232 ejabberd_web_admin:1413 mod_configure:1629 msgid "Never" msgstr "אף פעם" #: mod_register_web:397 msgid "New Password:" msgstr "סיסמה חדשה:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "שם כינוי" #: mod_muc:722 msgid "Nickname Registration at " msgstr "רישום שם כינוי אצל " #: mod_muc_room:2733 msgid "Nickname ~s does not exist in the room" msgstr "שם כינוי ~s לא קיים בחדר" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3095 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2525 #, fuzzy msgid "No 'item' element found" msgstr "צומת לא נמצא" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3942 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1493 msgid "No Data" msgstr "אין מידע" #: ejabberd_local:181 msgid "No available resource found" msgstr "לא נמצא משאב זמין" #: mod_announce:575 msgid "No body provided for announce message" msgstr "לא סופק גוף עבור הודעת בשורה" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "צומת לא נמצא" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "אין תכונות זמינות" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "לא נמצאו פריטים בתוך שאילתא זו" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "אין מודול אשר מטפל בשאילתא זו" #: mod_pubsub:1541 msgid "No node specified" msgstr "לא צויין צומת" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "לא נמצאו הרשמות ממתינות" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "לא נמצאה רשימת פרטיות בשם זה" #: mod_private:96 msgid "No private data found in this query" msgstr "לא נמצא מידע פרטי בתוך שאילתא זו" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "לא נמצא צומת מורץ" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "אין שירות זמין" #: mod_stats:101 msgid "No statistics found for this item" msgstr "לא נמצאה סטטיסטיקה לגבי פריט זה" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "צומת כבר קיים" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "מפתח צומת לא נמצא" #: ejabberd_web_admin:772 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "צומת לא נמצא" #: ejabberd_web_admin:1583 ejabberd_web_admin:1608 msgid "Node ~p" msgstr "צומת ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "‏Nodeprep נכשל" #: ejabberd_web_admin:1563 msgid "Nodes" msgstr "צמתים" #: ejabberd_web_admin:1348 ejabberd_web_admin:1544 ejabberd_web_admin:1554 #: ejabberd_web_admin:1964 mod_roster:907 msgid "None" msgstr "אין" #: ejabberd_web_admin:759 msgid "Not Found" msgstr "לא נמצא" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "לא רשום" #: mod_muc_log:483 msgid "November" msgstr "נובמבר" #: mod_configure:1212 msgid "Number of online users" msgstr "מספר של משתמשים מקוונים" #: mod_configure:1202 msgid "Number of registered users" msgstr "מספר של משתמשים רשומים" #: ejabberd_web_admin:1726 ejabberd_web_admin:1736 ejabberd_web_admin:1747 #: ejabberd_web_admin:1756 ejabberd_web_admin:1766 ejabberd_web_admin:1779 #: ejabberd_web_admin:1791 ejabberd_web_admin:1807 ejabberd_web_admin:1823 #: ejabberd_web_admin:1834 ejabberd_web_admin:1844 msgid "OK" msgstr "אישור" #: mod_muc_log:482 msgid "October" msgstr "אוקטובר" #: ejabberd_web_admin:1213 msgid "Offline Messages" msgstr "הודעות לא מקוונות" #: mod_offline:761 msgid "Offline Messages:" msgstr "הודעות לא מקוונות:" #: mod_register_web:393 msgid "Old Password:" msgstr "סיסמה ישנה:" #: ejabberd_web_admin:1250 ejabberd_web_admin:1424 mod_configure:1639 msgid "Online" msgstr "מקוון" #: ejabberd_web_admin:700 ejabberd_web_admin:1093 mod_configure:506 msgid "Online Users" msgstr "משתמשים מקוונים" #: ejabberd_web_admin:1306 ejabberd_web_admin:1325 ejabberd_web_admin:1937 msgid "Online Users:" msgstr "משתמשים מקוונים:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "רק תגיות או הינן מורשות" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "רק חברים רשאים לתשאל ארכיונים של חדר זה" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "רק אחראים ומשתתפים רשאים לשנות את הנושא בחדר זה" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "רק אחראים רשאים לשנות את הנושא בחדר זה" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "רק אחראים יכולים לאשר בקשות ביטוי" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:4009 msgid "Only occupants are allowed to send messages to the conference" msgstr "רק נוכחים רשאים לשלוח הודעות אל הועידה" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "רק נוכחים רשאים לשלוח שאילתות אל הועידה" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "רק מנהלי שירות רשאים לשלוח הודעות שירות" #: ejabberd_web_admin:2192 ejabberd_web_admin:2347 msgid "Options" msgstr "אפשרויות" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "שם ארגון" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "יחידת איגוד" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "חיבורי s2s יוצאים" #: ejabberd_web_admin:1309 msgid "Outgoing s2s Connections:" msgstr "חיבורי s2s יוצאים:" #: mod_muc_room:3043 mod_muc_room:3087 mod_muc_room:3717 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "נדרשות הרשאות בעלים" #: mod_offline:693 msgid "Packet" msgstr "חבילת מידע" #: mod_irc:578 msgid "Parse error" msgstr "שגיאת פענוח" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "פענוח הכשל" #: ejabberd_oauth:431 ejabberd_web_admin:1161 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:799 #: mod_register:229 msgid "Password" msgstr "סיסמה" #: mod_configure:1130 msgid "Password Verification" msgstr "אימות סיסמה" #: mod_register_web:287 mod_register_web:401 msgid "Password Verification:" msgstr "אימות סיסמה:" #: mod_irc:822 msgid "Password ~b" msgstr "סיסמה ~b" #: ejabberd_web_admin:1439 mod_register_web:264 mod_register_web:504 msgid "Password:" msgstr "סיסמה:" #: mod_configure:998 msgid "Path to Dir" msgstr "נתיב למדור" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "נתיב לקובץ" #: mod_roster:915 msgid "Pending" msgstr "ממתינות" #: ejabberd_web_admin:720 msgid "Period: " msgstr "משך זמן: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "חדרים קבועים" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "פינג" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "שאילתת פינג הינה שגויה" # האינטגרלי לחוד #: ejabberd_web_admin:1709 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "אנא שים לב כי אפשרויות אלו יגבו את מסד הנתונים המובנה Mnesia בלבד. אם הינך " "עושה שימוש במודול ODBC, עליך גם לגבות את מסד הנתונים SQL אשר מצוי ברשותך " "בנפרד." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "אנא, המתן לזמן מה לפני שליחת בקשת ביטוי חדשה" #: mod_adhoc:274 msgid "Pong" msgstr "פונג" #: ejabberd_web_admin:2189 msgid "Port" msgstr "פורט" #: mod_irc:828 msgid "Port ~b" msgstr "פורט ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2190 msgid "Protocol" msgstr "פרוטוקול" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "בקשת מנוי PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "‫Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "שאילתות אל חברי הועידה אינן מותרות בחדר זה" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "העתק RAM וגם תקליטור" #: mod_configure:889 msgid "RAM copy" msgstr "העתק RAM" #: ejabberd_web_admin:1616 msgid "RPC Call Error" msgstr "שגיאת קריאת RPC" #: ejabberd_web_admin:532 ejabberd_web_admin:631 msgid "Raw" msgstr "גולמי" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "באמת למחוק את בשורת היום?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "מקבל אינו מצוי בחדר הועידה" # רשום #: mod_register_web:294 msgid "Register" msgstr "הרשם" # Why masculine form matters. #: mod_register_web:210 mod_register_web:229 mod_register_web:237 msgid "Register a Jabber account" msgstr "רשום חשבון Jabber" #: ejabberd_web_admin:1092 msgid "Registered Users" msgstr "משתמשים רשומים" #: ejabberd_web_admin:1303 ejabberd_web_admin:1322 msgid "Registered Users:" msgstr "משתמשים רשומים:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "שמות כינוי רשומים" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "רישום בתוך mod_irc עבור " #: mod_configure:889 msgid "Remote copy" msgstr "העתק מרוחק" #: mod_roster:963 msgid "Remove" msgstr "הסר" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "הסר את כל ההודעות הלא מקוונות" #: ejabberd_web_admin:1446 mod_configure:1779 msgid "Remove User" msgstr "הסר משתמש" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "הוחלף בחיבור חדש" #: mod_configure:1675 msgid "Resources" msgstr "משאבים" #: ejabberd_web_admin:1602 ejabberd_web_admin:2220 ejabberd_web_admin:2364 msgid "Restart" msgstr "אתחל" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "אתחל שירות" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "שחזר" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "שחזר גיבוי מתוך קובץ אצל " #: ejabberd_web_admin:1739 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "שחזר גיבוי בינארי לאחר האתחול הבא של ejabberd (מצריך פחות זיכרון):" # ללא דיחוי #: ejabberd_web_admin:1729 msgid "Restore binary backup immediately:" msgstr "שחזר גיבוי בינארי לאלתר:" #: ejabberd_web_admin:1759 msgid "Restore plain text backup immediately:" msgstr "שחזר גיבוי טקסט גלוי (plain text) לאלתר:" #: mod_muc_log:635 msgid "Room Configuration" msgstr "תצורת חדר" #: mod_muc_log:655 msgid "Room Occupants" msgstr "נוכחי חדר" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "יצירת חדר נדחתה על ידי פוליסת שירות" #: mod_muc_log:827 msgid "Room description" msgstr "תיאור חדר" #: mod_muc_log:790 msgid "Room title" msgstr "כותרת חדר" #: mod_roster:1082 msgid "Roster" msgstr "רשימה" #: mod_roster:334 msgid "Roster module has failed" msgstr "מודול רשימה נכשל" #: mod_roster:968 msgid "Roster of " msgstr "רשימה של " #: mod_configure:1671 msgid "Roster size" msgstr "גודל רשימה" #: ejabberd_web_admin:1564 mod_configure:510 msgid "Running Nodes" msgstr "צמתים מורצים" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "יום שבת" #: mod_irc:582 msgid "Scan error" msgstr "שגיאת סריקה" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "סריקה נכשלה" #: ejabberd_web_admin:2008 msgid "Script check" msgstr "בדיקת תסריט" #: mod_vcard:453 msgid "Search Results for " msgstr "תוצאות חיפוש עבור " # בקרב #: mod_vcard:427 msgid "Search users in " msgstr "חיפוש משתמשים אצל " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "שלח בשורה לכל המשתמשים המקוונים" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "שלח בשורה לכל המשתמשים המקוונים בכל המארחים" #: mod_announce:613 msgid "Send announcement to all users" msgstr "שלח בשורה לכל המשתמשים" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "שלח בשורה לכל המשתמשים בכל המארחים" #: mod_muc_log:481 msgid "September" msgstr "ספטמבר" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "חיבור שרת נכשל" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "שרת ~b" #: mod_register_web:261 mod_register_web:390 mod_register_web:501 msgid "Server:" msgstr "שרת:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "קבע את בשורת היום ושלח למשתמשים מקוונים" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "קבע את בשורת היום בכל המארחים ושלח למשתמשים מקוונים" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "קבוצות רשימה משותפות" #: ejabberd_web_admin:742 msgid "Show Integral Table" msgstr "הצג טבלה אינטגרלית" #: ejabberd_web_admin:739 msgid "Show Ordinary Table" msgstr "הצג טבלה רגילה" # שירות כיבוי #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "כבה שירות" # בוטח # trust that your #: mod_register_web:277 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "ישנם לקוחות Jabber אשר מסוגלים לאחסן את הסיסמה שלך בתוך המחשב, אולם עליך " "לעשות זאת רק בתוך המחשב האישי שלך מסיבות ביטחוניות." #: ejabberd_web_admin:2244 ejabberd_web_admin:2380 msgid "Start" msgstr "התחל" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "התחל מודולים" # at (time)? בשעה #: mod_configure:936 msgid "Start Modules at " msgstr "התחל מודולים אצל " #: ejabberd_web_admin:749 ejabberd_web_admin:1597 mod_muc_admin:366 msgid "Statistics" msgstr "סטטיסטיקה" #: ejabberd_web_admin:1925 msgid "Statistics of ~p" msgstr "סטטיסטיקות של ~p" #: ejabberd_web_admin:1604 ejabberd_web_admin:2224 ejabberd_web_admin:2368 msgid "Stop" msgstr "הפסק" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "הפסק מודולים" # at (time)? בשעה #: mod_configure:918 msgid "Stop Modules at " msgstr "הפסק מודולים אצל " #: ejabberd_web_admin:1565 mod_configure:511 msgid "Stopped Nodes" msgstr "צמתים שנפסקו" #: ejabberd_web_admin:1678 msgid "Storage Type" msgstr "טיפוס אחסון" #: ejabberd_web_admin:1719 msgid "Store binary backup:" msgstr "אחסן גיבוי בינארי:" # תמליל ברור #: ejabberd_web_admin:1749 msgid "Store plain text backup:" msgstr "אחסן גיבוי טקסט גלוי (plain text):" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "נושא" #: ejabberd_web_admin:500 ejabberd_web_admin:540 ejabberd_web_admin:605 #: ejabberd_web_admin:670 ejabberd_web_admin:1689 mod_shared_roster:933 msgid "Submit" msgstr "שלח" #: ejabberd_web_admin:488 ejabberd_web_admin:527 ejabberd_web_admin:593 #: ejabberd_web_admin:626 ejabberd_web_admin:662 ejabberd_web_admin:1147 #: ejabberd_web_admin:1430 ejabberd_web_admin:1586 ejabberd_web_admin:1620 #: ejabberd_web_admin:1700 ejabberd_web_admin:1870 ejabberd_web_admin:1899 #: ejabberd_web_admin:1996 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "נשלח" #: mod_roster:914 msgid "Subscription" msgstr "הרשמה" #: mod_muc_room:3728 msgid "Subscriptions are not allowed" msgstr "הרשמות אינן מורשות" #: mod_muc_log:469 msgid "Sunday" msgstr "יום ראשון" #: mod_muc_room:998 mod_muc_room:1857 mod_muc_room:3757 msgid "That nickname is already in use by another occupant" msgstr "שם כינוי זה כבר מצוי בשימוש על ידי נוכח אחר" # note: another person > someone else #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1866 mod_muc_room:3760 msgid "That nickname is registered by another person" msgstr "שם כינוי זה הינו רשום על ידי מישהו אחר" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "‏CAPTCHA הינה תקפה." #: mod_muc_room:653 mod_muc_room:3692 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "אימות CAPTCHA נכשל" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "הסיסמה חלשה מדי" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "סיסמת חשבון Jabber שונתה בהצלחה." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "אירעה שגיאה בשינוי הסיסמה: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "אירעה שגיאה ביצירת החשבון: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "אירעה שגיאה במחיקת החשבון: " #: mod_register_web:255 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "חלק זה אינו ער לרישיות: macbeth הינה זהה למחרוזת MacBeth וגם Macbeth." #: mod_register_web:239 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "עמוד זה מתיר ליצור חשבון Jabber בשרת Jabber זה. כתובת JID ‏(Jabber " "IDentifier) תגובש באופן של: username@server. אנא קרא בזהירות את ההוראות " "למילוי נכון של השדות." #: mod_register_web:491 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "עמוד זה מתיר לך לבטל רישום של חשבון Jabber בתוך שרת Jabber זה." #: mod_muc_log:801 msgid "This room is not anonymous" msgstr "חדר זה אינו אנונימי" #: mod_muc_log:466 msgid "Thursday" msgstr "יום חמישי" #: mod_offline:690 msgid "Time" msgstr "זמן" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "זמן שיהוי" #: mod_offline:692 msgid "To" msgstr "לכבוד" #: mod_register:215 msgid "To register, visit ~s" msgstr "כדי להירשם, בקרו ~s" #: mod_configure:709 msgid "To ~s" msgstr "אל ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "סימן TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2561 mod_muc_room:3101 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1935 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "יותר מדי בקשות CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "יותר מדי יחידות bytestream פעילות" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "יותר מדי סטנזות בלי אישורי קבלה" #: mod_muc_room:1816 msgid "Too many users in this conference" msgstr "יותר מדי משתמשים בועידה זו" #: mod_register:355 msgid "Too many users registered" msgstr "יותר מדי משתמשים רשומים" #: mod_muc_admin:368 msgid "Total rooms" msgstr "חדרים סה״כ" # נעברה #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "מגבלת שיעור תעבורה נחצתה" #: ejabberd_web_admin:1945 msgid "Transactions Aborted:" msgstr "טרנזקציות שבוטלו:" #: ejabberd_web_admin:1941 msgid "Transactions Committed:" msgstr "טרנזקציות שבוצעו:" #: ejabberd_web_admin:1953 msgid "Transactions Logged:" msgstr "טרנזקציות שנרשמו:" #: ejabberd_web_admin:1949 msgid "Transactions Restarted:" msgstr "טרנזקציות שהותחלו מחדש:" #: mod_muc_log:464 msgid "Tuesday" msgstr "יום שלישי" #: mod_muc_room:1944 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "אין אפשרות להפיק CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "לא מורשה" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "פעולה לא צפויה" #: mod_register_web:509 msgid "Unregister" msgstr "בטל רישום" #: mod_register_web:215 mod_register_web:481 mod_register_web:489 msgid "Unregister a Jabber account" msgstr "בטל רישום חשבון Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "שאילתת MIX לא נתמכת" #: ejabberd_web_admin:1598 ejabberd_web_admin:2011 msgid "Update" msgstr "עדכן" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "עדכן את בשורת היום (אל תשלח)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "עדכן את בשורת היום בכל המארחים (אל תשלח)" #: ejabberd_web_admin:2004 msgid "Update plan" msgstr "תכנית עדכון" #: ejabberd_web_admin:2006 msgid "Update script" msgstr "תסריט עדכון" #: ejabberd_web_admin:1993 msgid "Update ~p" msgstr "עדכון ~p" #: ejabberd_web_admin:1929 msgid "Uptime:" msgstr "זמן פעילות:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "אסור שימוש בהרחבת STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "נדרש שימוש בהרחבת STARTTLS" #: ejabberd_web_admin:1156 ejabberd_web_admin:1212 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "משתמש" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "משתמש (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "ניהול משתמשים" #: mod_register:345 msgid "User already exists" msgstr "משתמש כבר קיים" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "סשן משתמש לא נמצא" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "סשן משתמש הסתיים" #: ejabberd_web_admin:1426 msgid "User ~s" msgstr "משתמש ~s" #: mod_register_web:249 mod_register_web:386 mod_register_web:497 msgid "Username:" msgstr "שם משתמש:" #: ejabberd_web_admin:685 ejabberd_web_admin:693 msgid "Users" msgstr "משתמשים" #: ejabberd_web_admin:716 msgid "Users Last Activity" msgstr "פעילות משתמשים אחרונה" # כה מהר #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "משתמשים אינם מורשים לרשום חשבונות כל כך במהירות" #: mod_roster:954 msgid "Validate" msgstr "הענק תוקף" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3682 mod_muc_room:3822 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3617 mod_muc_room:3661 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "ערך של '~s' צריך להיות boolean" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "ערך של '~s' צריך להיות מחרוזת datetime" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "ערך של '~s' צריך להיות integer" # וירטואליים #: ejabberd_web_admin:676 msgid "Virtual Hosts" msgstr "מארחים מדומים" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "מבקרים אינם מורשים לשנות את שמות הכינויים שלהם בחדר זה" # רשאים #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "מבקרים אינם מורשים לשלוח הודעות אל כל הנוכחים" #: mod_muc_room:3899 msgid "Voice request" msgstr "בקשת ביטוי" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "בקשות ביטוי מנוטרלות בועידה זו" #: mod_muc_log:465 msgid "Wednesday" msgstr "יום רביעי" #: mod_register_web:274 msgid "You can later change your password using a Jabber client." msgstr "באפשרותך לשנות את הסיסמה שלך מאוחר יותר באמצעות לקוח Jabber." #: mod_muc_room:1844 msgid "You have been banned from this room" msgstr "נאסרת מן חדר זה" #: mod_muc_room:1825 msgid "You have joined too many conferences" msgstr "הצטרפת ליותר מדי ועידות" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "עליך למלא את השדה \"שם כינוי\" בתוך התבנית" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "עליך להשתמש בלקוח אשר תומך x:data וגם CAPTCHA כדי להירשם" # to register nickname #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "עליך להשתמש בלקוח אשר תומך x:data כדי לרשום את השם כינוי" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר הגדרות mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי לחפש" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "אינך מורשה ליצור צמתים" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "חשבון Jabber נוצר בהצלחה." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "חשבון Jabber נמחק בהצלחה." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "רשימת הפרטיות הפעילה שלך אסרה את הניתוב של סטנזה זו." # תור הודעות לא מקוונות של הקשר שלך הינו #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "תור הודעות קשר לא מקוונות הינו מלא. ההודעה סולקה." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "מודול IRC של ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "מודול MUC של ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "שירות שידור מרובב של ejabberd" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "מודול Publish-Subscribe של ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "מודול SOCKS5 Bytestreams של ejabberd" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "מנהל רשת ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "מודול vCard של ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "נאסר/ה" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "נבעט/ה" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "נבעט/ה משום כיבוי מערכת" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "נבעט/ה משום שינוי סינוף" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "נבעט/ה משום שהחדר שונה אל חברים-בלבד" #: mod_muc_log:410 msgid "is now known as" msgstr "ידועה כעת בכינוי" #: mod_muc_log:370 msgid "joins the room" msgstr "נכנס/ת אל החדר" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "עוזב/ת את החדר" #: mod_muc_room:3876 msgid "private, " msgstr "פרטי, " #: mod_muc_room:3975 msgid "the password is" msgstr "הסיסמה היא" #: mod_vcard:292 msgid "vCard User Search" msgstr "חיפוש משתמש vCard" #: ejabberd_web_admin:658 msgid "~s access rule configuration" msgstr "~s תצורת כללי גישה" #: mod_muc_room:3968 msgid "~s invites you to the room ~s" msgstr "‫~s מזמינך לחדר ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "תור הודעות לא מקוונות של ~s" #~ msgid "No resource provided" #~ msgstr "לא סופק משאב" #~ msgid "Server" #~ msgstr "שרת" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "יותר מדי (~p) אימותים כושלים מתוך כתובת IP זו (~s). הכתובת תורשה לקבל " #~ "גישה בשעה ~s UTC" #~ msgid "Please specify file size." #~ msgstr "אנא ציין גודל קובץ." #~ msgid "Please specify file name." #~ msgstr "אנא ציין שם קובץ." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "כתובת IP זו רשומה ברשימה שחורה בתוך ~s" #~ msgid "Empty Rooms" #~ msgstr "חדרים ריקים" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "מזהה Jabber ‏~s הינו שגוי" #~ msgid "Invalid affiliation: ~s" #~ msgstr "סינוף שגוי: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "תפקיד שגוי: ~s" #~ msgid "No limit" #~ msgstr "ללא הגבלה" #~ msgid "Present real Jabber IDs to" #~ msgstr "הצג כתובות Jabber ממשיות" #~ msgid "moderators only" #~ msgstr "לאחראים בלבד" #~ msgid "anyone" #~ msgstr "לכל אחד" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "תפקידים להם נוכחות הינה משודרת" #~ msgid "Moderator" #~ msgstr "אחראי" #~ msgid "Participant" #~ msgstr "משתתף" #~ msgid "Visitor" #~ msgstr "מבקר" #~ msgid "nobody" #~ msgstr "אף אחד" #~ msgid "Allow visitors to send voice requests" #~ msgstr "התר למבקרים לשלוח בקשות ביטוי" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "תדירות מינימלית בין בקשות ביטוי (בשניות)" #~ msgid "Enable message archiving" #~ msgstr "אפשר אחסון הודעות" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "הוצא כתובות Jabber מתוך אתגר CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר חדר" #~ msgid "Number of occupants" #~ msgstr "מספר של נוכחים" #~ msgid "User JID" #~ msgstr "‏JID משתמש" #~ msgid "Grant voice to this person?" #~ msgstr "להעניק ביטוי לאישיות זו?" #~ msgid "Node ID" #~ msgstr "מזהה צומת (NID)" #~ msgid "Subscriber Address" #~ msgstr "כתובת מנוי" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "להתיר למזהה Jabber זה להירשם לצומת PubSub זה?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "מסור מטעני ייעוד (מטע״ד) יחד עם התראות אירוע" #~ msgid "Deliver event notifications" #~ msgstr "מסור התראות אירוע" # משתמשים רשומים #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "הודע מנויים כאשר תצורת הצומת משתנה" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "הודע מנויים כאשר הצומת נמחק" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "הודע מנויים כאשר פריטים מוסרים מתוך הצומת" #~ msgid "Persist items to storage" #~ msgstr "פריטים קבועים לאחסון" #~ msgid "A friendly name for the node" #~ msgstr "שם ידידותי עבור הצומת" #~ msgid "Max # of items to persist" #~ msgstr "מספר מרבי של פריטים לקיבוע" # בין אם #~ msgid "Whether to allow subscriptions" #~ msgstr "האם להתיר הרשמות" #~ msgid "Specify the access model" #~ msgstr "ציין מודל גישה" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "קבוצות רשימה מורשות להירשם" #~ msgid "Specify the publisher model" #~ msgstr "ציין מודל פרסום" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "טהר את כל הפריטים כאשר המפרסם הרלוונטי הופך לבלתי מקוון" #~ msgid "Specify the event message type" #~ msgstr "ציין טיפוס הודעת אירוע" # בבתים בבייטים (bytes) #~ msgid "Max payload size in bytes" #~ msgstr "גודל מרבי של מטען ייעוד (payload) ביחידות מידה של byte" #~ msgid "When to send the last published item" #~ msgstr "מתי לשלוח את הפריט המפורסם האחרון" #~ msgid "Only deliver notifications to available users" #~ msgstr "מסור התראות למשתמשים זמינים בלבד" #~ msgid "The collections with which a node is affiliated" #~ msgstr "האוספים עמם צומת מסונף" # שקול #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "מלא את שדות אלו כדי לחפש עבור כל משתמש Jabber מבוקש" #~ msgid "Outgoing s2s Servers:" #~ msgstr "שרתי s2s יוצאים:" #~ msgid "Code Update" #~ msgstr "עדכון קוד" #~ msgid "Delete" #~ msgstr "מחק" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "משתתף זה נבעט מתוך החדר מכיוון שהוא שלח הודעת שגיאה" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "משתתף זה נבעט מתוך החדר משום שהוא שלח הודעת שגיאה אל משתתף אחר" # שגיאת נוכחות # נוכחות שגויה #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "משתתף זה נבעט מתוך החדר משום שהוא שלח נוכחות שגיאה" #~ msgid "Notify owners about new subscribers and unsubscribers" #~ msgstr "הודע בעלים אודות מנויים חדשים ומנויים שביטלו מנוי" #~ msgid "" #~ "The type of node data, usually specified by the namespace of the payload " #~ "(if any)" #~ msgstr "סוג מידע ממסר, לרוב מצוין לפי מרחב־שמות של מטען הייעוד (אם בכלל)" #~ msgid "Group ID:" #~ msgstr "קבוצה (GID):" #~ msgid "vCard" #~ msgstr "‫vCard" #~ msgid "vCard Photo:" #~ msgstr "תצלום vCard:" #~ msgid "Remove vCard" #~ msgstr "הסרת vCard" #~ msgid "vCard size (characters):" #~ msgstr "גודל vCard (תווים):" ejabberd-18.01/priv/msgs/cs.po0000644000232200023220000020150713225664356016546 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Jan Pinkas\n" "Language-Team: \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Czech (čeština)\n" "X-Additional-Translator: Lukáš Polívka [spike411] xmpp:spike411@jabber.cz\n" "X-Additional-Translator: Milos Svasek [DuxforD] from openheads.net\n" "X-Generator: Poedit 2.0.1\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " změnil(a) téma na: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Pro vstup do místnosti musíte zadat heslo" #: ejabberd_oauth:448 msgid "Accept" msgstr "Přijmout" #: mod_configure:1109 msgid "Access Configuration" msgstr "Konfigurace přístupů" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfigurace seznamu přístupových práv (ACL)" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Seznamy přístupových práv (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Pravidla přístupů" #: mod_configure:1095 msgid "Access control lists" msgstr "Seznamy přístupových práv (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Přístup byl zamítnut nastavením služby" #: mod_configure:1113 msgid "Access rules" msgstr "Pravidla přístupů" #: mod_configure:1772 msgid "Action on user" msgstr "Akce aplikovaná na uživatele" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Přidat Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Přidat nový" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Přidat uživatele" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administrace" #: mod_configure:1767 msgid "Administration of " msgstr "Administrace " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Potřebujete práva administrátora" #: mod_configure:507 msgid "All Users" msgstr "Všichni uživatelé" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Všechny aktivity" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Povolit uživatelům měnit téma místnosti" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Povolit uživatelům odesílat požadavky (query) ostatním uživatelům" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Povolit uživatelům posílání pozvánek" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Povolit uživatelům odesílat soukromé zprávy" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Povolit návštěvníkům měnit přezdívku" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Povolit návštěvníkům odesílat soukromé zprávy" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Povolit návštěvníkům posílat stavové zprávy ve statusu" #: mod_announce:611 msgid "Announcements" msgstr "Oznámení" #: mod_muc_log:476 msgid "April" msgstr ". dubna" #: mod_muc_log:480 msgid "August" msgstr ". srpna" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "Automatické vytváření uzlů není povoleno" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Zálohovat" #: mod_configure:584 msgid "Backup Management" msgstr "Správa zálohování" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Záloha ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Záloha do souboru na " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Nesprávný formát" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Datum narození" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Uživatelské jméno i zdroj jsou požadované položky" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Bytestream již byl aktivován" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Webová stránka CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Čas procesoru:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "Aktivní seznam nelze odebrat" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "Výchozí seznam nelze odebrat" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Změnit heslo" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Změnit heslo uživatele" #: mod_register:295 msgid "Changing password is not allowed" msgstr "Změna hesla není povolena" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "Změna role/příslušnosti není povolena" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Nepřípustné znaky:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Nastavení diskuzní místnosti bylo změněno" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Místnost vytvořena" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Místnost zrušena" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Místnost spuštěna" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Místnost zastavena" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Místnosti" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Zadejte jméno uživatele a heslo pro registraci na tomto serveru" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Vyberte moduly, které mají být zastaveny" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Vyberte typ úložiště pro tabulky" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Zvolte, zda chcete schválit odebírání touto entitou." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Město" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Příkazy" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Místnost neexistuje" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Konfigurace" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Konfigurace místnosti ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Připojené zdroje:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parametry spojení" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Země" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Databáze" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Konfigurace databázových tabulek " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Databázové tabulky na ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Chyba databáze" #: mod_muc_log:484 msgid "December" msgstr ". prosince" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Uživatelé jsou implicitně členy" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Smazat vybrané" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Smazat uživatele" #: mod_announce:629 msgid "Delete message of the day" msgstr "Smazat zprávu dne" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Smazat zprávu dne na všech hostitelích" #: mod_shared_roster:900 msgid "Description:" msgstr "Popis:" #: mod_configure:889 msgid "Disc only copy" msgstr "Jen kopie disku" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Zobrazené skupiny:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Nikdy nikomu nesdělujte své heslo, ani administrátorovi serveru Jabberu." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Uložit zálohu do textového souboru na " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Uložit do textového souboru" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Duplicitní skupiny nejsou povoleny dle RFC6121" #: mod_configure:1776 msgid "Edit Properties" msgstr "Upravit vlastnosti" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Povolit nebo odmítnout voice žádost." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Položek" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "E-mail" #: mod_register:292 msgid "Empty password" msgstr "Prázdné heslo" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Zaznamenávat konverzace" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "Aktivováno push bez atributu 'node' není podporováno" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Kódování pro server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Ukončit sezení uživatele" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Vložte seznam modulů {Modul, [Parametry]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Zadejte přezdívku, kterou chcete zaregistrovat" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Zadajte cestu k souboru se zálohou" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Zadejte cestu k jabberd14 spool adresáři" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Zadejte cestu k spool souboru jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Zadajte cestu k textovému souboru" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Zadejte text, který vidíte" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Zadejte přezdívku a kódování, které chcete používat pro připojení k serverům " "IRC. Stiskněte 'Další' pro více políček k vyplnění. Stiskněte 'Dokončit' pro " "uložení nastavení." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Zadejte přezdívku, kódování, porty a hesla, které chcete používat pro " "připojení k serverům IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Chyba" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Příklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].2\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Zálohovat všechny tabulky jako SQL dotazy do souboru:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "Exportovat všechny uživatele do souboru ve formátu PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exportovat uživatele na hostiteli do souboru ve formátu PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Chyba externí komponenty" #: mod_delegation:283 msgid "External component timeout" msgstr "Timeout externí komponenty" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Chyba při aktivaci bytestreamu" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Došlo k chybě při získávání Jabber ID z vaší žádosti o voice práva" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Chyba při mapování namespace pro externí komponentu" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Chyba parsování HTTP odpovědi" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Chyba při parsování chanserv" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Chyba při zpracování možnosti '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Příjmení" #: mod_muc_log:474 msgid "February" msgstr ". února" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "Soubor větší než ~w bytů" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Pro vyhledání uživatele Jabberu vyplňte formulář (na konec přidejte znak * " "pro vyhledání podřetězce)" #: mod_muc_log:467 msgid "Friday" msgstr "Pátek" #: mod_offline:691 msgid "From" msgstr "Od" #: mod_configure:723 msgid "From ~s" msgstr "Od ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Celé jméno" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Získat počet online uživatelů" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Získat počet registrovaných uživatelů" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Získat čas podleního přihlášení uživatele" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Získat heslo uživatele" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Získat statistiky uživatele" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Křestní jméno" #: mod_shared_roster:923 msgid "Group " msgstr "Skupina " #: mod_roster:916 msgid "Groups" msgstr "Skupiny" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Hostitel" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Neznámý hostitel" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP adresy" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC transport" #: mod_irc:496 msgid "IRC Username" msgstr "IRC přezdívka" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC kanál (bez počátečního #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "IRC spojení nebylo nalezeno" #: mod_irc:673 msgid "IRC server" msgstr "IRC přezdívka" #: mod_irc:756 msgid "IRC settings" msgstr "Nastavení IRC" #: mod_irc:766 msgid "IRC username" msgstr "IRC přezdívka" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Pokud zde nevidíte obrázek CAPTCHA, přejděte na webovou stránku." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Pokud chcete zadat jiné kódování pro IRC servery, vyplňte seznam s hodnotami " "ve formátu '{\"irc server\",\"encoding\", port, \"password\"}'. Výchozí " "kódování pro tuto službu je \"~s\", port ~p, empty password." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Import adresáře" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Import souboru" #: mod_configure:982 msgid "Import User from File at " msgstr "Importovat uživatele ze souboru na " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importovat uživatele z jabberd14 spool souborů" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importovat uživatele z adresáře na " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importovat uživatele z jabberd14 spool souborů:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importovat uživatele ze souboru ve formátu PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importovat uživatele z jabberd14 spool souborů:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Nesprávný atribut 'from'" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Nesprávný atribut 'to'" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Nesprávná část s doménou atributu 'from'" #: mod_muc_room:260 msgid "Improper message type" msgstr "Nesprávný typ zprávy" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Příchozí s2s spojení:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "Nesprávné odeslání CAPTCHA" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Nesprávný datový formulář" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Nesprávné heslo" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Nesprávná hodnota v datovém formuláři" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Nesprávná hodnota atributu 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Nesprávná hodnota atributu 'action' v datovém formuláři" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Nesprávná hodnota atributu 'path' v datovém formuláři" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Nesprávná hodnota atributu 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Nedostatečné oprávnění" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Nesprávný atribut 'from' v přeposlané zprávě" #: mod_privilege:300 msgid "Invalid element" msgstr "Nesprávný element " #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "Pozvánky nejsou povoleny v této místnosti" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Není povoleno posílat chybové zprávy do místnosti. Účastník (~s) odeslal " "chybovou zprávu (~s) a byl vyhozen z místnosti" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Je zakázáno posílat soukromé zprávy" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Není dovoleno odeslání soukromé zprávy typu \"skupinová zpráva\" " #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Není povoleno odesílat soukromé zprávy v této místnosti" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Registrace účtu Jabberu" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr ". ledna" #: mod_irc:665 msgid "Join IRC channel" msgstr "Vstoupit do IRC kanálu" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Vstoupit do tohoto IRC kanálu." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Vstupte do IRC kanálu s tímto Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr ". července" #: mod_muc_log:478 msgid "June" msgstr ". června" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Poslední aktivita" #: mod_configure:1646 msgid "Last login" msgstr "Poslední přihlášení" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Poslední měsíc" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Poslední rok" #: mod_configure:941 msgid "List of modules to start" msgstr "Seznam modulů, které mají být spuštěné" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Seznam místností" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Otevřené porty" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Otevřené porty na " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Nízkoúrovňový aktualizační skript" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Nastavit seznam účastníků jako veřejný" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Chránit místnost pomocí CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Zpřístupnit místnost jen členům" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Nastavit místnost jako moderovanou" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Chránit místnost heslem" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Nastavit místnost jako stálou" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Nastavit místnost jako veřejnou" #: mod_register:317 msgid "Malformed username" msgstr "Chybně formátováné jméno uživatele" #: mod_muc_log:475 msgid "March" msgstr ". března" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Počet účastníků" #: mod_muc_log:477 msgid "May" msgstr ". května" #: mod_shared_roster:907 msgid "Members:" msgstr "Členové:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Pro vstup do místnosti musíte být členem" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Svoje heslo si zapamatujte, nebo si jej poznamenejte na papírek a ten " "uschovejte v bezpečí. Jabber nemá žádný automatizovaný způsob obnovy hesla." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Paměť" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Tělo zprávy" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Zpráva nebyla nalezena v přeposlaném obsahu" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Druhé jméno" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Chybějící atribut 'channel' nebo 'server' v datovém formuláři" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Chybějící atribut 'from'" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Chybějící atribut 'to'" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Potřebujete práva moderátora" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Aktualizované moduly" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "Modul chyboval při zpracování dotazu" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduly" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Moduly v ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Pondělí" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Víceuživatelský chat" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Vícenásobný element není povolen dle RFC6121" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Jméno" #: mod_shared_roster:896 msgid "Name:" msgstr "Jméno:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Nebyl nalezen atribut 'jid' ani 'nick'" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Nebyl nalezen atribut 'role' ani 'affiliation'" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nikdy" #: mod_register_web:377 msgid "New Password:" msgstr "Nové heslo:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Přezdívka" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registrace přezdívky na " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Přezdívka ~s v místnosti neexistuje" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Chybějící atribut 'access' v datovém formuláři" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Chybějící atribut 'acls' v datovém formuláři" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "Chybějící atribut 'affiliation'" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "Element 'item' nebyl nalezen" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Chybějící atribut 'modules' v datovém formuláři" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Chybějící atribut 'password' v datovém formuláři" #: mod_register:148 msgid "No 'password' found in this query" msgstr "Chybějící atribut 'password' v tomto dotazu" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Chybějící atribut 'path' v datovém formuláři" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "Chybějící atribut 'to' v pozvánce" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Žádná data" #: ejabberd_local:181 msgid "No available resource found" msgstr "Nebyl nalezen žádný dostupný zdroj" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Zpráva neobsahuje text" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "Nebyl nalezen datový formulář" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "Žádné funce nejsou dostupné" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Žádný hook nebyl zpracován tímto příkazem" #: mod_last:218 msgid "No info about last activity found" msgstr "Nebyla žádná informace o poslední aktivitě" #: mod_blocking:99 msgid "No items found in this query" msgstr "Žádné položky nebyly nalezeny v tomto dotazu" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Žádný modul neobsluhuje tento dotaz" #: mod_pubsub:1541 msgid "No node specified" msgstr "Žádný uzel nebyl specifikován" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "Žádné čekající předplatné nebylo nalezeno" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "Žádný privacy list s tímto jménem nebyl nalezen" #: mod_private:96 msgid "No private data found in this query" msgstr "Žádná soukromá data nebyla nalezena tímto dotazem" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "Nebyl nalezen žádný běžící uzel" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "Žádné služby nejsou dostupné" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Nebyly nalezeny statistiky pro uvedenou položku" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "Uzel již existuje" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Index uzlu nebyl nalezen" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Uzel nenalezen" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Uzel ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Nodeprep chyboval" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Uzly" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nic" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Nenalezeno" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "Není odebíráno" #: mod_muc_log:483 msgid "November" msgstr ". listopadu" #: mod_configure:1212 msgid "Number of online users" msgstr "Počet online uživatelů" #: mod_configure:1202 msgid "Number of registered users" msgstr "Počet registrovaných uživatelů" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr ". října" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Offline zprávy" #: mod_offline:761 msgid "Offline Messages:" msgstr "Offline zprávy:" #: mod_register_web:373 msgid "Old Password:" msgstr "Současné heslo:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Online" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Připojení uživatelé" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Připojení uživatelé:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Pouze značky nebo jsou povoleny" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Pouze element je povolen v tomto dotazu" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Pouze moderátoři mají povoleno měnit téma místnosti" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Jen moderátoři a účastníci mají povoleno měnit téma této místnosti" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Jen moderátoři mají povoleno měnit téma místnosti" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Pouze moderátoři mohou schválit žádosti o voice práva" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Jen členové mají povolené zasílat zprávy do místnosti" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Jen členové mohou odesílat požadavky (query) do místnosti" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Pouze správci služby smí odesílat servisní zprávy" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Nastavení" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Název firmy" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Oddělení" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Odchozí s2s spojení" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Odchozí s2s spojení:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Jsou vyžadována práva vlastníka" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "Chyba parsování" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "Došlo k chybě při parsování" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Heslo" #: mod_configure:1130 msgid "Password Verification" msgstr "Ověření hesla" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Ověření hesla:" #: mod_irc:822 msgid "Password ~b" msgstr "Heslo ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Heslo:" #: mod_configure:998 msgid "Path to Dir" msgstr "Cesta k adresáři" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Cesta k souboru" #: mod_roster:915 msgid "Pending" msgstr "Čekající" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Čas: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Stálé místnosti" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Ping dotaz je nesprávný" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Podotýkáme, že tato nastavení budou zálohována do zabudované databáze " "Mnesia. Pokud používáte ODBC modul, musíte zálohovat svoji SQL databázi " "samostatně." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Prosím, počkejte chvíli před posláním nové žádosti o voice práva" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Atribut 'ask' není povolen dle RFC6121" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Žádost odběratele PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Publikování položek do collection uzlu není povoleno" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Požadavky (queries) na členy místnosti nejsou v této místnosti povolené" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Dotaz na jiné uživatele je zakázán" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Kopie RAM a disku" #: mod_configure:889 msgid "RAM copy" msgstr "Kopie RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Chyba RPC volání" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Zdroj" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Skutečně smazat zprávu dne?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Příjemce se nenachází v místnosti" #: mod_register_web:275 msgid "Register" msgstr "Zaregistrovat se" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Zaregistrujte si účet Jabberu" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Registrovaní uživatelé" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Registrovaní uživatelé:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Registrované přezdívky" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registrace do mod_irc na " #: mod_configure:889 msgid "Remote copy" msgstr "Vzdálená kopie" #: mod_roster:963 msgid "Remove" msgstr "Odstranit" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Odstranit všechny offline zprávy" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Odstranit uživatele" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Nahrazeno novým spojením" #: mod_configure:1675 msgid "Resources" msgstr "Zdroje" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Restart" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Restartovat službu" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Obnovit" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Obnovit zálohu ze souboru na " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Obnovit binární zálohu při následujícím restartu ejabberd (vyžaduje méně " "paměti):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Okamžitě obnovit binární zálohu:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Okamžitě obnovit zálohu z textového souboru:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Nastavení místnosti" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Počet účastníků" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Pravidla služby nepovolují vytvořit místnost" #: mod_muc_log:1064 msgid "Room description" msgstr "Popis místnosti" #: mod_muc_log:1027 msgid "Room title" msgstr "Název místnosti" #: mod_roster:1082 msgid "Roster" msgstr "Seznam kontaktů" #: mod_roster:334 msgid "Roster module has failed" msgstr "Modul Roster chyboval" #: mod_roster:968 msgid "Roster of " msgstr "Seznam kontaktů " #: mod_configure:1671 msgid "Roster size" msgstr "Velikost seznamu kontaktů" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Běžící uzly" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "SASL vyjednávání není povoleno v tomto stavu" #: mod_muc_log:468 msgid "Saturday" msgstr "Sobota" #: mod_irc:582 msgid "Scan error" msgstr "Chyba skenování" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "Při skenování došlo k chybě" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Kontrola skriptu" #: mod_vcard:453 msgid "Search Results for " msgstr "Výsledky hledání pro " #: mod_vcard:427 msgid "Search users in " msgstr "Hledat uživatele v " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Odeslat oznámení všem online uživatelům" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Odeslat oznámení všem online uživatelům na všech hostitelích" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Odeslat oznámení všem uživatelům" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Odeslat oznámení všem uživatelům na všech hostitelích" #: mod_muc_log:481 msgid "September" msgstr ". září" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Připojení serveru selhalo" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Serverová spojení k lokálním subdoménám je zakázáno" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Server:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Nastavit zprávu dne a odeslat ji online uživatelům" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "Nastavit zprávu dne a odeslat ji online uživatelům" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Skupiny pro sdílený seznam kontaktů" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Zobrazit kompletní tabulku" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Zobrazit běžnou tabulku" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Vypnout službu" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Někteří klienti umí uložit vaše heslo na disk počítače. Tuto funkci " "používejte, pouze pokud věříte zabezpečení svého počítače." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Start" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Spustit moduly" #: mod_configure:936 msgid "Start Modules at " msgstr "Spustit moduly na " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistiky" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistiky ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Stop" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Zastavit moduly" #: mod_configure:918 msgid "Stop Modules at " msgstr "Zastavit moduly na " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Zastavené uzly" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Typ úložiště" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Uložit binární zálohu:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Uložit zálohu do textového souboru:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Předmět" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Odeslat" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Odeslané" #: mod_roster:914 msgid "Subscription" msgstr "Přihlášení" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Předplatné není povoleno" #: mod_muc_log:469 msgid "Sunday" msgstr "Neděle" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Přezdívka je již používána jiným členem" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Přezdívka je zaregistrována jinou osobou" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "CAPTCHA souhlasí." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Ověření CAPTCHA se nezdařilo" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "Požadovaná vlastnost není podporována touto místností" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "Heslo obsahuje nepovolené znaky" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Heslo je příliš slabé" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Heslo vašeho účtu Jabberu bylo úspěšně změněno." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "Dotaz je povolen pouze pro místní uživatele" #: mod_roster:203 msgid "The query must not contain elements" msgstr "Dotaz nesmí obsahovat elementy " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "Stanza MUSÍ obsahovat pouze jeden element , jeden element nebo jeden element " #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Při změně hesla došlo k chybě: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Při vytváření účtu došlo k chybě:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Při mazání účtu došlo k chybě: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Zde nezáleží na velikosti písmen: macbeth je stejný jako MacBeth a Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Na této stránce si můžete vytvořit účet na tomto serveru Jabberu. Vaše JID " "(Jabber IDentifikátor) bude mít tvar: uživatelskéjméno@server. Přečtěte si " "prosím pozorně instrukce pro vyplnění údajů." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Zde můžete zrušit registraci účtu na tomto serveru Jabberu." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Tato místnost není anonymní" #: mod_muc_log:466 msgid "Thursday" msgstr "Čtvrtek" #: mod_offline:690 msgid "Time" msgstr "Čas" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Časový posun" #: mod_offline:692 msgid "To" msgstr "Pro" #: mod_register:215 msgid "To register, visit ~s" msgstr "Pokud se chcete zaregistrovat, navštivte ~s" #: mod_configure:709 msgid "To ~s" msgstr "Pro ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Token TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Příliš dlouhá hodnota atributu 'xml:lang'" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "Příliš mnoho elementů " #: mod_privacy:164 msgid "Too many elements" msgstr "Přilíš mnoho elementů " #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Přiliš mnoho CAPTCHA žádostí" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Příliš mnoho aktivních bytestreamů" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Příliš mnoho nepotvrzených stanz" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Přiliš mnoho uživatelů v této místnosti" #: mod_register:355 msgid "Too many users registered" msgstr "Příliš mnoho registrovaných uživatelů" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Celkem místností" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Byl překročen limit" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transakcí zrušených:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transakcí potvrzených:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transakcí zaznamenaných:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transakcí restartovaných:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Úterý" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Nebylo možné vygenerovat CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Není možné zaregistrovat routu na existující místní doménu" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Nemáte oprávnění" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Neočekávaná akce" #: mod_register_web:488 msgid "Unregister" msgstr "Zrušit registraci" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Zrušte registraci účtu Jabberu" #: mod_mam:526 msgid "Unsupported element" msgstr "Nepodporovaný element" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Nepodporovaný MIX dotaz" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Aktualizovat" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Aktualizovat zprávu dne (neodesílat)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Aktualizovat zprávu dne pro všechny hostitele (neodesílat)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Aktualizovat plán" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Aktualizované skripty" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Aktualizovat ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Čas běhu:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "Použití STARTTLS je zakázáno" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Je vyžadováno STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Uživatel" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Uživatel (JID)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Správa uživatelů" #: mod_register:345 msgid "User already exists" msgstr "Uživatel již existuje" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "Uživatelská část Jabber ID v elementu 'from' je prázdná" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Sezení uživatele nebylo nalezeno" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Sezení uživatele bylo ukončeno" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Uživatel ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Uživatelské jméno:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Uživatelé" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Poslední aktivita uživatele" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Je zakázáno registrovat účty v tak rychlém sledu" #: mod_roster:954 msgid "Validate" msgstr "Ověřit" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "Hodnota 'get' atrubutu 'type' není povolena" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Hodnota 'set' atrubutu 'type' není povolena" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "Hodnota '~s' by měla být boolean" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "Hodnota '~s' by měla být datetime řetězec" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "Hodnota '~s' by měla být celé číslo" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtuální hostitelé" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Návštěvníkům této místnosti je zakázáno měnit přezdívku" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "" "Návštevníci nemají povoleno zasílat zprávy všem účastníkům v této místnosti" #: mod_muc_room:3879 msgid "Voice request" msgstr "Žádost o voice práva" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Voice žádosti jsou v této místnosti zakázány" #: mod_muc_log:465 msgid "Wednesday" msgstr "Středa" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Později můžete své heslo změnit pomocí klienta Jabberu." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Byl jste vyloučen z této místnosti" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Vstoupil jste do příliš velkého množství místností" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Musíte vyplnit políčko \"Přezdívka\" ve formuláři" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Pro registraci potřebujete klienta s podporou x:data a CAPTCHA" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Pro registraci přezdívky potřebujete klienta s podporou x:data" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Pro konfiguraci mod_irc potřebujete klienta s podporou x:data" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "K vyhledávání potřebujete klienta podporujícího x:data" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "Nemáte povoleno vytvářet uzly" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Váš účet Jabberu byl úspěšně vytvořen." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Váš účet Jabberu byl úspěšně smazán." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Vaše nastavení soukromí znemožnilo směrování této stance." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "Fronta offline zpráv pro váš kontakt je plná. Zpráva byla zahozena." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Nesmíte posílat zprávy na ~s. Pro povolení navštivte ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC modul" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC modul" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Služba ejabberd Multicast" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe modul" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams modul" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Webová administrace ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard modul" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "byl(a) zablokován(a)" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "byl(a) vyhozen(a) z místnosti" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "byl(a) vyhozen(a), protože dojde k vypnutí systému" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "byl(a) vyhozen(a) kvůli změně přiřazení" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "byl(a) vyhozen(a), protože mísnost je nyní pouze pro členy" #: mod_muc_log:410 msgid "is now known as" msgstr "se přejmenoval(a) na" #: mod_muc_log:370 msgid "joins the room" msgstr "vstoupil(a) do místnosti" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "opustil(a) místnost" #: mod_muc_room:3856 msgid "private, " msgstr "soukromá, " #: mod_muc_room:3955 msgid "the password is" msgstr "heslo je" #: mod_vcard:292 msgid "vCard User Search" msgstr "Hledání uživatelů ve vizitkách" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s konfigurace pravidla přístupu" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s vás zve do místnosti ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Fronta offline zpráv uživatele ~s" #~ msgid "No resource provided" #~ msgstr "Nebyl poskytnut žádný zdroj" #~ msgid "Server" #~ msgstr "Server" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Příliš mnoho (~p) chybných pokusů o přihlášení z této IP adresy (~s). " #~ "Adresa bude zablokována do ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Zvolit velikost souboru." #~ msgid "Please specify file name." #~ msgstr "Zvolit jméno souboru." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "IP adresa je blokována na ~s" #~ msgid "Empty Rooms" #~ msgstr "Prázdné konference" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s je neplatné" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Neplatné přiřazení: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Neplatná role: ~s" #~ msgid "No limit" #~ msgstr "Bez limitu" #~ msgid "Present real Jabber IDs to" #~ msgstr "Odhalovat skutečná Jabber ID" #~ msgid "moderators only" #~ msgstr "moderátorům" #~ msgid "anyone" #~ msgstr "každému" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Role, pro které je zpráva o stavu šířena" #~ msgid "Moderator" #~ msgstr "Moderátor" #~ msgid "Participant" #~ msgstr "Účastník" #~ msgid "Visitor" #~ msgstr "Návštěvník" #~ msgid "nobody" #~ msgstr "nikdo" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Povolit uživatelům posílat žádosti o voice práva" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Minimální interval mezi žádostmi o voice práva (v sekundách)" #~ msgid "Enable message archiving" #~ msgstr "Povolit ukládání historie zpráv" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Vyloučit Jabber ID z procesu CAPTCHA ověřování" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Ke konfiguraci místnosti potřebujete klienta podporujícího x:data" #~ msgid "Number of occupants" #~ msgstr "Počet účastníků" #~ msgid "User JID" #~ msgstr "Jabber ID uživatele" #~ msgid "Grant voice to this person?" #~ msgstr "Udělit voice práva této osobě?" #~ msgid "Node ID" #~ msgstr "ID uzlu" #~ msgid "Subscriber Address" #~ msgstr "Adresa odběratele" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Povolit tomuto Jabber ID odebírat tento pubsub uzel?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Doručovat náklad s upozorněním na událost" #~ msgid "Deliver event notifications" #~ msgstr "Doručovat upozornění na události" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Upozornit odběratele na změnu nastavení uzlu" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Upozornit odběratele na smazání uzlu" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Upozornit odběratele na odstranění položek z uzlu" #~ msgid "Persist items to storage" #~ msgstr "Uložit položky natrvalo do úložiště" #~ msgid "A friendly name for the node" #~ msgstr "Přívětivé jméno pro uzel" #~ msgid "Max # of items to persist" #~ msgstr "Maximální počet položek, které je možné natrvalo uložit" #~ msgid "Whether to allow subscriptions" #~ msgstr "Povolit odebírání" #~ msgid "Specify the access model" #~ msgstr "Uveďte přístupový model" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Skupiny kontaktů, které mohou odebírat" #~ msgid "Specify the publisher model" #~ msgstr "Specifikovat model pro publikování" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Smazat všechny položky, pokud se příslušný poskytovatel odpojí" #~ msgid "Specify the event message type" #~ msgstr "Zvolte typ zpráv pro události" #~ msgid "Max payload size in bytes" #~ msgstr "Maximální náklad v bajtech" #~ msgid "When to send the last published item" #~ msgstr "Kdy odeslat poslední publikovanou položku" #~ msgid "Only deliver notifications to available users" #~ msgstr "Doručovat upozornění jen právě přihlášeným uživatelům" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Kolekce, se kterými je uzel spřízněn" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Vyplňte políčka pro vyhledání uživatele Jabberu" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Odchozí s2s servery:" #~ msgid "Delete" #~ msgstr "Smazat" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "Tento účastník byl vyhozen, protože odeslal chybovou zprávu" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Tento účastník byl vyhozen, protože odeslal chybovou zprávu jinému " #~ "účastníkovi" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "Tento účastník byl vyhozen, protože odeslal chybový status" ejabberd-18.01/priv/msgs/de.po0000644000232200023220000020355613225664356016537 0ustar debalancedebalance# Maximilian Trummer , 2017. msgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: 2017-11-01 18:22+0100\n" "Last-Translator: Maximilian Trummer \n" "Language-Team: German <>\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: German (deutsch)\n" "X-Additional-Translator: Nikolaus Polak \n" "X-Additional-Translator: Florian Zumbiehl\n" "X-Additional-Translator: Cord Beermann\n" "X-Additional-Translator: Marvin Preuss\n" "X-Additional-Translator: Patrick Dreker\n" "X-Additional-Translator: Torsten Werner\n" "X-Additional-Translator: Marina Hahn\n" "X-Generator: Lokalize 2.0\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " hat das Thema geändert auf: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Sie brauchen ein Passwort um diesen Raum zu betreten" #: ejabberd_oauth:448 msgid "Accept" msgstr "Akzeptieren" #: mod_configure:1109 msgid "Access Configuration" msgstr "Zugangskonfiguration" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfiguration der Zugangskontrolllisten" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Zugangskontrolllisten (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Zugangsregeln" #: mod_configure:1095 msgid "Access control lists" msgstr "Zugangskontrolllisten (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Zugang aufgrund der Dienstrichtlinien verweigert" #: mod_configure:1113 msgid "Access rules" msgstr "Zugangsregeln" #: mod_configure:1772 msgid "Action on user" msgstr "Aktion auf Benutzer" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Jabber-ID hinzufügen" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Neue(n) hinzufügen" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Benutzer hinzufügen" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Verwaltung" #: mod_configure:1767 msgid "Administration of " msgstr "Administration von " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Administratorenrechte benötigt" #: mod_configure:507 msgid "All Users" msgstr "Alle Benutzer" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Alle Aktivitäten" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Erlaube Benutzern das Thema zu ändern" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Erlaube Benutzern Informationen über andere Benutzer abzufragen" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Erlaube Benutzern Einladungen zu senden" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Erlaube Benutzern private Nachrichten zu senden" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Erlaube Besuchern ihren Benutzernamen zu ändern" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Erlaube Besuchern das Senden von privaten Nachrichten an" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Erlaube Besuchern einen Text bei Statusänderung zu senden" #: mod_announce:611 msgid "Announcements" msgstr "Ankündigungen" #: mod_muc_log:476 msgid "April" msgstr "April" #: mod_muc_log:480 msgid "August" msgstr "August" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "Automatische Knoten-Erstellung ist nicht aktiviert" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Datensicherung" #: mod_configure:584 msgid "Backup Management" msgstr "Datensicherungsverwaltung" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Sicherung von ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Datensicherung in die Datei " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Ungültiges Format" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Geburtsdatum" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Sowohl der Benutzername als auch die Ressource werden benötigt" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Bytestream bereits aktiviert" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA -Webseite" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "CPU-Zeit:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "Kann aktive Liste nicht entfernen" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "Kann Standardliste nicht entfernen" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Passwort ändern" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Benutzer-Passwort ändern" #: mod_register:295 msgid "Changing password is not allowed" msgstr "Ändern des Passwortes ist nicht erlaubt" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "Ändern der Rolle/Zugehörigkeit ist nicht erlaubt" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Nicht erlaubte Zeichen:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Chatraum-Konfiguration geändert" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Chatraum wurde erstellt" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Chatraum wurde entfernt" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Chatraum wurde gestartet" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Chatraum wurde beendet" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Chaträume" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Wählen sie zum Registrieren auf diesem Server einen Benutzernamen und ein" " Passwort" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Wähle zu stoppende Module" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Wähle Speichertyp der Tabellen" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Wählen Sie, ob dieses Abonnement akzeptiert werden soll." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Stadt" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Befehle" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Konferenzraum existiert nicht" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Konfiguration" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Konfiguration für Raum ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Verbundene Ressourcen:" #: mod_irc:526 msgid "Connections parameters" msgstr "Verbindungsparameter" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Land" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Datenbank" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Datenbanktabellen-Konfiguration auf " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Datenbanktabellen auf ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Datenbankfehler" #: mod_muc_log:484 msgid "December" msgstr "Dezember" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Benutzer werden standardmäßig vollwertige Teilnehmer" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Markierte löschen" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Benutzer löschen" #: mod_announce:629 msgid "Delete message of the day" msgstr "Lösche Nachricht des Tages" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Lösche Nachricht des Tages auf allen Hosts" #: mod_shared_roster:900 msgid "Description:" msgstr "Beschreibung:" #: mod_configure:889 msgid "Disc only copy" msgstr "Nur auf Festplatte" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Angezeigte Gruppen:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Geben Sie niemandem Ihr Passwort, auch nicht den Administratoren des" " Jabber-Servers." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Ausgabe der Sicherung in diese Textdatei " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Ausgabe in Textdatei" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Doppelte Gruppen sind laut RFC6121 nicht erlaubt" #: mod_configure:1776 msgid "Edit Properties" msgstr "Einstellungen ändern" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Anfrage für Sprachrechte entweder bestätigen oder ablehnen." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elemente" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "E-Mail" #: mod_register:292 msgid "Empty password" msgstr "Leeres Passwort" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Protokollierung aktivieren" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "push ohne 'node'-Attribut zu aktivieren wird nicht unterstützt" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Kodierung für Server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Benutzer-Sitzung beenden" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Geben Sie eine Liste bestehend aus {Modul, [Optionen]} ein" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Geben Sie den zu registrierenden Benutzernamen ein" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Geben Sie den Pfad zur Datensicherungsdatei ein" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Geben Sie den Pfad zum jabberd14-Spool-Verzeichnis ein" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Geben Sie den Pfad zur jabberd14-Spool-Datei ein" #: mod_configure:974 msgid "Enter path to text file" msgstr "Geben Sie den Pfad zur Textdatei ein" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Geben Sie den Text den sie sehen ein" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Geben Sie Benutzernamen und Zeichenkodierungen für Verbindungen zu IRC" " Servern an. " "Drücken Sie 'Mehr' um leere Felder hinzuzufügen. Drücken Sie 'Beenden' um " "die Einstellungen zu speichern." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Geben Sie den Benutzernamen, Zeichenkodierungen, Ports und Passwörter, die" " Sie " "für die Verbindung zum IRC-Server verwenden wollen, an" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber-Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Fehler" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Beispiel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." "fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Alle Tabellen als SQL-Abfragen in eine Datei exportieren:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Alle Benutzerdaten des Servers in PIEFXIS-Dateien (XEP-0227) exportieren:" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Alle Benutzerdaten des Hosts in PIEFXIS-Dateien (XEP-0227) exportieren:" #: mod_delegation:275 msgid "External component failure" msgstr "Fehler externer Komponente" #: mod_delegation:283 msgid "External component timeout" msgstr "Zeitüberschreitung externer Komponente" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Konnte Bytestream nicht aktivieren" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" "Fehler beim Auslesen der JID aus der Anfragenbestätigung für Sprachrechte" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Konnte delegierten Namensraum nicht externer Komponente zuordnen" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Konnte HTTP-Antwort nicht parsen" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Konnte Chanserv nicht parsen" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Konnte Option '~s' nicht verarbeiten" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Nachname" #: mod_muc_log:474 msgid "February" msgstr "Februar" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "Datei größer als ~w Bytes" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Füllen sie die Felder aus, um nach passenden Jabber-Benutzern zu suchen " "(beenden Sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)" #: mod_muc_log:467 msgid "Friday" msgstr "Freitag" #: mod_offline:691 msgid "From" msgstr "Von" #: mod_configure:723 msgid "From ~s" msgstr "Von ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Vollständiger Name" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Anzahl der angemeldeten Benutzer abrufen" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Anzahl der registrierten Benutzer abrufen" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "letzte Anmeldezeit abrufen" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Benutzer-Passwort abrufen" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Benutzer-Statistiken abrufen" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Vorname" #: mod_shared_roster:923 msgid "Group " msgstr "Gruppe " #: mod_roster:916 msgid "Groups" msgstr "Gruppen" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Host unbekannt" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP-Adressen" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC-Transport" #: mod_irc:496 msgid "IRC Username" msgstr "IRC-Benutzername" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC-Channel (ohne dem ersten #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "IRC-Verbindung nicht gefunden" #: mod_irc:673 msgid "IRC server" msgstr "IRC-Server" #: mod_irc:756 msgid "IRC settings" msgstr "IRC-Einstellungen" #: mod_irc:766 msgid "IRC username" msgstr "IRC-Benutzername" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" "Wenn Sie das CAPTCHA-Bild nicht sehen, besuchen Sie bitte die Webseite." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Wenn Sie verschiedene Ports, Passwörter und Zeichenkodierungen für IRC-Server " "angeben wollen, erstellen Sie die Liste in folgendem Format '{\"IRC-Server" "\", \"Zeichenkodierung\", Port, \"Passwort\"}'. Standardmäßig benutzt dieser " "Dienst die \"~s\" Zeichenkodierung, den Port ~p und kein Passwort." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Verzeichnis importieren" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Datei importieren" #: mod_configure:982 msgid "Import User from File at " msgstr "Benutzer aus dieser Datei importieren " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importiere Benutzer aus jabberd14-Spool-Dateien" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Benutzer importieren aus dem Verzeichnis " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importiere Benutzer von jabberd14-Spool-Datei:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Benutzerdaten von einer PIEFXIS-Datei (XEP-0227) importieren:" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importiere Benutzer von jabberd14-Spool-Verzeichnis:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Falsches 'from'-Attribut" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Falsches 'to'-Attribut" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Falscher Domain-Teil des 'from'-Attributs" #: mod_muc_room:260 msgid "Improper message type" msgstr "Unzulässiger Nachrichtentyp" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Eingehende s2s-Verbindungen:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "Falsche CAPTCHA-Eingabe" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Falsches Datenformular" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Falsches Passwort" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Falscher Wert in Datenformular" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Falscher Wert des 'action'-Attributs" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Falscher Wert von 'action' in Datenformular" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Falscher Wert von 'path' in Datenformular" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Falscher Wert des 'type'-Attributs" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Unzureichende Privilegien" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Ungültiges 'from'-Attribut in weitergeleiteter Nachricht" #: mod_privilege:300 msgid "Invalid element" msgstr "Ungültiges -Element" #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "Einladungen sind in dieser Konferenz nicht erlaubt" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Es ist nicht erlaubt Fehlermeldungen an den Raum zu senden. Der Teilnehmer " "(~s) hat eine Fehlermeldung (~s) gesendet und wurde aus dem Raum gekickt" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Es ist nicht erlaubt private Nachrichten zu senden" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "" "Es ist nicht erlaubt private Nachrichten des Typs \"Gruppenchat\" zu senden" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Es ist nicht erlaubt private Nachrichten an den Raum zu schicken" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Jabber-Konto-Anmeldung" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber-ID" #: mod_muc_log:473 msgid "January" msgstr "Januar" #: mod_irc:665 msgid "Join IRC channel" msgstr "IRC-Channel beitreten" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Hier dem IRC-Channel beitreten." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Dem IRC-Channel mit dieser Jabber ID beitreten: ~s" #: mod_muc_log:479 msgid "July" msgstr "Juli" #: mod_muc_log:478 msgid "June" msgstr "Juni" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Letzte Aktivität" #: mod_configure:1646 msgid "Last login" msgstr "Letzte Anmeldung" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Letzter Monat" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Letztes Jahr" #: mod_configure:941 msgid "List of modules to start" msgstr "Liste der zu startenden Module" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Liste von Chaträumen" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Aktive Ports" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Aktive Ports bei" #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Low-Level-Aktualisierungsscript" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Teilnehmerliste öffentlich machen" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Raum mittels CAPTCHA schützen" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Raum nur für Mitglieder zugänglich machen" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Raum moderiert machen" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Raum mit Passwort schützen" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Raum persistent machen" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Raum öffentlich suchbar machen" #: mod_register:317 msgid "Malformed username" msgstr "Ungültiger Benutzername" #: mod_muc_log:475 msgid "March" msgstr "März" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Maximale Anzahl von Teilnehmern" #: mod_muc_log:477 msgid "May" msgstr "Mai" #: mod_shared_roster:907 msgid "Members:" msgstr "Mitglieder:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Um diesen Raum zu betreten müssen Sie Mitglied sein" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Merken Sie sich Ihr Passwort, oder schreiben Sie es auf einen Zettel den Sie " "sicher verwahren. Bei Jabber gibt es keine automatische Möglichkeit, das " "Passwort wiederherzustellen." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Speicher" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Nachrichtentext" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Nachricht nicht in weitergeleiteten Nutzdaten gefunden" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Zweiter Vorname" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Im Datenformular fehlt 'channel' oder 'server'" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Fehlendes 'from'-Attribut" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Fehlendes 'to'-Attribut" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Moderatorrechte benötigt" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Geänderte Module" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "Modul konnte die Anfrage nicht verarbeiten" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Module" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Module bei ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Montag" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Mehrbenutzer-Chat (MUC)" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Mehrere -Elemente sind laut RFC6121 nicht erlaubt" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Vorname" #: mod_shared_roster:896 msgid "Name:" msgstr "Name:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Weder 'jid'- noch 'nick'-Attribut gefunden" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Weder 'role'- noch 'affiliation'-Attribut gefunden" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nie" #: mod_register_web:377 msgid "New Password:" msgstr "Neues Passwort:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Benutzername" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registrieren des Benutzernames auf " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Der Benutzername ~s existiert im Raum nicht" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Kein 'access' in Datenformular gefunden" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Kein 'acls' in Datenformular gefunden" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "Kein 'affiliation'-Attribut gefunden" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "Kein 'item'-Element gefunden" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Kein 'modules' in Datenformular gefunden" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Kein 'password' in Datenformular gefunden" #: mod_register:148 msgid "No 'password' found in this query" msgstr "Kein 'password' in dieser Anfrage gefunden" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Kein 'path' in Datenformular gefunden" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "Kein 'to'-Attribut in der Einladung gefunden" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Keine Daten" #: ejabberd_local:181 msgid "No available resource found" msgstr "Keine verfügbare Ressource gefunden" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Kein Text für die Ankündigungsnachricht angegeben" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "Kein Datenformular gefunden" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "Keine Eigenschaften verfügbar" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Kein Hook hat diesen Befehl verarbeitet" #: mod_last:218 msgid "No info about last activity found" msgstr "Keine Informationen über letzte Aktivität gefunden" #: mod_blocking:99 msgid "No items found in this query" msgstr "Keine Elemente in dieser Anfrage gefunden" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Kein Modul verarbeitet diese Anfrage" #: mod_pubsub:1541 msgid "No node specified" msgstr "Kein Knoten angegeben" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "Keine ausstehenden Abonnements gefunden" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "Keine Privacy-Liste mit diesem Namen gefunden" #: mod_private:96 msgid "No private data found in this query" msgstr "Keine privaten Daten in dieser Anfrage gefunden" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "Kein laufender Knoten gefunden" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "Keine Dienste verfügbar" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Keine Statistiken für dieses Element gefunden" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "Knoten existiert bereits" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Knotenindex nicht gefunden" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Knoten nicht gefunden" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Knoten ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Nodeprep schlug fehl" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Knoten" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Keine" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Nicht gefunden" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "Nicht abonniert" #: mod_muc_log:483 msgid "November" msgstr "November" #: mod_configure:1212 msgid "Number of online users" msgstr "Anzahl der angemeldeten Benutzer" #: mod_configure:1202 msgid "Number of registered users" msgstr "Anzahl der registrierten Benutzer" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Oktober" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Offline-Nachrichten" #: mod_offline:761 msgid "Offline Messages:" msgstr "Offline-Nachrichten:" #: mod_register_web:373 msgid "Old Password:" msgstr "Altes Passwort:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Angemeldet" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Angemeldete Benutzer" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Angemeldete Benutzer:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Nur - oder -Tags sind erlaubt" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Nur -Elemente sind in dieser Anfrage erlaubt" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Nur Mitglieder dürfen den Verlauf dieses Raumes abrufen" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Nur Moderatoren und Mitglieder dürfen das Thema in diesem Raum ändern" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Nur Moderatoren dürfen das Thema in diesem Raum ändern" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Nur Moderatoren können Anfragen für Sprachrechte bestätigen" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Nur Teilnehmer dürfen Nachrichten an die Konferenz schicken" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Nur Teilnehmer sind berechtigt Anfragen an die Konferenz zu senden" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Nur Service-Administratoren sind berechtigt, Servicenachrichten zu versenden" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Optionen" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Name der Organisation" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Abteilung" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Ausgehende s2s-Verbindungen" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Ausgehende s2s-Verbindungen:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Besitzerrechte benötigt" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "Parse-Fehler" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "Parsen fehlgeschlagen" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Passwort" #: mod_configure:1130 msgid "Password Verification" msgstr "Passwort bestätigen" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Passwort bestätigen:" #: mod_irc:822 msgid "Password ~b" msgstr "Passwort ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Passwort:" #: mod_configure:998 msgid "Path to Dir" msgstr "Pfad zum Verzeichnis" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Pfad zur Datei" #: mod_roster:915 msgid "Pending" msgstr "Ausstehend" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Zeitraum: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Permanente Räume" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Ping-Anfrage ist falsch" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Beachten Sie, dass diese Optionen nur die eingebaute Mnesia-Datenbank " "sichern. Wenn sie das ODBC-Modul verwenden, müssen Sie auch die SQL-Datenbank" " separat sichern." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" "Bitte warten Sie ein wenig, bevor Sie eine weitere Anfrage für Sprachrechte " "senden" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Ein 'ask'-Attribut zu besitzen ist laut RFC6121 nicht erlaubt" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokoll" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub-Abonnenten-Anfrage" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Es ist nicht erlaubt Elemente auf dem Sammelknoten zu veröffentlichen" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Anfragen an die Teilnehmer sind in diesem Raum nicht erlaubt" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Anfrage an andere Benutzer ist verboten" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM und Festplatte" #: mod_configure:889 msgid "RAM copy" msgstr "Nur RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Fehler bei RPC-Aufruf" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Unformatiert" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Die Nachricht des Tages wirklich löschen?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Der Empfänger ist nicht im Raum" #: mod_register_web:275 msgid "Register" msgstr "Anmelden" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Jabber-Konto registrieren" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Registrierte Benutzer" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Registrierte Benutzer:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Registrierte Benutzernamen" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registrierung in mod_irc für " #: mod_configure:889 msgid "Remote copy" msgstr "Fernkopie" #: mod_roster:963 msgid "Remove" msgstr "Entfernen" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Alle Offline-Nachrichten löschen" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Benutzer löschen" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Durch neue Verbindung ersetzt" #: mod_configure:1675 msgid "Resources" msgstr "Ressourcen" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Neustart" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Dienst neustarten" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Wiederherstellung" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Datenwiederherstellung aus der Datei " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Stelle binäre Sicherung beim nächsten ejabberd-Neustart wieder her (benötigt " "weniger Speicher):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Stelle binäre Sicherung sofort wieder her:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Stelle Klartext-Sicherung sofort wieder her:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Raum-Konfiguration" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Teilnehmer in diesem Raum" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Anlegen des Raumes aufgrund der Dienstrichtlinien verweigert" #: mod_muc_log:1064 msgid "Room description" msgstr "Raumbeschreibung" #: mod_muc_log:1027 msgid "Room title" msgstr "Raumname" #: mod_roster:1082 msgid "Roster" msgstr "Kontaktliste" #: mod_roster:334 msgid "Roster module has failed" msgstr "Roster-Modul schlug fehl" #: mod_roster:968 msgid "Roster of " msgstr "Kontaktliste von " #: mod_configure:1671 msgid "Roster size" msgstr "Kontaktlistengröße" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Aktive Knoten" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "SASL-Verhandlung ist in diesem Zustand nicht erlaubt" #: mod_muc_log:468 msgid "Saturday" msgstr "Samstag" #: mod_irc:582 msgid "Scan error" msgstr "Scanfehler" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "Scan fehlgeschlagen" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Script-Überprüfung" #: mod_vcard:453 msgid "Search Results for " msgstr "Suchergebnisse für " #: mod_vcard:427 msgid "Search users in " msgstr "Benutzer suchen in " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Sende Ankündigung an alle angemeldeten Benutzer" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Sende Ankündigung an alle angemeldeten Benutzer auf allen Hosts" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Sende Ankündigung an alle Benutzer" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Sende Ankündigung an alle Benutzer auf allen Hosts" #: mod_muc_log:481 msgid "September" msgstr "September" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Serververbindung fehlgeschlagen" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Serververbindungen zu lokalen Subdomains sind verboten" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Server:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Setze Nachricht des Tages und sende sie an alle angemeldeten Benutzer" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Setze Nachricht des Tages auf allen Hosts und sende sie an alle angemeldeten " "Benutzer" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Gruppen der gemeinsamen Kontaktliste" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Integral-Tabelle anzeigen" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Gewöhnliche Tabelle anzeigen" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Dienst herunterfahren" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Einige Jabber-Clients speichern Ihr Passwort auf dem Computer. Aus" " Sicherheitsgründen sollten Sie das nur auf Ihrem persönlichen Computer tun." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Starten" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Module starten" #: mod_configure:936 msgid "Start Modules at " msgstr "Starte Module auf " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistiken" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistiken von ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Stoppen" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Module stoppen" #: mod_configure:918 msgid "Stop Modules at " msgstr "Stoppe Module auf " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Angehaltene Knoten" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Speichertyp" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Speichere binäre Sicherung:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Speichere Klartext-Sicherung:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Betreff" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Senden" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Gesendet" #: mod_roster:914 msgid "Subscription" msgstr "Abonnement" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Abonnements sind nicht erlaubt" #: mod_muc_log:469 msgid "Sunday" msgstr "Sonntag" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Dieser Benutzername wird bereits von einem anderen Teilnehmer genutzt" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Dieser Benutzername wurde bereits von jemand anderem registriert" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Das CAPTCHA ist gültig." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Die CAPTCHA-Verifizierung schlug fehl" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "Die gewünschte Eigenschaft wird von der Konferenz nicht unterstützt" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "Das Passwort enthält ungültige Zeichen" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Das Passwort ist zu schwach" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Das Passwort von Ihrem Jabber-Konto wurde geändert." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "Die Anfrage ist nur von lokalen Benutzern erlaubt" #: mod_roster:203 msgid "The query must not contain elements" msgstr "Die Anfrage darf keine -Elemente enthalten" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "Das Stanza darf nur ein -Element, ein -Element oder ein <" "list/>-Element enthalten." #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Es trat ein Fehler beim Ändern des Passworts auf: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Es trat ein Fehler beim Erstellen des Kontos auf: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Es trat ein Fehler beim Löschen des Kontos auf: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Groß-/Kleinschreibung spielt hierbei keine Rolle: macbeth ist gleich MacBeth " "und Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Diese Seite erlaubt das Anlegen eines Jabber-Kontos auf diesem Jabber-Server." " Ihre JID (Jabber IDentifier) setzt sich folgend zusammen:" " benutzername@server. " "Bitte lesen sie die Hinweise genau durch, um die Felder korrekt auszufüllen." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Diese Seite erlaubt es, ein Jabber-Konto von diesem Server zu entfernen." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Dieser Raum ist nicht anonym" #: mod_muc_log:466 msgid "Thursday" msgstr "Donnerstag" #: mod_offline:690 msgid "Time" msgstr "Zeit" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Zeitverzögerung" #: mod_offline:692 msgid "To" msgstr "An" #: mod_register:215 msgid "To register, visit ~s" msgstr "Um sich anzumelden, besuchen Sie ~s" #: mod_configure:709 msgid "To ~s" msgstr "An ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Token TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Zu langer Wert des 'xml:lang'-Attributs" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "Zu viele -Elemente" #: mod_privacy:164 msgid "Too many elements" msgstr "Zu viele -Elemente" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Zu viele CAPTCHA-Anfragen" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Zu viele aktive Bytestreams" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Zu viele unbestätigte Stanzas" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Zu viele Benutzer in dieser Konferenz" #: mod_register:355 msgid "Too many users registered" msgstr "Zu viele registrierte Benutzer" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Alle Chaträume" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Datenratenlimit wurde überschritten" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Abgebrochene Transaktionen:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Durchgeführte Transaktionen:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Protokollierte Transaktionen:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Neu gestartete Transaktionen:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Dienstag" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Konnte CAPTCHA nicht erstellen" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Konnte Route auf existierender lokaler Domain nicht registrieren" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Nicht berechtigt" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Unerwartete Aktion" #: mod_register_web:488 msgid "Unregister" msgstr "Abmelden" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Jabber-Konto entfernen" #: mod_mam:526 msgid "Unsupported element" msgstr "Nicht unterstütztes -Element" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Nicht unterstützte MIX-Anfrage" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Aktualisieren" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Aktualisiere Nachricht des Tages (nicht senden)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Aktualisiere Nachricht des Tages auf allen Hosts (nicht senden)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Aktualisierungsplan" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Aktualisierungsscript" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Aktualisierung ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Betriebszeit:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "Verwendung von STARTTLS verboten" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Verwendung von STARTTLS erforderlich" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Benutzer" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Benutzer (JID)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Benutzerverwaltung" #: mod_register:345 msgid "User already exists" msgstr "Benutzer existiert bereits" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "Benutzerteil der JID in 'from' ist leer" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Benutzersitzung nicht gefunden" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Benutzersitzung beendet" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Benutzer ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Benutzername:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Benutzer" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Letzte Benutzeraktivität" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Benutzer dürfen Konten nicht so schnell registrieren" #: mod_roster:954 msgid "Validate" msgstr "Validieren" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "Wert 'get' des 'type'-Attributs ist nicht erlaubt" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Wert 'set' des 'type'-Attributs ist nicht erlaubt" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "Wert von '~s' sollte boolesch sein" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "Wert von '~s' sollte datetime-String sein" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "Wert von '~s' sollte eine Ganzzahl sein" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtuelle Hosts" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Besucher dürfen in diesem Raum ihren Benutzernamen nicht ändern" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken" #: mod_muc_room:3879 msgid "Voice request" msgstr "Anfrage für Sprachrechte" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Anfragen für Sprachrechte sind in diesem Raum deaktiviert" #: mod_muc_log:465 msgid "Wednesday" msgstr "Mittwoch" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Sie können das Passwort später mit einem Jabber-Client ändern." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Sie wurden aus diesem Raum verbannt" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Sie sind zu vielen Konferenzen beigetreten" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Sie müssen das Feld \"Benutzername\" ausfüllen" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Sie benötigen einen Client der x:data und CAPTCHA unterstützt, um Ihren " "Benutzernamen zu registrieren" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Sie benötigen einen Client der x:data unterstützt, um Ihren Benutzernamen " "zu registrieren" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Sie benötigen einen Client der x:data unterstützt, um die mod_irc-" "Einstellungen zu konfigurieren" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "" "Sie benötigen einen Client, der x:data unterstützt, um die Suche verwenden " "zu können" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "Es ist Ihnen nicht erlaubt Knoten zu erstellen" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Ihr Jabber Konto wurde erfolgreich erstellt." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Ihr Jabber Konto wurde erfolgreich gelöscht." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" "Ihre aktive Privacy-Liste hat die Weiterleitung des Stanzas unterbunden." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Ihre Offline-Nachrichten-Warteschlange ist voll. Die Nachricht wurde" " verworfen." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Ihre Nachrichten an ~s werden blockiert. Um dies zu ändern, besuchen Sie ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC-Modul" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC-Modul" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberd Multicast-Dienst" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe-Modul" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5-Bytestreams-Modul" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web-Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard-Modul" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "wurde gebannt" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "wurde gekickt" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "wurde wegen einer Systemabschaltung gekickt" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "wurde wegen Änderung des Mitgliederstatus gekickt" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "wurde gekickt weil der Raum auf Nur-Mitglieder umgestellt wurde" #: mod_muc_log:410 msgid "is now known as" msgstr "ist nun bekannt als" #: mod_muc_log:370 msgid "joins the room" msgstr "betritt den Raum" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "verlässt den Raum" #: mod_muc_room:3856 msgid "private, " msgstr "privat, " #: mod_muc_room:3955 msgid "the password is" msgstr "das Passwort lautet" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard-Benutzer-Suche" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s Zugangsregel-Konfiguration" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s lädt Sie in den Raum ~s ein" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~ss Offline-Nachrichten-Warteschlange" #~ msgid "No resource provided" #~ msgstr "Keine Ressource angegeben" #~ msgid "Server" #~ msgstr "Server" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Zu viele (~p) fehlgeschlagene Anmeldeversuche von dieser IP Adresse (~s). " #~ "Die Adresse wird bis ~s UTC blockiert." #~ msgid "Please specify file size." #~ msgstr "Bitte geben Sie die Dateigröße an." #~ msgid "Please specify file name." #~ msgstr "Bitte geben Sie den Dateinamen an." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Diese IP Adresse ist blockiert in ~s" #~ msgid "Empty Rooms" #~ msgstr "Leere Räume" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Die Jabber-ID ~s ist ungültig" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Ungültige Mitgliedschaft: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Ungültige Rolle: ~s" #~ msgid "No limit" #~ msgstr "Keine Begrenzung" #~ msgid "Present real Jabber IDs to" #~ msgstr "Echte Jabber-IDs anzeigen für" #~ msgid "moderators only" #~ msgstr "ausschliesslich Moderatoren" #~ msgid "anyone" #~ msgstr "jeden" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Rollen, für die der Status übertragen wird" #~ msgid "Moderator" #~ msgstr "Moderator" #~ msgid "Participant" #~ msgstr "Teilnehmer" #~ msgid "Visitor" #~ msgstr "Besucher" #~ msgid "nobody" #~ msgstr "niemanden" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Anfragen von Sprachrechten für Benutzer erlauben" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Mindestdauer zwischen Anfragen für Sprachrechte (in Sekunden)" #~ msgid "Enable message archiving" #~ msgstr "Nachrichtenarchivierung aktivieren" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Von CAPTCHA Überprüfung ausgeschlossene Jabber IDs" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Sie benötigen einen Client, der x:data unterstützt, um den Raum zu " #~ "konfigurieren" #~ msgid "Number of occupants" #~ msgstr "Anzahl der Teilnehmer" #~ msgid "User JID" #~ msgstr "Benutzer JID" #~ msgid "Grant voice to this person?" #~ msgstr "Sprachrechte dieser Person erteilen?" #~ msgid "Node ID" #~ msgstr "Knoten-ID" #~ msgid "Subscriber Address" #~ msgstr "Abonnenten-Adresse" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Dieser Jabber-ID das Abonnement dieses pubsub-Knotens erlauben?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Nachrichten mit Ereignis-Benachrichtigungen zustellen" #~ msgid "Deliver event notifications" #~ msgstr "Ereignisbenachrichtigung zustellen" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "" #~ "Abonnenten benachrichtigen, wenn sich die Knotenkonfiguration ändert" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Abonnenten benachrichtigen, wenn der Knoten gelöscht wird" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "" #~ "Abonnenten benachrichtigen, wenn Einträge vom Knoten entfernt werden" #~ msgid "Persist items to storage" #~ msgstr "Einträge dauerhaft speichern" #~ msgid "A friendly name for the node" #~ msgstr "Ein merkbarer Name für den Knoten" #~ msgid "Max # of items to persist" #~ msgstr "Maximale Anzahl dauerhaft zu speichernder Einträge" #~ msgid "Whether to allow subscriptions" #~ msgstr "Ob Abonnements erlaubt sind" #~ msgid "Specify the access model" #~ msgstr "Geben sie das Zugangsmodell an" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Kontaktlisten-Gruppen die abonnieren dürfen" #~ msgid "Specify the publisher model" #~ msgstr "Geben sie das Publikationsmodell an" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Alle Einträge entfernen, wenn der relevante Veröffentlicher offline geht" #~ msgid "Specify the event message type" #~ msgstr "Geben sie den Ereignis-Nachrichtentyp an" #~ msgid "Max payload size in bytes" #~ msgstr "Maximale Nutzlastgrösse in Bytes" #~ msgid "When to send the last published item" #~ msgstr "Wann das letzte veröffentlichte Objekt gesendet werden soll" #~ msgid "Only deliver notifications to available users" #~ msgstr "Benachrichtigungen nur an verfügbare Benutzer schicken" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Sammlungen, mit denen ein Knoten verknüpft ist" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "" #~ "Füllen sie die Felder aus, um nach bestimmten Jabber-Benutzern zu suchen" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Ausgehende s2s-Server:" #~ msgid "Delete" #~ msgstr "Löschen" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Dieser Teilnehmer wurde aus dem Raum geworfen, da er eine fehlerhafte " #~ "Nachricht gesendet hat" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Dieser Teilnehmer wurde aus dem Raum geworfen, da er eine fehlerhafte " #~ "Nachricht an einen anderen Teilnehmer gesendet hat" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Dieser Teilnehmer wurde aus dem Raum gekickt, da er einen fehlerhaften " #~ "Status gesendet hat" ejabberd-18.01/priv/msgs/uk.po0000644000232200023220000022035213225664356016557 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Konstantin Khomoutov \n" "Language-Team: \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Ukrainian (українська)\n" "X-Additional-Translator: Oleg Deordiev\n" "X-Additional-Translator: Ruslan Rakhmanin\n" "X-Additional-Translator: Stoune\n" "X-Additional-Translator: Sergei Golovan\n" "X-Generator: Poedit 1.8.6\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " встановив(ла) тему: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Щоб зайти в цю конференцію, необхідно ввести пароль" #: ejabberd_oauth:448 msgid "Accept" msgstr "Прийняти" #: mod_configure:1109 msgid "Access Configuration" msgstr "Конфігурація доступу" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Конфігурація списків керування доступом" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Списки керування доступом" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Правила доступу" #: mod_configure:1095 msgid "Access control lists" msgstr "Списки керування доступом" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Доступ заборонений політикою служби" #: mod_configure:1113 msgid "Access rules" msgstr "Правила доступу" #: mod_configure:1772 msgid "Action on user" msgstr "Дія над користувачем" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Додати Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Додати" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Додати користувача" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Адміністрування" #: mod_configure:1767 msgid "Administration of " msgstr "Адміністрування " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Необхідні права адміністратора" #: mod_configure:507 msgid "All Users" msgstr "Всі користувачі" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Вся статистика" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Дозволити користувачам змінювати тему" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Дозволити iq-запити до користувачів" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Дозволити користувачам надсилати запрошення" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Дозволити приватні повідомлення" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Дозволити відвідувачам змінювати псевдонім" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Дозволити відвідувачам відсилати приватні повідомлення" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" "Дозволити відвідувачам відсилати текст статусу в оновленнях присутності" #: mod_announce:611 msgid "Announcements" msgstr "Сповіщення" #: mod_muc_log:476 msgid "April" msgstr "квітня" #: mod_muc_log:480 msgid "August" msgstr "серпня" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Резервне копіювання" #: mod_configure:584 msgid "Backup Management" msgstr "Керування резервним копіюванням" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Резервне копіювання ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Резервне копіювання в файл на " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Неправильний формат" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "День народження" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Адреса капчі" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Процесорний час:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Змінити пароль" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Змінити Пароль Користувача" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Заборонені символи:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Заборонені символи:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Заборонені символи:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Конфігурація кімнати змінилась" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Створено кімнату" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Знищено кімнату" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Запущено кімнату" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Зупинено кімнату" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Кімнати" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Виберіть назву користувача та пароль для реєстрації на цьому сервері" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Виберіть модулі, які необхідно зупинити" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Оберіть тип збереження таблиць" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Вирішіть, чи задовольнити запит цього об'єкту на підписку" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Місто" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Команди" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Конференція не існує" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Конфігурація" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Конфігурація кімнати ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Підключені ресурси:" #: mod_irc:526 msgid "Connections parameters" msgstr "Параметри з'єднання" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Країна" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "База даних" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Конфігурація таблиць бази даних на " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Таблиці бази даних на ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "База даних" #: mod_muc_log:484 msgid "December" msgstr "грудня" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Зробити користувачів учасниками за замовчуванням" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Видалити виділені" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Видалити Користувача" #: mod_announce:629 msgid "Delete message of the day" msgstr "Видалити повідомлення дня" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Видалити повідомлення дня на усіх хостах" #: mod_shared_roster:900 msgid "Description:" msgstr "Опис:" #: mod_configure:889 msgid "Disc only copy" msgstr "Тільки диск" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Видимі групи:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "Нікому не кажіть свій пароль, навіть адміністраторам сервера." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Копіювання в текстовий файл на " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Копіювання в текстовий файл" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Змінити параметри" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Підтвердить або відхилите голосовий запит" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Елементи" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Електронна пошта" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "пароль:" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Включити журнал роботи" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Кодування для сервера ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Закінчити Сеанс Користувача" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Введіть перелік такого виду {Module, [Options]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Введіть псевдонім, який ви хочете зареєструвати" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Введіть шлях до резервного файла" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Введіть шлях до директорії спула jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Введіть шлях до файла зі спула jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Введіть шлях до текстового файла" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Введіть текст, що ви бачите" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Введіть ім'я користувача та кодування, які будуть використовуватися при " "підключенні до IRC-серверів Натисніть 'Далі' для заповнення додаткових " "полів. Натисніть 'Завершити' для збереження параметрів." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Введіть ім'я користувача, кодування, порти та паролі, що будуть " "використовуватися при підключенні до IRC-серверів" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Помилка" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Приклад: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." "fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Експорт усіх таблиць, як SQL запити, у файл" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "Експорт даних всіх користувачів сервера до файлу PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Експорт даних користувачів домена до файлу PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Помилка витягнення JID з вашого схвалення голосового запиту" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Прізвище" #: mod_muc_log:474 msgid "February" msgstr "лютого" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Заповніть поля для пошуку користувача Jabber (Додайте * в кінець поля для " "пошуку підрядка)" #: mod_muc_log:467 msgid "Friday" msgstr "П'ятниця" #: mod_offline:691 msgid "From" msgstr "Від кого" #: mod_configure:723 msgid "From ~s" msgstr "Від ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Повне ім'я" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Отримати Кількість Підключених Користувачів" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Отримати Кількість Зареєстрованих Користувачів" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Отримати Час Останнього Підключення Користувача" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Отримати Пароль Користувача" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Отримати Статистику по Користувачу" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "По-батькові" #: mod_shared_roster:923 msgid "Group " msgstr "Група " #: mod_roster:916 msgid "Groups" msgstr "Групи" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Хост" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP адреси" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Транспорт" #: mod_irc:496 msgid "IRC Username" msgstr "Ім'я користувача IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Канал IRC (не включаючи #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Вузол не знайдено" #: mod_irc:673 msgid "IRC server" msgstr "IRC-сервер" #: mod_irc:756 msgid "IRC settings" msgstr "Парметри IRC" #: mod_irc:766 msgid "IRC username" msgstr "Ім'я користувача IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Якщо ви не бачите зображення капчі, перейдіть за за цією адресою." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Щоб вказати різні порти, паролі та кодування для різних серверів IRC, " "заповніть список значеннями в форматі '{\"irc server\", \"encoding\", port, " "\"password\"}'. За замовчуванням ця служба використовує \"~s\" кодування, " "порт ~p, пустий пароль." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Імпорт з директорії" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Імпорт з файла" #: mod_configure:982 msgid "Import User from File at " msgstr "Імпортування користувача з файла на " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Імпорт користувачів з jabberd14 файлів \"Spool\"" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Імпортування користувача з директорії на " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Імпорт користувачів з файла спула jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Імпорт даних користовучів з файлу PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Імпорт користувачів з діректорії спула jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Неправильний тип повідомлення" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Вхідні s2s-з'єднання:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Неправильний пароль" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Неправильний пароль" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Голосові запити відключені в цій конференції" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Не дозволяється відправляти помилкові повідомлення в кімнату. Учасник (~s) " "відправив помилкове повідомлення (~s), та був виганий з кімнати" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Приватні повідомлення не дозволені" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Не дозволяється надсилати приватні повідомлення типу \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Не дозволяється надсилати приватні повідомлення в конференцію" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Реєстрація Jabber-акаунту" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "січня" #: mod_irc:665 msgid "Join IRC channel" msgstr "Приєднатися до каналу IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Приєднатися до каналу IRC" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Приєднатися до каналу IRC з Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "липня" #: mod_muc_log:478 msgid "June" msgstr "червня" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Останнє підключення" #: mod_configure:1646 msgid "Last login" msgstr "Останнє підключення" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "За останній місяць" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "За останній рік" #: mod_configure:941 msgid "List of modules to start" msgstr "Список завантажуваних модулів" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Перелік кімнат" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Відкриті порти" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Відкриті порти на " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Низькорівневий сценарій поновлення" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Зробити список учасників видимим всім" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Зробити кімнату захищеною капчею" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Кімната тільки для зареєтрованых учасників" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Зробити кімнату модерованою" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Зробити кімнату захищеною паролем" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Зробити кімнату постійною" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Зробити кімнату видимою всім" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Ім'я користувача IRC" #: mod_muc_log:475 msgid "March" msgstr "березня" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Максимальна кількість учасників" #: mod_muc_log:477 msgid "May" msgstr "травня" #: mod_shared_roster:907 msgid "Members:" msgstr "Члени:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "В цю конференцію можуть входити тільки її члени" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Запам'ятайте пароль, або запишіть його на папері, який треба зберегти у " "безпечному місці. У Jabber'і немає автоматизованих засобів відновлення " "пароля на той випадок, якщо ви його забудете." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Пам'ять" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Тіло повідомлення" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "По-батькові" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Необхідні права модератора" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Змінені модулі" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Модуль" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Модулі" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Модулі на ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Понеділок" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Багато-користувальницький чат" #: mod_multicast:267 msgid "Multicast" msgstr "Мультікаст" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Назва" #: mod_shared_roster:896 msgid "Name:" msgstr "Назва:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Ніколи" #: mod_register_web:377 msgid "New Password:" msgstr "Новий Пароль:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Псевдонім" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Реєстрація псевдоніма на " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Псевдонім ~s в кімнаті відсутній" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Вузол не знайдено" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Немає даних" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Тіло оголошення має бути непустим" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Вузол не знайдено" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Вузол не знайдено" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Вузол не знайдено" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Вузол не знайдено" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Вузол ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Вузли" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Немає" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "не знайдено" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "листопада" #: mod_configure:1212 msgid "Number of online users" msgstr "Кількість підключених користувачів" #: mod_configure:1202 msgid "Number of registered users" msgstr "Кількість зареєстрованих користувачів" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "Продовжити" #: mod_muc_log:482 msgid "October" msgstr "грудня" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Офлайнові повідомлення" #: mod_offline:761 msgid "Offline Messages:" msgstr "Офлайнові повідомлення:" #: mod_register_web:373 msgid "Old Password:" msgstr "Старий пароль:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Підключений" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Підключені користувачі" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Підключені користувачі:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Тільки модератори можуть запитувати архіви цієї кімнати" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Тільки модератори та учасники можуть змінювати тему в цій кімнаті" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Тільки модератори можуть змінювати тему в цій кімнаті" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Тільки модератори можуть схвалювати голосові запити" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Тільки присутнім дозволяється надсилати повідомленняя в конференцію" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Тільки присутнім дозволяється відправляти запити в конференцію" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Тільки адміністратор сервісу може надсилати службові повідомлення" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Параметри" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Назва організації" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Відділ організації" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Вихідні s2s-з'єднання" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Вихідні s2s-з'єднання:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Необхідні права власника" #: mod_offline:693 msgid "Packet" msgstr "Пакет" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Пароль" #: mod_configure:1130 msgid "Password Verification" msgstr "Перевірка Пароля" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Перевірка Пароля:" #: mod_irc:822 msgid "Password ~b" msgstr "Пароль ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Пароль:" #: mod_configure:998 msgid "Path to Dir" msgstr "Шлях до директорії" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Шлях до файла" #: mod_roster:915 msgid "Pending" msgstr "Очікування" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Період" #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Постійні кімнати" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Пінг" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Зауважте, що ця опція відповідає за резервне копіювання тільки вбудованної " "бази даних Mnesia. Якщо Ви також використовуєте інше сховище для даних " "(наприклад за допомогою модуля ODBC), то його резервне копіювання потрібно " "робити окремо." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" "Будь ласка, почекайте деякий час перед тим, як знову відправляти голосовий " "запит" #: mod_adhoc:274 msgid "Pong" msgstr "Понг" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Порт" #: mod_irc:828 msgid "Port ~b" msgstr "Порт ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Протокол" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Запит на підписку PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Публікація-Підписка" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Запити до користувачів в цій конференції заборонені" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "ОЗП та диск" #: mod_configure:889 msgid "RAM copy" msgstr "ОЗП" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Помилка виклику RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "необроблений формат" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Насправді, видалити повідомлення дня?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Адресата немає в конференції" #: mod_register_web:275 msgid "Register" msgstr "Реєстрація" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Зареєструвати Jabber-акаунт" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Зареєстровані користувачі" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Зареєстровані користувачі:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Зареєстровані імена" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Реєстрація в mod_irc для " #: mod_configure:889 msgid "Remote copy" msgstr "не зберігаеться локально" #: mod_roster:963 msgid "Remove" msgstr "Видалити" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Видалити всі офлайнові повідомлення" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Видалити користувача" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Замінено новим з'єднанням" #: mod_configure:1675 msgid "Resources" msgstr "Ресурси" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Перезапустити" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Перезапустити Сервіс" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Відновлення з резервної копії" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Відновлення з резервної копії на " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Відновити з бінарної резервної копії при наступному запуску (потребує менше " "пам'яті):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Відновити з бінарної резервної копії негайно:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Відновити з текстової резервної копії негайно:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Конфігурація кімнати" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Учасники кімнати" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Створювати конференцію заборонено політикою служби" #: mod_muc_log:1064 msgid "Room description" msgstr "Опис кімнати" #: mod_muc_log:1027 msgid "Room title" msgstr "Назва кімнати" #: mod_roster:1082 msgid "Roster" msgstr "Ростер" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Ростер користувача " #: mod_configure:1671 msgid "Roster size" msgstr "Кількість контактів" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Працюючі вузли" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Субота" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "Перевірка капчею закінчилась невдало" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Перевірка сценарію" #: mod_vcard:453 msgid "Search Results for " msgstr "Результати пошуку в " #: mod_vcard:427 msgid "Search users in " msgstr "Пошук користувачів в " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Надіслати сповіщення всім підключеним користувачам" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" "Надіслати сповіщення всім підключеним користувачам на всіх віртуальних " "серверах" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Надіслати сповіщення всім користувачам" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Надіслати сповіщення до усіх користувачів на усіх хостах" #: mod_muc_log:481 msgid "September" msgstr "вересня" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Сервер ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Сервер:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Встановити повідомлення дня та надіслати його підключеним користувачам" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Встановити повідомлення дня на всіх хостах та надійслати його підключеним " "користувачам" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Спільні групи контактів" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Показати інтегральну таблицю" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Показати звичайну таблицю" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Вимкнути Сервіс" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Деякі Jabber-клієнти можуть зберігати пароль на вашому комп'ютері. " "Користуйтесь цією функцією тільки у тому випадку, якщо вважаєте її безпечною." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Запустити" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Запуск модулів" #: mod_configure:936 msgid "Start Modules at " msgstr "Запуск модулів на " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Статистика" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Статистика вузла ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Зупинити" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Зупинка модулів" #: mod_configure:918 msgid "Stop Modules at " msgstr "Зупинка модулів на " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Зупинені вузли" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Тип таблиці" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Зберегти бінарну резервну копію:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Зберегти текстову резервну копію:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Тема" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Відправити" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Відправлено" #: mod_roster:914 msgid "Subscription" msgstr "Підписка" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Неділя" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Псевдонім зайнято кимось з присутніх" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Псевдонім зареєстровано кимось іншим" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Перевірку капчею закінчено успішно" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Перевірку капчею не пройдено" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Пароль надто простий" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Пароль вашого Jabber-акаунту був успішно змінений." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Помилка при зміні пароля: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Помилка при створенні акаунту:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Помилка при видаленні акаунту: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Регістр не має значення: \"МАША\" та \"маша\" буде сприйматися як одне й те " "саме ім'я." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Тут ви можете зареєструвати обліковий запис Jabber на цьому сервері. Ваш JID " "(ідентифікатор Jabber) матиме вигляд \"користувач@сервер\". Щоб вірно " "заповнити поля нижче, будь ласка, уважно читайте інструкції до них." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Ця сторінка дозволяє видалити свій акаунт з Jabber-сервера." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Ця кімната не анонімна" #: mod_muc_log:466 msgid "Thursday" msgstr "Четвер" #: mod_offline:690 msgid "Time" msgstr "Час" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Час затримки" #: mod_offline:692 msgid "To" msgstr "Кому" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "До ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Надто багато CAPTCHA-запитів" #: mod_proxy65_service:223 #, fuzzy msgid "Too many active bytestreams" msgstr "Занадто багато пакетів без відповідей" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Занадто багато пакетів без відповідей" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Голосові запити відключені в цій конференції" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Всього кімнат" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Швидкість передачі інформації було перевищено" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Транзакції відмінені:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Транзакції завершені:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Транзакції запротокольовані:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Транзакції перезапущені:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Вівторок" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Нема можливості згенерувати капчу" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Не авторизовано" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Видалити" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Видалити Jabber-акаунт" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Обновити" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Оновити повідомлення дня (не надсилати)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Оновити повідомлення дня на всіх хостах (не надсилати)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "План оновлення" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Сценарій поновлення" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Оновлення ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Час роботи:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Ви мусите використовувати STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Ви мусите використовувати STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Користувач" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Управління Користувачами" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Вузол не знайдено" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Користувач ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Ім'я користувача:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Користувачі" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Статистика останнього підключення користувачів" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Користувачам не дозволено так часто реєструвати облікові записи" #: mod_roster:954 msgid "Validate" msgstr "Затвердити" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "віртуальні хости" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Відвідувачам не дозволяється надсилати повідомлення всім присутнім" #: mod_muc_room:3879 msgid "Voice request" msgstr "Голосовий запит" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Голосові запити відключені в цій конференції" #: mod_muc_log:465 msgid "Wednesday" msgstr "Середа" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Пізніше можна змінити пароль через Jabber-клієнт." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Вам заборонено входити в цю конференцію" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Вам необхідно заповнити поле \"Псевдонім\" у формі" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Для реєстрації псевдоніму необхідно використовувати клієнт з підтримкою x:" "data" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Для реєстрації псевдоніму необхідно використовувати клієнт з підтримкою x:" "data" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Для налагодження параметрів mod_irc необхідно використовувати клієнт, що має " "підтримку x:data" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Для пошуку необхідний клієнт із підтримкою x:data" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Приватні повідомлення не дозволені" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Ваш Jabber-акаунт було успішно створено." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Ваш Jabber-акаунт було успішно видалено." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Маршрутизація цієї строфи була відмінена активним списком приватності." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Черга повідомлень, що не були доставлені, переповнена. Повідомлення не було " "збережено." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Ваші повідомлення до ~s блокуються. Для розблокування відвідайте ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC модуль" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC модуль" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Мультікаст ejabberd сервіс" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Модуль ejabberd Публікації-Підписки" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams модуль" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Веб-інтерфейс Адміністрування ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard модуль" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "заборонили вхід в кімнату" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "вигнали з кімнати" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "вигнано з кімнати внаслідок зупинки системи" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "вигнано з кімнати внаслідок зміни рангу" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "вигнано з кімнати тому, що вона стала тільки для учасників" #: mod_muc_log:410 msgid "is now known as" msgstr "змінив(ла) псевдонім на" #: mod_muc_log:370 msgid "joins the room" msgstr "увійшов(ла) в кімнату" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "вийшов(ла) з кімнати" #: mod_muc_room:3856 msgid "private, " msgstr "приватна, " #: mod_muc_room:3955 msgid "the password is" msgstr "пароль:" #: mod_vcard:292 msgid "vCard User Search" msgstr "Пошук користувачів по vCard" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Конфігурація правила доступу ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s запрошує вас до кімнати ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Черга офлайнових повідомлень ~s" #~ msgid "No resource provided" #~ msgstr "Не вказаний ресурс" #~ msgid "Server" #~ msgstr "Сервер:" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Забагато (~p) помилок авторизації з цієї IP адреси (~s). Адресу буде " #~ "розблоковано о ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Будь ласка вкажіть розмір файлу." #~ msgid "Please specify file name." #~ msgstr "Будь ласка вкажіть ім'я файлу." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Ця IP адреса у чорному списку ~s" #~ msgid "Empty Rooms" #~ msgstr "Порожні кімнати" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s недопустимий" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Недопустимий ранг: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Недопустима роль: ~s" #~ msgid "No limit" #~ msgstr "Без обмежень" #~ msgid "Present real Jabber IDs to" #~ msgstr "Зробити реальні Jabber ID учасників видимими" #~ msgid "moderators only" #~ msgstr "тільки модераторам" #~ msgid "anyone" #~ msgstr "всім учасникам" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Ролі для яких поширюється наявність" #~ msgid "Moderator" #~ msgstr "Модератор" #~ msgid "Participant" #~ msgstr "Учасник" #~ msgid "Visitor" #~ msgstr "Відвідувач" #~ msgid "nobody" #~ msgstr "ніхто" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Дозволити відвідувачам надсилати голосові запрошення" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Мінімальний інтервал між голосовими запитами (в секундах)" #~ msgid "Enable message archiving" #~ msgstr "Ввімкнути архівацію повідомлень" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Пропускати ці Jabber ID без CAPTCHA-запиту" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Для конфігурування кімнати потрібно використовувати клієнт з підтримкою x:" #~ "data" #~ msgid "Number of occupants" #~ msgstr "Кількість присутніх" #~ msgid "User JID" #~ msgstr "JID Користувача" #~ msgid "Grant voice to this person?" #~ msgstr "Надати голос персоні?" #~ msgid "Node ID" #~ msgstr "ID вузла" #~ msgid "Subscriber Address" #~ msgstr "Адреса абонента" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Чи дозволити цьому Jabber ID підписатись новини наданого вузла" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Доставляти разом з повідомленнями про публікації самі публікації" #~ msgid "Deliver event notifications" #~ msgstr "Доставляти сповіщення про події" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Повідомляти абонентів про зміни в конфігурації збірника" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Повідомляти абонентів про видалення збірника" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Повідомляти абонентів про видалення публікацій із збірника" #~ msgid "Persist items to storage" #~ msgstr "Зберегати публікації до сховища" #~ msgid "A friendly name for the node" #~ msgstr "Псевдонім для вузла" #~ msgid "Max # of items to persist" #~ msgstr "Максимальне число збережених публікацій" #~ msgid "Whether to allow subscriptions" #~ msgstr "Дозволяти підписку" #~ msgid "Specify the access model" #~ msgstr "Визначити модель доступу" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Дозволені для підписки групи ростера" #~ msgid "Specify the publisher model" #~ msgstr "Умови публікації" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Видалити всі елементи, коли особа, що їх опублікувала, вимикається від " #~ "мережі" #~ msgid "Specify the event message type" #~ msgstr "Вкажіть тип повідомлень зі сповіщеннями про події" #~ msgid "Max payload size in bytes" #~ msgstr "Максимальний розмір корисного навантаження в байтах" #~ msgid "When to send the last published item" #~ msgstr "Коли надсилати останній опублікований елемент" #~ msgid "Only deliver notifications to available users" #~ msgstr "Доставляти повідомленнями тільки доступним користувачам" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Колекція, до якої входить вузол" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Заповніть поля для пошуку користувача Jabber" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Вихідні s2s-сервери:" #~ msgid "Delete" #~ msgstr "Видалити" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Цього учасника було відключено від кімнати через те, що він надіслав " #~ "помилкове повідомлення" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Цього учасника було відключено від кімнати через те, що він надіслав " #~ "помилкове повідомлення іншому учаснику" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Цього учасника було відключено від кімнати через те, що він надіслав " #~ "помилковий статус присутності" ejabberd-18.01/priv/msgs/zh.po0000644000232200023220000017132213225664356016563 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Shelley Shyan - shelleyshyan AT gmail DOT com\n" "Language-Team: \n" "Language: zh\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Chinese (中文)\n" "X-Additional-Translator: Zhan Caibao - zhancaibao AT gmail DOT com\n" "X-Additional-Translator: Mike Wang\n" "X-Generator: Poedit 1.8.7.1\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr "已将标题设置为: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "进入此房间需要密码" #: ejabberd_oauth:448 msgid "Accept" msgstr "接受" #: mod_configure:1109 msgid "Access Configuration" msgstr "访问配置" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "访问控制列表(ACL)配置" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "访问控制列表(ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "访问规则" #: mod_configure:1095 msgid "Access control lists" msgstr "访问控制列表(ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "访问被服务策略拒绝" #: mod_configure:1113 msgid "Access rules" msgstr "访问规则" #: mod_configure:1772 msgid "Action on user" msgstr "对用户的动作" #: mod_roster:982 msgid "Add Jabber ID" msgstr "添加Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "添加新用户" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "添加用户" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "管理" #: mod_configure:1767 msgid "Administration of " msgstr "管理" #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "需要管理员权限" #: mod_configure:507 msgid "All Users" msgstr "所有用户" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "所有活动" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "允许用户更改主题" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "允许用户查询其它用户" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "允许用户发送邀请" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "允许用户发送私聊消息" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "允许用户更改昵称" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "允许访客发送私聊消息至" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "更新在线状态时允许用户发送状态文本" #: mod_announce:611 msgid "Announcements" msgstr "通知" #: mod_muc_log:476 msgid "April" msgstr "四月" #: mod_muc_log:480 msgid "August" msgstr "八月" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "未启用自动节点创建" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "备份" #: mod_configure:584 msgid "Backup Management" msgstr "备份管理" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "~p的备份" #: mod_configure:948 msgid "Backup to File at " msgstr "备份文件位于" #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "格式错误" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "出生日期" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "用户名和资源均为必填项" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "字节流已经被激活" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "验证码网页" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "CPU时间:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "无法移除活跃列表" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "无法移除缺省列表" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "更改密码" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "更改用户密码" #: mod_register:295 msgid "Changing password is not allowed" msgstr "不允许修改密码" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "不允许修改角色/单位" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "不允许字符:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "聊天室配置已修改" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "聊天室已被创建" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "聊天室已被销毁" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "聊天室已被启动" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "聊天室已被停用" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "聊天室" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "请选择在此服务器上注册所需的用户名和密码" #: mod_configure:920 msgid "Choose modules to stop" msgstr "请选择要停止的模块" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "请选择表格的存储类型" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "选择是否允许该实体的订阅" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "城市" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "命令" #: mod_muc:461 msgid "Conference room does not exist" msgstr "会议室不存在" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "配置" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "房间~s的配置 " #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "已连接资源:" #: mod_irc:526 msgid "Connections parameters" msgstr "连接参数" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "国家" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "数据库" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "数据库表格配置位于" #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "位于~p的数据库表" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "数据库失败" #: mod_muc_log:484 msgid "December" msgstr "十二月" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "用户默认被视为参与人" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "删除已选内容" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "删除用户" #: mod_announce:629 msgid "Delete message of the day" msgstr "删除每日消息" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "删除所有主机上的每日消息" #: mod_shared_roster:900 msgid "Description:" msgstr "描述:" #: mod_configure:889 msgid "Disc only copy" msgstr "仅磁盘复制" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "已显示的组:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "不要将密码告诉任何人, 就算是Jabber服务器的管理员也不可以." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "转储备份到文本文件于" #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "转储到文本文件" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "按照RFC6121,不允许有重复组" #: mod_configure:1776 msgid "Edit Properties" msgstr "编辑属性" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "接受或拒绝声音请求" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "元素" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "电子邮件" #: mod_register:292 msgid "Empty password" msgstr "空密码" #: mod_muc_log:1055 msgid "Enable logging" msgstr "启用服务器端聊天记录" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "不支持未使用'node'属性就开启推送" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "服务器~b的编码" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "结束用户会话" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "请输入{模块, [选项]}列表" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "请输入您想要注册的昵称" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "请输入备份文件的路径" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "请输入jabberd14 spool目录的路径" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "请输入 jabberd14 spool 文件的路径" #: mod_configure:974 msgid "Enter path to text file" msgstr "请输入文本文件的路径" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "请输入您所看到的文本" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "请输入您想使用的用来连接到 IRC 服务器的用户名和编码. 按 '下一步' 获取更多待填" "字段. 按 '完成' 保存设置." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "请输入您想使用的用来连接到IRC服务器的用户名, 编码, 端口和密码." #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber服务器" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "错误" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "例如: [{\"irc.lucky.net\", \"koi8-r\"}, 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "将所有表以SQL查询语句导出到文件:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "将服务器上所有用户的数据导出到 PIEFXIS 文件 (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "将某主机的用户数据导出到 PIEFXIS 文件 (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "外部组件失败" #: mod_delegation:283 msgid "External component timeout" msgstr "外部组件超时" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "激活字节流失败" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "无法从你的声音请求确认信息中提取JID" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "未能将代理命名空间映射到外部组件" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "HTTP响应解析失败" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "chanserv机器人解析失败" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "选项'~s'处理失败" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "姓氏" #: mod_muc_log:474 msgid "February" msgstr "二月" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "文件大于 ~w 字节" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "填充表单以搜索任何匹配的Jabber用户(在字段末添加*来匹配子串)" #: mod_muc_log:467 msgid "Friday" msgstr "星期五" #: mod_offline:691 msgid "From" msgstr "从" #: mod_configure:723 msgid "From ~s" msgstr "来自~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "全名" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "获取在线用户数" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "获取注册用户数" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "获取用户上次登陆时间" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "获取用户密码" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "获取用户统计" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "中间名" #: mod_shared_roster:923 msgid "Group " msgstr "组" #: mod_roster:916 msgid "Groups" msgstr "组" #: ejabberd_web_admin:1365 msgid "Host" msgstr "主机" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "主人未知" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP地址" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC传输" #: mod_irc:496 msgid "IRC Username" msgstr "IRC用户名" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC频道 (不要输入第一个#号)" #: mod_irc:417 msgid "IRC connection not found" msgstr "没有找到 IRC 连接" #: mod_irc:673 msgid "IRC server" msgstr "IRC服务器" #: mod_irc:756 msgid "IRC settings" msgstr "IRC设置" #: mod_irc:766 msgid "IRC username" msgstr "IRC用户名" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "如果您在这里没有看到验证码图片, 请访问网页." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "如果您想为 IRC 服务器指定不同的端口, 密码, 编码, 请用 '{\"irc 服务器\", \"编" "码\", 端口, \"密码\"}' 格式的值填充此表单. 默认情况下此服务使用\"~s\"编码, " "~p 端口, 密码为空." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "导入目录" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "导入文件" #: mod_configure:982 msgid "Import User from File at " msgstr "导入用户的文件位于" #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "从 jabberd14 Spool 文件导入用户" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "导入用户的目录位于" #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "从 jabberd14 Spool 文件导入用户数据:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "从 PIEFXIS 文件 (XEP-0227) 导入用户数据:" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "从jabberd14 Spool目录导入用户数据:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "不恰当的'from'属性" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "不恰当的'to'属性" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "不恰当的'from'属性域名部分" #: mod_muc_room:260 msgid "Improper message type" msgstr "不恰当的消息类型" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "入站 s2s 连接:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "提交的验证码不正确" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "数据形式不正确" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "密码不正确" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "数据表单中有不正确的值" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "'action' 属性的值不正确" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "数据表单中 'action' 的值不正确" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "数据表单中 'path' 的值不正确" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "'type' 属性的值不正确" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "权限不足" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "转发的信息中 'from' 属性的值无效" #: mod_privilege:300 msgid "Invalid element" msgstr "无效的 元素" #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "此会议不允许邀请" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "不允许将错误消息发送到该房间. 参与者(~s)已发送过一条消息(~s)并已被踢出房间" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "不可以发送私聊消息" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "\"群组聊天\"类型不允许发送私聊消息" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "不允许向会议发送私聊消息" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Jabber帐户注册" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "一月" #: mod_irc:665 msgid "Join IRC channel" msgstr "加入IRC频道" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "在这里加入IRC频道." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "用此Jabber ID ~s加入IRC频道" #: mod_muc_log:479 msgid "July" msgstr "七月" #: mod_muc_log:478 msgid "June" msgstr "六月" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "上次活动" #: mod_configure:1646 msgid "Last login" msgstr "上次登陆" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "上个月" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "上一年" #: mod_configure:941 msgid "List of modules to start" msgstr "要启动的模块列表" #: mod_muc_admin:373 msgid "List of rooms" msgstr "房间列表" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "被监听的端口" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "监听的端口位于" #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "低级别更新脚本" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "公开参与人列表" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "保护房间验证码" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "设置房间只接收会员" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "设置房间只接收主持人" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "进入此房间需要密码" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "永久保存该房间" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "使房间可被公开搜索" #: mod_register:317 msgid "Malformed username" msgstr "用户名无效" #: mod_muc_log:475 msgid "March" msgstr "三月" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "允许的与会人最大数" #: mod_muc_log:477 msgid "May" msgstr "五月" #: mod_shared_roster:907 msgid "Members:" msgstr "会员:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "进入此房间需要会员身份" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "记住你的密码, 或将其记到纸上并放于安全位置. 如果你忘记了密码, Jabber也没有自" "动恢复密码的方式." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "内存" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "消息主体" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "转发的有效载荷中找不到消息" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "中间名" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "数据表单中缺少 'channel' 或 'server'" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "缺少 'from' 属性" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "缺少 'to' 属性" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "需要主持人权限" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "被修改模块" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "模块" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "模块未能处理查询" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "模块" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "位于~p的模块" #: mod_muc_log:463 msgid "Monday" msgstr "星期一" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "多用户聊天" #: mod_multicast:267 msgid "Multicast" msgstr "多重映射" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "按照 RFC6121,多个 元素是不允许的" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "姓名" #: mod_shared_roster:896 msgid "Name:" msgstr "姓名:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "属性 'jid' 或 'nick' 均未发现" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "属性 'role' 或 'affiliation' 均未发现" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "从未" #: mod_register_web:377 msgid "New Password:" msgstr "新密码:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "昵称" #: mod_muc:722 msgid "Nickname Registration at " msgstr "昵称注册于" #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "昵称~s不在该房间" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "数据表单中未发现 'access'" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "数据表单中未发现 'acls'" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "未发现 'affiliation' 属性" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "没有找到 'item' 元素" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "数据表单中未发现 'module'" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "数据表单中未发现 'password'" #: mod_register:148 msgid "No 'password' found in this query" msgstr "此查询中未发现 'password'" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "数据表单中未发现 'path'" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "邀请中未发现 'to' 标签" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "没有数据" #: ejabberd_local:181 msgid "No available resource found" msgstr "没发现可用资源" #: mod_announce:575 msgid "No body provided for announce message" msgstr "通知消息无正文内容" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "没有找到数据表单" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "没有可用特征" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "没有任何钩子已处理此命令" #: mod_last:218 msgid "No info about last activity found" msgstr "未找到上次活动的信息" #: mod_blocking:99 msgid "No items found in this query" msgstr "此查询中没发现任何项" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "没有正在处理此查询的模块" #: mod_pubsub:1541 msgid "No node specified" msgstr "无指定节点" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "未发现挂起的订阅" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "未找到带此名称的隐私列表" #: mod_private:96 msgid "No private data found in this query" msgstr "此查询中未发现私有数据" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "没有找到运行中的节点" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "无可用服务" #: mod_stats:101 msgid "No statistics found for this item" msgstr "未找到此项的统计数据" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "节点已存在" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "没有找到节点索引" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "没有找到节点" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "节点~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Nodeprep 已失效" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "节点" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "无" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "没有找到" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "未订阅" #: mod_muc_log:483 msgid "November" msgstr "十一月" #: mod_configure:1212 msgid "Number of online users" msgstr "在线用户数" #: mod_configure:1202 msgid "Number of registered users" msgstr "注册用户数" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "确定" #: mod_muc_log:482 msgid "October" msgstr "十月" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "离线消息" #: mod_offline:761 msgid "Offline Messages:" msgstr "离线消息:" #: mod_register_web:373 msgid "Old Password:" msgstr "旧密码: " #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "在线" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "在线用户" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "在线用户:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "仅允许 标签" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "此查询中只允许 元素" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "只有会员可以查询本房间的存档" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "只有主持人和参与人可以在此房间里更改主题" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "只有主持人可以在此房间里更改主题" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "仅主持人能确认声音请求" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "只有与会人可以向大会发送消息" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "只有与会人可以向大会发出查询请求" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "只有服务管理员可以发送服务消息" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "选项" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "组织名称" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "组织单位" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "出站 s2s 连接" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "出站 s2s 连接:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "需要持有人权限" #: mod_offline:693 msgid "Packet" msgstr "数据包" #: mod_irc:578 msgid "Parse error" msgstr "解析错误" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "解析失败" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "密码" #: mod_configure:1130 msgid "Password Verification" msgstr "确认密码" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "密码确认:" #: mod_irc:822 msgid "Password ~b" msgstr "~b的密码" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "密码:" #: mod_configure:998 msgid "Path to Dir" msgstr "目录的路径" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "文件路径" #: mod_roster:915 msgid "Pending" msgstr "挂起" #: ejabberd_web_admin:994 msgid "Period: " msgstr "持续时间: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "永久房间" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Ping 查询不正确" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "注意:这些选项仅将备份内置的 Mnesia 数据库. 如果您正在使用 ODBC 模块, 您还需" "要分别备份您的数据库." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "请稍后再发送新的声音请求" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "端口" #: mod_irc:828 msgid "Port ~b" msgstr "~b的端口" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "按照 RFC6121, 不允许有 'ask' 属性" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "协议" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub订阅人请求" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "发行-订阅" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "不允许" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "本房间不可以查询会议成员信息" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "禁止查询其他用户" #: mod_configure:889 msgid "RAM and disc copy" msgstr "内存与磁盘复制" #: mod_configure:889 msgid "RAM copy" msgstr "内存(RAM)复制" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "RPC 调用错误" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "原始格式" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "确实要删除每日消息吗?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "接收人不在会议室" #: mod_register_web:275 msgid "Register" msgstr "注册" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "注册Jabber帐户" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "注册用户" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "注册用户:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "注册的昵称" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "mod_irc 中的注册是为 " #: mod_configure:889 msgid "Remote copy" msgstr "远程复制" #: mod_roster:963 msgid "Remove" msgstr "移除" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "移除所有离线消息" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "删除用户" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "被新的连接替换" #: mod_configure:1675 msgid "Resources" msgstr "资源" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "重启" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "重启服务" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "恢复" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "要恢复的备份文件位于" #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "立即恢复二进制备份:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "立即恢复普通文本备份:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "房间配置" #: mod_muc_log:892 msgid "Room Occupants" msgstr "房间人数" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "创建房间被服务策略拒绝" #: mod_muc_log:1064 msgid "Room description" msgstr "房间描述" #: mod_muc_log:1027 msgid "Room title" msgstr "房间标题" #: mod_roster:1082 msgid "Roster" msgstr "花名册" #: mod_roster:334 msgid "Roster module has failed" msgstr "花名册模块已失效" #: mod_roster:968 msgid "Roster of " msgstr "花名册属于" #: mod_configure:1671 msgid "Roster size" msgstr "花名册大小" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "运行中的节点" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "此状态下不允许SASL协商" #: mod_muc_log:468 msgid "Saturday" msgstr "星期六" #: mod_irc:582 msgid "Scan error" msgstr "扫描错误" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "扫描失败." #: ejabberd_web_admin:2282 msgid "Script check" msgstr "脚本检查" #: mod_vcard:453 msgid "Search Results for " msgstr "搜索结果属于关键词 " #: mod_vcard:427 msgid "Search users in " msgstr "搜索用户于" #: mod_announce:617 msgid "Send announcement to all online users" msgstr "发送通知给所有在线用户" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "发送通知给所有主机的在线用户" #: mod_announce:613 msgid "Send announcement to all users" msgstr "发送通知给所有用户" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "发送通知给所有主机上的所有用户" #: mod_muc_log:481 msgid "September" msgstr "九月" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "服务器连接失败" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "禁止服务器连接到本地子域" #: mod_irc:842 msgid "Server ~b" msgstr "服务器~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "服务器:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "设定每日消息并发送给所有在线用户" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "设置所有主机上的每日消息并发送给在线用户" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "共享的花名册组群" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "显示完整列表" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "显示普通列表" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "关闭服务" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "某些 Jabber 客户端可以在你的计算机里存储密码. 请仅在你确认你的计算机安全的情" "况下使用该功能." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "开始" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "启动模块" #: mod_configure:936 msgid "Start Modules at " msgstr "要启动的模块位于 " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "统计" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "~p的统计" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "停止" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "停止模块" #: mod_configure:918 msgid "Stop Modules at " msgstr "要停止的模块位于 " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "已经停止的节点" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "存储类型" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "存储为二进制备份:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "存储为普通文本备份:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "标题" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "提交" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "已提交" #: mod_roster:914 msgid "Subscription" msgstr "订阅" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "不允许订阅" #: mod_muc_log:469 msgid "Sunday" msgstr "星期天" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "该昵称已被另一用户使用" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "该昵称已被另一个人注册了" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "验证码有效." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "验证码检查失败" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "会议不支持所请求的特征" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "密码包含不可接受的字符" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "密码强度太弱" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "你的Jabber帐户密码已成功更新." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "仅本地用户可以查询" #: mod_roster:203 msgid "The query must not contain elements" msgstr "查询不能包含 元素" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "本节必须只含一个 元素, 元素,或 元素" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "修改密码出错: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "帐户创建出错: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "帐户删除失败: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "此处不区分大小写: macbeth 与 MacBeth 和 Macbeth 是一样的." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "本页面允许在此服务器上创建Jabber帐户. 你的JID (Jabber ID) 的形式如下: 用户名@" "服务器. 请仔细阅读说明并正确填写相应字段." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "此页面允许在此Jabber服务器上注销Jabber帐户" #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "此房间不是匿名房间" #: mod_muc_log:466 msgid "Thursday" msgstr "星期四" #: mod_offline:690 msgid "Time" msgstr "时间" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "时间延迟" #: mod_offline:692 msgid "To" msgstr "到" #: mod_register:215 msgid "To register, visit ~s" msgstr "要注册,请访问 ~s" #: mod_configure:709 msgid "To ~s" msgstr "发送给~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "TTL令牌" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "'xml:lang'的值太长" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "太多 元素" #: mod_privacy:164 msgid "Too many elements" msgstr "太多 元素" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "验证码请求太多" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "活跃的字节流太多" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "未被确认的节太多" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "该会议的用户太多" #: mod_register:355 msgid "Too many users registered" msgstr "注册用户太多" #: mod_muc_admin:368 msgid "Total rooms" msgstr "所有房间" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "已经超过传输率限制" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "取消的事务:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "提交的事务:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "记入日志的事务:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "重启的事务:" #: mod_muc_log:464 msgid "Tuesday" msgstr "星期二" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "无法生成验证码" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "在已存在的本地域上无法注册路由" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "未认证的" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "意外行为" #: mod_register_web:488 msgid "Unregister" msgstr "取消注册" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "注销Jabber帐户" #: mod_mam:526 msgid "Unsupported element" msgstr "不支持的 元素" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "不支持的 MIX 查询" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "更新" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "更新每日消息(不发送)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "更新所有主机上的每日消息(不发送)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "更新计划" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "更新脚本" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "更新~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "正常运行时间:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "禁止使用 STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "要求使用 STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "用户" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "用户 (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "用户管理" #: mod_register:345 msgid "User already exists" msgstr "用户已存在" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "'from' 中 JID 值的用户部分为空" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "用户会话未找到" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "用户会话已终止" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "用户~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "用户名:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "用户" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "用户上次活动" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "不允许用户太频繁地注册帐户" #: mod_roster:954 msgid "Validate" msgstr "确认" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "不允许 'type' 属性的 'get' 值" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "不允许 'type' 属性的 'set' 值" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "'~s' 的值应为布尔型" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "'~s' 的值应为日期时间字符串" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "'~s' 的值应为整数" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "虚拟主机" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "此房间不允许用户更改昵称" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "不允许访客给所有占有者发送消息" #: mod_muc_room:3879 msgid "Voice request" msgstr "声音请求" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "该会议的声音请求已被禁用" #: mod_muc_log:465 msgid "Wednesday" msgstr "星期三" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "你可以稍后用Jabber客户端修改你的密码." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "您已被禁止进入该房间" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "您加入的会议太多" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "您必须填充表单中\"昵称\"项" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "您需要一个支持 x:data 和验证码的客户端进行注册" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "您需要一个支持 x:data 的客户端来注册昵称" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "您需要一个兼容 x:data 的客户端来配置mod_irc设置" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "您需要一个兼容 x:data 的客户端来搜索" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "您不可以创建节点" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "你的Jabber帐户已成功创建." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "你的 Jabber 帐户已成功删除." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "你的活跃私聊列表拒绝了在此房间进行路由分发." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "您的联系人离线消息队列已满. 消息已被丢弃" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC 模块" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC 模块" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberd多重映射服务" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd 发行-订阅模块" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 字节流模块" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd网页管理" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard模块" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "已被禁止" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "已被踢出" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "因系统关机而被踢出" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "因联属关系改变而被踢出" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "因该房间改为只对会员开放而被踢出" #: mod_muc_log:410 msgid "is now known as" msgstr "现在称呼为" #: mod_muc_log:370 msgid "joins the room" msgstr "加入房间" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "离开房间" #: mod_muc_room:3856 msgid "private, " msgstr "保密, " #: mod_muc_room:3955 msgid "the password is" msgstr "密码是" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard用户搜索" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s访问规则配置" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s邀请你到房间~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s的离线消息队列" #~ msgid "No resource provided" #~ msgstr "无资源提供" #~ msgid "Server" #~ msgstr "服务器" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "来自IP地址(~p)的(~s)失败认证太多. 该地址将在UTC时间~s被禁用." #~ msgid "Please specify file size." #~ msgstr "请指定文件大小." #~ msgid "Please specify file name." #~ msgstr "请指定文件名称." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "此IP地址在~s中已被列入黑名单" #~ msgid "Empty Rooms" #~ msgstr "空房间" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s 无效" #~ msgid "Invalid affiliation: ~s" #~ msgstr "无效加入: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "无效角色: ~s" #~ msgid "No limit" #~ msgstr "不限" #~ msgid "Present real Jabber IDs to" #~ msgstr "将真实Jabber ID显示给" #~ msgid "moderators only" #~ msgstr "仅主持人" #~ msgid "anyone" #~ msgstr "任何人" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "广播存在性的角色" #~ msgid "Moderator" #~ msgstr "主持人" #~ msgid "Participant" #~ msgstr "参与人" #~ msgid "Visitor" #~ msgstr "访客" #~ msgid "nobody" #~ msgstr "没有人" #~ msgid "Allow visitors to send voice requests" #~ msgstr "允许访客发送声音请求" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "声音请求的最小间隔(以秒为单位)" #~ msgid "Enable message archiving" #~ msgstr "启用消息归档" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "从验证码挑战中排除Jabber ID" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "您需要一个兼容 x:data 的客户端来配置房间" #~ msgid "Number of occupants" #~ msgstr "驻留人数" #~ msgid "User JID" #~ msgstr "用户JID" #~ msgid "Grant voice to this person?" #~ msgstr "为此人授权声音?" #~ msgid "Node ID" #~ msgstr "节点ID" #~ msgid "Subscriber Address" #~ msgstr "订阅人地址" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "允许该Jabber ID订阅该pubsub节点?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "用事件通告传输有效负载" #~ msgid "Deliver event notifications" #~ msgstr "传递事件通知" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "当节点设置改变时通知订阅人" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "当节点被删除时通知订阅人" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "当从节点删除内容条目时通知订阅人" #~ msgid "Persist items to storage" #~ msgstr "持久化内容条目" #~ msgid "A friendly name for the node" #~ msgstr "该节点的友好名称" #~ msgid "Max # of items to persist" #~ msgstr "允许持久化的最大内容条目数" #~ msgid "Whether to allow subscriptions" #~ msgstr "是否允许订阅" #~ msgid "Specify the access model" #~ msgstr "指定访问范例" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "允许订阅的花名册组" #~ msgid "Specify the publisher model" #~ msgstr "指定发布人范例" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "相关发布人离线后清除所有选项" #~ msgid "Specify the event message type" #~ msgstr "指定事件消息类型" # bytes was translated as 'bits'. It's corrected now. #~ msgid "Max payload size in bytes" #~ msgstr "最大有效负载字节数" #~ msgid "When to send the last published item" #~ msgstr "何时发送最新发布的内容条目" #~ msgid "Only deliver notifications to available users" #~ msgstr "仅将通知发送给可发送的用户" #~ msgid "The collections with which a node is affiliated" #~ msgstr "加入结点的集合" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "填充字段以搜索任何匹配的Jabber用户" #~ msgid "Outgoing s2s Servers:" #~ msgstr "出站 s2s 服务器" #~ msgid "Delete" #~ msgstr "删除" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "该参与人由于发送了错误消息而被踢出了房间" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "该参与人由于给其他人发送了出错消息而被踢出了房间" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "该用户由于发送了错误状态而被踢出了房间" #~ msgid "Encodings" #~ msgstr "编码" #~ msgid "(Raw)" #~ msgstr "(原始格式)" #~ msgid "Specified nickname is already registered" #~ msgstr "指定的昵称已被注册" #~ msgid "Size" #~ msgstr "大小" ejabberd-18.01/priv/msgs/sv.po0000644000232200023220000016207213225664356016574 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "Last-Translator: Gustaf Alströmer\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Swedish (svenska)\n" "X-Additional-Translator: Thore Alstromer\n" "X-Additional-Translator: Heysan\n" "X-Additional-Translator: Magnus Henoch\n" "X-Additional-Translator: Jonas Ådahl\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " har satt ämnet till: " #: mod_muc_room:1893 #, fuzzy msgid "A password is required to enter this room" msgstr "Lösenord erfordras" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Åtkomstkonfiguration" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfiguera ACL" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "ACL" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Åtkomstregler" #: mod_configure:1095 msgid "Access control lists" msgstr "ACL" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Åtkomst nekad enligt lokal policy" #: mod_configure:1113 msgid "Access rules" msgstr "Åtkomstregler" #: mod_configure:1772 msgid "Action on user" msgstr "Handling mot användare" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Lägg till Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Lägg till ny" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Lägg till användare" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administration" #: mod_configure:1767 msgid "Administration of " msgstr "Administration av " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Administrationsprivilegier krävs" #: mod_configure:507 msgid "All Users" msgstr "Alla användare" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "All aktivitet" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Tillåt användare att byta ämne" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Tillåt användare att söka efter andra användare" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Tillåt användare att skicka inbjudningar" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Tillåt användare att skicka privata meddelanden" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Tillåt gäster att kunna ändra smeknamn" #: mod_muc_log:1050 #, fuzzy msgid "Allow visitors to send private messages to" msgstr "Tillåt användare att skicka privata meddelanden" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Tillåt gäster att skicka statustext som uppdatering" #: mod_announce:611 msgid "Announcements" msgstr "Meddelanden" #: mod_muc_log:476 msgid "April" msgstr "April" #: mod_muc_log:480 msgid "August" msgstr "Augusti" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Säkerhetskopiera" #: mod_configure:584 msgid "Backup Management" msgstr "Hantera säkerhetskopior" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Backup av" #: mod_configure:948 msgid "Backup to File at " msgstr "Säkerhetskopiera till fil på " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Dåligt format" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Födelsedag" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "CPU tid" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Ändra lösenord" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Andra användarlösenord" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Lösenordet är" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Chattrum konfiguration modifierad" #: mod_muc_log:453 #, fuzzy msgid "Chatroom is created" msgstr "Chattrum" #: mod_muc_log:455 #, fuzzy msgid "Chatroom is destroyed" msgstr "Chattrum" #: mod_muc_log:457 #, fuzzy msgid "Chatroom is started" msgstr "Chattrum" #: mod_muc_log:459 #, fuzzy msgid "Chatroom is stopped" msgstr "Chattrum" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Chattrum" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Välj ett användarnamn och lösenord för att registrera mot denna server" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Välj vilka moduler som skall stoppas" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Välj lagringstyp för tabeller" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Välj om du vill godkänna hela denna prenumertion." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Stad" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Kommandon" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Rummet finns inte" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Konfiguration" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Konfiguration för ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Anslutna resurser:" #: mod_irc:526 msgid "Connections parameters" msgstr "Uppkopplingsparametrar" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Land" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Databas" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Databastabellers konfiguration" #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Databas tabell pa" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Databas" #: mod_muc_log:484 msgid "December" msgstr "December" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Gör om användare till deltagare" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Tabort valda" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Ta bort användare" #: mod_announce:629 msgid "Delete message of the day" msgstr "Ta bort dagens meddelande" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Ta bort dagens meddelande på alla värdar" #: mod_shared_roster:900 msgid "Description:" msgstr "Beskrivning:" #: mod_configure:889 msgid "Disc only copy" msgstr "Endast diskkopia" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Visade grupper:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Dumpa säkerhetskopia till textfil på " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Dumpa till textfil" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Redigera egenskaper" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elements" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "Lösenordet är" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Möjliggör login" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Encoding för server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Avsluta användarsession" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Skriv in en lista av {Module, [Options]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Skriv in smeknamnet du vill registrera" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Skriv in sökväg till fil för säkerhetskopia" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Skriv in sökväg till spoolkatalog från jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Skriv in sökväg till spoolfil från jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Skriv in sökväg till textfil" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Skriv in sökväg till textfil" #: mod_irc:759 #, fuzzy msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Skriv in användarnamn och textkodning du vill använda för att ansluta till " "IRC-servrar" #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Skriv in användarnamn och textkodning du vill använda för att ansluta till " "IRC-servrar" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Fel" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Exempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exportera data av alla användare i servern till en PIEFXIS fil (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Exportera data av användare i en host till PIEFXIS fil (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Efternamn" #: mod_muc_log:474 msgid "February" msgstr "Februari" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Fyll i formuläret för att söka efter en användare (lägg till * på slutet av " "fältet för att hitta alla som börjar så)" #: mod_muc_log:467 msgid "Friday" msgstr "Fredag" #: mod_offline:691 msgid "From" msgstr "Från" #: mod_configure:723 msgid "From ~s" msgstr "Från ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Fullständigt namn" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Hämta antal inloggade användare" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Hämta antal registrerade användare" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Hämta användarens senast inloggade tid" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Hämta användarlösenord" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Hämta användarstatistik" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Mellannamn" #: mod_shared_roster:923 msgid "Group " msgstr "Grupp " #: mod_roster:916 msgid "Groups" msgstr "Grupper" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Server" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP adresser" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC transport" #: mod_irc:496 msgid "IRC Username" msgstr "IRC-användarnamn" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC kanal (skriv inte första #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Noden finns inte" #: mod_irc:673 msgid "IRC server" msgstr "IRC-användarnamn" #: mod_irc:756 msgid "IRC settings" msgstr "IRC Inställningar" #: mod_irc:766 msgid "IRC username" msgstr "IRC-användarnamn" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Om du vill specifiera textkodning för IRC-servrar, fyll i listan med värden " "i formatet '{\"irc server\", \"encoding\", port, \"password\"}'. Som " "standard används \"~s\", port ~p, no password." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importera katalog" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importera fil" #: mod_configure:982 msgid "Import User from File at " msgstr "Importera användare från fil på " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importera användare från jabberd14 Spool filer" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importera användare från katalog på " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importera användare från jabberd14 Spool filer" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importera användardata från en PIEFXIS fil (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importera användare från jabberd14 Spool directory:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Felaktig medelandetyp" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Utgående s2s anslutning" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Fel lösenord" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Fel lösenord" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "" "Det är inte tillåtet att skicka privata medelanden till den här konferensen" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Det ar inte tillåtet att skicka privata meddelanden" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "" "Det är inte tillåtet att skicka privata medelanden med typen \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "" "Det är inte tillåtet att skicka privata medelanden till den här konferensen" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Januari" #: mod_irc:665 msgid "Join IRC channel" msgstr "Lägg till IRC kanal" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Lägg till IRC kanal här." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Lägg till IRC kanal till detta Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Juli" #: mod_muc_log:478 msgid "June" msgstr "Juni" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Senast aktivitet" #: mod_configure:1646 msgid "Last login" msgstr "Senaste login" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Senaste månaden" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Senaste året" #: mod_configure:941 msgid "List of modules to start" msgstr "Lista av moduler som skall startas" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Lyssnarport" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Lyssnande portar på " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Uppdaterade laglevel skript" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Gör deltagarlistan publik" #: mod_muc_log:1062 #, fuzzy msgid "Make room CAPTCHA protected" msgstr "Gör losenorden i rummet publika" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Gör om rummet till endast medlemmar" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Gör rummet modererat" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Gör losenorden i rummet publika" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Gör rummet permanent" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Gör rummet publikt sökbart" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "IRC-användarnamn" #: mod_muc_log:475 msgid "March" msgstr "Mars" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Maximalt antal av användare" #: mod_muc_log:477 msgid "May" msgstr "Maj" #: mod_shared_roster:907 msgid "Members:" msgstr "Medlemmar:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Du måste vara medlem för att komma in i det här rummet" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Minne" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Meddelande kropp" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Mellannamn" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Moderatorprivilegier krävs" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Uppdaterade moduler" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduler" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Moduler på" #: mod_muc_log:463 msgid "Monday" msgstr "Måndag" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Namn" #: mod_shared_roster:896 msgid "Name:" msgstr "Namn:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Aldrig" #: mod_register_web:377 #, fuzzy msgid "New Password:" msgstr "Lösenord:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Smeknamn" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registrera smeknamn på " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Smeknamnet ~s existerar inte i det här rummet" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Noden finns inte" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Ingen data" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Ingen kropp behövs för dessa meddelanden" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Noden finns inte" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Noden finns inte" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Noden finns inte" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Noden finns inte" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Nod " #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Noder" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Inga" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Noden finns inte" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "November" #: mod_configure:1212 msgid "Number of online users" msgstr "Antal inloggade användare" #: mod_configure:1202 msgid "Number of registered users" msgstr "Antal registrerade användare" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Oktober" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Offline meddelanden" #: mod_offline:761 msgid "Offline Messages:" msgstr "Offline meddelanden:" #: mod_register_web:373 #, fuzzy msgid "Old Password:" msgstr "Lösenord:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Ansluten" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Anslutna användare" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Inloggade användare" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Endast moderatorer får ändra ämnet i det här rummet" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Endast moderatorer och deltagare har tillåtelse att ändra ämnet i det här " "rummet" #: mod_muc_room:778 #, fuzzy msgid "Only moderators are allowed to change the subject in this room" msgstr "Endast moderatorer får ändra ämnet i det här rummet" #: mod_muc_room:917 #, fuzzy msgid "Only moderators can approve voice requests" msgstr "Tillåt användare att skicka inbjudningar" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Utomstående får inte skicka medelanden till den här konferensen" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Utomstående får inte skicka iq-queries till den här konferensen" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Endast administratörer får skicka tjänstmeddelanden" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Parametrar" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Organisationsnamn" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Organisationsenhet" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Utgaende s2s anslutning" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Utgående s2s anslutning" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Ägarprivilegier krävs" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Lösenord" #: mod_configure:1130 msgid "Password Verification" msgstr "Lösenordsverifikation" #: mod_register_web:268 mod_register_web:381 #, fuzzy msgid "Password Verification:" msgstr "Lösenordsverifikation" #: mod_irc:822 msgid "Password ~b" msgstr "Lösenord ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Lösenord:" #: mod_configure:998 msgid "Path to Dir" msgstr "Sökväg till katalog" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Sökväg till fil" #: mod_roster:915 msgid "Pending" msgstr "Ännu inte godkända" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Period: " #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "lämnar rummet" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Kom ihåg att dessa inställningar endast tar backup pa builtin Mnesias " "databas. Om du använder ODBC modul så måste du ta backup på SQLs databas " "enskilt" #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Pubsub prenumerationsforfrågan" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publikprenumeration" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Det är förbjudet att skicka iq-queries till konferensdeltagare" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM- och diskkopia" #: mod_configure:889 msgid "RAM copy" msgstr "RAM-kopia" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "RPC Uppringningserror" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Ra" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Verkligen ta bort dagens meddelanden?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Mottagaren finns inte i rummet" #: mod_register_web:275 #, fuzzy msgid "Register" msgstr "Kontaktlista" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Registrerade användare" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Registrerade användare" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Registrerade användare" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "mod_irc-registrering för " #: mod_configure:889 msgid "Remote copy" msgstr "Sparas inte lokalt" #: mod_roster:963 msgid "Remove" msgstr "Ta bort" #: mod_offline:765 #, fuzzy msgid "Remove All Offline Messages" msgstr "Offline meddelanden" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Ta bort användare" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Ersatt av ny anslutning" #: mod_configure:1675 msgid "Resources" msgstr "Resurser" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Omstart" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Starta om servicen" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Återställ" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Återställ säkerhetskopia från fil på " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "återställ den binära backupen efter nästa ejabberd omstart" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "återställ den binära backupen omedelbart" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "återställ textbackup omedelbart" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Rumkonfiguration" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Antal besökare" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Skapandet av rum är förbjudet enligt lokal policy" #: mod_muc_log:1064 #, fuzzy msgid "Room description" msgstr "Beskrivning:" #: mod_muc_log:1027 msgid "Room title" msgstr "Rumstitel" #: mod_roster:1082 msgid "Roster" msgstr "Kontaktlista" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Kontaktlista för " #: mod_configure:1671 msgid "Roster size" msgstr "Roster storlek" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Körande noder" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Lördag" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "Din CAPTCHA är godkänd." #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Skript kollat" #: mod_vcard:453 msgid "Search Results for " msgstr "Sökresultat för" #: mod_vcard:427 msgid "Search users in " msgstr "Sök efter användare på " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Sänd meddelanden till alla inloggade användare" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Sänd meddelanden till alla inloggade användare på alla värdar" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Sänd meddelanden till alla användare" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Sänd meddelanden till alla användare på alla värdar" #: mod_muc_log:481 msgid "September" msgstr "September" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 #, fuzzy msgid "Server:" msgstr "Server ~b" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Sätt dagens status meddelande och skicka till alla användare" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Sätt dagens status meddelande pa alla värdar och skicka till alla användare" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Delade Rostergrupper" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Visa kumulativ tabell" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Visa normal tabell" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Stäng ner servicen" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Starta" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Starta moduler" #: mod_configure:936 msgid "Start Modules at " msgstr "Starta moduler på " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistik" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistik på ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Stoppa" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Stanna moduler" #: mod_configure:918 msgid "Stop Modules at " msgstr "Stoppa moduler på " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Stannade noder" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Lagringstyp" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Lagra den binära backupen" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Lagra textbackup" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Ämne" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Skicka" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Skicka in" #: mod_roster:914 msgid "Subscription" msgstr "Prenumeration" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Söndag" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 #, fuzzy msgid "That nickname is already in use by another occupant" msgstr "Smeknamnet används redan" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Smeknamnet är reserverat" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Din CAPTCHA är godkänd." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 #, fuzzy msgid "The CAPTCHA verification has failed" msgstr "Din CAPTCHA är godkänd." #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 #, fuzzy msgid "The password is too weak" msgstr "Lösenordet är" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "" #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Detta rum är inte anonymt" #: mod_muc_log:466 msgid "Thursday" msgstr "Torsdag" #: mod_offline:690 msgid "Time" msgstr "Tid" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Tidsförsening" #: mod_offline:692 msgid "To" msgstr "Till" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Till ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Chattrum" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Trafikgränsen har överstigits" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transaktioner borttagna" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transaktioner kommittade" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transaktioner loggade " #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transaktioner omstartade" #: mod_muc_log:464 msgid "Tuesday" msgstr "Tisdag" #: mod_muc_room:1933 mod_register:244 #, fuzzy msgid "Unable to generate a CAPTCHA" msgstr "Kunde inte generera ett CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Ej auktoriserad" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Uppdatera" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Uppdatera dagens status meddelande (skicka inte)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Uppdatera dagens status meddelande på alla värdar (skicka inte)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Uppdateringsplan" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Uppdatera skript" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Uppdatera" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Tid upp" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Du måste använda STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Du måste använda STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Användarnamn" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Användarmanagement" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Noden finns inte" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Användare " #: mod_register_web:230 mod_register_web:366 mod_register_web:476 #, fuzzy msgid "Username:" msgstr "IRC-användarnamn" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Användare" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Användarens senaste aktivitet" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Det är inte tillåtet för användare att skapa konton så fort" #: mod_roster:954 msgid "Validate" msgstr "Validera" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtuella servrar" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Det är inte tillåtet for gäster att ändra sina smeknamn i detta rummet" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Besökare får inte skicka medelande till alla" #: mod_muc_room:3879 msgid "Voice request" msgstr "" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "" #: mod_muc_log:465 msgid "Wednesday" msgstr "Onsdag" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Du har blivit bannlyst från det här rummet" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Du måste fylla i fält \"smeknamn\" i formen" #: mod_register:222 #, fuzzy msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Du behöver en klient som stödjer x:data för att registrera smeknamn" #: mod_muc:731 #, fuzzy msgid "You need a client that supports x:data to register the nickname" msgstr "Du behöver en klient som stödjer x:data för att registrera smeknamn" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Du behöer en klient som stöjer x:data för att konfigurera mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Du behöver en klient som stödjer x:data, för att kunna söka" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Det ar inte tillåtet att skicka privata meddelanden" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "" #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "Din kontaktkö for offlinekontakter ar full" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Dina meddelanden till ~s är blockerade. För att avblockera dem, gå till ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC-modul" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC modul" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd publikprenumerations modul" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestrem modul" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard-modul" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "har blivit bannad" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "har blivit kickad" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "har blivit kickad p.g.a en systemnerstängning" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "har blivit kickad p.g.a en ändring av tillhörighet" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "har blivit kickad p.g.a att rummet har ändrats till endast användare" #: mod_muc_log:410 msgid "is now known as" msgstr "är känd som" #: mod_muc_log:370 msgid "joins the room" msgstr "joinar rummet" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "lämnar rummet" #: mod_muc_room:3856 msgid "private, " msgstr "privat, " #: mod_muc_room:3955 msgid "the password is" msgstr "Lösenordet är" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard användare sök" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Åtkomstregelkonfiguration för ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s bjöd in dig till rummet ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s's offline meddelandekö" #~ msgid "No resource provided" #~ msgstr "Ingen resurs angiven" #, fuzzy #~ msgid "Server" #~ msgstr "Server ~b" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Otillåtet Jabber ID ~s" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Ogiltlig rang: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Ogiltlig roll: ~s" #~ msgid "No limit" #~ msgstr "Ingen gräns" #~ msgid "Present real Jabber IDs to" #~ msgstr "Nuvarande äkta Jabber IDs till" #~ msgid "moderators only" #~ msgstr "endast moderatorer" #~ msgid "anyone" #~ msgstr "Vemsomhelst" #, fuzzy #~ msgid "Moderator" #~ msgstr "endast moderatorer" #, fuzzy #~ msgid "Allow visitors to send voice requests" #~ msgstr "Tillåt användare att skicka inbjudningar" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Du behöver en klient som stödjer x:data för att konfiguera detta rum" #~ msgid "Number of occupants" #~ msgstr "Antal besökare" #, fuzzy #~ msgid "User JID" #~ msgstr "Användare " #~ msgid "Node ID" #~ msgstr "Node ID" #~ msgid "Subscriber Address" #~ msgstr "Prenumerationsadress" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Tillåt denna Jabber ID att prenumerera på denna pubsub node" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Skicka innehåll tillsammans med notifikationer" #~ msgid "Deliver event notifications" #~ msgstr "Skicka eventnotifikation" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Meddela prenumeranter när nodens konfiguration ändras" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Meddela prenumeranter när noden tas bort" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Meddela prenumeranter när dataposter tas bort från noden" #~ msgid "Persist items to storage" #~ msgstr "Spara dataposter permanent" #~ msgid "A friendly name for the node" #~ msgstr "Ett vänligt namn for noden" #~ msgid "Max # of items to persist" #~ msgstr "Högsta antal dataposter som sparas" #~ msgid "Whether to allow subscriptions" #~ msgstr "Tillåta prenumerationer?" #~ msgid "Specify the access model" #~ msgstr "Specificera accessmodellen" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Rostergrupper tillåts att prenumerera" #~ msgid "Specify the publisher model" #~ msgstr "Ange publiceringsmodell" #, fuzzy #~ msgid "Specify the event message type" #~ msgstr "Specificera accessmodellen" #~ msgid "Max payload size in bytes" #~ msgstr "Högsta innehållsstorlek i bytes" #~ msgid "When to send the last published item" #~ msgstr "När att skicka senast publicerade ämne" #~ msgid "Only deliver notifications to available users" #~ msgstr "Skicka notifikationer bara till uppkopplade användare" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Fyll i fält för att söka efter jabberanvändare" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Utgående s2s server" #~ msgid "Delete" #~ msgstr "Ta bort" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Deltagaren har blivit kickad fran rummet p.g.a att han skickade ett " #~ "errormeddelande" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Deltagaren har blivit kickad från rummet p.g.a att han skickade ett " #~ "errormeddelande till en annan deltagare" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Denna deltagaren är kickad från rummet p.g.a att han skickade en " #~ "errorstatus" ejabberd-18.01/priv/msgs/pt.msg0000644000232200023220000001476513225664356016744 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Configuração de acessos"}. {"Access Control List Configuration","Configuração da Lista de Controlo de Acesso"}. {"Access control lists","Listas de Controlo de Acesso"}. {"Access Control Lists","Listas de Controlo de Acesso"}. {"Access denied by service policy","Acesso negado pela política de serviço"}. {"Access rules","Regras de acesso"}. {"Access Rules","Regras de Acesso"}. {"Action on user","Acção no utilizador"}. {"Add New","Adicionar novo"}. {"Add User","Adicionar utilizador"}. {"Administration of ","Administração de "}. {"Administrator privileges required","São necessários privilégios de administrador"}. {"All Users","Todos os utilizadores"}. {"Backup","Guardar cópia de segurança"}. {"Backup Management","Gestão de cópias de segurança"}. {"Backup to File at ","Guardar cópia de segurança para ficheiro em "}. {"Birthday","Data de nascimento"}. {"Change Password","Mudar palavra-chave"}. {"Choose a username and password to register with this server","Escolha um nome de utilizador e palavra-chave para se registar neste servidor"}. {"Choose modules to stop","Seleccione os módulos a parar"}. {"Choose storage type of tables","Seleccione o tipo de armazenagem das tabelas"}. {"City","Cidade"}. {"Conference room does not exist","A sala não existe"}. {"Configuration","Configuração"}. {"Connected Resources:","Recursos conectados:"}. {"Country","País"}. {"Delete Selected","Eliminar os seleccionados"}. {"Disc only copy","Cópia apenas em disco"}. {"Dump Backup to Text File at ","Exporta cópia de segurança para ficheiro de texto em "}. {"Dump to Text File","Exportar para ficheiro de texto"}. {"Edit Properties","Editar propriedades"}. {"ejabberd IRC module","Módulo de IRC ejabberd"}. {"ejabberd MUC module","Módulo MUC de ejabberd"}. {"ejabberd vCard module","Módulo vCard de ejabberd"}. {"Enter list of {Module, [Options]}","Introduza lista de {módulos, [opções]}"}. {"Enter nickname you want to register","Introduza a alcunha que quer registar"}. {"Enter path to backup file","Introduza o caminho do ficheiro de cópia de segurança"}. {"Enter path to jabberd14 spool dir","Introduza o caminho para o directório de spools do jabberd14"}. {"Enter path to jabberd14 spool file","Introduza o caminho para o ficheiro de spool do jabberd14"}. {"Enter path to text file","Introduza caminho para o ficheiro de texto"}. {"Erlang Jabber Server","Servidor Jabber em Erlang"}. {"Family Name","Apelido"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Nome completo"}. {"Groups","Grupos"}. {" has set the subject to: "," colocou o tópico: "}. {"Import Directory","Importar directório"}. {"Import File","Importar ficheiro"}. {"Import User from File at ","Importar utilizador a partir do ficheiro em "}. {"Import Users from Dir at ","Importar utilizadores a partir do directório em "}. {"Improper message type","Tipo de mensagem incorrecto"}. {"Incorrect password","Palavra-chave incorrecta"}. {"IRC Username","Nome do utilizador de IRC"}. {"It is not allowed to send private messages of type \"groupchat\"","Não é permitido enviar mensagens privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensagens privadas para a sala"}. {"Last Activity","Última actividade"}. {"Listened Ports at ","Portas em escuta em "}. {"List of modules to start","Lista de módulos a iniciar"}. {"Make room moderated","Tornar a sala moderada"}. {"Memory","Memória"}. {"Middle Name","Segundo nome"}. {"Moderator privileges required","São necessários privilégios de moderador"}. {"Module","Módulo"}. {"Modules","Módulos"}. {"Name","Nome"}. {"Never","Nunca"}. {"Nickname","Alcunha"}. {"Nickname Registration at ","Registo da alcunha em "}. {"Nickname ~s does not exist in the room","A alcunha ~s não existe na sala"}. {"Node not found","Nodo não encontrado"}. {"Nodes","Nodos"}. {"None","Nenhum"}. {"OK","OK"}. {"Online","Ligado"}. {"Online Users","Utilizadores ligados"}. {"Only occupants are allowed to send messages to the conference","Só os ocupantes podem enviar mensagens para a sala"}. {"Only occupants are allowed to send queries to the conference","Só os ocupantes podem enviar consultas para a sala"}. {"Only service administrators are allowed to send service messages","Só os administradores do serviço têm permissão para enviar mensagens de serviço"}. {"Options","Opções"}. {"Organization Name","Nome da organização"}. {"Organization Unit","Unidade da organização"}. {"Owner privileges required","São necessários privilégios de dono"}. {"Packet","Pacote"}. {"Password:","Palavra-chave:"}. {"Password","Palavra-chave"}. {"Path to Dir","Caminho para o directório"}. {"Path to File","Caminho do ficheiro"}. {"Pending","Pendente"}. {"Port","Porta"}. {"private, ","privado"}. {"Queries to the conference members are not allowed in this room","Nesta sala não são permitidas consultas aos seus membros"}. {"RAM and disc copy","Cópia em RAM e em disco"}. {"RAM copy","Cópia em RAM"}. {"Recipient is not in the conference room","O destinatário não está na sala"}. {"Registration in mod_irc for ","Registo no mod_irc para"}. {"Remote copy","Cópia remota"}. {"Remove","Remover"}. {"Remove User","Eliminar utilizador"}. {"Restart","Reiniciar"}. {"Restore Backup from File at ","Restaura cópia de segurança a partir do ficheiro em "}. {"Restore","Restaurar"}. {"Room title","Título da sala"}. {"Roster","Lista de contactos"}. {"Roster of ","Lista de contactos de "}. {"Running Nodes","Nodos a correr"}. {"~s access rule configuration","Configuração das Regra de Acesso ~s"}. {"Search users in ","Procurar utilizadores em "}. {"Start Modules at ","Iniciar os módulos em "}. {"Start Modules","Iniciar módulos"}. {"Statistics","Estatísticas"}. {"Stop Modules at ","Parar módulos em "}. {"Stop Modules","Parar módulos"}. {"Stop","Parar"}. {"Stopped Nodes","Nodos parados"}. {"Storage Type","Tipo de armazenagem"}. {"Submit","Enviar"}. {"Subscription","Subscrição"}. {"Time","Data"}. {"To","Para"}. {"To ~s","A ~s"}. {"Update","Actualizar"}. {"Users","Utilizadores"}. {"User","Utilizador"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens para todos os ocupantes"}. {"You have been banned from this room","Foi banido desta sala"}. {"You need an x:data capable client to configure mod_irc settings","É necessário um cliente com suporte de x:data para configurar as opções do mod_irc"}. {"You need an x:data capable client to search","É necessário um cliente com suporte de x:data para poder procurar"}. ejabberd-18.01/priv/msgs/ru.msg0000644000232200023220000010303413225664356016733 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Конфигурация доступа"}. {"Access Control List Configuration","Конфигурация списков управления доступом"}. {"Access control lists","Списки управления доступом"}. {"Access Control Lists","Списки управления доступом"}. {"Access denied by service policy","Доступ запрещён политикой службы"}. {"Access rules","Правила доступа"}. {"Access Rules","Правила доступа"}. {"Action on user","Действие над пользователем"}. {"Add Jabber ID","Добавить Jabber ID"}. {"Add New","Добавить"}. {"Add User","Добавить пользователя"}. {"Administration of ","Администрирование "}. {"Administration","Администрирование"}. {"Administrator privileges required","Требуются права администратора"}. {"All activity","Вся статистика"}. {"Allow users to change the subject","Разрешить пользователям изменять тему"}. {"Allow users to query other users","Разрешить iq-запросы к пользователям"}. {"Allow users to send invites","Разрешить пользователям посылать приглашения"}. {"Allow users to send private messages","Разрешить приватные сообщения"}. {"Allow visitors to change nickname","Разрешить посетителям изменять псевдоним"}. {"Allow visitors to send private messages to","Разрешить посетителям посылать приватные сообщения"}. {"Allow visitors to send status text in presence updates","Разрешить посетителям вставлять текcт статуса в сообщения о присутствии"}. {"All Users","Все пользователи"}. {"Announcements","Объявления"}. {"A password is required to enter this room","Чтобы войти в эту конференцию, нужен пароль"}. {"April","апреля"}. {"August","августа"}. {"Backup Management","Управление резервным копированием"}. {"Backup of ~p","Резервное копирование ~p"}. {"Backup to File at ","Резервное копирование в файл на "}. {"Backup","Резервное копирование"}. {"Bad format","Неправильный формат"}. {"Birthday","День рождения"}. {"CAPTCHA web page","Ссылка на капчу"}. {"Change Password","Сменить пароль"}. {"Change User Password","Изменить пароль пользователя"}. {"Characters not allowed:","Недопустимые символы:"}. {"Chatroom configuration modified","Конфигурация комнаты изменилась"}. {"Chatroom is created","Комната создана"}. {"Chatroom is destroyed","Комната уничтожена"}. {"Chatroom is started","Комната запущена"}. {"Chatroom is stopped","Комната остановлена"}. {"Chatrooms","Комнаты"}. {"Choose a username and password to register with this server","Выберите имя пользователя и пароль для регистрации на этом сервере"}. {"Choose modules to stop","Выберите модули, которые следует остановить"}. {"Choose storage type of tables","Выберите тип хранения таблиц"}. {"Choose whether to approve this entity's subscription.","Решите: предоставить ли подписку этому объекту."}. {"City","Город"}. {"Commands","Команды"}. {"Conference room does not exist","Конференция не существует"}. {"Configuration of room ~s","Конфигурация комнаты ~s"}. {"Configuration","Конфигурация"}. {"Connected Resources:","Подключённые ресурсы:"}. {"Connections parameters","Параметры соединения"}. {"Country","Страна"}. {"CPU Time:","Процессорное время:"}. {"Database Tables at ~p","Таблицы базы данных на ~p"}. {"Database Tables Configuration at ","Конфигурация таблиц базы данных на "}. {"Database","База данных"}. {"December","декабря"}. {"Default users as participants","Сделать пользователей участниками по умолчанию"}. {"Delete message of the day on all hosts","Удалить сообщение дня со всех виртуальных серверов"}. {"Delete message of the day","Удалить сообщение дня"}. {"Delete Selected","Удалить выделенные"}. {"Delete User","Удалить пользователя"}. {"Description:","Описание:"}. {"Disc only copy","только диск"}. {"Displayed Groups:","Видимые группы:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Не говорите никому свой пароль, даже администраторам сервера."}. {"Dump Backup to Text File at ","Копирование в текстовый файл на "}. {"Dump to Text File","Копирование в текстовый файл"}. {"Edit Properties","Изменить параметры"}. {"Either approve or decline the voice request.","Подтвердите или отклоните право голоса."}. {"ejabberd IRC module","ejabberd IRC модуль"}. {"ejabberd MUC module","ejabberd MUC модуль"}. {"ejabberd Multicast service","ejabberd Multicast сервис"}. {"ejabberd Publish-Subscribe module","Модуль ejabberd Публикации-Подписки"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams модуль"}. {"ejabberd vCard module","ejabberd vCard модуль"}. {"ejabberd Web Admin","Web-интерфейс администрирования ejabberd"}. {"Elements","Элементы"}. {"Email","Электронная почта"}. {"Enable logging","Включить журналирование"}. {"Encoding for server ~b","Кодировка сервера ~b"}. {"End User Session","Завершить сеанс пользователя"}. {"Enter list of {Module, [Options]}","Введите список вида {Module, [Options]}"}. {"Enter nickname you want to register","Введите псевдоним, который Вы хотели бы зарегистрировать"}. {"Enter path to backup file","Введите путь к резервному файлу"}. {"Enter path to jabberd14 spool dir","Введите путь к директории спула jabberd14"}. {"Enter path to jabberd14 spool file","Введите путь к файлу из спула jabberd14"}. {"Enter path to text file","Введите путь к текстовому файлу"}. {"Enter the text you see","Введите увиденный текст"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Введите имя пользователя и кодировки, которые будут использоваться при подключении к IRC-серверам. Нажмите 'Далее' для получения дополнительных полей для заполнения. Нажмите 'Завершить' для сохранения настроек."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Введите имя пользователя, кодировки, порты и пароли, которые будут использоваться при подключении к IRC-серверам"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Ошибка"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Пример: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Экспортировать все таблицы в виде SQL запросов в файл:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Экспорт данных всех пользователей сервера в файлы формата PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Экспорт пользовательских данных домена в файлы формата PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Ошибка обработки JID из вашего запроса на право голоса"}. {"Family Name","Фамилия"}. {"February","февраля"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Заполните форму для поиска пользователя Jabber (Если добавить * в конец поля, то происходит поиск подстроки)"}. {"Friday","Пятница"}. {"From ~s","От ~s"}. {"From","От кого"}. {"Full Name","Полное имя"}. {"Get Number of Online Users","Получить количество подключённых пользователей"}. {"Get Number of Registered Users","Получить количество зарегистрированных пользователей"}. {"Get User Last Login Time","Получить время последнего подключения пользователя"}. {"Get User Password","Получить пароль пользователя"}. {"Get User Statistics","Получить статистику по пользователю"}. {"Groups","Группы"}. {"Group ","Группа "}. {"has been banned","запретили входить в комнату"}. {"has been kicked because of an affiliation change","выгнали из комнаты вследствие смены ранга"}. {"has been kicked because of a system shutdown","выгнали из комнаты из-за останова системы"}. {"has been kicked because the room has been changed to members-only","выгнали из комнаты потому что она стала только для членов"}. {"has been kicked","выгнали из комнаты"}. {" has set the subject to: "," установил(а) тему: "}. {"Host","Хост"}. {"If you don't see the CAPTCHA image here, visit the web page.","Если вы не видите изображение капчи, перейдите по ссылке."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Чтобы указать различные порты, пароли, кодировки для разных серверов IRC, заполните список значениями в формате '{\"сервер IRC\", \"кодировка\", порт, \"пароль\"}'. По умолчанию сервис использует кодировку \"~s\", порт ~p, пустой пароль."}. {"Import Directory","Импорт из директории"}. {"Import File","Импорт из файла"}. {"Import user data from jabberd14 spool file:","Импорт пользовательских данных из буферного файла jabberd14:"}. {"Import User from File at ","Импорт пользователя из файла на "}. {"Import users data from a PIEFXIS file (XEP-0227):","Импорт пользовательских данных из файла формата PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Импорт пользовательских данных из буферной директории jabberd14:"}. {"Import Users from Dir at ","Импорт пользователей из директории на "}. {"Import Users From jabberd14 Spool Files","Импорт пользователей из спула jabberd14"}. {"Improper message type","Неправильный тип сообщения"}. {"Incorrect password","Неправильный пароль"}. {"IP addresses","IP адреса"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Канал IRC (без символа #)"}. {"IRC server","Сервер IRC"}. {"IRC settings","Настройки IRC"}. {"IRC Transport","IRC Транспорт"}. {"IRC username","Имя пользователя IRC"}. {"IRC Username","Имя пользователя IRC"}. {"is now known as","изменил(а) имя на"}. {"It is not allowed to send private messages of type \"groupchat\"","Нельзя посылать частные сообщения типа \"groupchat\""}. {"It is not allowed to send private messages to the conference","Не разрешается посылать частные сообщения прямо в конференцию"}. {"It is not allowed to send private messages","Запрещено посылать приватные сообщения"}. {"Jabber Account Registration","Регистрация Jabber-аккаунта"}. {"Jabber ID","Jabber ID"}. {"January","января"}. {"Join IRC channel","Присоединиться к каналу IRC"}. {"joins the room","вошёл(а) в комнату"}. {"Join the IRC channel here.","Присоединяйтесь к каналу IRC"}. {"Join the IRC channel in this Jabber ID: ~s","Присоединиться к каналу IRC с Jabber ID: ~s"}. {"July","июля"}. {"June","июня"}. {"Last Activity","Последнее подключение"}. {"Last login","Время последнего подключения"}. {"Last month","За последний месяц"}. {"Last year","За последний год"}. {"leaves the room","вышел(а) из комнаты"}. {"Listened Ports at ","Прослушиваемые порты на "}. {"Listened Ports","Прослушиваемые порты"}. {"List of modules to start","Список запускаемых модулей"}. {"List of rooms","Список комнат"}. {"Low level update script","Низкоуровневый сценарий обновления"}. {"Make participants list public","Сделать список участников видимым всем"}. {"Make room CAPTCHA protected","Сделать комнату защищённой капчей"}. {"Make room members-only","Комната только для зарегистрированных участников"}. {"Make room moderated","Сделать комнату модерируемой"}. {"Make room password protected","Сделать комнату защищённой паролем"}. {"Make room persistent","Сделать комнату постоянной"}. {"Make room public searchable","Сделать комнату видимой всем"}. {"March","марта"}. {"Maximum Number of Occupants","Максимальное количество участников"}. {"May","мая"}. {"Membership is required to enter this room","В эту конференцию могут входить только её члены"}. {"Members:","Члены:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Запомните пароль или запишите его на бумаге, которую сохраните в безопасном месте. В Jabber'е нет автоматизированного средства восстановления пароля в том случае, если Вы его забудете."}. {"Memory","Память"}. {"Message body","Тело сообщения"}. {"Middle Name","Отчество"}. {"Moderator privileges required","Требуются права модератора"}. {"Modified modules","Изменённые модули"}. {"Modules at ~p","Модули на ~p"}. {"Modules","Модули"}. {"Module","Модуль"}. {"Monday","Понедельник"}. {"Multicast","Мультикаст"}. {"Multi-User Chat","Конференция"}. {"Name:","Название:"}. {"Name","Название"}. {"Never","Никогда"}. {"New Password:","Новый пароль:"}. {"Nickname Registration at ","Регистрация псевдонима на "}. {"Nickname ~s does not exist in the room","Псевдоним ~s в комнате отсутствует"}. {"Nickname","Псевдоним"}. {"No body provided for announce message","Тело объявления не должно быть пустым"}. {"No Data","Нет данных"}. {"Node not found","Узел не найден"}. {"Node ~p","Узел ~p"}. {"Nodes","Узлы"}. {"None","Нет"}. {"Not Found","Не Найдено"}. {"November","ноября"}. {"Number of online users","Количество подключённых пользователей"}. {"Number of registered users","Количество зарегистрированных пользователей"}. {"October","октября"}. {"Offline Messages:","Офлайновые сообщения:"}. {"Offline Messages","Офлайновые сообщения"}. {"OK","Продолжить"}. {"Old Password:","Старый пароль:"}. {"Online Users:","Подключённые пользователи:"}. {"Online Users","Подключённые пользователи"}. {"Online","Подключён"}. {"Only moderators and participants are allowed to change the subject in this room","Только модераторы и участники могут изменять тему в этой комнате"}. {"Only moderators are allowed to change the subject in this room","Только модераторы могут изменять тему в этой комнате"}. {"Only moderators can approve voice requests","Только модераторы могут утверждать запросы на право голоса"}. {"Only occupants are allowed to send messages to the conference","Только присутствующим разрешается посылать сообщения в конференцию"}. {"Only occupants are allowed to send queries to the conference","Только присутствующим разрешается посылать запросы в конференцию"}. {"Only service administrators are allowed to send service messages","Только администратор службы может посылать служебные сообщения"}. {"Options","Параметры"}. {"Organization Name","Название организации"}. {"Organization Unit","Отдел организации"}. {"Outgoing s2s Connections:","Исходящие s2s-серверы:"}. {"Outgoing s2s Connections","Исходящие s2s-соединения"}. {"Owner privileges required","Требуются права владельца"}. {"Packet","Пакет"}. {"Password ~b","Пароль ~b"}. {"Password Verification:","Проверка пароля:"}. {"Password Verification","Проверка пароля"}. {"Password:","Пароль:"}. {"Password","Пароль"}. {"Path to Dir","Путь к директории"}. {"Path to File","Путь к файлу"}. {"Pending","Ожидание"}. {"Period: ","Период"}. {"Permanent rooms","Постоянные комнаты"}. {"Ping","Пинг"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Заметьте, что здесь производится резервное копирование только встроенной базы данных Mnesia. Если Вы также используете другое хранилище данных (например с помощью модуля ODBC), то его резервное копирование следует осуществлять отдельно."}. {"Please, wait for a while before sending new voice request","Пожалуйста, подождите перед тем как подать новый запрос на право голоса"}. {"Pong","Понг"}. {"Port ~b","Порт ~b"}. {"Port","Порт"}. {"private, ","приватная, "}. {"Protocol","Протокол"}. {"Publish-Subscribe","Публикация-Подписка"}. {"PubSub subscriber request","Запрос подписчика PubSub"}. {"Queries to the conference members are not allowed in this room","Запросы к пользователям в этой конференции запрещены"}. {"RAM and disc copy","ОЗУ и диск"}. {"RAM copy","ОЗУ"}. {"Raw","Необработанный формат"}. {"Really delete message of the day?","Действительно удалить сообщение дня?"}. {"Recipient is not in the conference room","Адресата нет в конференции"}. {"Register a Jabber account","Зарегистрировать Jabber-аккаунт"}. {"Registered nicknames","Зарегистрированные псевдонимы"}. {"Registered Users:","Зарегистрированные пользователи:"}. {"Registered Users","Зарегистрированные пользователи"}. {"Register","Зарегистрировать"}. {"Registration in mod_irc for ","Регистрация в mod_irc для "}. {"Remote copy","не хранится локально"}. {"Remove All Offline Messages","Удалить все офлайновые сообщения"}. {"Remove User","Удалить пользователя"}. {"Remove","Удалить"}. {"Replaced by new connection","Заменено новым соединением"}. {"Resources","Ресурсы"}. {"Restart Service","Перезапустить службу"}. {"Restart","Перезапустить"}. {"Restore Backup from File at ","Восстановление из резервной копии на "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Восстановить из бинарной резервной копии при следующем запуске (требует меньше памяти):"}. {"Restore binary backup immediately:","Восстановить из бинарной резервной копии немедленно:"}. {"Restore plain text backup immediately:","Восстановить из текстовой резервной копии немедленно:"}. {"Restore","Восстановление из резервной копии"}. {"Room Configuration","Конфигурация комнаты"}. {"Room creation is denied by service policy","Cоздавать конференцию запрещено политикой службы"}. {"Room description","Описание комнаты"}. {"Room Occupants","Участники комнаты"}. {"Room title","Название комнаты"}. {"Roster of ","Ростер пользователя "}. {"Roster size","Размер списка контактов"}. {"Roster","Ростер"}. {"RPC Call Error","Ошибка вызова RPC"}. {"Running Nodes","Работающие узлы"}. {"~s access rule configuration","Конфигурация правила доступа ~s"}. {"Saturday","Суббота"}. {"Script check","Проверка сценария"}. {"Search Results for ","Результаты поиска в "}. {"Search users in ","Поиск пользователей в "}. {"Send announcement to all online users on all hosts","Разослать объявление всем подключённым пользователям на всех виртуальных серверах"}. {"Send announcement to all online users","Разослать объявление всем подключённым пользователям"}. {"Send announcement to all users on all hosts","Разослать объявление всем пользователям на всех виртуальных серверах"}. {"Send announcement to all users","Разослать объявление всем пользователям"}. {"September","сентября"}. {"Server ~b","Сервер ~b"}. {"Server:","Сервер:"}. {"Set message of the day and send to online users","Установить сообщение дня и разослать его подключённым пользователям"}. {"Set message of the day on all hosts and send to online users","Установить сообщение дня на всех виртуальных серверах и разослать его подключённым пользователям"}. {"Shared Roster Groups","Группы общих контактов"}. {"Show Integral Table","Показать интегральную таблицу"}. {"Show Ordinary Table","Показать обычную таблицу"}. {"Shut Down Service","Остановить службу"}. {"~s invites you to the room ~s","~s приглашает вас в комнату ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Некоторые Jabber-клиенты могут сохранять пароль на Вашем компьютере. Используйте эту функцию только в том случае, если считаете это безопасным."}. {"~s's Offline Messages Queue","Oчередь офлайновых сообщений ~s"}. {"Start Modules at ","Запуск модулей на "}. {"Start Modules","Запуск модулей"}. {"Start","Запустить"}. {"Statistics of ~p","статистика узла ~p"}. {"Statistics","Статистика"}. {"Stop Modules at ","Остановка модулей на "}. {"Stop Modules","Остановка модулей"}. {"Stopped Nodes","Остановленные узлы"}. {"Stop","Остановить"}. {"Storage Type","Тип таблицы"}. {"Store binary backup:","Сохранить бинарную резервную копию:"}. {"Store plain text backup:","Сохранить текстовую резервную копию:"}. {"Subject","Тема"}. {"Submitted","Отправлено"}. {"Submit","Отправить"}. {"Subscription","Подписка"}. {"Sunday","Воскресенье"}. {"That nickname is already in use by another occupant","Этот псевдоним уже занят другим участником"}. {"That nickname is registered by another person","Этот псевдоним зарегистрирован кем-то другим"}. {"The CAPTCHA is valid.","Проверка капчи прошла успешно."}. {"The CAPTCHA verification has failed","Проверка капчи не пройдена"}. {"The password is too weak","Слишком слабый пароль"}. {"the password is","пароль:"}. {"The password of your Jabber account was successfully changed.","Пароль Вашего Jabber-аккаунта был успешно изменен."}. {"There was an error changing the password: ","Ошибка при смене пароля:"}. {"There was an error creating the account: ","Ошибка при создании аккаунта:"}. {"There was an error deleting the account: ","Ошибка при удалении аккаунта:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Регистр не имеет значения: \"маша\" и \"МАША\" будет считаться одним и тем же именем."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Здесь Вы можете создать Jabber-аккаунт на этом Jabber-сервере. Ваш JID (Jabber-идентификатор) будет в виде: \"пользователь@сервер\". Пожалуйста, внимательно читайте инструкции для правильного заполнения полей."}. {"This page allows to unregister a Jabber account in this Jabber server.","Здесь Вы можете удалить Jabber-аккаунт с этого сервера."}. {"This room is not anonymous","Эта комната не анонимная"}. {"Thursday","Четверг"}. {"Time delay","По истечение"}. {"Time","Время"}. {"Too many CAPTCHA requests","Слишком много запросов капчи"}. {"Too many unacked stanzas","Слишком много неподтверждённых пакетов"}. {"To ~s","К ~s"}. {"Total rooms","Все комнаты"}. {"To","Кому"}. {"Traffic rate limit is exceeded","Превышен лимит скорости посылки информации"}. {"Transactions Aborted:","Транзакции отмененные:"}. {"Transactions Committed:","Транзакции завершенные:"}. {"Transactions Logged:","Транзакции запротоколированные:"}. {"Transactions Restarted:","Транзакции перезапущенные:"}. {"Tuesday","Вторник"}. {"Unable to generate a CAPTCHA","Не получилось создать капчу"}. {"Unauthorized","Не авторизован"}. {"Unregister a Jabber account","Удалить Jabber-аккаунт"}. {"Unregister","Удалить"}. {"Update message of the day (don't send)","Обновить сообщение дня (не рассылать)"}. {"Update message of the day on all hosts (don't send)","Обновить сообщение дня на всех виртуальных серверах (не рассылать)"}. {"Update plan","План обновления"}. {"Update ~p","Обновление ~p"}. {"Update script","Сценарий обновления"}. {"Update","Обновить"}. {"Uptime:","Время работы:"}. {"Use of STARTTLS required","Вы обязаны использовать STARTTLS"}. {"User Management","Управление пользователями"}. {"Username:","Имя пользователя:"}. {"Users are not allowed to register accounts so quickly","Пользователи не могут регистрировать учётные записи так быстро"}. {"Users Last Activity","Статистика последнего подключения пользователей"}. {"Users","Пользователи"}. {"User ~s","Пользователь ~s"}. {"User","Пользователь"}. {"Validate","Утвердить"}. {"vCard User Search","Поиск пользователей по vCard"}. {"Virtual Hosts","Виртуальные хосты"}. {"Visitors are not allowed to change their nicknames in this room","Посетителям запрещено изменять свои псевдонимы в этой комнате"}. {"Visitors are not allowed to send messages to all occupants","Посетителям не разрешается посылать сообщения всем присутствующим"}. {"Voice requests are disabled in this conference","Запросы на право голоса отключены в этой конференции"}. {"Voice request","Запрос на право голоса"}. {"Wednesday","Среда"}. {"You can later change your password using a Jabber client.","Позже Вы можете изменить пароль через Jabber-клиент."}. {"You have been banned from this room","Вам запрещено входить в эту конференцию"}. {"You must fill in field \"Nickname\" in the form","Вы должны заполнить поле \"Псевдоним\" в форме"}. {"You need a client that supports x:data and CAPTCHA to register","Чтобы зарегистрироваться, требуется x:data-совместимый клиент"}. {"You need a client that supports x:data to register the nickname","Чтобы зарегистрировать псевдоним, требуется x:data-совместимый клиент"}. {"You need an x:data capable client to configure mod_irc settings","Чтобы настроить параметры mod_irc, требуется x:data-совместимый клиент"}. {"You need an x:data capable client to search","Чтобы воспользоваться поиском, требуется x:data-совместимый клиент"}. {"Your active privacy list has denied the routing of this stanza.","Маршрутизация этой строфы запрещена вашим активным списком приватности."}. {"Your contact offline message queue is full. The message has been discarded.","Очередь недоставленных сообщений Вашего адресата переполнена. Сообщение не было сохранено."}. {"Your Jabber account was successfully created.","Ваш Jabber-аккаунт был успешно создан."}. {"Your Jabber account was successfully deleted.","Ваш Jabber-аккаунт был успешно удален."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Ваши сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s"}. ejabberd-18.01/priv/msgs/id.po0000644000232200023220000016651513225664356016546 0ustar debalancedebalance# , 2010. msgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "PO-Revision-Date: 2011-02-15 07:09+0800\n" "Last-Translator: Irfan Mahfudz Guntur \n" "Language-Team: SmartCommunity \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Indonesian (Bahasa Indonesia)\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr "telah menetapkan topik yaitu:" #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Diperlukan kata sandi untuk masuk ruangan ini" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Akses Konfigurasi" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfigurasi Daftar Akses Pengendalian" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Akses Daftar Pengendalian" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Aturan Akses" #: mod_configure:1095 msgid "Access control lists" msgstr "Daftar Pengendalian Akses" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Akses ditolak oleh kebijakan layanan" #: mod_configure:1113 msgid "Access rules" msgstr "Akses peraturan" #: mod_configure:1772 msgid "Action on user" msgstr "Tindakan pada pengguna" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Tambah Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Tambah Baru" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Tambah Pengguna" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administrasi" #: mod_configure:1767 msgid "Administration of " msgstr "Administrasi" #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Hak istimewa Administrator dibutuhkan" #: mod_configure:507 msgid "All Users" msgstr "Semua Pengguna" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Semua aktifitas" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Perbolehkan pengguna untuk mengganti topik" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Perbolehkan pengguna untuk mengetahui pengguna lain" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Perbolehkan pengguna mengirimkan undangan" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "perbolehkan pengguna mengirimkan pesan ke pengguna lain secara pribadi" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Perbolehkan visitor mengganti nama julukan" #: mod_muc_log:1050 #, fuzzy msgid "Allow visitors to send private messages to" msgstr "perbolehkan pengguna mengirimkan pesan ke pengguna lain secara pribadi" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Izinkan pengunjung untuk mengirim teks status terbaru" #: mod_announce:611 msgid "Announcements" msgstr "Pengumuman" #: mod_muc_log:476 msgid "April" msgstr "April" #: mod_muc_log:480 msgid "August" msgstr "Agustus" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Backup" #: mod_configure:584 msgid "Backup Management" msgstr "Manajemen Backup" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Cadangan dari" #: mod_configure:948 msgid "Backup to File at " msgstr "Backup ke File pada" #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Format yang buruk" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Hari Lahir" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA laman web" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Waktu CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Ubah Kata Sandi" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Ubah User Password" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Karakter tidak diperbolehkan:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Karakter tidak diperbolehkan:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Karakter tidak diperbolehkan:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Konfigurasi ruang chat diubah" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Ruang chat telah dibuat" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Ruang chat dilenyapkan" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Ruang chat dimulai" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Ruang chat dihentikan" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Ruangan Chat" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Pilih nama pengguna dan kata sandi untuk mendaftar dengan layanan ini" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Pilih Modul untuk berhenti" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Pilih jenis penyimpanan tabel" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Pilih apakah akan menyetujui hubungan pertemanan ini." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Kota" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Perintah" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Ruang Konferensi tidak ada" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Pengaturan" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Pengaturan ruangan ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Sumber Daya Terhubung:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parameter Koneksi" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Negara" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Database" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Database Tabel Konfigurasi pada" #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Tabel Database pada" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Database" #: mod_muc_log:484 msgid "December" msgstr "Desember" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "pengguna pertama kali masuk sebagai participant" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Hapus Yang Terpilih" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Hapus Pengguna" #: mod_announce:629 msgid "Delete message of the day" msgstr "Hapus pesan harian" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Hapus pesan harian pada semua host" #: mod_shared_roster:900 msgid "Description:" msgstr "Keterangan:" #: mod_configure:889 msgid "Disc only copy" msgstr "Hanya salinan dari disc" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Tampilkan Grup:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Jangan memberitahukan kata sandi Anda ke siapapun, bahkan para administrator " "dari layanan Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Dump Backup ke File Teks di" #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Dump menjadi File Teks" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Ganti Properti" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elemen-elemen" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "kata sandi yaitu:" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Aktifkan catatan" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Pengkodean untuk layanan ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Akhir Sesi Pengguna" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Masukkan daftar {Modul, [Options]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Masukkan nama julukan Anda jika ingin mendaftar" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Masukkan path untuk file cadangan" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Masukkan path ke direktori spool jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Masukkan path ke file jabberd14 spool" #: mod_configure:974 msgid "Enter path to text file" msgstr "Masukkan path ke file teks" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Masukkan teks yang Anda lihat" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Masukkan username dan pengkodean yang ingin Anda gunakan untuk menghubungkan " "ke layanan IRC. Tekan 'Selanjutnya' untuk mendapatkan lagi formulir kemudian " "Tekan 'Lengkap' untuk menyimpan pengaturan." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Masukkan username, pengkodean, port dan sandi yang ingin Anda gunakan untuk " "menghubungkan ke layanan IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Layanan Erlang Jabber" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Kesalahan" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Contoh: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Ekspor data dari semua pengguna pada layanan ke berkas PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Ekspor data pengguna pada sebuah host ke berkas PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Nama Keluarga (marga)" #: mod_muc_log:474 msgid "February" msgstr "Februari" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Isi formulir untuk pencarian pengguna Jabber yang cocok (Tambahkan * ke " "mengakhiri pengisian untuk menyamakan kata)" #: mod_muc_log:467 msgid "Friday" msgstr "Jumat" #: mod_offline:691 msgid "From" msgstr "Dari" #: mod_configure:723 msgid "From ~s" msgstr "Dari ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nama Lengkap" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Dapatkan Jumlah User Yang Online" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Dapatkan Jumlah Pengguna Yang Terdaftar" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Dapatkan Waktu Login Terakhir Pengguna " #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Dapatkan User Password" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Dapatkan Statistik Pengguna" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Nama Tengah" #: mod_shared_roster:923 msgid "Group " msgstr "Grup" #: mod_roster:916 msgid "Groups" msgstr "Grup" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Alamat IP" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Transport" #: mod_irc:496 msgid "IRC Username" msgstr "Nama Pengguna IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Channel IRC (tidak perlu menempatkan # sebelumnya)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Node tidak ditemukan" #: mod_irc:673 msgid "IRC server" msgstr "Layanan IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Pengaturan IRC" #: mod_irc:766 msgid "IRC username" msgstr "Nama Pengguna IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" "Jika Anda tidak melihat gambar CAPTCHA disini, silahkan kunjungi halaman web." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Jika Anda ingin menentukan port yang berbeda, sandi, pengkodean untuk " "layanan IRC, isi daftar ini dengan nilai-nilai dalam format '{\"server irc " "\", \"encoding \", port, \"sandi \"}'. Secara default ini menggunakan " "layanan \"~s \" pengkodean, port ~p, kata sandi kosong." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Impor Direktori" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Impor File" #: mod_configure:982 msgid "Import User from File at " msgstr "Impor Pengguna dari File pada" #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Impor Pengguna Dari jabberd14 Spool File" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Impor Pengguna dari Dir di" #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Impor data pengguna dari sekumpulan berkas jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "impor data-data pengguna dari sebuah PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Импорт пользовательских данных из буферной директории jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Jenis pesan yang tidak benar" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Koneksi s2s yang keluar:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Kata sandi salah" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Kata sandi salah" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi ke konferensi" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "" "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi jenis \"groupchat \"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi ke konferensi" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Pendaftaran Akun Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Januari" #: mod_irc:665 msgid "Join IRC channel" msgstr "Gabung channel IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Gabung ke channel IRC disini" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Gabung ke channel IRC dengan Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Juli" #: mod_muc_log:478 msgid "June" msgstr "Juni" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Aktifitas Terakhir" #: mod_configure:1646 msgid "Last login" msgstr "Terakhir Login" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Akhir bulan" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Akhir tahun" #: mod_configure:941 msgid "List of modules to start" msgstr "Daftar modul untuk memulai" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Port Terdeteksi" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Mendeteksi Port-port di" #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Perbaruan naskah tingkat rendah" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Buat daftar participant diketahui oleh public" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Buat ruangan dilindungi dengan CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Buat ruangan hanya untuk member saja" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Buat ruangan hanya untuk moderator saja" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Buat ruangan yang dilindungi dengan kata sandi" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Buat ruangan menjadi permanent" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Buat ruangan dapat dicari" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Nama Pengguna IRC" #: mod_muc_log:475 msgid "March" msgstr "Maret" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Maksimum Jumlah Penghuni" #: mod_muc_log:477 msgid "May" msgstr "Mei" #: mod_shared_roster:907 msgid "Members:" msgstr "Anggota:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Hanya Member yang dapat masuk ruangan ini" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Hafalkan kata sandi Anda, atau dicatat dan letakkan di tempat yang aman. " "Didalam Jabber tidak ada cara otomatis untuk mendapatkan kembali password " "Anda jika Anda lupa." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memori" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Isi Pesan" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Nama Tengah" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Hak istimewa moderator dibutuhkan" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Modifikasi modul-modul" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Modul" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "modul-modul di" #: mod_muc_log:463 msgid "Monday" msgstr "Senin" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nama" #: mod_shared_roster:896 msgid "Name:" msgstr "Nama:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Tidak Pernah" #: mod_register_web:377 msgid "New Password:" msgstr "Password Baru:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Nama Julukan" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Pendaftaran Julukan pada" #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Nama Julukan ~s tidak berada di dalam ruangan" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Node tidak ditemukan" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Tidak Ada Data" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Tidak ada isi pesan yang disediakan untuk mengirimkan pesan" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Node tidak ditemukan" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Node tidak ditemukan" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Node tidak ditemukan" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Node tidak ditemukan" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Node" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Node-node" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Tak satupun" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Tidak Ditemukan" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "Nopember" #: mod_configure:1212 msgid "Number of online users" msgstr "Jumlah pengguna online" #: mod_configure:1202 msgid "Number of registered users" msgstr "Jumlah pengguna terdaftar" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "YA" #: mod_muc_log:482 msgid "October" msgstr "Oktober" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Pesan Offline" #: mod_offline:761 msgid "Offline Messages:" msgstr "Pesan Offline:" #: mod_register_web:373 msgid "Old Password:" msgstr "Password Lama:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Online" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Pengguna Yang Online" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Pengguna Online:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "" "Hanya moderator yang diperbolehkan untuk mengubah topik dalam ruangan ini" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Hanya moderator dan peserta yang diizinkan untuk mengganti topik pembicaraan " "di ruangan ini" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "" "Hanya moderator yang diperbolehkan untuk mengubah topik dalam ruangan ini" #: mod_muc_room:917 #, fuzzy msgid "Only moderators can approve voice requests" msgstr "Perbolehkan pengguna mengirimkan undangan" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Hanya penghuni yang diizinkan untuk mengirim pesan ke konferensi" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Hanya penghuni diizinkan untuk mengirim permintaan ke konferensi" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Layanan hanya diperuntukan kepada administrator yang diizinkan untuk " "mengirim layanan pesan" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Pilihan-pilihan" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nama Organisasi" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unit Organisasi" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Koneksi Keluar s2s" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Koneksi s2s yang keluar:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Hak istimewa owner dibutuhkan" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Sandi" #: mod_configure:1130 msgid "Password Verification" msgstr "Verifikasi Sandi" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Verifikasi Kata Sandi:" #: mod_irc:822 msgid "Password ~b" msgstr "Kata Sandi ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Kata Sandi:" #: mod_configure:998 msgid "Path to Dir" msgstr "Jalur ke Dir" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Jalur ke File" #: mod_roster:915 msgid "Pending" msgstr "Tertunda" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periode:" #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "meninggalkan ruangan" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Harap dicatat bahwa pilihan ini hanya akan membuat cadangan builtin Mnesia " "database. Jika Anda menggunakan modul ODBC, anda juga perlu untuk membuat " "cadangan database SQL Anda secara terpisah." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Permintaan pertemanan PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Setujui-Pertemanan" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Permintaan untuk para anggota konferensi tidak diperbolehkan di ruangan ini" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM dan disc salinan" #: mod_configure:889 msgid "RAM copy" msgstr "Salinan RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Panggilan Kesalahan RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "mentah" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Benar-benar ingin menghapus pesan harian?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Penerima tidak berada di ruangan konferensi" #: mod_register_web:275 msgid "Register" msgstr "Mendaftar" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Daftarkan sebuah akun jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Pengguna Terdaftar" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Pengguna Terdaftar:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Pengguna Terdaftar" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Pendaftaran di mod_irc untuk" #: mod_configure:889 msgid "Remote copy" msgstr "Salinan Remote" #: mod_roster:963 msgid "Remove" msgstr "Menghapus" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Hapus Semua Pesan Offline" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Hapus Pengguna" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Diganti dengan koneksi baru" #: mod_configure:1675 msgid "Resources" msgstr "Sumber daya" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Jalankan Ulang" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Restart Layanan" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Mengembalikan" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Kembalikan Backup dari File pada" #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Mengembalikan cadangan yang berpasanagn setelah ejabberd berikutnya " "dijalankan ulang (memerlukan memori lebih sedikit):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Segera mengembalikan cadangan yang berpasangan:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Segera mengembalikan cadangan teks biasa:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Konfigurasi Ruangan" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Penghuni Ruangan" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Pembuatan Ruangan ditolak oleh kebijakan layanan" #: mod_muc_log:1064 msgid "Room description" msgstr "Keterangan ruangan" #: mod_muc_log:1027 msgid "Room title" msgstr "Nama Ruangan" #: mod_roster:1082 msgid "Roster" msgstr "Kontak" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Kontak dari" #: mod_configure:1671 msgid "Roster size" msgstr "Ukuran Daftar Kontak" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Menjalankan Node" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Sabtu" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "Проверка капчи прошла успешно." #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Periksa naskah" #: mod_vcard:453 msgid "Search Results for " msgstr "Hasil Pencarian untuk" #: mod_vcard:427 msgid "Search users in " msgstr "Pencarian pengguna dalam" #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Kirim pengumuman untuk semua pengguna yang online" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Kirim pengumuman untuk semua pengguna yang online pada semua host" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Kirim pengumuman untuk semua pengguna" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Kirim pengumuman untuk semua pengguna pada semua host" #: mod_muc_log:481 msgid "September" msgstr "September" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Layanan ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Layanan:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Mengatur pesan harian dan mengirimkan ke pengguna yang online" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Mengatur pesan harian pada semua host dan kirimkan ke pengguna yang online" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Berbagi grup kontak" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Tampilkan Tabel Terpisah" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Tampilkan Tabel Normal" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Shut Down Layanan" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Beberapa klien Jabber dapat menyimpan password di komputer Anda. Gunakan " "fitur itu hanya jika Anda mempercayai komputer Anda aman." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Mulai" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Memulai Modul" #: mod_configure:936 msgid "Start Modules at " msgstr "Mulai Modul pada" #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistik" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "statistik dari ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Hentikan" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Hentikan Modul" #: mod_configure:918 msgid "Stop Modules at " msgstr "Hentikan Modul pada" #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Menghentikan node" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Jenis Penyimpanan" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Penyimpanan cadangan yang berpasangan:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Simpan cadangan teks biasa:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Subyek" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Serahkan" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Ulangi masukan" #: mod_roster:914 msgid "Subscription" msgstr "Berlangganan" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Minggu" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Julukan itu sudah digunakan oleh penghuni lain" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Julukan tersebut telah didaftarkan oleh orang lain" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Captcha ini benar." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Verifikasi CAPTCHA telah gagal" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Kata sandi terlalu lemah" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Kata sandi pada akun Jabber Anda telah berhasil diubah." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Ada kesalahan dalam mengubah password:" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Ada kesalahan saat membuat akun:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Ada kesalahan saat menghapus akun:" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Pada bagian ini huruf besar dan kecil tidak dibedakan: Misalnya macbeth " "adalah sama dengan MacBeth juga Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Halaman ini memungkinkan untuk membuat akun Jabber di layanan Jabber ini. " "JID Anda (Jabber Pengenal) akan berbentuk: namapengguna@layanan. Harap baca " "dengan seksama petunjuk-petunjuk untuk mengisi kolom dengan benar." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Pada bagian ini memungkinkan Anda untuk membatalkan pendaftaran akun Jabber " "pada layanan Jabber ini." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Ruangan ini tidak dikenal" #: mod_muc_log:466 msgid "Thursday" msgstr "Kamis" #: mod_offline:690 msgid "Time" msgstr "Waktu" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Waktu tunda" #: mod_offline:692 msgid "To" msgstr "Kepada" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Kepada ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Ruangan Chat" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Lalu lintas melebihi batas" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transaksi yang dibatalkan:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transaksi yang dilakukan:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transaksi yang ditempuh:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transaksi yang dijalankan ulang:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Selasa" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Tidak dapat menghasilkan CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Ditolak" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Nonaktifkan" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Nonaktifkan akun jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Memperbarui" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Rubah pesan harian (tidak dikirim)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Rubah pesan harian pada semua host (tidak dikirim)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Rencana Perubahan" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Perbarui naskah" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Memperbarui " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Sampai saat:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Penggunaan STARTTLS diperlukan" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Penggunaan STARTTLS diperlukan" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Pengguna" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Manajemen Pengguna" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Node tidak ditemukan" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Pengguna" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Nama Pengguna:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Pengguna" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Aktifitas terakhir para pengguna" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Pengguna tidak diperkenankan untuk mendaftar akun begitu cepat" #: mod_roster:954 msgid "Validate" msgstr "Mengesahkan" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtual Hosts" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Visitor tidak diperbolehkan untuk mengubah nama julukan di ruangan ini" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Visitor tidak diperbolehkan untuk mengirim pesan ke semua penghuni" #: mod_muc_room:3879 msgid "Voice request" msgstr "" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "" #: mod_muc_log:465 msgid "Wednesday" msgstr "Rabu" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" "Anda dapat mengubah kata sandi anda dilain waktu dengan menggunakan klien " "Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Anda telah diblokir dari ruangan ini" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Anda harus mengisi kolom \"Julukan\" dalam formulir" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Anda memerlukan klien yang mendukung x:data dan CAPTCHA untuk mendaftar" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Anda memerlukan klien yang mendukung x:data untuk mendaftar julukan" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Anda memerlukan x:data klien untuk mampu mengkonfigurasi pengaturan mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Anda memerlukan x:data klien untuk melakukan pencarian" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Jabber akun Anda telah sukses dibuat" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Jabber akun Anda berhasil dihapus." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Daftar privasi aktif Anda telah menolak routing ztanza ini" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Kontak offline Anda pada antrian pesan sudah penuh. Pesan telah dibuang." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Pesan Anda untuk ~s sedang diblokir. Untuk membuka blokir tersebut, kunjungi " "~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC modul" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC Module" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Modul ejabberd Setujui-Pertemanan" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "modul ejabberd SOCKS5 Bytestreams" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Admin Web ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Modul ejabberd vCard" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "telah dibanned" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "telah dikick" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "telah dikick karena sistem shutdown" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "telah dikick karena perubahan afiliasi" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "telah dikick karena ruangan telah diubah menjadi hanya untuk member" #: mod_muc_log:410 msgid "is now known as" msgstr "sekarang dikenal sebagai" #: mod_muc_log:370 msgid "joins the room" msgstr "bergabung ke ruangan" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "meninggalkan ruangan" #: mod_muc_room:3856 msgid "private, " msgstr "pribadi, " #: mod_muc_room:3955 msgid "the password is" msgstr "kata sandi yaitu:" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard Pencarian Pengguna" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s aturan akses konfigurasi" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s mengundang anda ke ruangan ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Antrian Pesan Offline ~s" #~ msgid "No resource provided" #~ msgstr "Tidak ada sumber daya yang disediakan" #, fuzzy #~ msgid "Server" #~ msgstr "Layanan:" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s tidak valid" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiliasi tidak valid: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Peran tidak valid: ~s" #~ msgid "No limit" #~ msgstr "Tidak terbatas" #~ msgid "Present real Jabber IDs to" #~ msgstr "Tampilkan Jabber ID secara lengkap" #~ msgid "moderators only" #~ msgstr "Hanya moderator" #~ msgid "anyone" #~ msgstr "Siapapun" #, fuzzy #~ msgid "Moderator" #~ msgstr "Hanya moderator" #, fuzzy #~ msgid "Allow visitors to send voice requests" #~ msgstr "Perbolehkan pengguna mengirimkan undangan" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Anda memerlukan x:data klien untuk dapat mengkonfigurasi ruangan" #~ msgid "Number of occupants" #~ msgstr "Jumlah Penghuni" #, fuzzy #~ msgid "User JID" #~ msgstr "Pengguna" #~ msgid "Node ID" #~ msgstr "ID Node" #~ msgid "Subscriber Address" #~ msgstr "Alamat Pertemanan" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Izinkan ID Jabber ini untuk berlangganan pada node pubsub ini?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Memberikan muatan dengan pemberitahuan acara" #~ msgid "Deliver event notifications" #~ msgstr "Memberikan pemberitahuan acara" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Beritahu pelanggan ketika ada perubahan konfigurasi node" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Beritahu pelanggan ketika node dihapus" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Beritahu pelanggan ketika item tersebut dikeluarkan dari node" #~ msgid "Persist items to storage" #~ msgstr "Pertahankan item ke penyimpanan" #~ msgid "A friendly name for the node" #~ msgstr "Nama yang dikenal untuk node" #~ msgid "Max # of items to persist" #~ msgstr "Max item untuk bertahan" #~ msgid "Whether to allow subscriptions" #~ msgstr "Apakah diperbolehkan untuk berlangganan" #~ msgid "Specify the access model" #~ msgstr "Tentukan model akses" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Kelompok kontak yang diizinkan untuk berlangganan" #~ msgid "Specify the publisher model" #~ msgstr "Tentukan model penerbitan" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Bersihkan semua item ketika penerbit yang relevan telah offline" #~ msgid "Specify the event message type" #~ msgstr "Tentukan jenis acara pesan" #~ msgid "Max payload size in bytes" #~ msgstr "Max kapasitas ukuran dalam bytes" #~ msgid "When to send the last published item" #~ msgstr "Ketika untuk mengirim item terakhir yang dipublikasikan" #~ msgid "Only deliver notifications to available users" #~ msgstr "Hanya mengirimkan pemberitahuan kepada pengguna yang tersedia" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Koleksi dengan yang berafiliasi dengan sebuah node" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Isi kolom untuk mencari pengguna Jabber yang sama" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Layanan s2s yang keluar:" #~ msgid "Delete" #~ msgstr "Hapus" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "Peserta ini dikick dari ruangan karena dia mengirim pesan kesalahan" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Participant ini dikick dari ruangan karena ia mengirim pesan kesalahan ke " #~ "participant lain" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Participant ini dikick dari ruangan karena ia mengirim kehadiran kesalahan" ejabberd-18.01/priv/msgs/no.msg0000644000232200023220000005536713225664356016740 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Tilgangskonfigurasjon"}. {"Access Control List Configuration","Konfigurasjon for Tilgangskontroll lister"}. {"Access Control Lists","Tilgangskontrollister"}. {"Access control lists","Tilgangskontroll lister"}. {"Access denied by service policy","Tilgang nektes på grunn av en tjeneste regel"}. {"Access rules","Tilgangsregler"}. {"Access Rules","Tilgangsregler"}. {"Action on user","Handling på bruker"}. {"Add Jabber ID","Legg til Jabber ID"}. {"Add New","Legg til ny"}. {"Add User","Legg til Bruker"}. {"Administration","Administrasjon"}. {"Administration of ","Administrasjon av "}. {"Administrator privileges required","Administratorprivilegier kreves"}. {"All activity","All aktivitet"}. {"Allow users to change the subject","Tillat brukere å endre emne"}. {"Allow users to query other users","Tillat brukere å sende forespørsel til andre brukere"}. {"Allow users to send invites","Tillat brukere å sende invitasjoner"}. {"Allow users to send private messages","Tillat brukere å sende private meldinger"}. {"Allow visitors to change nickname","Tillat besøkende å endre kallenavn"}. {"Allow visitors to send private messages to","Tillat brukere å sende private meldinger til"}. {"Allow visitors to send status text in presence updates","Tillat besøkende å sende status tekst i "}. {"All Users","Alle Brukere"}. {"Announcements","Kunngjøringer"}. {"A password is required to enter this room","Et passord kreves for tilgang til samtalerommet"}. {"April","april"}. {"August","august"}. {"Backup Management","Håndtere Sikkerehetskopiering"}. {"Backup","Sikkerhetskopier"}. {"Backup to File at ","Sikkerhetskopiere til Fil på "}. {"Bad format","Feil format"}. {"Birthday","Fødselsdag"}. {"CAPTCHA web page","CAPTCHA web side"}. {"Change Password","Endre Passord"}. {"Change User Password","Endre Brukers Passord"}. {"Characters not allowed:","Ikke godtatte tegn:"}. {"Chatroom configuration modified","Samtalerommets konfigurasjon er endret"}. {"Chatroom is created","Samtalerom er opprettet"}. {"Chatroom is destroyed","Samtalerom er fjernet"}. {"Chatroom is started","Samtalerom er startet"}. {"Chatroom is stopped","Samtalerom er stoppet"}. {"Chatrooms","Samtalerom"}. {"Choose a username and password to register with this server","Velg et brukernavn og passord for å registrere på "}. {"Choose modules to stop","Velg hvilke moduler som skal stoppes"}. {"Choose storage type of tables","Velg lagringstype for tabeller"}. {"Choose whether to approve this entity's subscription.","Velg om du vil godkjenne denne eksistensens abonement"}. {"City","By"}. {"Commands","Kommandoer"}. {"Conference room does not exist","Konferanserommet finnes ikke"}. {"Configuration","Konfigurasjon"}. {"Configuration of room ~s","Konfigurasjon for rom ~s"}. {"Connected Resources:","Tilkoblede Ressurser:"}. {"Connections parameters","Tilkoblings parametere"}. {"Country","Land"}. {"CPU Time:","CPU Tid:"}. {"Database","Database"}. {"Database Tables Configuration at ","Database Tabell Konfigurasjon på "}. {"December","desember"}. {"Default users as participants","Standard brukere som deltakere"}. {"Delete message of the day on all hosts","Slett melding for dagen på alle maskiner"}. {"Delete message of the day","Slett melding for dagen"}. {"Delete Selected","Slett valgte"}. {"Delete User","Slett Bruker"}. {"Description:","Beskrivelse:"}. {"Disc only copy","Kun diskkopi"}. {"Displayed Groups:","Viste grupper:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ikke fortell passordet til noen, ikke en gang til administratoren av Jabber serveren."}. {"Dump Backup to Text File at ","Dump Sikkerhetskopi til Tekstfil på "}. {"Dump to Text File","Dump til Tekstfil"}. {"Edit Properties","Redigere Egenskaper"}. {"Either approve or decline the voice request.","Enten godkjenn eller forby lyd forespørselen"}. {"ejabberd IRC module","ejabberd IRC modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"Elements","Elementer"}. {"Email","Epost"}. {"Enable logging","Slå på logging"}. {"Encoding for server ~b","Tekstkoding for server ~b"}. {"End User Session","Avslutt Bruker Sesjon"}. {"Enter list of {Module, [Options]}","Skriv inn en liste av {Module, [Options]}"}. {"Enter nickname you want to register","Skriv inn kallenavnet du ønsker å registrere"}. {"Enter path to backup file","Skriv inn sti til sikkerhetskopi filen"}. {"Enter path to jabberd14 spool dir","Skriv inn sti til jabberd14 spoolkatalog"}. {"Enter path to jabberd14 spool file","Skriv inn sti til jabberd14 spoolfil"}. {"Enter path to text file","Skriv inn sti til tekstfil"}. {"Enter the text you see","Skriv inn teksten du ser"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Angi brukernavn og kodinger du ønsker å bruke for å koble til IRC servere. Trykk 'Neste' for å få flere felt for å fylle i. Trykk 'Fullfør' for å lagre innstillingene."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Skriv brukernavn, tekstkoding, porter og passord du ønsker å bruke for tilkobling til IRC servere"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Feil"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Eksempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere i en server til PIEFXIS filer"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere i en host til PIEFXIS filer (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Feilet i forsøk på å hente JID fra din lyd forespørsel godkjenning"}. {"Family Name","Etternavn"}. {"February","februar"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Fyll inn skjemaet for å søke etter Jabber bruker (Legg til * på slutten av feltet for å treffe alle som starter slik)"}. {"Friday","fredag"}. {"From","Fra"}. {"From ~s","Fra ~s"}. {"Full Name","Fullstendig Navn"}. {"Get Number of Online Users","Vis Antall Tilkoblede Brukere"}. {"Get Number of Registered Users","Vis Antall Registrerte Brukere"}. {"Get User Last Login Time","Vis Brukers Siste Påloggings Tidspunkt"}. {"Get User Password","Hent Brukers Passord"}. {"Get User Statistics","Vis Bruker Statistikk"}. {"Group ","Gruppe "}. {"Groups","Grupper"}. {"has been banned","har blitt bannlyst"}. {"has been kicked because of an affiliation change","har blitt kastet ut på grunn av en tilknytnings endring"}. {"has been kicked because of a system shutdown","har blitt kastet ut på grunn av at systemet avslutter"}. {"has been kicked because the room has been changed to members-only","har blitt kastet ut på grunn av at rommet er endret til kun-for-medlemmer"}. {"has been kicked","har blitt kastet ut"}. {" has set the subject to: "," har satt emnet til: "}. {"Host","Maskin"}. {"If you don't see the CAPTCHA image here, visit the web page.","Dersom du ikke ser CAPTCHA bilde her, besøk web siden. "}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Om du ønsker å spesifisere tekstkoding for IRC tjenere, fyller du ut en liste med verdier i formatet '{\"irc server\", \"encoding\", port, \"password\"}'. Denne tjenesten bruker \"~s\" som standard, port ~p, empty password."}. {"Import Directory","Importer Katalog"}. {"Import File","Importer File"}. {"Import user data from jabberd14 spool file:","Importer bruker data fra jabberd14 spoolfiler:"}. {"Import User from File at ","Importer Bruker fra Fil på "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importer brukeres data fra en PIEFXIS fil (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importer brukeres data fra jabberd14 spoolfil katalog:"}. {"Import Users from Dir at ","Importer Brukere fra Katalog på "}. {"Import Users From jabberd14 Spool Files","Importer Brukere Fra jabberd14 Spoolfiler"}. {"Improper message type","Feilaktig meldingstype"}. {"Incorrect password","Feil passord"}. {"IP addresses","IP adresser"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC kanal (ikke skriv den første #)"}. {"IRC server","IRC server"}. {"IRC settings","IRC instillinger"}. {"IRC Transport","IRC Transport"}. {"IRC username","IRC brukernavn"}. {"IRC Username","IRC Brukernavn"}. {"is now known as","er nå kjent som"}. {"It is not allowed to send private messages","Det er ikke tillatt å sende private meldinger"}. {"It is not allowed to send private messages of type \"groupchat\"","Det er ikke tillatt å sende private meldinger med typen "}. {"It is not allowed to send private messages to the conference","Det er ikke tillatt å sende private meldinger til "}. {"Jabber Account Registration","Jabber Konto Registrering"}. {"Jabber ID","Jabber ID"}. {"January","januar"}. {"Join IRC channel","Bli med i IRC kanal"}. {"joins the room","kommer inn i rommet"}. {"Join the IRC channel here.","Bli med i IRC kanalen her. "}. {"Join the IRC channel in this Jabber ID: ~s","Bli med i IRC kanalen med denne Jabber ID: ~s"}. {"July","juli"}. {"June","juni"}. {"Last Activity","Siste Aktivitet"}. {"Last login","Siste pålogging"}. {"Last month","Siste måned"}. {"Last year","Siste året"}. {"leaves the room","forlater rommet"}. {"Listened Ports at ","Lyttende Porter på "}. {"Listened Ports","Lyttende Porter"}. {"List of modules to start","Liste over moduler som skal startes"}. {"Low level update script","Lavnivå oppdaterings skript"}. {"Make participants list public","Gjør deltakerlisten offentlig"}. {"Make room CAPTCHA protected","Gjør rommet CAPTCHA beskyttet"}. {"Make room members-only","Gjør rommet tilgjengelig kun for medlemmer"}. {"Make room moderated","Gjør rommet redaktørstyrt"}. {"Make room password protected","Passordbeskytt rommet"}. {"Make room persistent","Gjør rommet permanent"}. {"Make room public searchable","Gjør rommet offentlig søkbart"}. {"March","mars"}. {"Maximum Number of Occupants","Maksimum Antall Deltakere"}. {"May","mai"}. {"Membership is required to enter this room","Medlemskap kreves for tilgang til samtalerommet"}. {"Members:","Medlemmer:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Husk passordet, eller skriv det ned på et papir lagret på et trygt sted. I Jabber er det ingen automatisert måte å gjenskape passordet om du glemmer det. "}. {"Memory","Minne"}. {"Message body","Meldingskropp"}. {"Middle Name","Mellomnavn"}. {"Moderator privileges required","Redaktørprivilegier kreves"}. {"Modified modules","Endrede moduler"}. {"Module","Modul"}. {"Modules","Moduler"}. {"Monday","mandag"}. {"Name:","Navn:"}. {"Name","Navn"}. {"Never","Aldri"}. {"New Password:","Nytt Passord:"}. {"Nickname","Kallenavn"}. {"Nickname Registration at ","Registrer Kallenavn på "}. {"Nickname ~s does not exist in the room","Kallenavn ~s eksisterer ikke i dette rommet"}. {"No body provided for announce message","Ingen meldingskropp gitt for kunngjørings melding"}. {"No Data","Ingen Data"}. {"Node not found","Noden finnes ikke"}. {"Nodes","Noder"}. {"None","Ingen"}. {"Not Found","Finnes Ikke"}. {"November","november"}. {"Number of online users","Antall tilkoblede brukere"}. {"Number of registered users","Antall registrerte brukere"}. {"October","oktober"}. {"Offline Messages:","Frakoblede Meldinger:"}. {"Offline Messages","Frakoblede Meldinger"}. {"OK","OK"}. {"Old Password:","Gammelt Passord:"}. {"Online","Tilkoblet"}. {"Online Users:","Tilkoblede Brukere:"}. {"Online Users","Tilkoblede Brukere"}. {"Only moderators and participants are allowed to change the subject in this room","Bare redaktører og deltakere kan endre emnet i dette rommet"}. {"Only moderators are allowed to change the subject in this room","Bare ordstyrer tillates å endre emnet i dette rommet"}. {"Only moderators can approve voice requests","Bare ordstyrer kan godkjenne lyd forespørsler"}. {"Only occupants are allowed to send messages to the conference","Bare deltakere får sende normale meldinger til konferansen"}. {"Only occupants are allowed to send queries to the conference","Bare deltakere er tillatt å sende forespørsler til "}. {"Only service administrators are allowed to send service messages","Bare tjeneste administratorer er tilatt å sende tjeneste "}. {"Options","Alternativer"}. {"Organization Name","Organisasjonsnavn"}. {"Organization Unit","Organisasjonsenhet"}. {"Outgoing s2s Connections:","Utgående s2s Koblinger"}. {"Outgoing s2s Connections","Utgående s2s Koblinger"}. {"Owner privileges required","Eierprivilegier kreves"}. {"Packet","Pakke"}. {"Password ~b","Passord ~b"}. {"Password:","Passord:"}. {"Password","Passord"}. {"Password Verification:","Passord Bekreftelse:"}. {"Password Verification","Passord Bekreftelse"}. {"Path to Dir","Sti til Katalog"}. {"Path to File","Sti til Fil"}. {"Pending","Ventende"}. {"Period: ","Periode: "}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Merk at disse valgene vil bare sikkerhetskopiere den innebygde Mnesia databasen. Dersom du bruker ODBC modulen må du også ta backup av din SQL database."}. {"Please, wait for a while before sending new voice request","Vennligst vent en stund før du sender en ny lyd forespørsel"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","privat, "}. {"Protocol","Protokoll"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub abonements forespørsel"}. {"Queries to the conference members are not allowed in this room","Forespørsler til konferanse medlemmene er ikke tillat i dette rommet"}. {"RAM and disc copy","RAM og diskkopi"}. {"RAM copy","RAM kopi"}. {"Raw","Rå"}. {"Really delete message of the day?","Virkelig slette melding for dagen?"}. {"Recipient is not in the conference room","Mottakeren er ikke i konferanserommet"}. {"Register a Jabber account","Registrer en Jabber konto"}. {"Registered Users:","Registrerte Brukere:"}. {"Registered Users","Registrerte Brukere"}. {"Register","Registrer"}. {"Registration in mod_irc for ","Registrering i mod_irc for "}. {"Remote copy","Lagres ikke lokalt"}. {"Remove All Offline Messages","Fjern Alle Frakoblede Meldinger"}. {"Remove","Fjern"}. {"Remove User","Fjern Bruker"}. {"Replaced by new connection","Erstattet av en ny tilkobling"}. {"Resources","Ressurser"}. {"Restart Service","Start Tjeneste på Nytt"}. {"Restart","Starte på nytt"}. {"Restore Backup from File at ","Gjenopprett fra Sikkerhetsopifil på "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Gjenopprette binær backup etter neste ejabberd omstart (krever mindre minne):"}. {"Restore binary backup immediately:","Gjenopprette binær backup umiddelbart:"}. {"Restore","Gjenopprett"}. {"Restore plain text backup immediately:","Gjenopprette rentekst sikkerhetskopi umiddelbart:"}. {"Room Configuration","Rom Konfigurasjon"}. {"Room creation is denied by service policy","Oppretting av rom nektes av en tjenste regel"}. {"Room description","Rom beskrivelse"}. {"Room Occupants","Samtalerom Deltakere"}. {"Room title","Romtittel"}. {"Roster","Kontaktliste"}. {"Roster of ","Kontaktliste for "}. {"Roster size","Kontaktliste størrelse"}. {"RPC Call Error","RPC Kall Feil"}. {"Running Nodes","Kjørende Noder"}. {"~s access rule configuration","tilgangsregel konfigurasjon for ~s"}. {"Saturday","lørdag"}. {"Script check","Skript sjekk"}. {"Search Results for ","Søke Resultater for "}. {"Search users in ","Søk etter brukere i "}. {"Send announcement to all online users on all hosts","Send kunngjøring til alle tilkoblede brukere på alle "}. {"Send announcement to all online users","Send kunngjøring alle tilkoblede brukere"}. {"Send announcement to all users on all hosts","Send kunngjøring til alle brukere på alle maskiner"}. {"Send announcement to all users","Send kunngjøring til alle brukere"}. {"September","september"}. {"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Angi melding for dagen og send til tilkoblede brukere"}. {"Set message of the day on all hosts and send to online users","Angi melding for dagen på alle maskiner og send til "}. {"Shared Roster Groups","Delte Kontaktgrupper"}. {"Show Integral Table","Vis Integral Tabell"}. {"Show Ordinary Table","Vis Ordinær Tabell"}. {"Shut Down Service","Avslutt Tjeneste"}. {"~s invites you to the room ~s","~s inviterer deg til rommet ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Noen Jabber klienter kan lagre passordet på datamaskinen. Bruk bare den funksjonen dersom du er sikker på at maskinen er trygg."}. {"~s's Offline Messages Queue","~ss kø for Frakoblede Meldinger"}. {"Start Modules at ","Start Moduler på "}. {"Start Modules","Start Moduler"}. {"Start","Start"}. {"Statistics of ~p","Statistikk for ~p"}. {"Statistics","Statistikk"}. {"Stop Modules at ","Stopp Moduler på "}. {"Stop Modules","Stop Moduler"}. {"Stopped Nodes","Stoppede Noder"}. {"Stop","Stoppe"}. {"Storage Type","Lagringstype"}. {"Store binary backup:","Lagre binær sikkerhetskopi:"}. {"Store plain text backup:","Lagre rentekst sikkerhetskopi:"}. {"Subject","Tittel"}. {"Submit","Send"}. {"Submitted","Innsendt"}. {"Subscription","Abonnement"}. {"Sunday","søndag"}. {"That nickname is already in use by another occupant","Det kallenavnet er allerede i bruk av en annen deltaker"}. {"That nickname is registered by another person","Det kallenavnet er registrert av en annen person"}. {"The CAPTCHA is valid.","Captchaen er ikke gyldig"}. {"The CAPTCHA verification has failed","CAPTCHA godkjenningen har feilet"}. {"the password is","passordet er"}. {"The password is too weak","Passordet er for svakt"}. {"The password of your Jabber account was successfully changed.","Passordet for din Jabber konto ble endret."}. {"There was an error changing the password: ","En feil skjedde under endring av passordet:"}. {"There was an error creating the account: ","En feil skjedde under oppretting av kontoen:"}. {"There was an error deleting the account: ","En feil skjedde under sletting av kontoen: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Denne er ufølsom for små og store bokstaver: macbeth er det samme som MacBeth og Macbeth. "}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Denne siden lar deg lage en Jabber konto på denne Jabber serveren. Din JID (Jabber ID) vil være i formatet: brukernavn@server. Vennligst les instruksjonene nøye slik at du fyller ut skjemaet riktig."}. {"This page allows to unregister a Jabber account in this Jabber server.","Denne siden lar deg avregistrere en Jabber konto på denne Jabber serveren."}. {"This room is not anonymous","Dette rommet er ikke anonymt"}. {"Thursday","torsdag"}. {"Time delay","Tids forsinkelse"}. {"Time","Tid"}. {"Too many CAPTCHA requests","For mange CAPTCHA forespørsler"}. {"To ~s","Til ~s"}. {"To","Til"}. {"Traffic rate limit is exceeded","Trafikkmengde grense overskredet"}. {"Transactions Aborted:","Avbrutte Transasksjoner:"}. {"Transactions Committed:","Sendte Transaksjoner:"}. {"Transactions Logged:","Loggede Transasksjoner:"}. {"Transactions Restarted:","Omstartede Transasksjoner:"}. {"Tuesday","tirsdag"}. {"Unable to generate a CAPTCHA","Umulig å generere en CAPTCHA"}. {"Unauthorized","Uautorisert"}. {"Unregister a Jabber account","Avregistrer en Jabber konto"}. {"Unregister","Avregistrer"}. {"Update message of the day (don't send)","Oppdater melding for dagen (ikke send)"}. {"Update message of the day on all hosts (don't send)","Oppdater melding for dagen på alle maskiner (ikke send)"}. {"Update","Oppdatere"}. {"Update plan","Oppdaterings plan"}. {"Update script","Oppdaterings skript"}. {"Uptime:","Oppetid:"}. {"Use of STARTTLS required","Bruk av STARTTLS kreves"}. {"User","Bruker"}. {"User Management","Bruker Behandling"}. {"Username:","Brukernavn:"}. {"Users are not allowed to register accounts so quickly","Brukere får ikke lov til registrere kontoer så fort"}. {"Users","Brukere"}. {"Users Last Activity","Brukers Siste Aktivitet"}. {"Validate","Bekrefte gyldighet"}. {"vCard User Search","vCard Bruker Søk"}. {"Virtual Hosts","Virtuella Maskiner"}. {"Visitors are not allowed to change their nicknames in this room","Besøkende får ikke lov å endre kallenavn i dette "}. {"Visitors are not allowed to send messages to all occupants","Besøkende får ikke sende meldinger til alle deltakere"}. {"Voice request","Lyd forespørsel"}. {"Voice requests are disabled in this conference","Lyd forespørsler er blokkert i denne konferansen"}. {"Wednesday","onsdag"}. {"You can later change your password using a Jabber client.","Du kan når som helst endre passordet via en Jabber klient."}. {"You have been banned from this room","Du har blitt bannlyst i dette rommet."}. {"You must fill in field \"Nickname\" in the form","Du må fylle inn feltet \"Nickname\" i skjemaet"}. {"You need a client that supports x:data and CAPTCHA to register","Du trenger en klient som støtter x:data og CAPTCHA for registrering "}. {"You need a client that supports x:data to register the nickname","Du trenger en klient som støtter x:data for å registrere kallenavnet"}. {"You need an x:data capable client to configure mod_irc settings","Du trenger en x:data kompatibel klient for å konfigurere mod_irc instillinger"}. {"You need an x:data capable client to search","Du tregner en klient som støtter x:data for å kunne "}. {"Your active privacy list has denied the routing of this stanza.","Din aktive privat liste har blokkert rutingen av denne strofen."}. {"Your contact offline message queue is full. The message has been discarded.","Kontaktens frakoblede meldingskø er full. Meldingen har blitt kassert."}. {"Your Jabber account was successfully created.","Din Jabber konto ble opprettet"}. {"Your Jabber account was successfully deleted.","Dni Jabber konto er blitt sltettet."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Dine meldinger til ~s blir blokkert. For å åpne igjen, besøk ~s"}. ejabberd-18.01/priv/msgs/ja.msg0000644000232200023220000007024413225664356016705 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","許可"}. {"Access Configuration","アクセス設定"}. {"Access Control List Configuration","アクセスコントロールリスト設定"}. {"Access control lists","アクセスコントロールリスト"}. {"Access Control Lists","アクセスコントロールリスト"}. {"Access denied by service policy","サービスポリシーによってアクセスが禁止されました"}. {"Access rules","アクセスルール"}. {"Access Rules","アクセスルール"}. {"Action on user","ユーザー操作"}. {"Add Jabber ID","Jabber ID を追加"}. {"Add New","新規追加"}. {"Add User","ユーザーを追加"}. {"Administration of ","管理: "}. {"Administration","管理"}. {"Administrator privileges required","管理者権限が必要です"}. {"All activity","すべて"}. {"Allow users to change the subject","ユーザーによる件名の変更を許可"}. {"Allow users to query other users","ユーザーによる他のユーザーへのクエリーを許可"}. {"Allow users to send invites","ユーザーによる招待を許可"}. {"Allow users to send private messages","ユーザーによるプライベートメッセージの送信を許可"}. {"Allow visitors to change nickname","傍聴者のニックネームの変更を許可"}. {"Allow visitors to send private messages to","傍聴者によるプライベートメッセージの送信を次の相手に許可"}. {"Allow visitors to send status text in presence updates","傍聴者によるプレゼンス更新のステータス文の送信を許可"}. {"All Users","全ユーザー"}. {"Announcements","アナウンス"}. {"A password is required to enter this room","このチャットルームに入るにはパスワードが必要です"}. {"April","4月"}. {"August","8月"}. {"Backup","バックアップ"}. {"Backup Management","バックアップ管理"}. {"Backup of ~p","バックアップ: ~p"}. {"Backup to File at ","ファイルにバックアップ: "}. {"Bad format","不正なフォーマット"}. {"Birthday","誕生日"}. {"CAPTCHA web page","CAPTCHA ウェブページ"}. {"Change Password","パスワードを変更"}. {"Change User Password","パスワードを変更"}. {"Characters not allowed:","使用できない文字:"}. {"Chatroom configuration modified","チャットルームの設定が変更されました"}. {"Chatroom is created","チャットルームを作りました"}. {"Chatroom is destroyed","チャットルームを終了しました"}. {"Chatroom is started","チャットルームを開始しました"}. {"Chatroom is stopped","チャットルームを停止しました"}. {"Chatrooms","チャットルーム"}. {"Choose a username and password to register with this server","サーバーに登録するユーザー名とパスワードを選択してください"}. {"Choose modules to stop","停止するモジュールを選択"}. {"Choose storage type of tables","テーブルのストレージタイプを選択"}. {"Choose whether to approve this entity's subscription.","このエントリを承認するかどうかを選択してください"}. {"City","都道府県"}. {"Commands","コマンド"}. {"Conference room does not exist","会議室は存在しません"}. {"Configuration of room ~s","チャットルーム ~s の設定"}. {"Configuration","設定"}. {"Connected Resources:","接続リソース:"}. {"Connections parameters","接続パラメーター"}. {"Country","国"}. {"CPU Time:","CPU時間:"}. {"Database","データーベース"}. {"Database Tables at ~p","データーベーステーブル: ~p"}. {"Database Tables Configuration at ","データーベーステーブル設定 "}. {"December","12月"}. {"Default users as participants","デフォルトのユーザーは参加者"}. {"Delete message of the day on all hosts","全ホストのお知らせメッセージを削除"}. {"Delete message of the day","お知らせメッセージを削除"}. {"Delete Selected","選択した項目を削除"}. {"Delete User","ユーザーを削除"}. {"Description:","説明:"}. {"Disc only copy","ディスクだけのコピー"}. {"Displayed Groups:","表示グループ:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","パスワードは誰にも教えないようにしてください。Jabber サーバーの管理者があなたにパスワードを尋ねることはありません。"}. {"Dump Backup to Text File at ","テキストファイルにバックアップ: "}. {"Dump to Text File","テキストファイルに出力"}. {"Edit Properties","プロパティを編集"}. {"Either approve or decline the voice request.","発言権の要求を承認または却下します。"}. {"ejabberd IRC module","ejabberd IRC module"}. {"ejabberd MUC module","ejabberd MUCモジュール"}. {"ejabberd Multicast service","ejabberdマルチキャストサービス"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe モジュール"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams モジュール"}. {"ejabberd vCard module","ejabberd vCard モジュール"}. {"ejabberd Web Admin","ejabberd ウェブ管理"}. {"Elements","要素"}. {"Email","メールアドレス"}. {"Enable logging","ロギングを有効"}. {"Encoding for server ~b","サーバーのエンコーディング ~b"}. {"End User Session","エンドユーザーセッション"}. {"Enter list of {Module, [Options]}","{モジュール, [オプション]}のリストを入力してください"}. {"Enter nickname you want to register","登録するニックネームを入力してください"}. {"Enter path to backup file","バックアップファイルのパスを入力してください"}. {"Enter path to jabberd14 spool dir","jabberd14 spool ディレクトリのディレクトリを入力してください"}. {"Enter path to jabberd14 spool file","jabberd14 spool ファイルのパスを入力してください"}. {"Enter path to text file","テキストファイルのパスを入力してください"}. {"Enter the text you see","見えているテキストを入力してください"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","IRC サーバーに接続先するためのユーザー名と文字エンコーディングを入力してください。'Next' を押して次の項目に進みます。'Complete' を押すと設定が保存されます。"}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","IRC サーバーに接続先するために使用するユーザー名、文字エンコーディング、ポート、パスワードを入力してください"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","エラー"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","例: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","すべてのテーブルをSQL形式でファイルにエクスポート: "}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","サーバーにあるすべてのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","ホストのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. {"Failed to extract JID from your voice request approval","発言権要求の承認から JID を取り出すことに失敗しました"}. {"Family Name","姓"}. {"February","2月"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","項目を入力してユーザーを検索を行えます (* を使用すると部分文字列にマッチします)"}. {"Friday","金曜日"}. {"From ~s","From ~s"}. {"From","差出人"}. {"Full Name","氏名"}. {"Get Number of Online Users","オンラインユーザー数を取得"}. {"Get Number of Registered Users","登録ユーザー数を取得"}. {"Get User Last Login Time","最終ログイン時間を取得"}. {"Get User Password","パスワードを取得"}. {"Get User Statistics","ユーザー統計を取得"}. {"Group ","グループ"}. {"Groups","グループ"}. {"has been banned","はバンされました"}. {"has been kicked","はキックされました"}. {"has been kicked because of an affiliation change","は分掌が変更されたためキックされました"}. {"has been kicked because of a system shutdown","はシステムシャットダウンのためキックされました"}. {"has been kicked because the room has been changed to members-only","はチャットルームがメンバー制に変更されたためキックされました"}. {" has set the subject to: "," は件名を設定しました: "}. {"Host","ホスト"}. {"If you don't see the CAPTCHA image here, visit the web page.","ここに CAPTCHA 画像が表示されない場合、ウェブページを参照してください。"}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","別のポートやパスワード、文字エンコーディングを使用したい場合、'{\"irc server\", \"encoding\", port, \"password\"}' という形式のリストを入力してください。デフォルトでエンコーディングは \"~s\" を使用し、ポートは ~p、パスワードは空になっています。"}. {"Import Directory","ディレクトリインポート"}. {"Import File","ファイルからインポート"}. {"Import user data from jabberd14 spool file:","ユーザーデータを jabberd14 Spool ファイルからインポート:"}. {"Import User from File at ","ファイルからユーザーをインポート: "}. {"Import users data from a PIEFXIS file (XEP-0227):","ユーザーデータを PIEFXIS ファイルからインポート (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","ユーザーデータを jabberd14 Spool ディレクトリからインポート:"}. {"Import Users from Dir at ","ディレクトリからユーザーをインポート: "}. {"Import Users From jabberd14 Spool Files","jabberd14 Spool ファイルからユーザーをインポート"}. {"Improper message type","誤ったメッセージタイプです"}. {"Incoming s2s Connections:","内向き s2s コネクション:"}. {"Incorrect password","パスワードが違います"}. {"IP addresses","IP アドレス"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC チャンネル (先頭に#は不要)"}. {"IRC server","IRC サーバー"}. {"IRC settings","IRC 設定"}. {"IRC Transport","IRCトランスポート"}. {"IRC username","IRC ユーザー名"}. {"IRC Username","IRC ユーザー名"}. {"is now known as","は名前を変更しました: "}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","このルームにエラーメッセージを送ることは許可されていません。参加者(~s)はエラーメッセージを(~s)を送信してルームからキックされました。"}. {"It is not allowed to send private messages of type \"groupchat\"","種別が\"groupchat\" であるプライベートメッセージを送信することはできません"}. {"It is not allowed to send private messages to the conference","この会議にプライベートメッセージを送信することはできません"}. {"It is not allowed to send private messages","プライベートメッセージを送信することはできません"}. {"Jabber Account Registration","Jabber アカウント登録"}. {"Jabber ID","Jabber ID"}. {"January","1月"}. {"Join IRC channel","IRC チャンネルに参加"}. {"joins the room","がチャットルームに参加しました"}. {"Join the IRC channel here.","この IRC チャンネルに参加します。"}. {"Join the IRC channel in this Jabber ID: ~s","Jabber ID: ~s でこの IRC チャンネルに参加"}. {"July","7月"}. {"June","6月"}. {"Last Activity","活動履歴"}. {"Last login","最終ログイン"}. {"Last month","先月"}. {"Last year","去年"}. {"leaves the room","がチャットルームから退出しました"}. {"Listened Ports at ","Listen ポート "}. {"Listened Ports","Listen ポート"}. {"List of modules to start","起動モジュールの一覧"}. {"List of rooms","チャットルームの一覧"}. {"Low level update script","低レベル更新スクリプト"}. {"Make participants list public","参加者一覧を公開"}. {"Make room CAPTCHA protected","チャットルームを CAPTCHA で保護"}. {"Make room members-only","チャットルームをメンバーのみに制限"}. {"Make room moderated","チャットルームをモデレート化"}. {"Make room password protected","チャットルームをパスワードで保護"}. {"Make room persistent","チャットルームを永続化"}. {"Make room public searchable","チャットルームを検索可"}. {"March","3月"}. {"Maximum Number of Occupants","最大在室者数"}. {"May","5月"}. {"Members:","メンバー:"}. {"Membership is required to enter this room","このチャットルームに入るにはメンバーでなければなりません"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","パスワードは記憶するか、紙に書いて安全な場所に保管してください。もしあなたがパスワードを忘れてしまった場合、Jabber ではパスワードのリカバリを自動的に行うことはできません。"}. {"Memory","メモリ"}. {"Message body","本文"}. {"Middle Name","ミドルネーム"}. {"Moderator privileges required","モデレーター権限が必要です"}. {"Modified modules","更新されたモジュール"}. {"Module","モジュール"}. {"Modules","モジュール"}. {"Modules at ~p","モジュール ~p"}. {"Monday","月曜日"}. {"Multicast","マルチキャスト"}. {"Multi-User Chat","マルチユーザーチャット"}. {"Name","名"}. {"Name:","名前:"}. {"Never","なし"}. {"New Password:","新しいパスワード:"}. {"Nickname","ニックネーム"}. {"Nickname Registration at ","ニックネーム登録: "}. {"Nickname ~s does not exist in the room","ニックネーム ~s はこのチャットルームにいません"}. {"No body provided for announce message","アナウンスメッセージはありませんでした"}. {"No Data","データなし"}. {"Node not found","ノードが見つかりません"}. {"Node ~p","ノード ~p"}. {"Nodes","ノード"}. {"None","なし"}. {"Not Found","見つかりません"}. {"November","11月"}. {"Number of online users","オンラインユーザー数"}. {"Number of registered users","登録ユーザー数"}. {"October","10月"}. {"Offline Messages:","オフラインメッセージ:"}. {"Offline Messages","オフラインメッセージ"}. {"OK","OK"}. {"Old Password:","古いパスワード:"}. {"Online","オンライン"}. {"Online Users:","オンラインユーザー:"}. {"Online Users","オンラインユーザー"}. {"Only members may query archives of this room","メンバーのみがこのルームのアーカイブを取得できます"}. {"Only moderators and participants are allowed to change the subject in this room","モデレーターと参加者のみがチャットルームの件名を変更できます"}. {"Only moderators are allowed to change the subject in this room","モデレーターのみがチャットルームの件名を変更できます"}. {"Only moderators can approve voice requests","モデレーターだけが発言権の要求を承認できます"}. {"Only occupants are allowed to send messages to the conference","在室者のみがこの会議にメッセージを送ることができます"}. {"Only occupants are allowed to send queries to the conference","在室者のみが会議にクエリーを送信することができます"}. {"Only service administrators are allowed to send service messages","サービス管理者のみがサービスメッセージを送信できます"}. {"Options","オプション"}. {"Organization Name","会社名"}. {"Organization Unit","部署名"}. {"Outgoing s2s Connections:","外向き s2s コネクション:"}. {"Outgoing s2s Connections","外向き s2s コネクション"}. {"Owner privileges required","主宰者の権限が必要です"}. {"Packet","パケット"}. {"Password:","パスワード:"}. {"Password","パスワード"}. {"Password ~b","パスワード ~b"}. {"Password Verification:","パスワード (確認):"}. {"Password Verification","パスワード (確認)"}. {"Path to Dir","ディレクトリのパス"}. {"Path to File","ファイルのパス"}. {"Pending","保留"}. {"Period: ","期間: "}. {"Permanent rooms","永続チャットルーム"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","これらのオプションは組み込みの Mnesia データーベースのバックアップのみを行うことに注意してください。もし ODBC モジュールを使用している場合は、SQL データーベースのバックアップを別に行う必要があります。"}. {"Please, wait for a while before sending new voice request","新しい発言権の要求を送るまで少し間をおいてください"}. {"Pong","Pong"}. {"Port","ポート"}. {"Port ~b","ポート ~b"}. {"private, ","プライベート、"}. {"Protocol","プロトコル"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub 購読者のリクエスト"}. {"Queries to the conference members are not allowed in this room","このチャットルームでは、会議のメンバーへのクエリーは禁止されています"}. {"RAM and disc copy","RAM, ディスクコピー"}. {"RAM copy","RAM コピー"}. {"Raw","Raw"}. {"Really delete message of the day?","本当にお知らせメッセージを削除しますか ?"}. {"Recipient is not in the conference room","受信者はこの会議室にいません"}. {"Register a Jabber account","Jabber アカウントを登録"}. {"Registered nicknames","登録ニックネーム"}. {"Registered Users:","登録ユーザー:"}. {"Registered Users","登録ユーザー"}. {"Register","登録"}. {"Registration in mod_irc for ","mod_irc での登録: "}. {"Remote copy","リモートコピー"}. {"Remove All Offline Messages","すべてのオフラインメッセージを削除"}. {"Remove User","ユーザーを削除"}. {"Remove","削除"}. {"Replaced by new connection","新しいコネクションによって置き換えられました"}. {"Resources","リソース"}. {"Restart Service","サービスを再起動"}. {"Restart","再起動"}. {"Restore","リストア"}. {"Restore Backup from File at ","ファイルからバックアップをリストア: "}. {"Restore binary backup after next ejabberd restart (requires less memory):","ejabberd の再起動時にバイナリバックアップからリストア (メモリ少):"}. {"Restore binary backup immediately:","直ちにバイナリバックアップからリストア:"}. {"Restore plain text backup immediately:","直ちにプレーンテキストバックアップからリストア:"}. {"Room Configuration","チャットルームの設定"}. {"Room creation is denied by service policy","サービスポリシーによってチャットルームの作成が禁止されています"}. {"Room description","チャットルームの説明"}. {"Room Occupants","在室者"}. {"Room title","チャットルームのタイトル"}. {"Roster of ","名簿: "}. {"Roster size","名簿サイズ"}. {"Roster","名簿"}. {"RPC Call Error","RPC 呼び出しエラー"}. {"Running Nodes","起動ノード"}. {"~s access rule configuration","~s アクセスルール設定"}. {"Saturday","土曜日"}. {"Script check","スクリプトチェック"}. {"Search Results for ","検索結果: "}. {"Search users in ","ユーザーの検索: "}. {"Send announcement to all online users on all hosts","全ホストのオンラインユーザーにアナウンスを送信"}. {"Send announcement to all online users","すべてのオンラインユーザーにアナウンスを送信"}. {"Send announcement to all users on all hosts","全ホストのユーザーにアナウンスを送信"}. {"Send announcement to all users","すべてのユーザーにアナウンスを送信"}. {"September","9月"}. {"Server:","サーバー:"}. {"Server ~b","サーバー ~b"}. {"Set message of the day and send to online users","お知らせメッセージを設定し、オンラインユーザーに送信"}. {"Set message of the day on all hosts and send to online users","全ホストのお知らせメッセージを設定し、オンラインユーザーに送信"}. {"Shared Roster Groups","共有名簿グループ"}. {"Show Integral Table","累積の表を表示"}. {"Show Ordinary Table","通常の表を表示"}. {"Shut Down Service","サービスを停止"}. {"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Jabber クライアントはコンピューターにパスワードを記憶できます。コンピューターが安全であると信頼できる場合にのみ、この機能を使用してください。"}. {"~s's Offline Messages Queue","~s' のオフラインメッセージキュー"}. {"Start Modules at ","モジュールを開始: "}. {"Start Modules","モジュールを起動"}. {"Start","開始"}. {"Statistics of ~p","~p の統計"}. {"Statistics","統計"}. {"Stop Modules at ","モジュールを停止: "}. {"Stop Modules","モジュールを停止"}. {"Stopped Nodes","停止ノード"}. {"Stop","停止"}. {"Storage Type","ストレージタイプ"}. {"Store binary backup:","バイナリバックアップを保存:"}. {"Store plain text backup:","プレーンテキストバックアップを保存:"}. {"Subject","件名"}. {"Submitted","送信完了"}. {"Submit","送信"}. {"Subscription","認可"}. {"Sunday","日曜日"}. {"That nickname is already in use by another occupant","そのニックネームは既にほかの在室者によって使用されています"}. {"That nickname is registered by another person","ニックネームはほかの人によって登録されています"}. {"The CAPTCHA is valid.","CAPTCHA は有効です。"}. {"The CAPTCHA verification has failed","CAPTCHA 検証は失敗しました"}. {"the password is","パスワードは"}. {"The password is too weak","このパスワードは単純過ぎます"}. {"The password of your Jabber account was successfully changed.","Jabber アカウントのパスワード変更に成功しました。"}. {"There was an error changing the password: ","パスワードの変更中にエラーが発生しました: "}. {"There was an error creating the account: ","アカウントの作成中にエラーが発生しました: "}. {"There was an error deleting the account: ","アカウントの削除中にエラーが発生しました: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","大文字と小文字は区別しません: macbeth は MacBeth や Macbeth と同じです。"}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","ここはこの Jabber サーバーにアカウントを作成するページです。あなたの JID (JabberID) は username@server のような形式になります。注意事項どおり、正しく項目を記入してください。"}. {"This page allows to unregister a Jabber account in this Jabber server.","このページはサーバー上のJabberアカウントを削除するページです。"}. {"This room is not anonymous","このチャットルームは非匿名です"}. {"Thursday","木曜日"}. {"Time delay","遅延時間"}. {"Time","時間"}. {"Too many CAPTCHA requests","CAPTCHA 要求が多すぎます"}. {"Too many unacked stanzas","多くのスタンザが応答していません"}. {"To ~s","宛先 ~s"}. {"Total rooms","チャットルーム数"}. {"To","To"}. {"Traffic rate limit is exceeded","トラフィックレートの制限を超えました"}. {"Transactions Aborted:","トランザクションの失敗:"}. {"Transactions Committed:","トランザクションのコミット:"}. {"Transactions Logged:","トランザクションのログ: "}. {"Transactions Restarted:","トランザクションの再起動:"}. {"Tuesday","火曜日"}. {"Unable to generate a CAPTCHA","CAPTCHA を生成できません"}. {"Unauthorized","認証されていません"}. {"Unregister a Jabber account","Jabber アカウントを削除"}. {"Unregister","削除"}. {"Update message of the day (don't send)","お知らせメッセージを更新 (送信しない)"}. {"Update message of the day on all hosts (don't send)","全ホストのお知らせメッセージを更新 (送信しない)"}. {"Update plan","更新計画"}. {"Update ~p","更新 ~p"}. {"Update script","スクリプトの更新"}. {"Update","更新"}. {"Uptime:","起動時間:"}. {"Use of STARTTLS required","STARTTLS の使用が必須です"}. {"User","ユーザー"}. {"User Management","ユーザー管理"}. {"Username:","ユーザー名:"}. {"Users","ユーザー"}. {"Users are not allowed to register accounts so quickly","それほど速くアカウントを登録することはできません"}. {"Users Last Activity","ユーザーの活動履歴"}. {"User ~s","ユーザー ~s"}. {"Validate","検証"}. {"vCard User Search","vCard検索"}. {"Virtual Hosts","バーチャルホスト"}. {"Visitors are not allowed to change their nicknames in this room","傍聴者はこのチャットルームでニックネームを変更することはできません"}. {"Visitors are not allowed to send messages to all occupants","傍聴者はすべての在室者にメッセージを送信することはできません"}. {"Voice requests are disabled in this conference","この会議では、発言権の要求はできません"}. {"Voice request","発言権を要求"}. {"Wednesday","水曜日"}. {"You can later change your password using a Jabber client.","あなたは後で Jabber クライアントを使用してパスワードを変更できます。"}. {"You have been banned from this room","あなたはこのチャットルームからバンされています"}. {"You must fill in field \"Nickname\" in the form","フォームの\"ニックネーム\"欄を入力する必要があります"}. {"You need a client that supports x:data and CAPTCHA to register","登録を行うには x:data と CAPTCHA をサポートするクライアントが必要です"}. {"You need a client that supports x:data to register the nickname","ニックネームを登録するには x:data をサポートするクライアントが必要です"}. {"You need an x:data capable client to configure mod_irc settings","mod_irc の設定には x:data をサポートするクライアントが必要です"}. {"You need an x:data capable client to search","検索を行うためには x:data をサポートするクライアントが必要です"}. {"Your active privacy list has denied the routing of this stanza.","あなたのプライバシーリストはこのスタンザのルーティングを拒否しました。"}. {"Your contact offline message queue is full. The message has been discarded.","相手先のオフラインメッセージキューが一杯です。このメッセージは破棄されます。"}. {"Your Jabber account was successfully created.","Jabber アカウントの作成に成功しました。"}. {"Your Jabber account was successfully deleted.","Jabber アカウントの削除に成功しました。"}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","~s 宛のメッセージはブロックされています。解除するにはこちらを見てください ~s"}. ejabberd-18.01/priv/msgs/de.msg0000644000232200023220000006226613225664356016710 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Akzeptieren"}. {"Access Configuration","Zugangskonfiguration"}. {"Access Control List Configuration","Konfiguration der Zugangskontrolllisten"}. {"Access control lists","Zugangskontroll-Listen (ACL)"}. {"Access Control Lists","Zugangskontroll-Listen (ACL)"}. {"Access denied by service policy","Zugang aufgrund der Dienstrichtlinien verweigert"}. {"Access rules","Zugangsregeln"}. {"Access Rules","Zugangsregeln"}. {"Action on user","Aktion auf Benutzer"}. {"Add Jabber ID","Jabber-ID hinzufügen"}. {"Add New","Neue(n) hinzufügen"}. {"Add User","Benutzer hinzufügen"}. {"Administration of ","Administration von "}. {"Administration","Verwaltung"}. {"Administrator privileges required","Administratorenrechte benötigt"}. {"All activity","Alle Aktivitäten"}. {"Allow users to change the subject","Erlaube Benutzern das Thema zu ändern"}. {"Allow users to query other users","Erlaube Benutzern Informationen über andere Benutzer abzufragen"}. {"Allow users to send invites","Erlaube Benutzern Einladungen zu senden"}. {"Allow users to send private messages","Erlaube Benutzern private Nachrichten zu senden"}. {"Allow visitors to change nickname","Erlaube Besuchern ihren Benutzernamen zu ändern"}. {"Allow visitors to send private messages to","Erlaube Besuchern das Senden von privaten Nachrichten an"}. {"Allow visitors to send status text in presence updates","Erlaube Besuchern einen Text bei Statusänderung zu senden"}. {"All Users","Alle Benutzer"}. {"Announcements","Ankündigungen"}. {"A password is required to enter this room","Sie brauchen ein Passwort um diesen Raum zu betreten"}. {"April","April"}. {"August","August"}. {"Backup","Datensicherung"}. {"Backup Management","Datensicherungsverwaltung"}. {"Backup of ~p","Sicherung von ~p"}. {"Backup to File at ","Datensicherung in die Datei "}. {"Bad format","Ungültiges Format"}. {"Birthday","Geburtsdatum"}. {"CAPTCHA web page","CAPTCHA Webseite"}. {"Change Password","Passwort ändern"}. {"Change User Password","Benutzer-Passwort ändern"}. {"Characters not allowed:","Nicht erlaubte Zeichen:"}. {"Chatroom configuration modified","Chatraum-Konfiguration geändert"}. {"Chatroom is created","Chatraum wurde erstellt"}. {"Chatroom is destroyed","Chatraum wurde entfernt"}. {"Chatroom is started","Chatraum wurde gestartet"}. {"Chatroom is stopped","Chatraum wurde beendet"}. {"Chatrooms","Chaträume"}. {"Choose a username and password to register with this server","Wählen sie zum Registrieren einen Benutzernamen und ein Passwort"}. {"Choose modules to stop","Wähle zu stoppende Module"}. {"Choose storage type of tables","Wähle Speichertyp der Tabellen"}. {"Choose whether to approve this entity's subscription.","Wählen sie, ob dieses Abonnement akzeptiert werden soll."}. {"City","Stadt"}. {"Commands","Befehle"}. {"Conference room does not exist","Konferenzraum existiert nicht"}. {"Configuration","Konfiguration"}. {"Configuration of room ~s","Konfiguration für Raum ~s"}. {"Connected Resources:","Verbundene Ressourcen:"}. {"Connections parameters","Verbindungsparameter"}. {"Country","Land"}. {"CPU Time:","CPU-Zeit:"}. {"Database","Datenbank"}. {"Database Tables at ~p","Datenbanktabellen auf ~p"}. {"Database Tables Configuration at ","Datenbanktabellen-Konfiguration auf "}. {"December","Dezember"}. {"Default users as participants","Benutzer werden standardmäßig vollwertige Teilnehmer"}. {"Delete message of the day","Lösche Nachricht des Tages"}. {"Delete message of the day on all hosts","Lösche Nachricht des Tages auf allen Hosts"}. {"Delete Selected","Markierte löschen"}. {"Delete User","Benutzer löschen"}. {"Description:","Beschreibung:"}. {"Disc only copy","Nur auf Festplatte"}. {"Displayed Groups:","Angezeigte Gruppen:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Geben sie niemandem ihr Passwort, auch nicht den Administratoren des Jabber Servers."}. {"Dump Backup to Text File at ","Ausgabe der Sicherung in diese Textdatei "}. {"Dump to Text File","Ausgabe in Textdatei"}. {"Edit Properties","Einstellungen ändern"}. {"Either approve or decline the voice request.","Diese Anfrage für Sprachrechte bestätigen oder ablehnen."}. {"ejabberd IRC module","ejabberd IRC-Modul"}. {"ejabberd MUC module","ejabberd MUC-Modul"}. {"ejabberd Multicast service","ejabberd Multicast Dienst"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe-Modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5-Bytestreams-Modul"}. {"ejabberd vCard module","ejabberd vCard-Modul"}. {"ejabberd Web Admin","ejabberd Web-Admin"}. {"Elements","Elemente"}. {"Email","E-Mail"}. {"Enable logging","Protokollierung aktivieren"}. {"Encoding for server ~b","Kodierung für Server ~b"}. {"End User Session","Benutzer-Sitzung beenden"}. {"Enter list of {Module, [Options]}","Geben sie eine Liste bestehend aus {Modul, [Optionen]} ein"}. {"Enter nickname you want to register","Geben sie den zu registrierenden Benutzernamen ein"}. {"Enter path to backup file","Geben sie den Pfad zur Datensicherung ein"}. {"Enter path to jabberd14 spool dir","Geben sie den Pfad zum jabberd14-Spool-Verzeichnis ein"}. {"Enter path to jabberd14 spool file","Geben sie den Pfad zur jabberd14-Spool-Datei ein"}. {"Enter path to text file","Geben sie den Pfad zur Textdatei ein"}. {"Enter the text you see","Geben sie den Text den sie sehen ein"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Geben sie Benutzernamen und Kodierung für Verbindungen zu IRC Servern an. Drücken sie 'Mehr' um leere Felder hinzuzufügen. Drücken sie 'Beenden' um die Einstellungen zu speichern."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Geben sie den Benutzernamen, Zeichenkodierung, Ports und Passwörter, die sie für die Verbindung zum IRC-Server verwenden wollen, an"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Fehler"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Beispiel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Alle Tabellen als SQL Abfragen in eine Datei exportieren:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Servers in PIEFXIS Dateien (XEP-0227) exportieren:"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Hosts in PIEFXIS Dateien (XEP-0227) exportieren:"}. {"Failed to extract JID from your voice request approval","Fehler beim Auslesen der JID aus der Anfragenbestätigung für Sprachrechte"}. {"Family Name","Nachname"}. {"February","Februar"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Füllen sie die Felder aus, um nach passenden Jabber-Benutzern zu suchen (beenden Sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)"}. {"Friday","Freitag"}. {"From ~s","Von ~s"}. {"From","Von"}. {"Full Name","Vollständiger Name"}. {"Get Number of Online Users","Anzahl der angemeldeten Benutzer abrufen"}. {"Get Number of Registered Users","Anzahl der registrierten Benutzer abrufen"}. {"Get User Last Login Time","letzte Anmeldezeit abrufen"}. {"Get User Password","Benutzer-Passwort abrufen"}. {"Get User Statistics","Benutzer-Statistiken abrufen"}. {"Group ","Gruppe "}. {"Groups","Gruppen"}. {"has been banned","wurde gebannt"}. {"has been kicked because of an affiliation change","wurde wegen Änderung des Mitgliederstatus entfernt"}. {"has been kicked because of a system shutdown","wurde wegen einer Systemabschaltung entfernt"}. {"has been kicked because the room has been changed to members-only","wurde entfernt weil der Raum auf Nur-Mitglieder umgestellt wurde"}. {"has been kicked","wurde entfernt"}. {" has set the subject to: "," hat das Thema geändert auf: "}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Wenn sie das CAPTCHA Bild nicht sehen, besuchen sie bitte die Webseite."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Wenn sie verschiedene Ports, Passwörter und Kodierungen für IRC Server angeben wollen, erstellen sie die Liste mit folgendem Format '{\"IRC Server\", \"Kodierung\", Port, \"Passwort\"}'. Standardmäßig benutzt dieser Dienst die \"~s\" Kodierung, den Port ~p und kein Passwort."}. {"Import Directory","Verzeichnis importieren"}. {"Import File","Datei importieren"}. {"Import user data from jabberd14 spool file:","Importiere Benutzer von jabberd14 Spool Datei:"}. {"Import User from File at ","Benutzer aus dieser Datei importieren "}. {"Import users data from a PIEFXIS file (XEP-0227):","Benutzerdaten von einer PIEFXIS Datei (XEP-0227) importieren:"}. {"Import users data from jabberd14 spool directory:","Importiere Benutzer von jabberd14 Spool Verzeichnis:"}. {"Import Users from Dir at ","Benutzer importieren aus dem Verzeichnis "}. {"Import Users From jabberd14 Spool Files","Importiere Benutzer aus jabberd14-Spool-Dateien"}. {"Improper message type","Unzulässiger Nachrichtentyp"}. {"Incoming s2s Connections:","Eingehende s2s-Verbindungen:"}. {"Incorrect password","Falsches Passwort"}. {"IP addresses","IP Adressen"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC Channel (ohne dem ersten #)"}. {"IRC server","IRC Server"}. {"IRC settings","IRC Einstellungen"}. {"IRC Transport","IRC Transport"}. {"IRC username","IRC Benutzername"}. {"IRC Username","IRC-Benutzername"}. {"is now known as","ist nun bekannt als"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Es ist nicht erlaubt Fehlermeldungen an den Raum zu senden. Der Teilnehmer (~s) hat eine Fehlermeldung (~s) gesendet und wurde aus dem Raum entfernt"}. {"It is not allowed to send private messages","Es ist nicht erlaubt private Nachrichten zu senden"}. {"It is not allowed to send private messages of type \"groupchat\"","Es ist nicht erlaubt private Nachrichten des Typs \"Gruppenchat\" zu senden"}. {"It is not allowed to send private messages to the conference","Es ist nicht erlaubt private Nachrichten an den Raum zu schicken"}. {"Jabber Account Registration","Jabber Konto Anmeldung"}. {"Jabber ID","Jabber ID"}. {"January","Januar"}. {"Join IRC channel","IRC Channel beitreten"}. {"joins the room","betretet den Raum"}. {"Join the IRC channel here.","Hier den IRC Channel beitreten."}. {"Join the IRC channel in this Jabber ID: ~s","Den IRC Channel mit dieser Jabber ID beitreten: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Letzte Aktivität"}. {"Last login","Letzte Anmeldung"}. {"Last month","Letzter Monat"}. {"Last year","Letztes Jahr"}. {"leaves the room","verlässt den Raum"}. {"Listened Ports","Aktive Ports"}. {"Listened Ports at ","Aktive Ports bei"}. {"List of modules to start","Liste der zu startenden Module"}. {"List of rooms","Liste von Chaträumen"}. {"Low level update script","Low level Aktualisierungsscript"}. {"Make participants list public","Teilnehmerliste öffentlich machen"}. {"Make room CAPTCHA protected","Raum mit Verifizierung (Captcha) versehen"}. {"Make room members-only","Raum nur für Mitglieder zugänglich machen"}. {"Make room moderated","Raum moderiert machen"}. {"Make room password protected","Raum mit Passwort schützen"}. {"Make room persistent","Raum persistent machen"}. {"Make room public searchable","Raum öffentlich suchbar machen"}. {"March","März"}. {"Maximum Number of Occupants","Maximale Anzahl von Teilnehmern"}. {"May","Mai"}. {"Membership is required to enter this room","Um diesen Raum zu betreten müssen sie Mitglied sein"}. {"Members:","Mitglieder:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Merken sie sich ihr Passwort, oder schreiben sie es auf einen Zettel den sie sicher verwahren. Bei Jabber gibt es keine automatische Möglichkeit, das Passwort wiederherzustellen."}. {"Memory","Speicher"}. {"Message body","Nachrichtentext"}. {"Middle Name","Zweiter Vorname"}. {"Moderator privileges required","Moderatorrechte benötigt"}. {"Modified modules","Geänderte Module"}. {"Module","Modul"}. {"Modules at ~p","Module bei ~p"}. {"Modules","Module"}. {"Monday","Montag"}. {"Multicast","Multicast"}. {"Multi-User Chat","Mehrbenutzer-Chat (MUC)"}. {"Name:","Name:"}. {"Name","Vorname"}. {"Never","Nie"}. {"New Password:","Neues Passwort:"}. {"Nickname","Benutzername"}. {"Nickname Registration at ","Registrieren des Benutzernames auf "}. {"Nickname ~s does not exist in the room","Der Benutzername ~s existiert im Raum nicht"}. {"No body provided for announce message","Kein Text für die Ankündigungsnachricht angegeben"}. {"No Data","Keine Daten"}. {"Node not found","Knoten nicht gefunden"}. {"Node ~p","Knoten ~p"}. {"Nodes","Knoten"}. {"None","Keine"}. {"Not Found","Nicht gefunden"}. {"November","November"}. {"Number of online users","Anzahl der angemeldeten Benutzer"}. {"Number of registered users","Anzahl der registrierten Benutzer"}. {"October","Oktober"}. {"Offline Messages:","Offline-Nachrichten:"}. {"Offline Messages","Offline-Nachrichten"}. {"OK","OK"}. {"Old Password:","Aktuelles Passwort:"}. {"Online","Angemeldet"}. {"Online Users:","Angemeldete Benutzer:"}. {"Online Users","Angemeldete Benutzer"}. {"Only members may query archives of this room","Nur Mitglieder dürfen den Verlauf dieses Raumes abrufen"}. {"Only moderators and participants are allowed to change the subject in this room","Nur Moderatoren und Mitglieder dürfen das Thema in diesem Raum ändern"}. {"Only moderators are allowed to change the subject in this room","Nur Moderatoren dürfen das Thema in diesem Raum ändern"}. {"Only moderators can approve voice requests","Nur Moderatoren können Anfragen für Sprachrechte bestätigen"}. {"Only occupants are allowed to send messages to the conference","Nur Teilnehmer dürfen Nachrichten an den Raum schicken"}. {"Only occupants are allowed to send queries to the conference","Nur Teilnehmer sind berechtigt Anfragen an die Konferenz zu senden"}. {"Only service administrators are allowed to send service messages","Nur Service-Administratoren sind berechtigt, Servicenachrichten zu versenden"}. {"Options","Optionen"}. {"Organization Name","Name der Organisation"}. {"Organization Unit","Abteilung"}. {"Outgoing s2s Connections:","Ausgehende s2s-Verbindungen:"}. {"Outgoing s2s Connections","Ausgehende s2s-Verbindungen"}. {"Owner privileges required","Besitzerrechte benötigt"}. {"Packet","Paket"}. {"Password ~b","Passwort ~b"}. {"Password:","Passwort:"}. {"Password","Passwort"}. {"Password Verification:","Passwort bestätigen:"}. {"Password Verification","Passwort bestätigen"}. {"Path to Dir","Pfad zum Verzeichnis"}. {"Path to File","Pfad zur Datei"}. {"Pending","Schwebend"}. {"Period: ","Zeitraum: "}. {"Permanent rooms","Permanente Chaträume"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Beachten sie, das diese Optionen nur die eingebaute Mnesia-Datenbank sichern. Wenn sie das ODBC-Modul verwenden, müssen sie die SQL-Datenbank manuell sichern."}. {"Please, wait for a while before sending new voice request","Bitte warten sie ein wenig, bevor sie eine weitere Anfrage für Sprachrechte senden"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","privat, "}. {"Protocol","Protokoll"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub-Abonnenten-Anfrage"}. {"Queries to the conference members are not allowed in this room","Anfragen an die Teilnehmer sind in diesem Raum nicht erlaubt"}. {"RAM and disc copy","RAM und Festplatte"}. {"RAM copy","Nur RAM"}. {"Raw","Unformatiert"}. {"Really delete message of the day?","Die Nachricht des Tages wirklich löschen?"}. {"Recipient is not in the conference room","Der Empfänger ist nicht im Raum"}. {"Register a Jabber account","Jabber Konto registrieren"}. {"Register","Anmelden"}. {"Registered nicknames","Registrierte Benutzernamen"}. {"Registered Users:","Registrierte Benutzer:"}. {"Registered Users","Registrierte Benutzer"}. {"Registration in mod_irc for ","Registrierung in mod_irc für "}. {"Remote copy","Fernkopie"}. {"Remove All Offline Messages","Alle Offline Nachrichten löschen"}. {"Remove","Entfernen"}. {"Remove User","Benutzer löschen"}. {"Replaced by new connection","Durch neue Verbindung ersetzt"}. {"Resources","Ressourcen"}. {"Restart","Neustart"}. {"Restart Service","Dienst neustarten"}. {"Restore Backup from File at ","Datenwiederherstellung aus der Datei "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Stelle binäre Sicherung beim nächsten ejabberd-Neustart wieder her (benötigt weniger Speicher):"}. {"Restore binary backup immediately:","Stelle binäre Sicherung sofort wieder her:"}. {"Restore plain text backup immediately:","Stelle Klartext-Sicherung sofort wieder her:"}. {"Restore","Wiederherstellung"}. {"Room Configuration","Raum-Konfiguration"}. {"Room creation is denied by service policy","Anlegen des Raumes aufgrund der Dienstrichtlinien verweigert"}. {"Room description","Raum Beschreibung"}. {"Room Occupants","Teilnehmer in diesem Raum"}. {"Room title","Raumname"}. {"Roster","Kontaktliste"}. {"Roster of ","Kontaktliste von "}. {"Roster size","Kontaktlistengröße"}. {"RPC Call Error","Fehler bei RPC-Aufruf"}. {"Running Nodes","Aktive Knoten"}. {"~s access rule configuration","~s Zugangsregel-Konfiguration"}. {"Saturday","Samstag"}. {"Script check","Script-Überprüfung"}. {"Search Results for ","Suchergebnisse für "}. {"Search users in ","Benutzer suchen in "}. {"Send announcement to all online users on all hosts","Sende Ankündigung an alle angemeldeten Benutzer auf allen Hosts"}. {"Send announcement to all online users","Sende Ankündigung an alle angemeldeten Benutzer"}. {"Send announcement to all users on all hosts","Sende Ankündigung an alle Benutzer auf allen Hosts"}. {"Send announcement to all users","Sende Ankündigung an alle Benutzer"}. {"September","September"}. {"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Setze Nachricht des Tages und sende sie an alle angemeldeten Benutzer"}. {"Set message of the day on all hosts and send to online users","Setze Nachricht des Tages auf allen Hosts und sende sie an alle angemeldeten Benutzer"}. {"Shared Roster Groups","Gruppen der gemeinsamen Kontaktliste"}. {"Show Integral Table","Integrale Tabelle anzeigen"}. {"Show Ordinary Table","Gewöhnliche Tabelle anzeigen"}. {"Shut Down Service","Dienst herunterfahren"}. {"~s invites you to the room ~s","~s lädt sie in den Raum ~s ein"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Einige Jabber Client Programme speichern ihr Passwort auf ihrem Computer. Verwenden sie diese Möglichkeit nur auf Computern, die sie als sicher einstufen."}. {"~s's Offline Messages Queue","~s's Offline-Nachrichten-Warteschlange"}. {"Start Modules at ","Starte Module auf "}. {"Start Modules","Module starten"}. {"Start","Starten"}. {"Statistics of ~p","Statistiken von ~p"}. {"Statistics","Statistiken"}. {"Stop Modules at ","Stoppe Module auf "}. {"Stop Modules","Module stoppen"}. {"Stopped Nodes","Angehaltene Knoten"}. {"Stop","Stoppen"}. {"Storage Type","Speichertyp"}. {"Store binary backup:","Speichere binäre Sicherung:"}. {"Store plain text backup:","Speichere Klartext-Sicherung:"}. {"Subject","Betreff"}. {"Submit","Senden"}. {"Submitted","Gesendet"}. {"Subscription","Abonnement"}. {"Sunday","Sonntag"}. {"That nickname is already in use by another occupant","Dieser Benutzername wird bereits von einem Teilnehmer genutzt"}. {"That nickname is registered by another person","Dieser Benutzername wurde bereits von jemand anderem registriert"}. {"The CAPTCHA is valid.","Die Verifizierung ist gültig."}. {"The CAPTCHA verification has failed","Die CAPTCHA Verifizierung schlug fehl"}. {"the password is","das Passwort lautet"}. {"The password is too weak","Das Passwort ist zu einfach"}. {"The password of your Jabber account was successfully changed.","Das Passwort von ihrem Jabber Konto wurde geändert."}. {"There was an error changing the password: ","Es trat ein Fehler beim Ändern des Passworts auf: "}. {"There was an error creating the account: ","Es trat ein Fehler beim Erstellen des Kontos auf: "}. {"There was an error deleting the account: ","Es trat ein Fehler beim Löschen des Kontos auf: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Groß/Klein-Schreibung spielt hierbei keine Rolle: macbeth ist gleich MacBeth und Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Diese Seite erlaubt das anlegen eines Jabber Kontos auf diesem Jabber Server. Ihre JID (Jabber IDentifier) setzt sich folgend zusammen: benutzername@server. Bitte lesen sie die Hinweise genau durch, um die Felder korrekt auszufüllen."}. {"This page allows to unregister a Jabber account in this Jabber server.","Diese Seite erlaubt es, ein Jabber Konto von diesem Server zu entfernen."}. {"This room is not anonymous","Dieser Raum ist nicht anonym"}. {"Thursday","Donnerstag"}. {"Time delay","Zeitverzögerung"}. {"Time","Zeit"}. {"To","An"}. {"Too many CAPTCHA requests","Zu viele CAPTCHA Anfragen"}. {"Too many unacked stanzas","Zu viele unbestätigte Stanzas"}. {"To ~s","An ~s"}. {"Total rooms","Alle Chaträume"}. {"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}. {"Transactions Aborted:","Abgebrochene Transaktionen:"}. {"Transactions Committed:","Durchgeführte Transaktionen:"}. {"Transactions Logged:","Protokollierte Transaktionen:"}. {"Transactions Restarted:","Neu gestartete Transaktionen:"}. {"Tuesday","Dienstag"}. {"Unable to generate a CAPTCHA","Konnte CAPTCHA nicht erstellen"}. {"Unauthorized","Nicht berechtigt"}. {"Unregister","Abmelden"}. {"Unregister a Jabber account","Jabber Konto entfernen"}. {"Update","Aktualisieren"}. {"Update message of the day (don't send)","Aktualisiere Nachricht des Tages (nicht senden)"}. {"Update message of the day on all hosts (don't send)","Aktualisiere Nachricht des Tages auf allen Hosts (nicht senden)"}. {"Update ~p","Aktualisierung ~p"}. {"Update plan","Aktualisierungsplan"}. {"Update script","Aktualisierungsscript"}. {"Uptime:","Betriebszeit:"}. {"Use of STARTTLS required","Verwendung von STARTTLS erforderlich"}. {"User","Benutzer"}. {"User Management","Benutzerverwaltung"}. {"Username:","Benutzername:"}. {"Users are not allowed to register accounts so quickly","Benutzer dürfen Konten nicht so schnell registrieren"}. {"Users","Benutzer"}. {"User ~s","Benutzer ~s"}. {"Users Last Activity","Letzte Benutzeraktivität"}. {"Validate","Validieren"}. {"vCard User Search","vCard-Benutzer-Suche"}. {"Virtual Hosts","Virtuelle Hosts"}. {"Visitors are not allowed to change their nicknames in this room","Besucher dürfen in diesem Raum ihren Benutzernamen nicht ändern"}. {"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken"}. {"Voice request","Anfrage für Sprachrechte"}. {"Voice requests are disabled in this conference","Anfragen für Sprachrechte sind in diesem Raum deaktiviert"}. {"Wednesday","Mittwoch"}. {"You can later change your password using a Jabber client.","Sie können das Passwort später mit einem Jabber Client Programm ändern."}. {"You have been banned from this room","Sie wurden aus diesem Raum verbannt"}. {"You must fill in field \"Nickname\" in the form","Sie müssen das Feld \"Benutzername\" ausfüllen"}. {"You need a client that supports x:data and CAPTCHA to register","Sie benötigen einen Client, der x:data und CAPTCHA unterstützt, um Ihren Benutzernamen zu registrieren"}. {"You need a client that supports x:data to register the nickname","Sie benötigen einen Client, der x:data unterstützt, um Ihren Benutzernamen zu registrieren"}. {"You need an x:data capable client to configure mod_irc settings","Sie benötigen einen Client, der x:data unterstützt, um die mod_irc-Einstellungen zu konfigurieren"}. {"You need an x:data capable client to search","Sie benötigen einen Client, der x:data unterstützt, um die Suche verwenden zu können"}. {"Your active privacy list has denied the routing of this stanza.","Ihre aktive Privacy Liste hat die Weiterleitung des Stanzas unterbunden."}. {"Your contact offline message queue is full. The message has been discarded.","Ihre Offline-Nachrichten-Warteschlange ist voll. Die Nachricht wurde verworfen."}. {"Your Jabber account was successfully created.","Ihr Jabber Konto wurde erfolgreich erstellt."}. {"Your Jabber account was successfully deleted.","Ihr Jabber Konto wurde erfolgreich gelöscht."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Ihre Nachrichten an ~s werden blockiert. Um dies zu ändern, besuchen sie ~s"}. ejabberd-18.01/priv/msgs/tr.po0000644000232200023220000017150513225664356016572 0ustar debalancedebalance# translation of tr.po to Turkish # Doruk Fisek , 2009, 2012. msgid "" msgstr "" "Project-Id-Version: tr\n" "PO-Revision-Date: 2012-04-17 11:18+0300\n" "Last-Translator: Doruk Fisek \n" "Language-Team: Turkish \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Turkish (türkçe)\n" "X-Generator: KBabel 1.11.4\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " konuyu değiştirdi: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Bu odaya girmek için parola gerekiyor" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Erişim Ayarları" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Erişim Kontrol Listelerinin Ayarlanması (ACL)" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Erişim Kontrol Listeleri (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Erişim Kuralları" #: mod_configure:1095 msgid "Access control lists" msgstr "Erişim kontrol listeleri (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Servis politikası gereği erişim engellendi" #: mod_configure:1113 msgid "Access rules" msgstr "Erişim kuralları" #: mod_configure:1772 msgid "Action on user" msgstr "Kullanıcıya uygulanacak eylem" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Jabber ID'si Ekle" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Yeni Ekle" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Kullanıcı Ekle" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Yönetim" #: mod_configure:1767 msgid "Administration of " msgstr "Yönetim : " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Yönetim yetkileri gerekli" #: mod_configure:507 msgid "All Users" msgstr "Tüm Kullanıcılar" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Tüm aktivite" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Kullanıcıların konu değiştirmesine izin ver" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Kullanıcıların diğer kullanıcıları sorgulamalarına izin ver" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Kullanıcıların davetiye göndermelerine izin ver" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Kullanıcıların özel mesaj göndermelerine izin ver" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Ziyaretçilerin takma isim değiştirmelerine izin ver" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Ziyaretçilerin özel mesaj göndermelerine izin ver" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" "Ziyaretçilerin varlık (presence) güncellemelerinde durum metni " "göndermelerine izin ver" #: mod_announce:611 msgid "Announcements" msgstr "Duyurular" #: mod_muc_log:476 msgid "April" msgstr "Nisan" #: mod_muc_log:480 msgid "August" msgstr "Ağustos" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Yedekle" #: mod_configure:584 msgid "Backup Management" msgstr "Yedek Yönetimi" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Yedek : " #: mod_configure:948 msgid "Backup to File at " msgstr "Dosyaya Yedekle : " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Kötü biçem" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Doğumgünü" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA web sayfası" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "İşlemci Zamanı:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Parola Değiştir" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Kullanıcı Parolasını Değiştir" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "İzin verilmeyen karakterler:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "İzin verilmeyen karakterler:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "İzin verilmeyen karakterler:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Sohbet odası ayarı değiştirildi" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Sohbet odası oluşturuldu" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Sohbet odası kaldırıldı" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Sohbet odası başlatıldı" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Sohbet odası durduruldu" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Sohbet Odaları" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Bu sunucuya kayıt olmak için bir kullanıcı ismi ve parola seçiniz" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Durdurulacak modülleri seçiniz" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Tabloların veri depolama tipini seçiniz" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Bu varlığın üyeliğini onaylayıp onaylamamayı seçiniz." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "İl" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Komutlar" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Konferans odası bulunamadı" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Ayarlar" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "~s odasının ayarları" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Bağlı Kaynaklar:" #: mod_irc:526 msgid "Connections parameters" msgstr "Bağlantı parametreleri" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Ülke" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Veritabanı" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Veritabanı Tablo Ayarları : " #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Veritabanı Tabloları : " #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Veritabanı" #: mod_muc_log:484 msgid "December" msgstr "Aralık" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Kullanıcılar öntanımlı olarak katılımcı olsun" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Seçilenleri Sil" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Kullanıcıyı Sil" #: mod_announce:629 msgid "Delete message of the day" msgstr "Günün mesajını sil" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Tüm sunuculardaki günün mesajını sil" #: mod_shared_roster:900 msgid "Description:" msgstr "Tanım:" #: mod_configure:889 msgid "Disc only copy" msgstr "Sadece disk kopyala" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Gösterilen Gruplar:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "Parolanızı kimseye söylemeyin, Jabber sunucusunun yöneticilerine bile." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Metin Dosyasına Döküm Alarak Yedekle : " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Metin Dosyasına Döküm Al" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Özellikleri Düzenle" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Ses isteğini kabul edin ya da reddedin" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementler" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "E-posta" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "parola :" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Kayıt tutma özelliğini aç" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Sunucu için kodlama ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Kullanıcı Oturumunu Kapat" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "{Module, [Options]} listesi giriniz" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Kaydettirmek istediğiniz takma ismi giriniz" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Yedek dosyasının yolunu giriniz" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "jabberd14 spool dosyası için yol giriniz" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "jabberd14 spool dosyası için yol giriniz" #: mod_configure:974 msgid "Enter path to text file" msgstr "Metin dosyasının yolunu giriniz" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Gördüğünüz metni giriniz" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı isimleri ve " "kodlamaları giriniz. 'İleri' tuşuna basınca karşınıza dolduracak daha fazla " "alan çıkacak. 'Tamamla' tuşuna basarak ayarları kaydedin." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı ismi, " "kodlamalar, kapılar (portlar) ve parolaları giriniz" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Sunucusu" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Hata" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Örnek: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"gizli\"}, {\"vendetta.fef.net" "\", \"iso8859-1\", 7000}], {\"irc.sometestserver.net\", \"utf-8\"}]" #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Sunucudaki tüm kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa " "aktar:" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Bir sunucudaki kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa " "aktar:" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Ses isteği onayınızdan JID bilginize ulaşılamadı" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Soyisim" #: mod_muc_log:474 msgid "February" msgstr "Şubat" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Eşleşen jabber kullanıcılarını aramak için formu doldurunuz (Alt dizgi " "eşlemek için alanın sonuna * ekleyin)" #: mod_muc_log:467 msgid "Friday" msgstr "Cuma" #: mod_offline:691 msgid "From" msgstr "Kimden" #: mod_configure:723 msgid "From ~s" msgstr "Kimden ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Tam İsim" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Bağlı Kullanıcı Sayısını Al" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Kayıtlı Kullanıcı Sayısını Al" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Kullanıcı Son Giriş Zamanınlarını Al" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Kullanıcı Parolasını Al" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Kullanıcı İstatistiklerini Al" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Ortanca İsim" #: mod_shared_roster:923 msgid "Group " msgstr "Group " #: mod_roster:916 msgid "Groups" msgstr "Gruplar" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Sunucu" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP adresleri" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Nakli (Transport)" #: mod_irc:496 msgid "IRC Username" msgstr "IRC Kullanıcı İsmi" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC kanalı (ilk # işaretini koymayın)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Düğüm bulunamadı" #: mod_irc:673 msgid "IRC server" msgstr "IRC sunucusu" #: mod_irc:756 msgid "IRC settings" msgstr "IRC ayarları" #: mod_irc:766 msgid "IRC username" msgstr "IRC kullanıcı ismi" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" "Eğer burada CAPTCHA resmini göremiyorsanız, web sayfasını ziyaret edin." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "IRC sunucuları için farklı kapılar (portlar), parolalar, kodlamalar " "belirtmek istiyorsanız, '{\"irc sunucusu\", \"kodlama\",\"kapı\",\"parola" "\"}' biçeminde değerlerle bu listeyi doldurunuz. Öntanımlı olarak bu servis " "\"~s\" kodlamasını, ~p \"kapısını\", \"boş\" parolasını kullanıyor." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Dizini İçe Aktar" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Dosyayı İçe Aktar" #: mod_configure:982 msgid "Import User from File at " msgstr "Dosyadan Kullanıcıları İçe Aktar : " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçeri Aktar" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Dizinden Kullanıcıları İçe Aktar : " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçe Aktar:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Kullanıcıları bir PIEFXIS dosyasından (XEP-0227) içe aktar:" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Jabberd 1.4 Spool Dizininden Kullanıcıları İçe Aktar:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Uygunsuz mesaj tipi" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Giden s2s Bağlantıları:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Yanlış parola" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Yanlış parola" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Bu konferansta ses istekleri etkisizleştirilmiş durumda." #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Özel mesaj gönderilmesine izin verilmiyor" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "\"groupchat\" tipinde özel mesajlar gönderilmesine izin verilmiyor" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Konferansa özel mesajlar gönderilmesine izin verilmiyor" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Jabber Hesap Kaydı" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Ocak" #: mod_irc:665 msgid "Join IRC channel" msgstr "IRC kanalına katıl" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Buradaki IRC kanalına katıl." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "IRC kanalına bu Jabber ID'si ile katıl: ~s" #: mod_muc_log:479 msgid "July" msgstr "Temmuz" #: mod_muc_log:478 msgid "June" msgstr "Haziran" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Son Aktivite" #: mod_configure:1646 msgid "Last login" msgstr "Son giriş" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Geçen ay" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Geçen yıl" #: mod_configure:941 msgid "List of modules to start" msgstr "Başlatılacak modüllerin listesi" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Dinlenen Kapılar (Portlar)" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Dinlenen Kapılar (Portlar) : " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Düşük seviye güncelleme betiği" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Katılımcı listesini herkese açık hale getir" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Odayı insan doğrulaması (captcha) korumalı hale getir" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Odayı sadece üyelere açık hale getir" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Odayı moderasyonlu hale getir" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Odayı parola korumalı hale getir" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Odayı kalıcı hale getir" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Odayı herkes tarafından aranabilir hale getir" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "IRC kullanıcı ismi" #: mod_muc_log:475 msgid "March" msgstr "Mart" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Odada En Fazla Bulunabilecek Kişi Sayısı" #: mod_muc_log:477 msgid "May" msgstr "Mayıs" #: mod_shared_roster:907 msgid "Members:" msgstr "Üyeler:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Bu odaya girmek için üyelik gerekiyor" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Parolanızı ezberleyin ya da bir kağıda yazarak güvenli bir yerde saklayın. " "Jabber'da parolanızı unutursanız, otomatik kurtarmak için bir yöntem " "bulunmuyor." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Bellek" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Mesajın gövdesi" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Ortanca İsim" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Moderatör yetkileri gerekli" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Değişen modüller" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modül" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Modüller" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Modüller : " #: mod_muc_log:463 msgid "Monday" msgstr "Pazartesi" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "İsim" #: mod_shared_roster:896 msgid "Name:" msgstr "İsim:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Asla" #: mod_register_web:377 msgid "New Password:" msgstr "Yeni Parola:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Takma isim" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Takma İsim Kaydı : " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "~s takma ismi odada yok" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Düğüm bulunamadı" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Veri Yok" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Duyuru mesajının gövdesi yok" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Düğüm bulunamadı" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Düğüm bulunamadı" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Düğüm bulunamadı" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Düğüm bulunamadı" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Düğüm " #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Düğümler" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Hiçbiri" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Bulunamadı" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "Kasım" #: mod_configure:1212 msgid "Number of online users" msgstr "Bağlı kullanıcı sayısı" #: mod_configure:1202 msgid "Number of registered users" msgstr "Kayıtlı kullanıcı sayısı" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "Tamam" #: mod_muc_log:482 msgid "October" msgstr "Ekim" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Çevirim-dışı Mesajlar" #: mod_offline:761 msgid "Offline Messages:" msgstr "Çevirim-dışı Mesajlar:" #: mod_register_web:373 msgid "Old Password:" msgstr "Eski Parola:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Bağlı" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Bağlı Kullanıcılar" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Bağlı Kullanıcılar:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Sadece moderatörlerin bu odanın konusunu değiştirmesine izin veriliyor" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Sadece moderatörlerin ve katılımcıların bu odanın konusunu değiştirmesine " "izin veriliyor" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Sadece moderatörlerin bu odanın konusunu değiştirmesine izin veriliyor" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Yalnız moderatörler ses isteklerini onaylayabilir" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Sadece oda sakinlerinin konferansa mesaj göndermesine izin veriliyor" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Sadece oda sakinlerinin konferansa sorgu göndermesine izin veriliyor" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Sadece servis yöneticileri servis mesajı gönderebilirler" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Seçenekler" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Kurum İsmi" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Kurumun İlgili Birimi" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Giden s2s Bağlantıları" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Giden s2s Bağlantıları:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Sahip yetkileri gerekli" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Parola" #: mod_configure:1130 msgid "Password Verification" msgstr "Parola Doğrulaması" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Parola Doğrulaması:" #: mod_irc:822 msgid "Password ~b" msgstr "Parola ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Parola:" #: mod_configure:998 msgid "Path to Dir" msgstr "Dizinin Yolu" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Dosyanın Yolu" #: mod_roster:915 msgid "Pending" msgstr "Sıra Bekleyen" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periyot:" #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "odadan ayrıldı" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Bu seçeneklerin sadece gömülü Mnesia veritabanını yedekleyeceğine dikkat " "edin. Eğer ODBC modülünü kullanıyorsanız, SQL veritabanınızı da ayrıca " "yedeklemeniz gerekiyor." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Lütfen yeni bir ses isteği göndermeden önce biraz bekleyin" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Kapı (Port)" #: mod_irc:828 msgid "Port ~b" msgstr "Kapı (Port) ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub üye isteği" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Yayınla-Üye Ol" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Bu odada konferans üyelerine sorgu yapılmasına izin verilmiyor" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM ve disk kopyala" #: mod_configure:889 msgid "RAM copy" msgstr "RAM kopyala" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "RPC Çağrı Hatası" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Ham" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Günün mesajını silmek istediğinize emin misiniz?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Alıcı konferans odasında değil" #: mod_register_web:275 msgid "Register" msgstr "Kayıt Ol" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Bir Jabber hesabı kaydet" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Kayıtlı Kullanıcılar" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Kayıtlı Kullanıcılar:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Kayıtlı Kullanıcılar" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "mod_irc'ye kayıt : " #: mod_configure:889 msgid "Remote copy" msgstr "Uzak kopyala" #: mod_roster:963 msgid "Remove" msgstr "Kaldır" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Tüm Çevirim-dışı Mesajları Kaldır" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Kullanıcıyı Kaldır" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Eski bağlantı yenisi ile değiştirildi" #: mod_configure:1675 msgid "Resources" msgstr "Kaynaklar" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Tekrar Başlat" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Servisi Tekrar Başlat" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Yedekten Geri Al" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Dosyadaki Yedekten Geri Al : " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "ejabberd'nin bir sonraki tekrar başlatılışında ikili yedekten geri al (daha " "az bellek gerektirir)" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Hemen ikili yedekten geri al:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Hemen düz metin yedekten geri al" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Oda Ayarları" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Oda Sakini Sayısı" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Odanın oluşturulması servis politikası gereği reddedildi" #: mod_muc_log:1064 msgid "Room description" msgstr "Oda tanımı" #: mod_muc_log:1027 msgid "Room title" msgstr "Oda başlığı" #: mod_roster:1082 msgid "Roster" msgstr "Kontak Listesi" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Kontak Listesi : " #: mod_configure:1671 msgid "Roster size" msgstr "İsim listesi boyutu" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Çalışan Düğümler" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Cumartesi" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Betik kontrolü" #: mod_vcard:453 msgid "Search Results for " msgstr "Arama sonuçları : " #: mod_vcard:427 msgid "Search users in " msgstr "Kullanıcılarda arama yap : " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Duyuruyu tüm bağlı kullanıcılara yolla" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Duyuruyu tüm sunuculardaki tüm bağlı kullanıcılara yolla" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Duyuruyu tüm kullanıcılara yolla" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Tüm sunuculardaki tüm kullanıcılara duyuru yolla" #: mod_muc_log:481 msgid "September" msgstr "Eylül" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Sunucu ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Sunucu:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Günün mesajını belirle" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Tüm sunucularda günün mesajını belirle ve bağlı tüm kullanıcılara gönder" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Paylaşımlı Kontak Listesi Grupları" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Önemli Tabloyu Göster" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Sıradan Tabloyu Göster" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Servisi Kapat" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Bazı Jabber istemcileri parolanızı bilgisayarınızda saklayabilir. Bu " "özelliği ancak bilgisayarın güvenli olduğuna güveniyorsanız kullanın." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Başlat" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Modülleri Başlat" #: mod_configure:936 msgid "Start Modules at " msgstr "Modülleri Başlat : " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "İstatistikler" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "~p istatistikleri" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Durdur" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Modülleri Durdur" #: mod_configure:918 msgid "Stop Modules at " msgstr "Modülleri Durdur : " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Durdurulmuş Düğümler" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Depolama Tipi" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "İkili yedeği sakla:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Düz metin yedeği sakla:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Konu" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Gönder" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Gönderilenler" #: mod_roster:914 msgid "Subscription" msgstr "Üyelik" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Pazar" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Takma isim odanın başka bir sakini tarafından halihazırda kullanımda" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "O takma isim başka biri tarafından kaydettirilmiş" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "İnsan doğrulaması (captcha) geçerli." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "CAPTCHA doğrulaması başarısız oldu" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Parola çok zayıf" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Jabber hesabınızın parolası başarıyla değiştirildi." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Parolanın değiştirilmesi sırasında bir hata oluştu:" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Hesap oluşturulurken bir hata oluştu:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Hesabın silinmesi sırasında bir hata oluştu:" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Burada büyük küçük harfi yapılmaz: macbeth ile MacBeth ve Macbeth aynıdır." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Bu sayfa bu Jabber sunucusunda bir Jabber hesabı oluşturulmasına olanak " "tanıyor. Sizin JID'iniz (Jabber Tanımlayıcısı) şu biçemde olacaktır: " "kullanici_adi@sunucu. Lütfen alanları doğru doldurabilmek için yönergeleri " "dikkatle okuyunuz." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Bu sayfa bu Jabber sunucusundan bir Jabber hesabının kaydını silmeye olanak " "tanıyor." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Bu oda anonim değil" #: mod_muc_log:466 msgid "Thursday" msgstr "Perşembe" #: mod_offline:690 msgid "Time" msgstr "Zaman" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Zaman gecikmesi" #: mod_offline:692 msgid "To" msgstr "Kime" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Kime ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Çok fazla CAPTCHA isteği" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Bu konferansta ses istekleri etkisizleştirilmiş durumda." #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Sohbet Odaları" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Trafik oran sınırı aşıldı" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "İptal Edilen Hareketler (Transactions):" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Tamamlanan Hareketler (Transactions Committed):" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Kaydı Tutulan Hareketler (Transactions):" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Tekrar Başlatılan Hareketler (Transactions):" #: mod_muc_log:464 msgid "Tuesday" msgstr "Salı" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "İnsan doğrulaması (CAPTCHA) oluşturulamadı" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Yetkisiz" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Kaydı Sil" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Bir Jabber hesabı kaydı sil" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "GÜncelle" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Günün mesajını güncelle (gönderme)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Tüm sunuculardaki günün mesajını güncelle (gönderme)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Planı güncelle" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Betiği Güncelle" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Güncelle " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Hizmet Süresi:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "STARTTLS kullanımı gereklidir" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "STARTTLS kullanımı gereklidir" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Kullanıcı" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Kullanıcı Yönetimi" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Düğüm bulunamadı" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Kullanıcı " #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Kullanıcı adı:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Kullanıcılar" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Kullanıcıların Son Aktiviteleri" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Kullanıcıların bu kadar hızlı hesap açmalarına izin verilmiyor" #: mod_roster:954 msgid "Validate" msgstr "Geçerli" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Sanal Sunucuları" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "" "Bu odada ziyaretçilerin takma isimlerini değiştirmesine izin verilmiyor" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "" "Ziyaretçilerin odadaki tüm sakinlere mesaj göndermesine izin verilmiyor" #: mod_muc_room:3879 msgid "Voice request" msgstr "Ses isteği" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Bu konferansta ses istekleri etkisizleştirilmiş durumda." #: mod_muc_log:465 msgid "Wednesday" msgstr "Çarşamba" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" "Parolanızı daha sonra bir Jabber istemci kullanarak değiştirebilirsiniz." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Bu odaya girmeniz yasaklandı" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Formda \"Takma isim\" alanını doldurmanız gerekiyor" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Takma isminizi kaydettirmek için x:data ve CAPTCHA destekleyen bir istemciye " "gereksinimiz var" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Takma isminizi kaydettirmek için x:data destekleyen bir istemciye " "gereksinimiz var" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "mod_irc ayarlarını düzenlemek için x:data becerisine sahip bir istemciye " "gereksinimiz var" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "" "Arama yapabilmek için x:data becerisine sahip bir istemciye gereksinimiz var" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Özel mesaj gönderilmesine izin verilmiyor" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Jabber hesabınız başarıyla oluşturuldu." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Jabber hesabınız başarıyla silindi." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Etkin mahremiyet listeniz bu bölümün yönlendirilmesini engelledi." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "Çevirim-dışı mesaj kuyruğunuz dolu. Mesajını dikkate alınmadı." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "~s kullanıcısına mesajlarınız engelleniyor. Durumu düzeltmek için ~s " "adresini ziyaret ediniz." #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC modülü" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC modülü" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe modülü" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams modülü" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Yöneticisi" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard modülü" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "odaya girmesi yasaklandı" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "odadan atıldı" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "sistem kapandığından dolayı atıldı" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "ilişki değişikliğinden dolayı atıldı" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "oda üyelere-özel hale getirildiğinden dolayı atıldı" #: mod_muc_log:410 msgid "is now known as" msgstr "isim değiştirdi :" #: mod_muc_log:370 msgid "joins the room" msgstr "odaya katıldı" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "odadan ayrıldı" #: mod_muc_room:3856 msgid "private, " msgstr "özel" #: mod_muc_room:3955 msgid "the password is" msgstr "parola :" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard Kullanıcı Araması" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s erişim kuralları ayarları" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s sizi ~s odasına davet ediyor" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s Kullanıcısının Mesaj Kuyruğu" #~ msgid "No resource provided" #~ msgstr "Hiç kaynak sağlanmadı" #, fuzzy #~ msgid "Server" #~ msgstr "Sunucu:" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s geçersiz" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Geçersiz ilişki: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Geçersiz rol: ~s" #~ msgid "No limit" #~ msgstr "Sınırsız" #~ msgid "Present real Jabber IDs to" #~ msgstr "Gerçek Jabber ID'lerini göster :" #~ msgid "moderators only" #~ msgstr "sadece moderatörler" #~ msgid "anyone" #~ msgstr "herkes" #, fuzzy #~ msgid "Moderator" #~ msgstr "sadece moderatörler" #~ msgid "nobody" #~ msgstr "hiç kimse" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Ziyaretçilerin ses isteğine göndermelerine izin ver" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Ses istekleri arasında olabilecek en az aralık (saniye olarak)" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "CAPTCHA doğrulamasını şu Jabber ID'ler için yapma" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Odayı ayarlamak için x:data becerisine sahip bir istemciye gereksinimiz " #~ "var" #~ msgid "Number of occupants" #~ msgstr "Oda sakini sayısı" #~ msgid "User JID" #~ msgstr "Kullanıcı JID" #~ msgid "Grant voice to this person?" #~ msgstr "Bu kişiye ses verelim mi?" #~ msgid "Node ID" #~ msgstr "Düğüm ID" #~ msgid "Subscriber Address" #~ msgstr "Üye Olanın Adresi" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Bu Jabber ID bu pubsub düğümüne üye olmasına izin verilsin mi?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Yükleri (payload) olay uyarıları ile beraber gönder" #~ msgid "Deliver event notifications" #~ msgstr "Olay uyarıları gönderilsin" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Düğüm ayarları değiştiğinde üyeleri uyar" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Bir düğüm silindiğinde üyeleri uyar" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Düğümden öğeler kaldırıldığında üyeleri uyar" #~ msgid "Persist items to storage" #~ msgstr "Öğeleri depoda kalıcı hale getir" #~ msgid "A friendly name for the node" #~ msgstr "Düğüm için dostane bir isim" #~ msgid "Max # of items to persist" #~ msgstr "Kalıcı hale getirilecek en fazla öğe sayısı" #~ msgid "Whether to allow subscriptions" #~ msgstr "Üyeliklere izin verilsin mi" #~ msgid "Specify the access model" #~ msgstr "Erişim modelini belirtiniz" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Üye olunmasına izin verilen kontak listesi grupları" #~ msgid "Specify the publisher model" #~ msgstr "Yayıncı modelini belirtiniz" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "İlgili yayıncı çevirimdışı olunca tüm onunla ilgili olanları sil" #~ msgid "Specify the event message type" #~ msgstr "Olay mesaj tipini belirtiniz" #~ msgid "Max payload size in bytes" #~ msgstr "En fazla yük (payload) boyutu (bayt olarak)" #~ msgid "When to send the last published item" #~ msgstr "Son yayınlanan öğe ne zaman gönderilsin" #~ msgid "Only deliver notifications to available users" #~ msgstr "Uyarıları sadece durumu uygun kullanıcılara ulaştır" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Bir düğüm ile bağlantılı koleksiyonlar" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Eşleşen jabber kullanıcılarını aramak için alanları doldurunuz" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Giden s2s Sunucuları" #~ msgid "Delete" #~ msgstr "Sil" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "Bu katılımcı bir hata mesajı gönderdiği için odadan atıldı" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Bu katılımcı başka bir katılımcıya bir hata mesajı gönderdiği için odadan " #~ "atıldı" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Bu katılımcı bir hata varlığı (presence) gönderdiği için odadan atıldı" ejabberd-18.01/priv/msgs/sv.msg0000644000232200023220000004350413225664356016742 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Åtkomstkonfiguration"}. {"Access Control List Configuration","Konfiguera ACL"}. {"Access control lists","ACL"}. {"Access Control Lists","ACL"}. {"Access denied by service policy","Åtkomst nekad enligt lokal policy"}. {"Access rules","Åtkomstregler"}. {"Access Rules","Åtkomstregler"}. {"Action on user","Handling mot användare"}. {"Add Jabber ID","Lägg till Jabber ID"}. {"Add New","Lägg till ny"}. {"Add User","Lägg till användare"}. {"Administration","Administration"}. {"Administration of ","Administration av "}. {"Administrator privileges required","Administrationsprivilegier krävs"}. {"All activity","All aktivitet"}. {"Allow users to change the subject","Tillåt användare att byta ämne"}. {"Allow users to query other users","Tillåt användare att söka efter andra användare"}. {"Allow users to send invites","Tillåt användare att skicka inbjudningar"}. {"Allow users to send private messages","Tillåt användare att skicka privata meddelanden"}. {"Allow visitors to change nickname","Tillåt gäster att kunna ändra smeknamn"}. {"Allow visitors to send status text in presence updates","Tillåt gäster att skicka statustext som uppdatering"}. {"All Users","Alla användare"}. {"Announcements","Meddelanden"}. {"April","April"}. {"August","Augusti"}. {"Backup Management","Hantera säkerhetskopior"}. {"Backup","Säkerhetskopiera"}. {"Backup to File at ","Säkerhetskopiera till fil på "}. {"Bad format","Dåligt format"}. {"Birthday","Födelsedag"}. {"Change Password","Ändra lösenord"}. {"Change User Password","Andra användarlösenord"}. {"Chatroom configuration modified","Chattrum konfiguration modifierad"}. {"Chatrooms","Chattrum"}. {"Choose a username and password to register with this server","Välj ett användarnamn och lösenord för att registrera mot denna server"}. {"Choose modules to stop","Välj vilka moduler som skall stoppas"}. {"Choose storage type of tables","Välj lagringstyp för tabeller"}. {"Choose whether to approve this entity's subscription.","Välj om du vill godkänna hela denna prenumertion."}. {"City","Stad"}. {"Commands","Kommandon"}. {"Conference room does not exist","Rummet finns inte"}. {"Configuration","Konfiguration"}. {"Configuration of room ~s","Konfiguration för ~s"}. {"Connected Resources:","Anslutna resurser:"}. {"Connections parameters","Uppkopplingsparametrar"}. {"Country","Land"}. {"CPU Time:","CPU tid"}. {"Database","Databas"}. {"Database Tables Configuration at ","Databastabellers konfiguration"}. {"December","December"}. {"Default users as participants","Gör om användare till deltagare"}. {"Delete message of the day on all hosts","Ta bort dagens meddelande på alla värdar"}. {"Delete message of the day","Ta bort dagens meddelande"}. {"Delete Selected","Tabort valda"}. {"Delete User","Ta bort användare"}. {"Description:","Beskrivning:"}. {"Disc only copy","Endast diskkopia"}. {"Displayed Groups:","Visade grupper:"}. {"Dump Backup to Text File at ","Dumpa säkerhetskopia till textfil på "}. {"Dump to Text File","Dumpa till textfil"}. {"Edit Properties","Redigera egenskaper"}. {"ejabberd IRC module","ejabberd IRC-modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Publish-Subscribe module","ejabberd publikprenumerations modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestrem modul"}. {"ejabberd vCard module","ejabberd vCard-modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"Elements","Elements"}. {"Email","Email"}. {"Enable logging","Möjliggör login"}. {"Encoding for server ~b","Encoding för server ~b"}. {"End User Session","Avsluta användarsession"}. {"Enter list of {Module, [Options]}","Skriv in en lista av {Module, [Options]}"}. {"Enter nickname you want to register","Skriv in smeknamnet du vill registrera"}. {"Enter path to backup file","Skriv in sökväg till fil för säkerhetskopia"}. {"Enter path to jabberd14 spool dir","Skriv in sökväg till spoolkatalog från jabberd14"}. {"Enter path to jabberd14 spool file","Skriv in sökväg till spoolfil från jabberd14"}. {"Enter path to text file","Skriv in sökväg till textfil"}. {"Enter the text you see","Skriv in sökväg till textfil"}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Skriv in användarnamn och textkodning du vill använda för att ansluta till IRC-servrar"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Fel"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportera data av alla användare i servern till en PIEFXIS fil (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportera data av användare i en host till PIEFXIS fil (XEP-0227):"}. {"Family Name","Efternamn"}. {"February","Februari"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Fyll i formuläret för att söka efter en användare (lägg till * på slutet av fältet för att hitta alla som börjar så)"}. {"Friday","Fredag"}. {"From","Från"}. {"From ~s","Från ~s"}. {"Full Name","Fullständigt namn"}. {"Get Number of Online Users","Hämta antal inloggade användare"}. {"Get Number of Registered Users","Hämta antal registrerade användare"}. {"Get User Last Login Time","Hämta användarens senast inloggade tid"}. {"Get User Password","Hämta användarlösenord"}. {"Get User Statistics","Hämta användarstatistik"}. {"Group ","Grupp "}. {"Groups","Grupper"}. {"has been banned","har blivit bannad"}. {"has been kicked because of an affiliation change","har blivit kickad p.g.a en ändring av tillhörighet"}. {"has been kicked because of a system shutdown","har blivit kickad p.g.a en systemnerstängning"}. {"has been kicked because the room has been changed to members-only","har blivit kickad p.g.a att rummet har ändrats till endast användare"}. {"has been kicked","har blivit kickad"}. {" has set the subject to: "," har satt ämnet till: "}. {"Host","Server"}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Om du vill specifiera textkodning för IRC-servrar, fyll i listan med värden i formatet '{\"irc server\", \"encoding\", port, \"password\"}'. Som standard används \"~s\", port ~p, no password."}. {"Import Directory","Importera katalog"}. {"Import File","Importera fil"}. {"Import user data from jabberd14 spool file:","Importera användare från jabberd14 Spool filer"}. {"Import User from File at ","Importera användare från fil på "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importera användardata från en PIEFXIS fil (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importera användare från jabberd14 Spool directory:"}. {"Import Users from Dir at ","Importera användare från katalog på "}. {"Import Users From jabberd14 Spool Files","Importera användare från jabberd14 Spool filer"}. {"Improper message type","Felaktig medelandetyp"}. {"Incorrect password","Fel lösenord"}. {"IP addresses","IP adresser"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC kanal (skriv inte första #)"}. {"IRC server","IRC-användarnamn"}. {"IRC settings","IRC Inställningar"}. {"IRC Transport","IRC transport"}. {"IRC username","IRC-användarnamn"}. {"IRC Username","IRC-användarnamn"}. {"is now known as","är känd som"}. {"It is not allowed to send private messages","Det ar inte tillåtet att skicka privata meddelanden"}. {"It is not allowed to send private messages of type \"groupchat\"","Det är inte tillåtet att skicka privata medelanden med typen \"groupchat\""}. {"It is not allowed to send private messages to the conference","Det är inte tillåtet att skicka privata medelanden till den här konferensen"}. {"Jabber ID","Jabber ID"}. {"January","Januari"}. {"Join IRC channel","Lägg till IRC kanal"}. {"joins the room","joinar rummet"}. {"Join the IRC channel here.","Lägg till IRC kanal här."}. {"Join the IRC channel in this Jabber ID: ~s","Lägg till IRC kanal till detta Jabber ID: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Senast aktivitet"}. {"Last login","Senaste login"}. {"Last month","Senaste månaden"}. {"Last year","Senaste året"}. {"leaves the room","lämnar rummet"}. {"Listened Ports at ","Lyssnande portar på "}. {"Listened Ports","Lyssnarport"}. {"List of modules to start","Lista av moduler som skall startas"}. {"Low level update script","Uppdaterade laglevel skript"}. {"Make participants list public","Gör deltagarlistan publik"}. {"Make room members-only","Gör om rummet till endast medlemmar"}. {"Make room moderated","Gör rummet modererat"}. {"Make room password protected","Gör losenorden i rummet publika"}. {"Make room persistent","Gör rummet permanent"}. {"Make room public searchable","Gör rummet publikt sökbart"}. {"March","Mars"}. {"Maximum Number of Occupants","Maximalt antal av användare"}. {"May","Maj"}. {"Membership is required to enter this room","Du måste vara medlem för att komma in i det här rummet"}. {"Members:","Medlemmar:"}. {"Memory","Minne"}. {"Message body","Meddelande kropp"}. {"Middle Name","Mellannamn"}. {"Moderator privileges required","Moderatorprivilegier krävs"}. {"Modified modules","Uppdaterade moduler"}. {"Module","Modul"}. {"Modules","Moduler"}. {"Monday","Måndag"}. {"Name:","Namn:"}. {"Name","Namn"}. {"Never","Aldrig"}. {"Nickname Registration at ","Registrera smeknamn på "}. {"Nickname ~s does not exist in the room","Smeknamnet ~s existerar inte i det här rummet"}. {"Nickname","Smeknamn"}. {"No body provided for announce message","Ingen kropp behövs för dessa meddelanden"}. {"No Data","Ingen data"}. {"Node not found","Noden finns inte"}. {"Nodes","Noder"}. {"None","Inga"}. {"Not Found","Noden finns inte"}. {"November","November"}. {"Number of online users","Antal inloggade användare"}. {"Number of registered users","Antal registrerade användare"}. {"October","Oktober"}. {"Offline Messages:","Offline meddelanden:"}. {"Offline Messages","Offline meddelanden"}. {"OK","OK"}. {"Online","Ansluten"}. {"Online Users","Anslutna användare"}. {"Online Users:","Inloggade användare"}. {"Only moderators and participants are allowed to change the subject in this room","Endast moderatorer och deltagare har tillåtelse att ändra ämnet i det här rummet"}. {"Only occupants are allowed to send messages to the conference","Utomstående får inte skicka medelanden till den här konferensen"}. {"Only occupants are allowed to send queries to the conference","Utomstående får inte skicka iq-queries till den här konferensen"}. {"Only service administrators are allowed to send service messages","Endast administratörer får skicka tjänstmeddelanden"}. {"Options","Parametrar"}. {"Organization Name","Organisationsnamn"}. {"Organization Unit","Organisationsenhet"}. {"Outgoing s2s Connections","Utgaende s2s anslutning"}. {"Outgoing s2s Connections:","Utgående s2s anslutning"}. {"Owner privileges required","Ägarprivilegier krävs"}. {"Packet","Paket"}. {"Password ~b","Lösenord ~b"}. {"Password:","Lösenord:"}. {"Password","Lösenord"}. {"Password Verification","Lösenordsverifikation"}. {"Path to Dir","Sökväg till katalog"}. {"Path to File","Sökväg till fil"}. {"Pending","Ännu inte godkända"}. {"Period: ","Period: "}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Kom ihåg att dessa inställningar endast tar backup pa builtin Mnesias databas. Om du använder ODBC modul så måste du ta backup på SQLs databas enskilt"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","privat, "}. {"Protocol","Protocol"}. {"Publish-Subscribe","Publikprenumeration"}. {"PubSub subscriber request","Pubsub prenumerationsforfrågan"}. {"Queries to the conference members are not allowed in this room","Det är förbjudet att skicka iq-queries till konferensdeltagare"}. {"RAM and disc copy","RAM- och diskkopia"}. {"RAM copy","RAM-kopia"}. {"Raw","Ra"}. {"Really delete message of the day?","Verkligen ta bort dagens meddelanden?"}. {"Recipient is not in the conference room","Mottagaren finns inte i rummet"}. {"Registered Users:","Registrerade användare"}. {"Registered Users","Registrerade användare"}. {"Registration in mod_irc for ","mod_irc-registrering för "}. {"Remote copy","Sparas inte lokalt"}. {"Remove","Ta bort"}. {"Remove User","Ta bort användare"}. {"Replaced by new connection","Ersatt av ny anslutning"}. {"Resources","Resurser"}. {"Restart","Omstart"}. {"Restart Service","Starta om servicen"}. {"Restore","Återställ"}. {"Restore Backup from File at ","Återställ säkerhetskopia från fil på "}. {"Restore binary backup after next ejabberd restart (requires less memory):","återställ den binära backupen efter nästa ejabberd omstart"}. {"Restore binary backup immediately:","återställ den binära backupen omedelbart"}. {"Restore plain text backup immediately:","återställ textbackup omedelbart"}. {"Room Configuration","Rumkonfiguration"}. {"Room creation is denied by service policy","Skapandet av rum är förbjudet enligt lokal policy"}. {"Room Occupants","Antal besökare"}. {"Room title","Rumstitel"}. {"Roster","Kontaktlista"}. {"Roster of ","Kontaktlista för "}. {"Roster size","Roster storlek"}. {"RPC Call Error","RPC Uppringningserror"}. {"Running Nodes","Körande noder"}. {"~s access rule configuration","Åtkomstregelkonfiguration för ~s"}. {"Saturday","Lördag"}. {"Script check","Skript kollat"}. {"Search Results for ","Sökresultat för"}. {"Search users in ","Sök efter användare på "}. {"Send announcement to all online users on all hosts","Sänd meddelanden till alla inloggade användare på alla värdar"}. {"Send announcement to all online users","Sänd meddelanden till alla inloggade användare"}. {"Send announcement to all users on all hosts","Sänd meddelanden till alla användare på alla värdar"}. {"Send announcement to all users","Sänd meddelanden till alla användare"}. {"September","September"}. {"Server ~b","Server ~b"}. {"Set message of the day and send to online users","Sätt dagens status meddelande och skicka till alla användare"}. {"Set message of the day on all hosts and send to online users","Sätt dagens status meddelande pa alla värdar och skicka till alla användare"}. {"Shared Roster Groups","Delade Rostergrupper"}. {"Show Integral Table","Visa kumulativ tabell"}. {"Show Ordinary Table","Visa normal tabell"}. {"Shut Down Service","Stäng ner servicen"}. {"~s invites you to the room ~s","~s bjöd in dig till rummet ~s"}. {"~s's Offline Messages Queue","~s's offline meddelandekö"}. {"Start Modules at ","Starta moduler på "}. {"Start Modules","Starta moduler"}. {"Start","Starta"}. {"Statistics of ~p","Statistik på ~p"}. {"Statistics","Statistik"}. {"Stop Modules at ","Stoppa moduler på "}. {"Stop Modules","Stanna moduler"}. {"Stopped Nodes","Stannade noder"}. {"Stop","Stoppa"}. {"Storage Type","Lagringstyp"}. {"Store binary backup:","Lagra den binära backupen"}. {"Store plain text backup:","Lagra textbackup"}. {"Subject","Ämne"}. {"Submit","Skicka"}. {"Submitted","Skicka in"}. {"Subscription","Prenumeration"}. {"Sunday","Söndag"}. {"That nickname is registered by another person","Smeknamnet är reserverat"}. {"The CAPTCHA is valid.","Din CAPTCHA är godkänd."}. {"the password is","Lösenordet är"}. {"This room is not anonymous","Detta rum är inte anonymt"}. {"Thursday","Torsdag"}. {"Time delay","Tidsförsening"}. {"Time","Tid"}. {"To ~s","Till ~s"}. {"To","Till"}. {"Traffic rate limit is exceeded","Trafikgränsen har överstigits"}. {"Transactions Aborted:","Transaktioner borttagna"}. {"Transactions Committed:","Transaktioner kommittade"}. {"Transactions Logged:","Transaktioner loggade "}. {"Transactions Restarted:","Transaktioner omstartade"}. {"Tuesday","Tisdag"}. {"Unauthorized","Ej auktoriserad"}. {"Update message of the day (don't send)","Uppdatera dagens status meddelande (skicka inte)"}. {"Update message of the day on all hosts (don't send)","Uppdatera dagens status meddelande på alla värdar (skicka inte)"}. {"Update plan","Uppdateringsplan"}. {"Update script","Uppdatera skript"}. {"Update","Uppdatera"}. {"Uptime:","Tid upp"}. {"Use of STARTTLS required","Du måste använda STARTTLS"}. {"User","Användarnamn"}. {"User Management","Användarmanagement"}. {"Users","Användare"}. {"Users are not allowed to register accounts so quickly","Det är inte tillåtet för användare att skapa konton så fort"}. {"Users Last Activity","Användarens senaste aktivitet"}. {"Validate","Validera"}. {"vCard User Search","vCard användare sök"}. {"Virtual Hosts","Virtuella servrar"}. {"Visitors are not allowed to change their nicknames in this room","Det är inte tillåtet for gäster att ändra sina smeknamn i detta rummet"}. {"Visitors are not allowed to send messages to all occupants","Besökare får inte skicka medelande till alla"}. {"Wednesday","Onsdag"}. {"You have been banned from this room","Du har blivit bannlyst från det här rummet"}. {"You must fill in field \"Nickname\" in the form","Du måste fylla i fält \"smeknamn\" i formen"}. {"You need an x:data capable client to configure mod_irc settings","Du behöer en klient som stöjer x:data för att konfigurera mod_irc"}. {"You need an x:data capable client to search","Du behöver en klient som stödjer x:data, för att kunna söka"}. {"Your contact offline message queue is full. The message has been discarded.","Din kontaktkö for offlinekontakter ar full"}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Dina meddelanden till ~s är blockerade. För att avblockera dem, gå till ~s"}. ejabberd-18.01/priv/msgs/pt.po0000644000232200023220000015522313225664356016567 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "Last-Translator: Iceburn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Portuguese (português)\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " colocou o tópico: " #: mod_muc_room:1893 #, fuzzy msgid "A password is required to enter this room" msgstr "É necessária a palavra-chave para poder entrar nesta sala" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configuração de acessos" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configuração da Lista de Controlo de Acesso" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Listas de Controlo de Acesso" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Regras de Acesso" #: mod_configure:1095 msgid "Access control lists" msgstr "Listas de Controlo de Acesso" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Acesso negado pela política de serviço" #: mod_configure:1113 msgid "Access rules" msgstr "Regras de acesso" #: mod_configure:1772 msgid "Action on user" msgstr "Acção no utilizador" #: mod_roster:982 #, fuzzy msgid "Add Jabber ID" msgstr "Adicionar Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Adicionar novo" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Adicionar utilizador" #: ejabberd_web_admin:687 ejabberd_web_admin:698 #, fuzzy msgid "Administration" msgstr "Administração de " #: mod_configure:1767 msgid "Administration of " msgstr "Administração de " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "São necessários privilégios de administrador" #: mod_configure:507 msgid "All Users" msgstr "Todos os utilizadores" #: ejabberd_web_admin:1010 #, fuzzy msgid "All activity" msgstr "Última actividade" #: mod_muc_log:1046 #, fuzzy msgid "Allow users to change the subject" msgstr "Permitir aos utilizadores mudar o tópico?" #: mod_muc_log:1052 #, fuzzy msgid "Allow users to query other users" msgstr "Permitir aos utilizadores consultar outros utilizadores?" #: mod_muc_log:1054 #, fuzzy msgid "Allow users to send invites" msgstr "Permitir que os utilizadores enviem convites?" #: mod_muc_log:1048 #, fuzzy msgid "Allow users to send private messages" msgstr "Permitir que os utilizadores enviem mensagens privadas?" #: mod_muc_log:1057 #, fuzzy msgid "Allow visitors to change nickname" msgstr "Permitir aos utilizadores mudar o tópico?" #: mod_muc_log:1050 #, fuzzy msgid "Allow visitors to send private messages to" msgstr "Permitir que os utilizadores enviem mensagens privadas?" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" #: mod_announce:611 msgid "Announcements" msgstr "" #: mod_muc_log:476 msgid "April" msgstr "" #: mod_muc_log:480 msgid "August" msgstr "" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Guardar cópia de segurança" #: mod_configure:584 msgid "Backup Management" msgstr "Gestão de cópias de segurança" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Guardar cópia de segurança" #: mod_configure:948 msgid "Backup to File at " msgstr "Guardar cópia de segurança para ficheiro em " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 #, fuzzy msgid "Bad format" msgstr "formato inválido" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Data de nascimento" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "" #: ejabberd_web_admin:2207 #, fuzzy msgid "CPU Time:" msgstr "Tempo de processador consumido" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Mudar palavra-chave" #: mod_configure:174 mod_configure:528 #, fuzzy msgid "Change User Password" msgstr "Mudar palavra-chave" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Mudar palavra-chave" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "" #: mod_muc_log:358 mod_muc_log:367 #, fuzzy msgid "Chatroom configuration modified" msgstr "Configuração para " #: mod_muc_log:453 #, fuzzy msgid "Chatroom is created" msgstr "Configuração para " #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Escolha um nome de utilizador e palavra-chave para se registar neste servidor" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Seleccione os módulos a parar" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Seleccione o tipo de armazenagem das tabelas" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Cidade" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "" #: mod_muc:461 msgid "Conference room does not exist" msgstr "A sala não existe" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configuração" #: mod_muc_room:3163 #, fuzzy msgid "Configuration of room ~s" msgstr "Configuração para " #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Recursos conectados:" #: mod_irc:526 msgid "Connections parameters" msgstr "" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "País" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "" #: mod_configure:879 #, fuzzy msgid "Database Tables Configuration at " msgstr "Configuração de tabelas da BD em " #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Tabelas da BD em " #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Tabelas da BD em " #: mod_muc_log:484 msgid "December" msgstr "" #: mod_muc_log:1044 #, fuzzy msgid "Default users as participants" msgstr "Os utilizadores são membros por omissão?" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Eliminar os seleccionados" #: mod_configure:168 mod_configure:522 mod_configure:1135 #, fuzzy msgid "Delete User" msgstr "Eliminar" #: mod_announce:629 #, fuzzy msgid "Delete message of the day" msgstr "Eliminar os seleccionados" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "" #: mod_shared_roster:900 #, fuzzy msgid "Description:" msgstr "Subscrição" #: mod_configure:889 msgid "Disc only copy" msgstr "Cópia apenas em disco" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Exporta cópia de segurança para ficheiro de texto em " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Exportar para ficheiro de texto" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Editar propriedades" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 #, fuzzy msgid "Email" msgstr "email" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "Mudar palavra-chave" #: mod_muc_log:1055 #, fuzzy msgid "Enable logging" msgstr "Guardar históricos?" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Introduza lista de {módulos, [opções]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Introduza a alcunha que quer registar" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Introduza o caminho do ficheiro de cópia de segurança" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Introduza o caminho para o directório de spools do jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Introduza o caminho para o ficheiro de spool do jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Introduza caminho para o ficheiro de texto" #: ejabberd_captcha:70 #, fuzzy msgid "Enter the text you see" msgstr "Introduza caminho para o ficheiro de texto" #: mod_irc:759 #, fuzzy msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Introduza o nome de utilizador e codificações de caracteres que quer usar ao " "conectar-se aos servidores de IRC" #: mod_irc:540 #, fuzzy msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Introduza o nome de utilizador e codificações de caracteres que quer usar ao " "conectar-se aos servidores de IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Servidor Jabber em Erlang" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Apelido" #: mod_muc_log:474 msgid "February" msgstr "" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 #, fuzzy msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "Preencha os campos para procurar utilizadores Jabber coincidentes" #: mod_muc_log:467 msgid "Friday" msgstr "" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nome completo" #: mod_configure:183 mod_configure:536 #, fuzzy msgid "Get Number of Online Users" msgstr "Utilizadores ligados" #: mod_configure:180 mod_configure:534 #, fuzzy msgid "Get Number of Registered Users" msgstr "Utilizadores registados" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 #, fuzzy msgid "Get User Password" msgstr "Palavra-chave" #: mod_configure:178 mod_configure:532 mod_configure:1188 #, fuzzy msgid "Get User Statistics" msgstr "Estatísticas" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Segundo nome" #: mod_shared_roster:923 #, fuzzy msgid "Group " msgstr "Grupos" #: mod_roster:916 msgid "Groups" msgstr "Grupos" #: ejabberd_web_admin:1365 #, fuzzy msgid "Host" msgstr "Nome do servidor" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "" #: mod_configure:1673 msgid "IP addresses" msgstr "" #: mod_irc:439 msgid "IRC Transport" msgstr "" #: mod_irc:496 msgid "IRC Username" msgstr "Nome do utilizador de IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Nodo não encontrado" #: mod_irc:673 #, fuzzy msgid "IRC server" msgstr "Nome do utilizador de IRC" #: mod_irc:756 msgid "IRC settings" msgstr "" #: mod_irc:766 #, fuzzy msgid "IRC username" msgstr "Nome do utilizador de IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" #: mod_irc:503 #, fuzzy msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Se deseja especificar codificações de caracteres diferentes para cada " "servidor IRC preencha esta lista con valores no formato '{\"servidor irc\", " "\"codificação\"}'. Este serviço usa por omissão a codificação \"~s\"." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importar directório" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importar ficheiro" #: mod_configure:982 msgid "Import User from File at " msgstr "Importar utilizador a partir do ficheiro em " #: mod_configure:586 #, fuzzy msgid "Import Users From jabberd14 Spool Files" msgstr "Importar utilizadores a partir de ficheiros da spool do jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importar utilizadores a partir do directório em " #: ejabberd_web_admin:2100 #, fuzzy msgid "Import user data from jabberd14 spool file:" msgstr "Importar utilizadores a partir de ficheiros da spool do jabberd14" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "" #: ejabberd_web_admin:2111 #, fuzzy msgid "Import users data from jabberd14 spool directory:" msgstr "Importar utilizadores a partir de ficheiros da spool do jabberd14" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Tipo de mensagem incorrecto" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Conexões S2S para fora" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Palavra-chave incorrecta" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Palavra-chave incorrecta" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Impedir o envio de mensagens normais para a sala" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 #, fuzzy msgid "It is not allowed to send private messages" msgstr "Impedir o envio de mensagens privadas para a sala" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Não é permitido enviar mensagens privadas do tipo \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Impedir o envio de mensagens privadas para a sala" #: mod_register_web:181 mod_register_web:189 #, fuzzy msgid "Jabber Account Registration" msgstr "Configuração das Listas de Controlo de Acesso do ejabberd" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "" #: mod_muc_log:473 msgid "January" msgstr "" #: mod_irc:665 msgid "Join IRC channel" msgstr "" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "" #: mod_muc_log:479 msgid "July" msgstr "" #: mod_muc_log:478 msgid "June" msgstr "" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Última actividade" #: mod_configure:1646 msgid "Last login" msgstr "" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "" #: mod_configure:941 msgid "List of modules to start" msgstr "Lista de módulos a iniciar" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 #, fuzzy msgid "Listened Ports" msgstr "Portas em escuta em " #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Portas em escuta em " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "" #: mod_muc_log:1033 #, fuzzy msgid "Make participants list public" msgstr "Tornar pública a lista de participantes?" #: mod_muc_log:1062 #, fuzzy msgid "Make room CAPTCHA protected" msgstr "Proteger a sala com palavra-chave?" #: mod_muc_log:1040 #, fuzzy msgid "Make room members-only" msgstr "Tornar a sala exclusiva a membros?" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Tornar a sala moderada" #: mod_muc_log:1035 #, fuzzy msgid "Make room password protected" msgstr "Proteger a sala com palavra-chave?" #: mod_muc_log:1029 #, fuzzy msgid "Make room persistent" msgstr "Tornar a sala permanente?" #: mod_muc_log:1031 #, fuzzy msgid "Make room public searchable" msgstr "Tornar a sala publicamente visível?" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Nome do utilizador de IRC" #: mod_muc_log:475 msgid "March" msgstr "" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "" #: mod_muc_log:477 msgid "May" msgstr "" #: mod_shared_roster:907 msgid "Members:" msgstr "" #: mod_muc_room:1833 #, fuzzy msgid "Membership is required to enter this room" msgstr "É necessário ser membro desta sala para poder entrar" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memória" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Segundo nome" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "São necessários privilégios de moderador" #: ejabberd_web_admin:2279 #, fuzzy msgid "Modified modules" msgstr "Iniciar módulos" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Módulo" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Módulos" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Parar módulos em " #: mod_muc_log:463 msgid "Monday" msgstr "" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nome" #: mod_shared_roster:896 #, fuzzy msgid "Name:" msgstr "Nome" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nunca" #: mod_register_web:377 #, fuzzy msgid "New Password:" msgstr "Palavra-chave:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Alcunha" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registo da alcunha em " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "A alcunha ~s não existe na sala" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Nodo não encontrado" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Nodo não encontrado" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Nodo não encontrado" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Nodo não encontrado" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nodo não encontrado" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Nodo" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nodos" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nenhum" #: ejabberd_web_admin:1033 #, fuzzy msgid "Not Found" msgstr "Nodo não encontrado" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 #, fuzzy msgid "November" msgstr "Nunca" #: mod_configure:1212 #, fuzzy msgid "Number of online users" msgstr "Utilizadores ligados" #: mod_configure:1202 #, fuzzy msgid "Number of registered users" msgstr "Utilizadores registados" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "" #: ejabberd_web_admin:1487 #, fuzzy msgid "Offline Messages" msgstr "Mensagens diferidas" #: mod_offline:761 #, fuzzy msgid "Offline Messages:" msgstr "Mensagens diferidas:" #: mod_register_web:373 #, fuzzy msgid "Old Password:" msgstr "Palavra-chave:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Ligado" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Utilizadores ligados" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 #, fuzzy msgid "Online Users:" msgstr "Utilizadores ligados" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Só os moderadores podem mudar o tópico desta sala" #: mod_muc_room:773 #, fuzzy msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Só os moderadores e os participantes podem mudar o tópico desta sala" #: mod_muc_room:778 #, fuzzy msgid "Only moderators are allowed to change the subject in this room" msgstr "Só os moderadores podem mudar o tópico desta sala" #: mod_muc_room:917 #, fuzzy msgid "Only moderators can approve voice requests" msgstr "Permitir que os utilizadores enviem convites?" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Só os ocupantes podem enviar mensagens para a sala" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Só os ocupantes podem enviar consultas para a sala" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Só os administradores do serviço têm permissão para enviar mensagens de " "serviço" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opções" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nome da organização" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unidade da organização" #: mod_configure:508 #, fuzzy msgid "Outgoing s2s Connections" msgstr "Conexões S2S para fora" #: ejabberd_web_admin:1583 #, fuzzy msgid "Outgoing s2s Connections:" msgstr "Conexões S2S para fora" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "São necessários privilégios de dono" #: mod_offline:693 msgid "Packet" msgstr "Pacote" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Palavra-chave" #: mod_configure:1130 msgid "Password Verification" msgstr "" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "" #: mod_irc:822 #, fuzzy msgid "Password ~b" msgstr "Palavra-chave" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Palavra-chave:" #: mod_configure:998 msgid "Path to Dir" msgstr "Caminho para o directório" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Caminho do ficheiro" #: mod_roster:915 msgid "Pending" msgstr "Pendente" #: ejabberd_web_admin:994 msgid "Period: " msgstr "" #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "" #: mod_adhoc:168 mod_adhoc:259 #, fuzzy msgid "Ping" msgstr "Pendente" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" #: mod_adhoc:274 msgid "Pong" msgstr "" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Porta" #: mod_irc:828 #, fuzzy msgid "Port ~b" msgstr "Porta" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 #, fuzzy msgid "Protocol" msgstr "Porta" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Nesta sala não são permitidas consultas aos seus membros" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Cópia em RAM e em disco" #: mod_configure:889 msgid "RAM copy" msgstr "Cópia em RAM" #: ejabberd_web_admin:1890 #, fuzzy msgid "RPC Call Error" msgstr "Erro na chamada RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 #, fuzzy msgid "Raw" msgstr "modo texto" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "O destinatário não está na sala" #: mod_register_web:275 #, fuzzy msgid "Register" msgstr "Lista de contactos" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "" #: ejabberd_web_admin:1366 #, fuzzy msgid "Registered Users" msgstr "Utilizadores registados" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 #, fuzzy msgid "Registered Users:" msgstr "Utilizadores registados" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Utilizadores registados" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registo no mod_irc para" #: mod_configure:889 msgid "Remote copy" msgstr "Cópia remota" #: mod_roster:963 msgid "Remove" msgstr "Remover" #: mod_offline:765 #, fuzzy msgid "Remove All Offline Messages" msgstr "Mensagens diferidas" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Eliminar utilizador" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "" #: mod_configure:1675 #, fuzzy msgid "Resources" msgstr "Restaurar" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Reiniciar" #: mod_configure:162 mod_configure:588 mod_configure:1009 #, fuzzy msgid "Restart Service" msgstr "Reiniciar" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restaurar" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaura cópia de segurança a partir do ficheiro em " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" #: ejabberd_web_admin:2003 #, fuzzy msgid "Restore binary backup immediately:" msgstr "Recuperar uma cópia de segurança a partir de ficheiro" #: ejabberd_web_admin:2033 #, fuzzy msgid "Restore plain text backup immediately:" msgstr "Recuperar uma cópia de segurança a partir de ficheiro" #: mod_muc_log:872 #, fuzzy msgid "Room Configuration" msgstr "Configuração" #: mod_muc_log:892 msgid "Room Occupants" msgstr "" #: mod_muc:455 #, fuzzy msgid "Room creation is denied by service policy" msgstr "Acesso negado pela política de serviço" #: mod_muc_log:1064 #, fuzzy msgid "Room description" msgstr "Subscrição" #: mod_muc_log:1027 msgid "Room title" msgstr "Título da sala" #: mod_roster:1082 msgid "Roster" msgstr "Lista de contactos" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Lista de contactos de " #: mod_configure:1671 #, fuzzy msgid "Roster size" msgstr "Lista de contactos" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nodos a correr" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "" #: mod_vcard:453 #, fuzzy msgid "Search Results for " msgstr "Procurar utilizadores em " #: mod_vcard:427 msgid "Search users in " msgstr "Procurar utilizadores em " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" #: mod_announce:613 msgid "Send announcement to all users" msgstr "" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "" #: mod_muc_log:481 msgid "September" msgstr "" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 #, fuzzy msgid "Server:" msgstr "Nunca" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 #, fuzzy msgid "Shared Roster Groups" msgstr "Lista de contactos partilhada" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 #, fuzzy msgid "Start" msgstr "Reiniciar" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Iniciar módulos" #: mod_configure:936 msgid "Start Modules at " msgstr "Iniciar os módulos em " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Estatísticas" #: ejabberd_web_admin:2199 #, fuzzy msgid "Statistics of ~p" msgstr "Estatísticas" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Parar" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Parar módulos" #: mod_configure:918 msgid "Stop Modules at " msgstr "Parar módulos em " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nodos parados" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Tipo de armazenagem" #: ejabberd_web_admin:1993 #, fuzzy msgid "Store binary backup:" msgstr "Armazenar uma cópia de segurança no ficheiro" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "" #: mod_announce:516 mod_configure:1036 mod_configure:1076 #, fuzzy msgid "Subject" msgstr "Enviar" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Enviar" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 #, fuzzy msgid "Submitted" msgstr "enviado" #: mod_roster:914 msgid "Subscription" msgstr "Subscrição" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 #, fuzzy msgid "That nickname is already in use by another occupant" msgstr "A alcunha já está a ser usado por outro ocupante" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 #, fuzzy msgid "That nickname is registered by another person" msgstr "A alcunha já está registada por outra pessoa" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 #, fuzzy msgid "The password is too weak" msgstr "Mudar palavra-chave" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "" #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" #: mod_muc_log:1038 #, fuzzy msgid "This room is not anonymous" msgstr "Tornar a sala anónima?" #: mod_muc_log:466 msgid "Thursday" msgstr "" #: mod_offline:690 msgid "Time" msgstr "Data" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "" #: mod_offline:692 msgid "To" msgstr "Para" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "A ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "" #: ejabberd_web_admin:2219 #, fuzzy msgid "Transactions Aborted:" msgstr "Transacções abortadas" #: ejabberd_web_admin:2215 #, fuzzy msgid "Transactions Committed:" msgstr "Transacções realizadas" #: ejabberd_web_admin:2227 #, fuzzy msgid "Transactions Logged:" msgstr "Transacções armazenadas" #: ejabberd_web_admin:2223 #, fuzzy msgid "Transactions Restarted:" msgstr "Transacções reiniciadas" #: mod_muc_log:464 msgid "Tuesday" msgstr "" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Actualizar" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "" #: ejabberd_web_admin:2278 #, fuzzy msgid "Update plan" msgstr "Actualizar" #: ejabberd_web_admin:2280 #, fuzzy msgid "Update script" msgstr "Actualizar" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Actualizar" #: ejabberd_web_admin:2203 #, fuzzy msgid "Uptime:" msgstr "Tempo de funcionamento" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Utilizador" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 #, fuzzy msgid "User Management" msgstr "Gestão da BD" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Nodo não encontrado" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Utilizador" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 #, fuzzy msgid "Username:" msgstr "Nome do utilizador de IRC" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Utilizadores" #: ejabberd_web_admin:990 #, fuzzy msgid "Users Last Activity" msgstr "Última actividade" #: mod_register:375 #, fuzzy msgid "Users are not allowed to register accounts so quickly" msgstr "Os visitantes não podem enviar mensagens para todos os ocupantes" #: mod_roster:954 msgid "Validate" msgstr "" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 #, fuzzy msgid "Virtual Hosts" msgstr "Servidores virtuales" #: mod_muc_room:992 #, fuzzy msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Só os moderadores podem mudar o tópico desta sala" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Os visitantes não podem enviar mensagens para todos os ocupantes" #: mod_muc_room:3879 msgid "Voice request" msgstr "" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "" #: mod_muc_log:465 msgid "Wednesday" msgstr "" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Foi banido desta sala" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 #, fuzzy msgid "You must fill in field \"Nickname\" in the form" msgstr "Deve preencher o campo \"alcunha\" no formulário" #: mod_register:222 #, fuzzy msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "É necessário um cliente com suporte de x:data para poder registar a alcunha" #: mod_muc:731 #, fuzzy msgid "You need a client that supports x:data to register the nickname" msgstr "" "É necessário um cliente com suporte de x:data para poder registar a alcunha" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "É necessário um cliente com suporte de x:data para configurar as opções do " "mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "É necessário um cliente com suporte de x:data para poder procurar" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Impedir o envio de mensagens privadas para a sala" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "" #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Módulo de IRC ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Módulo MUC de ejabberd" #: mod_multicast:272 #, fuzzy msgid "ejabberd Multicast service" msgstr "Utilizadores do ejabberd" #: mod_pubsub:1067 #, fuzzy msgid "ejabberd Publish-Subscribe module" msgstr "Módulo pub/sub de ejabberd" #: mod_proxy65_service:170 #, fuzzy msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Módulo vCard de ejabberd" #: ejabberd_web_admin:311 ejabberd_web_admin:343 #, fuzzy msgid "ejabberd Web Admin" msgstr "Administração do ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Módulo vCard de ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" #: mod_muc_log:410 msgid "is now known as" msgstr "" #: mod_muc_log:370 msgid "joins the room" msgstr "" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "" #: mod_muc_room:3856 msgid "private, " msgstr "privado" #: mod_muc_room:3955 #, fuzzy msgid "the password is" msgstr "Mudar palavra-chave" #: mod_vcard:292 msgid "vCard User Search" msgstr "" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Configuração das Regra de Acesso ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "" #: mod_offline:677 #, fuzzy msgid "~s's Offline Messages Queue" msgstr "~s fila de mensagens diferidas" #~ msgid "No resource provided" #~ msgstr "Não foi passado nenhum recurso" #, fuzzy #~ msgid "Server" #~ msgstr "Nunca" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "O Jabber ID ~s não é válido" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiliação inválida: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Papel inválido: ~s" #, fuzzy #~ msgid "anyone" #~ msgstr "Nenhum" #, fuzzy #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permitir que os utilizadores enviem convites?" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "É necessário um cliente com suporte de x:data para configurar a sala" #, fuzzy #~ msgid "User JID" #~ msgstr "Utilizador" #, fuzzy #~ msgid "Node ID" #~ msgstr "Nodo" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Preencha os campos para procurar utilizadores Jabber coincidentes" #, fuzzy #~ msgid "Outgoing s2s Servers:" #~ msgstr "Servidores S2S de saída" #~ msgid "Delete" #~ msgstr "Eliminar" #~ msgid "Encodings" #~ msgstr "Codificações" #, fuzzy #~ msgid "(Raw)" #~ msgstr "(modo texto)" #~ msgid "Specified nickname is already registered" #~ msgstr "A alcunha especificada já está registada" #~ msgid "Size" #~ msgstr "Tamanho" #~ msgid "Backup Management at " #~ msgstr "Gestão da cópia de segurança em " #~ msgid "Choose host name" #~ msgstr "Introduza o nome do servidor" #~ msgid "Choose users to remove" #~ msgstr "Seleccione utilizadores a eliminar" #~ msgid "DB" #~ msgstr "BD" #~ msgid "Dump a database in a text file" #~ msgstr "Exportar uma Base de Dados para um ficheiro de texto" #~ msgid "Host name" #~ msgstr "Nome do servidor" #~ msgid "Hostname Configuration" #~ msgstr "Configuração do nome do servidor" #~ msgid "Install a database fallback from a file" #~ msgstr "Instalar uma recuperação de BD desde um ficheiro" #~ msgid "Listened Ports Management" #~ msgstr "Gestão das portas em escuta" #~ msgid "Make room moderated?" #~ msgstr "Tornar a sala moderada?" #~ msgid "Remove Users" #~ msgstr "Eliminar utilizadores" #~ msgid "Restore a database from a text file" #~ msgstr "Restaurar uma Base de Dados a partir de ficheiro de texto" #~ msgid "Results of search in " #~ msgstr "Resultados da procura em " #~ msgid "ejabberd (c) 2002-2005 Alexey Shchepin, 2005 Process One" #~ msgstr "ejabberd (c) 2002-2005 Alexey Shchepin, 2005 Process One" #~ msgid "ejabberd access rules configuration" #~ msgstr "Configuração das Regras de Acesso do ejabberd" #~ msgid "~p statistics" #~ msgstr "Estatísticas de ~p" ejabberd-18.01/priv/msgs/cs.msg0000644000232200023220000006124113225664356016715 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Přijmout"}. {"Access Configuration","Konfigurace přístupů"}. {"Access Control List Configuration","Konfigurace seznamu přístupových práv (ACL)"}. {"Access control lists","Seznamy přístupových práv (ACL)"}. {"Access Control Lists","Seznamy přístupových práv (ACL)"}. {"Access denied by service policy","Přístup byl zamítnut nastavením služby"}. {"Access rules","Pravidla přístupů"}. {"Access Rules","Pravidla přístupů"}. {"Action on user","Akce aplikovaná na uživatele"}. {"Add Jabber ID","Přidat Jabber ID"}. {"Add New","Přidat nový"}. {"Add User","Přidat uživatele"}. {"Administration","Administrace"}. {"Administration of ","Administrace "}. {"Administrator privileges required","Potřebujete práva administrátora"}. {"All activity","Všechny aktivity"}. {"Allow users to change the subject","Povolit uživatelům měnit téma místnosti"}. {"Allow users to query other users","Povolit uživatelům odesílat požadavky (query) ostatním uživatelům"}. {"Allow users to send invites","Povolit uživatelům posílání pozvánek"}. {"Allow users to send private messages","Povolit uživatelům odesílat soukromé zprávy"}. {"Allow visitors to change nickname","Povolit návštěvníkům měnit přezdívku"}. {"Allow visitors to send private messages to","Povolit návštěvníkům odesílat soukromé zprávy"}. {"Allow visitors to send status text in presence updates","Povolit návštěvníkům posílat stavové zprávy ve statusu"}. {"All Users","Všichni uživatelé"}. {"Announcements","Oznámení"}. {"A password is required to enter this room","Pro vstup do místnosti musíte zadat heslo"}. {"April",". dubna"}. {"August",". srpna"}. {"Backup Management","Správa zálohování"}. {"Backup of ~p","Záloha ~p"}. {"Backup to File at ","Záloha do souboru na "}. {"Backup","Zálohovat"}. {"Bad format","Nesprávný formát"}. {"Birthday","Datum narození"}. {"CAPTCHA web page","Webová stránka CAPTCHA"}. {"Change Password","Změnit heslo"}. {"Change User Password","Změnit heslo uživatele"}. {"Characters not allowed:","Nepřípustné znaky:"}. {"Chatroom configuration modified","Nastavení diskuzní místnosti bylo změněno"}. {"Chatroom is created","Konference vytvořena"}. {"Chatroom is destroyed","Konference zrušena"}. {"Chatroom is started","Konference spuštěna"}. {"Chatroom is stopped","Konference zastavena"}. {"Chatrooms","Konference"}. {"Choose a username and password to register with this server","Zadejte jméno uživatele a heslo pro registraci na tomto serveru"}. {"Choose modules to stop","Vyberte moduly, které mají být zastaveny"}. {"Choose storage type of tables","Vyberte typ úložiště pro tabulky"}. {"Choose whether to approve this entity's subscription.","Zvolte, zda chcete schválit odebírání touto entitou"}. {"City","Město"}. {"Commands","Příkazy"}. {"Conference room does not exist","Konferenční místnost neexistuje"}. {"Configuration","Konfigurace"}. {"Configuration of room ~s","Konfigurace místnosti ~s"}. {"Connected Resources:","Připojené zdroje:"}. {"Connections parameters","Parametry spojení"}. {"Country","Země"}. {"CPU Time:","Čas procesoru"}. {"Database","Databáze"}. {"Database Tables at ~p","Databázové tabulky na ~p"}. {"Database Tables Configuration at ","Konfigurace databázových tabulek "}. {"December",". prosince"}. {"Default users as participants","Uživatelé jsou implicitně členy"}. {"Delete message of the day on all hosts","Smazat zprávu dne na všech hostitelích"}. {"Delete message of the day","Smazat zprávu dne"}. {"Delete Selected","Smazat vybrané"}. {"Delete User","Smazat uživatele"}. {"Description:","Popis:"}. {"Disc only copy","Jen kopie disku"}. {"Displayed Groups:","Zobrazené skupiny:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Nikdy nikomu nesdělujte své heslo, ani administrátorovi serveru Jabberu."}. {"Dump Backup to Text File at ","Uložit zálohu do textového souboru na "}. {"Dump to Text File","Uložit do textového souboru"}. {"Edit Properties","Upravit vlastnosti"}. {"Either approve or decline the voice request.","Povolit nebo odmítnout voice žádost."}. {"ejabberd IRC module","ejabberd IRC modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Multicast service","Služba ejabberd Multicast"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","Webová administrace ejabberd"}. {"Elements","Položek"}. {"Email","E-mail"}. {"Enable logging","Zaznamenávat konverzace"}. {"Encoding for server ~b","Kódování pro server ~b"}. {"End User Session","Ukončit sezení uživatele"}. {"Enter list of {Module, [Options]}","Vložte seznam modulů {Modul, [Parametry]}"}. {"Enter nickname you want to register","Zadejte přezdívku, kterou chcete zaregistrovat"}. {"Enter path to backup file","Zadajte cestu k souboru se zálohou"}. {"Enter path to jabberd14 spool dir","Zadejte cestu k jabberd14 spool adresáři"}. {"Enter path to jabberd14 spool file","Zadejte cestu k spool souboru jabberd14"}. {"Enter path to text file","Zadajte cestu k textovému souboru"}. {"Enter the text you see","Zadejte text, který vidíte"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Zadejte přezdívku a kódování, které chcete používat pro připojení k serverům IRC. Stiskněte 'Další' pro více políček k vyplnění. Stiskněte 'Dokončit' pro uložení nastavení."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Zadejte přezdívku, kódování, porty a hesla, které chcete používat pro připojení k serverům IRC"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Chyba"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Příklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].2\"}]."}. {"Export all tables as SQL queries to a file:","Zálohovat všechny tabulky jako SQL dotazy do souboru:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportovat všechny uživatele do souboru ve formátu PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportovat uživatele na hostiteli do souboru ve formátu PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Došlo k chybě při získávání Jabber ID z vaší žádosti o voice práva"}. {"Family Name","Příjmení"}. {"February",". února"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Pro vyhledání uživatele Jabberu vyplňte formulář (na konec přidejte znak * pro vyhledání podřetězce)"}. {"Friday","Pátek"}. {"From","Od"}. {"From ~s","Od ~s"}. {"Full Name","Celé jméno"}. {"Get Number of Online Users","Získat počet online uživatelů"}. {"Get Number of Registered Users","Získat počet registrovaných uživatelů"}. {"Get User Last Login Time","Získat čas podleního přihlášení uživatele"}. {"Get User Password","Získat heslo uživatele"}. {"Get User Statistics","Získat statistiky uživatele"}. {"Group ","Skupina "}. {"Groups","Skupiny"}. {"has been banned","byl(a) zablokován(a)"}. {"has been kicked because of an affiliation change","byl(a) vyhozen(a) kvůli změně přiřazení"}. {"has been kicked because of a system shutdown","byl(a) vyhozen(a), protože dojde k vypnutí systému"}. {"has been kicked because the room has been changed to members-only","byl(a) vyhozen(a), protože mísnost je nyní pouze pro členy"}. {"has been kicked","byl(a) vyhozen(a) z místnosti"}. {" has set the subject to: "," změnil(a) téma na: "}. {"Host","Hostitel"}. {"If you don't see the CAPTCHA image here, visit the web page.","Pokud zde nevidíte obrázek CAPTCHA, přejděte na webovou stránku."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Pokud chcete zadat jiné kódování pro IRC servery, vyplňte seznam s hodnotami ve formátu '{\"irc server\",\"encoding\", port, \"password\"}'. Výchozí kódování pro tuto službu je \"~s\", port ~p, empty password."}. {"Import Directory","Import adresáře"}. {"Import File","Import souboru"}. {"Import user data from jabberd14 spool file:","Importovat uživatele z jabberd14 spool souborů:"}. {"Import User from File at ","Importovat uživatele ze souboru na "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importovat uživatele ze souboru ve formátu PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importovat uživatele z jabberd14 spool souborů:"}. {"Import Users from Dir at ","Importovat uživatele z adresáře na "}. {"Import Users From jabberd14 Spool Files","Importovat uživatele z jabberd14 spool souborů"}. {"Improper message type","Nesprávný typ zprávy"}. {"Incoming s2s Connections:","Příchozí s2s spojení:"}. {"Incorrect password","Nesprávné heslo"}. {"IP addresses","IP adresy"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC kanál (bez počátečního #)"}. {"IRC server","IRC přezdívka"}. {"IRC settings","Nastavení IRC"}. {"IRC Transport","IRC transport"}. {"IRC username","IRC přezdívka"}. {"IRC Username","IRC přezdívka"}. {"is now known as","se přejmenoval(a) na"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Není povoleno posílat chybové zprávy do konference. Účastník (~s) odeslal chybovou zprávu (~s) a byl vyhozen z konference."}. {"It is not allowed to send private messages","Je zakázáno posílat soukromé zprávy"}. {"It is not allowed to send private messages of type \"groupchat\"","Není dovoleno odeslání soukromé zprávy typu \"skupinová zpráva\" "}. {"It is not allowed to send private messages to the conference","Není povoleno odesílat soukromé zprávy do konference"}. {"Jabber Account Registration","Registrace účtu Jabberu"}. {"Jabber ID","Jabber ID"}. {"January",". ledna"}. {"Join IRC channel","Vstoupit do IRC kanálu"}. {"joins the room","vstoupil(a) do místnosti"}. {"Join the IRC channel here.","Vstoupit do tohoto IRC kanálu."}. {"Join the IRC channel in this Jabber ID: ~s","Vstupte do IRC kanálu s tímto Jabber ID: ~s"}. {"July",". července"}. {"June",". června"}. {"Last Activity","Poslední aktivita"}. {"Last login","Poslední přihlášení"}. {"Last month","Poslední měsíc"}. {"Last year","Poslední rok"}. {"leaves the room","opustil(a) místnost"}. {"Listened Ports at ","Otevřené porty na "}. {"Listened Ports","Otevřené porty"}. {"List of modules to start","Seznam modulů, které mají být spuštěné"}. {"List of rooms","Seznam konferencí"}. {"Low level update script","Nízkoúrovňový aktualizační skript"}. {"Make participants list public","Nastavit seznam účastníků jako veřejný"}. {"Make room CAPTCHA protected","Chránit místnost pomocí CAPTCHA"}. {"Make room members-only","Zpřístupnit místnost jen členům"}. {"Make room moderated","Nastavit místnost jako moderovanou"}. {"Make room password protected","Chránit místnost heslem"}. {"Make room persistent","Nastavit místnost jako stálou"}. {"Make room public searchable","Nastavit místnost jako veřejnou"}. {"March",". března"}. {"Maximum Number of Occupants","Počet účastníků"}. {"May",". května"}. {"Members:","Členové:"}. {"Membership is required to enter this room","Pro vstup do místnosti musíte být členem"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Svoje heslo si zapamatujte, nebo si jej poznamenejte na papírek a ten uschovejte v bezpečí. Jabber nemá žádný automatizovaný způsob obnovy hesla."}. {"Memory","Paměť"}. {"Message body","Tělo zprávy"}. {"Middle Name","Druhé jméno"}. {"Moderator privileges required","Potřebujete práva moderátora"}. {"Modified modules","Aktualizované moduly"}. {"Module","Modul"}. {"Modules at ~p","Moduly v ~p"}. {"Modules","Moduly"}. {"Monday","Pondělí"}. {"Multicast","Multicast"}. {"Multi-User Chat","Víceuživatelský chat"}. {"Name:","Jméno:"}. {"Name","Jméno"}. {"Never","Nikdy"}. {"New Password:","Nové heslo:"}. {"Nickname","Přezdívka"}. {"Nickname Registration at ","Registrace přezdívky na "}. {"Nickname ~s does not exist in the room","Přezdívka ~s v místnosti neexistuje"}. {"No body provided for announce message","Zpráva neobsahuje text"}. {"No Data","Žádná data"}. {"Node not found","Uzel nenalezen"}. {"Node ~p","Uzel ~p"}. {"Nodes","Uzly"}. {"None","Nic"}. {"Not Found","Nenalezeno"}. {"November",". listopadu"}. {"Number of online users","Počet online uživatelů"}. {"Number of registered users","Počet registrovaných uživatelů"}. {"October",". října"}. {"Offline Messages:","Offline zprávy:"}. {"Offline Messages","Offline zprávy"}. {"OK","OK"}. {"Old Password:","Současné heslo:"}. {"Online","Online"}. {"Online Users:","Online uživatelé:"}. {"Online Users","Online uživatelé"}. {"Only members may query archives of this room","Pouze moderátoři mají povoleno měnit téma místnosti"}. {"Only moderators and participants are allowed to change the subject in this room","Jen moderátoři a účastníci mají povoleno měnit téma této místnosti"}. {"Only moderators are allowed to change the subject in this room","Jen moderátoři mají povoleno měnit téma místnosti"}. {"Only moderators can approve voice requests","Pouze moderátoři mohou schválit žádosti o voice práva"}. {"Only occupants are allowed to send messages to the conference","Jen členové mají povolené zasílat zprávy do konference"}. {"Only occupants are allowed to send queries to the conference","Jen členové mohou odesílat požadavky (query) do konference"}. {"Only service administrators are allowed to send service messages","Pouze správci služby smí odesílat servisní zprávy"}. {"Options","Nastavení"}. {"Organization Name","Název firmy"}. {"Organization Unit","Oddělení"}. {"Outgoing s2s Connections:","Odchozí s2s spojení:"}. {"Outgoing s2s Connections","Odchozí s2s spojení"}. {"Owner privileges required","Jsou vyžadována práva vlastníka"}. {"Packet","Paket"}. {"Password ~b","Heslo ~b"}. {"Password:","Heslo:"}. {"Password","Heslo"}. {"Password Verification:","Ověření hesla:"}. {"Password Verification","Ověření hesla"}. {"Path to Dir","Cesta k adresáři"}. {"Path to File","Cesta k souboru"}. {"Pending","Čekající"}. {"Period: ","Čas: "}. {"Permanent rooms","Stálých konferencí"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Podotýkáme, že tato nastavení budou zálohována do zabudované databáze Mnesia. Pokud používáte ODBC modul, musíte zálohovat svoji SQL databázi samostatně."}. {"Please, wait for a while before sending new voice request","Prosím, počkejte chvíli před posláním nové žádosti o voice práva"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","soukromá, "}. {"Protocol","Protokol"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","Žádost odběratele PubSub"}. {"Queries to the conference members are not allowed in this room","Požadavky (queries) na členy konference nejsou v této místnosti povolené"}. {"RAM and disc copy","Kopie RAM a disku"}. {"RAM copy","Kopie RAM"}. {"Raw","Zdroj"}. {"Really delete message of the day?","Skutečně smazat zprávu dne?"}. {"Recipient is not in the conference room","Příjemce se nenachází v konferenční místnosti"}. {"Register a Jabber account","Zaregistrujte si účet Jabberu"}. {"Registered nicknames","Registrované přezdívky"}. {"Registered Users","Registrovaní uživatelé"}. {"Registered Users:","Registrovaní živatelé:"}. {"Register","Zaregistrovat se"}. {"Registration in mod_irc for ","Registrace do mod_irc na "}. {"Remote copy","Vzdálená kopie"}. {"Remove All Offline Messages","Odstranit všechny offline zprávy"}. {"Remove","Odstranit"}. {"Remove User","Odstranit uživatele"}. {"Replaced by new connection","Nahrazeno novým spojením"}. {"Resources","Zdroje"}. {"Restart","Restart"}. {"Restart Service","Restartovat službu"}. {"Restore Backup from File at ","Obnovit zálohu ze souboru na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Obnovit binární zálohu při následujícím restartu ejabberd (vyžaduje méně paměti)"}. {"Restore binary backup immediately:","Okamžitě obnovit binární zálohu:"}. {"Restore","Obnovit"}. {"Restore plain text backup immediately:","Okamžitě obnovit zálohu z textového souboru:"}. {"Room Configuration","Nastavení místnosti"}. {"Room creation is denied by service policy","Pravidla služby nepovolují vytvořit místnost"}. {"Room description","Popis místnosti"}. {"Room Occupants","Počet účastníků"}. {"Room title","Název místnosti"}. {"Roster of ","Seznam kontaktů "}. {"Roster","Seznam kontaktů"}. {"Roster size","Velikost seznamu kontaktů"}. {"RPC Call Error","Chyba RPC volání"}. {"Running Nodes","Běžící uzly"}. {"~s access rule configuration","~s konfigurace pravidla přístupu"}. {"Saturday","Sobota"}. {"Script check","Kontrola skriptu"}. {"Search Results for ","Výsledky hledání pro "}. {"Search users in ","Hledat uživatele v "}. {"Send announcement to all online users","Odeslat oznámení všem online uživatelům"}. {"Send announcement to all online users on all hosts","Odeslat oznámení všem online uživatelům na všech hostitelích"}. {"Send announcement to all users","Odeslat oznámení všem uživatelům"}. {"Send announcement to all users on all hosts","Odeslat oznámení všem uživatelům na všech hostitelích"}. {"September",". září"}. {"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}. {"Set message of the day on all hosts and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}. {"Shared Roster Groups","Skupiny pro sdílený seznam kontaktů"}. {"Show Integral Table","Zobrazit kompletní tabulku"}. {"Show Ordinary Table","Zobrazit běžnou tabulku"}. {"Shut Down Service","Vypnout službu"}. {"~s invites you to the room ~s","~s vás zve do místnosti ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Někteří klienti umí uložit vaše heslo na disk počítače. Tuto funkci používejte, pouze pokud věříte zabezpečení svého počítače."}. {"~s's Offline Messages Queue","Fronta offline zpráv uživatele ~s"}. {"Start Modules at ","Spustit moduly na "}. {"Start Modules","Spustit moduly"}. {"Start","Start"}. {"Statistics of ~p","Statistiky ~p"}. {"Statistics","Statistiky"}. {"Stop Modules at ","Zastavit moduly na "}. {"Stop Modules","Zastavit moduly"}. {"Stopped Nodes","Zastavené uzly"}. {"Stop","Stop"}. {"Storage Type","Typ úložiště"}. {"Store binary backup:","Uložit binární zálohu:"}. {"Store plain text backup:","Uložit zálohu do textového souboru:"}. {"Subject","Předmět"}. {"Submit","Odeslat"}. {"Submitted","Odeslané"}. {"Subscription","Přihlášení"}. {"Sunday","Neděle"}. {"That nickname is already in use by another occupant","Přezdívka je již používána jiným členem"}. {"That nickname is registered by another person","Přezdívka je zaregistrována jinou osobou"}. {"The CAPTCHA is valid.","CAPTCHA souhlasí."}. {"The CAPTCHA verification has failed","Ověření CAPTCHA se nezdařilo"}. {"the password is","heslo je"}. {"The password is too weak","Heslo je příliš slabé"}. {"The password of your Jabber account was successfully changed.","Heslo vašeho účtu Jabberu bylo úspěšně změněno."}. {"There was an error changing the password: ","Při změně hesla došlo k chybě: "}. {"There was an error creating the account: ","Při vytváření účtu došlo k chybě."}. {"There was an error deleting the account: ","Při mazání účtu došlo k chybě: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Zde nezáleží na velikosti písmen: macbeth je stejný jako MacBeth a Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Na této stránce si můžete vytvořit účet na tomto serveru Jabberu. Vaše JID (Jabber IDentifikátor) bude mít tvar: uživatelskéjméno@server. Přečtěte si prosím pozorně instrukce pro vyplnění údajů."}. {"This page allows to unregister a Jabber account in this Jabber server.","Zde můžete zrušit registraci účtu na tomto serveru Jabberu."}. {"This room is not anonymous","Tato místnost není anonymní"}. {"Thursday","Čtvrtek"}. {"Time","Čas"}. {"Time delay","Časový posun"}. {"Too many CAPTCHA requests","Přiliš mnoho CAPTCHA žádostí"}. {"Too many unacked stanzas","Příliš mnoho nepotvrzených stanz"}. {"To","Pro"}. {"To ~s","Pro ~s"}. {"Total rooms","Celkem konferencí"}. {"Traffic rate limit is exceeded","Byl překročen limit"}. {"Transactions Aborted:","Transakce zrušena"}. {"Transactions Committed:","Transakce potvrzena"}. {"Transactions Logged:","Transakce zaznamenána"}. {"Transactions Restarted:","Transakce restartována"}. {"Tuesday","Úterý"}. {"Unable to generate a CAPTCHA","Nebylo možné vygenerovat CAPTCHA"}. {"Unauthorized","Nemáte oprávnění"}. {"Unregister a Jabber account","Zrušte registraci účtu Jabberu"}. {"Unregister","Zrušit registraci"}. {"Update","Aktualizovat"}. {"Update message of the day (don't send)","Aktualizovat zprávu dne (neodesílat)"}. {"Update message of the day on all hosts (don't send)","Aktualizovat zprávu dne pro všechny hostitele (neodesílat)"}. {"Update ~p","Aktualizovat ~p"}. {"Update plan","Aktualizovat plán"}. {"Update script","Aktualizované skripty"}. {"Uptime:","Čas běhu:"}. {"Use of STARTTLS required","Je vyžadováno STARTTLS"}. {"User Management","Správa uživatelů"}. {"Username:","Uživatelské jméno:"}. {"Users are not allowed to register accounts so quickly","Je zakázáno registrovat účty v tak rychlém sledu"}. {"Users Last Activity","Poslední aktivita uživatele"}. {"Users","Uživatelé"}. {"User ~s","Uživatel ~s"}. {"User","Uživatel"}. {"Validate","Ověřit"}. {"vCard User Search","Hledání uživatelů podle vizitek"}. {"Virtual Hosts","Virtuální hostitelé"}. {"Visitors are not allowed to change their nicknames in this room","Návštěvníkům této místnosti je zakázáno měnit přezdívku"}. {"Visitors are not allowed to send messages to all occupants","Návštevníci nemají povoleno zasílat zprávy všem účastníkům konference"}. {"Voice requests are disabled in this conference","Voice žádosti jsou v této konferenci zakázány"}. {"Voice request","Žádost o voice práva"}. {"Wednesday","Středa"}. {"You can later change your password using a Jabber client.","Později můžete své heslo změnit pomocí klienta Jabberu."}. {"You have been banned from this room","Byl jste vyloučen z této místnosti"}. {"You must fill in field \"Nickname\" in the form","Musíte vyplnit políčko \"Přezdívka\" ve formuláři"}. {"You need a client that supports x:data and CAPTCHA to register","Pro registraci potřebujete klienta s podporou x:data a CAPTCHA"}. {"You need a client that supports x:data to register the nickname","Pro registraci přezdívky potřebujete klienta s podporou x:data"}. {"You need an x:data capable client to configure mod_irc settings","Pro konfiguraci mod_irc potřebujete klienta s podporou x:data"}. {"You need an x:data capable client to search","K vyhledávání potřebujete klienta podporujícího x:data"}. {"Your active privacy list has denied the routing of this stanza.","Vaše nastavení soukromí znemožnilo směrování této stance."}. {"Your contact offline message queue is full. The message has been discarded.","Fronta offline zpráv pro váš kontakt je plná. Zpráva byla zahozena."}. {"Your Jabber account was successfully created.","Váš účet Jabberu byl úspěšně vytvořen."}. {"Your Jabber account was successfully deleted.","Váš účet Jabberu byl úspěšně smazán."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Nesmíte posílat zprávy na ~s. Pro povolení navštivte ~s"}. ejabberd-18.01/priv/msgs/ca.po0000644000232200023220000020271613225664356016527 0ustar debalancedebalance# Jan, 2012. msgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: 2017-10-11 16:20+0200\n" "Last-Translator: Badlop \n" "Language-Team: \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Catalan (català)\n" "X-Additional-Translator: Vicent Alberola Canet\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.7.1\n" #: mod_muc_log:413 mod_muc_log:588 msgid " has set the subject to: " msgstr " ha posat l'assumpte: " #: mod_muc_room:1896 msgid "A password is required to enter this room" msgstr "Es necessita contrasenya per a entrar en aquesta sala" #: ejabberd_oauth:448 msgid "Accept" msgstr "Acceptar" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configuració d'accesos" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configuració de la Llista de Control d'Accés" #: ejabberd_web_admin:484 ejabberd_web_admin:523 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Llista de Control d'Accés" #: ejabberd_web_admin:589 ejabberd_web_admin:622 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Regles d'Accés" #: mod_configure:1095 msgid "Access control lists" msgstr "Llistes de Control de Accés" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Accés denegat per la política del servei" #: mod_configure:1113 msgid "Access rules" msgstr "Regles d'accés" #: mod_configure:1772 msgid "Action on user" msgstr "Acció en l'usuari" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Afegir Jabber ID" #: ejabberd_web_admin:1003 mod_shared_roster:825 msgid "Add New" msgstr "Afegir nou" #: ejabberd_web_admin:1170 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Afegir usuari" #: ejabberd_web_admin:413 ejabberd_web_admin:424 msgid "Administration" msgstr "Administració" #: mod_configure:1767 msgid "Administration of " msgstr "Administració de " #: mod_muc_room:2540 msgid "Administrator privileges required" msgstr "Es necessita tenir privilegis d'administrador" #: mod_configure:507 msgid "All Users" msgstr "Tots els usuaris" #: ejabberd_web_admin:736 msgid "All activity" msgstr "Tota l'activitat" #: mod_muc_log:809 msgid "Allow users to change the subject" msgstr "Permetre que els usuaris canviin el tema" #: mod_muc_log:815 msgid "Allow users to query other users" msgstr "Permetre que els usuaris fagen peticions a altres usuaris" #: mod_muc_log:817 msgid "Allow users to send invites" msgstr "Permetre que els usuaris envien invitacions" #: mod_muc_log:811 msgid "Allow users to send private messages" msgstr "Permetre que els usuaris envien missatges privats" #: mod_muc_log:820 msgid "Allow visitors to change nickname" msgstr "Permetre als visitants canviar el sobrenom" #: mod_muc_log:813 msgid "Allow visitors to send private messages to" msgstr "Permetre als visitants enviar missatges privats a" #: mod_muc_log:822 msgid "Allow visitors to send status text in presence updates" msgstr "" "Permetre als visitants enviar text d'estat en les actualitzacions de " "presència" #: mod_announce:611 msgid "Announcements" msgstr "Anuncis" #: mod_muc_log:476 msgid "April" msgstr "Abril" #: mod_muc_log:480 msgid "August" msgstr "Agost" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "La creació automàtica de nodes no està activada" #: ejabberd_web_admin:1593 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Guardar còpia de seguretat" #: mod_configure:584 msgid "Backup Management" msgstr "Gestió de còpia de seguretat" #: ejabberd_web_admin:1705 msgid "Backup of ~p" msgstr "Còpia de seguretat de ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Desar còpia de seguretat a fitxer en " #: ejabberd_web_admin:489 ejabberd_web_admin:528 ejabberd_web_admin:594 #: ejabberd_web_admin:627 ejabberd_web_admin:663 ejabberd_web_admin:1148 #: ejabberd_web_admin:1431 ejabberd_web_admin:1587 ejabberd_web_admin:1871 #: ejabberd_web_admin:1900 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Format erroni" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Aniversari" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Es requereixen tant el nom d'usuari com el recurs" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "El Bytestream ja està activat" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Pàgina web del CAPTCHA" #: ejabberd_web_admin:1933 msgid "CPU Time:" msgstr "Temps de CPU" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "No es pot eliminar la llista activa" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "No es pot eliminar la llista per defecte" #: ejabberd_web_admin:1405 mod_register_web:212 mod_register_web:373 #: mod_register_web:381 mod_register_web:406 msgid "Change Password" msgstr "Canviar Contrasenya" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Canviar Contrasenya d'Usuari" #: mod_register:295 msgid "Changing password is not allowed" msgstr "No està permès canviar la contrasenya" #: mod_muc_room:2776 msgid "Changing role/affiliation is not allowed" msgstr "No està permès canviar el rol/afiliació" #: mod_register_web:258 msgid "Characters not allowed:" msgstr "Caràcters no permesos:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Configuració de la sala de xat modificada" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "La sala s'ha creat" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "La sala s'ha destruït" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "La sala s'ha iniciat" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "La sala s'ha aturat" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Sales de xat" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Tria nom d'usuari i contrasenya per a registrar-te en aquest servidor" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Selecciona mòduls a detindre" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Selecciona el tipus d'almacenament de les taules" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Tria si aprova aquesta entitat de subscripció" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Ciutat" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Comandaments" #: mod_muc:461 msgid "Conference room does not exist" msgstr "La sala de conferències no existeix" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configuració" #: mod_muc_room:3175 msgid "Configuration of room ~s" msgstr "Configuració de la sala ~s" #: ejabberd_web_admin:1437 msgid "Connected Resources:" msgstr "Recursos connectats:" #: mod_irc:526 msgid "Connections parameters" msgstr "Paràmetres de connexió" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Pais" #: ejabberd_web_admin:1592 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Base de dades" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Configuració de la base de dades en " #: ejabberd_web_admin:1667 msgid "Database Tables at ~p" msgstr "Taules de la base de dades en ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Error a la base de dades" #: mod_muc_log:484 msgid "December" msgstr "Decembre" #: mod_muc_log:807 msgid "Default users as participants" msgstr "Els usuaris són participants per defecte" #: ejabberd_web_admin:537 ejabberd_web_admin:637 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Eliminar els seleccionats" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Eliminar Usuari" #: mod_announce:629 msgid "Delete message of the day" msgstr "Eliminar el missatge del dia" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Elimina el missatge del dis de tots els hosts" #: mod_shared_roster:900 msgid "Description:" msgstr "Descripció:" #: mod_configure:889 msgid "Disc only copy" msgstr "Còpia sols en disc" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Mostrar grups:" #: mod_register_web:270 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "No li donis la teva contrasenya a ningú, ni tan sols als administradors del " "servidor Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Exporta còpia de seguretat a fitxer de text en " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Exportar a fitxer de text" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "No estan permesos els grups duplicats al RFC6121" #: mod_configure:1776 msgid "Edit Properties" msgstr "Editar propietats" #: mod_muc_room:3893 msgid "Either approve or decline the voice request." msgstr "Aprova o denega la petició de veu" #: ejabberd_web_admin:1679 msgid "Elements" msgstr "Elements" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 msgid "Empty password" msgstr "Contrasenya buida" #: mod_muc_log:818 msgid "Enable logging" msgstr "Habilitar el registre de la conversa" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "No està suportat activar Push sense l'atribut 'node'" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Codificació pel servidor ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Finalitzar Sesió d'Usuari" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Introdueix llista de {mòdul, [opcions]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Introdueix el sobrenom que vols registrar" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Introdueix ruta al fitxer de còpia de seguretat" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Introdueix la ruta al directori de jabberd14 spools" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Introdueix ruta al fitxer jabberd14 spool" #: mod_configure:974 msgid "Enter path to text file" msgstr "Introdueix ruta al fitxer de text" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Introdueix el text que veus" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Introdueix el nom d'usuari i les codificacions de caràcters per a utilitzar " "als servidors de IRC. Apreta \"Seguent\" per veure més caps per omplir. " "Apreta \"Completar\" per guardar la configuració. " #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Introdueix el nom d'usuari, les codificacions de caràcters, els ports i " "contrasenyes per a utilitzar al connectar als servidors de IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Servidor Erlang Jabber" #: ejabberd_web_admin:1702 ejabberd_web_admin:1873 msgid "Error" msgstr "Error" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:1810 msgid "Export all tables as SQL queries to a file:" msgstr "Exporta totes les taules a un fitxer SQL:" #: ejabberd_web_admin:1782 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exportar dades de tots els usuaris del servidor a arxius PIEFXIS (XEP-0227):" #: ejabberd_web_admin:1794 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Exportar dades d'usuaris d'un host a arxius PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Error al component extern" #: mod_delegation:283 msgid "External component timeout" msgstr "Temps esgotat al component extern" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Errada al activar bytestream" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "No s'ha pogut extraure el JID de la teva aprovació de petició de veu" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Ha fallat mapejar la delegació de l'espai de noms al component extern" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Ha fallat el processat de la resposta HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Ha fallat el processat de Chanserv" #: mod_muc_room:3309 msgid "Failed to process option '~s'" msgstr "H fallat el processat de la opció '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Cognom" #: mod_muc_log:474 msgid "February" msgstr "Febrer" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "El fitxer es més gran que ~w bytes" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Emplena el formulari per a buscar usuaris Jabber. Afegix * al final d'un " "camp per a buscar subcadenes." #: mod_muc_log:467 msgid "Friday" msgstr "Divendres" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nom complet" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Obtenir Número d'Usuaris Connectats" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Obtenir Número d'Usuaris Registrats" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Obtenir la última connexió d'Usuari" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Obtenir Contrasenya d'usuari" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Obtenir Estadístiques d'Usuari" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Nom propi" #: mod_shared_roster:923 msgid "Group " msgstr "Grup " #: mod_roster:916 msgid "Groups" msgstr "Grups" #: ejabberd_web_admin:1091 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Host desconegut" #: ejabberd_web_admin:2189 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Adreça IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transport a IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Nom d'usuari al IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canal d'IRC (no posis la primera #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "Connexió IRC no trobada" #: mod_irc:673 msgid "IRC server" msgstr "Servidor d'IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Configuració d'IRC." #: mod_irc:766 msgid "IRC username" msgstr "Nom d'usuari al IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Si no veus la imatge CAPTCHA açí, visita la pàgina web." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Si vols especificar codificacions de caràcters diferents per a cada servidor " "IRC emplena aquesta llista amb els valors amb el format '{\"servidor irc\", " "\"codificació\", port, \"contrasenya\"}'. Aquest servei utilitza per " "defecte la codificació \"~s\", port ~p, no contrasenya." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importar directori" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importar fitxer" #: mod_configure:982 msgid "Import User from File at " msgstr "Importa usuari des de fitxer en " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importar usuaris de jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importar usuaris des del directori en " #: ejabberd_web_admin:1826 msgid "Import user data from jabberd14 spool file:" msgstr "Importar dades d'usuaris de l'arxiu de spool de jabberd14" #: ejabberd_web_admin:1769 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importar dades d'usuaris des d'un arxiu PIEFXIS (XEP-0227):" #: ejabberd_web_admin:1837 msgid "Import users data from jabberd14 spool directory:" msgstr "Importar dades d'usuaris del directori de spool de jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Atribut 'from' impropi" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Atribut 'to' impropi" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "La part de domini de l'atribut 'from' es impròpia" #: mod_muc_room:260 msgid "Improper message type" msgstr "Tipus de missatge incorrecte" #: ejabberd_web_admin:1312 msgid "Incoming s2s Connections:" msgstr "Connexions s2s d'entrada" #: mod_muc_room:3681 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "El CAPTCHA proporcionat és incorrecte" #: mod_muc:772 mod_muc_room:3064 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "El formulari de dades és incorrecte" #: mod_muc_room:1946 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Contrasenya incorrecta" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Valor al formulari de dades incorrecte" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Valor incorrecte del atribut 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Valor incorrecte de 'action' al formulari de dades" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Valor incorrecte de 'path' al formulari de dades" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Valor incorrecte del atribut 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Privilegi insuficient" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Atribut 'from' invàlid al missatge reenviat" #: mod_privilege:300 msgid "Invalid element" msgstr "Element invàlid" #: mod_muc_room:3938 msgid "Invitations are not allowed in this conference" msgstr "Les invitacions no estan permeses en aquesta sala de conferència" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1052 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "No està permés enviar missatges d'error a la sala. El participant (~s) ha " "enviat un missatge d'error (~s) i ha sigut expulsat de la sala" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "No està permés enviar missatges privats" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "No està permés enviar missatges del tipus \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "No està permès l'enviament de missatges privats a la sala" #: mod_register_web:199 mod_register_web:207 msgid "Jabber Account Registration" msgstr "Registre de compte Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "ID Jabber" #: mod_muc_log:473 msgid "January" msgstr "Gener" #: mod_irc:665 msgid "Join IRC channel" msgstr "Entra a canal d'IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Entra al canal d'IRC aquí." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Entra al canal d'IRC en aquesta Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Juliol" #: mod_muc_log:478 msgid "June" msgstr "Juny" #: ejabberd_web_admin:1214 ejabberd_web_admin:1441 msgid "Last Activity" msgstr "Última activitat" #: mod_configure:1646 msgid "Last login" msgstr "Últim login" #: ejabberd_web_admin:733 msgid "Last month" msgstr "Últim mes" #: ejabberd_web_admin:734 msgid "Last year" msgstr "Últim any" #: mod_configure:941 msgid "List of modules to start" msgstr "Llista de mòduls a iniciar" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Llista de sales" #: ejabberd_web_admin:1595 msgid "Listened Ports" msgstr "Ports a l'escolta" #: ejabberd_web_admin:1865 msgid "Listened Ports at " msgstr "Ports a la escolta en " #: ejabberd_web_admin:2007 msgid "Low level update script" msgstr "Script d'actualització de baix nivell" #: mod_muc_log:796 msgid "Make participants list public" msgstr "Crear una llista de participants pública" #: mod_muc_log:825 msgid "Make room CAPTCHA protected" msgstr "Crear una sala protegida per CAPTCHA" #: mod_muc_log:803 msgid "Make room members-only" msgstr "Crear una sala de \"només membres\"" #: mod_muc_log:805 msgid "Make room moderated" msgstr "Crear una sala moderada" #: mod_muc_log:798 msgid "Make room password protected" msgstr "Crear una sala amb contrasenya" #: mod_muc_log:792 msgid "Make room persistent" msgstr "Crear una sala persistent" #: mod_muc_log:794 msgid "Make room public searchable" msgstr "Crear una sala pública" #: mod_register:317 msgid "Malformed username" msgstr "Nom d'usuari mal format" #: mod_muc_log:475 msgid "March" msgstr "Març" #: mod_muc_log:831 msgid "Maximum Number of Occupants" msgstr "Número màxim d'ocupants" #: mod_muc_log:477 msgid "May" msgstr "Maig" #: mod_shared_roster:907 msgid "Members:" msgstr "Membre:" #: mod_muc_room:1839 msgid "Membership is required to enter this room" msgstr "Necessites ser membre d'aquesta sala per a poder entrar" #: mod_register_web:281 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Memoritza la teva contrasenya, o escriu-la en un paper guardat a un lloc " "segur.A Jabber no hi ha una forma automatitzada de recuperar la teva " "contrasenya si la oblides." #: ejabberd_web_admin:1680 msgid "Memory" msgstr "Memòria" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Missatge" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Missatge no trobat al contingut reenviat" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Segon nom" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Falten 'channel' o 'server' al formulari de dades" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Falta l'atribut 'from'" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Falta l'atribut 'to'" #: mod_muc_room:2548 mod_muc_room:3733 mod_muc_room:3777 mod_muc_room:3810 msgid "Moderator privileges required" msgstr "Es necessita tenir privilegis de moderador" #: ejabberd_web_admin:2005 msgid "Modified modules" msgstr "Mòduls modificats" #: ejabberd_web_admin:2191 ejabberd_web_admin:2346 msgid "Module" msgstr "Mòdul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "El modul ha fallat al gestionar la petició" #: ejabberd_web_admin:1611 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Mòduls" #: ejabberd_web_admin:1894 msgid "Modules at ~p" msgstr "Mòduls en ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Dilluns" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Multi-Usuari Converses" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "No estan permesos múltiples elements per RFC6121" #: ejabberd_web_admin:1677 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nom" #: mod_shared_roster:896 msgid "Name:" msgstr "Nom:" #: mod_muc_room:2708 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "No s'han trobat els atributs 'jid' ni 'nick'" #: mod_muc_room:2530 mod_muc_room:2713 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "No s'han trobat els atributs 'role' ni 'affiliation'" #: ejabberd_web_admin:1232 ejabberd_web_admin:1413 mod_configure:1629 msgid "Never" msgstr "Mai" #: mod_register_web:397 msgid "New Password:" msgstr "Nova Contrasenya:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Sobrenom" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registre del sobrenom en " #: mod_muc_room:2725 msgid "Nickname ~s does not exist in the room" msgstr "El sobrenom ~s no existeix a la sala" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "No s'ha trobat 'access' al formulari de dades" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "No s'ha trobat 'acls' al formulari de dades" #: mod_muc_room:3087 msgid "No 'affiliation' attribute found" msgstr "No s'ha trobat l'atribut 'affiliation'" #: mod_muc_room:2517 msgid "No 'item' element found" msgstr "No s'ha trobat l'element 'item'" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "No s'ha trobat 'modules' al formulari de dades" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "No s'ha trobat 'password' al formulari de dades" #: mod_register:148 msgid "No 'password' found in this query" msgstr "No s'ha trobat 'password' en esta petició" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "No s'ha trobat 'path' en el formulari de dades" #: mod_muc_room:3934 msgid "No 'to' attribute found in the invitation" msgstr "No s'ha trobat l'atribut 'to' en la invitació" #: ejabberd_web_admin:1493 msgid "No Data" msgstr "No hi ha dades" #: ejabberd_local:181 msgid "No available resource found" msgstr "No s'ha trobat un recurs disponible" #: mod_announce:575 msgid "No body provided for announce message" msgstr "No hi ha proveedor per al missatge anunci" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "No s'ha trobat el formulari de dades" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "No n'hi ha característiques disponibles" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Cap event ha processat este comandament" #: mod_last:218 msgid "No info about last activity found" msgstr "No s'ha trobat informació de l'ultima activitat" #: mod_blocking:99 msgid "No items found in this query" msgstr "Cap element ha sigut trobat a la petició " #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Cap element està manegant esta petició" #: mod_pubsub:1541 msgid "No node specified" msgstr "No s'ha especificat node" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "No s'han trobat subscripcions pendents" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "No s'ha trobat cap llista de privacitat amb aquest nom" #: mod_private:96 msgid "No private data found in this query" msgstr "No s'ha trobat dades privades en esta petició" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "No s'ha trobat node en marxa" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "No n'hi ha serveis disponibles" #: mod_stats:101 msgid "No statistics found for this item" msgstr "No n'hi ha estadístiques disponibles per a aquest element" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "El node ja existeix" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Index de node no trobat" #: ejabberd_web_admin:772 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Node no trobat" #: ejabberd_web_admin:1583 ejabberd_web_admin:1608 msgid "Node ~p" msgstr "Node ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Ha fallat Nodeprep" #: ejabberd_web_admin:1563 msgid "Nodes" msgstr "Nodes" #: ejabberd_web_admin:1348 ejabberd_web_admin:1544 ejabberd_web_admin:1554 #: ejabberd_web_admin:1964 mod_roster:907 msgid "None" msgstr "Cap" #: ejabberd_web_admin:759 msgid "Not Found" msgstr "No Trobat" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "No subscrit" #: mod_muc_log:483 msgid "November" msgstr "Novembre" #: mod_configure:1212 msgid "Number of online users" msgstr "Número d'usuaris connectats" #: mod_configure:1202 msgid "Number of registered users" msgstr "Número d'Usuaris Registrats" #: ejabberd_web_admin:1726 ejabberd_web_admin:1736 ejabberd_web_admin:1747 #: ejabberd_web_admin:1756 ejabberd_web_admin:1766 ejabberd_web_admin:1779 #: ejabberd_web_admin:1791 ejabberd_web_admin:1807 ejabberd_web_admin:1823 #: ejabberd_web_admin:1834 ejabberd_web_admin:1844 msgid "OK" msgstr "Acceptar" #: mod_muc_log:482 msgid "October" msgstr "Octubre" #: ejabberd_web_admin:1213 msgid "Offline Messages" msgstr "Missatges offline" #: mod_offline:761 msgid "Offline Messages:" msgstr "Missatges fora de línia:" #: mod_register_web:393 msgid "Old Password:" msgstr "Antiga contrasenya:" #: ejabberd_web_admin:1250 ejabberd_web_admin:1424 mod_configure:1639 msgid "Online" msgstr "Connectat" #: ejabberd_web_admin:700 ejabberd_web_admin:1093 mod_configure:506 msgid "Online Users" msgstr "Usuaris conectats" #: ejabberd_web_admin:1306 ejabberd_web_admin:1325 ejabberd_web_admin:1937 msgid "Online Users:" msgstr "Usuaris en línia:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Només es permeten etiquetes o " #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "En esta petició només es permet l'element " #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Només membres poden consultar l'arxiu de missatges d'aquesta sala" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Només els moderadors i participants poden canviar l'assumpte d'aquesta sala" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Només els moderadors poden canviar l'assumpte d'aquesta sala" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Només els moderadors poden aprovar les peticions de veu" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:4001 msgid "Only occupants are allowed to send messages to the conference" msgstr "Sols els ocupants poden enviar missatges a la sala" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Sols els ocupants poden enviar sol·licituds a la sala" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Sols els administradors del servei tenen permís per a enviar missatges de " "servei" #: ejabberd_web_admin:2192 ejabberd_web_admin:2347 msgid "Options" msgstr "Opcions" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nom de la organizació" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unitat de la organizació" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Connexions s2s d'eixida" #: ejabberd_web_admin:1309 msgid "Outgoing s2s Connections:" msgstr "Connexions d'eixida s2s" #: mod_muc_room:3035 mod_muc_room:3079 mod_muc_room:3709 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Es requerixen privilegis de propietari de la sala" #: mod_offline:693 msgid "Packet" msgstr "Paquet" #: mod_irc:578 msgid "Parse error" msgstr "Error en el processat" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "El processat ha fallat" #: ejabberd_oauth:431 ejabberd_web_admin:1161 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:799 #: mod_register:229 msgid "Password" msgstr "Contrasenya" #: mod_configure:1130 msgid "Password Verification" msgstr "Verificació de la Contrasenya" #: mod_register_web:287 mod_register_web:401 msgid "Password Verification:" msgstr "Verificació de la Contrasenya:" #: mod_irc:822 msgid "Password ~b" msgstr "Contrasenya ~b" #: ejabberd_web_admin:1439 mod_register_web:264 mod_register_web:504 msgid "Password:" msgstr "Contrasenya:" #: mod_configure:998 msgid "Path to Dir" msgstr "Ruta al directori" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Ruta al fitxer" #: mod_roster:915 msgid "Pending" msgstr "Pendent" #: ejabberd_web_admin:720 msgid "Period: " msgstr "Període: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Sales permanents" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "La petició de Ping es incorrecta" #: ejabberd_web_admin:1709 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Recorda que aquestes opcions només fan còpia de seguretat de la base de " "dades Mnesia. Si estàs utilitzant el mòdul d'ODBC també deus de fer una " "còpia de seguretat de la base de dades de SQL a part." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Si us plau, espera una mica abans d'enviar una nova petició de veu" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2189 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Processar l'atribut 'ask' no està permès per RFC6121" #: ejabberd_web_admin:2190 msgid "Protocol" msgstr "Protocol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Petició de subscriptor PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publicar-subscriure't" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Publicar elements en una col·lecció de nodes no està permès" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" " En aquesta sala no es permeten sol·licituds als membres de la conferència" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Enviar peticions a altres usuaris no està permès" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Còpia en RAM i disc" #: mod_configure:889 msgid "RAM copy" msgstr "Còpia en RAM" #: ejabberd_web_admin:1616 msgid "RPC Call Error" msgstr "Error de cridada RPC" #: ejabberd_web_admin:532 ejabberd_web_admin:631 msgid "Raw" msgstr "en format text" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Segur que vols eliminar el missatge del dia?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "El receptor no està en la sala de conferència" #: mod_register_web:294 msgid "Register" msgstr "Registrar" #: mod_register_web:210 mod_register_web:229 mod_register_web:237 msgid "Register a Jabber account" msgstr "Registrar un compte Jabber" #: ejabberd_web_admin:1092 msgid "Registered Users" msgstr "Usuaris registrats" #: ejabberd_web_admin:1303 ejabberd_web_admin:1322 msgid "Registered Users:" msgstr "Usuaris registrats:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Sobrenoms registrats" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registre en mod_irc per a" #: mod_configure:889 msgid "Remote copy" msgstr "Còpia remota" #: mod_roster:963 msgid "Remove" msgstr "Borrar" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Eliminar tots els missatges offline" #: ejabberd_web_admin:1446 mod_configure:1779 msgid "Remove User" msgstr "Eliminar usuari" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Reemplaçat per una nova connexió" #: mod_configure:1675 msgid "Resources" msgstr "Recursos" #: ejabberd_web_admin:1602 ejabberd_web_admin:2220 ejabberd_web_admin:2364 msgid "Restart" msgstr "Reiniciar" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Reiniciar el Servei" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restaurar" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaura còpia de seguretat des del fitxer en " #: ejabberd_web_admin:1739 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Restaurar una còpia de seguretat binària després de reiniciar el ejabberd " "(requereix menys memòria:" #: ejabberd_web_admin:1729 msgid "Restore binary backup immediately:" msgstr "Restaurar una còpia de seguretat binària ara mateix." #: ejabberd_web_admin:1759 msgid "Restore plain text backup immediately:" msgstr "Restaurar una còpia de seguretat en format de text pla ara mateix:" #: mod_muc_log:635 msgid "Room Configuration" msgstr "Configuració de la sala" #: mod_muc_log:655 msgid "Room Occupants" msgstr "Nombre d'ocupants" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Se t'ha denegat el crear la sala per política del servei" #: mod_muc_log:827 msgid "Room description" msgstr "Descripció de la sala:" #: mod_muc_log:790 msgid "Room title" msgstr "Títol de la sala" #: mod_roster:1082 msgid "Roster" msgstr "Llista de contactes" #: mod_roster:334 msgid "Roster module has failed" msgstr "El modul de Roster ha fallat" #: mod_roster:968 msgid "Roster of " msgstr "Llista de contactes de " #: mod_configure:1671 msgid "Roster size" msgstr "Tamany de la llista" #: ejabberd_web_admin:1564 mod_configure:510 msgid "Running Nodes" msgstr "Nodes funcionant" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "La negociació SASL no està permesa en aquest estat" #: mod_muc_log:468 msgid "Saturday" msgstr "Dissabte" #: mod_irc:582 msgid "Scan error" msgstr "Error en el escanejat" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "L'escanejat ha fallat" #: ejabberd_web_admin:2008 msgid "Script check" msgstr "Comprovar script" #: mod_vcard:453 msgid "Search Results for " msgstr "Resultat de la búsqueda" #: mod_vcard:427 msgid "Search users in " msgstr "Cerca usuaris en " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Enviar anunci a tots els usuaris connectats" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Enviar anunci a tots els usuaris connectats a tots els hosts" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Enviar anunci a tots els usuaris" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Enviar anunci a tots els usuaris de tots els hosts" #: mod_muc_log:481 msgid "September" msgstr "Setembre" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Connexió al servidor ha fallat" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Les connexions de servidor a subdominis locals estan prohibides" #: mod_irc:842 msgid "Server ~b" msgstr "Servidor ~b" #: mod_register_web:261 mod_register_web:390 mod_register_web:501 msgid "Server:" msgstr "Servidor:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Configurar el missatge del dia i enviar a tots els usuaris" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Escriure missatge del dia en tots els hosts i enviar-ho als usuaris " "connectats" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Grups de contactes compartits" #: ejabberd_web_admin:742 msgid "Show Integral Table" msgstr "Mostrar Taula Integral" #: ejabberd_web_admin:739 msgid "Show Ordinary Table" msgstr "Mostrar Taula Ordinaria" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Apager el Servei" #: mod_register_web:277 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Alguns clients Jabber poden emmagatzemar la teva contrasenya al teu " "ordinador. Fes servir aquesta característica només si saps que el teu " "ordinador és segur." #: ejabberd_web_admin:2244 ejabberd_web_admin:2380 msgid "Start" msgstr "Iniciar" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Iniciar mòduls" #: mod_configure:936 msgid "Start Modules at " msgstr "Iniciar mòduls en " #: ejabberd_web_admin:749 ejabberd_web_admin:1597 mod_muc_admin:366 msgid "Statistics" msgstr "Estadístiques" #: ejabberd_web_admin:1925 msgid "Statistics of ~p" msgstr "Estadístiques de ~p" #: ejabberd_web_admin:1604 ejabberd_web_admin:2224 ejabberd_web_admin:2368 msgid "Stop" msgstr "Detindre" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Parar mòduls" #: mod_configure:918 msgid "Stop Modules at " msgstr "Detindre mòduls en " #: ejabberd_web_admin:1565 mod_configure:511 msgid "Stopped Nodes" msgstr "Nodes parats" #: ejabberd_web_admin:1678 msgid "Storage Type" msgstr "Tipus d'emmagatzematge" #: ejabberd_web_admin:1719 msgid "Store binary backup:" msgstr "Guardar una còpia de seguretat binària:" #: ejabberd_web_admin:1749 msgid "Store plain text backup:" msgstr "Guardar una còpia de seguretat en format de text pla:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Assumpte" #: ejabberd_web_admin:500 ejabberd_web_admin:540 ejabberd_web_admin:605 #: ejabberd_web_admin:670 ejabberd_web_admin:1689 mod_shared_roster:933 msgid "Submit" msgstr "Enviar" #: ejabberd_web_admin:488 ejabberd_web_admin:527 ejabberd_web_admin:593 #: ejabberd_web_admin:626 ejabberd_web_admin:662 ejabberd_web_admin:1147 #: ejabberd_web_admin:1430 ejabberd_web_admin:1586 ejabberd_web_admin:1620 #: ejabberd_web_admin:1700 ejabberd_web_admin:1870 ejabberd_web_admin:1899 #: ejabberd_web_admin:1996 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Enviat" #: mod_roster:914 msgid "Subscription" msgstr "Subscripció" #: mod_muc_room:3720 msgid "Subscriptions are not allowed" msgstr "Les subscripcions no estan permeses" #: mod_muc_log:469 msgid "Sunday" msgstr "Diumenge" #: mod_muc_room:998 mod_muc_room:1849 mod_muc_room:3749 msgid "That nickname is already in use by another occupant" msgstr "El Nickname està siguent utilitzat per una altra persona" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1858 mod_muc_room:3752 msgid "That nickname is registered by another person" msgstr "El nickname ja està registrat per una altra persona" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "El CAPTCHA es vàlid." #: mod_muc_room:653 mod_muc_room:3684 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "La verificació CAPTCHA ha fallat" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" "La característica sol·licitada no està suportada per la sala de conferència" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "La contrasenya conté caràcters inacceptables" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "La contrasenya és massa simple" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "La contrasenya del teu compte Jabber s'ha canviat correctament." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "La petició està permesa només d'usuaris locals" #: mod_roster:203 msgid "The query must not contain elements" msgstr "La petició no pot contenir elements " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "El paquet DEU contindre només un element , un element , o " "un element " #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Hi ha hagut un error canviant la contrasenya: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Hi ha hagut un error creant el compte: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Hi ha hagut un error esborrant el compte: " #: mod_register_web:255 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Això no distingeix majúscules de minúscules: macbeth es el mateix que " "MacBeth i Macbeth." #: mod_register_web:239 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Aquesta pàgina permet crear un compte Jabber en aquest servidor Jabber. El " "teu JID (Jabber IDentifier; Identificador Jabber) tindrà aquesta forma: " "usuari@servidor. Si us plau, llegeix amb cura les instruccions per emplenar " "correctament els camps." #: mod_register_web:491 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Aquesta pàgina permet anul·lar el registre d'un compte Jabber en aquest " "servidor Jabber." #: mod_muc_log:801 msgid "This room is not anonymous" msgstr "Aquesta sala no és anònima" #: mod_muc_log:466 msgid "Thursday" msgstr "Dijous" #: mod_offline:690 msgid "Time" msgstr "Data" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Temps de retard" #: mod_offline:692 msgid "To" msgstr "Per a" #: mod_register:215 msgid "To register, visit ~s" msgstr "Per a registrar-te, visita ~s" #: mod_configure:709 msgid "To ~s" msgstr "A ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Token TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "El valor de l'atribut 'xml:lang' és massa llarg" #: mod_muc_room:2553 mod_muc_room:3093 msgid "Too many elements" msgstr "N'hi ha massa elements " #: mod_privacy:164 msgid "Too many elements" msgstr "N'hi ha massa elements " #: mod_muc_room:1927 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Massa peticions de CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "N'hi ha massa Bytestreams actius" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Massa missatges sense haver reconegut la seva recepció" #: mod_muc_room:1808 msgid "Too many users in this conference" msgstr "N'hi ha massa usuaris en esta sala de conferència" #: mod_register:355 msgid "Too many users registered" msgstr "Massa usuaris registrats" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Nombre total de sales" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "El llímit de tràfic ha sigut sobrepassat" #: ejabberd_web_admin:1945 msgid "Transactions Aborted:" msgstr "Transaccions Avortades" #: ejabberd_web_admin:1941 msgid "Transactions Committed:" msgstr "Transaccions Realitzades:" #: ejabberd_web_admin:1953 msgid "Transactions Logged:" msgstr "Transaccions registrades" #: ejabberd_web_admin:1949 msgid "Transactions Restarted:" msgstr "Transaccions reiniciades" #: mod_muc_log:464 msgid "Tuesday" msgstr "Dimarts" #: mod_muc_room:1936 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "No s'ha pogut generar un CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "No s'ha pogut registrar la ruta al domini local existent" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "No autoritzat" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Acció inesperada" #: mod_register_web:509 msgid "Unregister" msgstr "Anul·lar el registre" #: mod_register_web:215 mod_register_web:481 mod_register_web:489 msgid "Unregister a Jabber account" msgstr "Anul·lar el registre d'un compte Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "Element no soportat" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Petició MIX no soportada" #: ejabberd_web_admin:1598 ejabberd_web_admin:2011 msgid "Update" msgstr "Actualitzar" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Actualitzar el missatge del dia (no enviar)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Actualitza el missatge del dia en tots els hosts (no enviar)" #: ejabberd_web_admin:2004 msgid "Update plan" msgstr "Pla d'actualització" #: ejabberd_web_admin:2006 msgid "Update script" msgstr "Script d'actualització" #: ejabberd_web_admin:1993 msgid "Update ~p" msgstr "Actualitzar ~p" #: ejabberd_web_admin:1929 msgid "Uptime:" msgstr "Temps en marxa" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "No està permès utilitzar STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "És obligatori utilitzar STARTTLS" #: ejabberd_web_admin:1156 ejabberd_web_admin:1212 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Usuari" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Usuari (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Gestió d'Usuaris" #: mod_register:345 msgid "User already exists" msgstr "El usuari ja existeix" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "La part d'usuari del JID a 'from' està buida" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Sessió d'usuari no trobada" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Sessió d'usuari terminada" #: ejabberd_web_admin:1426 msgid "User ~s" msgstr "Usuari ~s" #: mod_register_web:249 mod_register_web:386 mod_register_web:497 msgid "Username:" msgstr "Nom d'usuari:" #: ejabberd_web_admin:685 ejabberd_web_admin:693 msgid "Users" msgstr "Usuaris" #: ejabberd_web_admin:716 msgid "Users Last Activity" msgstr "Última activitat d'usuari" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Els usuaris no tenen permís per a crear comptes tan depresa" #: mod_roster:954 msgid "Validate" msgstr "Validar" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3674 mod_muc_room:3814 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "El valor 'get' a l'atribut 'type' no és permès" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3609 mod_muc_room:3653 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "El valor 'set' a l'atribut 'type' no és permès" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "El valor de '~s' deuria ser booleà" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "El valor de '~s' deuria ser una data" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "El valor de '~s' deuria ser un numero enter" #: ejabberd_web_admin:676 msgid "Virtual Hosts" msgstr "Hosts virtuals" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Els visitants no tenen permés canviar el seus Nicknames en esta sala" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Els visitants no poden enviar missatges a tots els ocupants" #: mod_muc_room:3891 msgid "Voice request" msgstr "Petició de veu" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Les peticions de veu es troben desactivades en aquesta conferència" #: mod_muc_log:465 msgid "Wednesday" msgstr "Dimecres" #: mod_register_web:274 msgid "You can later change your password using a Jabber client." msgstr "" "Podràs canviar la teva contrasenya més endavant utilitzant un client Jabber." #: mod_muc_room:1836 msgid "You have been banned from this room" msgstr "Has sigut bloquejat en aquesta sala" #: mod_muc_room:1817 msgid "You have joined too many conferences" msgstr "Has entrat en massa sales de conferència" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Deus d'omplir el camp \"Nickname\" al formulari" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Necessites un client amb suport x:data i de CAPTCHA para poder registrar-te" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Necessites un client amb suport x:data per a poder registrar el sobrenom" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Necessites un client amb suport x:data per a configurar les opcions de " "mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Necessites un client amb suport x:data per a poder buscar" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "No tens permís per a crear nodes" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "El teu compte Jabber ha sigut creat correctament." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "El teu compte Jabber ha sigut esborrat correctament." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" "La teva llista de privacitat activa ha denegat l'encaminament d'aquesta " "stanza." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "La cua de missatges offline és plena. El missatge ha sigut descartat" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Els teus missatges per ~s s'estan bloquejant. Per desbloquejar-los, visita ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "mòdul ejabberd IRC" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "mòdul ejabberd MUC" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Servei de Multicast d'ejabberd" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Mòdul ejabberd Publicar-Subscriure" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "mòdul ejabberd SOCKS5 Bytestreams" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Web d'administració del ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Mòdul ejabberd vCard" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "Has sigut banejat" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "Has sigut expulsat" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "Has sigut expulsat perquè el sistema s'ha apagat" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "Has sigut expulsat a causa d'un canvi d'afiliació" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "Has sigut expulsat perquè la sala ha canviat a sols membres" #: mod_muc_log:410 msgid "is now known as" msgstr "ara es conegut com" #: mod_muc_log:370 msgid "joins the room" msgstr "Entrar a la sala" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "Deixar la sala" #: mod_muc_room:3868 msgid "private, " msgstr "privat" #: mod_muc_room:3967 msgid "the password is" msgstr "la contrasenya és" #: mod_vcard:292 msgid "vCard User Search" msgstr "Recerca de vCard d'usuari" #: ejabberd_web_admin:658 msgid "~s access rule configuration" msgstr "Configuració de les Regles d'Accés ~s" #: mod_muc_room:3960 msgid "~s invites you to the room ~s" msgstr "~s et convida a la sala ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s's cua de missatges offline" #~ msgid "No resource provided" #~ msgstr "Recurs no disponible" #~ msgid "Server" #~ msgstr "Servidor" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Massa autenticacions (~p) han fallat des d'aquesta adreça IP (~s). " #~ "L'adreça serà desbloquejada en ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Per favor especifica la mida del fitxer." #~ msgid "Please specify file name." #~ msgstr "Per favor especifica el nom del fitxer." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Esta adreça IP està a la llista negra en ~s" #~ msgid "Empty Rooms" #~ msgstr "Sales buides " #~ msgid "Jabber ID ~s is invalid" #~ msgstr "El Jabber ID ~s no és vàlid" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiliació invàlida: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Rol invàlid: ~s" #~ msgid "No limit" #~ msgstr "Sense Llímit" #~ msgid "Present real Jabber IDs to" #~ msgstr "Presentar Jabber ID's reals a" #~ msgid "moderators only" #~ msgstr "només moderadors" #~ msgid "anyone" #~ msgstr "qualsevol" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Rols per als que sí se difon la seua presencia" #~ msgid "Moderator" #~ msgstr "Moderador" #~ msgid "Participant" #~ msgstr "Participant" #~ msgid "Visitor" #~ msgstr "Visitant" #~ msgid "nobody" #~ msgstr "ningú" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permetre als visitants enviar peticions de veu" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Interval mínim entre peticions de veu (en segons)" #~ msgid "Enable message archiving" #~ msgstr "Activar l'emmagatzematge de missatges" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Excloure Jabber IDs de la comprovació CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Necessites un client amb suport x:data per a configurar la sala" #~ msgid "Number of occupants" #~ msgstr "Número d'ocupants" #~ msgid "User JID" #~ msgstr "JID del usuari " #~ msgid "Grant voice to this person?" #~ msgstr "Concedir veu a aquesta persona?" #~ msgid "Node ID" #~ msgstr "ID del Node" #~ msgid "Subscriber Address" #~ msgstr "Adreça del Subscriptor" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "" #~ "Permetre que aquesta Jabber ID es puga subscriure a aquest node pubsub" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Enviar payloads junt a les notificacions d'events" #~ msgid "Deliver event notifications" #~ msgstr "Entrega de notificacions d'events" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Notificar subscriptors quan canvia la configuració del node" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Notificar subscriptors quan el node és eliminat" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Notificar subscriptors quan els elements són eliminats del node" #~ msgid "Persist items to storage" #~ msgstr "Persistir elements al guardar" #~ msgid "A friendly name for the node" #~ msgstr "Un nom per al node" #~ msgid "Max # of items to persist" #~ msgstr "Màxim # d'elements que persistixen" #~ msgid "Whether to allow subscriptions" #~ msgstr "Permetre subscripcions" #~ msgid "Specify the access model" #~ msgstr "Especificar el model d'accés" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Llista de grups que tenen permés subscriures" #~ msgid "Specify the publisher model" #~ msgstr "Especificar el model del publicant" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Eliminar tots els elements quan el publicant relevant es desconnecti" #~ msgid "Specify the event message type" #~ msgstr "Especifica el tipus de missatge d'event" #~ msgid "Max payload size in bytes" #~ msgstr "Màxim tamany del payload en bytes" #~ msgid "When to send the last published item" #~ msgstr "Quan s'ha enviat l'última publicació" #~ msgid "Only deliver notifications to available users" #~ msgstr "Sols enviar notificacions als usuaris disponibles" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Les col.leccions amb les que un node està afiliat" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Emplena camps per a buscar usuaris Jabber que concorden" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Servidors d'eixida de s2s" #~ msgid "Delete" #~ msgstr "Eliminar" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Aquest participant ha sigut expulsat de la sala perque ha enviat un " #~ "missatge d'error" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Aquest participant ha sigut expulsat de la sala perque ha enviat un " #~ "missatge erroni a un altre participant" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Aquest participant ha sigut expulsat de la sala perque ha enviat un error " #~ "de presencia" ejabberd-18.01/priv/msgs/pt-br.msg0000644000232200023220000006164113225664356017340 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Aceito"}. {"Access Configuration","Configuração de Acesso"}. {"Access Control List Configuration","Configuração da Lista de Controle de Acesso"}. {"Access control lists","Listas de Controle de Acesso"}. {"Access Control Lists","Listas de Controle de Acesso"}. {"Access denied by service policy","Acesso negado pela política do serviço"}. {"Access rules","Regras de acesso"}. {"Access Rules","Regras de Acesso"}. {"Action on user","Ação no usuário"}. {"Add Jabber ID","Adicionar ID jabber"}. {"Add New","Adicionar novo"}. {"Add User","Adicionar usuário"}. {"Administration","Administração"}. {"Administration of ","Administração de "}. {"Administrator privileges required","Se necessita privilégios de administrador"}. {"All activity","Todas atividades"}. {"Allow users to change the subject","Permitir a usuários modificar o assunto"}. {"Allow users to query other users","Permitir a usuários pesquisar informações sobre os demais"}. {"Allow users to send invites","Permitir a usuários envio de convites"}. {"Allow users to send private messages","Permitir a usuários enviarem mensagens privadas"}. {"Allow visitors to change nickname","Permitir mudança de apelido aos visitantes"}. {"Allow visitors to send private messages to","Permitir visitantes enviar mensagem privada para"}. {"Allow visitors to send status text in presence updates","Permitir atualizações de status aos visitantes"}. {"All Users","Todos os usuários"}. {"Announcements","Anúncios"}. {"A password is required to enter this room","Se necessita senha para entrar nesta sala"}. {"April","Abril"}. {"August","Agosto"}. {"Backup Management","Gestão de Backup"}. {"Backup of ~p","Backup de ~p"}. {"Backup","Salvar cópia de segurança"}. {"Backup to File at ","Salvar backup para arquivo em "}. {"Bad format","Formato incorreto"}. {"Birthday","Aniversário"}. {"CAPTCHA web page","CAPTCHA web page"}. {"Change Password","Mudar senha"}. {"Change User Password","Alterar Senha do Usuário"}. {"Characters not allowed:","Caracteres não aceitos:"}. {"Chatroom configuration modified","Configuração da sala de bate-papo modificada"}. {"Chatroom is created","A sala de chat está criada"}. {"Chatroom is destroyed","A sala de chat está destruída"}. {"Chatroom is started","A sala de chat está iniciada"}. {"Chatroom is stopped","A sala de chat está parada"}. {"Chatrooms","Salas de Chat"}. {"Choose a username and password to register with this server","Escolha um nome de usuário e senha para registrar-se neste servidor"}. {"Choose modules to stop","Selecione módulos a parar"}. {"Choose storage type of tables","Selecione o tipo de armazenamento das tabelas"}. {"Choose whether to approve this entity's subscription.","Aprovar esta assinatura."}. {"City","Cidade"}. {"Commands","Comandos"}. {"Conference room does not exist","A sala de conferência não existe"}. {"Configuration","Configuração"}. {"Configuration of room ~s","Configuração para ~s"}. {"Connected Resources:","Recursos conectados:"}. {"Connections parameters","Parâmetros para as Conexões"}. {"Country","País"}. {"CPU Time:","Tempo de CPU"}. {"Database","Base de dados"}. {"Database Tables at ~p","Tabelas da Base de dados em ~p"}. {"Database Tables Configuration at ","Configuração de Tabelas de Base de dados em "}. {"December","Dezembro"}. {"Default users as participants","Usuários padrões como participantes"}. {"Delete message of the day","Apagar mensagem do dia"}. {"Delete message of the day on all hosts","Apagar a mensagem do dia em todos os hosts"}. {"Delete Selected","Remover os selecionados"}. {"Delete User","Deletar Usuário"}. {"Description:","Descrição:"}. {"Disc only copy","Somente cópia em disco"}. {"Displayed Groups:","Grupos Exibidos:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Não revele a sua senha a ninguém, nem mesmo para o administrador deste servidor Jabber."}. {"Dump Backup to Text File at ","Exportar backup para texto em "}. {"Dump to Text File","Exportar para arquivo texto"}. {"Edit Properties","Editar propriedades"}. {"Either approve or decline the voice request.","Você deve aprovar/desaprovar a requisição de voz."}. {"ejabberd IRC module","Módulo de IRC para ejabberd"}. {"ejabberd MUC module","Módulo de MUC para ejabberd"}. {"ejabberd Multicast service","ejabberd Multicast service"}. {"ejabberd Publish-Subscribe module","Módulo para Publicar Tópicos do ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Modulo ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"Elements","Elementos"}. {"Email","Email"}. {"Enable logging","Permitir criação de logs"}. {"Encoding for server ~b","Codificação para o servidor ~b"}. {"End User Session","Terminar Sessão do Usuário"}. {"Enter list of {Module, [Options]}","Introduza lista de {módulo, [opções]}"}. {"Enter nickname you want to register","Introduza o apelido que quer registrar"}. {"Enter path to backup file","Introduza o caminho do arquivo de backup"}. {"Enter path to jabberd14 spool dir","Introduza o caminho para o diretório de fila do jabberd14"}. {"Enter path to jabberd14 spool file","Insira o caminho para a fila (arquivo) do jabberd14"}. {"Enter path to text file","Introduza caminho para o arquivo texto"}. {"Enter the text you see","Insira o texto que você vê"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Insira o nome de usuário e codificações que você deseja usar para conectar-se aos servidores de IRC. Depois, presione 'Next' ('Próximo') para exibir mais campos que devem ser preenchidos. Ao final, pressione 'Complete' ('Completar') para salvar a configuração."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Insira o nome de usuário, codificações, portas e senhas que você deseja para usar nos servidores IRC"}. {"Erlang Jabber Server","Servidor Jabber em Erlang"}. {"Error","Erro"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemplo: [{\"irc.teste.net\", \"koi8-r\"}, 6667, \"senha\"}, {\"dominio.foo.net\", \"iso8859-1\", 7000}, {\"irc.servidordeteste.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Exportar todas as tabelas como SQL para um arquivo:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar todos os dados de todos os usuários no servidor, para arquivos formato PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar dados dos usuários em um host, para arquivos PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Não foi possível extrair o JID (Jabber ID) da requisição de voz"}. {"Family Name","Sobrenome"}. {"February","Fevereiro"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Preencha o formulário para buscar usuários Jabber. Agrega * ao final de um campo para buscar sub-palavras."}. {"Friday","Sexta"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Nome completo"}. {"Get Number of Online Users","Obter Número de Usuários Online"}. {"Get Number of Registered Users","Obter Número de Usuários Registrados"}. {"Get User Last Login Time","Obter a Data do Último Login"}. {"Get User Password","Obter Senha do Usuário"}. {"Get User Statistics","Obter Estatísticas do Usuário"}. {"Group ","Grupo "}. {"Groups","Grupos"}. {"has been banned","foi banido"}. {"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. {"has been kicked because of a system shutdown","foi desconectado porque o sistema foi desligado"}. {"has been kicked because the room has been changed to members-only","foi desconectado porque a política da sala mudou, só membros são permitidos"}. {"has been kicked","foi removido"}. {" has set the subject to: "," mudou o assunto para: "}. {"Host","Máquina"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se você não conseguir ver o CAPTCHA aqui, visite a web page."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se você deseja especificar portas diferentes, senhas ou codifações para servidores de IRC, complete esta lista com os valores no formato: '{\"servidor IRC\", \"codificação\", porta, \"senha\"}'. Por padrão, este serviço usa a codificação \"~s\", porta \"~p\", e senha em branco (vazia)"}. {"Import Directory","Importar diretório"}. {"Import File","Importar arquivo"}. {"Import user data from jabberd14 spool file:","Importar dados dos usuários de uma fila jabberd14:"}. {"Import User from File at ","Importar usuário a partir do arquivo em "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importar usuários de um arquivo PIEFXIS (XEP-0227): "}. {"Import users data from jabberd14 spool directory:","Importar dados dos usuários de um diretório-fila jabberd14:"}. {"Import Users from Dir at ","Importar usuários a partir do diretório em "}. {"Import Users From jabberd14 Spool Files","Importar usuários de arquivos jabberd14 (spool files)"}. {"Improper message type","Tipo de mensagem incorreto"}. {"Incoming s2s Connections:","Conexões que entram de s2s"}. {"Incorrect password","Senha incorreta"}. {"IP addresses","Endereços IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canal IRC (não coloque o #)"}. {"IRC server","Servidor IRC"}. {"IRC settings","Configurações do IRC"}. {"IRC Transport","Transporte IRC"}. {"IRC username","Usuário IRC"}. {"IRC Username","Usuário IRC"}. {"is now known as","é agora conhecido como"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Não é permitido o envio de mensagens de erro a esta sala. O membro (~s) enviou uma mensagem de erro (~s) e foi desconectado (\"kicked\")."}. {"It is not allowed to send private messages","Não é permitido enviar mensagens privadas"}. {"It is not allowed to send private messages of type \"groupchat\"","Não é permitido enviar mensagens privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensagens privadas para a sala"}. {"Jabber Account Registration","Registros de Contas Jabber"}. {"Jabber ID","ID Jabber"}. {"January","Janeiro"}. {"Join IRC channel","Juntar-se ao canal IRC"}. {"joins the room","Entrar na sala"}. {"Join the IRC channel here.","Aqui! Juntar-se ao canal IRC."}. {"Join the IRC channel in this Jabber ID: ~s","Entrar no canal IRC, neste ID Jabber: ~s"}. {"July","Julho"}. {"June","Junho"}. {"Last Activity","Última atividade"}. {"Last login","Último login"}. {"Last month","Último mês"}. {"Last year","Último ano"}. {"leaves the room","Sair da sala"}. {"Listened Ports at ","Portas abertas em "}. {"Listened Ports","Portas abertas"}. {"List of modules to start","Listas de módulos para inicializar"}. {"List of rooms","Lista de salas"}. {"Low level update script","Script de atualização low level"}. {"Make participants list public","Tornar pública a lista de participantes"}. {"Make room CAPTCHA protected","Tornar protegida a senha da sala"}. {"Make room members-only","Tornar sala apenas para membros"}. {"Make room moderated","Tornar a sala moderada"}. {"Make room password protected","Tornar sala protegida à senha"}. {"Make room persistent","Tornar sala persistente"}. {"Make room public searchable","Tornar sala pública possível de ser encontrada"}. {"March","Março"}. {"Maximum Number of Occupants","Número máximo de participantes"}. {"May","Maio"}. {"Membership is required to enter this room","Necessitas ser membro desta sala para poder entrar"}. {"Members:","Membros:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memorize a sua senha, ou escreva-a em um papel e guarde-o em um lugar seguro. Jabber não é uma maneira automatizada para recuperar a sua senha, se você a esquecer eventualmente."}. {"Memory","Memória"}. {"Message body","Corpo da mensagem"}. {"Middle Name","Nome do meio"}. {"Moderator privileges required","Se necessita privilégios de moderador"}. {"Modified modules","Módulos atualizados"}. {"Module","Módulo"}. {"Modules at ~p","Módulos em ~p"}. {"Modules","Módulos"}. {"Monday","Segunda"}. {"Multicast","Multicast"}. {"Multi-User Chat","Chat multi-usuário"}. {"Name:","Nome:"}. {"Name","Nome"}. {"Never","Nunca"}. {"New Password:","Nova Senha:"}. {"Nickname","Apelido"}. {"Nickname Registration at ","Registro do apelido em "}. {"Nickname ~s does not exist in the room","O apelido ~s não existe na sala"}. {"No body provided for announce message","Nenhum corpo de texto fornecido para anunciar mensagem"}. {"No Data","Nenhum dado"}. {"Node not found","Nó não encontrado"}. {"Node ~p","Nó ~p"}. {"Nodes","Nós"}. {"None","Nenhum"}. {"Not Found","Não encontrado"}. {"November","Novembro"}. {"Number of online users","Número de usuários online"}. {"Number of registered users","Número de usuários registrados"}. {"October","Outubro"}. {"Offline Messages:","Mensagens offline"}. {"Offline Messages","Mensagens offline"}. {"OK","OK"}. {"Old Password:","Senha Antiga:"}. {"Online","Conectado"}. {"Online Users","Usuários conectados"}. {"Online Users:","Usuários online"}. {"Only members may query archives of this room","Somente os membros podem procurar nos arquivos desta sala"}. {"Only moderators and participants are allowed to change the subject in this room","Somente os moderadores e os participamentes podem alterar o assunto desta sala"}. {"Only moderators are allowed to change the subject in this room","Somente os moderadores podem alterar o assunto desta sala"}. {"Only moderators can approve voice requests","Somente moderadores podem aprovar requisições de voz"}. {"Only occupants are allowed to send messages to the conference","Somente os ocupantes podem enviar mensagens à sala"}. {"Only occupants are allowed to send queries to the conference","Somente os ocupantes podem enviar consultas à sala"}. {"Only service administrators are allowed to send service messages","Apenas administradores possuem permissão para enviar mensagens de serviço"}. {"Options","Opções"}. {"Organization Name","Nome da organização"}. {"Organization Unit","Departamento/Unidade"}. {"Outgoing s2s Connections","Conexões que partam de s2s"}. {"Outgoing s2s Connections:","Conexões que partem de s2s"}. {"Owner privileges required","Se requer privilégios de proprietário da sala"}. {"Packet","Pacote"}. {"Password ~b","Senha ~b"}. {"Password:","Senha:"}. {"Password","Senha"}. {"Password Verification:","Verificação de Senha"}. {"Password Verification","Verificação de Senha"}. {"Path to Dir","Caminho para o diretório"}. {"Path to File","Caminho do arquivo"}. {"Pending","Pendente"}. {"Period: ","Período: "}. {"Permanent rooms","Salas permanentes"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Observe que tais opções farão backup apenas da base de dados Mnesia. Caso você esteja utilizando o modulo ODBC, você precisará fazer backup de sua base de dados SQL separadamente."}. {"Please, wait for a while before sending new voice request","Por favor, espere antes de enviar uma nova requisição de voz"}. {"Pong","Pong"}. {"Port ~b","Porta ~b"}. {"Port","Porta"}. {"private, ","privado, "}. {"Protocol","Porta"}. {"Publish-Subscribe","Publicação de Tópico"}. {"PubSub subscriber request","PubSub requisição de assinante"}. {"Queries to the conference members are not allowed in this room","Nesta sala não se permite consultas aos membros da sala"}. {"RAM and disc copy","Cópias na RAM e disco rígido"}. {"RAM copy","Cópia em RAM"}. {"Raw","Intocado"}. {"Really delete message of the day?","Deletar realmente a mensagem do dia?"}. {"Recipient is not in the conference room","O receptor não está na sala de conferência"}. {"Register a Jabber account","Registrar uma conta Jabber"}. {"Registered nicknames","Usuários registrados"}. {"Registered Users:","Usuários registrados"}. {"Registered Users","Usuários Registrados"}. {"Register","Registrar"}. {"Registration in mod_irc for ","Registro em mod_irc para "}. {"Remote copy","Cópia remota"}. {"Remove All Offline Messages","Remover Todas as Mensagens Offline"}. {"Remove","Remover"}. {"Remove User","Remover usuário"}. {"Replaced by new connection","Substituído por nova conexão"}. {"Resources","Recursos"}. {"Restart","Reiniciar"}. {"Restart Service","Reiniciar Serviço"}. {"Restore Backup from File at ","Restaurar backup a partir do arquivo em "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar backup binário após reinicialização do ejabberd (requer menos memória):"}. {"Restore binary backup immediately:","Restaurar backup binário imediatamente"}. {"Restore plain text backup immediately:","Restaurar backup formato texto imediatamente:"}. {"Restore","Restaurar"}. {"Room Configuration","Configuração de salas"}. {"Room creation is denied by service policy","Sala não pode ser criada devido à política do serviço"}. {"Room description","Descrição da Sala"}. {"Room Occupants","Número de participantes"}. {"Room title","Título da sala"}. {"Roster","Lista de contatos"}. {"Roster of ","Lista de contatos de "}. {"Roster size","Tamanho da Lista"}. {"RPC Call Error","Erro de chamada RPC"}. {"Running Nodes","Nós em execução"}. {"~s access rule configuration","Configuração da Regra de Acesso ~s"}. {"Saturday","Sábado"}. {"Script check","Verificação de Script"}. {"Search Results for ","Resultados de pesquisa para "}. {"Search users in ","Procurar usuários em "}. {"Send announcement to all online users","Enviar anúncio a todos os usuárions online"}. {"Send announcement to all online users on all hosts","Enviar anúncio a todos usuários online em todas as máquinas"}. {"Send announcement to all users","Enviar anúncio a todos os usuários"}. {"Send announcement to all users on all hosts","Enviar aviso para todos os usuários em todos os hosts"}. {"September","Setembro"}. {"Server ~b","Servidor ~b"}. {"Server:","Servidor:"}. {"Set message of the day and send to online users","Definir mensagem do dia e enviar a todos usuários online"}. {"Set message of the day on all hosts and send to online users","Definir mensagem do dia em todos os hosts e enviar para os usuários online"}. {"Shared Roster Groups","Grupos Shared Roster"}. {"Show Integral Table","Mostrar Tabela Integral"}. {"Show Ordinary Table","Mostrar Tabela Ordinária"}. {"Shut Down Service","Parar Serviço"}. {"~s invites you to the room ~s","~s convidou você para a sala ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clientes jabber podem salvar a sua senha no seu computador. Use o recurso somente se você considera este computador seguro o suficiente."}. {"~s's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"Start","Iniciar"}. {"Start Modules at ","Iniciar módulos em "}. {"Start Modules","Iniciar módulos"}. {"Statistics","Estatísticas"}. {"Statistics of ~p","Estatísticas de ~p"}. {"Stop Modules at ","Parar módulos em "}. {"Stop Modules","Parar módulos"}. {"Stop","Parar"}. {"Stopped Nodes","Nós parados"}. {"Storage Type","Tipo de armazenamento"}. {"Store binary backup:","Armazenar backup binário:"}. {"Store plain text backup:","Armazenar backup em texto:"}. {"Subject","Assunto"}. {"Submit","Enviar"}. {"Submitted","Submetido"}. {"Subscription","Subscrição"}. {"Sunday","Domingo"}. {"That nickname is already in use by another occupant","O apelido (nick) já está sendo utilizado"}. {"That nickname is registered by another person","O apelido já está registrado por outra pessoa"}. {"The CAPTCHA is valid.","O CAPTCHA é inválido."}. {"The CAPTCHA verification has failed","A verificação do CAPTCHA falhou"}. {"the password is","a senha é"}. {"The password is too weak","Senha considerada fraca'"}. {"The password of your Jabber account was successfully changed.","A senha da sua conta Jabber foi mudada com sucesso."}. {"There was an error changing the password: ","Houve um erro ao mudar a senha: "}. {"There was an error creating the account: ","Houve um erro ao criar esta conta: "}. {"There was an error deleting the account: ","Houve um erro ao deletar esta conta: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Não é 'case insensitive': macbeth é o mesmo que MacBeth e ainda Macbeth. "}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta pagina aceita criações de novas contas Jabber neste servidor. O seu JID (Identificador Jabber) será da seguinte forma: 'usuário@servidor'. Por favor, leia cuidadosamente as instruções para preencher corretamente os campos."}. {"This page allows to unregister a Jabber account in this Jabber server.","Esta página aceita para deletar uma conta Jabber neste servidor."}. {"This room is not anonymous","Essa sala não é anônima"}. {"Thursday","Quinta"}. {"Time delay","Intervalo (Tempo)"}. {"Time","Tempo"}. {"Too many CAPTCHA requests","Número excessivo de requisições para o CAPTCHA"}. {"Too many unacked stanzas","número excessivo de instâncias sem confirmação"}. {"To","Para"}. {"To ~s","Para ~s"}. {"Total rooms","Salas no total"}. {"Traffic rate limit is exceeded","Limite de banda excedido"}. {"Transactions Aborted:","Transações abortadas:"}. {"Transactions Committed:","Transações salvas:"}. {"Transactions Logged:","Transações de log:"}. {"Transactions Restarted:","Transações reiniciadas:"}. {"Tuesday","Terça"}. {"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. {"Unauthorized","Não Autorizado"}. {"Unregister a Jabber account","Deletar conta Jabber"}. {"Unregister","Deletar registro"}. {"Update","Atualizar"}. {"Update message of the day (don't send)","Atualizar mensagem do dia (não enviar)"}. {"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. {"Update ~p","Atualizar ~p"}. {"Update plan","Plano de Atualização"}. {"Update script","Script de atualização"}. {"Uptime:","Uptime:"}. {"Use of STARTTLS required","É obrigatório uso de STARTTLS"}. {"User Management","Gerenciamento de Usuários"}. {"Username:","Usuário:"}. {"Users are not allowed to register accounts so quickly","Usuários não estão autorizados a registrar contas imediatamente"}. {"Users Last Activity","Últimas atividades dos usuários"}. {"User ~s","Usuário ~s"}. {"Users","Usuários"}. {"User","Usuário"}. {"Validate","Validar"}. {"vCard User Search","Busca de Usuário vCard"}. {"Virtual Hosts","Hosts virtuais"}. {"Visitors are not allowed to change their nicknames in this room","Nesta sala, os visitantes não podem mudar seus apelidos"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens a todos os ocupantes"}. {"Voice request","Requisição de voz"}. {"Voice requests are disabled in this conference","Requisições de voz estão desabilitadas nesta conferência"}. {"Wednesday","Quarta"}. {"You can later change your password using a Jabber client.","Mais tarde você pode alterar a sua senha usando um cliente Jabber."}. {"You have been banned from this room","Você foi banido desta sala"}. {"You must fill in field \"Nickname\" in the form","Você deve completar o campo \"Apelido\" no formulário"}. {"You need a client that supports x:data and CAPTCHA to register","Você precisa de um cliente com suporte de x:data para poder registrar o apelido"}. {"You need a client that supports x:data to register the nickname","Você precisa de um cliente com suporte a x:data para registrar o seu apelido"}. {"You need an x:data capable client to configure mod_irc settings","Necessitas um cliente com suporte de x:data para configurar as opções de mod_irc"}. {"You need an x:data capable client to search","Necessitas um cliente com suporte de x:data para poder buscar"}. {"Your active privacy list has denied the routing of this stanza.","Sua lista de privacidade ativa negou o roteamento desta instância."}. {"Your contact offline message queue is full. The message has been discarded.","Sua fila de mensagens offline esta cheia. Sua mensagem foi descartada"}. {"Your Jabber account was successfully created.","Sua conta jabber foi criada com sucesso."}. {"Your Jabber account was successfully deleted.","Sua conta Jabber foi deletada com sucesso."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s"}. ejabberd-18.01/priv/msgs/wa.po0000644000232200023220000017566313225664356016565 0ustar debalancedebalance# Pablo Saratxaga , 2015 msgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "PO-Revision-Date: 2015-01-18 12:19+0000\n" "Last-Translator: Pablo Saratxaga \n" "Language-Team: Pablo Saratxaga \n" "Language: wa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Walon (Walloon)\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " a candjî l' tite a: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "I fåt dner on scret po poleur intrer dins cisse såle ci" #: ejabberd_oauth:448 msgid "Accept" msgstr "Accepter" #: mod_configure:1109 msgid "Access Configuration" msgstr "Apontiaedje des accès" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Apontiaedje des droets (ACL)" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Droets (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Rîles d' accès" #: mod_configure:1095 msgid "Access control lists" msgstr "Droets (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "L' accès a stî rfuzé pal politike do siervice" #: mod_configure:1113 msgid "Access rules" msgstr "Rîles d' accès" #: mod_configure:1772 msgid "Action on user" msgstr "Accion so l' uzeu" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Radjouter èn ID Jabber" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Radjouter" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Radjouter èn uzeu" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Manaedjaedje" #: mod_configure:1767 msgid "Administration of " msgstr "Manaedjaedje di " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "I fåt des priviledjes di manaedjeu" #: mod_configure:507 msgid "All Users" msgstr "Tos les uzeus" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Dispoy todi" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Les uzeus polèt candjî l' tite" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Les uzeus polèt cweri ls ôtes uzeus" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Les uzeus polèt evoyî priyaedjes" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Les uzeus polèt evoyî des messaedjes privés" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Permete ki les viziteus candjexhe leus metous nos" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Les uzeus polèt evoyî des messaedjes privés" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" "Permete ki les viziteus evoyexhe des tecse d' estat dins leus messaedjes di " "prezince" #: mod_announce:611 msgid "Announcements" msgstr "Anonces" #: mod_muc_log:476 msgid "April" msgstr "avri" #: mod_muc_log:480 msgid "August" msgstr "awousse" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Copeye di såvrité" #: mod_configure:584 msgid "Backup Management" msgstr "Manaedjaedje des copeyes di såvrité" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Copeye di såvrité po ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Fé ene copeye di såvrité dins on fitchî so " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Mwais fôrmat" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Date d' askepiaedje" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Pådje web CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Tins CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Candjî l' sicret" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Candjî l' sicret d' l' uzeu" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Caracteres nén permetous:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Caracteres nén permetous:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Caracteres nén permetous:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "L' apontiaedje del såle di berdelaedje a candjî" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Li såle di berdelaedje est ahivêye" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Li såle di berdelaedje est distrûte" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Li såle di berdelaedje est enondêye" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Li såle di berdelaedje est ahotêye" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Såles di berdelaedje" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Tchoezixhoz on no d' uzeu eyet on scret po vs edjîstrer so ç' sierveu ci" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Tchoezixhoz les modules a-z arester" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Tchoezi l' sôre di wårdaedje po les tåves" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Tchoezi s' i fåt aprover ou nén l' abounmint di ciste intité." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Veye" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Comandes" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Li såle di conferince n' egzistêye nén" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Apontiaedjes" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Apontiaedje del såle ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Raloyî avou les rsoûces:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parametes des raloyaedjes" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Payis" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Båze di dnêyes" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Apontiaedje des tåves del båze di dnêyes so " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Tåves del båze di dnêyes so ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Båze di dnêyes" #: mod_muc_log:484 msgid "December" msgstr "decimbe" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Les uzeus sont des pårticipants come prémetowe dujhance" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Disfacer les elemints tchoezis" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Disfacer èn uzeu" #: mod_announce:629 msgid "Delete message of the day" msgstr "Disfacer l' messaedje do djoû" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Disfacer l' messaedje do djoû so tos les lodjoes" #: mod_shared_roster:900 msgid "Description:" msgstr "Discrijhaedje:" #: mod_configure:889 msgid "Disc only copy" msgstr "Copeye seulmint sol deure plake" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Groupes håynés:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "Ni dnez vosse sicret a nolu, nén ddja ås manaedjeus do sierveu Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Copeye di såvritè viè on fitchî tecse so " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Schaper en on fitchî tecse" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Candjî les prôpietés" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Aprover oudonbén rifuzer li dmande di vwès." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elemints" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Emile" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "li scret est" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Mete en alaedje li djournå" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Ecôdaedje pol sierveu ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Fini l' session d' l' uzeu" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Dinez ene djivêye del cogne {Module, [Tchuzes]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Dinez l' metou no ki vos vloz edjîstrer" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Dinez l' tchimin viè l' fitchî copeye di såvrité" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Dinez l' tchimin viè l' ridant di spool jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Dinez l' tchimin viè l' fitchî di spool jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Dinez l' tchimin viè l' fitchî tecse" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Tapez l' tecse ki vos voeyoz" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Dinez les nos d' uzeu et ls ecôdaedjes ki vos vloz eployî po vs raloyî åzès " "sierveus IRC Clitchîz so «Shuvant» po-z aveur di pus di tchamps a rimpli. " "Clitchîz so «Fini» po schaper les apontiaedjes." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Dinez l' no d' uzeu, les ecôdaedjes, les pôrts et les screts ki vos vloz " "eployî po vs raloyî åzès sierveus IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Sierveu Jabber Erlang" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Aroke" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Egzimpe: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Espoirter totes les tåves, come des cmandes SQL, viè on fitchî" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS " "(XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS " "(XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" "Nén moyén di rsaetchî on JID foû d' l' aprovaedje di vosse dimande di vwès" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "No d' famile" #: mod_muc_log:474 msgid "February" msgstr "fevrî" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Rimplixhoz les tchamps do formulaire po cweri èn uzeu Jabber (radjouter «*» " "al fén do tchamp po cweri tot l' minme kéne fén d' tchinne" #: mod_muc_log:467 msgid "Friday" msgstr "vénrdi" #: mod_offline:691 msgid "From" msgstr "Di" #: mod_configure:723 msgid "From ~s" msgstr "Dispoy ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "No etir" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Riçure li nombe d' uzeus raloyîs" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Riçure li nombe d' uzeus edjîstrés" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Riçure li date/eure do dierin elodjaedje di l' uzeu" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Riçure sicret d' l' uzeu" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Riçure les statistikes di l' uzeu" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "No do mitan" #: mod_shared_roster:923 msgid "Group " msgstr "Groupe " #: mod_roster:916 msgid "Groups" msgstr "Groupes" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Sierveu" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Adresses IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transpoirt IRC" #: mod_irc:496 msgid "IRC Username" msgstr "No d' uzeu IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canå IRC (èn nén mete li prumî #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Nuk nén trové" #: mod_irc:673 msgid "IRC server" msgstr "Sierveu IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Apontiaedjes IRC" #: mod_irc:766 msgid "IRC username" msgstr "No d' uzeu IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Si vos n' voeyoz nole imådje CAPTCHA chal, vizitez l' pådje waibe." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Si vos vloz dner des pôrts, sicrets ou ecôdaedjes diferins po les sierveus " "IRC, rimplixhoz cisse djivêye ci avou des valixhances del cogne «{\"sierveu " "irc\", \"ecôdaedje\", pôrt, \"sicret\"}». Les prémetowès valixhances do " "siervice sont «~s» po l' ecôdaedje, «~p» pol pôrt, et on vude sicret." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Sititchî d' on ridant" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Sititchî d' on fitchî" #: mod_configure:982 msgid "Import User from File at " msgstr "Sititchî uzeu d' on fitchî so " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Sititchî des uzeus Jabberd 1.4" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Sitichî des uzeus d' on ridant so " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Sititchî des dnêyes uzeus foû d' on fitchî spoûle jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Sititchî des dnêyes uzeus foû d' on fitchî PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Sititchî des dnêyes uzeus foû d' on ridant spoûle jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Sôre di messaedje nén valide" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Raloyaedjes s2s en intrêye:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Sicret nén corek" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Sicret nén corek" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Les dmandes di vwès sont dismetowes e cisse conferince ci" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "On n' pout nén evoyî des messaedjes d' aroke sol såle. Li pårticipan (~s) a-" "st evoyî on messaedje d' aroke (~s) ey a stî tapé foû." #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Ci n' est nén permetou d' evoyî des messaedjes privés" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "C' est nén possibe d' evoyî des messaedjes privés del sôre «groupchat»" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "On n' pout nén evoyî des messaedjes privés dins cisse conferince ci" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Edjîstraedje di conte Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "ID Jabber" #: mod_muc_log:473 msgid "January" msgstr "djanvî" #: mod_irc:665 msgid "Join IRC channel" msgstr "Radjonde canå IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Radjonde li canå IRC droci." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Radjonde li canå IRC e cist ID Jabber: ~s" #: mod_muc_log:479 msgid "July" msgstr "djulete" #: mod_muc_log:478 msgid "June" msgstr "djun" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Dierinne activité" #: mod_configure:1646 msgid "Last login" msgstr "Dierin elodjaedje" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Dierin moes" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Dierinne anêye" #: mod_configure:941 msgid "List of modules to start" msgstr "Djivêye di modules a-z enonder" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Djivêye des såles" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Pôrts drovous" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Pôrts drovous so " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Sicripe di metaedje a djoû d' bas livea" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Rinde publike li djivêye des pårticipants" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Rinde li såle di berdelaedje protedjeye pa CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Rinde li såle di berdelaedje ristrindowe ås mimbes seulmint" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Rinde li såle di berdelaedje moderêye" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Rinde li såle di berdelaedje protedjeye pa scret" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Rinde li såle permaninte" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Rinde li såle di berdelaedje cweråve publicmint" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "No d' uzeu IRC" #: mod_muc_log:475 msgid "March" msgstr "måss" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Nombe macsimom di prezints" #: mod_muc_log:477 msgid "May" msgstr "may" #: mod_shared_roster:907 msgid "Members:" msgstr "Mimbes:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "I fåt esse mimbe po poleur intrer dins cisse såle ci" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Rimimbrez vosse sicret, ou scrijhoz l' so on papî ki vos wådroz en ene place " "bén a hoûte, ca avou Jabber i n' a pont moyén di rapexhî vosse sicret si vos " "l' rovyîz." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memwere" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Coir do messaedje" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "No do mitan" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "I fåt des priviledjes di moderateu" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Modules di candjîs" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Module" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Modules" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Modules so ~p" #: mod_muc_log:463 msgid "Monday" msgstr "londi" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Berdelaedje a sacwants" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "No" #: mod_shared_roster:896 msgid "Name:" msgstr "Pitit no:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Måy" #: mod_register_web:377 msgid "New Password:" msgstr "Novea scret:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Metou no" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Edjîstraedje di metou no amon " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Li metou no ~s n' egzistêye nén dins l' såle" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Nuk nén trové" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Nole dinêye disponibe" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "I n' a nou coir do messaedje po ciste anonce la" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Nuk nén trové" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Nuk nén trové" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Nuk nén trové" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nuk nén trové" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Nuk ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nuks" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nole" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Nén trové" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "nôvimbe" #: mod_configure:1212 msgid "Number of online users" msgstr "Nombe d' uzeus raloyîs" #: mod_configure:1202 msgid "Number of registered users" msgstr "Nombe d' uzeus edjîstrés" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "'l est bon" #: mod_muc_log:482 msgid "October" msgstr "octôbe" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Messaedjes ki ratindèt" #: mod_offline:761 msgid "Offline Messages:" msgstr "Messaedjes ki ratindèt:" #: mod_register_web:373 msgid "Old Password:" msgstr "Vî scret:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Raloyî" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Uzeus raloyîs" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Uzeus raloyîs:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Seulmint les mimbes polèt cweri les årtchives dins cisse såle ci" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Seulmint les moderateus et les pårticipants polèt candjî l' sudjet dins " "cisse såle ci" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Seulmint les moderateus polèt candjî l' sudjet dins cisse såle ci" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Seulmint les moderateus polèt aprover des dmandes di vwès" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Seulmint les prezints polèt evoyî des messaedjes al conferince" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Seulmint les prezints polèt evoyî des cweraedjes sol conferince" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Seulmint les manaedjeus d' siervices polèt evoyî des messaedjes di siervice" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Tchuzes" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "No d' l' organizåcion" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unité d' l' organizåcion" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Raloyaedjes s2s e rexhowe" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Raloyaedjes s2s e rexhowe:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "I fåt des priviledjes di prôpietaire" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Sicret" #: mod_configure:1130 msgid "Password Verification" msgstr "Acertinaedje do scret" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Acertinaedje do scret:" #: mod_irc:822 msgid "Password ~b" msgstr "Sicret ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Sicret:" #: mod_configure:998 msgid "Path to Dir" msgstr "Tchimin viè l' ridant" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Tchimin viè l' fitchî" #: mod_roster:915 msgid "Pending" msgstr "Ratindant" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Termene:" #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Såles tofer la" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Notez ki ces tchuzes la vont seulmint fé ene copeye di såvrité del båze di " "dnêyes Mnesia costrûte å dvins do programe. Si vos eployîz ene difoûtrinne " "båze di dnêyes avou l' module ODBC, vos dvoz fé ene copeye di såvrité del " "båze SQL da vosse sepårumint." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" "Ratindez ene miete s' i vs plait divant d' rivoyî ene nouve dimande di vwès" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Pôrt" #: mod_irc:828 msgid "Port ~b" msgstr "Pôrt ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocole" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Dimande d' eplaidaedje-abounmint d' èn abouné" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Eplaidaedje-abounmint" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Les cweraedjes des mimbes del conferince ni sont nén permetous dins cisse " "såle ci" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Copeye e memwere (RAM) et sol deure plake" #: mod_configure:889 msgid "RAM copy" msgstr "Copeye e memwere (RAM)" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Aroke di houcaedje RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Dinêyes brutes" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Voloz vs vormint disfacer l' messaedje do djoû?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Li riçuveu n' est nén dins l' såle di conferince" #: mod_register_web:275 msgid "Register" msgstr "Edjîstrer" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Edjîstrer on conte Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Uzeus edjistrés" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Uzeus edjistrés:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Metous nos edjistrés" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Edjîstraedje dins mod_irc po " #: mod_configure:889 msgid "Remote copy" msgstr "Copeye å lon" #: mod_roster:963 msgid "Remove" msgstr "Oister" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Oister tos les messaedjes ki ratindèt" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Disfacer l' uzeu" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Replaecî pa on novea raloyaedje" #: mod_configure:1675 msgid "Resources" msgstr "Rissoûces" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Renonder" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Renonder siervice" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Rapexhî" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Rapexhî dispoy li fitchî copeye di såvrité so " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Rapexhî l' copeye di såvrité binaire après l' renondaedje ki vént " "d' ejabberd (çoula prind moens d' memwere del fé insi):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Rapexhî do côp foû d' ene copeye di såvrité binaire:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Rapexhî do côp foû d' ene copeye di såvrité tecse:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Apontiaedje del såle" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Prezints el såle" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "L' ahivaedje del såle est rfuzé pal politike do siervice" #: mod_muc_log:1064 msgid "Room description" msgstr "Discrijhaedje del såle" #: mod_muc_log:1027 msgid "Room title" msgstr "Tite del såle" #: mod_roster:1082 msgid "Roster" msgstr "Djivêye des soçons" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Djivêye des soçons da " #: mod_configure:1671 msgid "Roster size" msgstr "Grandeu del djivêye des soçons" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nuks en alaedje" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "semdi" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Acertinaedje do scripe" #: mod_vcard:453 msgid "Search Results for " msgstr "Rizultats do cweraedje po " #: mod_vcard:427 msgid "Search users in " msgstr "Cweri des uzeus dins " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Evoyî l' anonce a tos les uzeus raloyîs" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Evoyî l' anonce a tos les uzeus raloyîs so tos les lodjoes" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Evoyî l' anonce a tos les uzeus" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Evoyî l' anonce a tos les uzeus so tos les lodjoes" #: mod_muc_log:481 msgid "September" msgstr "setimbe" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Sierveu ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Sierveu:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Defini l' messaedje do djoû et l' evoyî åzès uzeus raloyîs" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Defini l' messaedje do djoû so tos les lodjoes et l' evoyî åzès uzeus raloyîs" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Pårtaedjîs groupes ezès djivêyes di soçons" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Mostrer totå" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Mostrer crexhince" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Arester siervice" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Des cliyints Jabber k' i gn a polèt wårder vosse sicret sol copiutrece, mins " "vos n' duvrîz fé çoula ki sol copiutrece da vosse, po des råjhons di såvrité." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Enonder" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Enonder des modules" #: mod_configure:936 msgid "Start Modules at " msgstr "Renonder les modules so " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Sitatistikes" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Sitatistikes di ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Arester" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Arester des modules" #: mod_configure:918 msgid "Stop Modules at " msgstr "Arester les modules so " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nuks essoctés" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Sôre di wårdaedje" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Copeye di såvrité binaire:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Copeye di såvrité tecse:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Sudjet" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Evoyî" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Candjmints evoyîs" #: mod_roster:914 msgid "Subscription" msgstr "Abounmimnt" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "dimegne" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Li metou no est ddja eployî pa ene ôte sakî sol såle" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Li metou no est ddja edjîstré pa ene ôte sakî" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Li CAPTCHA est valide." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Li verifiaedje CAPTCHA a fwait berwete" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "li scret est trop flåw" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Li scret do conte Jabber da vosse a stî candjî comifåt." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Åk n' a nén stî tot candjant l' sicret: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Åk n' a nén stî tot ahivant l' conte: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Åk n' a nén stî tot disfaçant l' conte: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Pont d' diferince etur les grandes et ptitès letes: «macbeth» est l' minme " "ki «MacBeth» ou co «Macbeth»" #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Cisse pådje permete d' ahiver on conte Jabber so ç' sierveu Jabber ci. Li " "JID (IDintifieu Jabber) da vosse serè del cogne: noduzeu@sierveu. Lijhoz " "atintivmint les instruccions po bén rimpli les tchamps." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Cisse pådje permete di disdjîstrer on conte Jabber so ç' sierveu ci." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Cisse såle ci n' est nén anonime" #: mod_muc_log:466 msgid "Thursday" msgstr "djudi" #: mod_offline:690 msgid "Time" msgstr "Date" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Tårdjaedje" #: mod_offline:692 msgid "To" msgstr "Po" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Viè ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Pår trop di dmandes CAPTCHA" #: mod_proxy65_service:223 #, fuzzy msgid "Too many active bytestreams" msgstr "Pår trop di messaedjes sins acertinaedje di rçuvaedje" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Pår trop di messaedjes sins acertinaedje di rçuvaedje" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Les dmandes di vwès sont dismetowes e cisse conferince ci" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Totå di såles" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Li limite pol volume di trafik a stî passêye" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transaccions arestêyes:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transaccions evoyeyes:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transaccions wårdêyes e djournå:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transaccions renondêyes:" #: mod_muc_log:464 msgid "Tuesday" msgstr "mårdi" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Nén moyén di djenerer on CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Nén otorijhî" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Disdjîstrer" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Disdjîstrer on conte Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Mete a djoû" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Mete a djoû l' messaedje do djoû (nén l' evoyî)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Mete a djoû l' messaedje do djoû so tos les lodjoes (nén l' evoyî)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plan d' metaedje a djoû" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Sicripe di metaedje a djoû" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Metaedje a djoû di ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Tins dispoy l' enondaedje:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "L' eployaedje di STARTTL est oblidjî" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "L' eployaedje di STARTTL est oblidjî" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Uzeu" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Manaedjaedje des uzeus" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Nuk nén trové" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Uzeu ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "No d' uzeu:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Uzeus" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Dierinne activité des uzeus" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Les noveas uzeus n' si polèt nén edjîstrer si raddimint" #: mod_roster:954 msgid "Validate" msgstr "Valider" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Forveyous sierveus" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Les viziteus èn polèt nén candjî leus metous no po ç' såle ci" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Les viziteus n' polèt nén evoyî des messaedjes a tos les prezints" #: mod_muc_room:3879 msgid "Voice request" msgstr "Dimande di vwès" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Les dmandes di vwès sont dismetowes e cisse conferince ci" #: mod_muc_log:465 msgid "Wednesday" msgstr "mierkidi" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Vos ploz candjî vosse sicret pus tård avou on cliyint Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Vos avoz stî bani di cisse såle ci" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Vos dvoz rimpli l' tchamp «Metou no» dins l' formiulaire" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Vos avoz mezåjhe d' on cliyint ki sopoite x:data eyet CAPTCHA po vs edjîstrer" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Vos avoz mezåjhe d' on cliyint ki sopoite x:data po-z edjîstrer l' metou no" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Vos avoz mezåjhe d' on cliyint ki sopoite x:data po candjî ls apontiaedjes " "di mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Vos avoz mezåjhe d' on cliyint ki sopoite x:data po fé on cweraedje" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Ci n' est nén permetou d' evoyî des messaedjes privés" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Li conte Jabber da vosse a stî ahivé comifåt." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Li conte Jabber da vosse a stî disfacé comifåt." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" "Vosse djivêye di privaceye active a rfuzé l' evoyaedje di ç' messaedje ci." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Li cawêye di messaedjes e môde disraloyî di vosse soçon est plinne. Li " "messaedje a stî tapé å diale." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Vos messaedjes po ~s sont blokés. Po les disbloker, alez vey ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Module IRC po ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Module MUC (såles di berdelaedje) po ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "siervice multicast d' ejabberd" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Module d' eplaidaedje-abounmint po ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Module SOCKS5 Bytestreams po ejabberd" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Manaedjeu waibe ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Module vCard ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "a stî bani" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "a stî pité evoye" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "a stî pité evoye cåze d' èn arestaedje do sistinme" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "a stî pité evoye cåze d' on candjmint d' afiyaedje" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" "a stî pité evoye cåze ki l' såle a stî ristrindowe åzès mimbes seulmint" #: mod_muc_log:410 msgid "is now known as" msgstr "est asteure kinoxhou come" #: mod_muc_log:370 msgid "joins the room" msgstr "arive sol såle" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "cwite li såle" #: mod_muc_room:3856 msgid "private, " msgstr "privé, " #: mod_muc_room:3955 msgid "the password is" msgstr "li scret est" #: mod_vcard:292 msgid "vCard User Search" msgstr "Calpin des uzeus" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Apontiaedje des rîles d' accès a ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s vos preye sol såle ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "messaedjes ki ratindèt el cawêye po ~s" #~ msgid "No resource provided" #~ msgstr "Nole rissoûce di dnêye" #~ msgid "Server" #~ msgstr "Sierveu" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "I gn a-st avou pår trop (~p) d' otintifiaedjes k' ont fwait berwete " #~ "vinant di ciste adresse IP la (~s). L' adresse serè disblokêye a ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Dinez l' grandeu do fitchî." #~ msgid "Please specify file name." #~ msgstr "Dinez l' no do fitchî." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Ciste adresse IP est so ene noere djivêye e ~s" #~ msgid "Empty Rooms" #~ msgstr "Såles vudes" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Li Jabber ID ~s n' est nén valide" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiyaedje nén valide: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Role nén valide: ~s" #~ msgid "No limit" #~ msgstr "Pont d' limite" #~ msgid "Present real Jabber IDs to" #~ msgstr "Mostrer les vraiys Jabber IDs a" #~ msgid "moderators only" #~ msgstr "les moderateus seulmint" #~ msgid "anyone" #~ msgstr "tot l' minme kî" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Roles ki leu prezince est difuzêye" #~ msgid "Moderator" #~ msgstr "Moderateu" #~ msgid "Participant" #~ msgstr "Pårticipant" #~ msgid "Visitor" #~ msgstr "Viziteu" #~ msgid "nobody" #~ msgstr "nolu" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Les uzeus polèt evoyî des dmandes di vwès" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Tins minimom etur deus dmandes di vwès (e segondes)" #~ msgid "Enable message archiving" #~ msgstr "Mete en alaedje l' årtchivaedje des messaedjes" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Esclure les IDs Jabber des kesses CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "I vs fåt on cliyint ki sopoite x:data por vos poleur apontyî l' såle" #~ msgid "Number of occupants" #~ msgstr "Nombe di prezints" #~ msgid "User JID" #~ msgstr "JID d' l' uzeu" #~ msgid "Grant voice to this person?" #~ msgstr "Permete li vwès po cisse djin ci?" #~ msgid "Node ID" #~ msgstr "ID d' nuk" #~ msgid "Subscriber Address" #~ msgstr "Adresse di l' abouné" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "" #~ "Permete ki ci Jabber ID ci si poye abouner a ç' nuk eplaidaedje-abounmint " #~ "ci?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Evoyî l' contnou avou les notifiaedjes d' evenmints" #~ msgid "Deliver event notifications" #~ msgstr "Evoyî des notifiaedjes d' evenmints" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Notifyî åzès abounés cwand l' apontiaedje do nuk candje" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Notifyî åzès abounés cwand l' nuk est disfacé" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Notifyî åzès abounés cwand des cayets sont oisté foû do nuk" #~ msgid "Persist items to storage" #~ msgstr "Cayets permanints a wårder" #~ msgid "A friendly name for the node" #~ msgstr "On no uzeu-ahessåve pol nuk" #~ msgid "Max # of items to persist" #~ msgstr "Nombe macsimoms di cayets permanints" #~ msgid "Whether to allow subscriptions" #~ msgstr "Si on permete les abounmints" #~ msgid "Specify the access model" #~ msgstr "Sipecifyî l' modele d' accès" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Pårtaedjîs groupes di soçons k' on s' î pout abouner" #~ msgid "Specify the publisher model" #~ msgstr "Dinez l' modele d' eplaideu" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Purdjî tos les cayets cwand l' eplaideu aloyî va foû raloyaedje" #~ msgid "Specify the event message type" #~ msgstr "Sipecifyî l' sôre do messaedje d' evenmint" #~ msgid "Max payload size in bytes" #~ msgstr "Contnou macsimom en octets" #~ msgid "When to send the last published item" #~ msgstr "Cwand evoyî l' dierin cayet eplaidî" #~ msgid "Only deliver notifications to available users" #~ msgstr "Seulmint evoyî des notifiaedje åzès uzeus disponibes" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Les ramexhnêyes k' on nuk est afiyî avou" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Rimplixhoz les tchamps po cweri èn uzeu Jabber" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Sierveus s2s e rexhowe:" #~ msgid "Delete" #~ msgstr "Disfacer" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Ci pårticipant ci a stî pité evoye del såle cåze k' il a-st evoyî on " #~ "messaedje d' aroke" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Ci pårticipant ci a stî pité evoye del såle cåze k' il a-st evoyî on " #~ "messaedje d' aroke a èn ôte pårticipant" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Ci pårticipant ci a stî pité evoye del såle cåze k' il a-st evoyî ene " #~ "aroke di prezince" #~ msgid "Encodings" #~ msgstr "Ecôdaedjes" #~ msgid "(Raw)" #~ msgstr "(Dinêyes brutes)" #~ msgid "Specified nickname is already registered" #~ msgstr "Li no metou dné est ddja edjîstré" #~ msgid "Size" #~ msgstr "Grandeu" ejabberd-18.01/priv/msgs/eo.msg0000644000232200023220000005644713225664356016727 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Agordo de atingo"}. {"Access Control List Configuration","Agordo de atingokontrolo"}. {"Access control lists","Atingokontrol-listoj"}. {"Access Control Lists","Atingokontrol-listoj"}. {"Access denied by service policy","Atingo rifuzita de serv-politiko"}. {"Access rules","Atingo-reguloj"}. {"Access Rules","Atingo-reguloj"}. {"Action on user","Ago je uzanto"}. {"Add Jabber ID","Aldonu Jabber ID"}. {"Add New","Aldonu novan"}. {"Add User","Aldonu Uzanton"}. {"Administration","Administro"}. {"Administration of ","Mastrumado de "}. {"Administrator privileges required","Administrantaj rajtoj bezonata"}. {"All activity","Ĉiu aktiveco"}. {"Allow users to change the subject","Permesu uzantojn ŝanĝi la temon"}. {"Allow users to query other users","Permesu uzantojn informpeti aliajn uzantojn"}. {"Allow users to send invites","Permesu uzantojn sendi invitojn"}. {"Allow users to send private messages","Permesu uzantojn sendi privatajn mesaĝojn"}. {"Allow visitors to change nickname","Permesu al vizitantoj ŝanĝi siajn kaŝnomojn"}. {"Allow visitors to send private messages to","Permesu uzantojn sendi privatajn mesaĝojn al"}. {"Allow visitors to send status text in presence updates","Permesu al vizitantoj sendi statmesaĝon en ĉeest-sciigoj"}. {"All Users","Ĉiuj Uzantoj"}. {"Announcements","Anoncoj"}. {"A password is required to enter this room","Pasvorto estas bezonata por eniri ĉi tiun babilejon"}. {"April","Aprilo"}. {"August","Aŭgusto"}. {"Backup","Faru Sekurkopion"}. {"Backup Management","Mastrumado de sekurkopioj"}. {"Backup of ~p","Sekurkopio de ~p"}. {"Backup to File at ","Faru sekurkopion je "}. {"Bad format","Malĝusta formo"}. {"Birthday","Naskiĝtago"}. {"CAPTCHA web page","CAPTCHA teksaĵ-paĝo"}. {"Change Password","Ŝanĝu pasvorton"}. {"Change User Password","Ŝanĝu pasvorton de uzanto"}. {"Characters not allowed:","Karaktroj ne permesata:"}. {"Chatroom configuration modified","Agordo de babilejo ŝanĝita"}. {"Chatroom is created","Babilejo kreita"}. {"Chatroom is destroyed","Babilejo neniigita"}. {"Chatroom is started","Babilejo lanĉita"}. {"Chatroom is stopped","Babilejo haltita"}. {"Chatrooms","Babilejoj"}. {"Choose a username and password to register with this server","Elektu uzantnomon kaj pasvorton por registri je ĉi tiu servilo"}. {"Choose modules to stop","Elektu modulojn por fini"}. {"Choose storage type of tables","Elektu konserv-tipon de tabeloj"}. {"Choose whether to approve this entity's subscription.","Elektu ĉu permesi la abonon de ĉi tiu ento"}. {"City","Urbo"}. {"Commands","Ordonoj"}. {"Conference room does not exist","Babilejo ne ekzistas"}. {"Configuration","Agordo"}. {"Configuration of room ~s","Agordo de babilejo ~s"}. {"Connected Resources:","Konektataj risurcoj:"}. {"Connections parameters","Konekto-parametroj"}. {"Country","Lando"}. {"CPU Time:","CPU-tempo"}. {"Database","Datumbazo"}. {"Database Tables at ~p","Datumbaz-tabeloj je ~p"}. {"Database Tables Configuration at ","Agordo de datumbaz-tabeloj je "}. {"December","Decembro"}. {"Default users as participants","Kutime farigu uzantojn kiel partpoprenantoj"}. {"Delete message of the day","Forigu mesaĝo de la tago"}. {"Delete message of the day on all hosts","Forigu mesaĝo de la tago je ĉiu gastigo"}. {"Delete Selected","Forigu elektata(j)n"}. {"Delete User","Forigu Uzanton"}. {"Description:","Priskribo:"}. {"Disc only copy","Nur disk-kopio"}. {"Displayed Groups:","Montrataj grupoj:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ne donu vian pasvorton al iun ajn, eĉ ne al la administrantoj de la Ĵabber-servilo."}. {"Dump Backup to Text File at ","Skribu sekurkopion en plata teksto al "}. {"Dump to Text File","Skribu en plata tekst-dosiero"}. {"Edit Properties","Redaktu atributojn"}. {"Either approve or decline the voice request.","Ĉu aprobu, aŭ malaprobu la voĉ-peton."}. {"ejabberd IRC module","ejabberd IRC-modulo"}. {"ejabberd MUC module","ejabberd MUC-modulo"}. {"ejabberd Multicast service","ejabberd Multicast-servo"}. {"ejabberd Publish-Subscribe module","ejabberd Public-Abonada modulo"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bajtfluo modulo"}. {"ejabberd vCard module","ejabberd vCard-modulo"}. {"ejabberd Web Admin","ejabberd Teksaĵa Administro"}. {"Elements","Eroj"}. {"Email","Retpoŝto"}. {"Enable logging","Ŝaltu protokoladon"}. {"Encoding for server ~b","Enkodigo por servilo ~b"}. {"End User Session","Haltigu Uzant-seancon"}. {"Enter list of {Module, [Options]}","Enmetu liston de {Modulo, [Elektebloj]}"}. {"Enter nickname you want to register","Enmetu kaŝnomon kiun vi volas registri"}. {"Enter path to backup file","Enmetu vojon por sekurkopio"}. {"Enter path to jabberd14 spool dir","Enmetu vojon al jabberd14-uzantdosierujo"}. {"Enter path to jabberd14 spool file","Enmetu vojon al jabberd14-uzantdosiero"}. {"Enter path to text file","Enmetu vojon al plata teksto"}. {"Enter the text you see","Enmetu montrita teksto"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Enmetu uzantnomon kaj enkodigojn kiujn vi volas uzi por konektoj al IRC-serviloj. Elektu 'Sekvonto' por ekhavi pliajn kampojn. Elektu 'Kompletigu' por savi agordojn."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Enmetu uzantnomon,j enkodigojn, pordojn kaj pasvortojn kiujn vi volas uzi por konektoj al IRC-serviloj"}. {"Erlang Jabber Server","Erlang-a Jabber-Servilo"}. {"Error","Eraro"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Ekzemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"sekreto\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.iutestservilo.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Eksportu ĉiuj tabeloj kiel SQL-informmendo al dosierujo:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksportu datumojn de ĉiuj uzantoj en servilo al PIEFXIS dosieroj (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Eksportu datumoj de uzantoj en gastigo al PIEFXIS dosieroj (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Malsukcesis ekstrakti JID-on de via voĉ-pet-aprobo"}. {"Family Name","Lasta Nomo"}. {"February","Februaro"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Kompletigu la formon por serĉi rekonata Jabber-uzanto (Aldonu * je la fino de la kampo por rekoni subĉenon"}. {"Friday","Vendredo"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Plena Nomo"}. {"Get Number of Online Users","Montru nombron de konektataj uzantoj"}. {"Get Number of Registered Users","Montru nombron de registritaj uzantoj"}. {"Get User Last Login Time","Montru tempon de lasta ensaluto"}. {"Get User Password","Montru pasvorton de uzanto"}. {"Get User Statistics","Montru statistikojn de uzanto"}. {"Group ","Grupo "}. {"Groups","Grupoj"}. {"has been banned","estas forbarita"}. {"has been kicked because of an affiliation change","estas forpelita pro aparteneca ŝanĝo"}. {"has been kicked because of a system shutdown","estas forpelita pro sistem-haltigo"}. {"has been kicked because the room has been changed to members-only","estas forpelita ĉar la babilejo fariĝis sole por membroj"}. {"has been kicked","estas forpelita"}. {" has set the subject to: "," ŝanĝis la temon al: "}. {"Host","Gastigo"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se vi ne vidas la CAPTCHA-imagon jene, vizitu la teksaĵ-paĝon."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se vi volas specifi diversajn pordojn, pasvortojn, enkodigojn por IRC-serviloj, kompletigu la jenan liston kun la formo '{\"irc-servilo\", \"enkodigo\", porto, \"pasvorto\"}'. Se ne specifita, ĉi tiu servilo uzas la enkodigo \"~s\", porto ~p, malplena pasvorto."}. {"Import Directory","Importu dosierujo"}. {"Import File","Importu dosieron"}. {"Import user data from jabberd14 spool file:","Importu uzantojn de jabberd14-uzantdosieroj"}. {"Import User from File at ","Importu uzanton de dosiero el "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importu uzanto-datumojn de PIEFXIS dosiero (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importu uzantojn de jabberd14-uzantdosieroj"}. {"Import Users from Dir at ","Importu uzantojn de dosierujo ĉe "}. {"Import Users From jabberd14 Spool Files","Importu uzantojn de jabberd14-uzantdosieroj"}. {"Improper message type","Malĝusta mesaĝo-tipo"}. {"Incorrect password","Nekorekta pasvorto"}. {"IP addresses","IP-adresoj"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC-babilejo (ne aldonu #-prefikson)"}. {"IRC server","IRC-servilo"}. {"IRC settings","IRC agordoj"}. {"IRC Transport","IRC-transportilo"}. {"IRC Username","IRC-kaŝnomo"}. {"IRC username","IRC-uzantnomo"}. {"is now known as","nun nomiĝas"}. {"It is not allowed to send private messages","Ne estas permesata sendi privatajn mesaĝojn"}. {"It is not allowed to send private messages of type \"groupchat\"","Malpermesas sendi mesaĝojn de tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Nur partoprenantoj rajtas sendi privatajn mesaĝojn al la babilejo"}. {"Jabber Account Registration","Ĵabber-konto registrado"}. {"Jabber ID","Jabber ID"}. {"January","Januaro"}. {"Join IRC channel","Eniras IRC-babilejon"}. {"joins the room","eniras la babilejo"}. {"Join the IRC channel here.","Eniru IRC-babilejon jen"}. {"Join the IRC channel in this Jabber ID: ~s","Eniru IRC-babilejon en ĉi Jabber-ID: ~s"}. {"July","Julio"}. {"June","Junio"}. {"Last Activity","Lasta aktiveco"}. {"Last login","Lasta ensaluto"}. {"Last month","Lasta monato"}. {"Last year","Lasta jaro"}. {"leaves the room","eliras la babilejo"}. {"Listened Ports at ","Atentataj pordoj je "}. {"Listened Ports","Atentataj pordoj"}. {"List of modules to start","Listo de moduloj por starti"}. {"List of rooms","Listo de babilejoj"}. {"Low level update script","Bazanivela ĝisdatigo-skripto"}. {"Make participants list public","Farigu partoprento-liston publika"}. {"Make room CAPTCHA protected","Farigu babilejon protektata per CAPTCHA"}. {"Make room members-only","Farigu babilejon sole por membroj"}. {"Make room moderated","Farigu babilejon moderigata"}. {"Make room password protected","Farigu babilejon protektata per pasvorto"}. {"Make room persistent","Farigu babilejon daŭra"}. {"Make room public searchable","Farigu babilejon publike trovebla"}. {"March","Marĉo"}. {"Maximum Number of Occupants","Limigo de nombro de partoprenantoj"}. {"May","Majo"}. {"Membership is required to enter this room","Membreco estas bezonata por eniri ĉi tiun babilejon"}. {"Members:","Membroj:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memoru vian pasvorton, aŭ skribu ĝin sur papero formetata je sekura loko. Je Ĵabber ne ekzistas aŭtomata metodo por reakiri vian pasvorton se vi forgesas ĝin."}. {"Memory","Memoro"}. {"Message body","Teksto de mesaĝo"}. {"Middle Name","Meza Nomo"}. {"Moderator privileges required","Moderantaj rajtoj bezonata"}. {"Modified modules","Ĝisdatigitaj moduloj"}. {"Module","Modulo"}. {"Modules at ~p","Moduloj je ~p"}. {"Modules","Moduloj"}. {"Monday","Lundo"}. {"Multicast","Multicast"}. {"Multi-User Chat","Grupbabilado"}. {"Name:","Nomo:"}. {"Name","Nomo"}. {"Never","Neniam"}. {"New Password:","Nova Pasvorto:"}. {"Nickname","Kaŝnomo"}. {"Nickname Registration at ","Kaŝnomo-registrado je "}. {"Nickname ~s does not exist in the room","Kaŝnomo ~s ne ekzistas en la babilejo"}. {"No body provided for announce message","Neniu teksto donita por anonc-mesaĝo"}. {"No Data","Neniu datumo"}. {"Node not found","Nodo ne trovita"}. {"Node ~p","Nodo ~p"}. {"Nodes","Nodoj"}. {"None","Nenio"}. {"Not Found","Ne trovita"}. {"November","Novembro"}. {"Number of online users","Nombro de konektataj uzantoj"}. {"Number of registered users","Nombro de registritaj uzantoj"}. {"October","Oktobro"}. {"Offline Messages:","Liverontaj mesaĝoj"}. {"Offline Messages","Liverontaj mesaĝoj"}. {"OK","Bone"}. {"Old Password:","Malnova Pasvorto:"}. {"Online","Konektata"}. {"Online Users:","Konektataj uzantoj:"}. {"Online Users","Konektataj Uzantoj"}. {"Only moderators and participants are allowed to change the subject in this room","Nur moderigantoj kaj partoprenantoj rajtas ŝanĝi la temon en ĉi tiu babilejo"}. {"Only moderators are allowed to change the subject in this room","Nur moderigantoj rajtas ŝanĝi la temon en ĉi tiu babilejo"}. {"Only moderators can approve voice requests","Nur moderigantoj povas aprobi voĉ-petojn"}. {"Only occupants are allowed to send messages to the conference","Nur partoprenantoj rajtas sendi mesaĝojn al la babilejo"}. {"Only occupants are allowed to send queries to the conference","Nur partoprenantoj rajtas sendi informmendojn al la babilejoj"}. {"Only service administrators are allowed to send service messages","Nur servo-administrantoj rajtas sendi serv-mesaĝojn"}. {"Options","Elektebloj"}. {"Organization Name","Organiz-nomo"}. {"Organization Unit","Organiz-parto"}. {"Outgoing s2s Connections:","Elirantaj s-al-s-konektoj:"}. {"Outgoing s2s Connections","Elirantaj s-al-s-konektoj"}. {"Owner privileges required","Mastraj rajtoj bezonata"}. {"Packet","Pakaĵo"}. {"Password ~b","Pasvorto ~b"}. {"Password:","Pasvorto:"}. {"Password","Pasvorto"}. {"Password Verification:","Pasvortkontrolo:"}. {"Password Verification","Pasvortkontrolo"}. {"Path to Dir","Vojo al dosierujo"}. {"Path to File","Voje de dosiero"}. {"Pending","Atendanta"}. {"Period: ","Periodo: "}. {"Permanent rooms","Permanentaj babilejoj"}. {"Ping","Sondaĵo"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Rimarku ke ĉi tiuj elektebloj nur sekurkopias la propran Mnesia-datumbazon. Se vi uzas la ODBC-modulon, vi ankaŭ devas sekurkopii tiujn SQL-datumbazoj aparte."}. {"Please, wait for a while before sending new voice request","Bonvolu atendi iomete antaŭ ol sendi plian voĉ-peton"}. {"Pong","Resondaĵo"}. {"Port ~b","Pordo ~b"}. {"Port","Pordo"}. {"private, ","privata, "}. {"Protocol","Protokolo"}. {"Publish-Subscribe","Public-Abonado"}. {"PubSub subscriber request","PubAbo abonpeto"}. {"Queries to the conference members are not allowed in this room","Malpermesas informmendoj al partoprenantoj en ĉi tiu babilejo"}. {"RAM and disc copy","RAM- kaj disk-kopio"}. {"RAM copy","RAM-kopio"}. {"Raw","Kruda"}. {"Really delete message of the day?","Ĉu vere forigi mesaĝon de la tago?"}. {"Recipient is not in the conference room","Ricevanto ne ĉeestas en la babilejo "}. {"Register a Jabber account","Registru Ĵabber-konton"}. {"Registered nicknames","Registritaj uzantnomoj"}. {"Registered Users:","Registritaj uzantoj:"}. {"Registered Users","Registritaj uzantoj"}. {"Register","Registru"}. {"Registration in mod_irc for ","Registraĵo en mod_irc de "}. {"Remote copy","Fora kopio"}. {"Remove All Offline Messages","Forigu ĉiujn liverontajn mesaĝojn"}. {"Remove","Forigu"}. {"Remove User","Forigu uzanton"}. {"Replaced by new connection","Anstataŭigita je nova konekto"}. {"Resources","Risurcoj"}. {"Restart","Restartu"}. {"Restart Service","Restartu Servon"}. {"Restore Backup from File at ","Restaŭrigu de dosiero el "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaŭrigu duuman sekurkopion post sekvonta ejabberd-restarto"}. {"Restore binary backup immediately:","Restaŭrigu duuman sekurkopion tuj:"}. {"Restore plain text backup immediately:","Restaŭrigu sekurkopion el plata tekstdosiero tuj"}. {"Restore","Restaŭru"}. {"Room Configuration","Babilejo-agordo"}. {"Room creation is denied by service policy","Ĉi tiu serv-politiko ne permesas babilejo-kreadon"}. {"Room description","Babilejo-priskribo"}. {"Room Occupants","Nombro de ĉeestantoj"}. {"Room title","Babilejo-nomo"}. {"Roster","Kontaktlisto"}. {"Roster of ","Kontaktlisto de "}. {"Roster size","Kontaktlist-grando"}. {"RPC Call Error","Eraro de RPC-alvoko"}. {"Running Nodes","Funkciantaj Nodoj"}. {"~s access rule configuration","Agordo de atingo-reguloj de ~s"}. {"Saturday","Sabato"}. {"Script check","Skript-kontrolo"}. {"Search Results for ","Serĉ-rezultoj de "}. {"Search users in ","Serĉu uzantojn en "}. {"Send announcement to all online users on all hosts","Sendu anoncon al ĉiu konektata uzanto de ĉiu gastigo"}. {"Send announcement to all online users","Sendu anoncon al ĉiu konektata uzanto"}. {"Send announcement to all users on all hosts","Sendu anoncon al ĉiu uzanto de ĉiu gastigo"}. {"Send announcement to all users","Sendu anoncon al ĉiu uzanto"}. {"September","Septembro"}. {"Server ~b","Servilo ~b"}. {"Server:","Servilo:"}. {"Set message of the day and send to online users","Enmetu mesaĝon de la tago kaj sendu al konektataj uzantoj"}. {"Set message of the day on all hosts and send to online users","Enmetu mesaĝon de la tago je ĉiu gastigo kaj sendu al konektataj uzantoj"}. {"Shared Roster Groups","Komuna Kontaktlist-grupo"}. {"Show Integral Table","Montru integran tabelon"}. {"Show Ordinary Table","Montru ordinaran tabelon"}. {"Shut Down Service","Haltigu Servon"}. {"~s invites you to the room ~s","~s invitas vin al la babilejo ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Kelkaj Ĵabber-klientoj povas memori vian pasvorton je via komputilo. Nur uzu tiun eblon se vi fidas ke via komputilo estas sekura."}. {"~s's Offline Messages Queue","Mesaĝo-atendovico de ~s"}. {"Start Modules at ","Startu modulojn je "}. {"Start Modules","Startu Modulojn"}. {"Start","Startu"}. {"Statistics of ~p","Statistikoj de ~p"}. {"Statistics","Statistikoj"}. {"Stop","Haltigu"}. {"Stop Modules at ","Haltigu modulojn je "}. {"Stop Modules","Haltigu Modulojn"}. {"Stopped Nodes","Neaktivaj Nodoj"}. {"Storage Type","Konserv-tipo"}. {"Store binary backup:","Konservu duuman sekurkopion:"}. {"Store plain text backup:","Skribu sekurkopion en plata tekstdosiero"}. {"Subject","Temo"}. {"Submit","Sendu"}. {"Submitted","Sendita"}. {"Subscription","Abono"}. {"Sunday","Dimanĉo"}. {"That nickname is already in use by another occupant","Tiu kaŝnomo jam estas uzata de alia partoprenanto"}. {"That nickname is registered by another person","Kaŝnomo estas registrita de alia persono"}. {"The CAPTCHA is valid.","La CAPTCHA ĝustas"}. {"The CAPTCHA verification has failed","La CAPTCHA-kontrolado malsukcesis"}. {"the password is","la pasvorto estas"}. {"The password is too weak","La pasvorto estas ne sufiĉe forta"}. {"The password of your Jabber account was successfully changed.","La pasvorto de via Ĵabber-konto estas sukcese ŝanĝata."}. {"There was an error changing the password: ","Estis eraro dum ŝanĝi de la pasvortro:"}. {"There was an error creating the account: ","Estis eraro dum kreado de la konto:"}. {"There was an error deleting the account: ","Estis eraro dum forigado de la konto:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Uskleco ne signifas: macbeth estas la sama ol MacBeth kaj Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Jena paĝo ebligas kreadon de Ĵabber-konto je ĉi-Ĵabber-servilo. Via JID (Ĵabber-IDentigilo) estos ĉi-tiel: uzantnomo@servilo. Bonvolu legu bone la instrukciojn por korekta enmetigo de la kampoj. "}. {"This page allows to unregister a Jabber account in this Jabber server.","Jena pagxo ebligas malregistri Jxabber-konton je ĉi-servilo."}. {"This room is not anonymous","Ĉi tiu babilejo ne estas anonima"}. {"Thursday","Ĵaŭdo"}. {"Time delay","Prokrasto"}. {"Time","Tempo"}. {"To","Ĝis"}. {"Too many CAPTCHA requests","Tro multaj CAPTCHA-petoj"}. {"Too many unacked stanzas","Tro da neagnoskitaj stancoj"}. {"To ~s","Al ~s"}. {"Total rooms","Babilejoj"}. {"Traffic rate limit is exceeded","Trafikrapida limigo superita"}. {"Transactions Aborted:","Transakcioj nuligitaj"}. {"Transactions Committed:","Transakcioj enmetitaj"}. {"Transactions Logged:","Transakcioj protokolitaj"}. {"Transactions Restarted:","Transakcioj restartitaj"}. {"Tuesday","Mardo"}. {"Unable to generate a CAPTCHA","Ne eblis krei CAPTCHA"}. {"Unauthorized","Nepermesita"}. {"Unregister a Jabber account","Malregistru Ĵabber-konton"}. {"Unregister","Malregistru"}. {"Update","Ĝisdatigu"}. {"Update message of the day (don't send)","Ŝanĝu mesaĝon de la tago (ne sendu)"}. {"Update message of the day on all hosts (don't send)","Ŝanĝu mesaĝon de la tago je ĉiu gastigo (ne sendu)"}. {"Update ~p","Ĝisdatigu ~p-n"}. {"Update plan","Ĝisdatigo-plano"}. {"Update script","Ĝisdatigo-skripto"}. {"Uptime:","Daŭro de funkciado"}. {"Use of STARTTLS required","Uzo de STARTTLS bezonata"}. {"User Management","Uzanto-administrado"}. {"Username:","Uzantnomo"}. {"Users are not allowed to register accounts so quickly","Ne estas permesata al uzantoj registri tiel rapide"}. {"Users Last Activity","Lasta aktiveco de uzanto"}. {"Users","Uzantoj"}. {"User ~s","Uzanto ~s"}. {"User","Uzanto"}. {"Validate","Validigu"}. {"vCard User Search","Serĉado de vizitkartoj"}. {"Virtual Hosts","Virtual-gastigoj"}. {"Visitors are not allowed to change their nicknames in this room","Ne estas permesata al vizitantoj ŝanĝi siajn kaŝnomojn en ĉi tiu ĉambro"}. {"Visitors are not allowed to send messages to all occupants","Vizitantoj ne rajtas sendi mesaĝojn al ĉiuj partoprenantoj"}. {"Voice requests are disabled in this conference","Voĉ-petoj estas malebligita en jena babilejo"}. {"Voice request","Voĉ-peto"}. {"Wednesday","Merkredo"}. {"You can later change your password using a Jabber client.","Poste vi povas ŝanĝi vian pasvorton per Ĵabber-kliento."}. {"You have been banned from this room","Vi estas malpermesata en ĉi tiu babilejo"}. {"You must fill in field \"Nickname\" in the form","Vi devas kompletigi la \"Kaŝnomo\" kampon"}. {"You need a client that supports x:data and CAPTCHA to register","Vi bezonas klienton subtenante x:data-funkcio kaj CAPTCHA por registri kaŝnomon"}. {"You need a client that supports x:data to register the nickname","Vi bezonas klienton subtenante x:data-funkcio por registri kaŝnomon"}. {"You need an x:data capable client to configure mod_irc settings","Vi bezonas klienton kun x:data-funkcio por agordi mod_irc"}. {"You need an x:data capable client to search","Vi bezonas klienton kun x:data-funkcio por serĉado"}. {"Your active privacy list has denied the routing of this stanza.","Via aktiva privatec-listo malpermesas enkursigi ĉi-tiun pakaĵon"}. {"Your contact offline message queue is full. The message has been discarded.","Mesaĝo-atendovico de la senkonekta kontakto estas plena. La mesaĝo estas forĵetita"}. {"Your Jabber account was successfully created.","Via Ĵabber-konto estis sukcese kreata."}. {"Your Jabber account was successfully deleted.","Via Ĵabber-konto estas sukcese forigita."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Viaj mesaĝoj al ~s estas blokata. Por malbloki ilin, iru al ~s"}. ejabberd-18.01/priv/msgs/it.msg0000644000232200023220000006071213225664356016726 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Configurazione dell'accesso"}. {"Access Control List Configuration","Configurazione dei diritti di accesso (ACL)"}. {"Access control lists","Diritti di accesso (ACL)"}. {"Access Control Lists","Diritti di accesso (ACL)"}. {"Access denied by service policy","Accesso impedito dalle politiche del servizio"}. {"Access rules","Regole di accesso"}. {"Access Rules","Regole di accesso"}. {"Action on user","Azione sull'utente"}. {"Add Jabber ID","Aggiungere un Jabber ID (Jabber ID)"}. {"Add New","Aggiungere nuovo"}. {"Add User","Aggiungere un utente"}. {"Administration","Amministrazione"}. {"Administration of ","Amministrazione di "}. {"Administrator privileges required","Necessari i privilegi di amministratore"}. {"All activity","Tutta l'attività"}. {"Allow users to change the subject","Consentire agli utenti di cambiare l'oggetto"}. {"Allow users to query other users","Consentire agli utenti query verso altri utenti"}. {"Allow users to send invites","Consentire agli utenti l'invio di inviti"}. {"Allow users to send private messages","Consentire agli utenti l'invio di messaggi privati"}. {"Allow visitors to change nickname","Consentire ai visitatori di cambiare il nickname"}. {"Allow visitors to send private messages to","Consentire agli ospiti l'invio di messaggi privati a"}. {"Allow visitors to send status text in presence updates","Consentire ai visitatori l'invio di testo sullo stato in aggiornamenti sulla presenza"}. {"All Users","Tutti gli utenti"}. {"Announcements","Annunci"}. {"A password is required to enter this room","Per entrare in questa stanza è prevista una password"}. {"April","Aprile"}. {"August","Agosto"}. {"Backup Management","Gestione dei salvataggi"}. {"Backup","Salvare"}. {"Backup to File at ","Salvataggio sul file "}. {"Bad format","Formato non valido"}. {"Birthday","Compleanno"}. {"CAPTCHA web page","Pagina web CAPTCHA"}. {"Change Password","Modificare la password"}. {"Change User Password","Cambiare la password dell'utente"}. {"Characters not allowed:","Caratteri non consentiti:"}. {"Chatroom configuration modified","Configurazione della stanza modificata"}. {"Chatroom is created","La stanza è creata"}. {"Chatroom is destroyed","La stanza è eliminata"}. {"Chatroom is started","La stanza è avviata"}. {"Chatroom is stopped","La stanza è arrestata"}. {"Chatrooms","Stanze"}. {"Choose a username and password to register with this server","Scegliere un nome utente e una password per la registrazione con questo server"}. {"Choose modules to stop","Selezionare i moduli da arrestare"}. {"Choose storage type of tables","Selezionare una modalità di conservazione delle tabelle"}. {"Choose whether to approve this entity's subscription.","Scegliere se approvare l'iscrizione per questa entità"}. {"City","Città"}. {"Commands","Comandi"}. {"Conference room does not exist","La stanza per conferenze non esiste"}. {"Configuration","Configurazione"}. {"Configuration of room ~s","Configurazione per la stanza ~s"}. {"Connected Resources:","Risorse connesse:"}. {"Connections parameters","Parametri delle connessioni"}. {"Country","Paese"}. {"CPU Time:","Tempo CPU:"}. {"Database","Database"}. {"Database Tables Configuration at ","Configurazione delle tabelle del database su "}. {"December","Dicembre"}. {"Default users as participants","Definire per default gli utenti come partecipanti"}. {"Delete message of the day","Eliminare il messaggio del giorno (MOTD)"}. {"Delete message of the day on all hosts","Eliminare il messaggio del giorno (MOTD) su tutti gli host"}. {"Delete Selected","Eliminare gli elementi selezionati"}. {"Delete User","Eliminare l'utente"}. {"Description:","Descrizione:"}. {"Disc only copy","Copia su disco soltanto"}. {"Displayed Groups:","Gruppi visualizzati:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Non comunicare la tua password a nessuno, neppure agli amministratori del server Jabber."}. {"Dump Backup to Text File at ","Trascrivere il salvataggio sul file di testo "}. {"Dump to Text File","Trascrivere su file di testo"}. {"Edit Properties","Modificare le proprietà"}. {"Either approve or decline the voice request.","Approva oppure respingi la richiesta di parola."}. {"ejabberd IRC module","Modulo IRC per ejabberd"}. {"ejabberd MUC module","Modulo MUC per ejabberd"}. {"ejabberd Publish-Subscribe module","Modulo Pubblicazione/Iscrizione (PubSub) per ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Modulo SOCKS5 Bytestreams per ejabberd"}. {"ejabberd vCard module","Modulo vCard per ejabberd"}. {"ejabberd Web Admin","Amministrazione web ejabberd"}. {"Elements","Elementi"}. {"Email","E-mail"}. {"Enable logging","Abilitare i log"}. {"Encoding for server ~b","Codifica per il server ~b"}. {"End User Session","Terminare la sessione dell'utente"}. {"Enter list of {Module, [Options]}","Immettere un elenco di {Modulo, [Opzioni]}"}. {"Enter nickname you want to register","Immettere il nickname che si vuole registrare"}. {"Enter path to backup file","Immettere il percorso del file di salvataggio"}. {"Enter path to jabberd14 spool dir","Immettere il percorso della directory di spool di jabberd14"}. {"Enter path to jabberd14 spool file","Immettere il percorso del file di spool di jabberd14"}. {"Enter path to text file","Immettere il percorso del file di testo"}. {"Enter the text you see","Immettere il testo visibile"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Immettere il nome utente e le codifiche che si desidera utilizzare per la connessione ai server IRC. Premere \"Avanti\" per vedere i successivi campi da compilare. Premere \"Fatto\" per salvare le impostazioni."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Immettere il nome utente, le codifiche, le porte e le password che si desidera utilizzare per la connessione ai server IRC"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Errore"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Esempio: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"segreto\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.serverdiprova.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Esportare i dati di tutti gli utenti nel server in file PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Esportare i dati degli utenti di un host in file PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Impossibile estrarre il JID dall'approvazione della richiesta di parola"}. {"Family Name","Cognome"}. {"February","Febbraio"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Riempire il modulo per la ricerca di utenti Jabber corrispondenti ai criteri (Aggiungere * alla fine del campo per la ricerca di una sottostringa"}. {"Friday","Venerdì"}. {"From","Da"}. {"From ~s","Da ~s"}. {"Full Name","Nome completo"}. {"Get Number of Online Users","Ottenere il numero di utenti online"}. {"Get Number of Registered Users","Ottenere il numero di utenti registrati"}. {"Get User Last Login Time","Ottenere la data di ultimo accesso dell'utente"}. {"Get User Password","Ottenere la password dell'utente"}. {"Get User Statistics","Ottenere le statistiche dell'utente"}. {"Group ","Gruppo "}. {"Groups","Gruppi"}. {"has been banned","è stata/o bandita/o"}. {"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}. {"has been kicked because of a system shutdown","è stato espulso a causa dello spegnimento del sistema"}. {"has been kicked because the room has been changed to members-only","è stato espulso per la limitazione della stanza ai soli membri"}. {"has been kicked","è stata/o espulsa/o"}. {" has set the subject to: "," ha modificato l'oggetto in: "}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se qui non vedi l'immagine CAPTCHA, visita la pagina web."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se si vogliono specificare differenti porte, password, codifiche per i server IRC, si riempia questo elenco con valori nel formato '{\"server IRC\", \"codifica\", porta, \"password\"}'. Per default questo servizio utilizza la codifica \"~s\", la porta ~p, la password vuota."}. {"Import Directory","Importare una directory"}. {"Import File","Importare un file"}. {"Import user data from jabberd14 spool file:","Importare i dati utente da file di spool di jabberd14:"}. {"Import User from File at ","Importare un utente dal file "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importare i dati utenti da un file PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importare i dati utenti da directory di spool di jabberd14:"}. {"Import Users from Dir at ","Importare utenti dalla directory "}. {"Import Users From jabberd14 Spool Files","Importare utenti da file di spool di jabberd14"}. {"Improper message type","Tipo di messaggio non corretto"}. {"Incorrect password","Password non esatta"}. {"IP addresses","Indirizzi IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canale IRC (senza il # iniziale)"}. {"IRC server","Server IRC"}. {"IRC settings","Impostazioni IRC"}. {"IRC Transport","Transport IRC"}. {"IRC username","Nome utente IRC"}. {"IRC Username","Nome utente IRC"}. {"is now known as","è ora conosciuta/o come"}. {"It is not allowed to send private messages","Non è consentito l'invio di messaggi privati"}. {"It is not allowed to send private messages of type \"groupchat\"","Non è consentito l'invio di messaggi privati di tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Non è consentito l'invio di messaggi privati alla conferenza"}. {"Jabber Account Registration","Registrazione account Jabber"}. {"Jabber ID","Jabber ID (Jabber ID)"}. {"January","Gennaio"}. {"Join IRC channel","Entra nel canale IRC"}. {"joins the room","entra nella stanza"}. {"Join the IRC channel here.","Entra nel canale IRC qui."}. {"Join the IRC channel in this Jabber ID: ~s","Entra nel canale IRC in questo ID Jabber: ~s"}. {"July","Luglio"}. {"June","Giugno"}. {"Last Activity","Ultima attività"}. {"Last login","Ultimo accesso"}. {"Last month","Ultimo mese"}. {"Last year","Ultimo anno"}. {"leaves the room","esce dalla stanza"}. {"Listened Ports at ","Porte in ascolto su "}. {"Listened Ports","Porte in ascolto"}. {"List of modules to start","Elenco dei moduli da avviare"}. {"Low level update script","Script di aggiornamento di basso livello"}. {"Make participants list public","Rendere pubblica la lista dei partecipanti"}. {"Make room CAPTCHA protected","Rendere la stanza protetta da CAPTCHA"}. {"Make room members-only","Rendere la stanza riservata ai membri"}. {"Make room moderated","Rendere la stanza moderata"}. {"Make room password protected","Rendere la stanza protetta da password"}. {"Make room persistent","Rendere la stanza persistente"}. {"Make room public searchable","Rendere la sala visibile al pubblico"}. {"March","Marzo"}. {"Maximum Number of Occupants","Numero massimo di occupanti"}. {"May","Maggio"}. {"Membership is required to enter this room","Per entrare in questa stanza è necessario essere membro"}. {"Members:","Membri:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memorizza la password, o scrivila su un foglio di carta da conservare in un luogo sicuro. Jabber non prevede una modalità automatica per il recupero di una password dimenticata."}. {"Memory","Memoria"}. {"Message body","Corpo del messaggio"}. {"Middle Name","Altro nome"}. {"Moderator privileges required","Necessari i privilegi di moderatore"}. {"Modified modules","Moduli modificati"}. {"Module","Modulo"}. {"Modules","Moduli"}. {"Monday","Lunedì"}. {"Name:","Nome:"}. {"Name","Nome"}. {"Never","Mai"}. {"New Password:","Nuova password:"}. {"Nickname","Nickname"}. {"Nickname Registration at ","Registrazione di un nickname su "}. {"Nickname ~s does not exist in the room","Il nickname ~s non esiste nella stanza"}. {"No body provided for announce message","Nessun corpo fornito per il messaggio di annuncio"}. {"No Data","Nessuna informazione"}. {"Node not found","Nodo non trovato"}. {"Nodes","Nodi"}. {"None","Nessuno"}. {"Not Found","Non trovato"}. {"November","Novembre"}. {"Number of online users","Numero di utenti online"}. {"Number of registered users","Numero di utenti registrati"}. {"October","Ottobre"}. {"Offline Messages:","Messaggi offline:"}. {"Offline Messages","Messaggi offline"}. {"OK","OK"}. {"Old Password:","Vecchia password:"}. {"Online","Online"}. {"Online Users:","Utenti connessi:"}. {"Online Users","Utenti online"}. {"Only moderators and participants are allowed to change the subject in this room","La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori e ai partecipanti"}. {"Only moderators are allowed to change the subject in this room","La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori"}. {"Only moderators can approve voice requests","Soltanto i moderatori possono approvare richieste di parola"}. {"Only occupants are allowed to send messages to the conference","L'invio di messaggi alla conferenza è consentito soltanto ai presenti"}. {"Only occupants are allowed to send queries to the conference","L'invio di query alla conferenza è consentito ai soli presenti"}. {"Only service administrators are allowed to send service messages","L'invio di messaggi di servizio è consentito solamente agli amministratori del servizio"}. {"Options","Opzioni"}. {"Organization Name","Nome dell'organizzazione"}. {"Organization Unit","Unità dell'organizzazione"}. {"Outgoing s2s Connections:","Connessioni s2s in uscita:"}. {"Outgoing s2s Connections","Connessioni s2s in uscita"}. {"Owner privileges required","Necessari i privilegi di proprietario"}. {"Packet","Pacchetto"}. {"Password ~b","Password ~b"}. {"Password:","Password:"}. {"Password","Password"}. {"Password Verification:","Verifica della password:"}. {"Password Verification","Verifica della password"}. {"Path to Dir","Percorso della directory"}. {"Path to File","Percorso del file"}. {"Pending","Pendente"}. {"Period: ","Periodo:"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","N.B.: Queste opzioni comportano il salvataggio solamente del database interno Mnesia. Se si sta utilizzando il modulo ODBC, è necessario salvare anche il proprio database SQL separatamente."}. {"Please, wait for a while before sending new voice request","Attendi qualche istante prima di inviare una nuova richiesta di parola"}. {"Pong","Pong"}. {"Port ~b","Porta ~b"}. {"Port","Porta"}. {"private, ","privato, "}. {"Protocol","Protocollo"}. {"Publish-Subscribe","Pubblicazione-Iscrizione"}. {"PubSub subscriber request","Richiesta di iscrizione per PubSub"}. {"Queries to the conference members are not allowed in this room","In questa stanza non sono consentite query ai membri della conferenza"}. {"RAM and disc copy","Copia in memoria (RAM) e su disco"}. {"RAM copy","Copia in memoria (RAM)"}. {"Raw","Grezzo"}. {"Really delete message of the day?","Si conferma l'eliminazione del messaggio del giorno (MOTD)?"}. {"Recipient is not in the conference room","Il destinatario non è nella stanza per conferenze"}. {"Register a Jabber account","Registra un account Jabber"}. {"Registered Users:","Utenti registrati:"}. {"Registered Users","Utenti registrati"}. {"Register","Registra"}. {"Registration in mod_irc for ","Registrazione in mod_irc per "}. {"Remote copy","Copia remota"}. {"Remove All Offline Messages","Eliminare tutti i messaggi offline"}. {"Remove","Eliminare"}. {"Remove User","Eliminare l'utente"}. {"Replaced by new connection","Sostituito da una nuova connessione"}. {"Resources","Risorse"}. {"Restart","Riavviare"}. {"Restart Service","Riavviare il servizio"}. {"Restore Backup from File at ","Recuperare il salvataggio dal file "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Recuperare un salvataggio binario dopo il prossimo riavvio di ejabberd (necessita di meno memoria):"}. {"Restore binary backup immediately:","Recuperare un salvataggio binario adesso:"}. {"Restore plain text backup immediately:","Recuperare un salvataggio come semplice testo adesso:"}. {"Restore","Recuperare"}. {"Room Configuration","Configurazione della stanza"}. {"Room creation is denied by service policy","La creazione di stanze è impedita dalle politiche del servizio"}. {"Room description","Descrizione della stanza"}. {"Room Occupants","Presenti nella stanza"}. {"Room title","Titolo della stanza"}. {"Roster","Lista dei contatti"}. {"Roster of ","Lista dei contatti di "}. {"Roster size","Dimensione della lista dei contatti"}. {"RPC Call Error","Errore di chiamata RPC"}. {"Running Nodes","Nodi attivi"}. {"~s access rule configuration","Configurazione delle regole di accesso per ~s"}. {"Saturday","Sabato"}. {"Script check","Verifica dello script"}. {"Search Results for ","Risultati della ricerca per "}. {"Search users in ","Cercare utenti in "}. {"Send announcement to all online users","Inviare l'annuncio a tutti gli utenti online"}. {"Send announcement to all online users on all hosts","Inviare l'annuncio a tutti gli utenti online su tutti gli host"}. {"Send announcement to all users","Inviare l'annuncio a tutti gli utenti"}. {"Send announcement to all users on all hosts","Inviare l'annuncio a tutti gli utenti su tutti gli host"}. {"September","Settembre"}. {"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Impostare il messaggio del giorno (MOTD) ed inviarlo agli utenti online"}. {"Set message of the day on all hosts and send to online users","Impostare il messaggio del giorno (MOTD) su tutti gli host e inviarlo agli utenti online"}. {"Shared Roster Groups","Gruppi di liste di contatti comuni"}. {"Show Integral Table","Mostrare la tabella integrale"}. {"Show Ordinary Table","Mostrare la tabella normale"}. {"Shut Down Service","Terminare il servizio"}. {"~s invites you to the room ~s","~s ti invita nella stanza ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alcuni client Jabber possono conservare la password nel tuo computer. Utilizza tale funzione soltanto se ritieni che il tuo computer sia sicuro."}. {"~s's Offline Messages Queue","Coda di ~s messaggi offline"}. {"Start","Avviare"}. {"Start Modules at ","Avviare moduli su "}. {"Start Modules","Avviare moduli"}. {"Statistics of ~p","Statistiche di ~p"}. {"Statistics","Statistiche"}. {"Stop","Arrestare"}. {"Stop Modules","Arrestare moduli"}. {"Stop Modules at ","Arrestare moduli su "}. {"Stopped Nodes","Nodi arrestati"}. {"Storage Type","Tipo di conservazione"}. {"Store binary backup:","Conservare un salvataggio binario:"}. {"Store plain text backup:","Conservare un salvataggio come semplice testo:"}. {"Subject","Oggetto"}. {"Submit","Inviare"}. {"Submitted","Inviato"}. {"Subscription","Iscrizione"}. {"Sunday","Domenica"}. {"That nickname is already in use by another occupant","Il nickname è già in uso all'interno della conferenza"}. {"That nickname is registered by another person","Questo nickname è registrato da un'altra persona"}. {"The CAPTCHA is valid.","Il CAPTCHA è valido."}. {"The CAPTCHA verification has failed","La verifica del CAPTCHA ha avuto esito negativo"}. {"the password is","la password è"}. {"The password is too weak","La password è troppo debole"}. {"The password of your Jabber account was successfully changed.","Il cambio di password del tuo account Jabber è andato a buon fine."}. {"There was an error changing the password: ","Si è verificato un errore nel cambio di password: "}. {"There was an error creating the account: ","Si è verificato un errore nella creazione dell'account: "}. {"There was an error deleting the account: ","Si è verificato un errore nella cancellazione dell'account: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Non fa differenza fra minuscolo e maiuscolo: macbeth, MacBeth e Macbeth si equivalgono."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Questa pagina consente di creare un account Jabber in questo server Jabber. Il tuo JID (Jabber IDentifier) avrà la forma: nome_utente@server. Leggi attentamente le istruzioni per compilare i campi correttamente."}. {"This page allows to unregister a Jabber account in this Jabber server.","Questa pagina consente di eliminare un account Jabber da questo server Jabber."}. {"This room is not anonymous","Questa stanza non è anonima"}. {"Thursday","Giovedì"}. {"Time delay","Ritardo"}. {"Time","Ora"}. {"To","A"}. {"Too many CAPTCHA requests","Troppe richieste CAPTCHA"}. {"To ~s","A ~s"}. {"Traffic rate limit is exceeded","Limite di traffico superato"}. {"Transactions Aborted:","Transazioni abortite:"}. {"Transactions Committed:","Transazioni avvenute:"}. {"Transactions Logged:","Transazioni con log:"}. {"Transactions Restarted:","Transazioni riavviate:"}. {"Tuesday","Martedì"}. {"Unable to generate a CAPTCHA","Impossibile generare un CAPTCHA"}. {"Unauthorized","Non autorizzato"}. {"Unregister a Jabber account","Elimina un account Jabber"}. {"Unregister","Elimina"}. {"Update","Aggiornare"}. {"Update message of the day (don't send)","Aggiornare il messaggio del giorno (MOTD) (non inviarlo)"}. {"Update message of the day on all hosts (don't send)","Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)"}. {"Update plan","Piano di aggiornamento"}. {"Update script","Script di aggiornamento"}. {"Uptime:","Tempo dall'avvio:"}. {"Use of STARTTLS required","Utilizzo di STARTTLS obbligatorio"}. {"User Management","Gestione degli utenti"}. {"Username:","Nome utente:"}. {"Users are not allowed to register accounts so quickly","Non è consentito agli utenti registrare account così rapidamente"}. {"Users Last Activity","Ultima attività degli utenti"}. {"Users","Utenti"}. {"User","Utente"}. {"Validate","Validare"}. {"vCard User Search","Ricerca di utenti per vCard"}. {"Virtual Hosts","Host Virtuali"}. {"Visitors are not allowed to change their nicknames in this room","Non è consentito ai visitatori cambiare il nickname in questa stanza"}. {"Visitors are not allowed to send messages to all occupants","Non è consentito ai visitatori l'invio di messaggi a tutti i presenti"}. {"Voice request","Richiesta di parola"}. {"Voice requests are disabled in this conference","In questa conferenza le richieste di parola sono escluse"}. {"Wednesday","Mercoledì"}. {"You can later change your password using a Jabber client.","Potrai in seguito cambiare la password utilizzando un client Jabber."}. {"You have been banned from this room","Sei stata/o bandita/o da questa stanza"}. {"You must fill in field \"Nickname\" in the form","Si deve riempire il campo \"Nickname\" nel modulo"}. {"You need a client that supports x:data and CAPTCHA to register","La registrazione richiede un client che supporti x:data e CAPTCHA"}. {"You need a client that supports x:data to register the nickname","Per registrare il nickname è necessario un client che supporti x:data"}. {"You need an x:data capable client to configure mod_irc settings","Per la configurazione del modulo IRC è necessario un client che supporti x:data"}. {"You need an x:data capable client to search","Per effettuare ricerche è necessario un client che supporti x:data"}. {"Your active privacy list has denied the routing of this stanza.","In base alla tua attuale lista privacy questa stanza è stata esclusa dalla navigazione."}. {"Your contact offline message queue is full. The message has been discarded.","La coda dei messaggi offline del contatto è piena. Il messaggio è stato scartato"}. {"Your Jabber account was successfully created.","La creazione del tuo account Jabber è andata a buon fine."}. {"Your Jabber account was successfully deleted.","La cancellazione del tuo account Jabber è andata a buon fine."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s"}. ejabberd-18.01/priv/msgs/fr.po0000644000232200023220000020357713225664356016561 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Christophe Romain \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: French (française)\n" "X-Additional-Translator: Mickaël Rémond\n" "X-Additional-Translator: Vincent Ricard\n" "X-Additional-Translator: Nicolas Vérité\n" "X-Generator: Poedit 2.0.4\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " a changé le sujet: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Un mot de passe est nécessaire pour accéder à ce salon" #: ejabberd_oauth:448 msgid "Accept" msgstr "Accepter" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configuration d'accès" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configuration des droits (ACL)" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Droits (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Règles d'accès" #: mod_configure:1095 msgid "Access control lists" msgstr "Droits (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "L'accès au service est refusé" #: mod_configure:1113 msgid "Access rules" msgstr "Règles d'accès" #: mod_configure:1772 msgid "Action on user" msgstr "Action sur l'utilisateur" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Ajouter un Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Ajouter" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 mod_configure:1118 msgid "Add User" msgstr "Ajouter un utilisateur" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administration" #: mod_configure:1767 msgid "Administration of " msgstr "Administration de " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Les droits d'administrateur sont nécessaires" #: mod_configure:507 msgid "All Users" msgstr "Tous les utilisateurs" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Toute activité" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Autoriser les utilisateurs à changer le sujet" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Autoriser les utilisateurs à envoyer des requêtes aux autres utilisateurs" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Autoriser les utilisateurs à envoyer des invitations" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Autoriser les utilisateurs à envoyer des messages privés" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Autoriser les visiteurs à changer de pseudo" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Autoriser les visiteurs à envoyer des messages privés" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Autoriser les visiteurs à envoyer un message d'état avec leur présence" #: mod_announce:611 msgid "Announcements" msgstr "Annonces" #: mod_muc_log:476 msgid "April" msgstr "Avril" #: mod_muc_log:480 msgid "August" msgstr "Août" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "La creation implicite de nœud n'est pas disponible" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Sauvegarde" #: mod_configure:584 msgid "Backup Management" msgstr "Gestion des sauvegardes" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Sauvegarde de ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Sauvegarde fichier sur " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Mauvais format" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 mod_vcard_mnesia:119 #: mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Date d'anniversaire" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Le nom d'utilisateur et sa ressource sont nécessaires" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Le flux SOCKS5 est déjà activé" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Page web de CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Temps CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "La liste active ne peut être supprimée" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "La liste par défaut ne peut être supprimée" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Modifier le mot de passe" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Changer le mot de passe de l'utilisateur" #: mod_register:295 msgid "Changing password is not allowed" msgstr "La modification du mot de passe n'est pas autorisée" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "La modification role/affiliation n'est pas autorisée" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Caractères non-autorisés:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Configuration du salon modifiée" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Le salon de discussion est créé" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Le salon de discussion est détruit" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Le salon de discussion a démarré" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Le salon de discussion est stoppé" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Salons de discussion" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Choisissez un nom d'utilisateur et un mot de passe pour ce serveur" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Sélectionnez les modules à arrêter" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Choisissez un type de stockage pour les tables" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Accepter cet abonnement ?" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 mod_vcard_mnesia:121 #: mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Ville" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Commandes" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Le salon de discussion n'existe pas" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configuration" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Configuration pour le salon ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Ressources connectées:" #: mod_irc:526 msgid "Connections parameters" msgstr "Paramètres de connexion" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 mod_vcard_mnesia:120 #: mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Pays" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Base de données" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Configuration des tables de base de données sur " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Tables de base de données sur ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 mod_privacy:347 #: mod_privacy:364 mod_private:103 mod_private:110 mod_proxy65_service:231 #: mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 mod_pubsub:3598 mod_pubsub:3604 #: mod_pubsub:3607 mod_vcard:226 node_flat_sql:801 nodetree_tree_sql:128 #: nodetree_tree_sql:142 nodetree_tree_sql:257 msgid "Database failure" msgstr "Echec sur la base de données" #: mod_muc_log:484 msgid "December" msgstr "Décembre" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Les utilisateurs sont participant par défaut" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Suppression des éléments sélectionnés" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Supprimer l'utilisateur" #: mod_announce:629 msgid "Delete message of the day" msgstr "Supprimer le message du jour" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Supprimer le message du jour sur tous les domaines" #: mod_shared_roster:900 msgid "Description:" msgstr "Description:" #: mod_configure:889 msgid "Disc only copy" msgstr "Copie sur disque uniquement" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Groupes affichés:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the Jabber " "server." msgstr "" "Ne révélez votre mot de passe à personne, pas même l'administrateur de ce " "serveur." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Enregistrer la sauvegarde dans un fichier texte sur " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Sauvegarder dans un fichier texte" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Les groupes ne peuvent être dupliqués (rfc6121)" #: mod_configure:1776 msgid "Edit Properties" msgstr "Modifier les propriétés" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Accepter ou refuser la demande de voix" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Éléments" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 mod_vcard_mnesia:122 #: mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 msgid "Empty password" msgstr "Le mot de passe est vide" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Activer l'archivage" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "L'activation push ne peut se faire sans l'attribut 'node'" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Codage pour le serveur ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Terminer la session de l'utilisateur" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Entrez une liste de {Module, [Options]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Entrez le pseudo que vous souhaitez enregistrer" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Entrez le chemin vers le fichier de sauvegarde" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Entrez le chemin vers le répertoire spool de Jabberd 1.4" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Entrez le chemin vers le fichier spool de Jabberd 1.4" #: mod_configure:974 msgid "Enter path to text file" msgstr "Entrez le chemin vers le fichier texte" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Tapez le texte que vous voyez" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save settings." msgstr "" "Entrez le nom d'utilisateur et les encodages que vous souhaitez utiliser pour " "vous connecter aux serveurs IRC. Appuyez sur 'Suivant' pour pour avoir d'autres " "champs à remplir. Appuyez sur 'Terminer' pour sauver les paramètres." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for connecting " "to IRC servers" msgstr "" "Entrez le nom d'utilisateur, les encodages, les ports et mots de passe que vous " "souhaitez utiliser pour vous connecter aux serveurs IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Serveur Jabber Erlang" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Erreur" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net" "\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net" "\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Exporter toutes les tables vers un fichier SQL:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exporter les données de tous les utilisateurs du serveur vers un fichier " "PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exporter les données utilisateurs d'un hôte vers un fichier PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Erreur de composant externe" #: mod_delegation:283 msgid "External component timeout" msgstr "Dépassement de delai du composant externe" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Echec d'activation du flux SOCKS5" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Echec d'extraction du JID dans la requête de voix" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Echec d'association d'espace de nom vers un composant externe" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Echec de lecture de la réponse HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Echec de lecture du 'chanserv'" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Echec de traitement de l'option '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 mod_vcard_mnesia:117 #: mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Nom de famille" #: mod_muc_log:474 msgid "February" msgstr "Février" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "Taille de fichier suppérieur à ~w octets" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Remplissez le formulaire pour recherche un utilisateur Jabber (Ajouter * à la " "fin du champ pour chercher n'importe quelle fin de chaîne" #: mod_muc_log:467 msgid "Friday" msgstr "Vendredi" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 mod_vcard_mnesia:114 #: mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nom complet" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Récupérer le nombre d'utilisateurs en ligne" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Récupérer le nombre d'utilisateurs enregistrés" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Récupérer la dernière date de connexion de l'utilisateur" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Récupérer le mot de passe de l'utilisateur" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Récupérer les statistiques de l'utilisateur" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Nom" #: mod_shared_roster:923 msgid "Group " msgstr "Groupe " #: mod_roster:916 msgid "Groups" msgstr "Groupes" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Serveur" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Serveur inconnu" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Adresses IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Passerelle IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Nom d'utilisateur IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canal IRC (ne pas insérer le premier caractère #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "Connection IRC non trouvé" #: mod_irc:673 msgid "IRC server" msgstr "Serveur IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Configuration IRC" #: mod_irc:766 msgid "IRC username" msgstr "Nom d'utilisateur IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "SI vous ne voyez pas l'image CAPTCHA ici, visitez la page web." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC servers, " "fill this list with values in format '{\"irc server\", \"encoding\", port, " "\"password\"}'. By default this service use \"~s\" encoding, port ~p, empty " "password." msgstr "" "Si vous voulez préciser différents ports, mots de passe, et encodages pour les " "serveurs IRC, remplissez cette liste avec des valeurs dans le format " "'{\"serveur irc\", \"encodage\", port, \"mot de passe\"}'. Par défaut ce " "service utilise l'encodage \"~s\", port ~p, mot de passe vide." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importer un répertoire" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importer un fichier" #: mod_configure:982 msgid "Import User from File at " msgstr "Importer un utilisateur depuis le fichier sur " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importer des utilisateurs depuis un fichier spool Jabberd 1.4" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importer des utilisateurs depuis le répertoire sur " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importer des utilisateurs depuis un fichier spool Jabberd 1.4:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "" "Importer les données utilisateurs à partir d'un fichier PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importer des utilisateurs depuis un fichier spool Jabberd 1.4:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Attribut 'from' incorrect" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Attribut 'to' incorrect" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Le domaine de l'attribut 'from' est incorrect" #: mod_muc_room:260 msgid "Improper message type" msgstr "Mauvais type de message" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Connexions s2s entrantes:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "Entrée CAPTCHA incorrecte" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Formulaire incorrect" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Mot de passe incorrect" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Valeur incorrecte dans le formulaire" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Valeur de l'attribut 'action' incorrecte" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Valeur de l'attribut 'action' incorrecte dans le formulaire" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Valeur de l'attribut 'path' incorrecte dans le formulaire" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Valeur de l'attribut 'type' incorrecte" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Droits insuffisants" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "L'attribut 'from' du message transféré est incorrect" #: mod_privilege:300 msgid "Invalid element" msgstr "Element invalide" #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "Les invitations ne sont pas autorisées dans ce salon" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) has " "sent an error message (~s) and got kicked from the room" msgstr "" "L'envoyer de messages d'erreur au salon n'est pas autorisé. Le participant (~s) " "à envoyé un message d'erreur (~s) et à été expulsé du salon" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "L'envoi de messages privés n'est pas autorisé" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Il n'est pas permis d'envoyer des messages privés de type \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Il n'est pas permis d'envoyer des messages \"normaux\" au salon" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Enregistrement du Compte Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Janvier" #: mod_irc:665 msgid "Join IRC channel" msgstr "Rejoindre un canal IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Rejoindre un canal IRC ici" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Rejoindre un canal IRC avec ce Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Juillet" #: mod_muc_log:478 msgid "June" msgstr "Juin" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Dernière Activité" #: mod_configure:1646 msgid "Last login" msgstr "Dernière connexion" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Dernier mois" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Dernière année" #: mod_configure:941 msgid "List of modules to start" msgstr "Liste des modules à démarrer" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Liste des salons" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Ports ouverts" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Ports ouverts sur " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Script de mise à jour de bas-niveau" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Rendre la liste des participants publique" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Protéger le salon par un CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Réserver le salon aux membres uniquement" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Rendre le salon modéré" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Protéger le salon par mot de passe" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Rendre le salon persistant" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Rendre le salon public" #: mod_register:317 msgid "Malformed username" msgstr "Nom d'utilisateur invalide" #: mod_muc_log:475 msgid "March" msgstr "Mars" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Nombre maximum d'occupants" #: mod_muc_log:477 msgid "May" msgstr "Mai" #: mod_shared_roster:907 msgid "Members:" msgstr "Membres:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Vous devez être membre pour accèder à ce salon" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget it." msgstr "" "Mémorisez votre mot de passe, ou écrivez-le sur un papier conservé dans un " "endroit secret. Dans Jabber il n'y a pas de mécanisme pour retrouver votre mot " "de passe si vous l'avez oublié." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Mémoire" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Corps du message" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Message non trouvé dans l'enveloppe transférée" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 mod_vcard_mnesia:116 #: mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Autre nom" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Entrée 'channel' ou 'serveur' manquant dans le formulaire" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Attribut 'from' absent" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Attribut 'to' absent" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Les droits de modérateur sont nécessaires" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Modules mis à jour" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Module" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "Echec de traitement de la demande" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Modules" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Modules sur ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Lundi" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Discussion de groupe" #: mod_multicast:267 msgid "Multicast" msgstr "Multidiffusion" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Les elements multiples ne sont pas autorisés (rfc6121)" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nom" #: mod_shared_roster:896 msgid "Name:" msgstr "Nom:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Attribut 'jid' ou 'nick' absent" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Attribut 'role' ou 'affiliation' absent" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Jamais" #: mod_register_web:377 msgid "New Password:" msgstr "Nouveau mot de passe:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Pseudo" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Enregistrement d'un pseudo sur " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Le pseudo ~s n'existe pas dans ce salon" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Entrée 'access' absente du formulaire" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Entrée 'acls' absente du formulaire" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "Attribut 'affiliation' absent" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "Aucun élément 'item' trouvé" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Entrée 'modules' absente du formulaire" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Entrée 'password' absente du formulaire" #: mod_register:148 msgid "No 'password' found in this query" msgstr "L'élément 'password' est absent de la requête" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Entrée 'path' absente du formulaire" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "L'élément 'to' est absent de l'invitation" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Aucune information disponible" #: ejabberd_local:181 msgid "No available resource found" msgstr "Aucune ressource disponible" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Pas de corps de message pour l'annonce" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "Formulaire non trouvé" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "Aucune fonctionalité disponible" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Aucun gestionnaire n'a pris en charge cette commande" #: mod_last:218 msgid "No info about last activity found" msgstr "Aucune activité précédente trouvée" #: mod_blocking:99 msgid "No items found in this query" msgstr "Aucun item trouvé dans cette requête" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Aucun module ne supporte cette requête" #: mod_pubsub:1541 msgid "No node specified" msgstr "Nœud non spécifié" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "Aucune demande d'abonnement trouvée" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "Liste non trouvée" #: mod_private:96 msgid "No private data found in this query" msgstr "Aucune donnée privée trouvée dans cette requête" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "Nœud non trouvé" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "Aucun service disponible" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Pas de statistiques" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "Ce nœud existe déjà" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Index de nœud non trouvé" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nœud non trouvé" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Nœud ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Echec de formattage" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nœuds" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Aucun" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Nœud non trouvé" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "Pas abonné" #: mod_muc_log:483 msgid "November" msgstr "Novembre" #: mod_configure:1212 msgid "Number of online users" msgstr "Nombre d'utilisateurs en ligne" #: mod_configure:1202 msgid "Number of registered users" msgstr "Nombre d'utilisateurs enregistrés" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Octobre" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Messages en attente" #: mod_offline:761 msgid "Offline Messages:" msgstr "Messages en attente:" #: mod_register_web:373 msgid "Old Password:" msgstr "Ancien mot de passe:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "En ligne" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Utilisateurs en ligne" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Utilisateurs connectés:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Seul le tag ou est autorisé" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Seul l'élément est autorisé dans cette requête" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Seuls les membres peuvent accéder aux archives de ce salon" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this room" msgstr "" "Seuls les modérateurs et les participants peuvent changer le sujet dans ce salon" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Seuls les modérateurs peuvent changer le sujet dans ce salon" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Seuls les modérateurs peuvent accépter les requêtes voix" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Seuls les occupants peuvent envoyer des messages à la conférence" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Seuls les occupants sont autorisés à envoyer des requêtes à la conférence" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Seuls les administrateurs du service sont autoriser à envoyer des messages de " "service" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Options" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 mod_vcard_mnesia:123 #: mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nom de l'organisation" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 mod_vcard_mnesia:124 #: mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unité de l'organisation" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Connexions s2s sortantes" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Connexions s2s sortantes:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 mod_pubsub:2383 #: mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Les droits de propriétaire sont nécessaires" #: mod_offline:693 msgid "Packet" msgstr "Paquet" #: mod_irc:578 msgid "Parse error" msgstr "Erreur d'interprétation" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "Echec d'interprétation" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Mot de passe" #: mod_configure:1130 msgid "Password Verification" msgstr "Vérification du mot de passe" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Vérification du mot de passe:" #: mod_irc:822 msgid "Password ~b" msgstr "Mot de passe ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Mot de passe:" #: mod_configure:998 msgid "Path to Dir" msgstr "Chemin vers le répertoire" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Chemin vers le fichier" #: mod_roster:915 msgid "Pending" msgstr "En suspens" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Période: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Salons persistent" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "Requête ping incorrecte" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. If " "you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Ces options sauvegardent uniquement la base de données interne Mnesia. Si vous " "utilisez le module ODBC vous devez sauvegarde votre base SQL séparément." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Attendez un moment avant de re-lancer une requête de voix" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Le traitement de l'attribut 'ack' n'est pas autorisé (rfc6121)" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocole" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Demande d'abonnement PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publication-Abonnement" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "La publication sur un nœud de type collection n'est pas autorisé" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Les requêtes sur les membres de la conférence ne sont pas autorisé dans ce salon" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Requête vers un autre utilisateur interdite" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Copie en mémoire vive (RAM) et sur disque" #: mod_configure:889 msgid "RAM copy" msgstr "Copie en mémoire vive (RAM)" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Erreur d'appel RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Brut" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Confirmer la suppression du message du jour ?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Le destinataire n'est pas dans la conférence" #: mod_register_web:275 msgid "Register" msgstr "Enregistrer" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Enregistrer un compte Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Utilisateurs enregistrés" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Utilisateurs enregistrés:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Pseudos enregistrés" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Enregistrement du mod_irc pour " #: mod_configure:889 msgid "Remote copy" msgstr "Copie distante" #: mod_roster:963 msgid "Remove" msgstr "Supprimer" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Effacer tous les messages hors ligne" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Supprimer l'utilisateur" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Remplacé par une nouvelle connexion" #: mod_configure:1675 msgid "Resources" msgstr "Ressources" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Redémarrer" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Redémarrer le service" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restauration" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaurer la sauvegarde depuis le fichier sur " #: ejabberd_web_admin:2013 msgid "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Restauration de la sauvegarde binaire après redémarrage (nécessite moins de " "mémoire):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Restauration immédiate d'une sauvegarde binaire:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Restauration immédiate d'une sauvegarde texte:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Configuration du salon" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Occupants du salon" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "La création de salons est interdite par le service" #: mod_muc_log:1064 msgid "Room description" msgstr "Description du salon" #: mod_muc_log:1027 msgid "Room title" msgstr "Titre du salon" #: mod_roster:1082 msgid "Roster" msgstr "Liste de contacts" #: mod_roster:334 msgid "Roster module has failed" msgstr "Echec du module roster" #: mod_roster:968 msgid "Roster of " msgstr "Liste de contact de " #: mod_configure:1671 msgid "Roster size" msgstr "Taille de la liste de contacts" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Noeuds actifs" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "La négociation SASL n'est pas autorisé à ce stade" #: mod_muc_log:468 msgid "Saturday" msgstr "Samedi" #: mod_irc:582 msgid "Scan error" msgstr "Erreur d'interprétation" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "Echec d'interprétation" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Validation du script" #: mod_vcard:453 msgid "Search Results for " msgstr "Résultats de recherche pour " #: mod_vcard:427 msgid "Search users in " msgstr "Rechercher des utilisateurs " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Envoyer l'annonce à tous les utilisateurs en ligne" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Envoyer l'annonce à tous les utilisateurs en ligne sur tous les serveurs" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Envoyer l'annonce à tous les utilisateurs" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Envoyer une annonce à tous les utilisateurs de tous les domaines" #: mod_muc_log:481 msgid "September" msgstr "Septembre" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "La connection au serveur à échouée" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "La connection aux sous-domaines locaux est interdite" #: mod_irc:842 msgid "Server ~b" msgstr "Serveur ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Serveur:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Définir le message du jour et l'envoyer aux utilisateurs en ligne" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Définir le message du jour pour tous domaines et l'envoyer aux utilisateurs en " "ligne" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Groupes de liste de contacts partagée" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Montrer la table intégralement" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Montrer la table ordinaire" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Arrêter le service" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should do " "this only in your personal computer for safety reasons." msgstr "" "Certains clients Jabber peuvent stocker votre mot de passe sur votre " "ordinateur. N'utilisez cette fonctionnalité que si vous avez confiance en la " "sécurité de votre ordinateur." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Démarrer" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Modules de démarrage" #: mod_configure:936 msgid "Start Modules at " msgstr "Démarrer les modules sur " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistiques" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistiques de ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Arrêter" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Modules d'arrêt" #: mod_configure:918 msgid "Stop Modules at " msgstr "Arrêter les modules sur " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nœuds arrêtés" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Type de stockage" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Sauvegarde binaire:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Sauvegarde texte:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Sujet" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Soumettre" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Soumis" #: mod_roster:914 msgid "Subscription" msgstr "Abonnement" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Les abonnement ne sont pas autorisés" #: mod_muc_log:469 msgid "Sunday" msgstr "Dimanche" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Le pseudo est déjà utilisé par un autre occupant" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Le pseudo est enregistré par une autre personne" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Le CAPTCHA est valide" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "La vérification du CAPTCHA a échoué" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "La demande de fonctionalité n'est pas supportée par la conférence" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "Le mot de passe contient des caractères non-acceptables" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Le mot de passe est trop faible" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Le mot de passe de votre compte Jabber a été changé avec succès." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "La requête n'est autorisé qu'aux utilisateurs locaux" #: mod_roster:203 msgid "The query must not contain elements" msgstr "La requête ne doit pas contenir d'élément " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, or " "one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Il y a eu une erreur en changeant le mot de passe: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Il y a eu une erreur en créant le compte: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Il y a eu une erreur en effaçant le compte: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "C'est insensible à la casse : macbeth est identique à MacBeth et Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read carefully " "the instructions to fill correctly the fields." msgstr "" "Cette page permet de créer un compte Jabber sur ce serveur Jabber. Votre JID " "(Jabber IDentifier, identifiant Jabber) sera de la forme : nom@serveur. Prière " "de lire avec attention les instructions pour remplir correctement ces champs." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Cette page permet d'effacer un compte Jabber sur ce serveur Jabber." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Ce salon n'est pas anonyme" #: mod_muc_log:466 msgid "Thursday" msgstr "Jeudi" #: mod_offline:690 msgid "Time" msgstr "Heure" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Délais" #: mod_offline:692 msgid "To" msgstr "A" #: mod_register:215 msgid "To register, visit ~s" msgstr "Pour vous enregistrer, visitez ~s" #: mod_configure:709 msgid "To ~s" msgstr "A ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Jeton TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "L'attribut 'xml:lang' est trop long" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "Trop d'éléments " #: mod_privacy:164 msgid "Too many elements" msgstr "Trop d'éléments " #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Trop de requêtes CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Trop de flux SOCKS5 actifs" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Trop de stanzas sans accusé de réception (ack)" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Trop d'utilisateurs dans cette conférence" #: mod_register:355 msgid "Too many users registered" msgstr "Trop d'utilisateurs enregistrés" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Nombre de salons" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "La limite de trafic a été dépassée" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transactions annulées:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transactions commitées:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transactions journalisées:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transactions redémarrées:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Mardi" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Impossible de générer le CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Impossible d'enregistrer la route sur un domaine locale existant" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Non autorisé" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Action inattendu" #: mod_register_web:488 msgid "Unregister" msgstr "Désinscrire" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Effacer un compte Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "Elément non supporté" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Requête MIX non supportée" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Mettre à jour" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Mise à jour du message du jour (pas d'envoi)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plan de mise à jour" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Script de mise à jour" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Mise à jour de ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Temps depuis le démarrage :" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "L'utilisation de STARTTLS est interdit" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "L'utilisation de STARTTLS est impérative" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Utilisateur" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Utilisateur (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Gestion des utilisateurs" #: mod_register:345 msgid "User already exists" msgstr "L'utilisateur existe déjà" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "L'utilisateur n'est pas spécifié dans le JID de l'attribut 'from'" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Session utilisateur non trouvée" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Session utilisateur terminée" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Utilisateur ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Nom d'utilisateur:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Utilisateurs" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Dernière activité des utilisateurs" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "" "Les utilisateurs ne sont pas autorisés à enregistrer des comptes si rapidement" #: mod_roster:954 msgid "Validate" msgstr "Valider" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "La valeur de l'attribut 'type' ne peut être 'get'" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 mod_irc:285 #: mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 mod_muc:539 #: mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 mod_stats:55 #: mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "La valeur de l'attribut 'type' ne peut être 'set'" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "La valeur de '~s' ne peut être booléen" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "La valeur de '~s' doit être une chaine datetime" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "La valeur de '~s' doit être un entier" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Serveurs virtuels" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Les visiteurs ne sont pas autorisés à changer de pseudo dans ce salon" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "" "Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants" #: mod_muc_room:3879 msgid "Voice request" msgstr "Demande de voix" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Les demandes de voix sont désactivées dans cette conférence" #: mod_muc_log:465 msgid "Wednesday" msgstr "Mercredi" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" "Vous pouvez changer votre mot de passe plus tard en utilisant un client Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Vous avez été exclus de ce salon" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Vous avec rejoint trop de conférences" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Vous devez préciser le champ \"pseudo\" dans le formulaire" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Vous avez besoin d'un client prenant en charge x:data et CAPTCHA pour " "enregistrer un pseudo" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Vous avez besoin d'un client prenant en charge x:data pour enregistrer un pseudo" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Vous avez besoin d'un client supportant x:data pour configurer le module IRC" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Vous avez besoin d'un client supportant x:data pour faire une recherche" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "Vous n'êtes pas autorisé à créer des nœuds" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Votre compte Jabber a été créé avec succès." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Votre compte Jabber a été effacé avec succès." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Votre règle de flitrage active a empêché le routage de ce stanza." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "La file d'attente de message de votre contact est pleine. Votre message a été " "détruit." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Vos messages pour ~s sont bloqués. Pour les débloquer, veuillez visiter ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Module IRC ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Module MUC ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Service de Multidiffusion d'ejabberd" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Module Publish-Subscribe d'ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams module" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Console Web d'administration de ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Module vCard ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "a été banni" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "a été expulsé" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "a été éjecté en raison de l'arrêt du système" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "a été éjecté à cause d'un changement d'autorisation" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "a été éjecté car la salle est désormais réservée aux membres" #: mod_muc_log:410 msgid "is now known as" msgstr "est maintenant connu comme" #: mod_muc_log:370 msgid "joins the room" msgstr "rejoint le salon" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "quitte le salon" #: mod_muc_room:3856 msgid "private, " msgstr "privé" #: mod_muc_room:3955 msgid "the password is" msgstr "le mot de passe est" #: mod_vcard:292 msgid "vCard User Search" msgstr "Recherche dans l'annnuaire" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Configuration des règles d'accès ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s vous a invité dans la salle de discussion ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s messages en file d'attente" #~ msgid "No resource provided" #~ msgstr "Aucune ressource fournie" #, fuzzy #~ msgid "Server" #~ msgstr "Serveur :" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The address " #~ "will be unblocked at ~s UTC" #~ msgstr "" #~ "Trop (~p) d'authentification ont échoué pour cette adresse IP (~s). " #~ "L'adresse sera débloquée à ~s UTC" #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Cette adresse IP est blacklistée dans ~s" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Le Jabber ID ~s n'est pas valide" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Affiliation invalide : ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Role invalide : ~s" #~ msgid "No limit" #~ msgstr "Pas de limite" #~ msgid "Present real Jabber IDs to" #~ msgstr "Rendre le Jabber ID réel visible pour" #~ msgid "moderators only" #~ msgstr "modérateurs seulement" #~ msgid "anyone" #~ msgstr "tout le monde" #, fuzzy #~ msgid "Moderator" #~ msgstr "modérateurs seulement" #~ msgid "nobody" #~ msgstr "personne" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permettre aux visiteurs d'envoyer des demandes de 'voice'" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Intervalle minimum entre les demandes de 'voice' (en secondes)" #~ msgid "Enable message archiving" #~ msgstr "Activer l'archivage de messages" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Exempter des Jabberd IDs du test CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Vous avez besoin d'un client supportant x:data pour configurer le salon" #~ msgid "Number of occupants" #~ msgstr "Nombre d'occupants" #~ msgid "User JID" #~ msgstr "JID de l'utilisateur " #~ msgid "Grant voice to this person?" #~ msgstr "Accorder 'voice' à cet utilisateur" #~ msgid "Node ID" #~ msgstr "Identifiant du nœud" #~ msgid "Subscriber Address" #~ msgstr "Adresse de l'abonné" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Autoriser ce Jabber ID à s'abonner à ce nœud PubSub" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Inclure le contenu du message avec la notification" #~ msgid "Deliver event notifications" #~ msgstr "Envoyer les notifications d'événement" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Avertir les abonnés lorsque la configuration du nœud change" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Avertir les abonnés lorsque le nœud est supprimé" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Avertir les abonnés lorsque des éléments sont supprimés sur le nœud" #~ msgid "Persist items to storage" #~ msgstr "Stockage persistant des éléments" #~ msgid "A friendly name for the node" #~ msgstr "Un nom convivial pour le noeud" #~ msgid "Max # of items to persist" #~ msgstr "Nombre maximum d'éléments à stocker" #~ msgid "Whether to allow subscriptions" #~ msgstr "Autoriser l'abonnement ?" #~ msgid "Specify the access model" #~ msgstr "Définir le modèle d'accès" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Groupes de liste de contact autorisés à s'abonner" #~ msgid "Specify the publisher model" #~ msgstr "Définir le modèle de publication" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Purger tous les items lorsque publieur est hors-ligne" #~ msgid "Specify the event message type" #~ msgstr "Définir le type de message d'événement" #~ msgid "Max payload size in bytes" #~ msgstr "Taille maximum pour le contenu du message en octet" #~ msgid "When to send the last published item" #~ msgstr "A quel moment envoyer le dernier élément publié" #~ msgid "Only deliver notifications to available users" #~ msgstr "Envoyer les notifications uniquement aux utilisateurs disponibles" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Les collections avec lesquelle un nœud est affilié" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Remplissez les champs pour rechercher un utilisateur Jabber" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Serveurs s2s sortants" #~ msgid "Delete" #~ msgstr "Supprimer" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Ce participant est expulsé du salon pour avoir envoyé un message erronée" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message to " #~ "another participant" #~ msgstr "" #~ "Ce participant est expulsé du salon pour avoir envoyé un message erronée à " #~ "un autre participant" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Ce participant est expulsé du salon pour avoir envoyé une présence erronée" #~ msgid "Encodings" #~ msgstr "Encodages" #~ msgid "(Raw)" #~ msgstr "(Brut)" ejabberd-18.01/priv/msgs/it.po0000644000232200023220000017251713225664356016565 0ustar debalancedebalance# Luca Brivio , 2012. msgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "PO-Revision-Date: 2012-04-24 16:48+0200\n" "Last-Translator: Luca Brivio \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Italian (italiano)\n" "X-Additional-Translator: Gabriele Stilli \n" "X-Additional-Translator: Smart2128\n" "X-Generator: Lokalize 1.2\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " ha modificato l'oggetto in: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Per entrare in questa stanza è prevista una password" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configurazione dell'accesso" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configurazione dei diritti di accesso (ACL)" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Diritti di accesso (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Regole di accesso" #: mod_configure:1095 msgid "Access control lists" msgstr "Diritti di accesso (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Accesso impedito dalle politiche del servizio" #: mod_configure:1113 msgid "Access rules" msgstr "Regole di accesso" #: mod_configure:1772 msgid "Action on user" msgstr "Azione sull'utente" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Aggiungere un Jabber ID (Jabber ID)" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Aggiungere nuovo" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Aggiungere un utente" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Amministrazione" #: mod_configure:1767 msgid "Administration of " msgstr "Amministrazione di " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Necessari i privilegi di amministratore" #: mod_configure:507 msgid "All Users" msgstr "Tutti gli utenti" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Tutta l'attività" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Consentire agli utenti di cambiare l'oggetto" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Consentire agli utenti query verso altri utenti" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Consentire agli utenti l'invio di inviti" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Consentire agli utenti l'invio di messaggi privati" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Consentire ai visitatori di cambiare il nickname" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Consentire agli ospiti l'invio di messaggi privati a" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" "Consentire ai visitatori l'invio di testo sullo stato in aggiornamenti sulla " "presenza" #: mod_announce:611 msgid "Announcements" msgstr "Annunci" #: mod_muc_log:476 msgid "April" msgstr "Aprile" #: mod_muc_log:480 msgid "August" msgstr "Agosto" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Salvare" #: mod_configure:584 msgid "Backup Management" msgstr "Gestione dei salvataggi" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Salvataggio di " #: mod_configure:948 msgid "Backup to File at " msgstr "Salvataggio sul file " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Formato non valido" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Compleanno" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Pagina web CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Tempo CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Modificare la password" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Cambiare la password dell'utente" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Caratteri non consentiti:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Caratteri non consentiti:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Caratteri non consentiti:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Configurazione della stanza modificata" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "La stanza è creata" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "La stanza è eliminata" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "La stanza è avviata" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "La stanza è arrestata" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Stanze" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Scegliere un nome utente e una password per la registrazione con questo " "server" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Selezionare i moduli da arrestare" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Selezionare una modalità di conservazione delle tabelle" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Scegliere se approvare l'iscrizione per questa entità" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Città" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Comandi" #: mod_muc:461 msgid "Conference room does not exist" msgstr "La stanza per conferenze non esiste" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configurazione" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Configurazione per la stanza ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Risorse connesse:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parametri delle connessioni" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Paese" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Database" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Configurazione delle tabelle del database su " #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Tabelle del database su " #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Database" #: mod_muc_log:484 msgid "December" msgstr "Dicembre" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Definire per default gli utenti come partecipanti" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Eliminare gli elementi selezionati" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Eliminare l'utente" #: mod_announce:629 msgid "Delete message of the day" msgstr "Eliminare il messaggio del giorno (MOTD)" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Eliminare il messaggio del giorno (MOTD) su tutti gli host" #: mod_shared_roster:900 msgid "Description:" msgstr "Descrizione:" #: mod_configure:889 msgid "Disc only copy" msgstr "Copia su disco soltanto" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Gruppi visualizzati:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Non comunicare la tua password a nessuno, neppure agli amministratori del " "server Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Trascrivere il salvataggio sul file di testo " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Trascrivere su file di testo" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Modificare le proprietà" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Approva oppure respingi la richiesta di parola." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementi" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "E-mail" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "la password è" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Abilitare i log" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Codifica per il server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Terminare la sessione dell'utente" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Immettere un elenco di {Modulo, [Opzioni]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Immettere il nickname che si vuole registrare" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Immettere il percorso del file di salvataggio" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Immettere il percorso della directory di spool di jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Immettere il percorso del file di spool di jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Immettere il percorso del file di testo" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Immettere il testo visibile" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Immettere il nome utente e le codifiche che si desidera utilizzare per la " "connessione ai server IRC. Premere \"Avanti\" per vedere i successivi campi " "da compilare. Premere \"Fatto\" per salvare le impostazioni." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Immettere il nome utente, le codifiche, le porte e le password che si " "desidera utilizzare per la connessione ai server IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Errore" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Esempio: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"segreto\"}, {\"vendetta." "fef.net\", \"iso8859-1\", 7000}, {\"irc.serverdiprova.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Esportare i dati di tutti gli utenti nel server in file PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Esportare i dati degli utenti di un host in file PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" "Impossibile estrarre il JID dall'approvazione della richiesta di parola" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Cognome" #: mod_muc_log:474 msgid "February" msgstr "Febbraio" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Riempire il modulo per la ricerca di utenti Jabber corrispondenti ai criteri " "(Aggiungere * alla fine del campo per la ricerca di una sottostringa" #: mod_muc_log:467 msgid "Friday" msgstr "Venerdì" #: mod_offline:691 msgid "From" msgstr "Da" #: mod_configure:723 msgid "From ~s" msgstr "Da ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nome completo" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Ottenere il numero di utenti online" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Ottenere il numero di utenti registrati" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Ottenere la data di ultimo accesso dell'utente" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Ottenere la password dell'utente" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Ottenere le statistiche dell'utente" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Altro nome" #: mod_shared_roster:923 msgid "Group " msgstr "Gruppo " #: mod_roster:916 msgid "Groups" msgstr "Gruppi" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Indirizzi IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transport IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Nome utente IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canale IRC (senza il # iniziale)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Nodo non trovato" #: mod_irc:673 msgid "IRC server" msgstr "Server IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Impostazioni IRC" #: mod_irc:766 msgid "IRC username" msgstr "Nome utente IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Se qui non vedi l'immagine CAPTCHA, visita la pagina web." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Se si vogliono specificare differenti porte, password, codifiche per i " "server IRC, si riempia questo elenco con valori nel formato '{\"server IRC" "\", \"codifica\", porta, \"password\"}'. Per default questo servizio " "utilizza la codifica \"~s\", la porta ~p, la password vuota." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importare una directory" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importare un file" #: mod_configure:982 msgid "Import User from File at " msgstr "Importare un utente dal file " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importare utenti da file di spool di jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importare utenti dalla directory " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importare i dati utente da file di spool di jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importare i dati utenti da un file PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importare i dati utenti da directory di spool di jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Tipo di messaggio non corretto" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Connessioni s2s in uscita:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Password non esatta" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Password non esatta" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "In questa conferenza le richieste di parola sono escluse" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Non è consentito l'invio di messaggi privati" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Non è consentito l'invio di messaggi privati di tipo \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Non è consentito l'invio di messaggi privati alla conferenza" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Registrazione account Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID (Jabber ID)" #: mod_muc_log:473 msgid "January" msgstr "Gennaio" #: mod_irc:665 msgid "Join IRC channel" msgstr "Entra nel canale IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Entra nel canale IRC qui." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Entra nel canale IRC in questo ID Jabber: ~s" #: mod_muc_log:479 msgid "July" msgstr "Luglio" #: mod_muc_log:478 msgid "June" msgstr "Giugno" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Ultima attività" #: mod_configure:1646 msgid "Last login" msgstr "Ultimo accesso" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Ultimo mese" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Ultimo anno" #: mod_configure:941 msgid "List of modules to start" msgstr "Elenco dei moduli da avviare" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Porte in ascolto" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Porte in ascolto su " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Script di aggiornamento di basso livello" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Rendere pubblica la lista dei partecipanti" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Rendere la stanza protetta da CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Rendere la stanza riservata ai membri" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Rendere la stanza moderata" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Rendere la stanza protetta da password" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Rendere la stanza persistente" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Rendere la sala visibile al pubblico" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Nome utente IRC" #: mod_muc_log:475 msgid "March" msgstr "Marzo" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Numero massimo di occupanti" #: mod_muc_log:477 msgid "May" msgstr "Maggio" #: mod_shared_roster:907 msgid "Members:" msgstr "Membri:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Per entrare in questa stanza è necessario essere membro" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Memorizza la password, o scrivila su un foglio di carta da conservare in un " "luogo sicuro. Jabber non prevede una modalità automatica per il recupero di " "una password dimenticata." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memoria" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Corpo del messaggio" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Altro nome" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Necessari i privilegi di moderatore" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Moduli modificati" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modulo" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduli" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Moduli su " #: mod_muc_log:463 msgid "Monday" msgstr "Lunedì" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nome" #: mod_shared_roster:896 msgid "Name:" msgstr "Nome:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Mai" #: mod_register_web:377 msgid "New Password:" msgstr "Nuova password:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Nickname" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registrazione di un nickname su " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Il nickname ~s non esiste nella stanza" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Nodo non trovato" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Nessuna informazione" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Nessun corpo fornito per il messaggio di annuncio" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Nodo non trovato" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Nodo non trovato" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Nodo non trovato" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nodo non trovato" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Nodo " #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nodi" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nessuno" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Non trovato" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "Novembre" #: mod_configure:1212 msgid "Number of online users" msgstr "Numero di utenti online" #: mod_configure:1202 msgid "Number of registered users" msgstr "Numero di utenti registrati" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Ottobre" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Messaggi offline" #: mod_offline:761 msgid "Offline Messages:" msgstr "Messaggi offline:" #: mod_register_web:373 msgid "Old Password:" msgstr "Vecchia password:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Online" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Utenti online" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Utenti connessi:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "" "La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "La modifica dell'oggetto di questa stanza è consentita soltanto ai " "moderatori e ai partecipanti" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "" "La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Soltanto i moderatori possono approvare richieste di parola" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "L'invio di messaggi alla conferenza è consentito soltanto ai presenti" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "L'invio di query alla conferenza è consentito ai soli presenti" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "L'invio di messaggi di servizio è consentito solamente agli amministratori " "del servizio" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opzioni" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nome dell'organizzazione" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unità dell'organizzazione" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Connessioni s2s in uscita" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Connessioni s2s in uscita:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Necessari i privilegi di proprietario" #: mod_offline:693 msgid "Packet" msgstr "Pacchetto" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Password" #: mod_configure:1130 msgid "Password Verification" msgstr "Verifica della password" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Verifica della password:" #: mod_irc:822 msgid "Password ~b" msgstr "Password ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Password:" #: mod_configure:998 msgid "Path to Dir" msgstr "Percorso della directory" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Percorso del file" #: mod_roster:915 msgid "Pending" msgstr "Pendente" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periodo:" #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "esce dalla stanza" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "N.B.: Queste opzioni comportano il salvataggio solamente del database " "interno Mnesia. Se si sta utilizzando il modulo ODBC, è necessario salvare " "anche il proprio database SQL separatamente." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Attendi qualche istante prima di inviare una nuova richiesta di parola" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Porta" #: mod_irc:828 msgid "Port ~b" msgstr "Porta ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocollo" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Richiesta di iscrizione per PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Pubblicazione-Iscrizione" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "In questa stanza non sono consentite query ai membri della conferenza" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Copia in memoria (RAM) e su disco" #: mod_configure:889 msgid "RAM copy" msgstr "Copia in memoria (RAM)" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Errore di chiamata RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Grezzo" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Si conferma l'eliminazione del messaggio del giorno (MOTD)?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Il destinatario non è nella stanza per conferenze" #: mod_register_web:275 msgid "Register" msgstr "Registra" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Registra un account Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Utenti registrati" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Utenti registrati:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Utenti registrati" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registrazione in mod_irc per " #: mod_configure:889 msgid "Remote copy" msgstr "Copia remota" #: mod_roster:963 msgid "Remove" msgstr "Eliminare" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Eliminare tutti i messaggi offline" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Eliminare l'utente" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Sostituito da una nuova connessione" #: mod_configure:1675 msgid "Resources" msgstr "Risorse" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Riavviare" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Riavviare il servizio" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Recuperare" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Recuperare il salvataggio dal file " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Recuperare un salvataggio binario dopo il prossimo riavvio di ejabberd " "(necessita di meno memoria):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Recuperare un salvataggio binario adesso:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Recuperare un salvataggio come semplice testo adesso:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Configurazione della stanza" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Presenti nella stanza" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "La creazione di stanze è impedita dalle politiche del servizio" #: mod_muc_log:1064 msgid "Room description" msgstr "Descrizione della stanza" #: mod_muc_log:1027 msgid "Room title" msgstr "Titolo della stanza" #: mod_roster:1082 msgid "Roster" msgstr "Lista dei contatti" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Lista dei contatti di " #: mod_configure:1671 msgid "Roster size" msgstr "Dimensione della lista dei contatti" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nodi attivi" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Sabato" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Verifica dello script" #: mod_vcard:453 msgid "Search Results for " msgstr "Risultati della ricerca per " #: mod_vcard:427 msgid "Search users in " msgstr "Cercare utenti in " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Inviare l'annuncio a tutti gli utenti online" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Inviare l'annuncio a tutti gli utenti online su tutti gli host" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Inviare l'annuncio a tutti gli utenti" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Inviare l'annuncio a tutti gli utenti su tutti gli host" #: mod_muc_log:481 msgid "September" msgstr "Settembre" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Server:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "" "Impostare il messaggio del giorno (MOTD) ed inviarlo agli utenti online" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Impostare il messaggio del giorno (MOTD) su tutti gli host e inviarlo agli " "utenti online" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Gruppi di liste di contatti comuni" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Mostrare la tabella integrale" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Mostrare la tabella normale" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Terminare il servizio" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Alcuni client Jabber possono conservare la password nel tuo computer. " "Utilizza tale funzione soltanto se ritieni che il tuo computer sia sicuro." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Avviare" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Avviare moduli" #: mod_configure:936 msgid "Start Modules at " msgstr "Avviare moduli su " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistiche" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistiche di ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Arrestare" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Arrestare moduli" #: mod_configure:918 msgid "Stop Modules at " msgstr "Arrestare moduli su " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nodi arrestati" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Tipo di conservazione" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Conservare un salvataggio binario:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Conservare un salvataggio come semplice testo:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Oggetto" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Inviare" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Inviato" #: mod_roster:914 msgid "Subscription" msgstr "Iscrizione" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Domenica" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Il nickname è già in uso all'interno della conferenza" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Questo nickname è registrato da un'altra persona" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Il CAPTCHA è valido." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "La verifica del CAPTCHA ha avuto esito negativo" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "La password è troppo debole" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Il cambio di password del tuo account Jabber è andato a buon fine." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Si è verificato un errore nel cambio di password: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Si è verificato un errore nella creazione dell'account: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Si è verificato un errore nella cancellazione dell'account: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Non fa differenza fra minuscolo e maiuscolo: macbeth, MacBeth e Macbeth si " "equivalgono." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Questa pagina consente di creare un account Jabber in questo server Jabber. " "Il tuo JID (Jabber IDentifier) avrà la forma: nome_utente@server. Leggi " "attentamente le istruzioni per compilare i campi correttamente." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Questa pagina consente di eliminare un account Jabber da questo server " "Jabber." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Questa stanza non è anonima" #: mod_muc_log:466 msgid "Thursday" msgstr "Giovedì" #: mod_offline:690 msgid "Time" msgstr "Ora" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Ritardo" #: mod_offline:692 msgid "To" msgstr "A" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "A ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Troppe richieste CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "In questa conferenza le richieste di parola sono escluse" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Stanze" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Limite di traffico superato" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transazioni abortite:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transazioni avvenute:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transazioni con log:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transazioni riavviate:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Martedì" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Impossibile generare un CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Non autorizzato" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Elimina" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Elimina un account Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Aggiornare" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Aggiornare il messaggio del giorno (MOTD) (non inviarlo)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "" "Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Piano di aggiornamento" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Script di aggiornamento" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Aggiornare " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Tempo dall'avvio:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Utilizzo di STARTTLS obbligatorio" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Utilizzo di STARTTLS obbligatorio" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Utente" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Gestione degli utenti" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Nodo non trovato" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Utente " #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Nome utente:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Utenti" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Ultima attività degli utenti" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Non è consentito agli utenti registrare account così rapidamente" #: mod_roster:954 msgid "Validate" msgstr "Validare" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Host Virtuali" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Non è consentito ai visitatori cambiare il nickname in questa stanza" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Non è consentito ai visitatori l'invio di messaggi a tutti i presenti" #: mod_muc_room:3879 msgid "Voice request" msgstr "Richiesta di parola" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "In questa conferenza le richieste di parola sono escluse" #: mod_muc_log:465 msgid "Wednesday" msgstr "Mercoledì" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Potrai in seguito cambiare la password utilizzando un client Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Sei stata/o bandita/o da questa stanza" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Si deve riempire il campo \"Nickname\" nel modulo" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "La registrazione richiede un client che supporti x:data e CAPTCHA" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Per registrare il nickname è necessario un client che supporti x:data" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Per la configurazione del modulo IRC è necessario un client che supporti x:" "data" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Per effettuare ricerche è necessario un client che supporti x:data" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Non è consentito l'invio di messaggi privati" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "La creazione del tuo account Jabber è andata a buon fine." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "La cancellazione del tuo account Jabber è andata a buon fine." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" "In base alla tua attuale lista privacy questa stanza è stata esclusa dalla " "navigazione." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "La coda dei messaggi offline del contatto è piena. Il messaggio è stato " "scartato" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Modulo IRC per ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Modulo MUC per ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Modulo Pubblicazione/Iscrizione (PubSub) per ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Modulo SOCKS5 Bytestreams per ejabberd" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "Amministrazione web ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Modulo vCard per ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "è stata/o bandita/o" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "è stata/o espulsa/o" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "è stato espulso a causa dello spegnimento del sistema" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "è stato espulso a causa di un cambiamento di appartenenza" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "è stato espulso per la limitazione della stanza ai soli membri" #: mod_muc_log:410 msgid "is now known as" msgstr "è ora conosciuta/o come" #: mod_muc_log:370 msgid "joins the room" msgstr "entra nella stanza" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "esce dalla stanza" #: mod_muc_room:3856 msgid "private, " msgstr "privato, " #: mod_muc_room:3955 msgid "the password is" msgstr "la password è" #: mod_vcard:292 msgid "vCard User Search" msgstr "Ricerca di utenti per vCard" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Configurazione delle regole di accesso per ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s ti invita nella stanza ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Coda di ~s messaggi offline" #~ msgid "No resource provided" #~ msgstr "Nessuna risorsa fornita" #, fuzzy #~ msgid "Server" #~ msgstr "Server:" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Il Jabber ID ~s non è valido" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Affiliazione non valida: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Ruolo non valido: ~s" #~ msgid "No limit" #~ msgstr "Nessun limite" #~ msgid "Present real Jabber IDs to" #~ msgstr "Rendere visibile il Jabber ID reale a" #~ msgid "moderators only" #~ msgstr "moderatori soltanto" #~ msgid "anyone" #~ msgstr "tutti" #, fuzzy #~ msgid "Moderator" #~ msgstr "moderatori soltanto" #~ msgid "nobody" #~ msgstr "nessuno" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Consentire agli ospiti l'invio di richieste di parola" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Intervallo minimo fra due richieste di parola (in secondi)" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Escludi degli ID Jabber dal passaggio CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "Per la configurazione della stanza è necessario un client che supporti x:" #~ "data" #~ msgid "Number of occupants" #~ msgstr "Numero di presenti" #~ msgid "User JID" #~ msgstr "JID utente" #~ msgid "Grant voice to this person?" #~ msgstr "Dare parola a questa persona?" #~ msgid "Node ID" #~ msgstr "ID del nodo" #~ msgid "Subscriber Address" #~ msgstr "Indirizzo dell'iscritta/o" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Consentire a questo Jabber ID l'iscrizione a questo nodo pubsub?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Inviare il contenuto del messaggio con la notifica dell'evento" #~ msgid "Deliver event notifications" #~ msgstr "Inviare notifiche degli eventi" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Notificare gli iscritti quando la configurazione del nodo cambia" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Notificare gli iscritti quando il nodo è cancellato" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "" #~ "Notificare gli iscritti quando sono eliminati degli elementi dal nodo" #~ msgid "Persist items to storage" #~ msgstr "Conservazione persistente degli elementi" #~ msgid "A friendly name for the node" #~ msgstr "Un nome comodo per il nodo" #~ msgid "Max # of items to persist" #~ msgstr "Numero massimo di elementi da conservare persistentemente" #~ msgid "Whether to allow subscriptions" #~ msgstr "Consentire iscrizioni?" #~ msgid "Specify the access model" #~ msgstr "Specificare il modello di accesso" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Gruppi roster abilitati alla registrazione" #~ msgid "Specify the publisher model" #~ msgstr "Definire il modello di pubblicazione" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Cancella tutti gli elementi quando chi li ha pubblicati non è più online" #~ msgid "Specify the event message type" #~ msgstr "Specificare il tipo di messaggio di evento" #~ msgid "Max payload size in bytes" #~ msgstr "Dimensione massima del contenuto del messaggio in byte" #~ msgid "When to send the last published item" #~ msgstr "Quando inviare l'ultimo elemento pubblicato" #~ msgid "Only deliver notifications to available users" #~ msgstr "Inviare le notifiche solamente agli utenti disponibili" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Le collezioni a cui è affiliato un nodo" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "" #~ "Riempire i campi per la ricerca di utenti Jabber corrispondenti ai criteri" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Server s2s in uscita" #~ msgid "Delete" #~ msgstr "Eliminare" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Partecipante espulso dalla stanza perché ha inviato un messaggio non " #~ "valido" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Partecipante espulso dalla stanza perché ha inviato un messaggio non " #~ "valido a un altro partecipante" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Partecipante espulso dalla stanza perché ha inviato una presenza non " #~ "valido" #, fuzzy #~ msgid "CAPTCHA test failed" #~ msgstr "Il CAPTCHA è valido." ejabberd-18.01/priv/msgs/es.po0000644000232200023220000020414613225664356016552 0ustar debalancedebalance# translation of es.po to # Badlop , 2009. msgid "" msgstr "" "Project-Id-Version: es\n" "POT-Creation-Date: \n" "PO-Revision-Date: 2017-10-06 01:15+0200\n" "Last-Translator: Badlop \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Spanish (castellano)\n" "X-Generator: Poedit 1.8.7.1\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " ha puesto el asunto: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Se necesita contraseña para entrar en esta sala" #: ejabberd_oauth:448 msgid "Accept" msgstr "Aceptar" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configuración de accesos" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configuración de la Lista de Control de Acceso" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Listas de Control de Acceso" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Reglas de Acceso" #: mod_configure:1095 msgid "Access control lists" msgstr "Listas de Control de Acceso" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Acceso denegado por la política del servicio" #: mod_configure:1113 msgid "Access rules" msgstr "Reglas de acceso" #: mod_configure:1772 msgid "Action on user" msgstr "Acción en el usuario" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Añadir Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Añadir nuevo" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Añadir usuario" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administración" #: mod_configure:1767 msgid "Administration of " msgstr "Administración de " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Se necesita privilegios de administrador" #: mod_configure:507 msgid "All Users" msgstr "Todos los usuarios" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Toda la actividad" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Permitir a los usuarios cambiar el asunto" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Permitir a los usuarios consultar a otros usuarios" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Permitir a los usuarios enviar invitaciones" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Permitir a los usuarios enviar mensajes privados" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Permitir a los visitantes cambiarse el apodo" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Permitir a los visitantes enviar mensajes privados a" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" "Permitir a los visitantes enviar texto de estado en las actualizaciones de " "presencia" #: mod_announce:611 msgid "Announcements" msgstr "Anuncios" #: mod_muc_log:476 msgid "April" msgstr "abril" #: mod_muc_log:480 msgid "August" msgstr "agosto" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "La creación automática de nodo no está activada" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Guardar copia de seguridad" #: mod_configure:584 msgid "Backup Management" msgstr "Gestión de copia de seguridad" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Copia de seguridad de ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Guardar copia de seguridad en fichero en " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Mal formato" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Cumpleaños" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Se requiere tanto el nombre de usuario como el recurso" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Bytestream ya está activado" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Página web de CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Tiempo consumido de CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "No se puede borrar la lista activa" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "No se puede borrar la lista por defecto" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Cambiar contraseña" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Cambiar contraseña de usuario" #: mod_register:295 msgid "Changing password is not allowed" msgstr "No está permitido cambiar la contraseña" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "No está permitido cambiar el rol/afiliación" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Caracteres no permitidos:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Configuración de la sala modificada" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Se ha creado la sala" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Se ha destruido la sala" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Se ha iniciado la sala" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Se ha detenido la sala" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Salas de charla" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Escoge un nombre de usuario y contraseña para registrarte en este servidor" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Selecciona módulos a detener" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Selecciona tipo de almacenamiento de las tablas" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Decidir si aprobar la subscripción de esta entidad." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Ciudad" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Comandos" #: mod_muc:461 msgid "Conference room does not exist" msgstr "La sala de conferencias no existe" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configuración" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Configuración para la sala ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Recursos conectados:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parámetros de conexiones" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "País" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Base de datos" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Configuración de tablas de la base de datos en " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Tablas de la base de datos en ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Error en la base de datos" #: mod_muc_log:484 msgid "December" msgstr "diciembre" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Los usuarios son participantes por defecto" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Eliminar los seleccionados" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Borrar usuario" #: mod_announce:629 msgid "Delete message of the day" msgstr "Borrar mensaje del dia" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Borrar el mensaje del día en todos los dominios" #: mod_shared_roster:900 msgid "Description:" msgstr "Descripción:" #: mod_configure:889 msgid "Disc only copy" msgstr "Copia en disco solamente" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Mostrar grupos:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "No le digas tu contraseña a nadie, ni siquiera a los administradores del " "servidor Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Exporta copia de seguridad a fichero de texto en " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Exportar a fichero de texto" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Los grupos duplicados no están permitidos por RFC6121" #: mod_configure:1776 msgid "Edit Properties" msgstr "Editar propiedades" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Aprueba o rechaza la petición de voz." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementos" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "correo" #: mod_register:292 msgid "Empty password" msgstr "Contraseña vacía" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Guardar históricos" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "No está soportado activar Push sin el atributo 'node'" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Codificación del servidor ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Cerrar sesión de usuario" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Introduce lista de {módulo, [opciones]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Introduce el apodo que quieras registrar" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Introduce ruta al fichero de copia de seguridad" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Introduce la ruta al directorio de jabberd14 spools" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Introduce ruta al fichero jabberd14 spool" #: mod_configure:974 msgid "Enter path to text file" msgstr "Introduce ruta al fichero de texto" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Teclea el texto que ves" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Introduce el nombre de usuario y codificaciones de carácteres que quieras " "usar al conectar en los servidores de IRC. Pulsa Siguiente para conseguir " "más campos en el formulario. Pulsa Completar para guardar las opciones." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Introduce el nombre de usuario, codificaciones de carácteres, puertos y " "contraseñas que quieras usar al conectar en los servidores de IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Servidor Jabber en Erlang" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Error" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Ejemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Exportar todas las tablas a un fichero SQL:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exportar datos de todos los usuarios del servidor a ficheros PIEFXIS " "(XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exportar datos de los usuarios de un dominio a ficheros PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Fallo en el componente externo" #: mod_delegation:283 msgid "External component timeout" msgstr "Demasiado retraso (timeout) en el componente externo" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Falló la activación de bytestream" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Fallo al extraer el Jabber ID de tu aprobación de petición de voz" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "Falló el mapeo de espacio de nombres delegado al componente externo" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Falló la comprensión de la respuesta HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Falló la comprensión de chanserv" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Falló el procesado de la opción '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Apellido" #: mod_muc_log:474 msgid "February" msgstr "febrero" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "El fichero es más grande que ~w bytes" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Rellena el formulario para buscar usuarios Jabber. Añade * al final de un " "campo para buscar subcadenas." #: mod_muc_log:467 msgid "Friday" msgstr "viernes" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nombre completo" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Ver número de usuarios conectados" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Ver número de usuarios registrados" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Ver fecha de la última conexión de usuario" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Ver contraseña de usuario" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Ver estadísticas de usuario" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Nombre" #: mod_shared_roster:923 msgid "Group " msgstr "Grupo " #: mod_roster:916 msgid "Groups" msgstr "Grupos" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Dominio" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Dominio desconocido" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Direcciones IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transporte de IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Nombre de usuario en IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canal IRC (no pongas el # del principio)" #: mod_irc:417 msgid "IRC connection not found" msgstr "Conexión IRC no encontrada" #: mod_irc:673 msgid "IRC server" msgstr "Servidor IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Opciones de IRC" #: mod_irc:766 msgid "IRC username" msgstr "Nombre de usuario en IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Si no ves la imagen CAPTCHA aquí, visita la página web." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Si quieres especificar distintos codificaciones de carácteres, contraseñas o " "puertos para cada servidor IRC rellena esta lista con valores en el formato " "'{\"servidor irc\", \"codificación\", \"puerto\", \"contrasela\"}'. Este " "servicio usa por defecto la codificación \"~s\", puerto ~p, sin contraseña." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importar directorio" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importar fichero" #: mod_configure:982 msgid "Import User from File at " msgstr "Importa usuario desde fichero en " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importar usuarios de ficheros spool de jabberd-1.4" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importar usuarios desde el directorio en " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importar usuario de fichero spool de jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importar usuarios desde un fichero PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importar usuarios del directorio spool de jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Atributo 'from' impropio" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Atributo 'to' impropio" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Parte de dominio impropia en el atributo 'from'" #: mod_muc_room:260 msgid "Improper message type" msgstr "Tipo de mensaje incorrecto" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Conexiones S2S entrantes:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "El CAPTCHA proporcionado es incorrecto" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Formulario de datos incorrecto" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Contraseña incorrecta" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Valor incorrecto en el formulario de datos" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Valor incorrecto del atributo 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Valor incorrecto de 'action' en el formulario de datos" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Valor incorrecto de 'path' en el formulario de datos" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Valor incorrecto del atributo 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Privilegio insuficiente" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Atributo 'from' no válido en el mensaje reenviado" #: mod_privilege:300 msgid "Invalid element" msgstr "Elemento no válido" #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "Las invitaciones no están permitidas en esta sala" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "No está permitido enviar mensajes de error a la sala. Este participante (~s) " "ha enviado un mensaje de error (~s) y fue expulsado de la sala" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "No está permitido enviar mensajes privados" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "No está permitido enviar mensajes privados del tipo \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Impedir el envio de mensajes privados a la sala" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Registro de Cuenta Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "enero" #: mod_irc:665 msgid "Join IRC channel" msgstr "Entrar en canal IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Entrar en el canal de IRC aquí" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Entra en el canal de IRC en esta dirección Jabber: ~s" #: mod_muc_log:479 msgid "July" msgstr "julio" #: mod_muc_log:478 msgid "June" msgstr "junio" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Última actividad" #: mod_configure:1646 msgid "Last login" msgstr "Última conexión" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Último mes" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Último año" #: mod_configure:941 msgid "List of modules to start" msgstr "Lista de módulos a iniciar" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Lista de salas" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Puertos de escucha" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Puertos de escucha en " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Script de actualización a bajo nivel" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "La lista de participantes es pública" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Proteger la sala con CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Sala sólo para miembros" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Sala moderada" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Proteger la sala con contraseña" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Sala permanente" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Sala públicamente visible" #: mod_register:317 msgid "Malformed username" msgstr "Nombre de usuario mal formado" #: mod_muc_log:475 msgid "March" msgstr "marzo" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Número máximo de ocupantes" #: mod_muc_log:477 msgid "May" msgstr "mayo" #: mod_shared_roster:907 msgid "Members:" msgstr "Miembros:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Necesitas ser miembro de esta sala para poder entrar" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Memoriza tu contraseña, o apúntala en un papel en un lugar seguro. En Jabber " "no hay un método automatizado para recuperar la contraseña si la olvidas." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memoria" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Cuerpo del mensaje" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Mensaje no encontrado en el contenido reenviado" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Segundo nombre" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "No se encuentra 'channel' o 'server' en el formulario de datos" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "No se encuentra el atributo 'from'" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "No se encuentra el atributo 'to'" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Se necesita privilegios de moderador" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Módulos modificados" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Módulo" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "El módulo falló al gestionar la petición" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Módulos" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Módulos en ~p" #: mod_muc_log:463 msgid "Monday" msgstr "lunes" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Salas de Charla" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "No se permiten múltiples elementos en RFC6121" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nombre" #: mod_shared_roster:896 msgid "Name:" msgstr "Nombre:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "No se encontraron los atributos 'jid' ni 'nick'" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "No se encontraron los atributos 'role' ni 'affiliation'" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nunca" #: mod_register_web:377 msgid "New Password:" msgstr "Nueva contraseña:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Apodo" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registro del apodo en " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "El apodo ~s no existe en la sala" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "No se encontró 'access' en el formulario de datos" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "No se encontró 'acls' en el formulario de datos" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "No se encontró el atributo 'affiliation'" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "No se encontró el elemento 'item'" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "No se encontró 'modules' en el formulario de datos" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "No se encontró 'password' en el formulario de datos" #: mod_register:148 msgid "No 'password' found in this query" msgstr "No se encontró 'password' en esta petición" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "No se encontró 'path' en este formulario de datos" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "No se encontró el atributo 'to' en la invitación" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Sin datos" #: ejabberd_local:181 msgid "No available resource found" msgstr "No se encontró un recurso conectado" #: mod_announce:575 msgid "No body provided for announce message" msgstr "No se ha proporcionado cuerpo de mensaje para el anuncio" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "No se encontró formulario de datos" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "No hay características disponibles" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Ningún evento ha procesado este comando" #: mod_last:218 msgid "No info about last activity found" msgstr "No hay información respeto a la última actividad" #: mod_blocking:99 msgid "No items found in this query" msgstr "No se han encontrado elementos en esta petición" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Ningún modulo está gestionando esta petición" #: mod_pubsub:1541 msgid "No node specified" msgstr "No se ha especificado ningún nodo" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "No se han encontrado suscripciones pendientes" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "No se ha encontrado una lista de privacidad con este nombre" #: mod_private:96 msgid "No private data found in this query" msgstr "No se ha encontrado ningún elemento de dato privado en esta petición" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "No se ha encontrado ningún nodo activo" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "No hay servicios disponibles" #: mod_stats:101 msgid "No statistics found for this item" msgstr "No se han encontrado estadísticas para este elemento" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "El nodo ya existe" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "No se ha encontrado índice de nodo" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nodo no encontrado" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Nodo ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Ha fallado el procesado del nombre de nodo (nodeprep)" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nodos" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Ninguno" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "No encontrado" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "No suscrito" #: mod_muc_log:483 msgid "November" msgstr "noviembre" #: mod_configure:1212 msgid "Number of online users" msgstr "Número de usuarios conectados" #: mod_configure:1202 msgid "Number of registered users" msgstr "Número de usuarios registrados" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "Aceptar" #: mod_muc_log:482 msgid "October" msgstr "octubre" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Mensajes diferidos" #: mod_offline:761 msgid "Offline Messages:" msgstr "Mensajes diferidos:" #: mod_register_web:373 msgid "Old Password:" msgstr "Contraseña antigua:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Conectado" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Usuarios conectados" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Usuarios conectados:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Solo se permiten las etiquetas o " #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Solo se permite el elemento en esta petición" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Solo miembros pueden consultar el archivo de mensajes de la sala" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Solo los moderadores y participantes pueden cambiar el asunto de esta sala" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Solo los moderadores pueden cambiar el asunto de esta sala" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Solo los moderadores pueden aprobar peticiones de voz" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Solo los ocupantes pueden enviar mensajes a la sala" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Solo los ocupantes pueden enviar solicitudes a la sala" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Solo los administradores del servicio tienen permiso para enviar mensajes de " "servicio" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opciones" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nombre de la organización" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unidad de la organización" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Conexiones S2S salientes" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Conexiones S2S salientes:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Se requieren privilegios de propietario de la sala" #: mod_offline:693 msgid "Packet" msgstr "Paquete" #: mod_irc:578 msgid "Parse error" msgstr "Error en el procesado" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "El procesado falló" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Contraseña" #: mod_configure:1130 msgid "Password Verification" msgstr "Verificación de la contraseña" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Verificación de la contraseña:" #: mod_irc:822 msgid "Password ~b" msgstr "Contraseña ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Contraseña:" #: mod_configure:998 msgid "Path to Dir" msgstr "Ruta al directorio" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Ruta al fichero" #: mod_roster:915 msgid "Pending" msgstr "Pendiente" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periodo: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Salas permanentes" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "La petición de Ping es incorrecta" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Ten en cuenta que estas opciones solo harán copia de seguridad de la base de " "datos Mnesia embebida. Si estás usando ODBC tendrás que hacer también copia " "de seguridad de tu base de datos SQL." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Por favor, espera un poco antes de enviar otra petición de voz" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Puerto" #: mod_irc:828 msgid "Port ~b" msgstr "Puerto ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Poseer el atributo 'ask' no está permitido por RFC6121" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocolo" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Petición de subscriptor de PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Servicio de Publicar-Subscribir" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Publicar elementos en un nodo de colección no está permitido" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "En esta sala no se permiten solicitudes a los miembros de la sala" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "Enviar solicitudes a otros usuarios está prohibido" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Copia en RAM y disco" #: mod_configure:889 msgid "RAM copy" msgstr "Copia en RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Error en la llamada RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Crudo" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "¿Está seguro de quere borrar el mensaje del dia?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "El receptor no está en la sala de conferencia" #: mod_register_web:275 msgid "Register" msgstr "Registrar" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Registrar una cuenta Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Usuarios registrados" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Usuarios registrados:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Apodos registrados" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registro en mod_irc para" #: mod_configure:889 msgid "Remote copy" msgstr "Copia remota" #: mod_roster:963 msgid "Remove" msgstr "Borrar" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Borrar todos los mensajes diferidos" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Eliminar usuario" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Reemplazado por una nueva conexión" #: mod_configure:1675 msgid "Resources" msgstr "Recursos" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Reiniciar" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Reiniciar el servicio" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restaurar" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaura copia de seguridad desde el fichero en " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Restaurar copia de seguridad binaria en el siguiente reinicio de ejabberd " "(requiere menos memoria que si instantánea):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Restaurar inmediatamente copia de seguridad binaria:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Restaurar copias de seguridad de texto plano inmediatamente:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Configuración de la sala" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Ocupantes de la sala" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Se te ha denegado crear la sala por política del servicio" #: mod_muc_log:1064 msgid "Room description" msgstr "Descripción de la sala" #: mod_muc_log:1027 msgid "Room title" msgstr "Título de la sala" #: mod_roster:1082 msgid "Roster" msgstr "Lista de contactos" #: mod_roster:334 msgid "Roster module has failed" msgstr "El módulo Roster ha fallado" #: mod_roster:968 msgid "Roster of " msgstr "Lista de contactos de " #: mod_configure:1671 msgid "Roster size" msgstr "Tamaño de la lista de contactos" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nodos funcionando" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "No está permitida la negociación SASL en este estado" #: mod_muc_log:468 msgid "Saturday" msgstr "sábado" #: mod_irc:582 msgid "Scan error" msgstr "Error de escaneo" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "El escaneo ha fallado" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Comprobación de script" #: mod_vcard:453 msgid "Search Results for " msgstr "Buscar resultados por " #: mod_vcard:427 msgid "Search users in " msgstr "Buscar usuarios en " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Enviar anuncio a todos los usuarios conectados" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Enviar anuncio a todos los usuarios conectados en todos los dominios" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Enviar anuncio a todos los usuarios" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Enviar anuncio a todos los usuarios en todos los dominios" #: mod_muc_log:481 msgid "September" msgstr "septiembre" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Conexión al Servidor Fallida" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Conexiones de servidor a subdominios locales están prohibidas" #: mod_irc:842 msgid "Server ~b" msgstr "Servidor ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Servidor:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Poner mensaje del dia y enviar a todos los usuarios conectados" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Poner mensaje del día en todos los dominios y enviar a los usuarios " "conectados" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Grupos Compartidos" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Mostrar Tabla Integral" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Mostrar Tabla Ordinaria" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Detener el servicio" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Algunos clientes Jabber pueden recordar tu contraseña en la máquina. Usa esa " "opción solo si confías en que la máquina que usas es segura." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Iniciar" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Iniciar módulos" #: mod_configure:936 msgid "Start Modules at " msgstr "Iniciar módulos en " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Estadísticas" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Estadísticas de ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Detener" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Detener módulos" #: mod_configure:918 msgid "Stop Modules at " msgstr "Detener módulos en " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nodos detenidos" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Tipo de almacenamiento" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Guardar copia de seguridad binaria:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Guardar copia de seguridad en texto plano:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Asunto" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Enviar" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Enviado" #: mod_roster:914 msgid "Subscription" msgstr "Subscripción" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Las subscripciones no están permitidas" #: mod_muc_log:469 msgid "Sunday" msgstr "domingo" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Ese apodo ya está siendo usado por otro ocupante" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "El apodo ya está registrado por otra persona" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "El CAPTCHA es válido." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "La verificación de CAPTCHA ha fallado" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" "La característica solicitada no está soportada por la sala de conferencia" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "La contraseña contiene caracteres inaceptables" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "La contraseña es demasiado débil" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "La contraseña de tu cuenta Jabber se ha cambiado correctamente." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "La solicitud está permitida solo para usuarios locales" #: mod_roster:203 msgid "The query must not contain elements" msgstr "La solicitud no debe contener elementos " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "El paquete DEBE contener solo un elemento , un elemento , " "o un elemento " #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Hubo un error cambiando la contraseña." #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Hubo uno error al crear la cuenta:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Hubo un error borrando la cuenta." #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "No importa si usas mayúsculas: macbeth es lo mismo que MacBeth y Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Esta página te permite crear una cuenta Jabber este servidor Jabber. Tu JID " "(Jabber IDentificador) será de la forma: nombredeusuario@servidor. Por favor " "lee detenidamente las instrucciones para rellenar correctamente los campos." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Esta página te permite borrar tu cuenta Jabber en este servidor Jabber." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Sala no anónima" #: mod_muc_log:466 msgid "Thursday" msgstr "jueves" #: mod_offline:690 msgid "Time" msgstr "Fecha" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Retraso temporal" #: mod_offline:692 msgid "To" msgstr "Para" #: mod_register:215 msgid "To register, visit ~s" msgstr "Para registrarte, visita ~s" #: mod_configure:709 msgid "To ~s" msgstr "A ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Token TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Valor demasiado largo para el atributo 'xml:lang'" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "Demasiados elementos " #: mod_privacy:164 msgid "Too many elements" msgstr "Demasiados elementos " #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Demasiadas peticiones de CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Demasiados bytestreams activos" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Demasiados mensajes sin haber reconocido recibirlos" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Demasiados usuarios en esta sala" #: mod_register:355 msgid "Too many users registered" msgstr "Demasiados usuarios registrados" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Salas totales" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Se ha exedido el límite de tráfico" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transacciones abortadas:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transacciones finalizadas:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transacciones registradas:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transacciones reiniciadas:" #: mod_muc_log:464 msgid "Tuesday" msgstr "martes" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "No se pudo generar un CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "No se ha podido registrar la ruta en este dominio local existente" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "No autorizado" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Acción inesperada" #: mod_register_web:488 msgid "Unregister" msgstr "Borrar" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Borrar una cuenta Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "Elemento no soportado" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Petición MIX no soportada" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Actualizar" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Actualizar mensaje del dia, pero no enviarlo" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Actualizar el mensaje del día en todos los dominos (pero no enviarlo)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plan de actualización" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Script de actualización" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Actualizar ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Tiempo desde el inicio:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "Prohibido el uso de STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Es obligatorio usar STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Usuario" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Usuario (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Administración de usuarios" #: mod_register:345 msgid "User already exists" msgstr "El usuario ya existe" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "La parte de usuario del JID en 'from' está vacía" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Sesión de usuario no encontrada" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Sesión de usuario terminada" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Usuario ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Nombre de usuario:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Usuarios" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Última actividad de los usuarios" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Los usuarios no tienen permitido crear cuentas con tanta rapidez" #: mod_roster:954 msgid "Validate" msgstr "Validar" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "El valor 'get' del atributo 'type' no está permitido" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "El valor 'set' del atributo 'type' no está permitido" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "El valor de '~s' debería ser booleano" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "El valor de '~s' debería ser una fecha" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "El valor de '~s' debería ser un entero" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Dominios Virtuales" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Los visitantes no tienen permitido cambiar sus apodos en esta sala" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Los visitantes no pueden enviar mensajes a todos los ocupantes" #: mod_muc_room:3879 msgid "Voice request" msgstr "Petición de voz" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Las peticiones de voz están desactivadas en esta sala" #: mod_muc_log:465 msgid "Wednesday" msgstr "miércoles" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Puedes cambiar tu contraseña después, usando un cliente Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Has sido bloqueado en esta sala" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Has entrado en demasiadas salas de conferencia" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Debes rellenar el campo \"Apodo\" en el formulario" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Necesitas un cliente con soporte de x:data y CAPTCHA para registrarte" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Necesitas un cliente con soporte de x:data para poder registrar el apodo" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Necesitas un cliente con soporte de x:data para configurar las opciones de " "mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Necesitas un cliente con soporte de x:data para poder buscar" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "No tienes permitido crear nodos" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Tu cuenta Jabber se ha creado correctamente." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Tu cuenta Jabber se ha borrado correctamente." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Tu lista de privacidad activa ha denegado el envío de este paquete." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Tu cola de mensajes diferidos de contactos está llena. El mensaje se ha " "descartado." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Tus mensajes a ~s están siendo bloqueados. Para desbloquearlos, visita ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Módulo de IRC para ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Módulo de MUC para ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Servicio Multicast de ejabberd" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Módulo de Publicar-Subscribir de ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Módulo SOCKS5 Bytestreams para ejabberd" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Módulo vCard para ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "ha sido bloqueado" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "ha sido expulsado" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "ha sido expulsado porque el sistema se va a detener" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "ha sido expulsado por un cambio de su afiliación" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "ha sido expulsado porque la sala es ahora solo para miembros" #: mod_muc_log:410 msgid "is now known as" msgstr "se cambia el nombre a" #: mod_muc_log:370 msgid "joins the room" msgstr "entra en la sala" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "sale de la sala" #: mod_muc_room:3856 msgid "private, " msgstr "privado" #: mod_muc_room:3955 msgid "the password is" msgstr "la contraseña es" #: mod_vcard:292 msgid "vCard User Search" msgstr "Buscar vCard de usuario" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Configuración de las Regla de Acceso ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s te invita a la sala ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Cola de mensajes diferidos de ~s" #, fuzzy #~ msgid "" #~ "It is not allowed to send error messages to the room. The participant " #~ "(~s) has sent an error message (~s) and got kicked from the roOOom" #~ msgstr "" #~ "No está permitido enviar mensajes de error a la sala. Este participante " #~ "(~s) ha enviado un mensaje de error (~s) y fue expulsado de la sala" #~ msgid "Server" #~ msgstr "Servidor" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Demasiadas (~p) autenticaciones fallidas de esta dirección IP (~s). La " #~ "dirección será desbloqueada en ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Por favor especifica el tamaño del fichero." #~ msgid "Please specify file name." #~ msgstr "Por favor especifica el nombre del fichero." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Esta dirección IP está en la lista negra en ~s" #~ msgid "Empty Rooms" #~ msgstr "Salas vacías" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "El Jabber ID ~s no es válido" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiliación no válida: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Rol no válido: ~s" #~ msgid "No limit" #~ msgstr "Sin límite" #~ msgid "Present real Jabber IDs to" #~ msgstr "Los Jabber ID reales pueden verlos" #~ msgid "moderators only" #~ msgstr "solo moderadores" #~ msgid "anyone" #~ msgstr "cualquiera" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Roles para los que sí se difunde su Presencia" #~ msgid "Moderator" #~ msgstr "Moderador" #~ msgid "Participant" #~ msgstr "Participante" #~ msgid "Visitor" #~ msgstr "Visitante" #~ msgid "nobody" #~ msgstr "nadie" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permitir a los visitantes enviar peticiones de voz" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Intervalo mínimo entre peticiones de voz (en segundos)" #~ msgid "Enable message archiving" #~ msgstr "Activar el almacenamiento de mensajes" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Excluir Jabber IDs de las pruebas de CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Necesitas un cliente con soporte de x:data para configurar la sala" #~ msgid "Number of occupants" #~ msgstr "Número de ocupantes" #~ msgid "User JID" #~ msgstr "Jabber ID del usuario" #~ msgid "Grant voice to this person?" #~ msgstr "¿Conceder voz a esta persona?" #~ msgid "Node ID" #~ msgstr "Nodo ID" #~ msgid "Subscriber Address" #~ msgstr "Dirección del subscriptor" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "" #~ "¿Deseas permitir a este Jabber ID que se subscriba a este nodo PubSub?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Enviar contenidos junto con las notificaciones de eventos" #~ msgid "Deliver event notifications" #~ msgstr "Entregar notificaciones de eventos" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Notificar subscriptores cuando cambia la configuración del nodo" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Notificar subscriptores cuando el nodo se borra" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Notificar subscriptores cuando los elementos se borran del nodo" #~ msgid "Persist items to storage" #~ msgstr "Persistir elementos al almacenar" #~ msgid "A friendly name for the node" #~ msgstr "Un nombre sencillo para el nodo" #~ msgid "Max # of items to persist" #~ msgstr "Máximo # de elementos que persisten" #~ msgid "Whether to allow subscriptions" #~ msgstr "Permitir subscripciones" #~ msgid "Specify the access model" #~ msgstr "Especifica el modelo de acceso" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Grupos de contactos que pueden suscribirse" #~ msgid "Specify the publisher model" #~ msgstr "Especificar el modelo del publicante" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Borra todos los elementos cuando el publicador relevante se desconecta" #~ msgid "Specify the event message type" #~ msgstr "Especifica el tipo del mensaje de evento" #~ msgid "Max payload size in bytes" #~ msgstr "Máximo tamaño del contenido en bytes" #~ msgid "When to send the last published item" #~ msgstr "Cuando enviar el último elemento publicado" #~ msgid "Only deliver notifications to available users" #~ msgstr "Solo enviar notificaciones a los usuarios disponibles" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Las colecciones a las que un nodo está afiliado" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Rellena campos para buscar usuarios Jabber que concuerden" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Servidores S2S salientes:" #~ msgid "Delete" #~ msgstr "Eliminar" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Este participante ha sido expulsado de la sala porque envió un mensaje de " #~ "error" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Este participante ha sido expulsado de la sala porque envió un mensaje de " #~ "error a otro participante" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Este participante ha sido expulsado de la sala porque envió una presencia " #~ "de error" ejabberd-18.01/priv/msgs/ja.po0000644000232200023220000020262413225664356016534 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: ejabberd 2.1.x\n" "PO-Revision-Date: 2012-04-16 15:48+0900\n" "Last-Translator: Tsukasa Hamano \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Japanese (日本語)\n" "X-Additional-Translator: Tsukasa Hamano \n" "X-Additional-Translator: Mako N \n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " は件名を設定しました: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "このチャットルームに入るにはパスワードが必要です" #: ejabberd_oauth:448 msgid "Accept" msgstr "許可" #: mod_configure:1109 msgid "Access Configuration" msgstr "アクセス設定" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "アクセスコントロールリスト設定" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "アクセスコントロールリスト" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "アクセスルール" #: mod_configure:1095 msgid "Access control lists" msgstr "アクセスコントロールリスト" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "サービスポリシーによってアクセスが禁止されました" #: mod_configure:1113 msgid "Access rules" msgstr "アクセスルール" #: mod_configure:1772 msgid "Action on user" msgstr "ユーザー操作" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Jabber ID を追加" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "新規追加" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "ユーザーを追加" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "管理" #: mod_configure:1767 msgid "Administration of " msgstr "管理: " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "管理者権限が必要です" #: mod_configure:507 msgid "All Users" msgstr "全ユーザー" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "すべて" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "ユーザーによる件名の変更を許可" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "ユーザーによる他のユーザーへのクエリーを許可" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "ユーザーによる招待を許可" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "ユーザーによるプライベートメッセージの送信を許可" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "傍聴者のニックネームの変更を許可" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "傍聴者によるプライベートメッセージの送信を次の相手に許可" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "傍聴者によるプレゼンス更新のステータス文の送信を許可" #: mod_announce:611 msgid "Announcements" msgstr "アナウンス" #: mod_muc_log:476 msgid "April" msgstr "4月" #: mod_muc_log:480 msgid "August" msgstr "8月" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "バックアップ" #: mod_configure:584 msgid "Backup Management" msgstr "バックアップ管理" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "バックアップ: ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "ファイルにバックアップ: " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "不正なフォーマット" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "誕生日" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA ウェブページ" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "CPU時間:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "パスワードを変更" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "パスワードを変更" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "使用できない文字:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "使用できない文字:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "使用できない文字:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "チャットルームの設定が変更されました" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "チャットルームを作りました" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "チャットルームを終了しました" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "チャットルームを開始しました" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "チャットルームを停止しました" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "チャットルーム" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "サーバーに登録するユーザー名とパスワードを選択してください" #: mod_configure:920 msgid "Choose modules to stop" msgstr "停止するモジュールを選択" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "テーブルのストレージタイプを選択" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "このエントリを承認するかどうかを選択してください" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "都道府県" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "コマンド" #: mod_muc:461 msgid "Conference room does not exist" msgstr "会議室は存在しません" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "設定" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "チャットルーム ~s の設定" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "接続リソース:" #: mod_irc:526 msgid "Connections parameters" msgstr "接続パラメーター" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "国" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "データーベース" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "データーベーステーブル設定 " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "データーベーステーブル: ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "データーベース" #: mod_muc_log:484 msgid "December" msgstr "12月" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "デフォルトのユーザーは参加者" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "選択した項目を削除" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "ユーザーを削除" #: mod_announce:629 msgid "Delete message of the day" msgstr "お知らせメッセージを削除" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "全ホストのお知らせメッセージを削除" #: mod_shared_roster:900 msgid "Description:" msgstr "説明:" #: mod_configure:889 msgid "Disc only copy" msgstr "ディスクだけのコピー" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "表示グループ:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "パスワードは誰にも教えないようにしてください。Jabber サーバーの管理者があなた" "にパスワードを尋ねることはありません。" #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "テキストファイルにバックアップ: " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "テキストファイルに出力" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "プロパティを編集" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "発言権の要求を承認または却下します。" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "要素" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "メールアドレス" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "パスワードは" #: mod_muc_log:1055 msgid "Enable logging" msgstr "ロギングを有効" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "サーバーのエンコーディング ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "エンドユーザーセッション" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "{モジュール, [オプション]}のリストを入力してください" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "登録するニックネームを入力してください" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "バックアップファイルのパスを入力してください" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "jabberd14 spool ディレクトリのディレクトリを入力してください" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "jabberd14 spool ファイルのパスを入力してください" #: mod_configure:974 msgid "Enter path to text file" msgstr "テキストファイルのパスを入力してください" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "見えているテキストを入力してください" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "IRC サーバーに接続先するためのユーザー名と文字エンコーディングを入力してくだ" "さい。'Next' を押して次の項目に進みます。'Complete' を押すと設定が保存されま" "す。" #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "IRC サーバーに接続先するために使用するユーザー名、文字エンコーディング、ポー" "ト、パスワードを入力してください" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "エラー" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "例: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net" "\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "すべてのテーブルをSQL形式でファイルにエクスポート: " #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "サーバーにあるすべてのユーザーデータを PIEFXIS ファイルにエクスポート " "(XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "ホストのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "発言権要求の承認から JID を取り出すことに失敗しました" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "姓" #: mod_muc_log:474 msgid "February" msgstr "2月" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "項目を入力してユーザーを検索を行えます (* を使用すると部分文字列にマッチしま" "す)" #: mod_muc_log:467 msgid "Friday" msgstr "金曜日" #: mod_offline:691 msgid "From" msgstr "差出人" #: mod_configure:723 msgid "From ~s" msgstr "From ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "氏名" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "オンラインユーザー数を取得" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "登録ユーザー数を取得" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "最終ログイン時間を取得" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "パスワードを取得" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "ユーザー統計を取得" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "ミドルネーム" #: mod_shared_roster:923 msgid "Group " msgstr "グループ" #: mod_roster:916 msgid "Groups" msgstr "グループ" #: ejabberd_web_admin:1365 msgid "Host" msgstr "ホスト" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP アドレス" #: mod_irc:439 msgid "IRC Transport" msgstr "IRCトランスポート" #: mod_irc:496 msgid "IRC Username" msgstr "IRC ユーザー名" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC チャンネル (先頭に#は不要)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "ノードが見つかりません" #: mod_irc:673 msgid "IRC server" msgstr "IRC サーバー" #: mod_irc:756 msgid "IRC settings" msgstr "IRC 設定" #: mod_irc:766 msgid "IRC username" msgstr "IRC ユーザー名" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" "ここに CAPTCHA 画像が表示されない場合、ウェブページを参照してください。" #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "別のポートやパスワード、文字エンコーディングを使用したい場合、'{\"irc server" "\", \"encoding\", port, \"password\"}' という形式のリストを入力してください。" "デフォルトでエンコーディングは \"~s\" を使用し、ポートは ~p、パスワードは空に" "なっています。" #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "ディレクトリインポート" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "ファイルからインポート" #: mod_configure:982 msgid "Import User from File at " msgstr "ファイルからユーザーをインポート: " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "jabberd14 Spool ファイルからユーザーをインポート" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "ディレクトリからユーザーをインポート: " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "ユーザーデータを jabberd14 Spool ファイルからインポート:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "ユーザーデータを PIEFXIS ファイルからインポート (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "ユーザーデータを jabberd14 Spool ディレクトリからインポート:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "誤ったメッセージタイプです" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "内向き s2s コネクション:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "パスワードが違います" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "パスワードが違います" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "この会議では、発言権の要求はできません" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "このルームにエラーメッセージを送ることは許可されていません。参加者(~s)はエ" "ラーメッセージを(~s)を送信してルームからキックされました。" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "プライベートメッセージを送信することはできません" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "" "種別が\"groupchat\" であるプライベートメッセージを送信することはできません" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "この会議にプライベートメッセージを送信することはできません" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Jabber アカウント登録" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "1月" #: mod_irc:665 msgid "Join IRC channel" msgstr "IRC チャンネルに参加" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "この IRC チャンネルに参加します。" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Jabber ID: ~s でこの IRC チャンネルに参加" #: mod_muc_log:479 msgid "July" msgstr "7月" #: mod_muc_log:478 msgid "June" msgstr "6月" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "活動履歴" #: mod_configure:1646 msgid "Last login" msgstr "最終ログイン" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "先月" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "去年" #: mod_configure:941 msgid "List of modules to start" msgstr "起動モジュールの一覧" #: mod_muc_admin:373 msgid "List of rooms" msgstr "チャットルームの一覧" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Listen ポート" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Listen ポート " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "低レベル更新スクリプト" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "参加者一覧を公開" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "チャットルームを CAPTCHA で保護" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "チャットルームをメンバーのみに制限" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "チャットルームをモデレート化" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "チャットルームをパスワードで保護" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "チャットルームを永続化" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "チャットルームを検索可" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "IRC ユーザー名" #: mod_muc_log:475 msgid "March" msgstr "3月" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "最大在室者数" #: mod_muc_log:477 msgid "May" msgstr "5月" #: mod_shared_roster:907 msgid "Members:" msgstr "メンバー:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "このチャットルームに入るにはメンバーでなければなりません" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "パスワードは記憶するか、紙に書いて安全な場所に保管してください。もしあなたが" "パスワードを忘れてしまった場合、Jabber ではパスワードのリカバリを自動的に行う" "ことはできません。" #: ejabberd_web_admin:1954 msgid "Memory" msgstr "メモリ" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "本文" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "ミドルネーム" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "モデレーター権限が必要です" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "更新されたモジュール" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "モジュール" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "モジュール" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "モジュール ~p" #: mod_muc_log:463 msgid "Monday" msgstr "月曜日" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "マルチユーザーチャット" #: mod_multicast:267 msgid "Multicast" msgstr "マルチキャスト" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "名" #: mod_shared_roster:896 msgid "Name:" msgstr "名前:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "なし" #: mod_register_web:377 msgid "New Password:" msgstr "新しいパスワード:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "ニックネーム" #: mod_muc:722 msgid "Nickname Registration at " msgstr "ニックネーム登録: " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "ニックネーム ~s はこのチャットルームにいません" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "ノードが見つかりません" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "データなし" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "アナウンスメッセージはありませんでした" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "ノードが見つかりません" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "ノードが見つかりません" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "ノードが見つかりません" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "ノードが見つかりません" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "ノード ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "ノード" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "なし" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "見つかりません" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "11月" #: mod_configure:1212 msgid "Number of online users" msgstr "オンラインユーザー数" #: mod_configure:1202 msgid "Number of registered users" msgstr "登録ユーザー数" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "10月" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "オフラインメッセージ" #: mod_offline:761 msgid "Offline Messages:" msgstr "オフラインメッセージ:" #: mod_register_web:373 msgid "Old Password:" msgstr "古いパスワード:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "オンライン" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "オンラインユーザー" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "オンラインユーザー:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "メンバーのみがこのルームのアーカイブを取得できます" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "モデレーターと参加者のみがチャットルームの件名を変更できます" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "モデレーターのみがチャットルームの件名を変更できます" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "モデレーターだけが発言権の要求を承認できます" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "在室者のみがこの会議にメッセージを送ることができます" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "在室者のみが会議にクエリーを送信することができます" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "サービス管理者のみがサービスメッセージを送信できます" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "オプション" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "会社名" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "部署名" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "外向き s2s コネクション" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "外向き s2s コネクション:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "主宰者の権限が必要です" #: mod_offline:693 msgid "Packet" msgstr "パケット" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "パスワード" #: mod_configure:1130 msgid "Password Verification" msgstr "パスワード (確認)" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "パスワード (確認):" #: mod_irc:822 msgid "Password ~b" msgstr "パスワード ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "パスワード:" #: mod_configure:998 msgid "Path to Dir" msgstr "ディレクトリのパス" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "ファイルのパス" #: mod_roster:915 msgid "Pending" msgstr "保留" #: ejabberd_web_admin:994 msgid "Period: " msgstr "期間: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "永続チャットルーム" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "これらのオプションは組み込みの Mnesia データーベースのバックアップのみを行う" "ことに注意してください。もし ODBC モジュールを使用している場合は、SQL デー" "ターベースのバックアップを別に行う必要があります。" #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "新しい発言権の要求を送るまで少し間をおいてください" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "ポート" #: mod_irc:828 msgid "Port ~b" msgstr "ポート ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "プロトコル" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub 購読者のリクエスト" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "このチャットルームでは、会議のメンバーへのクエリーは禁止されています" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM, ディスクコピー" #: mod_configure:889 msgid "RAM copy" msgstr "RAM コピー" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "RPC 呼び出しエラー" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Raw" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "本当にお知らせメッセージを削除しますか ?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "受信者はこの会議室にいません" #: mod_register_web:275 msgid "Register" msgstr "登録" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Jabber アカウントを登録" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "登録ユーザー" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "登録ユーザー:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "登録ニックネーム" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "mod_irc での登録: " #: mod_configure:889 msgid "Remote copy" msgstr "リモートコピー" #: mod_roster:963 msgid "Remove" msgstr "削除" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "すべてのオフラインメッセージを削除" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "ユーザーを削除" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "新しいコネクションによって置き換えられました" #: mod_configure:1675 msgid "Resources" msgstr "リソース" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "再起動" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "サービスを再起動" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "リストア" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "ファイルからバックアップをリストア: " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "ejabberd の再起動時にバイナリバックアップからリストア (メモリ少):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "直ちにバイナリバックアップからリストア:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "直ちにプレーンテキストバックアップからリストア:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "チャットルームの設定" #: mod_muc_log:892 msgid "Room Occupants" msgstr "在室者" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "サービスポリシーによってチャットルームの作成が禁止されています" #: mod_muc_log:1064 msgid "Room description" msgstr "チャットルームの説明" #: mod_muc_log:1027 msgid "Room title" msgstr "チャットルームのタイトル" #: mod_roster:1082 msgid "Roster" msgstr "名簿" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "名簿: " #: mod_configure:1671 msgid "Roster size" msgstr "名簿サイズ" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "起動ノード" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "土曜日" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "キャプチャのテストに失敗しました" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "スクリプトチェック" #: mod_vcard:453 msgid "Search Results for " msgstr "検索結果: " #: mod_vcard:427 msgid "Search users in " msgstr "ユーザーの検索: " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "すべてのオンラインユーザーにアナウンスを送信" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "全ホストのオンラインユーザーにアナウンスを送信" #: mod_announce:613 msgid "Send announcement to all users" msgstr "すべてのユーザーにアナウンスを送信" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "全ホストのユーザーにアナウンスを送信" #: mod_muc_log:481 msgid "September" msgstr "9月" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "サーバー ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "サーバー:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "お知らせメッセージを設定し、オンラインユーザーに送信" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "全ホストのお知らせメッセージを設定し、オンラインユーザーに送信" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "共有名簿グループ" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "累積の表を表示" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "通常の表を表示" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "サービスを停止" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Jabber クライアントはコンピューターにパスワードを記憶できます。コンピューター" "が安全であると信頼できる場合にのみ、この機能を使用してください。" #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "開始" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "モジュールを起動" #: mod_configure:936 msgid "Start Modules at " msgstr "モジュールを開始: " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "統計" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "~p の統計" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "停止" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "モジュールを停止" #: mod_configure:918 msgid "Stop Modules at " msgstr "モジュールを停止: " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "停止ノード" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "ストレージタイプ" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "バイナリバックアップを保存:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "プレーンテキストバックアップを保存:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "件名" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "送信" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "送信完了" #: mod_roster:914 msgid "Subscription" msgstr "認可" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "日曜日" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "そのニックネームは既にほかの在室者によって使用されています" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "ニックネームはほかの人によって登録されています" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "CAPTCHA は有効です。" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "CAPTCHA 検証は失敗しました" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "このパスワードは単純過ぎます" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Jabber アカウントのパスワード変更に成功しました。" #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "パスワードの変更中にエラーが発生しました: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "アカウントの作成中にエラーが発生しました: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "アカウントの削除中にエラーが発生しました: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "大文字と小文字は区別しません: macbeth は MacBeth や Macbeth と同じです。" #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "ここはこの Jabber サーバーにアカウントを作成するページです。あなたの JID " "(JabberID) は username@server のような形式になります。注意事項どおり、正しく" "項目を記入してください。" #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "このページはサーバー上のJabberアカウントを削除するページです。" #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "このチャットルームは非匿名です" #: mod_muc_log:466 msgid "Thursday" msgstr "木曜日" #: mod_offline:690 msgid "Time" msgstr "時間" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "遅延時間" #: mod_offline:692 msgid "To" msgstr "To" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "宛先 ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "CAPTCHA 要求が多すぎます" #: mod_proxy65_service:223 #, fuzzy msgid "Too many active bytestreams" msgstr "多くのスタンザが応答していません" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "多くのスタンザが応答していません" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "この会議では、発言権の要求はできません" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "チャットルーム数" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "トラフィックレートの制限を超えました" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "トランザクションの失敗:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "トランザクションのコミット:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "トランザクションのログ: " #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "トランザクションの再起動:" #: mod_muc_log:464 msgid "Tuesday" msgstr "火曜日" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "CAPTCHA を生成できません" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "認証されていません" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "削除" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Jabber アカウントを削除" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "更新" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "お知らせメッセージを更新 (送信しない)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "全ホストのお知らせメッセージを更新 (送信しない)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "更新計画" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "スクリプトの更新" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "更新 ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "起動時間:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "STARTTLS の使用が必須です" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "STARTTLS の使用が必須です" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "ユーザー" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "ユーザー管理" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "ノードが見つかりません" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "ユーザー ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "ユーザー名:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "ユーザー" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "ユーザーの活動履歴" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "それほど速くアカウントを登録することはできません" #: mod_roster:954 msgid "Validate" msgstr "検証" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "バーチャルホスト" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "傍聴者はこのチャットルームでニックネームを変更することはできません" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "傍聴者はすべての在室者にメッセージを送信することはできません" #: mod_muc_room:3879 msgid "Voice request" msgstr "発言権を要求" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "この会議では、発言権の要求はできません" #: mod_muc_log:465 msgid "Wednesday" msgstr "水曜日" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "あなたは後で Jabber クライアントを使用してパスワードを変更できます。" #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "あなたはこのチャットルームからバンされています" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "フォームの\"ニックネーム\"欄を入力する必要があります" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "登録を行うには x:data と CAPTCHA をサポートするクライアントが必要です" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "ニックネームを登録するには x:data をサポートするクライアントが必要です" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "mod_irc の設定には x:data をサポートするクライアントが必要です" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "検索を行うためには x:data をサポートするクライアントが必要です" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "プライベートメッセージを送信することはできません" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Jabber アカウントの作成に成功しました。" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Jabber アカウントの削除に成功しました。" #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "あなたのプライバシーリストはこのスタンザのルーティングを拒否しました。" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "相手先のオフラインメッセージキューが一杯です。このメッセージは破棄されます。" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "~s 宛のメッセージはブロックされています。解除するにはこちらを見てください ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC module" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUCモジュール" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberdマルチキャストサービス" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe モジュール" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams モジュール" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd ウェブ管理" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard モジュール" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "はバンされました" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "はキックされました" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "はシステムシャットダウンのためキックされました" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "は分掌が変更されたためキックされました" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "はチャットルームがメンバー制に変更されたためキックされました" #: mod_muc_log:410 msgid "is now known as" msgstr "は名前を変更しました: " #: mod_muc_log:370 msgid "joins the room" msgstr "がチャットルームに参加しました" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "がチャットルームから退出しました" #: mod_muc_room:3856 msgid "private, " msgstr "プライベート、" #: mod_muc_room:3955 msgid "the password is" msgstr "パスワードは" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard検索" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s アクセスルール設定" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s はあなたをチャットルーム ~s に招待しています" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s' のオフラインメッセージキュー" #~ msgid "No resource provided" #~ msgstr "リソースが指定されていません" #~ msgid "Server" #~ msgstr "サーバー" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "~p回の認証に失敗しました。このIPアドレス(~s)は~s UTCまでブロックされます。" #~ msgid "Please specify file size." #~ msgstr "ファイルサイズを指定してください。" #~ msgid "Please specify file name." #~ msgstr "ファイル名を指定してください。" #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "このIPアドレスはアクセスを禁止されています ~s" #~ msgid "Empty Rooms" #~ msgstr "空のルーム" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s は無効です" #~ msgid "Invalid affiliation: ~s" #~ msgstr "無効な分掌です: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "無効な役です: ~s" #~ msgid "No limit" #~ msgstr "制限なし" #~ msgid "Present real Jabber IDs to" #~ msgstr "本当の Jabber ID を公開" #~ msgid "moderators only" #~ msgstr "モデレーターにのみ" #~ msgid "anyone" #~ msgstr "誰にでも" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "プレゼンスをブロードキャストするロール" #~ msgid "Moderator" #~ msgstr "モデレーター" #~ msgid "Participant" #~ msgstr "参加者" #~ msgid "Visitor" #~ msgstr "傍聴者" #~ msgid "nobody" #~ msgstr "誰にも許可しない" #~ msgid "Allow visitors to send voice requests" #~ msgstr "傍聴者による発言権の要求を許可" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "発言権の要求の最小時間間隔 (秒)" #~ msgid "Enable message archiving" #~ msgstr "メッセージアーカイブを有効化" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "CAPTCHA 入力を免除する Jabber ID" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "チャットルームを設定するには x:data をサポートするクライアントが必要です" #~ msgid "Number of occupants" #~ msgstr "在室者の数" #~ msgid "User JID" #~ msgstr "ユーザー JID" #~ msgid "Grant voice to this person?" #~ msgstr "この人に発言権を与えますか ?" #~ msgid "Node ID" #~ msgstr "ノードID" #~ msgid "Subscriber Address" #~ msgstr "購読者のアドレス" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "この Jabber ID に、この pubsubノードの購読を許可しますか ?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "イベント通知と同時にペイロードを配送する" #~ msgid "Deliver event notifications" #~ msgstr "イベント通知を配送する" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "ノード設定に変更があった時に購読者へ通知する" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "ノードが削除された時に購読者へ通知する" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "アイテムがノードから消された時に購読者へ通知する" #~ msgid "Persist items to storage" #~ msgstr "アイテムをストレージに保存する" #~ msgid "A friendly name for the node" #~ msgstr "ノードのフレンドリネーム" #~ msgid "Max # of items to persist" #~ msgstr "アイテムの最大保存数" #~ msgid "Whether to allow subscriptions" #~ msgstr "購読を許可するかどうか" #~ msgid "Specify the access model" #~ msgstr "アクセスモデルを設定する" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "名簿グループは購読を許可しました" #~ msgid "Specify the publisher model" #~ msgstr "公開モデルを指定する" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "公開者がオフラインになるときに、すべてのアイテムを削除" #~ msgid "Specify the event message type" #~ msgstr "イベントメッセージ種別を設定" #~ msgid "Max payload size in bytes" #~ msgstr "最大ぺイロードサイズ (byte)" #~ msgid "When to send the last published item" #~ msgstr "最後の公開アイテムを送信するタイミングで" #~ msgid "Only deliver notifications to available users" #~ msgstr "有効なユーザーにのみ告知を送信する" #~ msgid "The collections with which a node is affiliated" #~ msgstr "提携されたノードの集合です" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "項目を入力してユーザーを検索してください" #~ msgid "Outgoing s2s Servers:" #~ msgstr "外向き s2s サービス:" #~ msgid "Delete" #~ msgstr "削除" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "エラーメッセージを送信したため、この参加者はキックされました" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "他の参加者にエラーメッセージを送信したため、この参加者はキックされました" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "エラープレゼンスを送信したため、この参加者はキックされました" #~ msgid "ejabberd virtual hosts" #~ msgstr "ejabberd バーチャルホスト" #~ msgid "Encodings" #~ msgstr "エンコーディング" #~ msgid "(Raw)" #~ msgstr "(Raw)" #~ msgid "Specified nickname is already registered" #~ msgstr "指定されたニックネームは既に登録されています" #~ msgid "Size" #~ msgstr "サイズ" ejabberd-18.01/priv/msgs/fr.msg0000644000232200023220000006206413225664356016723 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Configuration d'accès"}. {"Access Control List Configuration","Configuration des droits (ACL)"}. {"Access control lists","Droits (ACL)"}. {"Access Control Lists","Droits (ACL)"}. {"Access denied by service policy","L'accès au service est refusé"}. {"Access rules","Règles d'accès"}. {"Access Rules","Règles d'accès"}. {"Action on user","Action sur l'utilisateur"}. {"Add Jabber ID","Ajouter un Jabber ID"}. {"Add New","Ajouter"}. {"Add User","Ajouter un utilisateur"}. {"Administration","Administration"}. {"Administration of ","Administration de "}. {"Administrator privileges required","Les droits d'administrateur sont nécessaires"}. {"All activity","Toute activité"}. {"Allow users to change the subject","Autoriser les utilisateurs à changer le sujet"}. {"Allow users to query other users","Permettre aux utilisateurs d'envoyer des requêtes aux autres utilisateurs"}. {"Allow users to send invites","Permettre aux utilisateurs d'envoyer des invitations"}. {"Allow users to send private messages","Autoriser les utilisateurs à envoyer des messages privés"}. {"Allow visitors to change nickname","Autoriser les visiteurs à changer de pseudo"}. {"Allow visitors to send private messages to","Autoriser les visiteurs à envoyer des messages privés"}. {"Allow visitors to send status text in presence updates","Autoriser les visiteurs à envoyer un message d'état avec leur présence"}. {"All Users","Tous les utilisateurs"}. {"Announcements","Annonces"}. {"A password is required to enter this room","Un mot de passe est nécessaire pour accèder à ce salon"}. {"April","Avril"}. {"August","Août"}. {"Backup Management","Gestion des sauvegardes"}. {"Backup of ~p","Sauvegarde de ~p"}. {"Backup","Sauvegarde"}. {"Backup to File at ","Sauvegarde sur fichier sur "}. {"Bad format","Mauvais format"}. {"Birthday","Date d'anniversaire"}. {"CAPTCHA web page","Page web de CAPTCHA"}. {"Change Password","Modifier le mot de passe"}. {"Change User Password","Changer le mot de passe de l'utilisateur"}. {"Characters not allowed:","Caractères non-autorisés :"}. {"Chatroom configuration modified","Configuration du salon modifiée"}. {"Chatroom is created","Le salon de discussion est créé"}. {"Chatroom is destroyed","Le salon de discussion est détruit"}. {"Chatroom is started","Le salon de discussion a démarré"}. {"Chatroom is stopped","Le salon de discussion est stoppé"}. {"Chatrooms","Salons de discussion"}. {"Choose a username and password to register with this server","Choisissez un nom d'utilisateur et un mot de passe pour s'enregistrer sur ce serveur"}. {"Choose modules to stop","Sélectionnez les modules à arrêter"}. {"Choose storage type of tables","Choisissez un type de stockage pour les tables"}. {"Choose whether to approve this entity's subscription.","Accepter cet abonnement ?"}. {"City","Ville"}. {"Commands","Commandes"}. {"Conference room does not exist","La salle de conférence n'existe pas"}. {"Configuration","Configuration"}. {"Configuration of room ~s","Configuration pour le salon ~s"}. {"Connected Resources:","Ressources connectées:"}. {"Connections parameters","Paramètres de connexion"}. {"Country","Pays"}. {"CPU Time:","Temps CPU :"}. {"Database","Base de données"}. {"Database Tables at ~p","Tables de base de données sur ~p"}. {"Database Tables Configuration at ","Configuration des tables de base de données sur "}. {"December","Décembre"}. {"Default users as participants","Les utilisateurs sont par défaut participant"}. {"Delete message of the day on all hosts","Supprimer le message du jour sur tous les domaines"}. {"Delete message of the day","Supprimer le message du jour"}. {"Delete Selected","Suppression des éléments sélectionnés"}. {"Delete User","Supprimer l'utilisateur"}. {"Description:","Description :"}. {"Disc only copy","Copie sur disque uniquement"}. {"Displayed Groups:","Groupes affichés :"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ne révélez votre mot de passe à personne, pas même l'administrateur de ce serveur."}. {"Dump Backup to Text File at ","Enregistrer la sauvegarde dans un fichier texte sur "}. {"Dump to Text File","Sauvegarder dans un fichier texte"}. {"Edit Properties","Modifier les propriétés"}. {"Either approve or decline the voice request.","Approuver ou refuser la demande de 'voice'"}. {"ejabberd IRC module","Module IRC ejabberd"}. {"ejabberd MUC module","Module MUC ejabberd"}. {"ejabberd Multicast service","Service de Multidiffusion d'ejabberd"}. {"ejabberd Publish-Subscribe module","Module Publish-Subscribe d'ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. {"ejabberd vCard module","Module vCard ejabberd"}. {"ejabberd Web Admin","Console Web d'administration de ejabberd"}. {"Elements","Éléments"}. {"Email","Email"}. {"Enable logging","Activer l'archivage"}. {"Encoding for server ~b","Codage pour le serveur ~b"}. {"End User Session","Terminer la session de l'utilisateur"}. {"Enter list of {Module, [Options]}","Entrez une liste de {Module, [Options]}"}. {"Enter nickname you want to register","Entrez le pseudo que vous souhaitez enregistrer"}. {"Enter path to backup file","Entrez le chemin vers le fichier de sauvegarde"}. {"Enter path to jabberd14 spool dir","Entrez le chemin vers le répertoire de spool jabberd14"}. {"Enter path to jabberd14 spool file","Entrez le chemin vers le fichier spool jabberd14"}. {"Enter path to text file","Entrez le chemin vers le fichier texte"}. {"Enter the text you see","Tapez le texte que vous voyez"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Entrez le nom d'utilisateur et les encodages que vous souhaitez utiliser pour vous connecter aux serveurs IRC. Appuyez sur 'Suivant' pour pour avoir d'autres champs à remplir. Appuyez sur 'Terminer' pour sauver les paramètres."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Entrez le nom d'utilisateur, les encodages, les ports et mots de passe que vous souhaitez utiliser pour vous connecter aux serveurs IRC"}. {"Erlang Jabber Server","Serveur Jabber Erlang"}. {"Error","Erreur"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Exporter toutes les tables en tant que requêtes SQL vers un fichier:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporter les données de tous les utilisateurs du serveur vers un fichier PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exporter les données utilisateurs d'un hôte vers un fichier PIEFXIS (XEP-0227):"}. {"Family Name","Nom de famille"}. {"February","Février"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Remplissez le formulaire pour recherche un utilisateur Jabber (Ajouter * à la fin du champ pour chercher n'importe quelle fin de chaîne"}. {"Friday","Vendredi"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Nom complet"}. {"Get Number of Online Users","Récupérer le nombre d'utilisateurs en ligne"}. {"Get Number of Registered Users","Récupérer le nombre d'utilisateurs enregistrés"}. {"Get User Last Login Time","Récupérer la dernière date de connexion de l'utilisateur"}. {"Get User Password","Récupérer le mot de passe de l'utilisateur"}. {"Get User Statistics","Récupérer les statistiques de l'utilisateur"}. {"Group ","Groupe "}. {"Groups","Groupes"}. {"has been banned","a été banni"}. {"has been kicked","a été expulsé"}. {"has been kicked because of an affiliation change","a été éjecté à cause d'un changement d'autorisation"}. {"has been kicked because of a system shutdown","a été éjecté en raison de l'arrêt du système"}. {"has been kicked because the room has been changed to members-only","a été éjecté car la salle est désormais réservée aux membres"}. {" has set the subject to: "," a changé le sujet pour: "}. {"Host","Serveur"}. {"If you don't see the CAPTCHA image here, visit the web page.","SI vous ne voyez pas l'image CAPTCHA ici, visitez la page web."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si vous voulez préciser différents ports, mots de passe, et encodages pour les serveurs IRC, remplissez cette liste avec des valeurs dans le format '{\"serveur irc\", \"encodage\", port, \"mot de passe\"}'. Par défaut ce service utilise l'encodage \"~s\", port ~p, mot de passe vide."}. {"Import Directory","Importer une répertoire"}. {"Import File","Importer un fichier"}. {"Import user data from jabberd14 spool file:","Importer des utilisateurs depuis un fichier spool Jabberd 1.4:"}. {"Import User from File at ","Importer un utilisateur depuis le fichier sur "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importer les données utilisateurs à partir d'un fichier PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importer des utilisateurs depuis un fichier spool Jabberd 1.4:"}. {"Import Users from Dir at ","Importer des utilisateurs depuis le répertoire sur "}. {"Import Users From jabberd14 Spool Files","Importer des utilisateurs depuis un fichier spool Jabberd 1.4"}. {"Improper message type","Mauvais type de message"}. {"Incorrect password","Mot de passe incorrect"}. {"IP addresses","Adresses IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canal IRC (ne pas insérer le premier caractère #)"}. {"IRC server","Serveur IRC"}. {"IRC settings","Configuration IRC"}. {"IRC Transport","Passerelle IRC"}. {"IRC username","Nom d'utilisateur IRC"}. {"IRC Username","Nom d'utilisateur IRC"}. {"is now known as","est maintenant connu comme"}. {"It is not allowed to send private messages","L'envoi de messages privés n'est pas autorisé"}. {"It is not allowed to send private messages of type \"groupchat\"","Il n'est pas permis d'envoyer des messages privés de type \"groupchat\""}. {"It is not allowed to send private messages to the conference","Il n'est pas permis d'envoyer des messages \"normaux\" à la conférence"}. {"Jabber Account Registration","Enregistrement du Compte Jabber"}. {"Jabber ID","Jabber ID"}. {"January","Janvier"}. {"Join IRC channel","Rejoindre un canal IRC"}. {"joins the room","rejoint le salon"}. {"Join the IRC channel here.","Rejoindre un canal IRC ici"}. {"Join the IRC channel in this Jabber ID: ~s","Rejoindre un canal IRC avec ce Jabber ID: ~s"}. {"July","Juillet"}. {"June","Juin"}. {"Last Activity","Dernière Activité"}. {"Last login","Dernière connexion"}. {"Last month","Dernier mois"}. {"Last year","Dernière année"}. {"leaves the room","quitte le salon"}. {"Listened Ports at ","Ports ouverts sur "}. {"Listened Ports","Ports ouverts"}. {"List of modules to start","Liste des modules à démarrer"}. {"List of rooms","Liste des salons"}. {"Low level update script","Script de mise à jour de bas-niveau"}. {"Make participants list public","Rendre la liste des participants publique"}. {"Make room CAPTCHA protected","Protéger le salon par un CAPTCHA"}. {"Make room members-only","Réserver le salon aux membres uniquement"}. {"Make room moderated","Rendre le salon modéré"}. {"Make room password protected","Protéger le salon par mot de passe"}. {"Make room persistent","Rendre le salon persistant"}. {"Make room public searchable","Rendre le salon public"}. {"March","Mars"}. {"Maximum Number of Occupants","Nombre maximum d'occupants"}. {"May","Mai"}. {"Membership is required to enter this room","Vous devez être membre pour accèder à ce salon"}. {"Members:","Membres :"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Mémorisez votre mot de passe, ou écrivez-le sur un papier conservé dans un endroit secret. Dans Jabber il n'y a pas de mécanisme pour retrouver votre mot de passe si vous l'avez oublié."}. {"Memory","Mémoire"}. {"Message body","Corps du message"}. {"Middle Name","Autre nom"}. {"Moderator privileges required","Les droits de modérateur sont nécessaires"}. {"Modified modules","Modules mis à jour"}. {"Module","Module"}. {"Modules at ~p","Modules sur ~p"}. {"Modules","Modules"}. {"Monday","Lundi"}. {"Multicast","Multidiffusion"}. {"Multi-User Chat","Discussion de groupe"}. {"Name:","Nom :"}. {"Name","Nom"}. {"Never","Jamais"}. {"New Password:","Nouveau mot de passe:"}. {"Nickname","Pseudo"}. {"Nickname Registration at ","Enregistrement d'un pseudo sur "}. {"Nickname ~s does not exist in the room","Le pseudo ~s n'existe pas dans ce salon"}. {"No body provided for announce message","Pas de corps de message pour l'annonce"}. {"No Data","Aucune information disponible"}. {"Node not found","Noeud non trouvé"}. {"Node ~p","Noeud ~p"}. {"Nodes","Noeuds"}. {"None","Aucun"}. {"Not Found","Nœud non trouvé"}. {"November","Novembre"}. {"Number of online users","Nombre d'utilisateurs en ligne"}. {"Number of registered users","Nombre d'utilisateurs enregistrés"}. {"October","Octobre"}. {"Offline Messages:","Messages en attente :"}. {"Offline Messages","Messages en attente"}. {"OK","OK"}. {"Old Password:","Ancien mot de passe:"}. {"Online","En ligne"}. {"Online Users:","Utilisateurs connectés:"}. {"Online Users","Utilisateurs en ligne"}. {"Only moderators and participants are allowed to change the subject in this room","Seuls les modérateurs et les participants peuvent changer le sujet dans ce salon"}. {"Only moderators are allowed to change the subject in this room","Seuls les modérateurs peuvent changer le sujet dans ce salon"}. {"Only occupants are allowed to send messages to the conference","Seuls les occupants peuvent envoyer des messages à la conférence"}. {"Only occupants are allowed to send queries to the conference","Seuls les occupants sont autorisés à envoyer des requêtes à la conférence"}. {"Only service administrators are allowed to send service messages","Seuls les administrateurs du service sont autoriser à envoyer des messages de service"}. {"Options","Options"}. {"Organization Name","Nom de l'organisation"}. {"Organization Unit","Unité de l'organisation"}. {"Outgoing s2s Connections:","Connexions s2s sortantes:"}. {"Outgoing s2s Connections","Connexions s2s sortantes"}. {"Owner privileges required","Les droits de propriétaire sont nécessaires"}. {"Packet","Paquet"}. {"Password ~b","Mot de passe ~b"}. {"Password:","Mot de passe:"}. {"Password","Mot de passe"}. {"Password Verification:","Vérification du mot de passe :"}. {"Password Verification","Vérification du mot de passe"}. {"Path to Dir","Chemin vers le répertoire"}. {"Path to File","Chemin vers le fichier"}. {"Pending","En suspens"}. {"Period: ","Période :"}. {"Permanent rooms","Salons persistent"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ces options sauvegardent uniquement la base de données interne Mnesia. Si vous utilisez le module ODBC vous devez sauvegarde votre base SQL séparément."}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","privé"}. {"Protocol","Protocole"}. {"Publish-Subscribe","Publication-Abonnement"}. {"PubSub subscriber request","Demande d'abonnement PubSub"}. {"Queries to the conference members are not allowed in this room","Les requêtes sur les membres de la conférence ne sont pas autorisé dans ce salon"}. {"RAM and disc copy","Copie en mémoire vive (RAM) et sur disque"}. {"RAM copy","Copie en mémoire vive (RAM)"}. {"Raw","Brut"}. {"Really delete message of the day?","Confirmer la suppression du message du jour ?"}. {"Recipient is not in the conference room","Le destinataire n'est pas dans la conférence"}. {"Register a Jabber account","Enregistrer un compte Jabber"}. {"Registered nicknames","Pseudos enregistrés"}. {"Registered Users:","Utilisateurs enregistrés:"}. {"Registered Users","Utilisateurs enregistrés"}. {"Register","Enregistrer"}. {"Registration in mod_irc for ","Enregistrement du mod_irc pour "}. {"Remote copy","Copie distante"}. {"Remove All Offline Messages","Effacer tous les messages hors ligne"}. {"Remove","Enlever"}. {"Remove User","Supprimer l'utilisateur"}. {"Replaced by new connection","Remplacé par une nouvelle connexion"}. {"Resources","Ressources"}. {"Restart","Redémarrer"}. {"Restart Service","Redémarrer le service"}. {"Restore Backup from File at ","Restaurer la sauvegarde depuis le fichier sur "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restauration de la sauvegarde binaire après redémarrage (nécessite moins de mémoire):"}. {"Restore binary backup immediately:","Restauration immédiate d'une sauvegarde binaire:"}. {"Restore plain text backup immediately:","Restauration immédiate d'une sauvegarde texte:"}. {"Restore","Restauration"}. {"Room Configuration","Configuration du salon"}. {"Room creation is denied by service policy","La création de salons est interdite par le service"}. {"Room description","Description :"}. {"Room Occupants","Occupants du salon"}. {"Room title","Titre du salon"}. {"Roster","Liste de contacts"}. {"Roster of ","Liste de contact de "}. {"Roster size","Taille de la liste de contacts"}. {"RPC Call Error","Erreur d'appel RPC"}. {"Running Nodes","Noeuds actifs"}. {"~s access rule configuration","Configuration des règles d'accès ~s"}. {"Saturday","Samedi"}. {"Script check","Validation du script"}. {"Search Results for ","Résultats de recherche pour "}. {"Search users in ","Rechercher des utilisateurs "}. {"Send announcement to all online users","Envoyer l'annonce à tous les utilisateurs en ligne"}. {"Send announcement to all online users on all hosts","Envoyer l'annonce à tous les utilisateurs en ligne sur tous les serveurs"}. {"Send announcement to all users","Envoyer l'annonce à tous les utilisateurs"}. {"Send announcement to all users on all hosts","Envoyer une annonce à tous les utilisateurs de tous les domaines"}. {"September","Septembre"}. {"Server ~b","Serveur ~b"}. {"Server:","Serveur :"}. {"Set message of the day and send to online users","Définir le message du jour et l'envoyer aux utilisateurs en ligne"}. {"Set message of the day on all hosts and send to online users","Définir le message du jour pour tous domaines et l'envoyer aux utilisateurs en ligne"}. {"Shared Roster Groups","Groupes de liste de contacts partagée"}. {"Show Integral Table","Montrer la table intégralement"}. {"Show Ordinary Table","Montrer la table ordinaire"}. {"Shut Down Service","Arrêter le service"}. {"~s invites you to the room ~s","~s vous a invité dans la salle de discussion ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Certains clients Jabber peuvent stocker votre mot de passe sur votre ordinateur. N'utilisez cette fonctionnalité que si vous avez confiance en la sécurité de votre ordinateur."}. {"~s's Offline Messages Queue","~s messages en file d'attente"}. {"Start","Démarrer"}. {"Start Modules at ","Démarrer les modules sur "}. {"Start Modules","Modules de démarrage"}. {"Statistics of ~p","Statistiques de ~p"}. {"Statistics","Statistiques"}. {"Stop","Arrêter"}. {"Stop Modules at ","Arrêter les modules sur "}. {"Stop Modules","Modules d'arrêt"}. {"Stopped Nodes","Noeuds arrêtés"}. {"Storage Type","Type de stockage"}. {"Store binary backup:","Sauvegarde binaire:"}. {"Store plain text backup:","Sauvegarde texte:"}. {"Subject","Sujet"}. {"Submit","Soumettre"}. {"Submitted","Soumis"}. {"Subscription","Abonnement"}. {"Sunday","Dimanche"}. {"That nickname is already in use by another occupant","Le pseudo est déjà utilisé par un autre occupant"}. {"That nickname is registered by another person","Le pseudo est enregistré par une autre personne"}. {"The CAPTCHA is valid.","Le CAPTCHA est valide"}. {"The CAPTCHA verification has failed","La vérification du CAPTCHA a échoué"}. {"the password is","le mot de passe est"}. {"The password is too weak","Le mot de passe est trop faible"}. {"The password of your Jabber account was successfully changed.","Le mot de passe de votre compte Jabber a été changé avec succès."}. {"There was an error changing the password: ","Il y a eu une erreur en changeant le mot de passe :"}. {"There was an error creating the account: ","Il y a eu une erreur en créant le compte :"}. {"There was an error deleting the account: ","Il y a eu une erreur en effaçant le compte :"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","C'est insensible à la casse : macbeth est identique à MacBeth et Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Cette page permet de créer un compte Jabber sur ce serveur Jabber. Votre JID (Jabber IDentifier, identifiant Jabber) sera de la forme : nom@serveur. Prière de lire avec attention les instructions pour remplir correctement ces champs."}. {"This page allows to unregister a Jabber account in this Jabber server.","Cette page permet d'effacer un compte Jabber sur ce serveur Jabber."}. {"This room is not anonymous","Ce salon n'est pas anonyme"}. {"Thursday","Jeudi"}. {"Time delay","Délais"}. {"Time","Heure"}. {"To","A"}. {"Too many unacked stanzas","Trop de stanzas sans accusé de réception (ack)"}. {"To ~s","A ~s"}. {"Total rooms","Nombre de salons"}. {"Traffic rate limit is exceeded","La limite de trafic a été dépassée"}. {"Transactions Aborted:","Transactions annulées :"}. {"Transactions Committed:","Transactions commitées :"}. {"Transactions Logged:","Transactions journalisées :"}. {"Transactions Restarted:","Transactions redémarrées :"}. {"Tuesday","Mardi"}. {"Unable to generate a CAPTCHA","Impossible de générer le CAPTCHA"}. {"Unauthorized","Non autorisé"}. {"Unregister a Jabber account","Effacer un compte Jabber"}. {"Unregister","Effacer"}. {"Update message of the day (don't send)","Mise à jour du message du jour (pas d'envoi)"}. {"Update message of the day on all hosts (don't send)","Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)"}. {"Update","Mettre à jour"}. {"Update plan","Plan de mise à jour"}. {"Update ~p","Mise à jour de ~p"}. {"Update script","Script de mise à jour"}. {"Uptime:","Temps depuis le démarrage :"}. {"Use of STARTTLS required","L'utilisation de STARTTLS est impérative"}. {"User Management","Gestion des utilisateurs"}. {"Username:","Nom d'utilisateur :"}. {"Users are not allowed to register accounts so quickly","Les utilisateurs ne sont pas autorisés à enregistrer des comptes si rapidement"}. {"Users Last Activity","Dernière activité des utilisateurs"}. {"User ~s","Utilisateur ~s"}. {"Users","Utilisateurs"}. {"User","Utilisateur"}. {"Validate","Valider"}. {"vCard User Search","Recherche dans l'annnuaire"}. {"Virtual Hosts","Serveurs virtuels"}. {"Visitors are not allowed to change their nicknames in this room","Les visiteurs ne sont pas autorisés à changer de pseudo dans ce salon"}. {"Visitors are not allowed to send messages to all occupants","Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants"}. {"Voice request","Demande de 'voice'"}. {"Wednesday","Mercredi"}. {"You can later change your password using a Jabber client.","Vous pouvez changer votre mot de passe plus tard en utilisant un client Jabber."}. {"You have been banned from this room","Vous avez été exclus de ce salon"}. {"You must fill in field \"Nickname\" in the form","Vous devez préciser le champ \"pseudo\" dans le formulaire"}. {"You need a client that supports x:data and CAPTCHA to register","Vous avez besoin d'un client prenant en charge x:data et CAPTCHA pour enregistrer un pseudo"}. {"You need a client that supports x:data to register the nickname","Vous avez besoin d'un client prenant en charge x:data pour enregistrer un pseudo"}. {"You need an x:data capable client to configure mod_irc settings","Vous avez besoin d'un client supportant x:data pour configurer le module IRC"}. {"You need an x:data capable client to search","Vous avez besoin d'un client supportant x:data pour faire une recherche"}. {"Your active privacy list has denied the routing of this stanza.","Votre règle de flitrage active a empêché le routage de ce stanza."}. {"Your contact offline message queue is full. The message has been discarded.","La file d'attente de message de votre contact est pleine. Votre message a été détruit."}. {"Your Jabber account was successfully created.","Votre compte Jabber a été créé avec succès."}. {"Your Jabber account was successfully deleted.","Votre compte Jabber a été effacé avec succès."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Vos messages pour ~s sont bloqués. Pour les débloquer, veuillez visiter ~s"}. ejabberd-18.01/priv/msgs/nl.po0000644000232200023220000017175513225664356016565 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Andreas van Cranenburgh \n" "Language-Team: \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Dutch (nederlands)\n" "X-Additional-Translator: Sander Devrieze\n" "X-Generator: Poedit 1.6.10\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " veranderde het onderwerp in: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Toegangsinstellingen" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Instellingen van access control lists" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Access control lists" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Access rules" #: mod_configure:1095 msgid "Access control lists" msgstr "Access control lists" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "De toegang werd geweigerd door het beleid van deze dienst" #: mod_configure:1113 msgid "Access rules" msgstr "Access rules" #: mod_configure:1772 msgid "Action on user" msgstr "Actie op gebruiker" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Jabber ID toevoegen" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Toevoegen" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Gebruiker toevoegen" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Beheer" #: mod_configure:1767 msgid "Administration of " msgstr "Beheer van " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "U hebt beheerdersprivileges nodig" #: mod_configure:507 msgid "All Users" msgstr "Alle gebruikers" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Alle activiteit" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Sta gebruikers toe het onderwerp te veranderen" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Gebruikers mogen naar andere gebruikers verzoeken verzenden" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Gebruikers mogen uitnodigingen verzenden" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Gebruikers mogen privéberichten verzenden" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Sta bezoekers toe hun naam te veranderen" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Gebruikers mogen privéberichten verzenden aan" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Sta bezoekers toe hun statusbericht in te stellen" #: mod_announce:611 msgid "Announcements" msgstr "Mededelingen" #: mod_muc_log:476 msgid "April" msgstr "April" #: mod_muc_log:480 msgid "August" msgstr "Augustus" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Backup" #: mod_configure:584 msgid "Backup Management" msgstr "Backup" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Backup maken van ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Binaire backup maken op " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Verkeerd formaat" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Geboortedatum" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA webpagina." #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Processortijd:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Wachtwoord wijzigen" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Verander Gebruikerswachtwoord" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Niet-toegestane karakters:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Niet-toegestane karakters:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Niet-toegestane karakters:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "De instellingen van de chatruimte werden veranderd" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Gespreksruimte gecreëerd" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Gespreksruimte vernietigd" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Gespreksruimte gestart" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Gespreksruimte gestopt" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Groepsgesprekken" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Kies een gebruikersnaam en een wachtwoord om u te registreren op deze server" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Selecteer de modules die u wilt stoppen" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Opslagmethode voor tabellen kiezen" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Beslis of dit verzoek tot abonneren zal worden goedgekeurd" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Plaats" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Commando's" #: mod_muc:461 msgid "Conference room does not exist" msgstr "De chatruimte bestaat niet" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Instellingen" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Instellingen van chatruimte ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Verbonden bronnen:" #: mod_irc:526 msgid "Connections parameters" msgstr "Verbindingsparameters" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Land" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Database" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Instellingen van databasetabellen op " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Databasetabellen van ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Database" #: mod_muc_log:484 msgid "December" msgstr "December" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Gebruikers standaard instellen als deelnemers" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Geselecteerde verwijderen" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Verwijder Gebruiker" #: mod_announce:629 msgid "Delete message of the day" msgstr "Bericht van de dag verwijderen" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Verwijder bericht-van-de-dag op alle hosts" #: mod_shared_roster:900 msgid "Description:" msgstr "Beschrijving:" #: mod_configure:889 msgid "Disc only copy" msgstr "Harde schijf" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Weergegeven groepen:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Geef Uw wachtwoord aan niemand, zelfs niet aan de beheerders van deze Jabber-" "server." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Backup naar een tekstbestand schrijven op " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Backup naar een tekstbestand schrijven" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Eigenschappen bewerken" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Keur stemaanvraag goed of af." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementen" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "E-mail" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "het wachtwoord is" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Logs aanzetten" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Karakterset voor server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Verwijder Gebruikers-sessie" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Voer lijst met op te starten modules als volgt in: {Module, [Opties]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Voer de bijnaam in die u wilt registreren" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Voer pad naar backupbestand in" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Voer pad naar jabberd14-spool-directory in" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Voer pad naar jabberd14-spool-bestand in" #: mod_configure:974 msgid "Enter path to text file" msgstr "Voer pad naar backupbestand in" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Voer de getoonde tekst in" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Voer de gebruikersnaam en de coderingen in die u wilt gebruiken voor " "verbindingen met IRC-servers. Klik op 'Volgende' om meer velden aan te " "maken. Klik op \"Voltooi' om de instellingen op te slaan." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Voer de gebruikersnaam, coderingen, poorten en wachtwoorden in die U wilt " "gebruiken voor het verbinden met IRC-servers" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Fout" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Voorbeeld: [{\"irc.example.org\", \"koi8-r\", 6667, \"geheim\"}, {\"vendetta." "example.net\", \"iso8859-1\", 7000}, {irc,testserver.nl\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Exporteer alle tabellen als SQL-queries naar een bestand:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exporteer data van alle gebruikers in de server naar PIEFXIS-bestanden " "(XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exporteer data van alle gebruikers van een host naar PIEXFIS-bestanden " "(XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Er kon geen JID worden ontleend uit deze stemaanvraag" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Achternaam" #: mod_muc_log:474 msgid "February" msgstr "Februari" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken " "naar alles wat met het eerste deel begint.)." #: mod_muc_log:467 msgid "Friday" msgstr "Vrijdag" #: mod_offline:691 msgid "From" msgstr "Van" #: mod_configure:723 msgid "From ~s" msgstr "Van ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Volledige naam" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Aantal Aanwezige Gebruikers Opvragen" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Aantal Geregistreerde Gebruikers Opvragen" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Tijd van Laatste Aanmelding Opvragen" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Gebruikerswachtwoord Opvragen" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Gebruikers-statistieken Opvragen" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Tussennaam" #: mod_shared_roster:923 msgid "Group " msgstr "Groep " #: mod_roster:916 msgid "Groups" msgstr "Groepen" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP-adres" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC-transport" #: mod_irc:496 msgid "IRC Username" msgstr "Gebruikersnaam voor IRC:" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC kanaal (zonder eerste #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Node niet gevonden" #: mod_irc:673 msgid "IRC server" msgstr "IRC-server" #: mod_irc:756 msgid "IRC settings" msgstr "IRC instellingen" #: mod_irc:766 msgid "IRC username" msgstr "Gebruikersnaam voor IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Als U het CAPTCHA-plaatje niet ziet, bezoek dan de webpagina." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Als u verschillende poorten, wachtwoorden en coderingen wilt opgeven voor " "elke IRC-server, vul dan deze lijst met het volgende formaat: '{\"IRC-server" "\", \"codering\", poort, \"wachtwoord\"}'. Standaard gebruikt deze service " "de codering \"~s\", poort ~p, leeg wachtwoord." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Directory importeren" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Bestand importeren" #: mod_configure:982 msgid "Import User from File at " msgstr "Importeer gebruiker via bestand op " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importeer gebruikers via spool-bestanden van jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Gebruikers importeren vanaf directory op " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importeer gebruikersdata via spool-bestanden van jabberd14" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importeer gebruikersdata van een PIEFXIS-bestand (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importeer gebruikersdata via spool-bestanden van jabberd14" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Onjuist berichttype" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Uitgaande s2s-verbindingen:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Foutief wachtwoord" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Foutief wachtwoord" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Stemaanvragen zijn uitgeschakeld voor deze chatruimte" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Het is niet toegestaan priveberichten te sturen" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "" "Er mogen geen privéberichten van het type \"groupchat\" worden verzonden" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Er mogen geen privéberichten naar de chatruimte worden verzonden" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Jabber-account registratie" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Januari" #: mod_irc:665 msgid "Join IRC channel" msgstr "Ga IRC kanaal binnen" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Ga het IRC kanaal binnen" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Ga het IRC kanaal van deze Jabber ID binnen: ~s" #: mod_muc_log:479 msgid "July" msgstr "Juli" #: mod_muc_log:478 msgid "June" msgstr "Juni" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Laatste activiteit" #: mod_configure:1646 msgid "Last login" msgstr "Laatste Aanmelding" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Afgelopen maand" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Afgelopen jaar" #: mod_configure:941 msgid "List of modules to start" msgstr "Lijst met op te starten modules" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Lijst van groepsgesprekken" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Openstaande poorten" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Openstaande poorten op " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Lowlevel script voor de opwaardering" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Deelnemerslijst publiek maken" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Chatruimte beveiligen met een geautomatiseerde Turing test" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Chatruimte enkel toegankelijk maken voor leden" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Chatruimte gemodereerd maken" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Chatruimte beveiligen met een wachtwoord" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Chatruimte blijvend maken" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Chatruimte doorzoekbaar maken" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Gebruikersnaam voor IRC" #: mod_muc_log:475 msgid "March" msgstr "Maart" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Maximum aantal aanwezigen" #: mod_muc_log:477 msgid "May" msgstr "Mei" #: mod_shared_roster:907 msgid "Members:" msgstr "Groepsleden:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "U moet lid zijn om deze chatruimte te kunnen betreden" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Onthou het wachtwoord, of schrijf het op en bewaar het op een veilige " "plaats. Met Jabber is er geen geautomatiseerde manier om het wachtwoord " "terug te halen als U het vergeet." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Geheugen" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Bericht" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Tussennaam" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "U hebt moderatorprivileges nodig" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Gewijzigde modules" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Module" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Modules" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Modules op ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Maandag" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Groepschat" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Naam" #: mod_shared_roster:896 msgid "Name:" msgstr "Naam:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nooit" #: mod_register_web:377 msgid "New Password:" msgstr "Nieuw Wachtwoord:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Bijnaam" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registratie van een bijnaam op " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "De bijnaam ~s bestaat niet in deze chatruimte" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Node niet gevonden" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Geen gegevens" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "De mededeling bevat geen bericht" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Node niet gevonden" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Node niet gevonden" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Node niet gevonden" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Node niet gevonden" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Node ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nodes" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Geen" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Niet gevonden" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "November" #: mod_configure:1212 msgid "Number of online users" msgstr "Aantal Aanwezige Gebruikers" #: mod_configure:1202 msgid "Number of registered users" msgstr "Aantal Geregistreerde Gebruikers" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Oktober" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Offline berichten" #: mod_offline:761 msgid "Offline Messages:" msgstr "Offline berichten:" #: mod_register_web:373 msgid "Old Password:" msgstr "Oud Wachtwoord:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Online" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Online gebruikers" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Online gebruikers:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Alleen moderators mogen het onderwerp van deze chatruimte veranderen" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Alleen moderators en deelnemers mogen het onderwerp van deze chatruimte " "veranderen" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Alleen moderators mogen het onderwerp van deze chatruimte veranderen" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Alleen moderators kunnen stemaanvragen goedkeuren" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Alleen aanwezigen mogen berichten naar de chatruimte verzenden" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Alleen aanwezigen mogen verzoeken verzenden naar de chatruimte" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Alleen beheerders van deze dienst mogen mededelingen verzenden naar alle " "chatruimtes" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opties" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Organisatie" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Afdeling" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Uitgaande s2s-verbindingen" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Uitgaande s2s-verbindingen:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "U hebt eigenaarsprivileges nodig" #: mod_offline:693 msgid "Packet" msgstr "Pakket" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Wachtwoord" #: mod_configure:1130 msgid "Password Verification" msgstr "Wachtwoord Bevestiging" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Wachtwoord Bevestiging:" #: mod_irc:822 msgid "Password ~b" msgstr "Wachtwoord ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Wachtwoord:" #: mod_configure:998 msgid "Path to Dir" msgstr "Pad naar directory" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Pad naar bestand" #: mod_roster:915 msgid "Pending" msgstr "Bezig" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periode: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Permanente groepsgesprekken" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Merk op dat volgende opties enkel backups maken van de ingebouwde database " "Mnesia. Als U de ODBC module gebruikt dan moeten daarvan afzonderlijke " "backups gemaakt worden." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Wacht s.v.p. met het maken van een nieuwe stemaanvraag." #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Poort" #: mod_irc:828 msgid "Port ~b" msgstr "Poort ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub abonnee verzoek" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "" "Er mogen geen verzoeken verzenden worden naar deelnemers in deze chatruimte" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM en harde schijf" #: mod_configure:889 msgid "RAM copy" msgstr "RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "RPC-oproepfout" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Ruw" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Wilt u het bericht van de dag verwijderen?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "De ontvanger is niet in de chatruimte" #: mod_register_web:275 msgid "Register" msgstr "Registreer" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Registreer een Jabber-account" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Geregistreerde gebruikers" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Geregistreerde gebruikers:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Geregistreerde gebruikersnamen" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registratie van " #: mod_configure:889 msgid "Remote copy" msgstr "Op andere nodes in de cluster" #: mod_roster:963 msgid "Remove" msgstr "Verwijderen" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Verwijder alle offline berichten" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Gebruiker verwijderen" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Vervangen door een nieuwe verbinding" #: mod_configure:1675 msgid "Resources" msgstr "Bronnen" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Herstarten" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Herstart Service" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Binaire backup direct herstellen" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Binaire backup direct herstellen op " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Binaire backup herstellen na herstart van ejabberd (vereist minder geheugen):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Binaire backup direct herstellen:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Backup in een tekstbestand direct herstellen:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Instellingen van de chatruimte" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Aantal aanwezigen" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "" "De aanmaak van de chatruimte is verhinderd door de instellingen van deze " "server" #: mod_muc_log:1064 msgid "Room description" msgstr "Beschrijving" #: mod_muc_log:1027 msgid "Room title" msgstr "Naam van de chatruimte" #: mod_roster:1082 msgid "Roster" msgstr "Roster" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Roster van " #: mod_configure:1671 msgid "Roster size" msgstr "Contactlijst Groote" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Draaiende nodes" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Zaterdag" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "De geautomatiseerde Turing-test is geslaagd." #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Controle van script" #: mod_vcard:453 msgid "Search Results for " msgstr "Zoekresultaten voor " #: mod_vcard:427 msgid "Search users in " msgstr "Gebruikers zoeken in " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Mededeling verzenden naar alle online gebruikers" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "" "Mededeling verzenden naar alle online gebruikers op alle virtuele hosts" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Mededeling verzenden naar alle gebruikers" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Stuur aankondiging aan alle gebruikers op alle hosts" #: mod_muc_log:481 msgid "September" msgstr "September" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Server:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Bericht van de dag instellen en verzenden naar online gebruikers" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Stel bericht-van-de-dag in op alle hosts en stuur naar aanwezige gebruikers" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Gedeelde rostergroepen" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Volledige tabel laten zien" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Deel van tabel laten zien" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Stop Service" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Sommige Jabber-clienten kunnen het wachtwoord opslaan op Uw computer. " "Gebruik deze mogelijkheid alleen als U vertrouwd dat Uw computer afdoende " "beveiligd is." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Starten" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Modules starten" #: mod_configure:936 msgid "Start Modules at " msgstr "Modules starten op " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistieken" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistieken van ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Stoppen" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Modules stoppen" #: mod_configure:918 msgid "Stop Modules at " msgstr "Modules stoppen op " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Gestopte nodes" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Opslagmethode" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Binaire backup maken:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Backup naar een tekstbestand schrijven:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Onderwerp" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Verzenden" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Verzonden" #: mod_roster:914 msgid "Subscription" msgstr "Inschrijving" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Zondag" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Deze bijnaam is al in gebruik door een andere aanwezige" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Deze bijnaam is al geregistreerd door iemand anders" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "De geautomatiseerde Turing-test is geslaagd." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "De CAPTCHA-verificatie is mislukt" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Het wachtwoord is te zwak" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Het wachtwoord van Uw Jabber-account is succesvol veranderd." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Er was een fout bij het veranderen van het wachtwoord:" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Er was een fout bij het creeern van de account:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Er was een fout bij het verwijderen van de account." #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Dit is niet hoofdlettergevoelig: macbeth is hetzelfde als MacBeth en Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Deze pagina maakt het mogelijk een Jabber-account te registreren op deze " "server. Uw JID (Jabber IDentiteit) zal er als volg uit zien: " "gebruikersnaam@server. Lees de instructies zorgvuldig teneinde de velden " "correct in te vullen." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Deze pagina maakt het mogelijk een Jabber-account op deze server op te " "heffen." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Deze chatruimte is niet anoniem" #: mod_muc_log:466 msgid "Thursday" msgstr "Donderdag" #: mod_offline:690 msgid "Time" msgstr "Tijd" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Vertraging" #: mod_offline:692 msgid "To" msgstr "Aan" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Naar ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Te veel CAPTCHA-aanvragen" #: mod_proxy65_service:223 #, fuzzy msgid "Too many active bytestreams" msgstr "Te veel niet-bevestigde stanzas" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Te veel niet-bevestigde stanzas" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Stemaanvragen zijn uitgeschakeld voor deze chatruimte" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Aantal groepsgesprekken" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Dataverkeerslimiet overschreden" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Afgebroken transacties:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Bevestigde transacties:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Gelogde transacties:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Herstarte transacties:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Dinsdag" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Het generen van een CAPTCHA is mislukt" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Niet geautoriseerd" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Opheffen" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Opheffen van Jabber-account" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Bijwerken" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Bericht van de dag bijwerken (niet verzenden)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Verander bericht-van-de-dag op alle hosts (niet versturen)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plan voor de opwaardering" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Script voor de opwaardering" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Opwaarderen van ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Uptime:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Gebruik van STARTTLS is vereist" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Gebruik van STARTTLS is vereist" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Gebruiker" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Gebruikersbeheer" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Node niet gevonden" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Gebruiker ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Gebruikersnaam:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Gebruikers" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Laatste activiteit van gebruikers" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Het is gebruikers niet toegestaan zo snel achter elkaar te registreren" #: mod_roster:954 msgid "Validate" msgstr "Bevestigen" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtuele hosts" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Bezoekers mogen geen berichten verzenden naar alle aanwezigen" #: mod_muc_room:3879 msgid "Voice request" msgstr "Stemaanvraag" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Stemaanvragen zijn uitgeschakeld voor deze chatruimte" #: mod_muc_log:465 msgid "Wednesday" msgstr "Woensdag" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "U can het wachtwoord later veranderen met een Jabber-client." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "U werd verbannen uit deze chatruimte" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "U moet het veld \"bijnaam\" invullen" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "U hebt een client nodig die x:data en CAPTCHA ondersteunt om een bijnaam te " "registreren" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "U hebt een client nodig die x:data ondersteunt om een bijnaam te registreren" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "U hebt een client nodig die x:data ondersteunt om dit IRC-transport in te " "stellen" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "U hebt een client nodig die x:data ondersteunt om te zoeken" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Het is niet toegestaan priveberichten te sturen" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Uw Jabber-account is succesvol gecreeerd." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Uw Jabber-account is succesvol verwijderd." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Uw actieve privacy-lijst verbied het routeren van dit stanza." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Te veel offline berichten voor dit contactpersoon. Het bericht is niet " "opgeslagen." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd's IRC-module" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd's MUC module" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberd Multicast service" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe module" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams module" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Webbeheer" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd's vCard-module" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "is verbannen" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "is weggestuurd" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "is weggestuurd omdat het systeem gestopt wordt" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "is weggestuurd vanwege een affiliatieverandering" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" "is weggestuurd omdat de chatruimte vanaf heden alleen toegankelijk is voor " "leden" #: mod_muc_log:410 msgid "is now known as" msgstr "heet nu" #: mod_muc_log:370 msgid "joins the room" msgstr "betrad de chatruimte" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "verliet de chatruimte" #: mod_muc_room:3856 msgid "private, " msgstr "privé, " #: mod_muc_room:3955 msgid "the password is" msgstr "het wachtwoord is" #: mod_vcard:292 msgid "vCard User Search" msgstr "Gebruikers zoeken" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Access rules op ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s nodigt je uit voor het groepsgesprek ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "offline berichten van ~s" #~ msgid "No resource provided" #~ msgstr "Geen bron opgegeven" #, fuzzy #~ msgid "Server" #~ msgstr "Server:" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Te veel (~p) mislukte authenticatie-pogingen van dit IP-adres (~s). Dit " #~ "adres zal worden gedeblokkeerd om ~s UTC" #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Dit IP-adres is geblokkeerd in ~s" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "De Jabber ID ~s is ongeldig" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Ongeldige affiliatie: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Ongeldige rol: ~s" #~ msgid "No limit" #~ msgstr "Geen limiet" #~ msgid "Present real Jabber IDs to" #~ msgstr "Jabber ID's kunnen achterhaald worden door" #~ msgid "moderators only" #~ msgstr "moderators" #~ msgid "anyone" #~ msgstr "iedereen" #, fuzzy #~ msgid "Moderator" #~ msgstr "moderators" #~ msgid "nobody" #~ msgstr "niemand" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Gebruikers mogen stemaanvragen verzenden" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Minimale interval tussen stemaanvragen (in seconden)" #~ msgid "Enable message archiving" #~ msgstr "Zet bericht-archivering aan" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Geen CAPTCHA test voor Jabber IDs" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "" #~ "U hebt een client nodig die x:data ondersteunt om deze chatruimte in te " #~ "stellen" #~ msgid "Number of occupants" #~ msgstr "Aantal aanwezigen" #~ msgid "User JID" #~ msgstr "JID Gebruiker" #~ msgid "Grant voice to this person?" #~ msgstr "Stemaanvraag honoreren voor deze persoon?" #~ msgid "Node ID" #~ msgstr "Node ID" #~ msgid "Subscriber Address" #~ msgstr "Abonnee Adres" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Deze gebruiker toestaan te abonneren op deze pubsub node?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Berichten bezorgen samen met gebeurtenisnotificaties" #~ msgid "Deliver event notifications" #~ msgstr "Gebeurtenisbevestigingen Sturen" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Abonnees informeren wanneer de instellingen van de node veranderen" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Abonnees informeren wanneer de node verwijderd word" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Abonnees informeren wanneer items verwijderd worden uit de node" #~ msgid "Persist items to storage" #~ msgstr "Items in het geheugen bewaren" #~ msgid "A friendly name for the node" #~ msgstr "Bijnaam voor deze knoop" #~ msgid "Max # of items to persist" #~ msgstr "Maximum aantal in het geheugen te bewaren items" #~ msgid "Whether to allow subscriptions" #~ msgstr "Abonnementsaanvraag toestaan" #~ msgid "Specify the access model" #~ msgstr "Geef toegangsmodel" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Contactlijst-groepen die mogen abonneren" #~ msgid "Specify the publisher model" #~ msgstr "Publicatietype opgeven" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Verwijder alle items wanneer de gerelateerde publiceerder offline gaat" #~ msgid "Specify the event message type" #~ msgstr "Geef type van eventbericht" #~ msgid "Max payload size in bytes" #~ msgstr "Maximumgrootte van bericht in bytes" #~ msgid "When to send the last published item" #~ msgstr "Wanneer het laatst gepubliceerde item verzonden moet worden" #~ msgid "Only deliver notifications to available users" #~ msgstr "Notificaties alleen verzenden naar online gebruikers" #~ msgid "The collections with which a node is affiliated" #~ msgstr "De collecties waar een node mee is gerelateerd" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Vul de velden in om te zoeken naar Jabber-gebruikers op deze server" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Uitgaande s2s-verbindingen:" #~ msgid "Delete" #~ msgstr "Verwijderen" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Deze deelnemer wordt weggestuurd vanwege het sturen van een " #~ "foutmeldingsbericht" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Deze deelnemer wordt weggestuurd vanwege het sturen van een " #~ "foutmeldingsbericht aan een andere deelnemer" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Deze deelnemer wordt weggestuurd vanwege het sturen van een foutmelding-" #~ "aanwezigheid" ejabberd-18.01/priv/msgs/gl.msg0000644000232200023220000006137213225664356016717 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Aceptar"}. {"Access Configuration","Configuración de accesos"}. {"Access Control List Configuration","Configuración da Lista de Control de Acceso"}. {"Access control lists","Listas de Control de Acceso"}. {"Access Control Lists","Listas de Control de Acceso"}. {"Access denied by service policy","Acceso denegado pola política do servizo"}. {"Access rules","Regras de acceso"}. {"Access Rules","Regras de Acceso"}. {"Action on user","Acción no usuario"}. {"Add Jabber ID","Engadir ID Jabber"}. {"Add New","Engadir novo"}. {"Add User","Engadir usuario"}. {"Administration","Administración"}. {"Administration of ","Administración de "}. {"Administrator privileges required","Necesítase privilexios de administrador"}. {"All activity","Toda a actividade"}. {"Allow users to change the subject","Permitir aos usuarios cambiar o asunto"}. {"Allow users to query other users","Permitir aos usuarios consultar a outros usuarios"}. {"Allow users to send invites","Permitir aos usuarios enviar invitacións"}. {"Allow users to send private messages","Permitir aos usuarios enviar mensaxes privadas"}. {"Allow visitors to change nickname","Permitir aos visitantes cambiarse o alcume"}. {"Allow visitors to send private messages to","Permitir aos visitantes enviar mensaxes privadas a"}. {"Allow visitors to send status text in presence updates","Permitir aos visitantes enviar texto de estado nas actualizacións depresenza"}. {"All Users","Todos os usuarios"}. {"Announcements","Anuncios"}. {"A password is required to enter this room","Necesítase contrasinal para entrar nesta sala"}. {"April","Abril"}. {"August","Agosto"}. {"Backup","Copia de seguridade"}. {"Backup Management","Xestión de copia de seguridade"}. {"Backup of ~p","Copia de seguridade de ~p"}. {"Backup to File at ","Copia de seguridade de arquivos en "}. {"Bad format","Mal formato"}. {"Birthday","Aniversario"}. {"CAPTCHA web page","CAPTCHA páxina Web"}. {"Change Password","Cambiar contrasinal"}. {"Change User Password","Cambiar contrasinal de usuario"}. {"Characters not allowed:","Caracteres non permitidos:"}. {"Chatroom configuration modified","Configuración da sala modificada"}. {"Chatroom is created","Creouse a sala"}. {"Chatroom is destroyed","Destruíuse a sala"}. {"Chatroom is started","Iniciouse a sala"}. {"Chatroom is stopped","Detívose a sala"}. {"Chatrooms","Salas de charla"}. {"Choose a username and password to register with this server","Escolle un nome de usuario e contrasinal para rexistrarche neste servidor"}. {"Choose modules to stop","Selecciona módulos a deter"}. {"Choose storage type of tables","Selecciona tipo de almacenamento das táboas"}. {"Choose whether to approve this entity's subscription.","Decidir se aprobar a subscripción desta entidade."}. {"City","Cidade"}. {"Commands","Comandos"}. {"Conference room does not exist","A sala de conferencias non existe"}. {"Configuration","Configuración"}. {"Configuration of room ~s","Configuración para a sala ~s"}. {"Connected Resources:","Recursos conectados:"}. {"Connections parameters","Parámetros de conexiones"}. {"Country","País"}. {"CPU Time:","Tempo consumido de CPU:"}. {"Database","Base de datos"}. {"Database Tables at ~p","Táboas da base de datos en ~p"}. {"Database Tables Configuration at ","Configuración de táboas da base de datos en "}. {"December","Decembro"}. {"Default users as participants","Os usuarios son participantes por defecto"}. {"Delete message of the day","Borrar mensaxe do dia"}. {"Delete message of the day on all hosts","Borrar a mensaxe do día en todos os dominios"}. {"Delete Selected","Eliminar os seleccionados"}. {"Delete User","Borrar usuario"}. {"Description:","Descrición:"}. {"Disc only copy","Copia en disco soamente"}. {"Displayed Groups:","Mostrar grupos:"}. {"Dump Backup to Text File at ","Exporta copia de seguridade a ficheiro de texto en "}. {"Dump to Text File","Exportar a ficheiro de texto"}. {"Edit Properties","Editar propiedades"}. {"Either approve or decline the voice request.","Aproba ou rexeita a petición de voz."}. {"ejabberd IRC module","Módulo de IRC para ejabberd"}. {"ejabberd MUC module","Módulo de MUC para ejabberd"}. {"ejabberd Multicast service","Servizo Multicast de ejabberd"}. {"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","Ejabberd Administrador Web"}. {"Elements","Elementos"}. {"Email","Email"}. {"Enable logging","Gardar históricos"}. {"Encoding for server ~b","Codificación de servidor ~b"}. {"End User Session","Pechar sesión de usuario"}. {"Enter list of {Module, [Options]}","Introduce lista de {Módulo, [Opcións]}"}. {"Enter nickname you want to register","Introduce o alcume que queiras rexistrar"}. {"Enter path to backup file","Introduce ruta ao ficheiro de copia de seguridade"}. {"Enter path to jabberd14 spool dir","Introduce a ruta ao directorio de jabberd14 spools"}. {"Enter path to jabberd14 spool file","Introduce ruta ao ficheiro jabberd14 spool"}. {"Enter path to text file","Introduce ruta ao ficheiro de texto"}. {"Enter the text you see","Introduza o texto que ves"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Introduce o nome de usuario e codificaciones de carácteres que queiras usar ao conectar nos servidores de IRC. Presione 'Siguiente' para obtener más campos para rellenar Presione 'completo' para guardar axustes."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Introduce o nome de usuario, codificaciones de carácteres, portos e contrasinai que queiras usar ao conectar nos servidores de IRC"}. {"Erlang Jabber Server","Servidor Jabber en Erlang"}. {"Error","Erro"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Exportar todas as táboas a un ficheiro SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar datos dos usuarios dun dominio a ficheiros PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Fallo ao extraer o Jabber ID da túa aprobación de petición de voz"}. {"Family Name","Apelido"}. {"February","Febreiro"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Enche o formulario para buscar usuarios Jabber (Engade * ao final dun campo para buscar subcadenas)"}. {"Friday","Venres"}. {"From","De"}. {"From ~s","De ~s"}. {"Full Name","Nome completo"}. {"Get Number of Online Users","Ver número de usuarios conectados"}. {"Get Number of Registered Users","Ver número de usuarios rexistrados"}. {"Get User Last Login Time","Ver data da última conexión de usuario"}. {"Get User Password","Ver contrasinal de usuario"}. {"Get User Statistics","Ver estatísticas de usuario"}. {"Group ","Grupo "}. {"Groups","Grupos"}. {"has been banned","foi bloqueado"}. {"has been kicked because of an affiliation change","foi expulsado debido a un cambio de afiliación"}. {"has been kicked because of a system shutdown","foi expulsado porque o sistema vaise a deter"}. {"has been kicked because the room has been changed to members-only","foi expulsado, porque a sala cambiouse a só-membros"}. {"has been kicked","foi expulsado"}. {" has set the subject to: "," puxo o asunto: "}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si non ves a imaxe CAPTCHA aquí, visita a páxina web."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se quere especificar codificaciones de caracteres diferentes, contrasinal ou servidor IRC rechea esta lista con valores no formato '{\"servidor irc\", \"codificación\", \"porto\", \"contrasinal\"}'. Este servizo utiliza por defecto a codificación \"~s\", porto ~p, sen contrasinal."}. {"Import Directory","Importar directorio"}. {"Import File","Importar ficheiro"}. {"Import user data from jabberd14 spool file:","Importar usuario de ficheiro spool de jabberd14:"}. {"Import User from File at ","Importa usuario desde ficheiro en "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importar usuarios en un fichero PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importar usuarios do directorio spool de jabberd14:"}. {"Import Users from Dir at ","Importar usuarios desde o directorio en "}. {"Import Users From jabberd14 Spool Files","Importar usuarios de ficheiros spool de jabberd-1.4"}. {"Improper message type","Tipo de mensaxe incorrecta"}. {"Incoming s2s Connections:","Conexións S2S saíntes:"}. {"Incorrect password","Contrasinal incorrecta"}. {"IP addresses","Direccións IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canle de IRC (non poñer o primeiro #)"}. {"IRC server","Servidor IRC"}. {"IRC settings","IRC axustes"}. {"IRC Transport","Transporte IRC"}. {"IRC username","Nome de usuario en IRC"}. {"IRC Username","Nome de usuario en IRC"}. {"is now known as","cámbiase o nome a"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Non está permitido enviar mensaxes de erro á sala. Este participante (~s) enviou unha mensaxe de erro (~s) e foi expulsado da sala"}. {"It is not allowed to send private messages","Non está permitido enviar mensaxes privadas"}. {"It is not allowed to send private messages of type \"groupchat\"","Non está permitido enviar mensaxes privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensaxes privadas á sala"}. {"Jabber Account Registration","Rexistro de conta Jabber"}. {"Jabber ID","Jabber ID"}. {"January","Xaneiro"}. {"Join IRC channel","Entrar en canle IRC"}. {"joins the room","entra na sala"}. {"Join the IRC channel here.","Únete á canle de IRC aquí."}. {"Join the IRC channel in this Jabber ID: ~s","Únete á canle de IRC con este IDE de Jabber: ~s"}. {"July","Xullo"}. {"June","Xuño"}. {"Last Activity","Última actividade"}. {"Last login","Última conexión"}. {"Last month","Último mes"}. {"Last year","Último ano"}. {"leaves the room","sae da sala"}. {"Listened Ports at ","Portos de escoita en "}. {"Listened Ports","Portos de escoita"}. {"List of modules to start","Lista de módulos a iniciar"}. {"List of rooms","Lista de salas"}. {"Low level update script","Script de actualización a baixo nivel"}. {"Make participants list public","A lista de participantes é pública"}. {"Make room CAPTCHA protected","Protexer a sala con CAPTCHA"}. {"Make room members-only","Sala só para membros"}. {"Make room moderated","Facer sala moderada"}. {"Make room password protected","Protexer a sala con contrasinal"}. {"Make room persistent","Sala permanente"}. {"Make room public searchable","Sala publicamente visible"}. {"March","Marzo"}. {"Maximum Number of Occupants","Número máximo de ocupantes"}. {"May","Maio"}. {"Membership is required to enter this room","Necesitas ser membro desta sala para poder entrar"}. {"Members:","Membros:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memorice o seu contrasinal ou escribilo nun papel colocado nun lugar seguro. En Jabber non hai unha forma automatizada para recuperar o seu contrasinal si a esquece"}. {"Memory","Memoria"}. {"Message body","Corpo da mensaxe"}. {"Middle Name","Segundo nome"}. {"Moderator privileges required","Necesítase privilexios de moderador"}. {"Modified modules","Módulos Modificados"}. {"Module","Módulo"}. {"Modules at ~p","Módulos en ~p"}. {"Modules","Módulos"}. {"Monday","Luns"}. {"Multicast","Multicast"}. {"Multi-User Chat","Salas de Charla"}. {"Name:","Nome:"}. {"Name","Nome"}. {"Never","Nunca"}. {"New Password:","Novo contrasinal:"}. {"Nickname","Alcume"}. {"Nickname Registration at ","Rexistro do alcume en "}. {"Nickname ~s does not exist in the room","O alcume ~s non existe na sala"}. {"No body provided for announce message","Non se proporcionou corpo de mensaxe para o anuncio"}. {"No Data","Sen datos"}. {"Node not found","Nodo non atopado"}. {"Node ~p","Nodo ~p"}. {"Nodes","Nodos"}. {"None","Ningún"}. {"Not Found","Non atopado"}. {"November","Novembro"}. {"Number of online users","Número de usuarios conectados"}. {"Number of registered users","Número de usuarios rexistrados"}. {"October","Outubro"}. {"Offline Messages","Mensaxes diferidas"}. {"Offline Messages:","Mensaxes sen conexión:"}. {"OK","Aceptar"}. {"Old Password:","Contrasinal anterior:"}. {"Online","Conectado"}. {"Online Users:","Usuarios conectados:"}. {"Online Users","Usuarios conectados"}. {"Only members may query archives of this room","Só membros poden consultar o arquivo de mensaxes da sala"}. {"Only moderators and participants are allowed to change the subject in this room","Só os moderadores e os participantes se lles permite cambiar o tema nesta sala"}. {"Only moderators are allowed to change the subject in this room","Só os moderadores están autorizados a cambiar o tema nesta sala"}. {"Only moderators can approve voice requests","Só os moderadores poden aprobar peticións de voz"}. {"Only occupants are allowed to send messages to the conference","Só os ocupantes poden enviar mensaxes á sala"}. {"Only occupants are allowed to send queries to the conference","Só os ocupantes poden enviar solicitudes á sala"}. {"Only service administrators are allowed to send service messages","Só os administradores do servizo teñen permiso para enviar mensaxes de servizo"}. {"Options","Opcións"}. {"Organization Name","Nome da organización"}. {"Organization Unit","Unidade da organización"}. {"Outgoing s2s Connections:","Conexións S2S saíntes:"}. {"Outgoing s2s Connections","Conexións S2S saíntes"}. {"Owner privileges required","Requírense privilexios de propietario da sala"}. {"Packet","Paquete"}. {"Password ~b","Contrasinal ~b"}. {"Password:","Contrasinal:"}. {"Password","Contrasinal"}. {"Password Verification:","Verificación da contrasinal"}. {"Password Verification","Verificación da contrasinal"}. {"Path to Dir","Ruta ao directorio"}. {"Path to File","Ruta ao ficheiro"}. {"Pending","Pendente"}. {"Period: ","Periodo: "}. {"Permanent rooms","Salas permanentes"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ten en conta que estas opcións só farán copia de seguridade da base de datos Mnesia. Se está a utilizar o módulo de ODBC, tamén necesita unha copia de seguridade da súa base de datos SQL por separado."}. {"Please, wait for a while before sending new voice request","Por favor, espera un pouco antes de enviar outra petición de voz"}. {"Pong","Pong"}. {"Port ~b","Porto ~b"}. {"Port","Porto"}. {"private, ","privado"}. {"Protocol","Protocolo"}. {"Publish-Subscribe","Publicar-Subscribir"}. {"PubSub subscriber request","Petición de subscriptor de PubSub"}. {"Queries to the conference members are not allowed in this room","Nesta sala non se permiten solicitudes aos membros da sala"}. {"RAM and disc copy","Copia en RAM e disco"}. {"RAM copy","Copia en RAM"}. {"Raw","Cru"}. {"Really delete message of the day?","¿Está seguro que quere borrar a mensaxe do dia?"}. {"Recipient is not in the conference room","O receptor non está na sala de conferencia"}. {"Register a Jabber account","Rexistrar unha conta Jabber"}. {"Registered nicknames","Alcumes rexistrados"}. {"Registered Users:","Usuarios rexistrados:"}. {"Registered Users","Usuarios rexistrados"}. {"Register","Rexistrar"}. {"Registration in mod_irc for ","Rexistro en mod_irc para"}. {"Remote copy","Copia remota"}. {"Remove All Offline Messages","Borrar Todas as Mensaxes Sen conexión"}. {"Remove","Borrar"}. {"Remove User","Eliminar usuario"}. {"Replaced by new connection","Substituído por unha nova conexión"}. {"Resources","Recursos"}. {"Restart","Reiniciar"}. {"Restart Service","Reiniciar o servizo"}. {"Restore Backup from File at ","Restaura copia de seguridade desde o ficheiro en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd (require menos memoria):"}. {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridade binaria:"}. {"Restore plain text backup immediately:","Restaurar copias de seguridade de texto plano inmediatamente:"}. {"Restore","Restaurar"}. {"Room Configuration","Configuración da Sala"}. {"Room creation is denied by service policy","Denegar crear a sala por política do servizo"}. {"Room description","Descrición da sala"}. {"Room Occupants","Ocupantes da sala"}. {"Room title","Título da sala"}. {"Roster","Lista de contactos"}. {"Roster of ","Lista de contactos de "}. {"Roster size","Tamaño da lista de contactos"}. {"RPC Call Error","Erro na chamada RPC"}. {"Running Nodes","Nodos funcionando"}. {"~s access rule configuration","Configuración das Regra de Acceso ~s"}. {"Saturday","Sábado"}. {"Script check","Comprobación de script"}. {"Search Results for ","Buscar resultados por "}. {"Search users in ","Buscar usuarios en "}. {"Send announcement to all online users","Enviar anuncio a todos os usuarios conectados"}. {"Send announcement to all online users on all hosts","Enviar anuncio a todos os usuarios conectados en todos os dominios"}. {"Send announcement to all users","Enviar anuncio a todos os usuarios"}. {"Send announcement to all users on all hosts","Enviar anuncio a todos os usuarios en todos os dominios"}. {"September","Setembro"}. {"Server ~b","Servidor ~b"}. {"Server:","Servidor:"}. {"Set message of the day and send to online users","Pór mensaxe do dia e enviar a todos os usuarios conectados"}. {"Set message of the day on all hosts and send to online users","Pór mensaxe do día en todos os dominios e enviar aos usuarios conectados"}. {"Shared Roster Groups","Grupos Compartidos"}. {"Show Integral Table","Mostrar Táboa Integral"}. {"Show Ordinary Table","Mostrar Táboa Ordinaria"}. {"Shut Down Service","Deter o servizo"}. {"~s invites you to the room ~s","~s invítache á sala ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Algúns clientes Jabber pode almacenar o contrasinal no computador, pero debe facer isto só no seu computador persoal por razóns de seguridade."}. {"~s's Offline Messages Queue","Cola de mensaxes diferidas de ~s"}. {"Start","Iniciar"}. {"Start Modules at ","Iniciar módulos en "}. {"Start Modules","Iniciar módulos"}. {"Statistics","Estatísticas"}. {"Statistics of ~p","Estatísticas de ~p"}. {"Stop","Deter"}. {"Stop Modules at ","Deter módulos en "}. {"Stop Modules","Deter módulos"}. {"Stopped Nodes","Nodos detidos"}. {"Storage Type","Tipo de almacenamento"}. {"Store binary backup:","Gardar copia de seguridade binaria:"}. {"Store plain text backup:","Gardar copia de seguridade en texto plano:"}. {"Subject","Asunto"}. {"Submit","Enviar"}. {"Submitted","Enviado"}. {"Subscription","Subscripción"}. {"Sunday","Domingo"}. {"That nickname is already in use by another occupant","Ese alcume xa está a ser usado por outro ocupante"}. {"That nickname is registered by another person","O alcume xa está rexistrado por outra persoa"}. {"The CAPTCHA is valid.","O CAPTCHA é válido."}. {"The CAPTCHA verification has failed","A verificación de CAPTCHA fallou"}. {"the password is","a contrasinal é"}. {"The password is too weak","O contrasinal é demasiado débil"}. {"The password of your Jabber account was successfully changed.","O contrasinal da súa conta Jabber cambiouse correctamente."}. {"There was an error changing the password: ","Produciuse un erro ao cambiar o contrasinal: "}. {"There was an error creating the account: ","Produciuse un erro ao crear a conta: "}. {"There was an error deleting the account: ","Produciuse un erro ao eliminar a conta: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Esta é insensible: Macbeth é o mesmo que MacBeth e Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta páxina permite crear unha conta Jabber neste servidor Jabber. o seu JID (Jabber IDentificador) será da forma: nomeusuario@servidor. Por favor le coidadosamente as instrucións para encher correctamente os campos."}. {"This page allows to unregister a Jabber account in this Jabber server.","Esta páxina permite anular o rexistro dunha conta Jabber neste servidor Jabber."}. {"This room is not anonymous","Sala non anónima"}. {"Thursday","Xoves"}. {"Time","Data"}. {"Time delay","Atraso temporal"}. {"Too many CAPTCHA requests","Demasiadas peticións de CAPTCHA"}. {"Too many unacked stanzas","Demasiadas mensaxes sen recoñecer recibilos"}. {"To","Para"}. {"To ~s","A ~s"}. {"Total rooms","Salas totais"}. {"Traffic rate limit is exceeded","Hase exedido o límite de tráfico"}. {"Transactions Aborted:","Transaccións abortadas:"}. {"Transactions Committed:","Transaccións finalizadas:"}. {"Transactions Logged:","Transaccións rexistradas:"}. {"Transactions Restarted:","Transaccións reiniciadas:"}. {"Tuesday","Martes"}. {"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. {"Unauthorized","Non autorizado"}. {"Unregister a Jabber account","Eliminar o rexistro dunha conta Jabber"}. {"Unregister","Eliminar rexistro"}. {"Update","Actualizar"}. {"Update message of the day (don't send)","Actualizar mensaxe do dia, pero non envialo"}. {"Update message of the day on all hosts (don't send)","Actualizar a mensaxe do día en todos os dominos (pero non envialo)"}. {"Update ~p","Actualizar ~p"}. {"Update plan","Plan de actualización"}. {"Update script","Script de actualización"}. {"Uptime:","Tempo desde o inicio:"}. {"Use of STARTTLS required","Requírese o uso de STARTTLS"}. {"User Management","Administración de usuarios"}. {"Username:","Nome de usuario:"}. {"Users are not allowed to register accounts so quickly","Os usuarios non están autorizados a rexistrar contas con tanta rapidez"}. {"Users Last Activity","Última actividade dos usuarios"}. {"User ~s","Usuario ~s"}. {"Users","Usuarios"}. {"User","Usuario"}. {"Validate","Validar"}. {"vCard User Search","vCard busqueda de usuario"}. {"Virtual Hosts","Hosts Virtuais"}. {"Visitors are not allowed to change their nicknames in this room","Os visitantes non teñen permitido cambiar os seus alcumes nesta sala"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes non poden enviar mensaxes a todos os ocupantes"}. {"Voice request","Petición de voz"}. {"Voice requests are disabled in this conference","As peticións de voz están desactivadas nesta sala"}. {"Wednesday","Mércores"}. {"You can later change your password using a Jabber client.","Máis tarde, pode cambiar o seu contrasinal utilizando un cliente Jabber."}. {"You have been banned from this room","fuches bloqueado nesta sala"}. {"You must fill in field \"Nickname\" in the form","Debes encher o campo \"Alcumo\" no formulario"}. {"You need a client that supports x:data and CAPTCHA to register","Necesitas un cliente con soporte de x:data e CAPTCHA para rexistrarche"}. {"You need a client that supports x:data to register the nickname","Necesitas un cliente con soporte de x:data para poder rexistrar o alcume"}. {"You need an x:data capable client to configure mod_irc settings","Necesitas un cliente con soporte de x:data para configurar as opcións de mod_irc"}. {"You need an x:data capable client to search","Necesitas un cliente con soporte de x:data para poder buscar"}. {"Your active privacy list has denied the routing of this stanza.","A súa lista de privacidade activa negou o encaminamiento desta estrofa."}. {"Your contact offline message queue is full. The message has been discarded.","A túa cola de mensaxes diferidas de contactos está chea. A mensaxe descartouse."}. {"Your Jabber account was successfully created.","A súa conta Jabber creouse correctamente."}. {"Your Jabber account was successfully deleted.","A súa conta Jabber eliminouse correctamente."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","As súas mensaxes a ~s encóntranse bloqueadas. Para desbloquear, visite ~s"}. ejabberd-18.01/priv/msgs/pl.msg0000644000232200023220000006133613225664356016730 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Zaakceptuj"}. {"Access Configuration","Konfiguracja dostępu"}. {"Access Control List Configuration","Konfiguracja listy dostępowej"}. {"Access Control Lists","Lista dostępowa"}. {"Access control lists","Listy dostępowe"}. {"Access denied by service policy","Dostęp zabroniony zgodnie z zasadami usługi"}. {"Access rules","Reguły dostępu"}. {"Access Rules","Zasady dostępu"}. {"Action on user","Wykonaj na użytkowniku"}. {"Add Jabber ID","Dodaj Jabber ID"}. {"Add New","Dodaj nowe"}. {"Add User","Dodaj użytkownika"}. {"Administration","Administracja"}. {"Administration of ","Zarządzanie "}. {"Administrator privileges required","Wymagane uprawnienia administratora"}. {"All activity","Cała aktywność"}. {"Allow users to change the subject","Pozwól użytkownikom zmieniać temat"}. {"Allow users to query other users","Pozwól użytkownikom pobierać informacje o innych użytkownikach"}. {"Allow users to send invites","Pozwól użytkownikom wysyłać zaproszenia"}. {"Allow users to send private messages","Pozwól użytkownikom wysyłać prywatne wiadomości"}. {"Allow visitors to change nickname","Pozwól uczestnikom na zmianę nicka"}. {"Allow visitors to send private messages to","Pozwól użytkownikom wysyłać prywatne wiadomości"}. {"Allow visitors to send status text in presence updates","Pozwól uczestnikom na wysyłanie statusów opisowych"}. {"All Users","Wszyscy użytkownicy"}. {"Announcements","Powiadomienia"}. {"A password is required to enter this room","Aby wejść do pokoju wymagane jest hasło"}. {"April","Kwiecień"}. {"August","Sierpień"}. {"Backup Management","Zarządzanie kopiami zapasowymi"}. {"Backup of ~p","Kopia zapasowa ~p"}. {"Backup to File at ","Zapisz kopię w pliku na "}. {"Backup","Wykonaj kopie"}. {"Bad format","Błędny format"}. {"Birthday","Data urodzenia"}. {"CAPTCHA web page","Strona internetowa CAPTCHA"}. {"Change Password","Zmień hasło"}. {"Change User Password","Zmień hasło użytkownika"}. {"Characters not allowed:","Te znaki są niedozwolone:"}. {"Chatroom configuration modified","Konfiguracja pokoju zmodyfikowana"}. {"Chatroom is created","Pokój został stworzony"}. {"Chatroom is destroyed","Pokój został usunięty"}. {"Chatroom is started","Pokój został uruchomiony"}. {"Chatroom is stopped","Pokój został zatrzymany"}. {"Chatrooms","Pokoje rozmów"}. {"Choose a username and password to register with this server","Wybierz nazwę użytkownika i hasło aby zarejestrować się na tym serwerze"}. {"Choose modules to stop","Wybierz moduły do zatrzymania"}. {"Choose storage type of tables","Wybierz typ bazy dla tablel"}. {"Choose whether to approve this entity's subscription.","Wybierz, czy akceptować subskrypcję tej jednostki"}. {"City","Miasto"}. {"Commands","Polecenia"}. {"Conference room does not exist","Pokój konferencyjny nie istnieje"}. {"Configuration","Konfiguracja"}. {"Configuration of room ~s","Konfiguracja pokoju ~s"}. {"Connected Resources:","Zasoby zalogowane:"}. {"Connections parameters","Parametry połączeń"}. {"Country","Państwo"}. {"CPU Time:","Czas CPU:"}. {"Database","Baza danych"}. {"Database Tables at ~p","Tabele bazy na ~p"}. {"Database Tables Configuration at ","Konfiguracja tabel bazy na "}. {"December","Grudzień"}. {"Default users as participants","Domyślni użytkownicy jako uczestnicy"}. {"Delete message of the day on all hosts","Usuń wiadomość dnia ze wszystkich hostów"}. {"Delete message of the day","Usuń wiadomość dnia"}. {"Delete Selected","Usuń zaznaczone"}. {"Delete User","Usuń użytkownika"}. {"Description:","Opis:"}. {"Disc only copy","Kopia tylko na dysku"}. {"Displayed Groups:","Wyświetlane grupy:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Nie podawaj swojego hasła nikomu, nawet administratorowi serwera Jabber."}. {"Dump Backup to Text File at ","Zapisz kopię zapasową w pliku tekstowym na "}. {"Dump to Text File","Wykonaj kopie do pliku tekstowego"}. {"Edit Properties","Edytuj właściwości"}. {"Either approve or decline the voice request.","Zatwierdź lub odrzuć żądanie głosowe"}. {"ejabberd IRC module","Moduł IRC ejabberd"}. {"ejabberd MUC module","Moduł MUC"}. {"ejabberd Multicast service","Serwis multicast ejabbera"}. {"ejabberd Publish-Subscribe module","Moduł Publish-Subscribe"}. {"ejabberd SOCKS5 Bytestreams module","Moduł SOCKS5 Bytestreams"}. {"ejabberd vCard module","Moduł vCard ejabberd"}. {"ejabberd Web Admin","ejabberd: Panel Administracyjny"}. {"Elements","Elementy"}. {"Email","Email"}. {"Enable logging","Włącz logowanie"}. {"Encoding for server ~b","Kodowanie znaków dla serwera ~b"}. {"End User Session","Zakończ sesję uzytkownika"}. {"Enter list of {Module, [Options]}","Wprowadź listę {Moduł, [Opcje]}"}. {"Enter nickname you want to register","Wprowadz nazwę użytkownika którego chcesz zarejestrować"}. {"Enter path to backup file","Wprowadź scieżkę do pliku kopii zapasowej"}. {"Enter path to jabberd14 spool dir","Wprowadź ścieżkę do roboczego katalogu serwera jabberd14"}. {"Enter path to jabberd14 spool file","Wprowadź ścieżkę do roboczego pliku serwera jabberd14"}. {"Enter path to text file","Wprowadź scieżkę do pliku tekstowego"}. {"Enter the text you see","Przepisz tekst z obrazka"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Wprowadź nazwę użytkownika i kodowania których chcesz używać do łączenia z serwerami IRC. Wciśnij \"Dalej\" aby ustawić więcej parametrów połączenia. Wciśnij \"Zakończ\" aby zapisać ustawienia."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Wprowadź nazwę użytkownika, port i kodowanie, których chcesz używać do łączenia z serwerami IRC"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Błąd"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Przykład: [{\"wroclaw.irc.pl\",\"utf-8\"}, {\"warszawa.irc.pl\", \"iso8859-2\"}]."}. {"Export all tables as SQL queries to a file:","Wyeksportuj wszystkie tabele jako zapytania SQL do pliku:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksportuj dane wszystkich użytkowników serwera do plików w formacie PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Eksportuj dane użytkowników z hosta do plików w formacie PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Nie udało się wydobyć JID-u z twojego żądania"}. {"Family Name","Nazwisko"}. {"February","Luty"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Wypełnij formularz aby wyszukać użytkowników Jabbera (dodaj * na koniec zapytania aby wyszukać po fragmencie)"}. {"Friday","Piątek"}. {"From","Od"}. {"From ~s","Od ~s"}. {"Full Name","Pełna nazwa"}. {"Get Number of Online Users","Pokaż liczbę zalogowanych użytkowników"}. {"Get Number of Registered Users","Pokaż liczbę zarejestrowanych użytkowników"}. {"Get User Last Login Time","Pokaż czas ostatniego zalogowania uzytkownika"}. {"Get User Password","Pobierz hasło użytkownika"}. {"Get User Statistics","Pobierz statystyki użytkownika"}. {"Group ","Grupa "}. {"Groups","Grupy"}. {"has been banned","został wykluczony"}. {"has been kicked because of an affiliation change","został wyrzucony z powodu zmiany przynależności"}. {"has been kicked because of a system shutdown","został wyrzucony z powodu wyłączenia systemu"}. {"has been kicked because the room has been changed to members-only","został wyrzucony z powodu zmiany pokoju na \"Tylko dla Członków\""}. {"has been kicked","został wyrzucony"}. {" has set the subject to: "," zmienił temat na: "}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Jeśli nie widzisz obrazka CAPTCHA, odwiedź stronę internetową."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Jeśli chcesz ustawić inne hasła, porty lub kodowania dla poszczególnych serwerów IRC, wypełnij tą listę wartościami w formacie '{\"irc server\",\"encoding\", port, \"password\"}'. Domyślne ta usługa używa kodowania \"~s\", portu ~p, bez hasła."}. {"Import Directory","Importuj katalog"}. {"Import File","Importuj plik"}. {"Import user data from jabberd14 spool file:","Importuj dane użytkownika z pliku roboczego serwera jabberd14:"}. {"Import User from File at ","Importuj użytkownika z pliku na "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importuj dane użytkowników z pliku w formacie PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importuj użytkowników z katalogu roboczego serwera jabberd14"}. {"Import Users from Dir at ","Importuj użytkowników z katalogu na "}. {"Import Users From jabberd14 Spool Files","Importuj użytkowników z plików roboczych serwera jabberd14"}. {"Improper message type","Nieprawidłowy typ wiadomości"}. {"Incoming s2s Connections:","Przychodzące połączenia s2s:"}. {"Incorrect password","Nieprawidłowe hasło"}. {"IP addresses","Adresy IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Kanał IRC (nie używaj #)"}. {"IRC server","Serwer IRC"}. {"IRC settings","Ustawienia IRC"}. {"IRC Transport","Transport IRC"}. {"IRC username","Nazwa użytkownika IRC"}. {"IRC Username","Nazwa użytkownika IRC"}. {"is now known as","jest teraz znany jako"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Użytkownik nie może wysyłać wiadomości o błędach do pokoju. Użytkownik (~s) wysłał błąd (~s) i został wyrzucony z pokoju"}. {"It is not allowed to send private messages of type \"groupchat\"","Nie można wysyłać prywatnych wiadomości typu \"groupchat\""}. {"It is not allowed to send private messages to the conference","Nie wolno wysyłac prywatnych wiadomości na konferencję"}. {"It is not allowed to send private messages","Wysyłanie prywatnych wiadomości jest zabronione"}. {"Jabber Account Registration","Zakładanie konta Jabber"}. {"Jabber ID","Jabber ID"}. {"January","Styczeń"}. {"Join IRC channel","Dołącz do kanału IRC"}. {"joins the room","dołącza do pokoju"}. {"Join the IRC channel here.","Dołącz do kanału IRC."}. {"Join the IRC channel in this Jabber ID: ~s","Dołącz do kanału IRC pod tym Jabber ID: ~s"}. {"July","Lipiec"}. {"June","Czerwiec"}. {"Last Activity","Ostatnia aktywność"}. {"Last login","Ostatnie logowanie"}. {"Last month","Miniony miesiąc"}. {"Last year","Miniony rok"}. {"leaves the room","opuszcza pokój"}. {"Listened Ports at ","Porty nasłuchujące na "}. {"Listened Ports","Porty nasłuchujące"}. {"List of modules to start","Lista modułów do uruchomienia"}. {"List of rooms","Lista pokoi"}. {"Low level update script","Skrypt aktualizacji niskiego poziomu"}. {"Make participants list public","Upublicznij listę uczestników"}. {"Make room CAPTCHA protected","Pokój zabezpieczony captchą"}. {"Make room members-only","Pokój tylko dla członków"}. {"Make room moderated","Pokój moderowany"}. {"Make room password protected","Pokój zabezpieczony hasłem"}. {"Make room persistent","Utwórz pokój na stałe"}. {"Make room public searchable","Pozwól wyszukiwać pokój"}. {"March","Marzec"}. {"Maximum Number of Occupants","Maksymalna liczba uczestników"}. {"May","Maj"}. {"Members:","Członkowie:"}. {"Membership is required to enter this room","Musisz być na liście członków tego pokoju aby do niego wejść"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Zapamiętaj swoje hasło lub zapisz je na kartce i zachowaj w bezpiecznym miejscu. Na Jabberze nie ma zautomatyzowanego systemu odzyskiwania haseł."}. {"Memory","Pamięć"}. {"Message body","Treść wiadomości"}. {"Middle Name","Drugie imię"}. {"Moderator privileges required","Wymagane uprawnienia moderatora"}. {"Modified modules","Zmodyfikowane moduły"}. {"Module","Moduł"}. {"Modules at ~p","Moduły na ~p"}. {"Modules","Moduły"}. {"Monday","Poniedziałek"}. {"Multicast","Multicast"}. {"Multi-User Chat","Wieloosobowa rozmowa"}. {"Name","Imię"}. {"Name:","Nazwa:"}. {"Never","Nigdy"}. {"New Password:","Nowe hasło:"}. {"Nickname","Nazwa użytkownika"}. {"Nickname Registration at ","Rejestracja nazwy użytkownika na "}. {"Nickname ~s does not exist in the room","Nie ma nicka ~s w tym pokoju"}. {"No body provided for announce message","Brak treści powiadomienia"}. {"No Data","Brak danych"}. {"Node not found","Węzeł nie został znaleziony"}. {"Node ~p","Węzeł ~p"}. {"Nodes","Węzły"}. {"None","Brak"}. {"Not Found","Nie znaleziono"}. {"November","Listopad"}. {"Number of online users","Liczba zalogowanych użytkowników"}. {"Number of registered users","Liczba zarejestrowanych użytkowników"}. {"October","Październik"}. {"Offline Messages:","Wiadomości offline:"}. {"Offline Messages","Wiadomości offline"}. {"OK","OK"}. {"Old Password:","Stare hasło:"}. {"Online","Dostępny"}. {"Online Users:","Użytkownicy zalogowani:"}. {"Online Users","Użytkownicy zalogowani"}. {"Only members may query archives of this room","Tylko moderatorzy mogą przeglądać archiwa tego pokoju"}. {"Only moderators and participants are allowed to change the subject in this room","Tylko moderatorzy i uczestnicy mogą zmienić temat tego pokoju"}. {"Only moderators are allowed to change the subject in this room","Tylko moderatorzy mogą zmienić temat tego pokoju"}. {"Only moderators can approve voice requests","Tylko moderatorzy mogą zatwierdzać żądania głosowe"}. {"Only occupants are allowed to send messages to the conference","Tylko uczestnicy mogą wysyłać wiadomości na konferencję"}. {"Only occupants are allowed to send queries to the conference","Tylko uczestnicy mogą wysyłać zapytania do konferencji"}. {"Only service administrators are allowed to send service messages","Tylko administratorzy mogą wysyłać wiadomości"}. {"Options","Opcje"}. {"Organization Name","Nazwa organizacji"}. {"Organization Unit","Dział"}. {"Outgoing s2s Connections:","Wychodzące połączenia s2s:"}. {"Outgoing s2s Connections","Wychodzące połączenia s2s"}. {"Owner privileges required","Wymagane uprawnienia właściciela"}. {"Packet","Pakiet"}. {"Password ~b","Hasło ~b"}. {"Password:","Hasło:"}. {"Password","Hasło"}. {"Password Verification:","Weryfikacja hasła:"}. {"Password Verification","Weryfikacja hasła"}. {"Path to Dir","Ścieżka do katalogu"}. {"Path to File","Scieżka do pliku"}. {"Pending","Oczekuje"}. {"Period: ","Przedział czasu: "}. {"Permanent rooms","Stałych pokoi"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Te opcje kopii zapasowych dotyczą tylko wbudowanej bazy danych typu Mnesia. Jeśli korzystasz z modułu ODBC, musisz wykonać kopie bazy we własnym zakresie."}. {"Please, wait for a while before sending new voice request","Proszę poczekać chwile, zanim wyślesz nowe żądanie głosowe"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","prywatny, "}. {"Protocol","Protokół"}. {"Publish-Subscribe","PubSub"}. {"PubSub subscriber request","Żądanie subskrybcji PubSub"}. {"Queries to the conference members are not allowed in this room","Informacje o członkach konferencji nie są dostępne w tym pokoju"}. {"RAM and disc copy","Kopia na dysku i w pamięci RAM"}. {"RAM copy","Kopia w pamięci RAM"}. {"Raw","Żródło"}. {"Really delete message of the day?","Na pewno usunąć wiadomość dnia?"}. {"Recipient is not in the conference room","Odbiorcy nie ma w pokoju"}. {"Register a Jabber account","Załóż konto Jabber"}. {"Registered nicknames","Zarejestrowanych nicków"}. {"Registered Users:","Użytkownicy zarejestrowani:"}. {"Registered Users","Użytkownicy zarejestrowani"}. {"Register","Zarejestruj"}. {"Registration in mod_irc for ","Rejestracja w mod_irc dla "}. {"Remote copy","Kopia zdalna"}. {"Remove All Offline Messages","Usuń wszystkie wiadomości typu 'Offline'"}. {"Remove User","Usuń użytkownika"}. {"Remove","Usuń"}. {"Replaced by new connection","Połączenie zostało zastąpione"}. {"Resources","Zasoby"}. {"Restart Service","Restart usługi"}. {"Restart","Uruchom ponownie"}. {"Restore Backup from File at ","Odtwórz bazę danych z kopii zapasowej na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Odtwórz kopię binarną podczas następnego uruchomienia ejabberd (wymaga mniej zasobów):"}. {"Restore binary backup immediately:","Natychmiast odtwórz kopię binarną:"}. {"Restore plain text backup immediately:","Natychmiast odtwórz kopię z postaci tekstowej:"}. {"Restore","Przywróć z kopii"}. {"Room Configuration","Konfiguracja pokoju"}. {"Room creation is denied by service policy","Zasady serwera zabraniają tworzyć nowe pokoje"}. {"Room description","Opis pokoju"}. {"Room Occupants","Lista uczestników"}. {"Room title","Tytuł pokoju"}. {"Roster","Lista kontaktów"}. {"Roster of ","Lista kontaktów "}. {"Roster size","Rozmiar listy kontaktów"}. {"RPC Call Error","Błąd żądania RPC"}. {"Running Nodes","Uruchomione węzły"}. {"~s access rule configuration","~s konfiguracja zasad dostępu"}. {"Saturday","Sobota"}. {"Script check","Sprawdź skrypt"}. {"Search Results for ","Wyniki wyszukiwania dla "}. {"Search users in ","Wyszukaj użytkowników w "}. {"Send announcement to all online users on all hosts","Wyślij powiadomienie do wszystkich zalogowanych użytkowników na wszystkich hostach"}. {"Send announcement to all online users","Wyślij powiadomienie do wszystkich zalogowanych użytkowników"}. {"Send announcement to all users on all hosts","Wyślij powiadomienie do wszystkich użytkowników na wszystkich hostach"}. {"Send announcement to all users","Wyślij powiadomienie do wszystkich użytkowników"}. {"September","Wrzesień"}. {"Server ~b","Serwer ~b"}. {"Server:","Serwer:"}. {"Set message of the day and send to online users","Wyślij wiadomość dnia do wszystkich zalogowanych użytkowników"}. {"Set message of the day on all hosts and send to online users","Ustaw wiadomość dnia dla wszystkich hostów i wyślij do zalogowanych uzytkowników"}. {"Shared Roster Groups","Wspólne grupy kontaktów"}. {"Show Integral Table","Pokaż tabelę całkowitą"}. {"Show Ordinary Table","Pokaż zwykłą tabelę"}. {"Shut Down Service","Wyłącz usługę"}. {"~s invites you to the room ~s","~s zaprasza Cię do pokoju ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Niektóre klienty Jabber mogą zapisywać Twoje hasło na komputerze. Używaj tej opcji tylko jeśli ufasz komputerowi na którym pracujesz."}. {"~s's Offline Messages Queue","Kolejka wiadomości offline użytkownika ~s"}. {"Start Modules at ","Uruchom moduły na "}. {"Start Modules","Uruchom moduły"}. {"Start","Uruchom"}. {"Statistics of ~p","Statystyki ~p"}. {"Statistics","Statystyki"}. {"Stop Modules at ","Zatrzymaj moduły na "}. {"Stop Modules","Zatrzymaj moduły"}. {"Stopped Nodes","Zatrzymane węzły"}. {"Stop","Zatrzymaj"}. {"Storage Type","Typ bazy"}. {"Store binary backup:","Zachowaj kopię binarną:"}. {"Store plain text backup:","Zachowaj kopię w postaci tekstowej:"}. {"Subject","Temat"}. {"Submitted","Wprowadzone"}. {"Submit","Wyślij"}. {"Subscription","Subskrypcja"}. {"Sunday","Niedziela"}. {"That nickname is already in use by another occupant","Ta nazwa użytkownika jest używana przez kogoś innego"}. {"That nickname is registered by another person","Ta nazwa użytkownika jest już zarejestrowana przez inną osobę"}. {"The CAPTCHA is valid.","Captcha jest poprawna."}. {"The CAPTCHA verification has failed","Weryfikacja CAPTCHA nie powiodła się."}. {"the password is","hasło to:"}. {"The password is too weak","Hasło nie jest wystarczająco trudne"}. {"The password of your Jabber account was successfully changed.","Hasło do Twojego konta zostało zmienione."}. {"There was an error changing the password: ","Podczas próby zmiany hasła wystąpił błąd:"}. {"There was an error creating the account: ","Wystąpił błąd podczas tworzenia konta:"}. {"There was an error deleting the account: ","Podczas usuwania konta wystąpił błąd:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Pole nie rozróżnia wielkości liter: słowo Hanna jest takie samo jak hAnna lub haNNa."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Niniejsza strona pozwala na założenie konta Jabber na tym serwerze. Twój JID (Jabber IDentyfikator) będzie miał postać: nazwa_użytkownika@serwer. Przeczytaj dokładnie instrukcję i wypełnij pola."}. {"This page allows to unregister a Jabber account in this Jabber server.","Ta strona pozwala usunąć konto Jabber z tego serwera."}. {"This room is not anonymous","Ten pokój nie jest anonimowy"}. {"Thursday","Czwartek"}. {"Time","Czas"}. {"Time delay","Opóźnienie"}. {"To","Do"}. {"Too many CAPTCHA requests","Za dużo żądań CAPTCHA"}. {"Too many unacked stanzas","Zbyt wiele niepotwierdzonych pakietów"}. {"To ~s","Do ~s"}. {"Total rooms","Wszystkich pokoi"}. {"Traffic rate limit is exceeded","Limit transferu przekroczony"}. {"Transactions Aborted:","Transakcje anulowane:"}. {"Transactions Committed:","Transakcje zakończone:"}. {"Transactions Logged:","Transakcje zalogowane:"}. {"Transactions Restarted:","Transakcje uruchomione ponownie:"}. {"Tuesday","Wtorek"}. {"Unable to generate a CAPTCHA","Nie można wygenerować CAPTCHA"}. {"Unauthorized","Nie autoryzowano"}. {"Unregister a Jabber account","Usuń konto Jabber"}. {"Unregister","Wyrejestruj"}. {"Update","Aktualizuj"}. {"Update message of the day (don't send)","Aktualizuj wiadomość dnia (bez wysyłania)"}. {"Update message of the day on all hosts (don't send)","Aktualizuj wiadomość dnia na wszystkich hostach (bez wysyłania)"}. {"Update plan","Plan aktualizacji"}. {"Update ~p","Uaktualnij ~p"}. {"Update script","Skrypt aktualizacji"}. {"Uptime:","Czas pracy:"}. {"Use of STARTTLS required","Wymagane jest użycie STARTTLS"}. {"User Management","Zarządzanie użytkownikami"}. {"Username:","Nazwa użytkownika:"}. {"Users are not allowed to register accounts so quickly","Użytkowncy nie mogą tak szybko rejestrować nowych kont"}. {"Users Last Activity","Ostatnia aktywność użytkowników"}. {"Users","Użytkownicy"}. {"User ~s","Użytkownik ~s"}. {"User","Użytkownik"}. {"Validate","Potwierdź"}. {"vCard User Search","Wyszukiwanie vCard użytkowników"}. {"Virtual Hosts","Wirtualne Hosty"}. {"Visitors are not allowed to change their nicknames in this room","Uczestnicy tego pokoju nie mogą zmieniać swoich nicków"}. {"Visitors are not allowed to send messages to all occupants","Odwiedzający nie mogą wysyłać wiadomości do wszystkich obecnych"}. {"Voice requests are disabled in this conference","Głosowe żądania są wyłączone w tym pokoju"}. {"Voice request","Żądanie głosowe"}. {"Wednesday","Środa"}. {"You can later change your password using a Jabber client.","Możesz później zmienić swoje hasło za pomocą dowolnego klienta Jabber."}. {"You have been banned from this room","Zostałeś wykluczony z tego pokoju"}. {"You must fill in field \"Nickname\" in the form","Musisz wypełnić pole \"Nazwa użytkownika\" w formularzu"}. {"You need a client that supports x:data and CAPTCHA to register","Potrzebujesz klienta obsługującego x:data aby zarejestrować nick"}. {"You need a client that supports x:data to register the nickname","Potrzebujesz klienta obsługującego x:data aby zarejestrować nick"}. {"You need an x:data capable client to configure mod_irc settings","Potrzebujesz klienta obsługującego x:data aby skonfigurować mod_irc"}. {"You need an x:data capable client to search","Potrzebujesz klienta obsługującego x:data aby wyszukiwać"}. {"Your active privacy list has denied the routing of this stanza.","Aktualna lista prywatności zabrania przesyłania tej stanzy"}. {"Your contact offline message queue is full. The message has been discarded.","Kolejka wiadomości offline adresata jest pełna. Wiadomość została odrzucona."}. {"Your Jabber account was successfully created.","Twoje konto zostało stworzone."}. {"Your Jabber account was successfully deleted.","Twoje konto zostało usunięte."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s"}. ejabberd-18.01/priv/msgs/nl.msg0000644000232200023220000006076613225664356016734 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Toegangsinstellingen"}. {"Access Control List Configuration","Instellingen van access control lists"}. {"Access control lists","Access control lists"}. {"Access Control Lists","Access control lists"}. {"Access denied by service policy","De toegang werd geweigerd door het beleid van deze dienst"}. {"Access rules","Access rules"}. {"Access Rules","Access rules"}. {"Action on user","Actie op gebruiker"}. {"Add Jabber ID","Jabber ID toevoegen"}. {"Add New","Toevoegen"}. {"Add User","Gebruiker toevoegen"}. {"Administration","Beheer"}. {"Administration of ","Beheer van "}. {"Administrator privileges required","U hebt beheerdersprivileges nodig"}. {"All activity","Alle activiteit"}. {"Allow users to change the subject","Sta gebruikers toe het onderwerp te veranderen"}. {"Allow users to query other users","Gebruikers mogen naar andere gebruikers verzoeken verzenden"}. {"Allow users to send invites","Gebruikers mogen uitnodigingen verzenden"}. {"Allow users to send private messages","Gebruikers mogen privéberichten verzenden"}. {"Allow visitors to change nickname","Sta bezoekers toe hun naam te veranderen"}. {"Allow visitors to send private messages to","Gebruikers mogen privéberichten verzenden aan"}. {"Allow visitors to send status text in presence updates","Sta bezoekers toe hun statusbericht in te stellen"}. {"All Users","Alle gebruikers"}. {"Announcements","Mededelingen"}. {"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}. {"April","April"}. {"August","Augustus"}. {"Backup","Backup"}. {"Backup Management","Backup"}. {"Backup of ~p","Backup maken van ~p"}. {"Backup to File at ","Binaire backup maken op "}. {"Bad format","Verkeerd formaat"}. {"Birthday","Geboortedatum"}. {"CAPTCHA web page","CAPTCHA webpagina."}. {"Change Password","Wachtwoord wijzigen"}. {"Change User Password","Verander Gebruikerswachtwoord"}. {"Characters not allowed:","Niet-toegestane karakters:"}. {"Chatroom configuration modified","De instellingen van de chatruimte werden veranderd"}. {"Chatroom is created","Gespreksruimte gecreëerd"}. {"Chatroom is destroyed","Gespreksruimte vernietigd"}. {"Chatroom is started","Gespreksruimte gestart"}. {"Chatroom is stopped","Gespreksruimte gestopt"}. {"Chatrooms","Groepsgesprekken"}. {"Choose a username and password to register with this server","Kies een gebruikersnaam en een wachtwoord om u te registreren op deze server"}. {"Choose modules to stop","Selecteer de modules die u wilt stoppen"}. {"Choose storage type of tables","Opslagmethode voor tabellen kiezen"}. {"Choose whether to approve this entity's subscription.","Beslis of dit verzoek tot abonneren zal worden goedgekeurd"}. {"City","Plaats"}. {"Commands","Commando's"}. {"Conference room does not exist","De chatruimte bestaat niet"}. {"Configuration","Instellingen"}. {"Configuration of room ~s","Instellingen van chatruimte ~s"}. {"Connected Resources:","Verbonden bronnen:"}. {"Connections parameters","Verbindingsparameters"}. {"Country","Land"}. {"CPU Time:","Processortijd:"}. {"Database","Database"}. {"Database Tables at ~p","Databasetabellen van ~p"}. {"Database Tables Configuration at ","Instellingen van databasetabellen op "}. {"December","December"}. {"Default users as participants","Gebruikers standaard instellen als deelnemers"}. {"Delete message of the day","Bericht van de dag verwijderen"}. {"Delete message of the day on all hosts","Verwijder bericht-van-de-dag op alle hosts"}. {"Delete Selected","Geselecteerde verwijderen"}. {"Delete User","Verwijder Gebruiker"}. {"Description:","Beschrijving:"}. {"Disc only copy","Harde schijf"}. {"Displayed Groups:","Weergegeven groepen:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Geef Uw wachtwoord aan niemand, zelfs niet aan de beheerders van deze Jabber-server."}. {"Dump Backup to Text File at ","Backup naar een tekstbestand schrijven op "}. {"Dump to Text File","Backup naar een tekstbestand schrijven"}. {"Edit Properties","Eigenschappen bewerken"}. {"Either approve or decline the voice request.","Keur stemaanvraag goed of af."}. {"ejabberd IRC module","ejabberd's IRC-module"}. {"ejabberd MUC module","ejabberd's MUC module"}. {"ejabberd Multicast service","ejabberd Multicast service"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. {"ejabberd vCard module","ejabberd's vCard-module"}. {"ejabberd Web Admin","ejabberd Webbeheer"}. {"Elements","Elementen"}. {"Email","E-mail"}. {"Enable logging","Logs aanzetten"}. {"Encoding for server ~b","Karakterset voor server ~b"}. {"End User Session","Verwijder Gebruikers-sessie"}. {"Enter list of {Module, [Options]}","Voer lijst met op te starten modules als volgt in: {Module, [Opties]}"}. {"Enter nickname you want to register","Voer de bijnaam in die u wilt registreren"}. {"Enter path to backup file","Voer pad naar backupbestand in"}. {"Enter path to jabberd14 spool dir","Voer pad naar jabberd14-spool-directory in"}. {"Enter path to jabberd14 spool file","Voer pad naar jabberd14-spool-bestand in"}. {"Enter path to text file","Voer pad naar backupbestand in"}. {"Enter the text you see","Voer de getoonde tekst in"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Voer de gebruikersnaam en de coderingen in die u wilt gebruiken voor verbindingen met IRC-servers. Klik op 'Volgende' om meer velden aan te maken. Klik op \"Voltooi' om de instellingen op te slaan."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Voer de gebruikersnaam, coderingen, poorten en wachtwoorden in die U wilt gebruiken voor het verbinden met IRC-servers"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Fout"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Voorbeeld: [{\"irc.example.org\", \"koi8-r\", 6667, \"geheim\"}, {\"vendetta.example.net\", \"iso8859-1\", 7000}, {irc,testserver.nl\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Exporteer alle tabellen als SQL-queries naar een bestand:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporteer data van alle gebruikers in de server naar PIEFXIS-bestanden (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exporteer data van alle gebruikers van een host naar PIEXFIS-bestanden (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Er kon geen JID worden ontleend uit deze stemaanvraag"}. {"Family Name","Achternaam"}. {"February","Februari"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}. {"Friday","Vrijdag"}. {"From ~s","Van ~s"}. {"From","Van"}. {"Full Name","Volledige naam"}. {"Get Number of Online Users","Aantal Aanwezige Gebruikers Opvragen"}. {"Get Number of Registered Users","Aantal Geregistreerde Gebruikers Opvragen"}. {"Get User Last Login Time","Tijd van Laatste Aanmelding Opvragen"}. {"Get User Password","Gebruikerswachtwoord Opvragen"}. {"Get User Statistics","Gebruikers-statistieken Opvragen"}. {"Group ","Groep "}. {"Groups","Groepen"}. {"has been banned","is verbannen"}. {"has been kicked because of an affiliation change","is weggestuurd vanwege een affiliatieverandering"}. {"has been kicked because of a system shutdown","is weggestuurd omdat het systeem gestopt wordt"}. {"has been kicked because the room has been changed to members-only","is weggestuurd omdat de chatruimte vanaf heden alleen toegankelijk is voor leden"}. {"has been kicked","is weggestuurd"}. {" has set the subject to: "," veranderde het onderwerp in: "}. {"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Als U het CAPTCHA-plaatje niet ziet, bezoek dan de webpagina."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Als u verschillende poorten, wachtwoorden en coderingen wilt opgeven voor elke IRC-server, vul dan deze lijst met het volgende formaat: '{\"IRC-server\", \"codering\", poort, \"wachtwoord\"}'. Standaard gebruikt deze service de codering \"~s\", poort ~p, leeg wachtwoord."}. {"Import Directory","Directory importeren"}. {"Import File","Bestand importeren"}. {"Import user data from jabberd14 spool file:","Importeer gebruikersdata via spool-bestanden van jabberd14"}. {"Import User from File at ","Importeer gebruiker via bestand op "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importeer gebruikersdata van een PIEFXIS-bestand (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importeer gebruikersdata via spool-bestanden van jabberd14"}. {"Import Users from Dir at ","Gebruikers importeren vanaf directory op "}. {"Import Users From jabberd14 Spool Files","Importeer gebruikers via spool-bestanden van jabberd14"}. {"Improper message type","Onjuist berichttype"}. {"Incorrect password","Foutief wachtwoord"}. {"IP addresses","IP-adres"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC kanaal (zonder eerste #)"}. {"IRC server","IRC-server"}. {"IRC settings","IRC instellingen"}. {"IRC Transport","IRC-transport"}. {"IRC username","Gebruikersnaam voor IRC"}. {"IRC Username","Gebruikersnaam voor IRC:"}. {"is now known as","heet nu"}. {"It is not allowed to send private messages","Het is niet toegestaan priveberichten te sturen"}. {"It is not allowed to send private messages of type \"groupchat\"","Er mogen geen privéberichten van het type \"groupchat\" worden verzonden"}. {"It is not allowed to send private messages to the conference","Er mogen geen privéberichten naar de chatruimte worden verzonden"}. {"Jabber Account Registration","Jabber-account registratie"}. {"Jabber ID","Jabber ID"}. {"January","Januari"}. {"Join IRC channel","Ga IRC kanaal binnen"}. {"joins the room","betrad de chatruimte"}. {"Join the IRC channel here.","Ga het IRC kanaal binnen"}. {"Join the IRC channel in this Jabber ID: ~s","Ga het IRC kanaal van deze Jabber ID binnen: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Laatste activiteit"}. {"Last login","Laatste Aanmelding"}. {"Last month","Afgelopen maand"}. {"Last year","Afgelopen jaar"}. {"leaves the room","verliet de chatruimte"}. {"Listened Ports at ","Openstaande poorten op "}. {"Listened Ports","Openstaande poorten"}. {"List of modules to start","Lijst met op te starten modules"}. {"List of rooms","Lijst van groepsgesprekken"}. {"Low level update script","Lowlevel script voor de opwaardering"}. {"Make participants list public","Deelnemerslijst publiek maken"}. {"Make room CAPTCHA protected","Chatruimte beveiligen met een geautomatiseerde Turing test"}. {"Make room members-only","Chatruimte enkel toegankelijk maken voor leden"}. {"Make room moderated","Chatruimte gemodereerd maken"}. {"Make room password protected","Chatruimte beveiligen met een wachtwoord"}. {"Make room persistent","Chatruimte blijvend maken"}. {"Make room public searchable","Chatruimte doorzoekbaar maken"}. {"March","Maart"}. {"Maximum Number of Occupants","Maximum aantal aanwezigen"}. {"May","Mei"}. {"Members:","Groepsleden:"}. {"Membership is required to enter this room","U moet lid zijn om deze chatruimte te kunnen betreden"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Onthou het wachtwoord, of schrijf het op en bewaar het op een veilige plaats. Met Jabber is er geen geautomatiseerde manier om het wachtwoord terug te halen als U het vergeet."}. {"Memory","Geheugen"}. {"Message body","Bericht"}. {"Middle Name","Tussennaam"}. {"Moderator privileges required","U hebt moderatorprivileges nodig"}. {"Modified modules","Gewijzigde modules"}. {"Module","Module"}. {"Modules at ~p","Modules op ~p"}. {"Modules","Modules"}. {"Monday","Maandag"}. {"Multicast","Multicast"}. {"Multi-User Chat","Groepschat"}. {"Name:","Naam:"}. {"Name","Naam"}. {"Never","Nooit"}. {"New Password:","Nieuw Wachtwoord:"}. {"Nickname","Bijnaam"}. {"Nickname Registration at ","Registratie van een bijnaam op "}. {"Nickname ~s does not exist in the room","De bijnaam ~s bestaat niet in deze chatruimte"}. {"No body provided for announce message","De mededeling bevat geen bericht"}. {"No Data","Geen gegevens"}. {"Node not found","Node niet gevonden"}. {"Node ~p","Node ~p"}. {"Nodes","Nodes"}. {"None","Geen"}. {"Not Found","Niet gevonden"}. {"November","November"}. {"Number of online users","Aantal Aanwezige Gebruikers"}. {"Number of registered users","Aantal Geregistreerde Gebruikers"}. {"October","Oktober"}. {"Offline Messages:","Offline berichten:"}. {"Offline Messages","Offline berichten"}. {"OK","OK"}. {"Old Password:","Oud Wachtwoord:"}. {"Online","Online"}. {"Online Users:","Online gebruikers:"}. {"Online Users","Online gebruikers"}. {"Only moderators and participants are allowed to change the subject in this room","Alleen moderators en deelnemers mogen het onderwerp van deze chatruimte veranderen"}. {"Only moderators are allowed to change the subject in this room","Alleen moderators mogen het onderwerp van deze chatruimte veranderen"}. {"Only moderators can approve voice requests","Alleen moderators kunnen stemaanvragen goedkeuren"}. {"Only occupants are allowed to send messages to the conference","Alleen aanwezigen mogen berichten naar de chatruimte verzenden"}. {"Only occupants are allowed to send queries to the conference","Alleen aanwezigen mogen verzoeken verzenden naar de chatruimte"}. {"Only service administrators are allowed to send service messages","Alleen beheerders van deze dienst mogen mededelingen verzenden naar alle chatruimtes"}. {"Options","Opties"}. {"Organization Name","Organisatie"}. {"Organization Unit","Afdeling"}. {"Outgoing s2s Connections:","Uitgaande s2s-verbindingen:"}. {"Outgoing s2s Connections","Uitgaande s2s-verbindingen"}. {"Owner privileges required","U hebt eigenaarsprivileges nodig"}. {"Packet","Pakket"}. {"Password ~b","Wachtwoord ~b"}. {"Password Verification:","Wachtwoord Bevestiging:"}. {"Password Verification","Wachtwoord Bevestiging"}. {"Password:","Wachtwoord:"}. {"Password","Wachtwoord"}. {"Path to Dir","Pad naar directory"}. {"Path to File","Pad naar bestand"}. {"Pending","Bezig"}. {"Period: ","Periode: "}. {"Permanent rooms","Permanente groepsgesprekken"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Merk op dat volgende opties enkel backups maken van de ingebouwde database Mnesia. Als U de ODBC module gebruikt dan moeten daarvan afzonderlijke backups gemaakt worden."}. {"Please, wait for a while before sending new voice request","Wacht s.v.p. met het maken van een nieuwe stemaanvraag."}. {"Pong","Pong"}. {"Port ~b","Poort ~b"}. {"Port","Poort"}. {"private, ","privé, "}. {"Protocol","Protocol"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub abonnee verzoek"}. {"Queries to the conference members are not allowed in this room","Er mogen geen verzoeken verzenden worden naar deelnemers in deze chatruimte"}. {"RAM and disc copy","RAM en harde schijf"}. {"RAM copy","RAM"}. {"Raw","Ruw"}. {"Really delete message of the day?","Wilt u het bericht van de dag verwijderen?"}. {"Recipient is not in the conference room","De ontvanger is niet in de chatruimte"}. {"Register a Jabber account","Registreer een Jabber-account"}. {"Registered nicknames","Geregistreerde gebruikersnamen"}. {"Registered Users:","Geregistreerde gebruikers:"}. {"Registered Users","Geregistreerde gebruikers"}. {"Register","Registreer"}. {"Registration in mod_irc for ","Registratie van "}. {"Remote copy","Op andere nodes in de cluster"}. {"Remove All Offline Messages","Verwijder alle offline berichten"}. {"Remove User","Gebruiker verwijderen"}. {"Remove","Verwijderen"}. {"Replaced by new connection","Vervangen door een nieuwe verbinding"}. {"Resources","Bronnen"}. {"Restart","Herstarten"}. {"Restart Service","Herstart Service"}. {"Restore Backup from File at ","Binaire backup direct herstellen op "}. {"Restore","Binaire backup direct herstellen"}. {"Restore binary backup after next ejabberd restart (requires less memory):","Binaire backup herstellen na herstart van ejabberd (vereist minder geheugen):"}. {"Restore binary backup immediately:","Binaire backup direct herstellen:"}. {"Restore plain text backup immediately:","Backup in een tekstbestand direct herstellen:"}. {"Room Configuration","Instellingen van de chatruimte"}. {"Room creation is denied by service policy","De aanmaak van de chatruimte is verhinderd door de instellingen van deze server"}. {"Room description","Beschrijving"}. {"Room Occupants","Aantal aanwezigen"}. {"Room title","Naam van de chatruimte"}. {"Roster of ","Roster van "}. {"Roster","Roster"}. {"Roster size","Contactlijst Groote"}. {"RPC Call Error","RPC-oproepfout"}. {"Running Nodes","Draaiende nodes"}. {"~s access rule configuration","Access rules op ~s"}. {"Saturday","Zaterdag"}. {"Script check","Controle van script"}. {"Search Results for ","Zoekresultaten voor "}. {"Search users in ","Gebruikers zoeken in "}. {"Send announcement to all online users","Mededeling verzenden naar alle online gebruikers"}. {"Send announcement to all online users on all hosts","Mededeling verzenden naar alle online gebruikers op alle virtuele hosts"}. {"Send announcement to all users","Mededeling verzenden naar alle gebruikers"}. {"Send announcement to all users on all hosts","Stuur aankondiging aan alle gebruikers op alle hosts"}. {"September","September"}. {"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Bericht van de dag instellen en verzenden naar online gebruikers"}. {"Set message of the day on all hosts and send to online users","Stel bericht-van-de-dag in op alle hosts en stuur naar aanwezige gebruikers"}. {"Shared Roster Groups","Gedeelde rostergroepen"}. {"Show Integral Table","Volledige tabel laten zien"}. {"Show Ordinary Table","Deel van tabel laten zien"}. {"Shut Down Service","Stop Service"}. {"~s invites you to the room ~s","~s nodigt je uit voor het groepsgesprek ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Sommige Jabber-clienten kunnen het wachtwoord opslaan op Uw computer. Gebruik deze mogelijkheid alleen als U vertrouwd dat Uw computer afdoende beveiligd is."}. {"~s's Offline Messages Queue","offline berichten van ~s"}. {"Start Modules at ","Modules starten op "}. {"Start Modules","Modules starten"}. {"Start","Starten"}. {"Statistics of ~p","Statistieken van ~p"}. {"Statistics","Statistieken"}. {"Stop Modules at ","Modules stoppen op "}. {"Stop Modules","Modules stoppen"}. {"Stopped Nodes","Gestopte nodes"}. {"Stop","Stoppen"}. {"Storage Type","Opslagmethode"}. {"Store binary backup:","Binaire backup maken:"}. {"Store plain text backup:","Backup naar een tekstbestand schrijven:"}. {"Subject","Onderwerp"}. {"Submitted","Verzonden"}. {"Submit","Verzenden"}. {"Subscription","Inschrijving"}. {"Sunday","Zondag"}. {"That nickname is already in use by another occupant","Deze bijnaam is al in gebruik door een andere aanwezige"}. {"That nickname is registered by another person","Deze bijnaam is al geregistreerd door iemand anders"}. {"The CAPTCHA is valid.","De geautomatiseerde Turing-test is geslaagd."}. {"The CAPTCHA verification has failed","De CAPTCHA-verificatie is mislukt"}. {"the password is","het wachtwoord is"}. {"The password is too weak","Het wachtwoord is te zwak"}. {"The password of your Jabber account was successfully changed.","Het wachtwoord van Uw Jabber-account is succesvol veranderd."}. {"There was an error changing the password: ","Er was een fout bij het veranderen van het wachtwoord:"}. {"There was an error creating the account: ","Er was een fout bij het creeern van de account:"}. {"There was an error deleting the account: ","Er was een fout bij het verwijderen van de account."}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Dit is niet hoofdlettergevoelig: macbeth is hetzelfde als MacBeth en Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Deze pagina maakt het mogelijk een Jabber-account te registreren op deze server. Uw JID (Jabber IDentiteit) zal er als volg uit zien: gebruikersnaam@server. Lees de instructies zorgvuldig teneinde de velden correct in te vullen."}. {"This page allows to unregister a Jabber account in this Jabber server.","Deze pagina maakt het mogelijk een Jabber-account op deze server op te heffen."}. {"This room is not anonymous","Deze chatruimte is niet anoniem"}. {"Thursday","Donderdag"}. {"Time delay","Vertraging"}. {"Time","Tijd"}. {"To","Aan"}. {"Too many CAPTCHA requests","Te veel CAPTCHA-aanvragen"}. {"Too many unacked stanzas","Te veel niet-bevestigde stanzas"}. {"To ~s","Naar ~s"}. {"Total rooms","Aantal groepsgesprekken"}. {"Traffic rate limit is exceeded","Dataverkeerslimiet overschreden"}. {"Transactions Aborted:","Afgebroken transacties:"}. {"Transactions Committed:","Bevestigde transacties:"}. {"Transactions Logged:","Gelogde transacties:"}. {"Transactions Restarted:","Herstarte transacties:"}. {"Tuesday","Dinsdag"}. {"Unable to generate a CAPTCHA","Het generen van een CAPTCHA is mislukt"}. {"Unauthorized","Niet geautoriseerd"}. {"Unregister a Jabber account","Opheffen van Jabber-account"}. {"Unregister","Opheffen"}. {"Update","Bijwerken"}. {"Update message of the day (don't send)","Bericht van de dag bijwerken (niet verzenden)"}. {"Update message of the day on all hosts (don't send)","Verander bericht-van-de-dag op alle hosts (niet versturen)"}. {"Update plan","Plan voor de opwaardering"}. {"Update ~p","Opwaarderen van ~p"}. {"Update script","Script voor de opwaardering"}. {"Uptime:","Uptime:"}. {"Use of STARTTLS required","Gebruik van STARTTLS is vereist"}. {"User","Gebruiker"}. {"User Management","Gebruikersbeheer"}. {"Username:","Gebruikersnaam:"}. {"Users are not allowed to register accounts so quickly","Het is gebruikers niet toegestaan zo snel achter elkaar te registreren"}. {"User ~s","Gebruiker ~s"}. {"Users","Gebruikers"}. {"Users Last Activity","Laatste activiteit van gebruikers"}. {"Validate","Bevestigen"}. {"vCard User Search","Gebruikers zoeken"}. {"Virtual Hosts","Virtuele hosts"}. {"Visitors are not allowed to change their nicknames in this room","Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal"}. {"Visitors are not allowed to send messages to all occupants","Bezoekers mogen geen berichten verzenden naar alle aanwezigen"}. {"Voice requests are disabled in this conference","Stemaanvragen zijn uitgeschakeld voor deze chatruimte"}. {"Voice request","Stemaanvraag"}. {"Wednesday","Woensdag"}. {"You can later change your password using a Jabber client.","U can het wachtwoord later veranderen met een Jabber-client."}. {"You have been banned from this room","U werd verbannen uit deze chatruimte"}. {"You must fill in field \"Nickname\" in the form","U moet het veld \"bijnaam\" invullen"}. {"You need a client that supports x:data and CAPTCHA to register","U hebt een client nodig die x:data en CAPTCHA ondersteunt om een bijnaam te registreren"}. {"You need a client that supports x:data to register the nickname","U hebt een client nodig die x:data ondersteunt om een bijnaam te registreren"}. {"You need an x:data capable client to configure mod_irc settings","U hebt een client nodig die x:data ondersteunt om dit IRC-transport in te stellen"}. {"You need an x:data capable client to search","U hebt een client nodig die x:data ondersteunt om te zoeken"}. {"Your active privacy list has denied the routing of this stanza.","Uw actieve privacy-lijst verbied het routeren van dit stanza."}. {"Your contact offline message queue is full. The message has been discarded.","Te veel offline berichten voor dit contactpersoon. Het bericht is niet opgeslagen."}. {"Your Jabber account was successfully created.","Uw Jabber-account is succesvol gecreeerd."}. {"Your Jabber account was successfully deleted.","Uw Jabber-account is succesvol verwijderd."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s"}. ejabberd-18.01/priv/msgs/he.msg0000644000232200023220000006737713225664356016724 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","קבל"}. {"Access Configuration","תצורת גישה"}. {"Access Control List Configuration","תצורת רשימת בקרת גישה"}. {"Access control lists","רשימות בקרת גישה"}. {"Access Control Lists","רשימות בקרת גישה"}. {"Access denied by service policy","גישה נדחתה על ידי פוליסת שירות"}. {"Access rules","כללי גישה"}. {"Access Rules","כללי גישה"}. {"Action on user","פעולה על משתמש"}. {"Add Jabber ID","הוסף מזהה Jabber"}. {"Add New","הוסף חדש"}. {"Add User","הוסף משתמש"}. {"Administration of ","ניהול של "}. {"Administration","הנהלה"}. {"Administrator privileges required","נדרשות הרשאות מנהל"}. {"All activity","כל פעילות"}. {"Allow users to change the subject","התר למשתמשים לשנות את הנושא"}. {"Allow users to query other users","התר למשתמשים לתשאל משתמשים אחרים"}. {"Allow users to send invites","התר למשתמשים לשלוח הזמנות"}. {"Allow users to send private messages","התר למשתמשים לשלוח הודעות פרטיות"}. {"Allow visitors to change nickname","התר למבקרים לשנות שם כינוי"}. {"Allow visitors to send private messages to","התר למבקרים לשלוח הודעות פרטיות אל"}. {"Allow visitors to send status text in presence updates","התר למבקרים לשלוח טקסט מצב בתוך עדכוני נוכחות"}. {"All Users","כל המשתמשים"}. {"Announcements","בשורות"}. {"A password is required to enter this room","נדרשת סיסמה כדי להיכנס אל חדר זה"}. {"April","אפריל"}. {"August","אוגוסט"}. {"Backup Management","ניהול גיבוי"}. {"Backup of ~p","גיבוי של ~p"}. {"Backup to File at ","גבה לקובץ אצל "}. {"Backup","גיבוי"}. {"Bad format","פורמט רע"}. {"Birthday","יום הולדת"}. {"CAPTCHA web page","עמוד רשת CAPTCHA"}. {"Change Password","שנה סיסמה"}. {"Change User Password","שנה סיסמת משתמש"}. {"Characters not allowed:","תווים לא מורשים:"}. {"Chatroom configuration modified","תצורת חדר שיחה שונתה"}. {"Chatroom is created","חדר שיחה נוצר כעת"}. {"Chatroom is destroyed","חדר שיחה הינו הרוס"}. {"Chatroom is started","חדר שיחה מותחל כעת"}. {"Chatroom is stopped","חדר שיחה הינו מופסק"}. {"Chatrooms","חדרי שיחה"}. {"Choose a username and password to register with this server","בחר שם משתמש וסיסמה כדי להירשם בעזרת שרת זה"}. {"Choose modules to stop","בחר מודולים להפסקה"}. {"Choose storage type of tables","בחר טיפוס אחסון של טבלאות"}. {"Choose whether to approve this entity's subscription.","בחר האם לאשר את ההרשמה של ישות זו."}. {"City","עיר"}. {"Commands","פקודות"}. {"Conference room does not exist","חדר ועידה לא קיים"}. {"Configuration of room ~s","תצורת חדר ~s"}. {"Configuration","תצורה"}. {"Connected Resources:","משאבים מחוברים:"}. {"Connections parameters","פרמטרים של חיבור"}. {"Country","ארץ"}. {"CPU Time:","זמן מחשב (CPU):"}. {"Database Tables at ~p","טבלאות מסד נתונים אצל ~p"}. {"Database Tables Configuration at ","תצורת טבלאות מסד נתונים אצל "}. {"Database","מסד נתונים"}. {"December","דצמבר"}. {"Default users as participants","משתמשים שגרתיים כמשתתפים"}. {"Delete message of the day on all hosts","מחק את בשורת היום בכל המארחים"}. {"Delete message of the day","מחק את בשורת היום"}. {"Delete Selected","מחק נבחרות"}. {"Delete User","מחק משתמש"}. {"Description:","תיאור:"}. {"Disc only copy","העתק של תקליטור בלבד"}. {"Displayed Groups:","קבוצות מוצגות:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","אל תגלה את הסיסמה שלך לאף אחד, אפילו לא למנהלים של שרת Jabber."}. {"Dump Backup to Text File at ","השלך גיבוי לקובץ טקסט אצל "}. {"Dump to Text File","השלך לקובץ טקסט"}. {"Edit Properties","ערוך מאפיינים"}. {"Either approve or decline the voice request.","אשר או דחה בקשת ביטוי."}. {"ejabberd IRC module","מודול IRC של ejabberd"}. {"ejabberd MUC module","מודול MUC של ejabberd"}. {"ejabberd Multicast service","שירות שידור מרובב של ejabberd"}. {"ejabberd Publish-Subscribe module","מודול Publish-Subscribe של ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","מודול SOCKS5 Bytestreams של ejabberd"}. {"ejabberd vCard module","מודול vCard של ejabberd"}. {"ejabberd Web Admin","מנהל רשת ejabberd"}. {"Elements","אלמנטים"}. {"Email","דוא״ל"}. {"Enable logging","אפשר רישום פעילות"}. {"Encoding for server ~b","קידוד עבור שרת ~b"}. {"End User Session","סיים סשן משתמש"}. {"Enter list of {Module, [Options]}","הזן רשימה של {מודול, [אפשרויות]}"}. {"Enter nickname you want to register","הזן שם כינוי אשר ברצונך לרשום"}. {"Enter path to backup file","הזן נתיב לקובץ גיבוי"}. {"Enter path to jabberd14 spool dir","הזן נתיב למדור סליל (spool dir) של jabberd14"}. {"Enter path to jabberd14 spool file","הזן נתיב לקובץ סליל (spool file) של jabberd14"}. {"Enter path to text file","הזן נתיב לקובץ טקסט"}. {"Enter the text you see","הזן את הכיתוב שאתה רואה"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","הזן שם משתמש וקידודים בהם ברצונך להשתמש לצורך התחברות לשרתי IRC. לחץ 'הבא' כדי להשיג עוד שדות למילוי. לחץ 'סיים' כדי לשמור הגדרות."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","הזן שם משתמש, קידודים, פורטים וסיסמאות בהם ברצונך להשתמש לצורך התחברות לשרתי IRC"}. {"Erlang Jabber Server","שרת ג׳אבּר Erlang"}. {"Error","שגיאה"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","דוגמא: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","יצא את כל הטבלאות בתור שאילתות SQL לתוך קובץ:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך שרת זה לתוך קבצי PIEFXIS ‏(XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך מארח לתוך קבצי PIEFXIS ‏(XEP-0227):"}. {"Failed to extract JID from your voice request approval","נכשל לחלץ JID מתוך אישור בקשת הביטוי שלך"}. {"Family Name","שם משפחה"}. {"February","פברואר"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","מלא את הטופס כדי לחפש אחר כל משתמש Jabber מבוקש (באפשרותך להוסיף * בסוף שדה כדי להתאים למחרוזת-משנה)"}. {"Friday","יום שישי"}. {"From ~s","מאת ~s"}. {"From","מאת"}. {"Full Name","שם מלא"}. {"Get Number of Online Users","השג מספר של משתמשים מקוונים"}. {"Get Number of Registered Users","השג מספר של משתמשים רשומים"}. {"Get User Last Login Time","השג זמן כניסה אחרון של משתמש"}. {"Get User Password","השג סיסמת משתמש"}. {"Get User Statistics","השג סטטיסטיקת משתמש"}. {"Groups","קבוצות"}. {"Group ","קבוצה "}. {"has been banned","נאסר/ה"}. {"has been kicked because of an affiliation change","נבעט/ה משום שינוי סינוף"}. {"has been kicked because of a system shutdown","נבעט/ה משום כיבוי מערכת"}. {"has been kicked because the room has been changed to members-only","נבעט/ה משום שהחדר שונה אל חברים-בלבד"}. {"has been kicked","נבעט/ה"}. {" has set the subject to: "," הגדיר/ה את הנושא אל: "}. {"Host","מארח"}. {"If you don't see the CAPTCHA image here, visit the web page.","אם אינך רואה תמונת CAPTCHA כאן, בקר בעמוד רשת."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","אם ברצונך לציין פורטים, סיסמאות, קידודים אחרים עבור שרתים של IRC, מלא את רשימה זו עם ערכים בפורמט '{\"irc server\", \"encoding\", port, \"password\"}'. באופן שגרתי שירות זה משתמש בקידוד \"~s\", פורט ~p, סיסמה ריקה."}. {"Import Directory","ייבוא מדור"}. {"Import File","ייבוא קובץ"}. {"Import user data from jabberd14 spool file:","יבא נתוני משתמש מתוך קובץ סליל (spool file) של jabberd14:"}. {"Import User from File at ","ייבוא משתמש מתוך קובץ אצל "}. {"Import users data from a PIEFXIS file (XEP-0227):","יבא מידע משתמשים מתוך קובץ PIEFXIS ‏(XEP-0227):"}. {"Import users data from jabberd14 spool directory:","יבא נתוני משתמשים מתוך מדור סליל (spool directory) של jabberd14:"}. {"Import Users from Dir at ","ייבוא משתמשים מתוך מדור אצל "}. {"Import Users From jabberd14 Spool Files","יבא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14"}. {"Improper message type","טיפוס הודעה לא מתאים"}. {"Incoming s2s Connections:","חיבורי s2s נכנסים:"}. {"Incorrect password","מילת מעבר שגויה"}. {"IP addresses","כתובות IP"}. {"IP","‫IP"}. {"IRC channel (don't put the first #)","ערוץ IRC (אל תשים סימן # ראשון)"}. {"IRC server","שרת IRC"}. {"IRC settings","הגדרות IRC"}. {"IRC Transport","טרנספורט IRC"}. {"IRC username","שם משתמש IRC"}. {"IRC Username","שם משתמש IRC"}. {"is now known as","ידועה כעת בכינוי"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","אין זה מותר לשלוח הודעות שגיאה לחדר. משתתף זה (~s) שלח הודעת שגיאה (~s) ונבעט מתוך החדר"}. {"It is not allowed to send private messages of type \"groupchat\"","אין זה מותר לשלוח הודעות פרטיות מן טיפוס \"groupchat\""}. {"It is not allowed to send private messages to the conference","אין זה מותר לשלוח הודעות פרטיות לועידה"}. {"It is not allowed to send private messages","אין זה מותר לשלוח הודעות פרטיות"}. {"Jabber Account Registration","רישום חשבון Jabber"}. {"Jabber ID","מזהה Jabber"}. {"January","ינואר"}. {"Join IRC channel","הצטרף לערוץ IRC"}. {"joins the room","נכנס/ת אל החדר"}. {"Join the IRC channel here.","הצטרף לערוץ IRC כאן."}. {"Join the IRC channel in this Jabber ID: ~s","הצטרף לערוץ IRC במזהה Jabber זה: ~s"}. {"July","יולי"}. {"June","יוני"}. {"Last Activity","פעילות אחרונה"}. {"Last login","כניסה אחרונה"}. {"Last month","חודש אחרון"}. {"Last year","שנה אחרונה"}. {"leaves the room","עוזב/ת את החדר"}. {"Listened Ports at ","פורטים מואזנים אצל "}. {"Listened Ports","פורטים מואזנים"}. {"List of modules to start","רשימה של מודולים להפעלה"}. {"List of rooms","רשימה של חדרים"}. {"Low level update script","תסריט עדכון Low level"}. {"Make participants list public","הפוך רשימת משתתפים לפומבית"}. {"Make room CAPTCHA protected","הפוך חדר לחדר מוגן CAPTCHA"}. {"Make room members-only","הפוך חדר לחדר עבור חברים-בלבד"}. {"Make room moderated","הפוך חדר לחדר מבוקר"}. {"Make room password protected","הפוך חדר לחדר מוגן במילת מעבר"}. {"Make room persistent","הפוך חדר לחדר קבוע"}. {"Make room public searchable","הפוך חדר לחדר שנתון לחיפוש פומבי"}. {"March","מרץ"}. {"Maximum Number of Occupants","מספר מרבי של נוכחים"}. {"May","מאי"}. {"Membership is required to enter this room","נדרשת חברות כדי להיכנס אל חדר זה"}. {"Members:","חברים:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","שנן את הסיסמה שלך, או רשום אותה בנייר שמור במקום בטוח. אצל Jabber אין דרך אוטומטית לשחזר את הסיסמה שלך במידה וזו תישמט מתוך זיכרונך."}. {"Memory","זיכרון"}. {"Message body","גוף הודעה"}. {"Middle Name","שם אמצעי"}. {"Moderator privileges required","נדרשות הרשאות אחראי"}. {"Modified modules","מודולים שהותאמו"}. {"Modules at ~p","מודולים אצל ~p"}. {"Modules","מודולים"}. {"Module","מודול"}. {"Monday","יום שני"}. {"Multicast","שידור מרובב"}. {"Multi-User Chat","שיחה מרובת משתמשים"}. {"Name:","שם:"}. {"Name","שם"}. {"Never","אף פעם"}. {"New Password:","סיסמה חדשה:"}. {"Nickname Registration at ","רישום שם כינוי אצל "}. {"Nickname ~s does not exist in the room","שם כינוי ~s לא קיים בחדר"}. {"Nickname","שם כינוי"}. {"No body provided for announce message","לא סופק גוף עבור הודעת בשורה"}. {"No Data","אין מידע"}. {"Node not found","צומת לא נמצא"}. {"Node ~p","צומת ~p"}. {"Nodes","צמתים"}. {"None","אין"}. {"Not Found","לא נמצא"}. {"November","נובמבר"}. {"Number of online users","מספר של משתמשים מקוונים"}. {"Number of registered users","מספר של משתמשים רשומים"}. {"October","אוקטובר"}. {"Offline Messages:","הודעות לא מקוונות:"}. {"Offline Messages","הודעות לא מקוונות"}. {"OK","אישור"}. {"Old Password:","סיסמה ישנה:"}. {"Online Users:","משתמשים מקוונים:"}. {"Online Users","משתמשים מקוונים"}. {"Online","מקוון"}. {"Only members may query archives of this room","רק חברים רשאים לתשאל ארכיונים של חדר זה"}. {"Only moderators and participants are allowed to change the subject in this room","רק אחראים ומשתתפים רשאים לשנות את הנושא בחדר זה"}. {"Only moderators are allowed to change the subject in this room","רק אחראים רשאים לשנות את הנושא בחדר זה"}. {"Only moderators can approve voice requests","רק אחראים יכולים לאשר בקשות ביטוי"}. {"Only occupants are allowed to send messages to the conference","רק נוכחים רשאים לשלוח הודעות אל הועידה"}. {"Only occupants are allowed to send queries to the conference","רק נוכחים רשאים לשלוח שאילתות אל הועידה"}. {"Only service administrators are allowed to send service messages","רק מנהלי שירות רשאים לשלוח הודעות שירות"}. {"Options","אפשרויות"}. {"Organization Name","שם ארגון"}. {"Organization Unit","יחידת איגוד"}. {"Outgoing s2s Connections:","חיבורי s2s יוצאים:"}. {"Outgoing s2s Connections","חיבורי s2s יוצאים"}. {"Owner privileges required","נדרשות הרשאות בעלים"}. {"Packet","חבילת מידע"}. {"Password ~b","סיסמה ~b"}. {"Password Verification:","אימות סיסמה:"}. {"Password Verification","אימות סיסמה"}. {"Password:","סיסמה:"}. {"Password","סיסמה"}. {"Path to Dir","נתיב למדור"}. {"Path to File","נתיב לקובץ"}. {"Pending","ממתינות"}. {"Period: ","משך זמן: "}. {"Permanent rooms","חדרים קבועים"}. {"Ping","פינג"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","אנא שים לב כי אפשרויות אלו יגבו את מסד הנתונים המובנה Mnesia בלבד. אם הינך עושה שימוש במודול ODBC, עליך גם לגבות את מסד הנתונים SQL אשר מצוי ברשותך בנפרד."}. {"Please, wait for a while before sending new voice request","אנא, המתן לזמן מה לפני שליחת בקשת ביטוי חדשה"}. {"Pong","פונג"}. {"Port ~b","פורט ~b"}. {"Port","פורט"}. {"private, ","פרטי, "}. {"Protocol","פרוטוקול"}. {"Publish-Subscribe","‫Publish-Subscribe"}. {"PubSub subscriber request","בקשת מנוי PubSub"}. {"Queries to the conference members are not allowed in this room","שאילתות אל חברי הועידה אינן מותרות בחדר זה"}. {"RAM and disc copy","העתק RAM וגם תקליטור"}. {"RAM copy","העתק RAM"}. {"Raw","גולמי"}. {"Really delete message of the day?","באמת למחוק את בשורת היום?"}. {"Recipient is not in the conference room","מקבל אינו מצוי בחדר הועידה"}. {"Register a Jabber account","רשום חשבון Jabber"}. {"Registered nicknames","שמות כינוי רשומים"}. {"Registered Users:","משתמשים רשומים:"}. {"Registered Users","משתמשים רשומים"}. {"Register","הרשם"}. {"Registration in mod_irc for ","רישום בתוך mod_irc עבור "}. {"Remote copy","העתק מרוחק"}. {"Remove All Offline Messages","הסר את כל ההודעות הלא מקוונות"}. {"Remove User","הסר משתמש"}. {"Remove","הסר"}. {"Replaced by new connection","הוחלף בחיבור חדש"}. {"Resources","משאבים"}. {"Restart Service","אתחל שירות"}. {"Restart","אתחל"}. {"Restore Backup from File at ","שחזר גיבוי מתוך קובץ אצל "}. {"Restore binary backup after next ejabberd restart (requires less memory):","שחזר גיבוי בינארי לאחר האתחול הבא של ejabberd (מצריך פחות זיכרון):"}. {"Restore binary backup immediately:","שחזר גיבוי בינארי לאלתר:"}. {"Restore plain text backup immediately:","שחזר גיבוי טקסט גלוי (plain text) לאלתר:"}. {"Restore","שחזר"}. {"Room Configuration","תצורת חדר"}. {"Room creation is denied by service policy","יצירת חדר נדחתה על ידי פוליסת שירות"}. {"Room description","תיאור חדר"}. {"Room Occupants","נוכחי חדר"}. {"Room title","כותרת חדר"}. {"Roster of ","רשימה של "}. {"Roster size","גודל רשימה"}. {"Roster","רשימה"}. {"RPC Call Error","שגיאת קריאת RPC"}. {"Running Nodes","צמתים מורצים"}. {"~s access rule configuration","~s תצורת כללי גישה"}. {"Saturday","יום שבת"}. {"Script check","בדיקת תסריט"}. {"Search Results for ","תוצאות חיפוש עבור "}. {"Search users in ","חיפוש משתמשים אצל "}. {"Send announcement to all online users on all hosts","שלח בשורה לכל המשתמשים המקוונים בכל המארחים"}. {"Send announcement to all online users","שלח בשורה לכל המשתמשים המקוונים"}. {"Send announcement to all users on all hosts","שלח בשורה לכל המשתמשים בכל המארחים"}. {"Send announcement to all users","שלח בשורה לכל המשתמשים"}. {"September","ספטמבר"}. {"Server ~b","שרת ~b"}. {"Server:","שרת:"}. {"Set message of the day and send to online users","קבע את בשורת היום ושלח למשתמשים מקוונים"}. {"Set message of the day on all hosts and send to online users","קבע את בשורת היום בכל המארחים ושלח למשתמשים מקוונים"}. {"Shared Roster Groups","קבוצות רשימה משותפות"}. {"Show Integral Table","הצג טבלה אינטגרלית"}. {"Show Ordinary Table","הצג טבלה רגילה"}. {"Shut Down Service","כבה שירות"}. {"~s invites you to the room ~s","‫~s מזמינך לחדר ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","ישנם לקוחות Jabber אשר מסוגלים לאחסן את הסיסמה שלך בתוך המחשב, אולם עליך לעשות זאת רק בתוך המחשב האישי שלך מסיבות ביטחוניות."}. {"~s's Offline Messages Queue","תור הודעות לא מקוונות של ~s"}. {"Start Modules at ","התחל מודולים אצל "}. {"Start Modules","התחל מודולים"}. {"Start","התחל"}. {"Statistics of ~p","סטטיסטיקות של ~p"}. {"Statistics","סטטיסטיקה"}. {"Stop Modules at ","הפסק מודולים אצל "}. {"Stop Modules","הפסק מודולים"}. {"Stopped Nodes","צמתים שנפסקו"}. {"Stop","הפסק"}. {"Storage Type","טיפוס אחסון"}. {"Store binary backup:","אחסן גיבוי בינארי:"}. {"Store plain text backup:","אחסן גיבוי טקסט גלוי (plain text):"}. {"Subject","נושא"}. {"Submitted","נשלח"}. {"Submit","שלח"}. {"Subscription","הרשמה"}. {"Sunday","יום ראשון"}. {"That nickname is already in use by another occupant","שם כינוי זה כבר מצוי בשימוש על ידי נוכח אחר"}. {"That nickname is registered by another person","שם כינוי זה הינו רשום על ידי מישהו אחר"}. {"The CAPTCHA is valid.","‏CAPTCHA הינה תקפה."}. {"The CAPTCHA verification has failed","אימות CAPTCHA נכשל"}. {"The password is too weak","הסיסמה חלשה מדי"}. {"the password is","הסיסמה היא"}. {"The password of your Jabber account was successfully changed.","סיסמת חשבון Jabber שונתה בהצלחה."}. {"There was an error changing the password: ","אירעה שגיאה בשינוי הסיסמה: "}. {"There was an error creating the account: ","אירעה שגיאה ביצירת החשבון: "}. {"There was an error deleting the account: ","אירעה שגיאה במחיקת החשבון: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","חלק זה אינו ער לרישיות: macbeth הינה זהה למחרוזת MacBeth וגם Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","עמוד זה מתיר ליצור חשבון Jabber בשרת Jabber זה. כתובת JID ‏(Jabber IDentifier) תגובש באופן של: username@server. אנא קרא בזהירות את ההוראות למילוי נכון של השדות."}. {"This page allows to unregister a Jabber account in this Jabber server.","עמוד זה מתיר לך לבטל רישום של חשבון Jabber בתוך שרת Jabber זה."}. {"This room is not anonymous","חדר זה אינו אנונימי"}. {"Thursday","יום חמישי"}. {"Time delay","זמן שיהוי"}. {"Time","זמן"}. {"Too many CAPTCHA requests","יותר מדי בקשות CAPTCHA"}. {"Too many unacked stanzas","יותר מדי סטנזות בלי אישורי קבלה"}. {"To ~s","אל ~s"}. {"Total rooms","חדרים סה״כ"}. {"To","לכבוד"}. {"Traffic rate limit is exceeded","מגבלת שיעור תעבורה נחצתה"}. {"Transactions Aborted:","טרנזקציות שבוטלו:"}. {"Transactions Committed:","טרנזקציות שבוצעו:"}. {"Transactions Logged:","טרנזקציות שנרשמו:"}. {"Transactions Restarted:","טרנזקציות שהותחלו מחדש:"}. {"Tuesday","יום שלישי"}. {"Unable to generate a CAPTCHA","אין אפשרות להפיק CAPTCHA"}. {"Unauthorized","לא מורשה"}. {"Unregister a Jabber account","בטל רישום חשבון Jabber"}. {"Unregister","בטל רישום"}. {"Update message of the day (don't send)","עדכן את בשורת היום (אל תשלח)"}. {"Update message of the day on all hosts (don't send)","עדכן את בשורת היום בכל המארחים (אל תשלח)"}. {"Update plan","תכנית עדכון"}. {"Update ~p","עדכון ~p"}. {"Update script","תסריט עדכון"}. {"Update","עדכן"}. {"Uptime:","זמן פעילות:"}. {"Use of STARTTLS required","נדרש שימוש של STARTTLS"}. {"User Management","ניהול משתמשים"}. {"Username:","שם משתמש:"}. {"Users are not allowed to register accounts so quickly","משתמשים אינם מורשים לרשום חשבונות כל כך במהירות"}. {"Users Last Activity","פעילות משתמשים אחרונה"}. {"User ~s","משתמש ~s"}. {"Users","משתמשים"}. {"User","משתמש"}. {"Validate","הענק תוקף"}. {"vCard User Search","חיפוש משתמש vCard"}. {"Virtual Hosts","מארחים מדומים"}. {"Visitors are not allowed to change their nicknames in this room","מבקרים אינם מורשים לשנות את שמות הכינויים שלהם בחדר זה"}. {"Visitors are not allowed to send messages to all occupants","מבקרים אינם מורשים לשלוח הודעות אל כל הנוכחים"}. {"Voice requests are disabled in this conference","בקשות ביטוי מנוטרלות בועידה זו"}. {"Voice request","בקשת ביטוי"}. {"Wednesday","יום רביעי"}. {"You can later change your password using a Jabber client.","באפשרותך לשנות את הסיסמה שלך מאוחר יותר באמצעות לקוח Jabber."}. {"You have been banned from this room","נאסרת מן חדר זה"}. {"You must fill in field \"Nickname\" in the form","עליך למלא את השדה \"שם כינוי\" בתוך התבנית"}. {"You need a client that supports x:data and CAPTCHA to register","עליך להשתמש בלקוח אשר תומך x:data וגם CAPTCHA כדי להירשם"}. {"You need a client that supports x:data to register the nickname","עליך להשתמש בלקוח אשר תומך x:data כדי לרשום את השם כינוי"}. {"You need an x:data capable client to configure mod_irc settings","עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר הגדרות mod_irc"}. {"You need an x:data capable client to search","עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי לחפש"}. {"Your active privacy list has denied the routing of this stanza.","רשימת הפרטיות הפעילה שלך אסרה את הניתוב של סטנזה זו."}. {"Your contact offline message queue is full. The message has been discarded.","תור הודעות קשר לא מקוונות הינו מלא. ההודעה סולקה."}. {"Your Jabber account was successfully created.","חשבון Jabber נוצר בהצלחה."}. {"Your Jabber account was successfully deleted.","חשבון Jabber נמחק בהצלחה."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s"}. ejabberd-18.01/priv/msgs/gl.po0000644000232200023220000020247013225664356016543 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 16.02\n" "Last-Translator: Carlos E. Lopez \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Galician (galego)\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Language-Team: \n" "Language: gl\n" "X-Generator: Poedit 2.0.4\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " puxo o asunto: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Necesítase contrasinal para entrar nesta sala" #: ejabberd_oauth:448 msgid "Accept" msgstr "Aceptar" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configuración de accesos" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configuración da Lista de Control de Acceso" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Listas de Control de Acceso" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Regras de Acceso" #: mod_configure:1095 msgid "Access control lists" msgstr "Listas de Control de Acceso" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Acceso denegado pola política do servizo" #: mod_configure:1113 msgid "Access rules" msgstr "Regras de acceso" #: mod_configure:1772 msgid "Action on user" msgstr "Acción no usuario" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Engadir ID Jabber" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Engadir novo" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Engadir usuario" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administración" #: mod_configure:1767 msgid "Administration of " msgstr "Administración de " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Necesítase privilexios de administrador" #: mod_configure:507 msgid "All Users" msgstr "Todos os usuarios" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Toda a actividade" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Permitir aos usuarios cambiar o asunto" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Permitir aos usuarios consultar a outros usuarios" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Permitir aos usuarios enviar invitacións" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Permitir aos usuarios enviar mensaxes privadas" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Permitir aos visitantes cambiarse o alcume" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Permitir aos visitantes enviar mensaxes privadas a" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "" "Permitir aos visitantes enviar texto de estado nas actualizacións depresenza" #: mod_announce:611 msgid "Announcements" msgstr "Anuncios" #: mod_muc_log:476 msgid "April" msgstr "Abril" #: mod_muc_log:480 msgid "August" msgstr "Agosto" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "A creación automática de nodos non está habilitada" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Copia de seguridade" #: mod_configure:584 msgid "Backup Management" msgstr "Xestión de copia de seguridade" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Copia de seguridade de ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Copia de seguridade de arquivos en " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Mal formato" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Aniversario" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "Tanto o nome de usuario como o recurso son necesarios" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "Bytestream xa está activado" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA páxina Web" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Tempo da CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "Non se pode eliminar a lista activa" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "Non se pode eliminar a lista predeterminada" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Cambiar contrasinal" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Cambiar contrasinal de usuario" #: mod_register:295 msgid "Changing password is not allowed" msgstr "Non se permite cambiar o contrasinal" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "O cambio de rol/afiliación non está permitido" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Caracteres non permitidos:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Configuración da sala modificada" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Creouse a sala" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Destruíuse a sala" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Iniciouse a sala" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Detívose a sala" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Salas de charla" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "" "Escolle un nome de usuario e contrasinal para rexistrarche neste servidor" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Selecciona módulos a deter" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Selecciona tipo de almacenamento das táboas" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Decidir se aprobar a subscripción desta entidade." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Cidade" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Comandos" #: mod_muc:461 msgid "Conference room does not exist" msgstr "A sala de conferencias non existe" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configuración" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Configuración para a sala ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Recursos conectados:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parámetros de conexiones" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "País" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Base de datos" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Configuración de táboas da base de datos en " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Táboas da base de datos en ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 msgid "Database failure" msgstr "Erro na base de datos" #: mod_muc_log:484 msgid "December" msgstr "Decembro" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Os usuarios son participantes por defecto" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Eliminar os seleccionados" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Borrar usuario" #: mod_announce:629 msgid "Delete message of the day" msgstr "Borrar mensaxe do dia" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Borrar a mensaxe do día en todos os dominios" #: mod_shared_roster:900 msgid "Description:" msgstr "Descrición:" #: mod_configure:889 msgid "Disc only copy" msgstr "Copia en disco soamente" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Mostrar grupos:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Non digas o teu contrasinal a ninguén, nin sequera os administradores do " "servidor Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Exporta copia de seguridade a ficheiro de texto en " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Exportar a ficheiro de texto" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "Os grupos duplicados non están permitidos por RFC6121" #: mod_configure:1776 msgid "Edit Properties" msgstr "Editar Propiedades" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Aproba ou rexeita a petición de voz." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementos" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 msgid "Empty password" msgstr "Contrasinal baleiro" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Gardar históricos" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "Non se admite a activación do empuxe sen o atributo 'nodo'" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Codificación de servidor ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Pechar sesión de usuario" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Introduce lista de {Módulo, [Opcións]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Introduce o alcume que queiras rexistrar" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Introduce ruta ao ficheiro de copia de seguridade" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Introduce a ruta ao directorio de jabberd14 spools" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Introduce ruta ao ficheiro jabberd14 spool" #: mod_configure:974 msgid "Enter path to text file" msgstr "Introduce ruta ao ficheiro de texto" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Introduza o texto que ves" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Introduce o nome de usuario e codificaciones de carácteres que queiras usar " "ao conectar nos servidores de IRC. Presione 'Siguiente' para obtener más " "campos para rellenar Presione 'completo' para guardar axustes." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Introduce o nome de usuario, codificaciones de carácteres, portos e " "contrasinai que queiras usar ao conectar nos servidores de IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Servidor Jabber en Erlang" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Erro" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Exemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Exportar todas as táboas a un ficheiro SQL:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS " "(XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exportar datos dos usuarios dun dominio a ficheiros PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "Fallo de compoñente externo" #: mod_delegation:283 msgid "External component timeout" msgstr "Paso o tempo de espera do compoñente externo" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "Fallo ao activar bytestream" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Fallo ao extraer o Jabber ID da túa aprobación de petición de voz" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "O mapeo de espazo de nomes delegado fallou ao compoñente externo" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "Non se puido analizar a resposta HTTP" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "Non se puido analizar o chanserv" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "Fallo ao procesar a opción '~s'" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Apelido" #: mod_muc_log:474 msgid "February" msgstr "Febreiro" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "O ficheiro é maior que ~w bytes" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Enche o formulario para buscar usuarios Jabber (Engade * ao final dun campo " "para buscar subcadenas)" #: mod_muc_log:467 msgid "Friday" msgstr "Venres" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nome completo" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Ver número de usuarios conectados" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Ver número de usuarios rexistrados" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Ver data da última conexión de usuario" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Ver contrasinal de usuario" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Ver estatísticas de usuario" #: mod_vcard_ldap:330 mod_vcard_ldap:343 msgid "Given Name" msgstr "Nome" #: mod_shared_roster:923 msgid "Group " msgstr "Grupo " #: mod_roster:916 msgid "Groups" msgstr "Grupos" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Host" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "Dominio descoñecido" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Direccións IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transporte IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Nome de usuario en IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canle de IRC (non poñer o primeiro #)" #: mod_irc:417 msgid "IRC connection not found" msgstr "Conexión IRC non atopada" #: mod_irc:673 msgid "IRC server" msgstr "Servidor IRC" #: mod_irc:756 msgid "IRC settings" msgstr "IRC axustes" #: mod_irc:766 msgid "IRC username" msgstr "Nome de usuario en IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Si non ves a imaxe CAPTCHA aquí, visita a páxina web." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Se quere especificar codificaciones de caracteres diferentes, contrasinal ou " "servidor IRC rechea esta lista con valores no formato '{\"servidor irc\", " "\"codificación\", \"porto\", \"contrasinal\"}'. Este servizo utiliza por " "defecto a codificación \"~s\", porto ~p, sen contrasinal." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importar directorio" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importar ficheiro" #: mod_configure:982 msgid "Import User from File at " msgstr "Importa usuario desde ficheiro en " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importar usuarios de ficheiros spool de jabberd-1.4" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importar usuarios desde o directorio en " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importar usuario de ficheiro spool de jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importar usuarios en un fichero PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importar usuarios do directorio spool de jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "Atributo 'from' impropio" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "Atributo 'to' impropio" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "Parte de dominio impropio no atributo 'from'" #: mod_muc_room:260 msgid "Improper message type" msgstr "Tipo de mensaxe incorrecta" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Conexións S2S saíntes:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "O CAPTCHA proporcionado é incorrecto" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 msgid "Incorrect data form" msgstr "Formulario de datos incorrecto" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Contrasinal incorrecta" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "Valor incorrecto no formulario de datos" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "Valor incorrecto do atributo 'action'" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "Valor incorrecto de 'action' no formulario de datos" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "Valor incorrecto de 'path' no formulario de datos" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "Valor incorrecto do atributo 'type'" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "Privilexio insuficiente" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "Atributo 'from'' non é válido na mensaxe reenviada" #: mod_privilege:300 msgid "Invalid element" msgstr "Elemento non válido" #: mod_muc_room:3926 msgid "Invitations are not allowed in this conference" msgstr "As invitacións non están permitidas nesta sala" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Non está permitido enviar mensaxes de erro á sala. Este participante (~s) " "enviou unha mensaxe de erro (~s) e foi expulsado da sala" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Non está permitido enviar mensaxes privadas" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Non está permitido enviar mensaxes privadas do tipo \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Impedir o envio de mensaxes privadas á sala" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Rexistro de conta Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Xaneiro" #: mod_irc:665 msgid "Join IRC channel" msgstr "Entrar en canle IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Únete á canle de IRC aquí." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Únete á canle de IRC con este IDE de Jabber: ~s" #: mod_muc_log:479 msgid "July" msgstr "Xullo" #: mod_muc_log:478 msgid "June" msgstr "Xuño" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Última actividade" #: mod_configure:1646 msgid "Last login" msgstr "Última conexión" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Último mes" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Último ano" #: mod_configure:941 msgid "List of modules to start" msgstr "Lista de módulos a iniciar" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Lista de salas" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Portos de escoita" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Portos de escoita en " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Script de actualización a baixo nivel" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "A lista de participantes é pública" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Protexer a sala con CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Sala só para membros" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Facer sala moderada" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Protexer a sala con contrasinal" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Sala permanente" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Sala publicamente visible" #: mod_register:317 msgid "Malformed username" msgstr "Nome de usuario mal formado" #: mod_muc_log:475 msgid "March" msgstr "Marzo" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Número máximo de ocupantes" #: mod_muc_log:477 msgid "May" msgstr "Maio" #: mod_shared_roster:907 msgid "Members:" msgstr "Membros:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Necesitas ser membro desta sala para poder entrar" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Memorice o seu contrasinal ou escribilo nun papel colocado nun lugar seguro. " "En Jabber non hai unha forma automatizada para recuperar o seu contrasinal " "si a esquece." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memoria" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Corpo da mensaxe" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "Mensaxe non atopada no contido reenviado" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Segundo nome" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "Non se atopa 'channel' ou 'server' no formulario de datos" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "Non se atopa o atributo 'from'" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "Non se atopa o atributo 'to'" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Necesítase privilexios de moderador" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Módulos Modificados" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Módulo" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "O módulo non puido xestionar a consulta" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Módulos" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Módulos en ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Luns" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Salas de Charla" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "Múltiples elementos non están permitidos por RFC6121" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nome" #: mod_shared_roster:896 msgid "Name:" msgstr "Nome:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "Non se atopou o atributo 'jid' nin 'nick'" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "Non se atopou o atributo 'role' nin 'affiliation'" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nunca" #: mod_register_web:377 msgid "New Password:" msgstr "Novo contrasinal:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Alcume" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Rexistro do alcume en " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "O alcume ~s non existe na sala" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "Non se atopou 'access' no formulario de datos" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "Non se atopou 'acls' no formulario de datos" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "Non se atopou o atributo de 'affiliation'" #: mod_muc_room:2505 msgid "No 'item' element found" msgstr "Non se atopou o elemento 'item'" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "Non se atopan 'modules' no formulario de datos" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "Non se atopou 'password' no formulario de datos" #: mod_register:148 msgid "No 'password' found in this query" msgstr "Non se atopou 'password' nesta solicitude" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "Non se atopou 'path' neste formulario de datos" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "O atributo 'to' non se atopou na invitación" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Sen datos" #: ejabberd_local:181 msgid "No available resource found" msgstr "Non se atopou ningún recurso" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Non se proporcionou corpo de mensaxe para o anuncio" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 msgid "No data form found" msgstr "Non se atopou formulario de datos" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "Non hai características dispoñibles" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "Ningún evento procesou este comando" #: mod_last:218 msgid "No info about last activity found" msgstr "Non se atopou información sobre a última actividade" #: mod_blocking:99 msgid "No items found in this query" msgstr "Non se atoparon elementos nesta consulta" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "Ningún módulo manexa esta consulta" #: mod_pubsub:1541 msgid "No node specified" msgstr "Non se especificou nodo" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "Non se atoparon subscricións pendentes" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "Non se atopou ningunha lista de privacidade con este nome" #: mod_private:96 msgid "No private data found in this query" msgstr "Non se atopou ningún elemento de datos privado nesta solicitude" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 msgid "No running node found" msgstr "Non se atoparon nodos activos" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "Non hai servizos dispoñibles" #: mod_stats:101 msgid "No statistics found for this item" msgstr "Non se atopou ningunha estatística para este elemento" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "O nodo xa existe" #: nodetree_tree_sql:99 msgid "Node index not found" msgstr "Non se atopou índice de nodo" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nodo non atopado" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Nodo ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "Nodeprep fallou" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nodos" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Ningún" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Non atopado" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "Non subscrito" #: mod_muc_log:483 msgid "November" msgstr "Novembro" #: mod_configure:1212 msgid "Number of online users" msgstr "Número de usuarios conectados" #: mod_configure:1202 msgid "Number of registered users" msgstr "Número de usuarios rexistrados" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "Aceptar" #: mod_muc_log:482 msgid "October" msgstr "Outubro" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Mensaxes diferidas" #: mod_offline:761 msgid "Offline Messages:" msgstr "Mensaxes sen conexión:" #: mod_register_web:373 msgid "Old Password:" msgstr "Contrasinal anterior:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Conectado" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Usuarios conectados" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Usuarios conectados:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "Só se permiten etiquetas ou " #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "Só se admite o elemento nesta consulta" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Só membros poden consultar o arquivo de mensaxes da sala" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Só os moderadores e os participantes se lles permite cambiar o tema nesta " "sala" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Só os moderadores están autorizados a cambiar o tema nesta sala" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Só os moderadores poden aprobar peticións de voz" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Só os ocupantes poden enviar mensaxes á sala" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Só os ocupantes poden enviar solicitudes á sala" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Só os administradores do servizo teñen permiso para enviar mensaxes de " "servizo" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opcións" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nome da organización" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Unidade da organización" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Conexións S2S saíntes" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Conexións S2S saíntes:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Requírense privilexios de propietario da sala" #: mod_offline:693 msgid "Packet" msgstr "Paquete" #: mod_irc:578 msgid "Parse error" msgstr "Erro no procesado" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "Fallou o procesamento" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Contrasinal" #: mod_configure:1130 msgid "Password Verification" msgstr "Verificación da contrasinal" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Verificación da Contrasinal:" #: mod_irc:822 msgid "Password ~b" msgstr "Contrasinal ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Contrasinal:" #: mod_configure:998 msgid "Path to Dir" msgstr "Ruta ao directorio" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Ruta ao ficheiro" #: mod_roster:915 msgid "Pending" msgstr "Pendente" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periodo: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Salas permanentes" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "A solicitude de Ping é incorrecta" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Ten en conta que estas opcións só farán copia de seguridade da base de datos " "Mnesia. Se está a utilizar o módulo de ODBC, tamén necesita unha copia de " "seguridade da súa base de datos SQL por separado." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Por favor, espera un pouco antes de enviar outra petición de voz" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Porto" #: mod_irc:828 msgid "Port ~b" msgstr "Porto ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "Posuír o atributo 'ask' non está permitido por RFC6121" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protocolo" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Petición de subscriptor de PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publicar-Subscribir" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "Non se permite a publicación de elementos no nodo de colección" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Nesta sala non se permiten solicitudes aos membros da sala" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "É prohibido enviar solicitudes a outros usuarios" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Copia en RAM e disco" #: mod_configure:889 msgid "RAM copy" msgstr "Copia en RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Erro na chamada RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Cru" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "¿Está seguro que quere borrar a mensaxe do dia?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "O receptor non está na sala de conferencia" #: mod_register_web:275 msgid "Register" msgstr "Rexistrar" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Rexistrar unha conta Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Usuarios rexistrados" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Usuarios rexistrados:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Alcumes rexistrados" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Rexistro en mod_irc para " #: mod_configure:889 msgid "Remote copy" msgstr "Copia remota" #: mod_roster:963 msgid "Remove" msgstr "Borrar" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Borrar Todas as Mensaxes Sen conexión" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Eliminar usuario" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Substituído por unha nova conexión" #: mod_configure:1675 msgid "Resources" msgstr "Recursos" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Reiniciar" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Reiniciar o servizo" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restaurar" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaura copia de seguridade desde o ficheiro en " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd " "(require menos memoria):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Restaurar inmediatamente copia de seguridade binaria:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Restaurar copias de seguridade de texto plano inmediatamente:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Configuración da Sala" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Ocupantes da sala" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Denegar crear a sala por política do servizo" #: mod_muc_log:1064 msgid "Room description" msgstr "Descrición da sala" #: mod_muc_log:1027 msgid "Room title" msgstr "Título da sala" #: mod_roster:1082 msgid "Roster" msgstr "Lista de contactos" #: mod_roster:334 msgid "Roster module has failed" msgstr "O módulo de Roster fallou" #: mod_roster:968 msgid "Roster of " msgstr "Lista de contactos de " #: mod_configure:1671 msgid "Roster size" msgstr "Tamaño da lista de contactos" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nodos funcionando" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "A negociación SASL non se permite neste estado" #: mod_muc_log:468 msgid "Saturday" msgstr "Sábado" #: mod_irc:582 msgid "Scan error" msgstr "Erro de escaneo" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "O escaneo Fallou" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Comprobación de script" #: mod_vcard:453 msgid "Search Results for " msgstr "Buscar resultados por " #: mod_vcard:427 msgid "Search users in " msgstr "Buscar usuarios en " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Enviar anuncio a todos os usuarios conectados" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Enviar anuncio a todos os usuarios conectados en todos os dominios" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Enviar anuncio a todos os usuarios" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Enviar anuncio a todos os usuarios en todos os dominios" #: mod_muc_log:481 msgid "September" msgstr "Setembro" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "Conexión ao Servidor Fallou" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "Non se permiten conexións de servidor a subdominios locais" #: mod_irc:842 msgid "Server ~b" msgstr "Servidor ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Servidor:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Pór mensaxe do dia e enviar a todos os usuarios conectados" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Pór mensaxe do día en todos os dominios e enviar aos usuarios conectados" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Grupos Compartidos" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Mostrar Táboa Integral" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Mostrar Táboa Ordinaria" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Deter o servizo" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Algúns clientes Jabber pode almacenar o contrasinal no computador, pero debe " "facer isto só no seu computador persoal por razóns de seguridade." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Iniciar" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Iniciar módulos" #: mod_configure:936 msgid "Start Modules at " msgstr "Iniciar módulos en " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Estatísticas" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Estatísticas de ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Deter" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Deter módulos" #: mod_configure:918 msgid "Stop Modules at " msgstr "Deter módulos en " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nodos detidos" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Tipo de almacenamento" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Gardar copia de seguridade binaria:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Gardar copia de seguridade en texto plano:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Asunto" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Enviar" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Enviado" #: mod_roster:914 msgid "Subscription" msgstr "Subscripción" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "Non se permiten subscricións" #: mod_muc_log:469 msgid "Sunday" msgstr "Domingo" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Ese alcume xa está a ser usado por outro ocupante" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "O alcume xa está rexistrado por outra persoa" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "O CAPTCHA é válido." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "A verificación de CAPTCHA fallou" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "A sala de conferencias non admite a función solicitada" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "O contrasinal contén caracteres inaceptables" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "O contrasinal é demasiado débil" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "O contrasinal da súa conta Jabber cambiouse correctamente." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "A solicitude só se permite para usuarios locais" #: mod_roster:203 msgid "The query must not contain elements" msgstr "A solicitude non debe conter elementos " #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" "A estroa DEBEN conter un elemento , un elemento ou un " "elemento " #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Produciuse un erro ao cambiar o contrasinal: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Produciuse un erro ao crear a conta: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Produciuse un erro ao eliminar a conta: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "Esta é insensible: Macbeth é o mesmo que MacBeth e Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Esta páxina permite crear unha conta Jabber neste servidor Jabber. o seu JID " "(Jabber IDentificador) será da forma: nomeusuario@servidor. Por favor le " "coidadosamente as instrucións para encher correctamente os campos." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Esta páxina permite anular o rexistro dunha conta Jabber neste servidor " "Jabber." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Sala non anónima" #: mod_muc_log:466 msgid "Thursday" msgstr "Xoves" #: mod_offline:690 msgid "Time" msgstr "Data" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Atraso temporal" #: mod_offline:692 msgid "To" msgstr "Para" #: mod_register:215 msgid "To register, visit ~s" msgstr "Para rexistrarse, visita ~s" #: mod_configure:709 msgid "To ~s" msgstr "A ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "Token TTL" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "Valor demasiado longo do atributo 'xml:lang'" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "Demasiados elementos " #: mod_privacy:164 msgid "Too many elements" msgstr "Demasiados elementos " #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Demasiadas solicitudes CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "Demasiados bytestreams activos" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Demasiadas mensaxes sen recoñecer recibilos" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "Demasiados usuarios nesta sala" #: mod_register:355 msgid "Too many users registered" msgstr "Demasiados usuarios rexistrados" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Salas totais" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Hase exedido o límite de tráfico" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transaccións abortadas:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transaccións finalizadas:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transaccións rexistradas:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transaccións reiniciadas:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Martes" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "No se pudo generar un CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "Non se pode rexistrar a ruta no dominio local existente" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Non autorizado" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "Acción inesperada" #: mod_register_web:488 msgid "Unregister" msgstr "Eliminar rexistro" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Eliminar o rexistro dunha conta Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "Elemento non soportado" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "Petición MIX non soportada" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Actualizar" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Actualizar mensaxe do dia, pero non envialo" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Actualizar a mensaxe do día en todos os dominos (pero non envialo)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plan de actualización" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Script de actualización" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Actualizar ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Tempo desde o inicio:" #: xmpp_stream_out:533 msgid "Use of STARTTLS forbidden" msgstr "Prohibido o uso de STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Requírese o uso de STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Usuario" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "Usuario (jid)" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Administración de usuarios" #: mod_register:345 msgid "User already exists" msgstr "O usuario xa existe" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "A parte do usuario do JID en 'from' está baleira" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 msgid "User session not found" msgstr "Sesión de usuario non atopada" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "Sesión de usuario completada" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Usuario ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Nome de usuario:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Usuarios" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Última actividade dos usuarios" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Os usuarios non están autorizados a rexistrar contas con tanta rapidez" #: mod_roster:954 msgid "Validate" msgstr "Validar" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "O valor \"get\" do atributo 'type' non está permitido" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "O valor \"set\" do atributo 'type' non está permitido" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "O valor de '~s' debería ser booleano" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "O valor de '~s' debería ser unha data" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "O valor de '~s' debería ser un enteiro" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Hosts Virtuais" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Os visitantes non teñen permitido cambiar os seus alcumes nesta sala" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Os visitantes non poden enviar mensaxes a todos os ocupantes" #: mod_muc_room:3879 msgid "Voice request" msgstr "Petición de voz" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "As peticións de voz están desactivadas nesta sala" #: mod_muc_log:465 msgid "Wednesday" msgstr "Mércores" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" "Máis tarde, pode cambiar o seu contrasinal utilizando un cliente Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Fuches bloqueado nesta sala" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "Entrou en demasiadas salas de conferencia" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Debes encher o campo \"Alcumo\" no formulario" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Necesitas un cliente con soporte de x:data e CAPTCHA para rexistrarche" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Necesitas un cliente con soporte de x:data para poder rexistrar o alcume" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Necesitas un cliente con soporte de x:data para configurar as opcións de " "mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Necesitas un cliente con soporte de x:data para poder buscar" #: mod_pubsub:1504 msgid "You're not allowed to create nodes" msgstr "Non tes permiso para crear nodos" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "A súa conta Jabber creouse correctamente." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "A súa conta Jabber eliminouse correctamente." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" "A súa lista de privacidade activa negou o encaminamiento desta estrofa." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "A túa cola de mensaxes diferidas de contactos está chea. A mensaxe " "descartouse." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "As súas mensaxes a ~s encóntranse bloqueadas. Para desbloquear, visite ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Módulo de IRC para ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Módulo de MUC para ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "Servizo Multicast de ejabberd" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Módulo de Publicar-Subscribir de ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Módulo SOCKS5 Bytestreams para ejabberd" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Administrador Web" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Módulo vCard para ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "foi bloqueado" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "foi expulsado" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "foi expulsado porque o sistema vaise a deter" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "foi expulsado debido a un cambio de afiliación" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "foi expulsado, porque a sala cambiouse a só-membros" #: mod_muc_log:410 msgid "is now known as" msgstr "agora coñécese como" #: mod_muc_log:370 msgid "joins the room" msgstr "entra na sala" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "sae da sala" #: mod_muc_room:3856 msgid "private, " msgstr "privado, " #: mod_muc_room:3955 msgid "the password is" msgstr "a contrasinal é" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard busqueda de usuario" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Configuración das regra de acceso ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s invítache á sala ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Cola de mensaxes diferidas de ~s" #~ msgid "No resource provided" #~ msgstr "Non se proporcionou recurso" #~ msgid "Server" #~ msgstr "Servidor" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Demasiados (~p) fallou autenticaciones desde esta dirección IP (~s). A " #~ "dirección será desbloqueada as ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Por favor, especifica o tamaño do arquivo" #~ msgid "Please specify file name." #~ msgstr "Por favor, indique o nome do arquivo." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Esta dirección IP está na lista negra en ~s" #~ msgid "Empty Rooms" #~ msgstr "Salas baleiras" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "O Jabber ID ~s non é válido" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiliación non válida: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Rol non válido: ~s" #~ msgid "No limit" #~ msgstr "Sen límite" #~ msgid "Present real Jabber IDs to" #~ msgstr "Os Jabber ID reais poden velos" #~ msgid "moderators only" #~ msgstr "só moderadores" #~ msgid "anyone" #~ msgstr "calquera" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Roles para os que si se difunde a súa Presenza" #~ msgid "Moderator" #~ msgstr "Moderator" #~ msgid "Participant" #~ msgstr "Participante" #~ msgid "Visitor" #~ msgstr "Visitante" #~ msgid "nobody" #~ msgstr "ninguén" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permitir aos visitantes enviar peticións de voz" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Intervalo mínimo entre peticións de voz (en segundos)" #~ msgid "Enable message archiving" #~ msgstr "Activar o almacenamento de mensaxes" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Excluír Jabber IDs das probas de CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Necesitas un cliente con soporte de x:data para configurar a sala" #~ msgid "Number of occupants" #~ msgstr "Número de ocupantes" #~ msgid "User JID" #~ msgstr "Jabber ID do usuario" #~ msgid "Grant voice to this person?" #~ msgstr "¿Conceder voz a esta persoa?" #~ msgid "Node ID" #~ msgstr "Nodo ID" #~ msgid "Subscriber Address" #~ msgstr "Dirección do subscriptor" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "" #~ "Desexas permitir a este JabberID que se subscriba a este nodo PubSub?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Enviar payloads xunto coas notificacións de eventos" #~ msgid "Deliver event notifications" #~ msgstr "Entregar notificacións de eventos" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Notificar subscriptores cando cambia a configuración do nodo" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Notificar subscriptores cando o nodo bórrase" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Notificar subscriptores cando os elementos bórranse do nodo" #~ msgid "Persist items to storage" #~ msgstr "Persistir elementos ao almacenar" #~ msgid "A friendly name for the node" #~ msgstr "Un nome sinxelo para o nodo" #~ msgid "Max # of items to persist" #~ msgstr "Máximo # de elementos que persisten" #~ msgid "Whether to allow subscriptions" #~ msgstr "Permitir subscripciones" #~ msgid "Specify the access model" #~ msgstr "Especifica o modelo de acceso" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Lista de grupos autorizados a subscribir" #~ msgid "Specify the publisher model" #~ msgstr "Especificar o modelo do publicante" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Purgar todos os elementos cando o editor correspondente desconéctase" #~ msgid "Specify the event message type" #~ msgstr "Especifica o tipo da mensaxe de evento" #~ msgid "Max payload size in bytes" #~ msgstr "Máximo tamaño do payload en bytes" #~ msgid "When to send the last published item" #~ msgstr "Cando enviar o último elemento publicado" #~ msgid "Only deliver notifications to available users" #~ msgstr "Só enviar notificacións aos usuarios dispoñibles" #~ msgid "The collections with which a node is affiliated" #~ msgstr "As coleccións coas que un nodo está afiliado" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Rechea campos para buscar usuarios Jabber que concuerden" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Servidores S2S saíntes:" #~ msgid "Delete" #~ msgstr "Eliminar" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Este participante é expulsado da sala, xa que enviou unha mensaxe de erro" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Este participante é expulsado da sala, porque el enviou unha mensaxe de " #~ "erro a outro participante" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Este participante é expulsado da sala, porque el enviou un erro de " #~ "presenza" #~ msgid "CAPTCHA test failed" #~ msgstr "Fallou a proba de CAPTCHA" #~ msgid "Encodings" #~ msgstr "Codificaciones" #~ msgid "(Raw)" #~ msgstr "(Cru)" #~ msgid "Specified nickname is already registered" #~ msgstr "O alcume especificado xa está rexistrado" #~ msgid "Size" #~ msgstr "Tamaño" ejabberd-18.01/priv/msgs/wa.msg0000644000232200023220000006405413225664356016724 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Accept","Accepter"}. {"Access Configuration","Apontiaedje des accès"}. {"Access Control List Configuration","Apontiaedje des droets (ACL)"}. {"Access control lists","Droets (ACL)"}. {"Access Control Lists","Droets (ACL)"}. {"Access denied by service policy","L' accès a stî rfuzé pal politike do siervice"}. {"Access rules","Rîles d' accès"}. {"Access Rules","Rîles d' accès"}. {"Action on user","Accion so l' uzeu"}. {"Add Jabber ID","Radjouter èn ID Jabber"}. {"Add New","Radjouter"}. {"Add User","Radjouter èn uzeu"}. {"Administration","Manaedjaedje"}. {"Administration of ","Manaedjaedje di "}. {"Administrator privileges required","I fåt des priviledjes di manaedjeu"}. {"All activity","Dispoy todi"}. {"Allow users to change the subject","Les uzeus polèt candjî l' tite"}. {"Allow users to query other users","Les uzeus polèt cweri ls ôtes uzeus"}. {"Allow users to send invites","Les uzeus polèt evoyî priyaedjes"}. {"Allow users to send private messages","Les uzeus polèt evoyî des messaedjes privés"}. {"Allow visitors to change nickname","Permete ki les viziteus candjexhe leus metous nos"}. {"Allow visitors to send private messages to","Les uzeus polèt evoyî des messaedjes privés"}. {"Allow visitors to send status text in presence updates","Permete ki les viziteus evoyexhe des tecse d' estat dins leus messaedjes di prezince"}. {"All Users","Tos les uzeus"}. {"Announcements","Anonces"}. {"A password is required to enter this room","I fåt dner on scret po poleur intrer dins cisse såle ci"}. {"April","avri"}. {"August","awousse"}. {"Backup","Copeye di såvrité"}. {"Backup Management","Manaedjaedje des copeyes di såvrité"}. {"Backup of ~p","Copeye di såvrité po ~p"}. {"Backup to File at ","Fé ene copeye di såvrité dins on fitchî so "}. {"Bad format","Mwais fôrmat"}. {"Birthday","Date d' askepiaedje"}. {"CAPTCHA web page","Pådje web CAPTCHA"}. {"Change Password","Candjî l' sicret"}. {"Change User Password","Candjî l' sicret d' l' uzeu"}. {"Characters not allowed:","Caracteres nén permetous:"}. {"Chatroom configuration modified","L' apontiaedje del såle di berdelaedje a candjî"}. {"Chatroom is created","Li såle di berdelaedje est ahivêye"}. {"Chatroom is destroyed","Li såle di berdelaedje est distrûte"}. {"Chatroom is started","Li såle di berdelaedje est enondêye"}. {"Chatroom is stopped","Li såle di berdelaedje est ahotêye"}. {"Chatrooms","Såles di berdelaedje"}. {"Choose a username and password to register with this server","Tchoezixhoz on no d' uzeu eyet on scret po vs edjîstrer so ç' sierveu ci"}. {"Choose modules to stop","Tchoezixhoz les modules a-z arester"}. {"Choose storage type of tables","Tchoezi l' sôre di wårdaedje po les tåves"}. {"Choose whether to approve this entity's subscription.","Tchoezi s' i fåt aprover ou nén l' abounmint di ciste intité."}. {"City","Veye"}. {"Commands","Comandes"}. {"Conference room does not exist","Li såle di conferince n' egzistêye nén"}. {"Configuration","Apontiaedjes"}. {"Configuration of room ~s","Apontiaedje del såle ~s"}. {"Connected Resources:","Raloyî avou les rsoûces:"}. {"Connections parameters","Parametes des raloyaedjes"}. {"Country","Payis"}. {"CPU Time:","Tins CPU:"}. {"Database","Båze di dnêyes"}. {"Database Tables at ~p","Tåves del båze di dnêyes so ~p"}. {"Database Tables Configuration at ","Apontiaedje des tåves del båze di dnêyes so "}. {"December","decimbe"}. {"Default users as participants","Les uzeus sont des pårticipants come prémetowe dujhance"}. {"Delete message of the day","Disfacer l' messaedje do djoû"}. {"Delete message of the day on all hosts","Disfacer l' messaedje do djoû so tos les lodjoes"}. {"Delete Selected","Disfacer les elemints tchoezis"}. {"Delete User","Disfacer èn uzeu"}. {"Description:","Discrijhaedje:"}. {"Disc only copy","Copeye seulmint sol deure plake"}. {"Displayed Groups:","Groupes håynés:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ni dnez vosse sicret a nolu, nén ddja ås manaedjeus do sierveu Jabber."}. {"Dump Backup to Text File at ","Copeye di såvritè viè on fitchî tecse so "}. {"Dump to Text File","Schaper en on fitchî tecse"}. {"Edit Properties","Candjî les prôpietés"}. {"Either approve or decline the voice request.","Aprover oudonbén rifuzer li dmande di vwès."}. {"ejabberd IRC module","Module IRC po ejabberd"}. {"ejabberd MUC module","Module MUC (såles di berdelaedje) po ejabberd"}. {"ejabberd Multicast service","siervice multicast d' ejabberd"}. {"ejabberd Publish-Subscribe module","Module d' eplaidaedje-abounmint po ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Module SOCKS5 Bytestreams po ejabberd"}. {"ejabberd vCard module","Module vCard ejabberd"}. {"ejabberd Web Admin","Manaedjeu waibe ejabberd"}. {"Elements","Elemints"}. {"Email","Emile"}. {"Enable logging","Mete en alaedje li djournå"}. {"Encoding for server ~b","Ecôdaedje pol sierveu ~b"}. {"End User Session","Fini l' session d' l' uzeu"}. {"Enter list of {Module, [Options]}","Dinez ene djivêye del cogne {Module, [Tchuzes]}"}. {"Enter nickname you want to register","Dinez l' metou no ki vos vloz edjîstrer"}. {"Enter path to backup file","Dinez l' tchimin viè l' fitchî copeye di såvrité"}. {"Enter path to jabberd14 spool dir","Dinez l' tchimin viè l' ridant di spool jabberd14"}. {"Enter path to jabberd14 spool file","Dinez l' tchimin viè l' fitchî di spool jabberd14"}. {"Enter path to text file","Dinez l' tchimin viè l' fitchî tecse"}. {"Enter the text you see","Tapez l' tecse ki vos voeyoz"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Dinez les nos d' uzeu et ls ecôdaedjes ki vos vloz eployî po vs raloyî åzès sierveus IRC Clitchîz so «Shuvant» po-z aveur di pus di tchamps a rimpli. Clitchîz so «Fini» po schaper les apontiaedjes."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Dinez l' no d' uzeu, les ecôdaedjes, les pôrts et les screts ki vos vloz eployî po vs raloyî åzès sierveus IRC"}. {"Erlang Jabber Server","Sierveu Jabber Erlang"}. {"Error","Aroke"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Egzimpe: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export all tables as SQL queries to a file:","Espoirter totes les tåves, come des cmandes SQL, viè on fitchî"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Nén moyén di rsaetchî on JID foû d' l' aprovaedje di vosse dimande di vwès"}. {"Family Name","No d' famile"}. {"February","fevrî"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Rimplixhoz les tchamps do formulaire po cweri èn uzeu Jabber (radjouter «*» al fén do tchamp po cweri tot l' minme kéne fén d' tchinne"}. {"Friday","vénrdi"}. {"From","Di"}. {"From ~s","Dispoy ~s"}. {"Full Name","No etir"}. {"Get Number of Online Users","Riçure li nombe d' uzeus raloyîs"}. {"Get Number of Registered Users","Riçure li nombe d' uzeus edjîstrés"}. {"Get User Last Login Time","Riçure li date/eure do dierin elodjaedje di l' uzeu"}. {"Get User Password","Riçure sicret d' l' uzeu"}. {"Get User Statistics","Riçure les statistikes di l' uzeu"}. {"Group ","Groupe "}. {"Groups","Groupes"}. {"has been banned","a stî bani"}. {"has been kicked","a stî pité evoye"}. {"has been kicked because of an affiliation change","a stî pité evoye cåze d' on candjmint d' afiyaedje"}. {"has been kicked because of a system shutdown","a stî pité evoye cåze d' èn arestaedje do sistinme"}. {"has been kicked because the room has been changed to members-only","a stî pité evoye cåze ki l' såle a stî ristrindowe åzès mimbes seulmint"}. {" has set the subject to: "," a candjî l' tite a: "}. {"Host","Sierveu"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si vos n' voeyoz nole imådje CAPTCHA chal, vizitez l' pådje waibe."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si vos vloz dner des pôrts, sicrets ou ecôdaedjes diferins po les sierveus IRC, rimplixhoz cisse djivêye ci avou des valixhances del cogne «{\"sierveu irc\", \"ecôdaedje\", pôrt, \"sicret\"}». Les prémetowès valixhances do siervice sont «~s» po l' ecôdaedje, «~p» pol pôrt, et on vude sicret."}. {"Import Directory","Sititchî d' on ridant"}. {"Import File","Sititchî d' on fitchî"}. {"Import user data from jabberd14 spool file:","Sititchî des dnêyes uzeus foû d' on fitchî spoûle jabberd14:"}. {"Import User from File at ","Sititchî uzeu d' on fitchî so "}. {"Import users data from a PIEFXIS file (XEP-0227):","Sititchî des dnêyes uzeus foû d' on fitchî PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Sititchî des dnêyes uzeus foû d' on ridant spoûle jabberd14:"}. {"Import Users from Dir at ","Sitichî des uzeus d' on ridant so "}. {"Import Users From jabberd14 Spool Files","Sititchî des uzeus Jabberd 1.4"}. {"Improper message type","Sôre di messaedje nén valide"}. {"Incoming s2s Connections:","Raloyaedjes s2s en intrêye:"}. {"Incorrect password","Sicret nén corek"}. {"IP addresses","Adresses IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","Canå IRC (èn nén mete li prumî #)"}. {"IRC server","Sierveu IRC"}. {"IRC settings","Apontiaedjes IRC"}. {"IRC Transport","Transpoirt IRC"}. {"IRC username","No d' uzeu IRC"}. {"IRC Username","No d' uzeu IRC"}. {"is now known as","est asteure kinoxhou come"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","On n' pout nén evoyî des messaedjes d' aroke sol såle. Li pårticipan (~s) a-st evoyî on messaedje d' aroke (~s) ey a stî tapé foû."}. {"It is not allowed to send private messages","Ci n' est nén permetou d' evoyî des messaedjes privés"}. {"It is not allowed to send private messages of type \"groupchat\"","C' est nén possibe d' evoyî des messaedjes privés del sôre «groupchat»"}. {"It is not allowed to send private messages to the conference","On n' pout nén evoyî des messaedjes privés dins cisse conferince ci"}. {"Jabber Account Registration","Edjîstraedje di conte Jabber"}. {"Jabber ID","ID Jabber"}. {"January","djanvî"}. {"Join IRC channel","Radjonde canå IRC"}. {"joins the room","arive sol såle"}. {"Join the IRC channel here.","Radjonde li canå IRC droci."}. {"Join the IRC channel in this Jabber ID: ~s","Radjonde li canå IRC e cist ID Jabber: ~s"}. {"July","djulete"}. {"June","djun"}. {"Last Activity","Dierinne activité"}. {"Last login","Dierin elodjaedje"}. {"Last month","Dierin moes"}. {"Last year","Dierinne anêye"}. {"leaves the room","cwite li såle"}. {"Listened Ports at ","Pôrts drovous so "}. {"Listened Ports","Pôrts drovous"}. {"List of modules to start","Djivêye di modules a-z enonder"}. {"List of rooms","Djivêye des såles"}. {"Low level update script","Sicripe di metaedje a djoû d' bas livea"}. {"Make participants list public","Rinde publike li djivêye des pårticipants"}. {"Make room CAPTCHA protected","Rinde li såle di berdelaedje protedjeye pa CAPTCHA"}. {"Make room members-only","Rinde li såle di berdelaedje ristrindowe ås mimbes seulmint"}. {"Make room moderated","Rinde li såle di berdelaedje moderêye"}. {"Make room password protected","Rinde li såle di berdelaedje protedjeye pa scret"}. {"Make room persistent","Rinde li såle permaninte"}. {"Make room public searchable","Rinde li såle di berdelaedje cweråve publicmint"}. {"March","måss"}. {"Maximum Number of Occupants","Nombe macsimom di prezints"}. {"May","may"}. {"Membership is required to enter this room","I fåt esse mimbe po poleur intrer dins cisse såle ci"}. {"Members:","Mimbes:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Rimimbrez vosse sicret, ou scrijhoz l' so on papî ki vos wådroz en ene place bén a hoûte, ca avou Jabber i n' a pont moyén di rapexhî vosse sicret si vos l' rovyîz."}. {"Memory","Memwere"}. {"Message body","Coir do messaedje"}. {"Middle Name","No do mitan"}. {"Moderator privileges required","I fåt des priviledjes di moderateu"}. {"Modified modules","Modules di candjîs"}. {"Module","Module"}. {"Modules at ~p","Modules so ~p"}. {"Modules","Modules"}. {"Monday","londi"}. {"Multicast","Multicast"}. {"Multi-User Chat","Berdelaedje a sacwants"}. {"Name","No"}. {"Name:","Pitit no:"}. {"Never","Måy"}. {"New Password:","Novea scret:"}. {"Nickname","Metou no"}. {"Nickname Registration at ","Edjîstraedje di metou no amon "}. {"Nickname ~s does not exist in the room","Li metou no ~s n' egzistêye nén dins l' såle"}. {"No body provided for announce message","I n' a nou coir do messaedje po ciste anonce la"}. {"No Data","Nole dinêye disponibe"}. {"Node not found","Nuk nén trové"}. {"Node ~p","Nuk ~p"}. {"Nodes","Nuks"}. {"None","Nole"}. {"Not Found","Nén trové"}. {"November","nôvimbe"}. {"Number of online users","Nombe d' uzeus raloyîs"}. {"Number of registered users","Nombe d' uzeus edjîstrés"}. {"October","octôbe"}. {"Offline Messages:","Messaedjes ki ratindèt:"}. {"Offline Messages","Messaedjes ki ratindèt"}. {"OK","'l est bon"}. {"Old Password:","Vî scret:"}. {"Online","Raloyî"}. {"Online Users:","Uzeus raloyîs:"}. {"Online Users","Uzeus raloyîs"}. {"Only members may query archives of this room","Seulmint les mimbes polèt cweri les årtchives dins cisse såle ci"}. {"Only moderators and participants are allowed to change the subject in this room","Seulmint les moderateus et les pårticipants polèt candjî l' sudjet dins cisse såle ci"}. {"Only moderators are allowed to change the subject in this room","Seulmint les moderateus polèt candjî l' sudjet dins cisse såle ci"}. {"Only moderators can approve voice requests","Seulmint les moderateus polèt aprover des dmandes di vwès"}. {"Only occupants are allowed to send messages to the conference","Seulmint les prezints polèt evoyî des messaedjes al conferince"}. {"Only occupants are allowed to send queries to the conference","Seulmint les prezints polèt evoyî des cweraedjes sol conferince"}. {"Only service administrators are allowed to send service messages","Seulmint les manaedjeus d' siervices polèt evoyî des messaedjes di siervice"}. {"Options","Tchuzes"}. {"Organization Name","No d' l' organizåcion"}. {"Organization Unit","Unité d' l' organizåcion"}. {"Outgoing s2s Connections:","Raloyaedjes s2s e rexhowe:"}. {"Outgoing s2s Connections","Raloyaedjes s2s e rexhowe"}. {"Owner privileges required","I fåt des priviledjes di prôpietaire"}. {"Packet","Paket"}. {"Password ~b","Sicret ~b"}. {"Password:","Sicret:"}. {"Password","Sicret"}. {"Password Verification:","Acertinaedje do scret:"}. {"Password Verification","Acertinaedje do scret"}. {"Path to Dir","Tchimin viè l' ridant"}. {"Path to File","Tchimin viè l' fitchî"}. {"Pending","Ratindant"}. {"Period: ","Termene:"}. {"Permanent rooms","Såles tofer la"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Notez ki ces tchuzes la vont seulmint fé ene copeye di såvrité del båze di dnêyes Mnesia costrûte å dvins do programe. Si vos eployîz ene difoûtrinne båze di dnêyes avou l' module ODBC, vos dvoz fé ene copeye di såvrité del båze SQL da vosse sepårumint."}. {"Please, wait for a while before sending new voice request","Ratindez ene miete s' i vs plait divant d' rivoyî ene nouve dimande di vwès"}. {"Pong","Pong"}. {"Port ~b","Pôrt ~b"}. {"Port","Pôrt"}. {"private, ","privé, "}. {"Protocol","Protocole"}. {"Publish-Subscribe","Eplaidaedje-abounmint"}. {"PubSub subscriber request","Dimande d' eplaidaedje-abounmint d' èn abouné"}. {"Queries to the conference members are not allowed in this room","Les cweraedjes des mimbes del conferince ni sont nén permetous dins cisse såle ci"}. {"RAM and disc copy","Copeye e memwere (RAM) et sol deure plake"}. {"RAM copy","Copeye e memwere (RAM)"}. {"Raw","Dinêyes brutes"}. {"Really delete message of the day?","Voloz vs vormint disfacer l' messaedje do djoû?"}. {"Recipient is not in the conference room","Li riçuveu n' est nén dins l' såle di conferince"}. {"Register a Jabber account","Edjîstrer on conte Jabber"}. {"Register","Edjîstrer"}. {"Registered nicknames","Metous nos edjistrés"}. {"Registered Users:","Uzeus edjistrés:"}. {"Registered Users","Uzeus edjistrés"}. {"Registration in mod_irc for ","Edjîstraedje dins mod_irc po "}. {"Remote copy","Copeye å lon"}. {"Remove All Offline Messages","Oister tos les messaedjes ki ratindèt"}. {"Remove","Oister"}. {"Remove User","Disfacer l' uzeu"}. {"Replaced by new connection","Replaecî pa on novea raloyaedje"}. {"Resources","Rissoûces"}. {"Restart","Renonder"}. {"Restart Service","Renonder siervice"}. {"Restore Backup from File at ","Rapexhî dispoy li fitchî copeye di såvrité so "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Rapexhî l' copeye di såvrité binaire après l' renondaedje ki vént d' ejabberd (çoula prind moens d' memwere del fé insi):"}. {"Restore binary backup immediately:","Rapexhî do côp foû d' ene copeye di såvrité binaire:"}. {"Restore plain text backup immediately:","Rapexhî do côp foû d' ene copeye di såvrité tecse:"}. {"Restore","Rapexhî"}. {"Room Configuration","Apontiaedje del såle"}. {"Room creation is denied by service policy","L' ahivaedje del såle est rfuzé pal politike do siervice"}. {"Room description","Discrijhaedje del såle"}. {"Room Occupants","Prezints el såle"}. {"Room title","Tite del såle"}. {"Roster","Djivêye des soçons"}. {"Roster of ","Djivêye des soçons da "}. {"Roster size","Grandeu del djivêye des soçons"}. {"RPC Call Error","Aroke di houcaedje RPC"}. {"Running Nodes","Nuks en alaedje"}. {"~s access rule configuration","Apontiaedje des rîles d' accès a ~s"}. {"Saturday","semdi"}. {"Script check","Acertinaedje do scripe"}. {"Search Results for ","Rizultats do cweraedje po "}. {"Search users in ","Cweri des uzeus dins "}. {"Send announcement to all online users","Evoyî l' anonce a tos les uzeus raloyîs"}. {"Send announcement to all online users on all hosts","Evoyî l' anonce a tos les uzeus raloyîs so tos les lodjoes"}. {"Send announcement to all users","Evoyî l' anonce a tos les uzeus"}. {"Send announcement to all users on all hosts","Evoyî l' anonce a tos les uzeus so tos les lodjoes"}. {"September","setimbe"}. {"Server ~b","Sierveu ~b"}. {"Server:","Sierveu:"}. {"Set message of the day and send to online users","Defini l' messaedje do djoû et l' evoyî åzès uzeus raloyîs"}. {"Set message of the day on all hosts and send to online users","Defini l' messaedje do djoû so tos les lodjoes et l' evoyî åzès uzeus raloyîs"}. {"Shared Roster Groups","Pårtaedjîs groupes ezès djivêyes di soçons"}. {"Show Integral Table","Mostrer totå"}. {"Show Ordinary Table","Mostrer crexhince"}. {"Shut Down Service","Arester siervice"}. {"~s invites you to the room ~s","~s vos preye sol såle ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Des cliyints Jabber k' i gn a polèt wårder vosse sicret sol copiutrece, mins vos n' duvrîz fé çoula ki sol copiutrece da vosse, po des råjhons di såvrité."}. {"~s's Offline Messages Queue","messaedjes ki ratindèt el cawêye po ~s"}. {"Start","Enonder"}. {"Start Modules at ","Renonder les modules so "}. {"Start Modules","Enonder des modules"}. {"Statistics of ~p","Sitatistikes di ~p"}. {"Statistics","Sitatistikes"}. {"Stop","Arester"}. {"Stop Modules","Arester des modules"}. {"Stop Modules at ","Arester les modules so "}. {"Stopped Nodes","Nuks essoctés"}. {"Storage Type","Sôre di wårdaedje"}. {"Store binary backup:","Copeye di såvrité binaire:"}. {"Store plain text backup:","Copeye di såvrité tecse:"}. {"Subject","Sudjet"}. {"Submit","Evoyî"}. {"Submitted","Candjmints evoyîs"}. {"Subscription","Abounmimnt"}. {"Sunday","dimegne"}. {"That nickname is already in use by another occupant","Li metou no est ddja eployî pa ene ôte sakî sol såle"}. {"That nickname is registered by another person","Li metou no est ddja edjîstré pa ene ôte sakî"}. {"The CAPTCHA is valid.","Li CAPTCHA est valide."}. {"The CAPTCHA verification has failed","Li verifiaedje CAPTCHA a fwait berwete"}. {"the password is","li scret est"}. {"The password is too weak","li scret est trop flåw"}. {"The password of your Jabber account was successfully changed.","Li scret do conte Jabber da vosse a stî candjî comifåt."}. {"There was an error changing the password: ","Åk n' a nén stî tot candjant l' sicret: "}. {"There was an error creating the account: ","Åk n' a nén stî tot ahivant l' conte: "}. {"There was an error deleting the account: ","Åk n' a nén stî tot disfaçant l' conte: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Pont d' diferince etur les grandes et ptitès letes: «macbeth» est l' minme ki «MacBeth» ou co «Macbeth»"}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Cisse pådje permete d' ahiver on conte Jabber so ç' sierveu Jabber ci. Li JID (IDintifieu Jabber) da vosse serè del cogne: noduzeu@sierveu. Lijhoz atintivmint les instruccions po bén rimpli les tchamps."}. {"This page allows to unregister a Jabber account in this Jabber server.","Cisse pådje permete di disdjîstrer on conte Jabber so ç' sierveu ci."}. {"This room is not anonymous","Cisse såle ci n' est nén anonime"}. {"Thursday","djudi"}. {"Time","Date"}. {"Time delay","Tårdjaedje"}. {"Too many CAPTCHA requests","Pår trop di dmandes CAPTCHA"}. {"Too many unacked stanzas","Pår trop di messaedjes sins acertinaedje di rçuvaedje"}. {"To","Po"}. {"To ~s","Viè ~s"}. {"Total rooms","Totå di såles"}. {"Traffic rate limit is exceeded","Li limite pol volume di trafik a stî passêye"}. {"Transactions Aborted:","Transaccions arestêyes:"}. {"Transactions Committed:","Transaccions evoyeyes:"}. {"Transactions Logged:","Transaccions wårdêyes e djournå:"}. {"Transactions Restarted:","Transaccions renondêyes:"}. {"Tuesday","mårdi"}. {"Unable to generate a CAPTCHA","Nén moyén di djenerer on CAPTCHA"}. {"Unauthorized","Nén otorijhî"}. {"Unregister a Jabber account","Disdjîstrer on conte Jabber"}. {"Unregister","Disdjîstrer"}. {"Update message of the day (don't send)","Mete a djoû l' messaedje do djoû (nén l' evoyî)"}. {"Update message of the day on all hosts (don't send)","Mete a djoû l' messaedje do djoû so tos les lodjoes (nén l' evoyî)"}. {"Update","Mete a djoû"}. {"Update plan","Plan d' metaedje a djoû"}. {"Update ~p","Metaedje a djoû di ~p"}. {"Update script","Sicripe di metaedje a djoû"}. {"Uptime:","Tins dispoy l' enondaedje:"}. {"Use of STARTTLS required","L' eployaedje di STARTTL est oblidjî"}. {"User Management","Manaedjaedje des uzeus"}. {"Username:","No d' uzeu:"}. {"Users are not allowed to register accounts so quickly","Les noveas uzeus n' si polèt nén edjîstrer si raddimint"}. {"Users Last Activity","Dierinne activité des uzeus"}. {"User ~s","Uzeu ~s"}. {"Users","Uzeus"}. {"User","Uzeu"}. {"Validate","Valider"}. {"vCard User Search","Calpin des uzeus"}. {"Virtual Hosts","Forveyous sierveus"}. {"Visitors are not allowed to change their nicknames in this room","Les viziteus èn polèt nén candjî leus metous no po ç' såle ci"}. {"Visitors are not allowed to send messages to all occupants","Les viziteus n' polèt nén evoyî des messaedjes a tos les prezints"}. {"Voice request","Dimande di vwès"}. {"Voice requests are disabled in this conference","Les dmandes di vwès sont dismetowes e cisse conferince ci"}. {"Wednesday","mierkidi"}. {"You can later change your password using a Jabber client.","Vos ploz candjî vosse sicret pus tård avou on cliyint Jabber."}. {"You have been banned from this room","Vos avoz stî bani di cisse såle ci"}. {"You must fill in field \"Nickname\" in the form","Vos dvoz rimpli l' tchamp «Metou no» dins l' formiulaire"}. {"You need a client that supports x:data and CAPTCHA to register","Vos avoz mezåjhe d' on cliyint ki sopoite x:data eyet CAPTCHA po vs edjîstrer"}. {"You need a client that supports x:data to register the nickname","Vos avoz mezåjhe d' on cliyint ki sopoite x:data po-z edjîstrer l' metou no"}. {"You need an x:data capable client to configure mod_irc settings","Vos avoz mezåjhe d' on cliyint ki sopoite x:data po candjî ls apontiaedjes di mod_irc"}. {"You need an x:data capable client to search","Vos avoz mezåjhe d' on cliyint ki sopoite x:data po fé on cweraedje"}. {"Your active privacy list has denied the routing of this stanza.","Vosse djivêye di privaceye active a rfuzé l' evoyaedje di ç' messaedje ci."}. {"Your contact offline message queue is full. The message has been discarded.","Li cawêye di messaedjes e môde disraloyî di vosse soçon est plinne. Li messaedje a stî tapé å diale."}. {"Your Jabber account was successfully created.","Li conte Jabber da vosse a stî ahivé comifåt."}. {"Your Jabber account was successfully deleted.","Li conte Jabber da vosse a stî disfacé comifåt."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Vos messaedjes po ~s sont blokés. Po les disbloker, alez vey ~s"}. ejabberd-18.01/priv/msgs/eo.po0000644000232200023220000016725613225664356016560 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Andreas van Cranenburgh \n" "Language-Team: \n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Esperanto\n" "X-Generator: Poedit 1.6.10\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " ŝanĝis la temon al: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Pasvorto estas bezonata por eniri ĉi tiun babilejon" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Agordo de atingo" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Agordo de atingokontrolo" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Atingokontrol-listoj" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Atingo-reguloj" #: mod_configure:1095 msgid "Access control lists" msgstr "Atingokontrol-listoj" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Atingo rifuzita de serv-politiko" #: mod_configure:1113 msgid "Access rules" msgstr "Atingo-reguloj" #: mod_configure:1772 msgid "Action on user" msgstr "Ago je uzanto" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Aldonu Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Aldonu novan" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Aldonu Uzanton" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administro" #: mod_configure:1767 msgid "Administration of " msgstr "Mastrumado de " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Administrantaj rajtoj bezonata" #: mod_configure:507 msgid "All Users" msgstr "Ĉiuj Uzantoj" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Ĉiu aktiveco" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Permesu uzantojn ŝanĝi la temon" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Permesu uzantojn informpeti aliajn uzantojn" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Permesu uzantojn sendi invitojn" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Permesu uzantojn sendi privatajn mesaĝojn" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Permesu al vizitantoj ŝanĝi siajn kaŝnomojn" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Permesu uzantojn sendi privatajn mesaĝojn al" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Permesu al vizitantoj sendi statmesaĝon en ĉeest-sciigoj" #: mod_announce:611 msgid "Announcements" msgstr "Anoncoj" #: mod_muc_log:476 msgid "April" msgstr "Aprilo" #: mod_muc_log:480 msgid "August" msgstr "Aŭgusto" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Faru Sekurkopion" #: mod_configure:584 msgid "Backup Management" msgstr "Mastrumado de sekurkopioj" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Sekurkopio de ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Faru sekurkopion je " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Malĝusta formo" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Naskiĝtago" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA teksaĵ-paĝo" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "CPU-tempo" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Ŝanĝu pasvorton" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Ŝanĝu pasvorton de uzanto" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Karaktroj ne permesata:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Karaktroj ne permesata:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Karaktroj ne permesata:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Agordo de babilejo ŝanĝita" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Babilejo kreita" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Babilejo neniigita" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Babilejo lanĉita" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Babilejo haltita" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Babilejoj" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Elektu uzantnomon kaj pasvorton por registri je ĉi tiu servilo" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Elektu modulojn por fini" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Elektu konserv-tipon de tabeloj" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Elektu ĉu permesi la abonon de ĉi tiu ento" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Urbo" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Ordonoj" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Babilejo ne ekzistas" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Agordo" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Agordo de babilejo ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Konektataj risurcoj:" #: mod_irc:526 msgid "Connections parameters" msgstr "Konekto-parametroj" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Lando" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Datumbazo" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Agordo de datumbaz-tabeloj je " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Datumbaz-tabeloj je ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Datumbazo" #: mod_muc_log:484 msgid "December" msgstr "Decembro" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Kutime farigu uzantojn kiel partpoprenantoj" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Forigu elektata(j)n" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Forigu Uzanton" #: mod_announce:629 msgid "Delete message of the day" msgstr "Forigu mesaĝo de la tago" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Forigu mesaĝo de la tago je ĉiu gastigo" #: mod_shared_roster:900 msgid "Description:" msgstr "Priskribo:" #: mod_configure:889 msgid "Disc only copy" msgstr "Nur disk-kopio" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Montrataj grupoj:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Ne donu vian pasvorton al iun ajn, eĉ ne al la administrantoj de la Ĵabber-" "servilo." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Skribu sekurkopion en plata teksto al " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Skribu en plata tekst-dosiero" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Redaktu atributojn" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Ĉu aprobu, aŭ malaprobu la voĉ-peton." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Eroj" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Retpoŝto" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "la pasvorto estas" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Ŝaltu protokoladon" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Enkodigo por servilo ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Haltigu Uzant-seancon" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Enmetu liston de {Modulo, [Elektebloj]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Enmetu kaŝnomon kiun vi volas registri" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Enmetu vojon por sekurkopio" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Enmetu vojon al jabberd14-uzantdosierujo" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Enmetu vojon al jabberd14-uzantdosiero" #: mod_configure:974 msgid "Enter path to text file" msgstr "Enmetu vojon al plata teksto" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Enmetu montrita teksto" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Enmetu uzantnomon kaj enkodigojn kiujn vi volas uzi por konektoj al IRC-" "serviloj. Elektu 'Sekvonto' por ekhavi pliajn kampojn. Elektu 'Kompletigu' " "por savi agordojn." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Enmetu uzantnomon,j enkodigojn, pordojn kaj pasvortojn kiujn vi volas uzi " "por konektoj al IRC-serviloj" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang-a Jabber-Servilo" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Eraro" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Ekzemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"sekreto\"}, {\"vendetta." "fef.net\", \"iso8859-1\", 7000}, {\"irc.iutestservilo.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Eksportu ĉiuj tabeloj kiel SQL-informmendo al dosierujo:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Eksportu datumojn de ĉiuj uzantoj en servilo al PIEFXIS dosieroj (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Eksportu datumoj de uzantoj en gastigo al PIEFXIS dosieroj (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Malsukcesis ekstrakti JID-on de via voĉ-pet-aprobo" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Lasta Nomo" #: mod_muc_log:474 msgid "February" msgstr "Februaro" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Kompletigu la formon por serĉi rekonata Jabber-uzanto (Aldonu * je la fino " "de la kampo por rekoni subĉenon" #: mod_muc_log:467 msgid "Friday" msgstr "Vendredo" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Plena Nomo" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Montru nombron de konektataj uzantoj" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Montru nombron de registritaj uzantoj" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Montru tempon de lasta ensaluto" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Montru pasvorton de uzanto" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Montru statistikojn de uzanto" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Meza Nomo" #: mod_shared_roster:923 msgid "Group " msgstr "Grupo " #: mod_roster:916 msgid "Groups" msgstr "Grupoj" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Gastigo" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP-adresoj" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC-transportilo" #: mod_irc:496 msgid "IRC Username" msgstr "IRC-kaŝnomo" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC-babilejo (ne aldonu #-prefikson)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Nodo ne trovita" #: mod_irc:673 msgid "IRC server" msgstr "IRC-servilo" #: mod_irc:756 msgid "IRC settings" msgstr "IRC agordoj" #: mod_irc:766 msgid "IRC username" msgstr "IRC-uzantnomo" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Se vi ne vidas la CAPTCHA-imagon jene, vizitu la teksaĵ-paĝon." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Se vi volas specifi diversajn pordojn, pasvortojn, enkodigojn por IRC-" "serviloj, kompletigu la jenan liston kun la formo '{\"irc-servilo\", " "\"enkodigo\", porto, \"pasvorto\"}'. Se ne specifita, ĉi tiu servilo uzas la " "enkodigo \"~s\", porto ~p, malplena pasvorto." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importu dosierujo" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importu dosieron" #: mod_configure:982 msgid "Import User from File at " msgstr "Importu uzanton de dosiero el " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importu uzantojn de jabberd14-uzantdosieroj" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importu uzantojn de dosierujo ĉe " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importu uzantojn de jabberd14-uzantdosieroj" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importu uzanto-datumojn de PIEFXIS dosiero (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importu uzantojn de jabberd14-uzantdosieroj" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Malĝusta mesaĝo-tipo" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Elirantaj s-al-s-konektoj:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Nekorekta pasvorto" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Nekorekta pasvorto" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Voĉ-petoj estas malebligita en jena babilejo" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Ne estas permesata sendi privatajn mesaĝojn" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Malpermesas sendi mesaĝojn de tipo \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Nur partoprenantoj rajtas sendi privatajn mesaĝojn al la babilejo" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Ĵabber-konto registrado" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Januaro" #: mod_irc:665 msgid "Join IRC channel" msgstr "Eniras IRC-babilejon" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Eniru IRC-babilejon jen" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Eniru IRC-babilejon en ĉi Jabber-ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Julio" #: mod_muc_log:478 msgid "June" msgstr "Junio" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Lasta aktiveco" #: mod_configure:1646 msgid "Last login" msgstr "Lasta ensaluto" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Lasta monato" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Lasta jaro" #: mod_configure:941 msgid "List of modules to start" msgstr "Listo de moduloj por starti" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Listo de babilejoj" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Atentataj pordoj" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Atentataj pordoj je " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Bazanivela ĝisdatigo-skripto" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Farigu partoprento-liston publika" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Farigu babilejon protektata per CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Farigu babilejon sole por membroj" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Farigu babilejon moderigata" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Farigu babilejon protektata per pasvorto" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Farigu babilejon daŭra" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Farigu babilejon publike trovebla" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "IRC-uzantnomo" #: mod_muc_log:475 msgid "March" msgstr "Marĉo" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Limigo de nombro de partoprenantoj" #: mod_muc_log:477 msgid "May" msgstr "Majo" #: mod_shared_roster:907 msgid "Members:" msgstr "Membroj:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Membreco estas bezonata por eniri ĉi tiun babilejon" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Memoru vian pasvorton, aŭ skribu ĝin sur papero formetata je sekura loko. Je " "Ĵabber ne ekzistas aŭtomata metodo por reakiri vian pasvorton se vi forgesas " "ĝin." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memoro" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Teksto de mesaĝo" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Meza Nomo" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Moderantaj rajtoj bezonata" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Ĝisdatigitaj moduloj" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modulo" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduloj" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Moduloj je ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Lundo" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Grupbabilado" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nomo" #: mod_shared_roster:896 msgid "Name:" msgstr "Nomo:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Neniam" #: mod_register_web:377 msgid "New Password:" msgstr "Nova Pasvorto:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Kaŝnomo" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Kaŝnomo-registrado je " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Kaŝnomo ~s ne ekzistas en la babilejo" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Nodo ne trovita" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Neniu datumo" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Neniu teksto donita por anonc-mesaĝo" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Nodo ne trovita" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Nodo ne trovita" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Nodo ne trovita" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nodo ne trovita" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Nodo ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nodoj" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nenio" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Ne trovita" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "Novembro" #: mod_configure:1212 msgid "Number of online users" msgstr "Nombro de konektataj uzantoj" #: mod_configure:1202 msgid "Number of registered users" msgstr "Nombro de registritaj uzantoj" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "Bone" #: mod_muc_log:482 msgid "October" msgstr "Oktobro" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Liverontaj mesaĝoj" #: mod_offline:761 msgid "Offline Messages:" msgstr "Liverontaj mesaĝoj" #: mod_register_web:373 msgid "Old Password:" msgstr "Malnova Pasvorto:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Konektata" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Konektataj Uzantoj" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Konektataj uzantoj:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Nur moderigantoj rajtas ŝanĝi la temon en ĉi tiu babilejo" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Nur moderigantoj kaj partoprenantoj rajtas ŝanĝi la temon en ĉi tiu babilejo" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Nur moderigantoj rajtas ŝanĝi la temon en ĉi tiu babilejo" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Nur moderigantoj povas aprobi voĉ-petojn" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Nur partoprenantoj rajtas sendi mesaĝojn al la babilejo" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Nur partoprenantoj rajtas sendi informmendojn al la babilejoj" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Nur servo-administrantoj rajtas sendi serv-mesaĝojn" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Elektebloj" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Organiz-nomo" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Organiz-parto" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Elirantaj s-al-s-konektoj" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Elirantaj s-al-s-konektoj:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Mastraj rajtoj bezonata" #: mod_offline:693 msgid "Packet" msgstr "Pakaĵo" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Pasvorto" #: mod_configure:1130 msgid "Password Verification" msgstr "Pasvortkontrolo" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Pasvortkontrolo:" #: mod_irc:822 msgid "Password ~b" msgstr "Pasvorto ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Pasvorto:" #: mod_configure:998 msgid "Path to Dir" msgstr "Vojo al dosierujo" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Voje de dosiero" #: mod_roster:915 msgid "Pending" msgstr "Atendanta" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periodo: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Permanentaj babilejoj" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Sondaĵo" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Rimarku ke ĉi tiuj elektebloj nur sekurkopias la propran Mnesia-datumbazon. " "Se vi uzas la ODBC-modulon, vi ankaŭ devas sekurkopii tiujn SQL-datumbazoj " "aparte." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Bonvolu atendi iomete antaŭ ol sendi plian voĉ-peton" #: mod_adhoc:274 msgid "Pong" msgstr "Resondaĵo" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Pordo" #: mod_irc:828 msgid "Port ~b" msgstr "Pordo ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokolo" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubAbo abonpeto" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Public-Abonado" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Malpermesas informmendoj al partoprenantoj en ĉi tiu babilejo" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM- kaj disk-kopio" #: mod_configure:889 msgid "RAM copy" msgstr "RAM-kopio" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Eraro de RPC-alvoko" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Kruda" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Ĉu vere forigi mesaĝon de la tago?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Ricevanto ne ĉeestas en la babilejo " #: mod_register_web:275 msgid "Register" msgstr "Registru" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Registru Ĵabber-konton" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Registritaj uzantoj" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Registritaj uzantoj:" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Registritaj uzantnomoj" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registraĵo en mod_irc de " #: mod_configure:889 msgid "Remote copy" msgstr "Fora kopio" #: mod_roster:963 msgid "Remove" msgstr "Forigu" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Forigu ĉiujn liverontajn mesaĝojn" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Forigu uzanton" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Anstataŭigita je nova konekto" #: mod_configure:1675 msgid "Resources" msgstr "Risurcoj" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Restartu" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Restartu Servon" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restaŭru" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaŭrigu de dosiero el " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "Restaŭrigu duuman sekurkopion post sekvonta ejabberd-restarto" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Restaŭrigu duuman sekurkopion tuj:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Restaŭrigu sekurkopion el plata tekstdosiero tuj" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Babilejo-agordo" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Nombro de ĉeestantoj" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Ĉi tiu serv-politiko ne permesas babilejo-kreadon" #: mod_muc_log:1064 msgid "Room description" msgstr "Babilejo-priskribo" #: mod_muc_log:1027 msgid "Room title" msgstr "Babilejo-nomo" #: mod_roster:1082 msgid "Roster" msgstr "Kontaktlisto" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Kontaktlisto de " #: mod_configure:1671 msgid "Roster size" msgstr "Kontaktlist-grando" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Funkciantaj Nodoj" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Sabato" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "La aŭtomata Turingtesto estas ĝusta" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Skript-kontrolo" #: mod_vcard:453 msgid "Search Results for " msgstr "Serĉ-rezultoj de " #: mod_vcard:427 msgid "Search users in " msgstr "Serĉu uzantojn en " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Sendu anoncon al ĉiu konektata uzanto" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Sendu anoncon al ĉiu konektata uzanto de ĉiu gastigo" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Sendu anoncon al ĉiu uzanto" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Sendu anoncon al ĉiu uzanto de ĉiu gastigo" #: mod_muc_log:481 msgid "September" msgstr "Septembro" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Servilo ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Servilo:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Enmetu mesaĝon de la tago kaj sendu al konektataj uzantoj" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Enmetu mesaĝon de la tago je ĉiu gastigo kaj sendu al konektataj uzantoj" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Komuna Kontaktlist-grupo" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Montru integran tabelon" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Montru ordinaran tabelon" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Haltigu Servon" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Kelkaj Ĵabber-klientoj povas memori vian pasvorton je via komputilo. Nur uzu " "tiun eblon se vi fidas ke via komputilo estas sekura." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Startu" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Startu Modulojn" #: mod_configure:936 msgid "Start Modules at " msgstr "Startu modulojn je " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistikoj" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistikoj de ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Haltigu" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Haltigu Modulojn" #: mod_configure:918 msgid "Stop Modules at " msgstr "Haltigu modulojn je " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Neaktivaj Nodoj" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Konserv-tipo" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Konservu duuman sekurkopion:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Skribu sekurkopion en plata tekstdosiero" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Temo" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Sendu" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Sendita" #: mod_roster:914 msgid "Subscription" msgstr "Abono" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Dimanĉo" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Tiu kaŝnomo jam estas uzata de alia partoprenanto" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Kaŝnomo estas registrita de alia persono" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "La CAPTCHA ĝustas" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "La CAPTCHA-kontrolado malsukcesis" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "La pasvorto estas ne sufiĉe forta" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "La pasvorto de via Ĵabber-konto estas sukcese ŝanĝata." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Estis eraro dum ŝanĝi de la pasvortro:" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Estis eraro dum kreado de la konto:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Estis eraro dum forigado de la konto:" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "Uskleco ne signifas: macbeth estas la sama ol MacBeth kaj Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Jena paĝo ebligas kreadon de Ĵabber-konto je ĉi-Ĵabber-servilo. Via JID " "(Ĵabber-IDentigilo) estos ĉi-tiel: uzantnomo@servilo. Bonvolu legu bone la " "instrukciojn por korekta enmetigo de la kampoj. " #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Jena pagxo ebligas malregistri Jxabber-konton je ĉi-servilo." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Ĉi tiu babilejo ne estas anonima" #: mod_muc_log:466 msgid "Thursday" msgstr "Ĵaŭdo" #: mod_offline:690 msgid "Time" msgstr "Tempo" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Prokrasto" #: mod_offline:692 msgid "To" msgstr "Ĝis" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Al ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Tro multaj CAPTCHA-petoj" #: mod_proxy65_service:223 #, fuzzy msgid "Too many active bytestreams" msgstr "Tro da neagnoskitaj stancoj" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "Tro da neagnoskitaj stancoj" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Voĉ-petoj estas malebligita en jena babilejo" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Babilejoj" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Trafikrapida limigo superita" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transakcioj nuligitaj" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transakcioj enmetitaj" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transakcioj protokolitaj" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transakcioj restartitaj" #: mod_muc_log:464 msgid "Tuesday" msgstr "Mardo" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Ne eblis krei CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Nepermesita" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Malregistru" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Malregistru Ĵabber-konton" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Ĝisdatigu" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Ŝanĝu mesaĝon de la tago (ne sendu)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Ŝanĝu mesaĝon de la tago je ĉiu gastigo (ne sendu)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Ĝisdatigo-plano" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Ĝisdatigo-skripto" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Ĝisdatigu ~p-n" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Daŭro de funkciado" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Uzo de STARTTLS bezonata" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Uzo de STARTTLS bezonata" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Uzanto" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Uzanto-administrado" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Nodo ne trovita" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Uzanto ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Uzantnomo" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Uzantoj" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Lasta aktiveco de uzanto" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Ne estas permesata al uzantoj registri tiel rapide" #: mod_roster:954 msgid "Validate" msgstr "Validigu" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtual-gastigoj" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "" "Ne estas permesata al vizitantoj ŝanĝi siajn kaŝnomojn en ĉi tiu ĉambro" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Vizitantoj ne rajtas sendi mesaĝojn al ĉiuj partoprenantoj" #: mod_muc_room:3879 msgid "Voice request" msgstr "Voĉ-peto" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Voĉ-petoj estas malebligita en jena babilejo" #: mod_muc_log:465 msgid "Wednesday" msgstr "Merkredo" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Poste vi povas ŝanĝi vian pasvorton per Ĵabber-kliento." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Vi estas malpermesata en ĉi tiu babilejo" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Vi devas kompletigi la \"Kaŝnomo\" kampon" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Vi bezonas klienton subtenante x:data-funkcio kaj CAPTCHA por registri " "kaŝnomon" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Vi bezonas klienton subtenante x:data-funkcio por registri kaŝnomon" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Vi bezonas klienton kun x:data-funkcio por agordi mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Vi bezonas klienton kun x:data-funkcio por serĉado" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Ne estas permesata sendi privatajn mesaĝojn" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Via Ĵabber-konto estis sukcese kreata." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Via Ĵabber-konto estas sukcese forigita." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Via aktiva privatec-listo malpermesas enkursigi ĉi-tiun pakaĵon" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "" "Mesaĝo-atendovico de la senkonekta kontakto estas plena. La mesaĝo estas " "forĵetita" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Viaj mesaĝoj al ~s estas blokata. Por malbloki ilin, iru al ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC-modulo" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC-modulo" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberd Multicast-servo" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Public-Abonada modulo" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bajtfluo modulo" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Teksaĵa Administro" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard-modulo" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "estas forbarita" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "estas forpelita" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "estas forpelita pro sistem-haltigo" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "estas forpelita pro aparteneca ŝanĝo" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "estas forpelita ĉar la babilejo fariĝis sole por membroj" #: mod_muc_log:410 msgid "is now known as" msgstr "nun nomiĝas" #: mod_muc_log:370 msgid "joins the room" msgstr "eniras la babilejo" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "eliras la babilejo" #: mod_muc_room:3856 msgid "private, " msgstr "privata, " #: mod_muc_room:3955 msgid "the password is" msgstr "la pasvorto estas" #: mod_vcard:292 msgid "vCard User Search" msgstr "Serĉado de vizitkartoj" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Agordo de atingo-reguloj de ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s invitas vin al la babilejo ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "Mesaĝo-atendovico de ~s" #~ msgid "No resource provided" #~ msgstr "Neniu risurco donita" #, fuzzy #~ msgid "Server" #~ msgstr "Servilo:" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Tro da malsukcesaj aŭtentprovoj (~p) de ĉi tiu IP-adreso (~s). La adreso " #~ "estos malbarata je ~s UTC." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Ĉi tiu IP-adreso estas barata in ~s" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s estas nevalida" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Nevalida aparteneco: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Nevalida rolo: ~s" #~ msgid "No limit" #~ msgstr "Neniu limigo" #~ msgid "Present real Jabber IDs to" #~ msgstr "Montru verajn Jabber ID-ojn al" #~ msgid "moderators only" #~ msgstr "moderantoj sole" #~ msgid "anyone" #~ msgstr "iu ajn" #, fuzzy #~ msgid "Moderator" #~ msgstr "moderantoj sole" #~ msgid "nobody" #~ msgstr "neniu" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permesu uzantojn sendi voĉ-petojn" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Minimuma intervalo inter voĉ-petoj (je sekundoj)" #~ msgid "Enable message archiving" #~ msgstr "Ŝaltu mesaĝo-arkivo" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Esceptu Ĵabber-identigilojn je CAPTCHA-defio" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Vi bezonas klienton kun x:data-funkcio por agordi la babilejon" #~ msgid "Number of occupants" #~ msgstr "Nombro de ĉeestantoj" #~ msgid "User JID" #~ msgstr "Uzant-JID" #~ msgid "Grant voice to this person?" #~ msgstr "Koncedu voĉon al ĉi-persono?" #~ msgid "Node ID" #~ msgstr "Nodo ID" #~ msgid "Subscriber Address" #~ msgstr "Abonanta adreso" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Ĉu permesi ĉi tiun Jabber ID aboni al la jena PubAbo-nodo" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Liveru aĵojn de event-sciigoj" #~ msgid "Deliver event notifications" #~ msgstr "Liveru event-sciigojn" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Sciigu abonantoj kiam la agordo de la nodo ŝanĝas" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Sciigu abonantoj kiam la nodo estas forigita" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Sciigu abonantoj kiam eroj estas forigita de la nodo" #~ msgid "Persist items to storage" #~ msgstr "Savu erojn en konservado" #~ msgid "A friendly name for the node" #~ msgstr "Kromnomo por ĉi tiu nodo" #~ msgid "Max # of items to persist" #~ msgstr "Maksimuma kiomo de eroj en konservado" #~ msgid "Whether to allow subscriptions" #~ msgstr "Ĉu permesi aboni" #~ msgid "Specify the access model" #~ msgstr "Specifu atingo-modelon" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Kontaktlist-grupoj kiuj rajtas aboni" #~ msgid "Specify the publisher model" #~ msgstr "Enmetu publikadan modelon" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Forigu ĉiujn erojn kiam la rilata publikanto malkonektiĝas" #~ msgid "Specify the event message type" #~ msgstr "Specifu tipo de event-mesaĝo" #~ msgid "Max payload size in bytes" #~ msgstr "Maksimuma aĵo-grando je bajtoj" #~ msgid "When to send the last published item" #~ msgstr "Kiam sendi la laste publicitan eron" #~ msgid "Only deliver notifications to available users" #~ msgstr "Nur liveru sciigojn al konektataj uzantoj" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Aro kun kiu nodo estas filigita" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Kompletigu la formon por serĉi rekonata Jabber-uzanto" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Elirantaj s-al-s-serviloj" #~ msgid "Delete" #~ msgstr "Forigu" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Ĉi tiu partoprenanta estas forpelata de la babilejo pro sendado de erar-" #~ "mesaĝo" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Ĉi tiu partoprenanto estas forpelata de la babilejo pro sendo de erar-" #~ "mesaĝo al alia partoprenanto" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Ĉi tiu partoprenanto estas forpelata de la babilejo pro sendo de erar-" #~ "ĉeesto" #~ msgid "Encodings" #~ msgstr "Enkodigoj" #~ msgid "(Raw)" #~ msgstr "(Kruda)" #~ msgid "Specified nickname is already registered" #~ msgstr "Donita kaŝnomo jam estas registrita" #~ msgid "Size" #~ msgstr "Grando" #~ msgid "You must fill in field \"nick\" in the form" #~ msgstr "Vi devas enmeti kampon \"kaŝnomo\"" ejabberd-18.01/priv/msgs/sk.msg0000644000232200023220000005722213225664356016731 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Konfigurácia prístupu"}. {"Access Control List Configuration","Konfigurácia zoznamu prístupových oprávnení (ACL)"}. {"Access control lists","Zoznamy prístupových oprávnení (ACL)"}. {"Access Control Lists","Zoznamy prístupových oprávnení (ACL)"}. {"Access denied by service policy","Prístup bol zamietnutý nastavením služby"}. {"Access rules","Prístupové pravidlá"}. {"Access Rules","Prístupové pravidlá"}. {"Action on user","Operácia aplikovaná na užívateľa"}. {"Add Jabber ID","Pridať Jabber ID"}. {"Add New","Pridať nový"}. {"Add User","Pridať používateľa"}. {"Administration","Administrácia"}. {"Administration of ","Administrácia "}. {"Administrator privileges required","Sú potrebné práva administrátora"}. {"All activity","Všetky aktivity"}. {"Allow users to change the subject","Povoliť užívateľom meniť tému"}. {"Allow users to query other users","Povoliť užívateľom dotazovať sa informácie o iných užívateľoch"}. {"Allow users to send invites","Povoliť používateľom posielanie pozvánok"}. {"Allow users to send private messages","Povoliť užívateľom odosielať súkromné správy"}. {"Allow visitors to change nickname","Návštevníci môžu meniť prezývky"}. {"Allow visitors to send private messages to","Povoliť užívateľom odosielať súkromné správy"}. {"Allow visitors to send status text in presence updates","Návštevníci môžu posielať textové informácie v stavových správach"}. {"All Users","Všetci užívatelia"}. {"Announcements","Oznámenia"}. {"A password is required to enter this room","Pre vstup do miestnosti je potrebné heslo"}. {"April","Apríl"}. {"August","August"}. {"Backup Management","Správa zálohovania"}. {"Backup to File at ","Záloha do súboru na "}. {"Backup","Zálohovať"}. {"Bad format","Zlý formát"}. {"Birthday","Dátum narodenia"}. {"CAPTCHA web page","Webová stránka CAPTCHA"}. {"Change Password","Zmeniť heslo"}. {"Change User Password","Zmeniť heslo užívateľa"}. {"Characters not allowed:","Nepovolené znaky:"}. {"Chatroom configuration modified","Nastavenie diskusnej miestnosti bolo zmenené"}. {"Chatroom is created","Diskusná miestnosť je vytvorená"}. {"Chatroom is destroyed","Diskusná miestnosť je zrušená"}. {"Chatroom is started","Diskusná miestnosť je obnovená"}. {"Chatroom is stopped","Diskusná miestnosť je pozastavená"}. {"Chatrooms","Diskusné miestnosti"}. {"Choose a username and password to register with this server","Zvolte meno užívateľa a heslo pre registráciu na tomto servere"}. {"Choose modules to stop","Vyberte moduly, ktoré majú byť zastavené"}. {"Choose storage type of tables","Vyberte typ úložiska pre tabuľky"}. {"Choose whether to approve this entity's subscription.","Zvolte, či chcete povoliť toto odoberanie"}. {"City","Mesto"}. {"Commands","Príkazy"}. {"Conference room does not exist","Diskusná miestnosť neexistuje"}. {"Configuration","Konfigurácia"}. {"Configuration of room ~s","Konfigurácia miestnosti ~s"}. {"Connected Resources:","Pripojené zdroje:"}. {"Connections parameters","Parametre spojenia"}. {"Country","Krajina"}. {"CPU Time:","Čas procesoru"}. {"Database","Databáza"}. {"Database Tables Configuration at ","Konfigurácia databázových tabuliek "}. {"December","December"}. {"Default users as participants","Užívatelia sú implicitne členmi"}. {"Delete message of the day on all hosts","Zmazať správu dňa na všetkých serveroch"}. {"Delete message of the day","Zmazať správu dňa"}. {"Delete Selected","Zmazať vybrané"}. {"Delete User","Vymazať užívateľa"}. {"Description:","Popis:"}. {"Disc only copy","Len kópia disku"}. {"Displayed Groups:","Zobrazené skupiny:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Nevyzrádzajte heslo nikomu, ani administrátorom tohoto Jabber servera."}. {"Dump Backup to Text File at ","Uložiť zálohu do textového súboru na "}. {"Dump to Text File","Uložiť do textového súboru"}. {"Edit Properties","Editovať vlastnosti"}. {"Either approve or decline the voice request.","Povolte alebo zamietnite žiadosť o Voice."}. {"ejabberd IRC module","ejabberd IRC modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"Elements","Prvky"}. {"Email","E-mail"}. {"Enable logging","Zapnúť zaznamenávanie histórie"}. {"Encoding for server ~b","Kódovanie pre server ~b"}. {"End User Session","Ukončiť reláciu užívateľa"}. {"Enter list of {Module, [Options]}","Vložte zoznam modulov {Modul, [Parametre]}"}. {"Enter nickname you want to register","Zadajte prezývku, ktorú chcete registrovať"}. {"Enter path to backup file","Zadajte cestu k súboru so zálohou"}. {"Enter path to jabberd14 spool dir","Zadajte cestu k jabberd14 spool adresáru"}. {"Enter path to jabberd14 spool file","Zadajte cestu k spool súboru jabberd14"}. {"Enter path to text file","Zadajte cestu k textovému súboru"}. {"Enter the text you see","Zadajte zobrazený text"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Vložte meno používateľa a kódovanie, ktoré chcete používať pri pripojení na IRC servery. Kliknutím na tlačítko 'Ďalej' môžete zadať niektoré ďalšie hodnoty. Pomocou 'Ukončiť ' uložíte nastavenia."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Vložte meno používateľa, kódovanie, porty a heslo ktoré chcete používať pri pripojení na IRC server"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Error","Chyba"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Príklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportovať dáta všetkých uživateľov na serveri do súborov PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportovať dáta uživateľov na hostitelovi do súborov PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Nepodarilo sa nájsť JID v súhlase o Voice."}. {"Family Name","Priezvisko"}. {"February","Február"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Pre vyhľadanie Jabber používateľa vyplňte formulár (pridajte znak * na koniec, pre vyhľadanie podreťazca)"}. {"Friday","Piatok"}. {"From","Od"}. {"From ~s","Od ~s"}. {"Full Name","Celé meno: "}. {"Get Number of Online Users","Zobraziť počet pripojených užívateľov"}. {"Get Number of Registered Users","Zobraziť počet registrovaných užívateľov"}. {"Get User Last Login Time","Zobraziť čas posledného prihlásenia"}. {"Get User Password","Zobraziť heslo užívateľa"}. {"Get User Statistics","Zobraziť štatistiku užívateľa"}. {"Group ","Skupina "}. {"Groups","Skupiny"}. {"has been banned","bol(a) zablokovaný(á)"}. {"has been kicked because of an affiliation change","bol vyhodený(á) kvôli zmene priradenia"}. {"has been kicked because of a system shutdown","bol vyhodený(á) kvôli reštartu systému"}. {"has been kicked because the room has been changed to members-only","bol vyhodený(á), pretože miestnosť bola vyhradená len pre členov"}. {"has been kicked","bol(a) vyhodený(á) z miestnosti"}. {" has set the subject to: ","zmenil(a) tému na: "}. {"Host","Server"}. {"If you don't see the CAPTCHA image here, visit the web page.","Pokiaľ nevidíte obrázok CAPTCHA, navštívte webovú stránku."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Ak chcete zadať iné porty, heslá a kódovania pre IRC servery, vyplnte zoznam s hodnotami vo formáte '{\"irc server\",\"kódovanie\", \"port\", \"heslo\"}'. Predvolenéi hodnoty pre túto službu sú: kódovanie \"~s\", port ~p a žiadne heslo."}. {"Import Directory","Import adresára"}. {"Import File","Import súboru"}. {"Import user data from jabberd14 spool file:","Importovať dáta užívateľov z jabberd14 spool súboru:"}. {"Import User from File at ","Importovať užívateľa zo súboru na "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importovat dáta užívateľov zo súboru PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importovať dáta užívateľov z jabberd14 spool adresára:"}. {"Import Users from Dir at ","Importovať užívateľov z adresára na "}. {"Import Users From jabberd14 Spool Files","Importovať užívateľov z jabberd14 spool súborov"}. {"Improper message type","Nesprávny typ správy"}. {"Incorrect password","Nesprávne heslo"}. {"IP addresses","IP adresa"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC kanál (bez počiatočnej #)"}. {"IRC server","IRC server"}. {"IRC settings","Nastavania IRC"}. {"IRC Transport","IRC Transport"}. {"IRC username","IRC prezývka"}. {"IRC Username","IRC prezývka"}. {"is now known as","sa premenoval(a) na"}. {"It is not allowed to send private messages","Nieje povolené posielať súkromné správy"}. {"It is not allowed to send private messages of type \"groupchat\"","Nie je dovolené odoslanie súkromnej správy typu \"Skupinová správa\" "}. {"It is not allowed to send private messages to the conference","Nie je povolené odosielať súkromné správy do konferencie"}. {"Jabber Account Registration","Registrácia jabber účtu"}. {"Jabber ID","Jabber ID"}. {"January","Január"}. {"Join IRC channel","Pripojit IRC kanál"}. {"joins the room","vstúpil(a) do miestnosti"}. {"Join the IRC channel here.","Propojiť IRC kanál sem."}. {"Join the IRC channel in this Jabber ID: ~s","Pripojit IRC kanál k tomuto Jabber ID: ~s"}. {"July","Júl"}. {"June","Jún"}. {"Last Activity","Posledná aktivita"}. {"Last login","Posledné prihlásenie"}. {"Last month","Posledný mesiac"}. {"Last year","Posledný rok"}. {"leaves the room","odišiel(a) z miestnosti"}. {"Listened Ports at ","Otvorené porty na "}. {"Listened Ports","Otvorené portov"}. {"List of modules to start","Zoznam modulov, ktoré majú byť spustené"}. {"Low level update script","Nízkoúrovňový aktualizačný skript"}. {"Make participants list public","Nastaviť zoznam zúčastnených ako verejný"}. {"Make room CAPTCHA protected","Chrániť miestnosť systémom CAPTCHA"}. {"Make room members-only","Nastaviť miestnosť len pre členov"}. {"Make room moderated","Nastaviť miestnosť ako moderovanú"}. {"Make room password protected","Chrániť miestnosť heslom"}. {"Make room persistent","Nastaviť miestnosť ako trvalú"}. {"Make room public searchable","Nastaviť miestnosť ako verejne prehľadávateľnú"}. {"March","Marec"}. {"Maximum Number of Occupants","Počet účastníkov"}. {"May","Máj"}. {"Members:","Členovia:"}. {"Membership is required to enter this room","Pre vstup do miestnosti je potrebné byť členom"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Zapamätajte si heslo alebo si ho zapíšte na papier. Jabber neposkytuje automatickú funkciu ako zistiť zabudnuté heslo. "}. {"Memory","Pamäť"}. {"Message body","Telo správy"}. {"Middle Name","Prostredné meno: "}. {"Moderator privileges required","Sú potrebné práva moderátora"}. {"Modified modules","Modifikované moduly"}. {"Module","Modul"}. {"Modules","Moduly"}. {"Monday","Pondelok"}. {"Name:","Meno:"}. {"Name","Meno"}. {"Never","Nikdy"}. {"New Password:","Nové heslo:"}. {"Nickname","Prezývka"}. {"Nickname Registration at ","Registrácia prezývky na "}. {"Nickname ~s does not exist in the room","Prezývka ~s v miestnosti neexistuje"}. {"No body provided for announce message","Správa neobsahuje text"}. {"No Data","Žiadne dáta"}. {"Node not found","Uzol nenájdený"}. {"Nodes","Uzly"}. {"None","Nič"}. {"Not Found","Nebol nájdený"}. {"November","November"}. {"Number of online users","Počet online užívateľov"}. {"Number of registered users","Počet registrovaných užívateľov"}. {"October","Október"}. {"Offline Messages:","Offline správy"}. {"Offline Messages","Offline správy"}. {"OK","OK"}. {"Old Password:","Staré heslo:"}. {"Online","Online"}. {"Online Users:","Online používatelia:"}. {"Online Users","Online užívatelia"}. {"Only moderators and participants are allowed to change the subject in this room","Len moderátori a zúčastnený majú povolené meniť tému tejto miestnosti"}. {"Only moderators are allowed to change the subject in this room","Len moderátori majú povolené meniť tému miestnosti"}. {"Only moderators can approve voice requests","Len moderátori môžu schváliť žiadosť o Voice"}. {"Only occupants are allowed to send messages to the conference","Len členovia majú povolené zasielať správy do konferencie"}. {"Only occupants are allowed to send queries to the conference","Len členovia majú povolené dotazovať sa o konferencii"}. {"Only service administrators are allowed to send service messages","Iba správcovia služby majú povolené odosielanie servisných správ"}. {"Options","Nastavenia"}. {"Organization Name","Meno organizácie: "}. {"Organization Unit","Organizačná jednotka: "}. {"Outgoing s2s Connections:","Odchádzajúce s2s spojenia:"}. {"Outgoing s2s Connections","Odchádzajúce s2s spojenia"}. {"Owner privileges required","Sú vyžadované práva vlastníka"}. {"Packet","Paket"}. {"Password ~b","Heslo ~b"}. {"Password:","Heslo:"}. {"Password","Heslo"}. {"Password Verification:","Overenie hesla"}. {"Password Verification","Overenie hesla"}. {"Path to Dir","Cesta k adresáru"}. {"Path to File","Cesta k súboru"}. {"Pending","Čakajúce"}. {"Period: ","Čas:"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Prosím, berte na vedomie, že tieto nastavenia zázálohujú iba zabudovnú Mnesia databázu. Ak používate ODBC modul, musíte zálohovať vašu SQL databázu separátne."}. {"Please, wait for a while before sending new voice request","Prosím počkate, predtým než pošlete novú žiadosť o Voice"}. {"Pong","Pong"}. {"Port ~b","Port ~b"}. {"Port","Port"}. {"private, ","súkromná, "}. {"Protocol","Protokol"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","Žiadosť odberateľa PubSub"}. {"Queries to the conference members are not allowed in this room","Dotazovať sa o členoch nie je v tejto miestnosti povolené"}. {"RAM and disc copy","Kópia RAM a disku"}. {"RAM copy","Kópia RAM"}. {"Raw","Surové dáta"}. {"Really delete message of the day?","Skutočne zmazať správu dňa?"}. {"Recipient is not in the conference room","Príjemca sa nenachádza v konferenčnej miestnosti"}. {"Register a Jabber account","Zaregistrovať Jabber účet"}. {"Registered Users:","Registrovaní používatelia:"}. {"Registered Users","Registrovaní používatelia"}. {"Register","Zoznam kontaktov"}. {"Registration in mod_irc for ","Registrácia do mod_irc na"}. {"Remote copy","Vzdialená kópia"}. {"Remove All Offline Messages","Odstrániť všetky offline správy"}. {"Remove","Odstrániť"}. {"Remove User","Odstrániť užívateľa"}. {"Replaced by new connection","Nahradené novým spojením"}. {"Resources","Zdroje"}. {"Restart","Reštart"}. {"Restart Service","Reštartovať službu"}. {"Restore Backup from File at ","Obnoviť zálohu zo súboru na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Obnoviť binárnu zálohu pri nasledujúcom reštarte ejabberd (vyžaduje menej pamäte)"}. {"Restore binary backup immediately:","Okamžite obnoviť binárnu zálohu:"}. {"Restore","Obnoviť"}. {"Restore plain text backup immediately:","Okamžite obnoviť zálohu z textového súboru:"}. {"Room Configuration","Nastavenia miestnosti"}. {"Room creation is denied by service policy","Vytváranie miestnosti nie je povolené"}. {"Room description","Popis miestnosti"}. {"Room Occupants","Ľudí v miestnosti"}. {"Room title","Názov miestnosti"}. {"Roster of ","Zoznam kontaktov "}. {"Roster size","Počet kontaktov v zozname"}. {"Roster","Zoznam kontaktov"}. {"RPC Call Error","Chyba RPC volania"}. {"Running Nodes","Bežiace uzly"}. {"~s access rule configuration","~s konfigurácia prístupového pravidla"}. {"Saturday","Sobota"}. {"Script check","Kontrola skriptu"}. {"Search Results for ","Hľadať výsledky pre "}. {"Search users in ","Hľadať užívateľov v "}. {"Send announcement to all online users","Odoslať zoznam všetkým online používateľom"}. {"Send announcement to all online users on all hosts","Odoslať oznam všetkým online používateľom na všetkých serveroch"}. {"Send announcement to all users","Odoslať oznam všetkým používateľom"}. {"Send announcement to all users on all hosts","Poslať oznámenie všetkým užívateľom na všetkých serveroch"}. {"September","September"}. {"Server ~b","Server ~b"}. {"Set message of the day and send to online users","Nastaviť správu dňa a odoslať ju online používateľom"}. {"Set message of the day on all hosts and send to online users","Nastaviť správu dňa na všetkých serveroch a poslať ju online užívateľom"}. {"Shared Roster Groups","Skupiny pre zdieľaný zoznam kontaktov"}. {"Show Integral Table","Zobraziť kompletnú tabuľku"}. {"Show Ordinary Table","Zobraziť bežnú tabuľku"}. {"Shut Down Service","Vypnúť službu"}. {"~s invites you to the room ~s","~s Vás pozýva do miestnosti ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Niektorí Jabber klenti môžu ukladať heslá v počítači. Používajte túto funkciu len ak veríte, že sú tam v bezpečí. "}. {"~s's Offline Messages Queue","~s Offline správy"}. {"Start Modules at ","Spustiť moduly na "}. {"Start Modules","Spustiť moduly"}. {"Start","Štart"}. {"Statistics of ~p","Štatistiky ~p"}. {"Statistics","Štatistiky"}. {"Stop Modules at ","Zastaviť moduly na "}. {"Stop Modules","Zastaviť moduly"}. {"Stopped Nodes","Zastavené uzly"}. {"Stop","Zastaviť"}. {"Storage Type","Typ úložiska"}. {"Store binary backup:","Uložiť binárnu zálohu:"}. {"Store plain text backup:","Uložiť zálohu do textového súboru:"}. {"Subject","Predmet"}. {"Submit","Odoslať"}. {"Submitted","Odoslané"}. {"Subscription","Prihlásenie"}. {"Sunday","Nedeľa"}. {"That nickname is already in use by another occupant","Prezývka je už používaná iným členom"}. {"That nickname is registered by another person","Prezývka je už zaregistrovaná inou osobou"}. {"The CAPTCHA is valid.","Platná CAPTCHA."}. {"The CAPTCHA verification has failed","Overenie pomocou CAPTCHA zlihalo"}. {"the password is","heslo je"}. {"The password is too weak","heslo je"}. {"The password of your Jabber account was successfully changed.","Heslo k Jabber účtu bolo úspešne zmenené."}. {"There was an error changing the password: ","Pri zmene hesla nastala chyba: "}. {"There was an error creating the account: ","Pri vytváraní účtu nastala chyba: "}. {"There was an error deleting the account: ","Pri rušení účtu nastala chyba:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Veľké a malé písmená sa nerozlišujú: macbeth je to isté ako MacBeth a Macbeth."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Táto stránka umožňuje refistrovať Jabber účet na tomto serveri. Vaše JID (Jabber IDentifikátor) bude vo formáte: užívateľ@server. Pozorne sledujte inštrukcie, aby ste údaje vypnili správne."}. {"This page allows to unregister a Jabber account in this Jabber server.","Na tejto stránke si môžete zrušiť Jabber účet registrovaný na tomto serveri."}. {"This room is not anonymous","Táto miestnosť nie je anonymná"}. {"Thursday","Štvrtok"}. {"Time","Čas"}. {"Time delay","Časový posun"}. {"Too many CAPTCHA requests","Príliš veľa žiadostí o CAPTCHA"}. {"To","Pre"}. {"To ~s","Pre ~s"}. {"Traffic rate limit is exceeded","Bol prekročený prenosový limit"}. {"Transactions Aborted:","Transakcie zrušená"}. {"Transactions Committed:","Transakcie potvrdená"}. {"Transactions Logged:","Transakcie zaznamenaná"}. {"Transactions Restarted:","Transakcie reštartovaná"}. {"Tuesday","Utorok"}. {"Unable to generate a CAPTCHA","Nepodarilo sa vygenerovat CAPTCHA"}. {"Unauthorized","Neautorizovaný"}. {"Unregister a Jabber account","Zrušiť Jabber účet"}. {"Unregister","Zrušiť účet"}. {"Update","Aktualizovať"}. {"Update message of the day (don't send)","Aktualizovať správu dňa (neodosielať)"}. {"Update message of the day on all hosts (don't send)","Upraviť správu dňa na všetkých serveroch"}. {"Update plan","Aktualizovať plán"}. {"Update script","Aktualizované skripty"}. {"Uptime:","Uptime:"}. {"Use of STARTTLS required","Je vyžadované použitie STARTTLS "}. {"User Management","Správa užívateľov"}. {"Username:","IRC prezývka"}. {"Users are not allowed to register accounts so quickly","Nieje dovolené vytvárať účty tak rýchlo po sebe"}. {"Users Last Activity","Posledná aktivita používateľa"}. {"Users","Používatelia"}. {"User","Užívateľ"}. {"Validate","Overiť"}. {"vCard User Search","Hľadať užívateľov vo vCard"}. {"Virtual Hosts","Virtuálne servery"}. {"Visitors are not allowed to change their nicknames in this room","V tejto miestnosti nieje povolené meniť prezývky"}. {"Visitors are not allowed to send messages to all occupants","Návštevníci nemajú povolené zasielať správy všetkým prihláseným do konferencie"}. {"Voice requests are disabled in this conference","Žiadosti o Voice nie sú povolené v tejto konferencii"}. {"Voice request","Žiadosť o Voice"}. {"Wednesday","Streda"}. {"You can later change your password using a Jabber client.","Neskôr si heslo môžete zmeniť pomocou Jabber klienta."}. {"You have been banned from this room","Boli ste vylúčený z tejto miestnosti"}. {"You must fill in field \"Nickname\" in the form","Musíte vyplniť políčko \"Prezývka\" vo formulári"}. {"You need a client that supports x:data and CAPTCHA to register","Na registráciu prezývky potrebujete klienta podporujúceho z x:data"}. {"You need a client that supports x:data to register the nickname","Na registráciu prezývky potrebujete klienta podporujúceho z x:data"}. {"You need an x:data capable client to configure mod_irc settings","Pre konfiguráciu mod_irc potrebujete klienta podporujúceho x:data"}. {"You need an x:data capable client to search","Na vyhľadávanie potrebujete klienta podporujúceho x:data"}. {"Your active privacy list has denied the routing of this stanza.","Aktívny list súkromia zbránil v smerovaní tejto stanzy."}. {"Your contact offline message queue is full. The message has been discarded.","Fronta offline správ tohoto kontaktu je plná. Správa bola zahodená."}. {"Your Jabber account was successfully created.","Jabber účet bol úspešne vytvorený."}. {"Your Jabber account was successfully deleted.","Váš Jabber účet bol úspešne odstránený."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Správa určená pre ~s bola zablokovaná. Oblokovať ju môžete na ~s"}. ejabberd-18.01/priv/msgs/no.po0000644000232200023220000016561613225664356016567 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Stian B. Barmen \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Norwegian (bokmål)\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " har satt emnet til: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Et passord kreves for tilgang til samtalerommet" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Tilgangskonfigurasjon" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfigurasjon for Tilgangskontroll lister" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Tilgangskontrollister" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Tilgangsregler" #: mod_configure:1095 msgid "Access control lists" msgstr "Tilgangskontroll lister" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Tilgang nektes på grunn av en tjeneste regel" #: mod_configure:1113 msgid "Access rules" msgstr "Tilgangsregler" #: mod_configure:1772 msgid "Action on user" msgstr "Handling på bruker" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Legg til Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Legg til ny" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Legg til Bruker" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administrasjon" #: mod_configure:1767 msgid "Administration of " msgstr "Administrasjon av " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Administratorprivilegier kreves" #: mod_configure:507 msgid "All Users" msgstr "Alle Brukere" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "All aktivitet" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Tillat brukere å endre emne" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Tillat brukere å sende forespørsel til andre brukere" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Tillat brukere å sende invitasjoner" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Tillat brukere å sende private meldinger" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Tillat besøkende å endre kallenavn" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Tillat brukere å sende private meldinger til" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Tillat besøkende å sende status tekst i " #: mod_announce:611 msgid "Announcements" msgstr "Kunngjøringer" #: mod_muc_log:476 msgid "April" msgstr "april" #: mod_muc_log:480 msgid "August" msgstr "august" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Sikkerhetskopier" #: mod_configure:584 msgid "Backup Management" msgstr "Håndtere Sikkerehetskopiering" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Sikkerhetskopi av " #: mod_configure:948 msgid "Backup to File at " msgstr "Sikkerhetskopiere til Fil på " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Feil format" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Fødselsdag" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA web side" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "CPU Tid:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Endre Passord" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Endre Brukers Passord" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Ikke godtatte tegn:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Ikke godtatte tegn:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Ikke godtatte tegn:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Samtalerommets konfigurasjon er endret" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Samtalerom er opprettet" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Samtalerom er fjernet" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Samtalerom er startet" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Samtalerom er stoppet" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Samtalerom" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Velg et brukernavn og passord for å registrere på " #: mod_configure:920 msgid "Choose modules to stop" msgstr "Velg hvilke moduler som skal stoppes" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Velg lagringstype for tabeller" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Velg om du vil godkjenne denne eksistensens abonement" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "By" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Kommandoer" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Konferanserommet finnes ikke" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Konfigurasjon" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Konfigurasjon for rom ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Tilkoblede Ressurser:" #: mod_irc:526 msgid "Connections parameters" msgstr "Tilkoblings parametere" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Land" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Database" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Database Tabell Konfigurasjon på " #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Database Tabeller på " #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Database" #: mod_muc_log:484 msgid "December" msgstr "desember" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Standard brukere som deltakere" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Slett valgte" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Slett Bruker" #: mod_announce:629 msgid "Delete message of the day" msgstr "Slett melding for dagen" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Slett melding for dagen på alle maskiner" #: mod_shared_roster:900 msgid "Description:" msgstr "Beskrivelse:" #: mod_configure:889 msgid "Disc only copy" msgstr "Kun diskkopi" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Viste grupper:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Ikke fortell passordet til noen, ikke en gang til administratoren av Jabber " "serveren." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Dump Sikkerhetskopi til Tekstfil på " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Dump til Tekstfil" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Redigere Egenskaper" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Enten godkjenn eller forby lyd forespørselen" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementer" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Epost" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "passordet er" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Slå på logging" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Tekstkoding for server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Avslutt Bruker Sesjon" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Skriv inn en liste av {Module, [Options]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Skriv inn kallenavnet du ønsker å registrere" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Skriv inn sti til sikkerhetskopi filen" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Skriv inn sti til jabberd14 spoolkatalog" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Skriv inn sti til jabberd14 spoolfil" #: mod_configure:974 msgid "Enter path to text file" msgstr "Skriv inn sti til tekstfil" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Skriv inn teksten du ser" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Angi brukernavn og kodinger du ønsker å bruke for å koble til IRC servere. " "Trykk 'Neste' for å få flere felt for å fylle i. Trykk 'Fullfør' for å lagre " "innstillingene." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Skriv brukernavn, tekstkoding, porter og passord du ønsker å bruke for " "tilkobling til IRC servere" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Feil" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Eksempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." "fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "Eksporter data om alle brukere i en server til PIEFXIS filer" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "Eksporter data om alle brukere i en host til PIEFXIS filer (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Feilet i forsøk på å hente JID fra din lyd forespørsel godkjenning" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Etternavn" #: mod_muc_log:474 msgid "February" msgstr "februar" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Fyll inn skjemaet for å søke etter Jabber bruker (Legg til * på slutten av " "feltet for å treffe alle som starter slik)" #: mod_muc_log:467 msgid "Friday" msgstr "fredag" #: mod_offline:691 msgid "From" msgstr "Fra" #: mod_configure:723 msgid "From ~s" msgstr "Fra ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Fullstendig Navn" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Vis Antall Tilkoblede Brukere" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Vis Antall Registrerte Brukere" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Vis Brukers Siste Påloggings Tidspunkt" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Hent Brukers Passord" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Vis Bruker Statistikk" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Mellomnavn" #: mod_shared_roster:923 msgid "Group " msgstr "Gruppe " #: mod_roster:916 msgid "Groups" msgstr "Grupper" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Maskin" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP adresser" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Transport" #: mod_irc:496 msgid "IRC Username" msgstr "IRC Brukernavn" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC kanal (ikke skriv den første #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Noden finnes ikke" #: mod_irc:673 msgid "IRC server" msgstr "IRC server" #: mod_irc:756 msgid "IRC settings" msgstr "IRC instillinger" #: mod_irc:766 msgid "IRC username" msgstr "IRC brukernavn" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Dersom du ikke ser CAPTCHA bilde her, besøk web siden. " #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Om du ønsker å spesifisere tekstkoding for IRC tjenere, fyller du ut en " "liste med verdier i formatet '{\"irc server\", \"encoding\", port, \"password" "\"}'. Denne tjenesten bruker \"~s\" som standard, port ~p, empty password." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importer Katalog" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importer File" #: mod_configure:982 msgid "Import User from File at " msgstr "Importer Bruker fra Fil på " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importer Brukere Fra jabberd14 Spoolfiler" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importer Brukere fra Katalog på " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importer bruker data fra jabberd14 spoolfiler:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importer brukeres data fra en PIEFXIS fil (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importer brukeres data fra jabberd14 spoolfil katalog:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Feilaktig meldingstype" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Utgående s2s Koblinger" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Feil passord" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Feil passord" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Lyd forespørsler er blokkert i denne konferansen" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Det er ikke tillatt å sende private meldinger" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Det er ikke tillatt å sende private meldinger med typen " #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Det er ikke tillatt å sende private meldinger til " #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Jabber Konto Registrering" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "januar" #: mod_irc:665 msgid "Join IRC channel" msgstr "Bli med i IRC kanal" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Bli med i IRC kanalen her. " #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Bli med i IRC kanalen med denne Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "juli" #: mod_muc_log:478 msgid "June" msgstr "juni" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Siste Aktivitet" #: mod_configure:1646 msgid "Last login" msgstr "Siste pålogging" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Siste måned" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Siste året" #: mod_configure:941 msgid "List of modules to start" msgstr "Liste over moduler som skal startes" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Lyttende Porter" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Lyttende Porter på " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Lavnivå oppdaterings skript" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Gjør deltakerlisten offentlig" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Gjør rommet CAPTCHA beskyttet" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Gjør rommet tilgjengelig kun for medlemmer" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Gjør rommet redaktørstyrt" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Passordbeskytt rommet" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Gjør rommet permanent" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Gjør rommet offentlig søkbart" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "IRC brukernavn" #: mod_muc_log:475 msgid "March" msgstr "mars" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Maksimum Antall Deltakere" #: mod_muc_log:477 msgid "May" msgstr "mai" #: mod_shared_roster:907 msgid "Members:" msgstr "Medlemmer:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Medlemskap kreves for tilgang til samtalerommet" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Husk passordet, eller skriv det ned på et papir lagret på et trygt sted. I " "Jabber er det ingen automatisert måte å gjenskape passordet om du glemmer " "det. " #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Minne" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Meldingskropp" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Mellomnavn" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Redaktørprivilegier kreves" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Endrede moduler" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduler" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Moduler på " #: mod_muc_log:463 msgid "Monday" msgstr "mandag" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Navn" #: mod_shared_roster:896 msgid "Name:" msgstr "Navn:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Aldri" #: mod_register_web:377 msgid "New Password:" msgstr "Nytt Passord:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Kallenavn" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registrer Kallenavn på " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Kallenavn ~s eksisterer ikke i dette rommet" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Noden finnes ikke" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Ingen Data" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Ingen meldingskropp gitt for kunngjørings melding" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Noden finnes ikke" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Noden finnes ikke" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Noden finnes ikke" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Noden finnes ikke" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Node " #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Noder" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Ingen" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Finnes Ikke" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "november" #: mod_configure:1212 msgid "Number of online users" msgstr "Antall tilkoblede brukere" #: mod_configure:1202 msgid "Number of registered users" msgstr "Antall registrerte brukere" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "oktober" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Frakoblede Meldinger" #: mod_offline:761 msgid "Offline Messages:" msgstr "Frakoblede Meldinger:" #: mod_register_web:373 msgid "Old Password:" msgstr "Gammelt Passord:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Tilkoblet" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Tilkoblede Brukere" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Tilkoblede Brukere:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Bare ordstyrer tillates å endre emnet i dette rommet" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Bare redaktører og deltakere kan endre emnet i dette rommet" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Bare ordstyrer tillates å endre emnet i dette rommet" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Bare ordstyrer kan godkjenne lyd forespørsler" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Bare deltakere får sende normale meldinger til konferansen" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Bare deltakere er tillatt å sende forespørsler til " #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Bare tjeneste administratorer er tilatt å sende tjeneste " #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Alternativer" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Organisasjonsnavn" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Organisasjonsenhet" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Utgående s2s Koblinger" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Utgående s2s Koblinger" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Eierprivilegier kreves" #: mod_offline:693 msgid "Packet" msgstr "Pakke" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Passord" #: mod_configure:1130 msgid "Password Verification" msgstr "Passord Bekreftelse" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Passord Bekreftelse:" #: mod_irc:822 msgid "Password ~b" msgstr "Passord ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Passord:" #: mod_configure:998 msgid "Path to Dir" msgstr "Sti til Katalog" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Sti til Fil" #: mod_roster:915 msgid "Pending" msgstr "Ventende" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Periode: " #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "forlater rommet" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Merk at disse valgene vil bare sikkerhetskopiere den innebygde Mnesia " "databasen. Dersom du bruker ODBC modulen må du også ta backup av din SQL " "database." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Vennligst vent en stund før du sender en ny lyd forespørsel" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokoll" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub abonements forespørsel" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Forespørsler til konferanse medlemmene er ikke tillat i dette rommet" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "RAM og diskkopi" #: mod_configure:889 msgid "RAM copy" msgstr "RAM kopi" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "RPC Kall Feil" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Rå" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Virkelig slette melding for dagen?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Mottakeren er ikke i konferanserommet" #: mod_register_web:275 msgid "Register" msgstr "Registrer" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Registrer en Jabber konto" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Registrerte Brukere" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Registrerte Brukere:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Registrerte Brukere" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registrering i mod_irc for " #: mod_configure:889 msgid "Remote copy" msgstr "Lagres ikke lokalt" #: mod_roster:963 msgid "Remove" msgstr "Fjern" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Fjern Alle Frakoblede Meldinger" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Fjern Bruker" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Erstattet av en ny tilkobling" #: mod_configure:1675 msgid "Resources" msgstr "Ressurser" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Starte på nytt" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Start Tjeneste på Nytt" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Gjenopprett" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Gjenopprett fra Sikkerhetsopifil på " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Gjenopprette binær backup etter neste ejabberd omstart (krever mindre minne):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Gjenopprette binær backup umiddelbart:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Gjenopprette rentekst sikkerhetskopi umiddelbart:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Rom Konfigurasjon" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Samtalerom Deltakere" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Oppretting av rom nektes av en tjenste regel" #: mod_muc_log:1064 msgid "Room description" msgstr "Rom beskrivelse" #: mod_muc_log:1027 msgid "Room title" msgstr "Romtittel" #: mod_roster:1082 msgid "Roster" msgstr "Kontaktliste" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Kontaktliste for " #: mod_configure:1671 msgid "Roster size" msgstr "Kontaktliste størrelse" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Kjørende Noder" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "lørdag" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "Captchaen er ikke gyldig" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Skript sjekk" #: mod_vcard:453 msgid "Search Results for " msgstr "Søke Resultater for " #: mod_vcard:427 msgid "Search users in " msgstr "Søk etter brukere i " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Send kunngjøring alle tilkoblede brukere" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Send kunngjøring til alle tilkoblede brukere på alle " #: mod_announce:613 msgid "Send announcement to all users" msgstr "Send kunngjøring til alle brukere" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Send kunngjøring til alle brukere på alle maskiner" #: mod_muc_log:481 msgid "September" msgstr "september" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Server:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Angi melding for dagen og send til tilkoblede brukere" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "Angi melding for dagen på alle maskiner og send til " #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Delte Kontaktgrupper" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Vis Integral Tabell" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Vis Ordinær Tabell" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Avslutt Tjeneste" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Noen Jabber klienter kan lagre passordet på datamaskinen. Bruk bare den " "funksjonen dersom du er sikker på at maskinen er trygg." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Start" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Start Moduler" #: mod_configure:936 msgid "Start Modules at " msgstr "Start Moduler på " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Statistikk" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Statistikk for ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Stoppe" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Stop Moduler" #: mod_configure:918 msgid "Stop Modules at " msgstr "Stopp Moduler på " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Stoppede Noder" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Lagringstype" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Lagre binær sikkerhetskopi:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Lagre rentekst sikkerhetskopi:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Tittel" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Send" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Innsendt" #: mod_roster:914 msgid "Subscription" msgstr "Abonnement" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "søndag" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Det kallenavnet er allerede i bruk av en annen deltaker" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Det kallenavnet er registrert av en annen person" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Captchaen er ikke gyldig" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "CAPTCHA godkjenningen har feilet" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Passordet er for svakt" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Passordet for din Jabber konto ble endret." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "En feil skjedde under endring av passordet:" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "En feil skjedde under oppretting av kontoen:" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "En feil skjedde under sletting av kontoen: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Denne er ufølsom for små og store bokstaver: macbeth er det samme som " "MacBeth og Macbeth. " #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Denne siden lar deg lage en Jabber konto på denne Jabber serveren. Din JID " "(Jabber ID) vil være i formatet: brukernavn@server. Vennligst les " "instruksjonene nøye slik at du fyller ut skjemaet riktig." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Denne siden lar deg avregistrere en Jabber konto på denne Jabber serveren." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Dette rommet er ikke anonymt" #: mod_muc_log:466 msgid "Thursday" msgstr "torsdag" #: mod_offline:690 msgid "Time" msgstr "Tid" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Tids forsinkelse" #: mod_offline:692 msgid "To" msgstr "Til" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Til ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "For mange CAPTCHA forespørsler" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Lyd forespørsler er blokkert i denne konferansen" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Samtalerom" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Trafikkmengde grense overskredet" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Avbrutte Transasksjoner:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Sendte Transaksjoner:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Loggede Transasksjoner:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Omstartede Transasksjoner:" #: mod_muc_log:464 msgid "Tuesday" msgstr "tirsdag" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Umulig å generere en CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Uautorisert" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Avregistrer" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Avregistrer en Jabber konto" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Oppdatere" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Oppdater melding for dagen (ikke send)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Oppdater melding for dagen på alle maskiner (ikke send)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Oppdaterings plan" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Oppdaterings skript" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Oppdater " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Oppetid:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Bruk av STARTTLS kreves" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Bruk av STARTTLS kreves" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Bruker" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Bruker Behandling" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Noden finnes ikke" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Bruker " #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Brukernavn:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Brukere" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Brukers Siste Aktivitet" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Brukere får ikke lov til registrere kontoer så fort" #: mod_roster:954 msgid "Validate" msgstr "Bekrefte gyldighet" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtuella Maskiner" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Besøkende får ikke lov å endre kallenavn i dette " #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Besøkende får ikke sende meldinger til alle deltakere" #: mod_muc_room:3879 msgid "Voice request" msgstr "Lyd forespørsel" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Lyd forespørsler er blokkert i denne konferansen" #: mod_muc_log:465 msgid "Wednesday" msgstr "onsdag" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Du kan når som helst endre passordet via en Jabber klient." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Du har blitt bannlyst i dette rommet." #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Du må fylle inn feltet \"Nickname\" i skjemaet" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Du trenger en klient som støtter x:data og CAPTCHA for registrering " #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Du trenger en klient som støtter x:data for å registrere kallenavnet" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Du trenger en x:data kompatibel klient for å konfigurere mod_irc instillinger" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Du tregner en klient som støtter x:data for å kunne " #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Det er ikke tillatt å sende private meldinger" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Din Jabber konto ble opprettet" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Dni Jabber konto er blitt sltettet." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Din aktive privat liste har blokkert rutingen av denne strofen." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "Kontaktens frakoblede meldingskø er full. Meldingen har blitt kassert." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Dine meldinger til ~s blir blokkert. For å åpne igjen, besøk ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC modul" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC modul" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe modul" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams modul" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard modul" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "har blitt bannlyst" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "har blitt kastet ut" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "har blitt kastet ut på grunn av at systemet avslutter" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "har blitt kastet ut på grunn av en tilknytnings endring" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" "har blitt kastet ut på grunn av at rommet er endret til kun-for-medlemmer" #: mod_muc_log:410 msgid "is now known as" msgstr "er nå kjent som" #: mod_muc_log:370 msgid "joins the room" msgstr "kommer inn i rommet" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "forlater rommet" #: mod_muc_room:3856 msgid "private, " msgstr "privat, " #: mod_muc_room:3955 msgid "the password is" msgstr "passordet er" #: mod_vcard:292 msgid "vCard User Search" msgstr "vCard Bruker Søk" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "tilgangsregel konfigurasjon for ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s inviterer deg til rommet ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~ss kø for Frakoblede Meldinger" #~ msgid "No resource provided" #~ msgstr "Ingen ressurs angitt" #, fuzzy #~ msgid "Server" #~ msgstr "Server:" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Ugyldig Jabber ID ~s" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Ugyldig rang: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Ugyldig rolle: ~s" #~ msgid "No limit" #~ msgstr "Ingen grense" #~ msgid "Present real Jabber IDs to" #~ msgstr "Presenter ekte Jabber IDer til" #~ msgid "moderators only" #~ msgstr "kun for redaktører" #~ msgid "anyone" #~ msgstr "hvem som helst" #, fuzzy #~ msgid "Moderator" #~ msgstr "kun for redaktører" #~ msgid "nobody" #~ msgstr "ingen" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Tillat brukere å sende lyd forespørsler" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Minimums interval mellom lyd forespørsler (i sekunder)" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Ekskluder Jabber IDer fra CAPTCHA utfordring" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Du trenger en klient som støtter x:data for å " #~ msgid "Number of occupants" #~ msgstr "Antall deltakere" #~ msgid "User JID" #~ msgstr "Bruker JID" #~ msgid "Grant voice to this person?" #~ msgstr "Gi lyd til denne personen?" #~ msgid "Node ID" #~ msgstr "Node ID" #~ msgid "Subscriber Address" #~ msgstr "Abonnements Adresse" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Tillat denne Jabber ID å abonnere på denne pubsub " #~ msgid "Deliver payloads with event notifications" #~ msgstr "Send innhold sammen med kunngjøringer" #~ msgid "Deliver event notifications" #~ msgstr "Lever begivenhets kunngjøringer" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Informer abonnenter når node konfigurasjonen endres" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Informer abonnenter når noden slettes" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Informer abonnenter når elementer fjernes fra noden" #~ msgid "Persist items to storage" #~ msgstr "Vedvarende elementer til lagring" #~ msgid "A friendly name for the node" #~ msgstr "Et vennlig navn for noden" #~ msgid "Max # of items to persist" #~ msgstr "Høyeste # elementer som skal lagres" #~ msgid "Whether to allow subscriptions" #~ msgstr "Om man skal tillate abonnenter" #~ msgid "Specify the access model" #~ msgstr "Spesifiser aksess modellen" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Kontaktliste grupper som tillates å abonnere" #~ msgid "Specify the publisher model" #~ msgstr "Angi publiserings modell" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "Rydd alle elementer når den aktuelle utgiveren logger av" #~ msgid "Specify the event message type" #~ msgstr "Spesifiser hendelsesbeskjed type" #~ msgid "Max payload size in bytes" #~ msgstr "Største innholdsstørrelse i byte" #~ msgid "When to send the last published item" #~ msgstr "Når skal siste publiserte artikkel sendes" #~ msgid "Only deliver notifications to available users" #~ msgstr "Send kunngjøringer bare til tilgjengelige brukere" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Samlingene som en node er assosiert med" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Fyll inn felt for å søke etter Jabber brukere" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Utgående s2s Tjenere" #~ msgid "Delete" #~ msgstr "Slett" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Denne deltakeren er kastet ut av rommet fordi han sendte en feilmelding" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Denne deltakeren er kastet ut av rommet fordi han sendte en feilmelding " #~ "til en annen deltaker" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Denne deltakeren er kastet ut av rommet fordi han sendte feil " #~ "tilstederværelse" ejabberd-18.01/priv/msgs/th.msg0000644000232200023220000005545513225664356016735 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","การกำหนดค่าการเข้าถึง"}. {"Access Control List Configuration","การกำหนดค่ารายการควบคุมการเข้าถึง"}. {"Access control lists","รายการควบคุมการเข้าถึง"}. {"Access Control Lists","รายการควบคุมการเข้าถึง"}. {"Access denied by service policy","การเข้าถึงถูกปฏิเสธโดยนโยบายการบริการ"}. {"Access rules","กฎการเข้าถึง"}. {"Access Rules","กฎการเข้าถึง"}. {"Action on user","การดำเนินการกับผู้ใช้"}. {"Add Jabber ID","เพิ่ม Jabber ID"}. {"Add New","เพิ่มผู้ใช้ใหม่"}. {"Add User","เพิ่มผู้ใช้"}. {"Administration","การดูแล"}. {"Administration of ","การดูแล "}. {"Administrator privileges required","ต้องมีสิทธิพิเศษของผู้ดูแลระบบ"}. {"All activity","กิจกรรมทั้งหมด"}. {"Allow users to query other users","อนุญาตให้ผู้ใช้ถามคำถามกับผู้ใช้คนอื่นๆ ได้"}. {"Allow users to send invites","อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้"}. {"Allow users to send private messages","อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว"}. {"All Users","ผู้ใช้ทั้งหมด"}. {"Announcements","ประกาศ"}. {"April","เมษายน"}. {"August","สิงหาคม"}. {"Backup","การสำรองข้อมูล "}. {"Backup Management","การจัดการข้อมูลสำรอง"}. {"Backup to File at ","สำรองไฟล์ข้อมูลที่"}. {"Bad format","รูปแบบที่ไม่ถูกต้อง"}. {"Birthday","วันเกิด"}. {"Change Password","เปลี่ยนรหัสผ่าน"}. {"Change User Password","เปลี่ยนรหัสผ่านของผู้ใช้"}. {"Chatroom configuration modified","มีการปรับเปลี่ยนการกำหนดค่าของห้องสนทนา"}. {"Chatrooms","ห้องสนทนา"}. {"Choose a username and password to register with this server","เลือกชื่อผู้ใช้และรหัสผ่านเพื่อลงทะเบียนกับเซิร์ฟเวอร์นี้"}. {"Choose modules to stop","เลือกโมดูลเพื่อหยุดการทำงาน"}. {"Choose storage type of tables","เลือกชนิดการจัดเก็บของตาราง"}. {"Choose whether to approve this entity's subscription.","เลือกว่าจะอนุมัติการสมัครเข้าใช้งานของเอนทิตี้นี้หรือไม่"}. {"City","เมือง"}. {"Commands","คำสั่ง"}. {"Conference room does not exist","ไม่มีห้องประชุม"}. {"Configuration","การกำหนดค่า"}. {"Connected Resources:","ทรัพยากรที่เชื่อมต่อ:"}. {"Country","ประเทศ"}. {"CPU Time:","เวลาการทำงานของ CPU:"}. {"Database","ฐานข้อมูล"}. {"Database Tables Configuration at ","การกำหนดค่าตารางฐานข้อมูลที่"}. {"December","ธันวาคม"}. {"Default users as participants","ผู้ใช้เริ่มต้นเป็นผู้เข้าร่วม"}. {"Delete message of the day","ลบข้อความของวัน"}. {"Delete message of the day on all hosts","ลบข้อความของวันบนโฮสต์ทั้งหมด"}. {"Delete Selected","ลบข้อความที่เลือก"}. {"Delete User","ลบผู้ใช้"}. {"Description:","รายละเอียด:"}. {"Disc only copy","คัดลอกเฉพาะดิสก์"}. {"Displayed Groups:","กลุ่มที่แสดง:"}. {"Dump Backup to Text File at ","ถ่ายโอนการสำรองข้อมูลไปยังไฟล์ข้อความที่"}. {"Dump to Text File","ถ่ายโอนข้อมูลไปยังไฟล์ข้อความ"}. {"Edit Properties","แก้ไขคุณสมบัติ"}. {"ejabberd IRC module","ejabberd IRC module"}. {"ejabberd MUC module","ejabberd MUC module"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. {"ejabberd vCard module","ejabberd vCard module"}. {"Email","อีเมล"}. {"Enable logging","เปิดใช้งานการบันทึก"}. {"End User Session","สิ้นสุดเซสชันของผู้ใช้"}. {"Enter list of {Module, [Options]}","ป้อนรายการของ {โมดูล, [ตัวเลือก]}"}. {"Enter nickname you want to register","ป้อนชื่อเล่นที่คุณต้องการลงทะเบียน"}. {"Enter path to backup file","ป้อนพาธเพื่อสำรองไฟล์ข้อมูล"}. {"Enter path to jabberd14 spool dir","ป้อนพาธไปยัง jabberd14 spool dir"}. {"Enter path to jabberd14 spool file","ป้อนพาธไปยังไฟล์เก็บพักข้อมูล jabberd14"}. {"Enter path to text file","ป้อนพาธของไฟล์ข้อความ"}. {"Erlang Jabber Server","Erlang Jabber Server"}. {"Family Name","นามสกุล"}. {"February","กุมภาพันธ์"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","กรอกข้อมูลในแบบฟอร์มเพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน (ใส่เครื่องหมาย * ที่ท้ายสุดของฟิลด์เพื่อจับคู่กับสตริงย่อย)"}. {"Friday","วันศุกร์"}. {"From","จาก"}. {"From ~s","จาก ~s"}. {"Full Name","ชื่อเต็ม"}. {"Get Number of Online Users","แสดงจำนวนผู้ใช้ออนไลน์"}. {"Get Number of Registered Users","แสดงจำนวนผู้ใช้ที่ลงทะเบียน"}. {"Get User Last Login Time","แสดงเวลาเข้าสู่ระบบครั้งล่าสุดของผู้ใช้"}. {"Get User Password","ขอรับรหัสผ่านของผู้ใช้"}. {"Get User Statistics","แสดงสถิติของผู้ใช้"}. {"Group ","กลุ่ม"}. {"Groups","กลุ่ม"}. {"has been banned","ถูกสั่งห้าม"}. {"has been kicked","ถูกไล่ออก"}. {" has set the subject to: "," ตั้งหัวข้อว่า: "}. {"Host","โฮสต์"}. {"Import Directory","อิมพอร์ตไดเร็กทอรี"}. {"Import File","อิมพอร์ตไฟล์"}. {"Import User from File at ","อิมพอร์ตผู้ใช้จากไฟล์ที่"}. {"Import Users from Dir at ","อิมพอร์ตผู้ใช้จาก Dir ที่"}. {"Import Users From jabberd14 Spool Files","อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14"}. {"Improper message type","ประเภทข้อความไม่เหมาะสม"}. {"Incorrect password","รหัสผ่านไม่ถูกต้อง"}. {"IP addresses","ที่อยู่ IP"}. {"IRC Transport","การส่ง IRC"}. {"IRC Username","ชื่อผู้ใช้ IRC"}. {"is now known as","ซึ่งรู้จักกันในชื่อ"}. {"It is not allowed to send private messages of type \"groupchat\"","ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยัง \"กลุ่มสนทนา\""}. {"It is not allowed to send private messages to the conference","ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม"}. {"Jabber ID","Jabber ID"}. {"January","มกราคม"}. {"joins the room","เข้าห้องสนทนานี้"}. {"July","กรกฎาคม"}. {"June","มิถุนายน"}. {"Last Activity","กิจกรรมล่าสุด"}. {"Last login","การเข้าสู่ระบบครั้งล่าสุด"}. {"Last month","เดือนที่แล้ว"}. {"Last year","ปีที่แล้ว"}. {"leaves the room","ออกจากห้อง"}. {"Listened Ports","พอร์ทฟัง"}. {"Listened Ports at ","พอร์ทฟังที่"}. {"List of modules to start","รายการของโมดูลที่จะเริ่มการทำงาน"}. {"Low level update script","อัพเดตสคริปต์ระดับต่ำ"}. {"Make participants list public","สร้างรายการผู้เข้าร่วมสำหรับใช้งานโดยบุคคลทั่วไป"}. {"Make room members-only","สร้างห้องสำหรับสมาชิกเท่านั้น"}. {"Make room password protected","สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน"}. {"Make room persistent","สร้างเป็นห้องถาวร"}. {"Make room public searchable","สร้างเป็นห้องที่บุคคลทั่วไปสามารถค้นหาได้"}. {"March","มีนาคม"}. {"Maximum Number of Occupants","จำนวนผู้ครอบครองห้องสูงสุด"}. {"May","พฤษภาคม"}. {"Members:","สมาชิก:"}. {"Memory","หน่วยความจำ"}. {"Message body","เนื้อหาของข้อความ"}. {"Middle Name","ชื่อกลาง"}. {"Moderator privileges required","ต้องมีสิทธิพิเศษของผู้ดูแลการสนทนา"}. {"Module","โมดูล"}. {"Modules","โมดูล"}. {"Monday","วันจันทร์"}. {"Name:","ชื่อ:"}. {"Name","ชื่อ"}. {"Never","ไม่เคย"}. {"Nickname","ชื่อเล่น"}. {"Nickname Registration at ","การลงทะเบียนชื่อเล่นที่ "}. {"Nickname ~s does not exist in the room","ไม่มีชื่อเล่น ~s อยู่ในห้องนี้"}. {"No body provided for announce message","ไม่ได้ป้อนเนื้อหาสำหรับข้อความที่ประกาศ"}. {"No Data","ไม่มีข้อมูล"}. {"Node not found","ไม่พบโหนด"}. {"Nodes","โหนด"}. {"None","ไม่มี"}. {"November","พฤศจิกายน"}. {"Number of online users","จำนวนผู้ใช้ออนไลน์"}. {"Number of registered users","จำนวนผู้ใช้ที่ลงทะเบียน"}. {"October","ตุลาคม"}. {"Offline Messages:","ข้อความออฟไลน์:"}. {"Offline Messages","ข้อความออฟไลน์"}. {"OK","ตกลง"}. {"Online","ออนไลน์"}. {"Online Users:","ผู้ใช้ออนไลน์:"}. {"Online Users","ผู้ใช้ออนไลน์"}. {"Only occupants are allowed to send messages to the conference","ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความไปยังห้องประชุม"}. {"Only occupants are allowed to send queries to the conference","ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งกระทู้ถามไปยังห้องประชุม"}. {"Only service administrators are allowed to send service messages","ผู้ดูแลด้านการบริการเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความการบริการ"}. {"Options","ตัวเลือก"}. {"Organization Name","ชื่อองค์กร"}. {"Organization Unit","หน่วยขององค์กร"}. {"Outgoing s2s Connections:","การเชื่อมต่อ s2s ขาออก:"}. {"Outgoing s2s Connections","การเชื่อมต่อ s2s ขาออก"}. {"Owner privileges required","ต้องมีสิทธิพิเศษของเจ้าของ"}. {"Packet","แพ็กเก็ต"}. {"Password:","รหัสผ่าน:"}. {"Password","รหัสผ่าน"}. {"Password Verification","การตรวจสอบรหัสผ่าน"}. {"Path to Dir","พาธไปยัง Dir"}. {"Path to File","พาธของไฟล์ข้อมูล"}. {"Pending","ค้างอยู่"}. {"Period: ","ระยะเวลา:"}. {"Ping","Ping"}. {"Pong","Pong"}. {"Port","พอร์ท"}. {"private, ","ส่วนตัว, "}. {"Publish-Subscribe","เผยแพร่-สมัครเข้าใช้งาน"}. {"PubSub subscriber request","คำร้องขอของผู้สมัครเข้าใช้งาน PubSub"}. {"Queries to the conference members are not allowed in this room","ห้องนี้ไม่อนุญาตให้ส่งกระทู้ถามถึงสมาชิกในห้องประชุม"}. {"RAM and disc copy","คัดลอก RAM และดิสก์"}. {"RAM copy","คัดลอก RAM"}. {"Raw","ข้อมูลดิบ"}. {"Really delete message of the day?","แน่ใจว่าต้องการลบข้อความของวันหรือไม่"}. {"Recipient is not in the conference room","ผู้รับไม่ได้อยู่ในห้องประชุม"}. {"Registered Users:","ผู้ใช้ที่ลงทะเบียน:"}. {"Registered Users","ผู้ใช้ที่ลงทะเบียน"}. {"Registration in mod_irc for ","การลงทะเบียนใน mod_irc สำหรับ"}. {"Remote copy","คัดลอกระยะไกล"}. {"Remove","ลบ"}. {"Remove User","ลบผู้ใช้"}. {"Replaced by new connection","แทนที่ด้วยการเชื่อมต่อใหม่"}. {"Resources","ทรัพยากร"}. {"Restart","เริ่มต้นใหม่"}. {"Restart Service","เริ่มต้นการบริการใหม่อีกครั้ง"}. {"Restore","การคืนค่า"}. {"Restore Backup from File at ","คืนค่าการสำรองข้อมูลจากไฟล์ที่"}. {"Restore binary backup after next ejabberd restart (requires less memory):","คืนค่าข้อมูลสำรองแบบไบนารีหลังจากที่ ejabberd ถัดไปเริ่มการทำงานใหม่ (ใช้หน่วยความจำน้อยลง):"}. {"Restore binary backup immediately:","คืนค่าข้อมูลสำรองแบบไบนารีโดยทันที:"}. {"Restore plain text backup immediately:","คืนค่าข้อมูลสำรองที่เป็นข้อความธรรมดาโดยทันที:"}. {"Room Configuration","การกำหนดค่าห้องสนทนา"}. {"Room creation is denied by service policy","การสร้างห้องสนทนาถูกปฏิเสธโดยนโยบายการบริการ"}. {"Room title","ชื่อห้อง"}. {"Roster","บัญชีรายชื่อ"}. {"Roster of ","บัญชีรายชื่อของ "}. {"Roster size","ขนาดของบัญชีรายชื่อ"}. {"RPC Call Error","ข้อผิดพลาดจากการเรียกใช้ RPC"}. {"Running Nodes","โหนดที่ทำงาน"}. {"~s access rule configuration","~s การกำหนดค่ากฎการเข้าถึง"}. {"Saturday","วันเสาร์"}. {"Script check","ตรวจสอบคริปต์"}. {"Search Results for ","ผลการค้นหาสำหรับ "}. {"Search users in ","ค้นหาผู้ใช้ใน "}. {"Send announcement to all online users","ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมด"}. {"Send announcement to all online users on all hosts","ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมดบนโฮสต์ทั้งหมด"}. {"Send announcement to all users","ส่งประกาศถึงผู้ใช้ทั้งหมด"}. {"Send announcement to all users on all hosts","ส่งประกาศถึงผู้ใช้ทั้งหมดบนโฮสต์ทั้งหมด"}. {"September","กันยายน"}. {"Set message of the day and send to online users","ตั้งค่าข้อความของวันและส่งถึงผู้ใช้ออนไลน์"}. {"Set message of the day on all hosts and send to online users","ตั้งค่าข้อความของวันบนโฮสต์ทั้งหมดและส่งถึงผู้ใช้ออนไลน์"}. {"Shared Roster Groups","กลุ่มบัญชีรายชื่อที่ใช้งานร่วมกัน"}. {"Show Integral Table","แสดงตารางรวม"}. {"Show Ordinary Table","แสดงตารางทั่วไป"}. {"Shut Down Service","ปิดการบริการ"}. {"~s invites you to the room ~s","~s เชิญคุณเข้าร่วมสนทนาในห้อง ~s"}. {"~s's Offline Messages Queue","~s's ลำดับข้อความออฟไลน์"}. {"Start","เริ่ม"}. {"Start Modules","เริ่มโมดูล"}. {"Start Modules at ","เริ่มโมดูลที่"}. {"Statistics","สถิติ"}. {"Statistics of ~p","สถิติของ ~p"}. {"Stop","หยุด"}. {"Stop Modules","หยุดโมดูล"}. {"Stop Modules at ","หยุดโมดูลที่"}. {"Stopped Nodes","โหนดที่หยุด"}. {"Storage Type","ชนิดที่เก็บข้อมูล"}. {"Store binary backup:","จัดเก็บข้อมูลสำรองแบบไบนารี:"}. {"Store plain text backup:","จัดเก็บข้อมูลสำรองที่เป็นข้อความธรรมดา:"}. {"Subject","หัวเรื่อง"}. {"Submit","ส่ง"}. {"Submitted","ส่งแล้ว"}. {"Subscription","การสมัครสมาชิก"}. {"Sunday","วันอาทิตย์"}. {"the password is","รหัสผ่านคือ"}. {"This room is not anonymous","ห้องนี้ไม่ปิดบังชื่อ"}. {"Thursday","วันพฤหัสบดี"}. {"Time","เวลา"}. {"Time delay","การหน่วงเวลา"}. {"To","ถึง"}. {"To ~s","ถึง ~s"}. {"Traffic rate limit is exceeded","อัตราของปริมาณการเข้าใช้เกินขีดจำกัด"}. {"Transactions Aborted:","ทรานแซกชันที่ถูกยกเลิก:"}. {"Transactions Committed:","ทรานแซกชันที่ได้รับมอบหมาย:"}. {"Transactions Logged:","ทรานแซกชันที่บันทึก:"}. {"Transactions Restarted:","ทรานแซกชันที่เริ่มทำงานใหม่อีกครั้ง:"}. {"Tuesday","วันอังคาร"}. {"Update","อัพเดต"}. {"Update message of the day (don't send)","อัพเดตข้อความของวัน (ไม่ต้องส่ง)"}. {"Update message of the day on all hosts (don't send)","อัพเดตข้อความของวันบนโฮสต์ทั้งหมด (ไม่ต้องส่ง) "}. {"Update plan","แผนการอัพเดต"}. {"Update script","อัพเดตสคริปต์"}. {"Uptime:","เวลาการทำงานต่อเนื่อง:"}. {"Use of STARTTLS required","ต้องใช้ STARTTLS"}. {"User","ผู้ใช้"}. {"User Management","การจัดการผู้ใช้"}. {"Users","ผู้ใช้"}. {"Users Last Activity","กิจกรรมล่าสุดของผู้ใช้"}. {"Validate","ตรวจสอบ"}. {"vCard User Search","ค้นหาผู้ใช้ vCard "}. {"Virtual Hosts","โฮสต์เสมือน"}. {"Visitors are not allowed to send messages to all occupants","ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด"}. {"Wednesday","วันพุธ"}. {"You have been banned from this room","คุณถูกสั่งห้ามไมให้เข้าห้องนี้"}. {"You must fill in field \"Nickname\" in the form","คุณต้องกรอกฟิลด์ \"Nickname\" ในแบบฟอร์ม"}. {"You need an x:data capable client to configure mod_irc settings","คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดการตั้งค่า mod_irc"}. {"You need an x:data capable client to search","คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อค้นหา"}. {"Your contact offline message queue is full. The message has been discarded.","ลำดับข้อความออฟไลน์ของผู้ที่ติดต่อของคุณเต็มแล้ว ข้อความถูกลบทิ้งแล้ว"}. ejabberd-18.01/priv/msgs/el.msg0000644000232200023220000010607713225664356016717 0ustar debalancedebalance%% -*- coding: latin-1 -*- {"Access Configuration","Διαμόρφωση Πρόσβασης"}. {"Access Control List Configuration","Διαχείριση στις Λίστες Ελέγχου Πρόσβασης"}. {"Access control lists","Λίστες Ελέγχου Πρόσβασης"}. {"Access Control Lists","Λίστες Ελέγχου Πρόσβασης"}. {"Access denied by service policy","Άρνηση πρόσβασης, λόγω τακτικής παροχής υπηρεσιών"}. {"Access rules","Κανόνες Πρόσβασης"}. {"Access Rules","Κανόνες Πρόσβασης"}. {"Action on user","Eνέργεια για το χρήστη"}. {"Add Jabber ID","Προσθήκη Jabber Ταυτότητας"}. {"Add New","Προσθήκη νέου"}. {"Add User","Προσθήκη Χρήστη"}. {"Administration of ","Διαχείριση του"}. {"Administration","Διαχείριση"}. {"Administrator privileges required","Aπαιτούνται προνόμια διαχειριστή"}. {"All activity","Όλες οι δραστηριότητες"}. {"Allow users to change the subject","Επιτρέψετε στους χρήστες να αλλάζουν το θέμα"}. {"Allow users to query other users","Επιτρέπστε στους χρήστες να ερωτούν άλλους χρήστες"}. {"Allow users to send invites","Επιτρέψετε στους χρήστες να αποστέλλουν προσκλήσεις"}. {"Allow users to send private messages","Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα"}. {"Allow visitors to change nickname","Επιτρέψετε στους επισκέπτες να αλλάζου ψευδώνυμο"}. {"Allow visitors to send private messages to","Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα σε"}. {"Allow visitors to send status text in presence updates","Επιτρέψτε στους επισκέπτες να αποστέλλουν κατάσταση στις ενημερώσεις παρουσίας"}. {"All Users","Όλοι οι χρήστες"}. {"Announcements","Ανακοινώσεις"}. {"A password is required to enter this room","Απαιτείται κωδικός πρόσβασης για είσοδο σε αυτή την αίθουσα"}. {"April","Απρίλιος"}. {"August","Αύγουστος"}. {"Backup Management","Διαχείριση Αντιγράφου Ασφαλείας"}. {"Backup to File at ","Αποθήκευση Αντιγράφου Ασφαλείας σε Αρχείο στο "}. {"Backup","Αποθήκευση Αντιγράφου Ασφαλείας"}. {"Bad format","Ακατάλληλη μορφή"}. {"Birthday","Γενέθλια"}. {"CAPTCHA web page","Ιστοσελίδα CAPTCHA "}. {"Change Password","Αλλαγή κωδικού"}. {"Change User Password","Αλλαγή Κωδικού Πρόσβασης Χρήστη"}. {"Characters not allowed:","Χαρακτήρες δεν επιτρέπονται:"}. {"Chatroom configuration modified","Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε"}. {"Chatroom is created","Η αίθουσα σύνεδριασης δημιουργήθηκε"}. {"Chatroom is destroyed","Η αίθουσα σύνεδριασης διαγράφηκε"}. {"Chatroom is started","Η αίθουσα σύνεδριασης έχει ξεκινήσει"}. {"Chatroom is stopped","Η αίθουσα σύνεδριασης έχει σταματήσει"}. {"Chatrooms","Αίθουσες σύνεδριασης"}. {"Choose a username and password to register with this server","Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον διακομιστή"}. {"Choose modules to stop","Επιλέξτε modules για να σταματήσουν"}. {"Choose storage type of tables","Επιλέξτε τύπο αποθήκευσης των πινάκων"}. {"Choose whether to approve this entity's subscription.","Επιλέξτε αν θα εγκρίθεί η εγγραφή αυτής της οντότητας."}. {"City","Πόλη"}. {"Commands","Εντολές"}. {"Conference room does not exist","Αίθουσα σύνεδριασης δεν υπάρχει"}. {"Configuration of room ~s","Διαμόρφωση Αίθουσας σύνεδριασης ~s"}. {"Configuration","Διαμόρφωση"}. {"Connected Resources:","Συνδεδεμένοι Πόροι:"}. {"Connections parameters","Παράμετροι Συνδέσης"}. {"Country","Χώρα"}. {"CPU Time:","Ώρα CPU:"}. {"Database Tables Configuration at ","Διαμόρφωση Πίνακων βάσης δεδομένων στο "}. {"Database","Βάση δεδομένων"}. {"December","Δεκέμβριος"}. {"Default users as participants","Προεπιλογη χρήστων ως συμμετέχοντες"}. {"Delete message of the day on all hosts","Διαγράψτε το μήνυμα της ημέρας σε όλους τους κεντρικούς υπολογιστές"}. {"Delete message of the day","Διαγράψτε το μήνυμα της ημέρας"}. {"Delete Selected","Διαγραφή επιλεγμένων"}. {"Delete User","Διαγραφή Χρήστη"}. {"Description:","Περιγραφή:"}. {"Disc only copy","Αντίγραφο μόνο σε δίσκο"}. {"Displayed Groups:","Εμφανίσμενες Ομάδες:"}. {"Don't tell your password to anybody, not even the administrators of the Jabber server.","Μην πείτε τον κωδικό πρόσβασής σας σε κανέναν, ακόμη και στους διαχειριστές του διακομιστή Jabber."}. {"Dump Backup to Text File at ","Αποθήκευση Αντιγράφου Ασφαλείας σε αρχείο κειμένου στο "}. {"Dump to Text File","Αποθήκευση σε αρχείο κειμένου"}. {"Edit Properties","Επεξεργασία ιδιοτήτων"}. {"Either approve or decline the voice request.","Είτε εγκρίνετε ή απορρίψτε το αίτημα φωνής."}. {"ejabberd IRC module","ejabberd IRC module"}. {"ejabberd MUC module","ejabberd MUC module"}. {"ejabberd Publish-Subscribe module","ejabberd module Δημοσίευσης-Εγγραφής"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. {"ejabberd vCard module","ejabberd vCard module"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"Elements","Στοιχεία"}. {"Email","Email"}. {"Enable logging","Ενεργοποίηση καταγραφής"}. {"Encoding for server ~b","Κωδικοποίηση για διακομιστή ~b"}. {"End User Session","Τερματισμός Συνεδρίας Χρήστη"}. {"Enter list of {Module, [Options]}","Εισάγετε κατάλογο των (Module, [Επιλογές])"}. {"Enter nickname you want to register","Πληκτρολογήστε το ψευδώνυμο που θέλετε να εγγραφείτε"}. {"Enter path to backup file","Εισάγετε τοποθεσία αρχείου αντιγράφου ασφαλείας"}. {"Enter path to jabberd14 spool dir","Εισάγετε κατάλογο αρχείων σειράς jabberd14"}. {"Enter path to jabberd14 spool file","Εισάγετε τοποθεσία αρχείου σειράς jabberd14"}. {"Enter path to text file","Εισάγετε Τοποθεσία Αρχείου Κειμένου"}. {"Enter the text you see","Πληκτρολογήστε το κείμενο που βλέπετε"}. {"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Πληκτρολογήστε το όνομα χρήστη και κωδικοποιήσεις που θέλετε να χρησιμοποιήσετε για τη σύνδεση με διακομιστές IRC. Πατήστε 'Next' για να πάρετε περισσότερα πεδία να συμπληρώσετε. Πατήστε 'Complete' για να αποθηκεύσετε ρυθμίσεις."}. {"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Εισάγετε το όνομα χρήστη, κωδικοποιήσεις, τις θύρες και τους κωδικούς πρόσβασης που θέλετε να χρησιμοποιήσετε για σύνδεση με IRC διακομιστή"}. {"Erlang Jabber Server","Erlang Jabber Διακομιστής"}. {"Error","Σφάλμα"}. {"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Παράδειγμα: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Εξαγωγή δεδομένων όλων των χρηστών του διακομιστή σε PIEFXIS αρχεία (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Εξαγωγή δεδομένων των χρηστών κεντρικού υπολογιστή σε PIEFXIS αρχεία (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Απέτυχε η εξαγωγή JID από την έγκριση του αιτήματος φωνής σας"}. {"Family Name","Επώνυμο"}. {"February","Φεβρουάριος"}. {"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Συμπληρώστε τη φόρμα για να αναζητήσετε οποιαδήποτε Jabber χρήστη που ταιριάζει (Προσθέστε * στο τέλος τού πεδίου για να ταιριάξει σε μεγαλύτερες γραμματοσηρές)"}. {"Friday","Παρασκευή"}. {"From ~s","Από ~s"}. {"From","Από"}. {"Full Name","Ονοματεπώνυμο"}. {"Get Number of Online Users","Έκθεση αριθμού συνδεδεμένων χρηστών"}. {"Get Number of Registered Users","Έκθεση αριθμού εγγεγραμμένων χρηστών"}. {"Get User Last Login Time","Έκθεση Τελευταίας Ώρας Σύνδεσης Χρήστη"}. {"Get User Password","Έκθεση Κωδικού Πρόσβασης Χρήστη"}. {"Get User Statistics","Έκθεση Στατιστικών Χρήστη"}. {"Groups","Ομάδες"}. {"Group ","Ομάδα"}. {"has been banned","έχει απαγορευθεί"}. {"has been kicked because of an affiliation change","Έχει αποβληθεί λόγω αλλαγής υπαγωγής"}. {"has been kicked because of a system shutdown","αποβλήθηκε λόγω τερματισμού συστήματος"}. {"has been kicked because the room has been changed to members-only","αποβλήθηκε επειδή η αίθουσα αλλάξε γιά μέλη μόνο"}. {"has been kicked","αποβλήθηκε "}. {" has set the subject to: "," έχει θέσει το θέμα σε: "}. {"Host","Κεντρικός Υπολογιστής"}. {"If you don't see the CAPTCHA image here, visit the web page.","Εάν δεν βλέπετε την εικόνα CAPTCHA εδώ, επισκεφθείτε την ιστοσελίδα."}. {"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Εάν θέλετε να καθορίσετε διαφορετικές θύρες, κωδικούς πρόσβασης, κωδικοποιήσεις για IRC διακομιστές, εισάγετε πληροφορίες στη μορφή '{\"irc διακομιστής\", \"κωδικοποιήσεις\", θύρα, \"κωδικός πρόσβασης\"}'. Προεπιλεγμενα αυτή η υπηρεσία χρησιμοποιεί \"~s\" κωδικοποιήση, θύρα ~p, κενό κωδικό πρόσβασης."}. {"Import Directory","Εισαγωγή κατάλογου αρχείων"}. {"Import File","Εισαγωγή αρχείων"}. {"Import user data from jabberd14 spool file:","Εισαγωγή δεδομένων χρήστη από το αρχείο σειράς jabberd14:"}. {"Import User from File at ","Εισαγωγή χρηστών από αρχείο στο "}. {"Import users data from a PIEFXIS file (XEP-0227):","Εισαγωγή δεδομένων χρηστών από ένα αρχείο PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Εισαγωγή δεδομένων χρηστών από κατάλογο αρχείων σειράς jabberd14:"}. {"Import Users from Dir at ","Εισαγωγή χρηστών από κατάλογο αρχείων στο "}. {"Import Users From jabberd14 Spool Files","Εισαγωγή Χρηστών από αρχεία σειράς jabberd14"}. {"Improper message type","Ακατάλληλο είδος μηνύματος"}. {"Incorrect password","Εσφαλμένος κωδικός πρόσβασης"}. {"IP addresses","Διευθύνσεις IP"}. {"IP","IP"}. {"IRC channel (don't put the first #)","IRC κανάλι (μην τεθεί το πρώτο #)"}. {"IRC server","Διακομιστής IRC"}. {"IRC settings","IRC Ρυθμίσεις"}. {"IRC Transport","IRC Διαβιβάσεις"}. {"IRC username","IRC όνομα χρήστη"}. {"IRC Username","IRC Όνομα χρήστη"}. {"is now known as","είναι τώρα γνωστή ως"}. {"It is not allowed to send private messages of type \"groupchat\"","Δεν επιτρέπεται να στείλει προσωπικά μηνύματα του τύπου \"groupchat\""}. {"It is not allowed to send private messages to the conference","Δεν επιτρέπεται να στείλει προσωπικά μηνύματα για τη διάσκεψη"}. {"It is not allowed to send private messages","Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων"}. {"Jabber Account Registration","Εγγραφή λογαριασμού Jabber"}. {"Jabber ID","Ταυτότητα Jabber"}. {"January","Ιανουάριος"}. {"Join IRC channel","Είσοδος στο IRC κανάλι"}. {"joins the room","συνδέετε στην αίθουσα"}. {"Join the IRC channel here.","Είσοδος στο κανάλι IRC εδώ."}. {"Join the IRC channel in this Jabber ID: ~s","Είσοδος στο κανάλι IRC αυτής της Jabber Ταυτότητας: ~s"}. {"July","Ιούλιος"}. {"June","Ιούνιος"}. {"Last Activity","Τελευταία Δραστηριότητα"}. {"Last login","Τελευταία σύνδεση"}. {"Last month","Περασμένο μήνα"}. {"Last year","Πέρυσι"}. {"leaves the room","εγκαταλείπει την αίθουσα"}. {"Listened Ports at ","Παρακολουθούμενες Θύρες στο "}. {"Listened Ports","Παρακολουθούμενες Θύρες"}. {"List of modules to start","Λίστα των Module για Εκκίνηση"}. {"Low level update script","Προγράμα ενημέρωσης χαμηλού επίπεδου "}. {"Make participants list public","Κάντε κοινό τον κατάλογο συμμετεχόντων"}. {"Make room CAPTCHA protected","Κάντε την αίθουσα CAPTCHA προστατεύονομενη"}. {"Make room members-only","Κάντε την αίθουσα μόνο για μέλη"}. {"Make room moderated","Κάντε την αίθουσα εποπτεύονομενη"}. {"Make room password protected","Κάντε την αίθουσα προστατεύομενη με κωδικό πρόσβασης"}. {"Make room persistent","Κάντε αίθουσα μόνιμη"}. {"Make room public searchable","Κάντε την δημόσια αναζήτηση δυνατή για αυτή την αίθουσα"}. {"March","Μάρτιος"}. {"Maximum Number of Occupants","Μέγιστος αριθμός συμετεχόντων"}. {"May","Μάιος"}. {"Membership is required to enter this room","Απαιτείται αίτηση συμετοχής για είσοδο σε αυτή την αίθουσα"}. {"Members:","Μέλη:"}. {"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Απομνημονεύστε τον κωδικό πρόσβασής σας, ή γράψετε τον σε ένα χαρτί που είχε τοποθετηθεί σε ασφαλές μέρος. Στο Jabber δεν υπάρχει αυτοματοποιημένος τρόπος για να ανακτήσετε τον κωδικό σας αν τον ξεχάσετε."}. {"Memory","Μνήμη"}. {"Message body","Περιεχόμενο μηνυμάτως"}. {"Middle Name","Πατρώνυμο"}. {"Moderator privileges required","Aπαιτούνται προνόμια συντονιστή"}. {"Modified modules","Τροποποιημένα modules"}. {"Module","Module"}. {"Modules","Modules"}. {"Monday","Δευτέρα"}. {"Name:","Όνομα:"}. {"Name","Όνομα"}. {"Never","Ποτέ"}. {"New Password:","Νέος κωδικός πρόσβασης:"}. {"Nickname Registration at ","Εγγραφή με Ψευδώνυμο στο "}. {"Nickname ~s does not exist in the room","Ψευδώνυμο ~s δεν υπάρχει σε αυτή την αίθουσα"}. {"Nickname","Ψευδώνυμο"}. {"No body provided for announce message","Δεν προμηθεύτικε περιεχόμενο ανακοινώσης"}. {"No Data","Κανένα στοιχείο"}. {"Node not found","Κόμβος δεν βρέθηκε"}. {"Nodes","Κόμβοι"}. {"None","Κανένα"}. {"Not Found","Δεν Βρέθηκε"}. {"November","Νοέμβριος"}. {"Number of online users","Αριθμός συνδεδεμένων χρηστών"}. {"Number of registered users","Αριθμός εγγεγραμμένων χρηστών"}. {"October","Οκτώβριος"}. {"Offline Messages:","Χωρίς Σύνδεση Μηνύματα:"}. {"Offline Messages","Χωρίς Σύνδεση Μηνύματα"}. {"OK","Όλλα Καλά"}. {"Old Password:","Παλαιός κωδικός πρόσβασης:"}. {"Online Users:","Online Χρήστες:"}. {"Online Users","Συνδεμένοι χρήστες"}. {"Online","Συνδεδεμένο"}. {"Only moderators and participants are allowed to change the subject in this room","Μόνο οι συντονιστές και οι συμμετέχοντες μπορούν να αλλάξουν το θέμα αυτής της αίθουσας"}. {"Only moderators are allowed to change the subject in this room","Μόνο οι συντονιστές μπορούν να αλλάξουν το θέμα αυτής της αίθουσας"}. {"Only moderators can approve voice requests","Μόνο οι συντονιστές μπορούν να εγκρίνουν τις αιτήσεις φωνής"}. {"Only occupants are allowed to send messages to the conference","Μόνο οι συμμετέχωντες μπορούν να στέλνουν μηνύματα στο συνέδριο"}. {"Only occupants are allowed to send queries to the conference","Μόνο οι συμετεχόντες μπορούν να στείλουν ερωτήματα στη διάσκεψη"}. {"Only service administrators are allowed to send service messages","Μόνο οι διαχειριστές των υπηρεσιών επιτρέπεται να στείλουν υπηρεσιακά μηνύματα"}. {"Options","Επιλογές"}. {"Organization Name","Όνομα Οργανισμού"}. {"Organization Unit","Μονάδα Οργανισμού"}. {"Outgoing s2s Connections:","Εξερχόμενες S2S Συνδέσεις:"}. {"Outgoing s2s Connections","Εξερχόμενες S2S Συνδέσεις"}. {"Owner privileges required","Aπαιτούνται προνόμια ιδιοκτήτη"}. {"Packet","Πακέτο"}. {"Password ~b","Κωδικός πρόσβασης ~b"}. {"Password Verification:","Επαλήθευση κωδικού πρόσβασης:"}. {"Password Verification","Επαλήθευση κωδικού πρόσβασης"}. {"Password","Κωδικός Πρόσβασης"}. {"Password:","Κωδικός πρόσβασης:"}. {"Path to Dir","Τοποθεσία κατάλογου αρχείων"}. {"Path to File","Τοποθεσία Αρχείου"}. {"Pending","Εκκρεμεί"}. {"Period: ","Περίοδος: "}. {"Ping","Πινγκ"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Παρακαλώ σημειώστε ότι οι επιλογές αυτές θα αποθήκευσουν Αντιγράφο Ασφαλείας μόνο της ενσωματωμένης βάσης δεδομένων Mnesia. Εάν χρησιμοποιείτε το module ODBC, θα πρέπει επίσης να κάνετε χωριστά Αντιγράφο Ασφαλείας της SQL βάση δεδομένων σας ."}. {"Please, wait for a while before sending new voice request","Παρακαλώ, περιμένετε για λίγο πριν την αποστολή νέου αιτήματος φωνής"}. {"Pong","Πονγκ"}. {"Port ~b","Θύρα ~b"}. {"Port","Θύρα"}. {"private, ","ιδιωτικό,"}. {"Protocol","Πρωτόκολλο"}. {"Publish-Subscribe","Δημοσίευση-Εγγραφή"}. {"PubSub subscriber request","Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής"}. {"Queries to the conference members are not allowed in this room","Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα"}. {"RAM and disc copy","Αντίγραφο μόνο σε RAM καί δίσκο"}. {"RAM copy","Αντίγραφο σε RAM"}. {"Raw","Ακατέργαστο"}. {"Really delete message of the day?","Πραγματικά να διαγράψετε το μήνυμα της ημέρας;"}. {"Recipient is not in the conference room","Παραλήπτης δεν είναι στην αίθουσα συνεδριάσεων"}. {"Register a Jabber account","Καταχωρήστε έναν λογαριασμό Jabber"}. {"Registered Users:","Εγγεγραμμένοι Χρήστες:"}. {"Registered Users","Εγγεγραμμένοι Χρήστες"}. {"Register","Καταχωρήστε"}. {"Registration in mod_irc for ","Εγγραφή στο mod_irc για "}. {"Remote copy","Απομεμακρυσμένο αντίγραφο"}. {"Remove All Offline Messages","Αφαίρεση Όλων των Χωρίς Σύνδεση Μηνύματων"}. {"Remove User","Αφαίρεση χρήστη"}. {"Remove","Αφαίρεστε"}. {"Replaced by new connection","Αντικαταστάθικε από νέα σύνδεση"}. {"Resources","Πόροι"}. {"Restart Service","Επανεκκίνηση Υπηρεσίας"}. {"Restart","Επανεκκίνηση"}. {"Restore Backup from File at ","Επαναφορά Αντιγράφου Ασφαλείας από αρχείο στο "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Επαναφορά δυαδικού αντιγράφου ασφαλείας μετά την επόμενη επανεκκίνηση του ejabberd (απαιτεί λιγότερη μνήμη):"}. {"Restore binary backup immediately:","Επαναφορά δυαδικού αντιγράφου ασφαλείας αμέσως:"}. {"Restore plain text backup immediately:","Επαναφορά αντιγράφου ασφαλείας από αρχείο κειμένου αμέσως:"}. {"Restore","Επαναφορά Αντιγράφου Ασφαλείας"}. {"Room Configuration","Διαμόρφωση Αίθουσας σύνεδριασης"}. {"Room creation is denied by service policy","Άρνηση δημιουργίας αίθουσας , λόγω τακτικής παροχής υπηρεσιών"}. {"Room description","Περιγραφή Αίθουσας"}. {"Room Occupants","Συμετεχόντες Αίθουσας σύνεδριασης"}. {"Room title","Τίτλος Αίθουσας "}. {"Roster of ","Καταλόγος Επαφών τού"}. {"Roster size","Μέγεθος Καταλόγου Επαφών"}. {"Roster","Καταλόγος Επαφών"}. {"RPC Call Error","Σφάλμα RPC Κλήσης"}. {"Running Nodes","Ενεργοί Κόμβοι"}. {"~s access rule configuration","~s διαμόρφωση κανόνα πρόσβασης"}. {"Saturday","Σάββατο"}. {"Script check","Script ελέγχου"}. {"Search Results for ","Αποτελέσματα αναζήτησης για "}. {"Search users in ","Αναζήτηση χρηστών στο"}. {"Send announcement to all online users on all hosts","Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Send announcement to all online users","Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες"}. {"Send announcement to all users on all hosts","Αποστολή ανακοίνωσης σε όλους τους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Send announcement to all users","Αποστολή ανακοίνωσης σε όλους τους χρήστες"}. {"September","Σεπτέμβριος"}. {"Server ~b","Διακομιστής ~b"}. {"Server:","Διακομιστής:"}. {"Set message of the day and send to online users","Ορίστε μήνυμα ημέρας και αποστολή στους συνδεδεμένους χρήστες"}. {"Set message of the day on all hosts and send to online users","Ορίστε μήνυμα ημέρας και άμεση αποστολή στους συνδεδεμένους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Shared Roster Groups","Κοινές Ομάδες Καταλόγων Επαφών"}. {"Show Integral Table","Δείτε Ολοκληρωτικό Πίνακα"}. {"Show Ordinary Table","Δείτε Κοινό Πίνακα"}. {"Shut Down Service","Κλείσιμο Υπηρεσίας"}. {"~s invites you to the room ~s","~s σας προσκαλεί στην αίθουσα ~s"}. {"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Μερικοί πελάτες Jabber μπορεί να αποθηκεύσουν τον κωδικό πρόσβασής σας στον υπολογιστή σας. Χρησιμοποιήστε αυτό το χαρακτηριστικό μόνο εάν εμπιστεύεστε την ασφάλεια του υπολογιστή σας."}. {"~s's Offline Messages Queue","Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s"}. {"Start Modules at ","Εκκίνηση Modules στο "}. {"Start Modules","Εκκίνηση Modules"}. {"Start","Εκκίνηση"}. {"Statistics of ~p","Στατιστικές του ~p"}. {"Statistics","Στατιστικές"}. {"Stop Modules at ","Παύση Modules στο "}. {"Stop Modules","ΠαύσηModules"}. {"Stopped Nodes","Σταματημένοι Κόμβοι"}. {"Stop","Σταμάτημα"}. {"Storage Type","Τύπος Αποθήκευσης"}. {"Store binary backup:","Αποθηκεύση δυαδικού αντιγράφου ασφαλείας:"}. {"Store plain text backup:","Αποθηκεύση αντιγράφου ασφαλείας σε αρχείο κειμένου:"}. {"Subject","Θέμα"}. {"Submitted","Υποβλήθηκε"}. {"Submit","Υποβοβολή"}. {"Subscription","Συνδρομή"}. {"Sunday","Κυριακή"}. {"That nickname is already in use by another occupant","Αυτό το ψευδώνυμο είναι ήδη σε χρήση από άλλον συμμετέχων"}. {"That nickname is registered by another person","Αυτό το ψευδώνυμο είναι καταχωρημένο από άλλο πρόσωπο"}. {"The CAPTCHA is valid.","Το CAPTCHA είναι έγκυρο."}. {"The CAPTCHA verification has failed","Η επαλήθευση της εικόνας CAPTCHA απέτυχε"}. {"The password is too weak","Ο κωδικός πρόσβασης είναι πολύ ασθενές"}. {"the password is","ο κωδικός πρόσβασης είναι"}. {"The password of your Jabber account was successfully changed.","Ο κωδικός πρόσβασης του Jabber λογαριασμού σας έχει αλλάξει επιτυχώς."}. {"There was an error changing the password: ","Υπήρξε ένα σφάλμα κατά την αλλαγή του κωδικού πρόσβασης:"}. {"There was an error creating the account: ","Υπήρξε ένα σφάλμα κατά τη δημιουργία του λογαριασμού:"}. {"There was an error deleting the account: ","Υπήρξε ένα σφάλμα κατά τη διαγραφή του λογαριασμού:"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Ανεξαρτήτως με πεζά ή κεφαλαία: 'μιαλεξη' είναι το ίδιο με 'ΜιαΛέξη' και 'Μιαλέξη'."}. {"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Αυτή η σελίδα σας επιτρέπει να δημιουργήσετε ένα λογαριασμό Jabber σε αυτόν το διακομιστή Jabber. JID σας (Jabber Identifier) θα είναι της μορφής: όνομα_χρήστη@διακομιστής_Jabber. Παρακαλώ διαβάστε προσεκτικά τις οδηγίες για να συμπληρώσετε σωστά τα πεδία."}. {"This page allows to unregister a Jabber account in this Jabber server.","Η σελίδα αυτή δίνει τη δυνατότητα να καταργήσετε την καταχώρηση ενός λογαριασμό Jabber σε αυτόν το διακομιστή Jabber."}. {"This room is not anonymous","Η αίθουσα αυτή δεν είναι ανώνυμη"}. {"Thursday","Πέμπτη"}. {"Time delay","Χρόνος καθυστέρησης"}. {"Time","Χρόνος"}. {"Too many CAPTCHA requests","Πάρα πολλά αιτήματα CAPTCHA"}. {"To ~s","Πρώς ~s"}. {"To","Πρώς"}. {"Traffic rate limit is exceeded","Υπέρφορτωση"}. {"Transactions Aborted:","Αποτυχημένες συναλλαγές:"}. {"Transactions Committed:","Παραδοθείς συναλλαγές:"}. {"Transactions Logged:","Καταγραμμένες συναλλαγές:"}. {"Transactions Restarted:","Επανειλημμένες συναλλαγές:"}. {"Tuesday","Τρίτη"}. {"Unable to generate a CAPTCHA","Αδήνατο να δημιουργηθεί CAPTCHA"}. {"Unauthorized","Χορίς Εξουσιοδότηση"}. {"Unregister a Jabber account","Καταργήστε την εγγραφή ενός λογαριασμού Jabber"}. {"Unregister","Καταργήση εγγραφής"}. {"Update message of the day (don't send)","Ενημέρωση μηνύματως ημέρας (χωρίς άμεση αποστολή)"}. {"Update message of the day on all hosts (don't send)","Ενημέρωση μηνύματως ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση αποστολή)"}. {"Update plan","Σχέδιο ενημέρωσης"}. {"Update script","Προγράμα ενημέρωσης"}. {"Update","Ενημέρωση"}. {"Uptime:","Uptime:"}. {"Use of STARTTLS required","Απαιτείται χρήση STARTTLS "}. {"User Management","Διαχείριση χρηστών"}. {"Username:","Όνομα χρήστη"}. {"Users are not allowed to register accounts so quickly","Οι χρήστες δεν επιτρέπεται να εγγραφούν λογαριασμούς τόσο γρήγορα"}. {"Users Last Activity","Τελευταία Δραστηριότητα Χρήστη"}. {"Users","Χρήστες"}. {"User","Χρήστης"}. {"Validate","Επαληθεύστε"}. {"vCard User Search","vCard Αναζήτηση χρηστών"}. {"Virtual Hosts","εικονικοί κεντρικοί υπολογιστές"}. {"Visitors are not allowed to change their nicknames in this room","Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την αίθουσα"}. {"Visitors are not allowed to send messages to all occupants","Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους συμμετέχωντες"}. {"Voice requests are disabled in this conference","Τα αιτήματα φωνής είναι απενεργοποιημένα, σε αυτό το συνέδριο"}. {"Voice request","Αίτημα φωνής"}. {"Wednesday","Τετάρτη"}. {"You can later change your password using a Jabber client.","Μπορείτε αργότερα να αλλάξετε τον κωδικό πρόσβασής σας χρησιμοποιώντας έναν πελάτη Jabber."}. {"You have been banned from this room","Σας έχει απαγορευθεί η είσοδος σε αυτή την αίθουσα"}. {"You must fill in field \"Nickname\" in the form","Θα πρέπει να συμπληρώσετε το πεδίο \"Ψευδώνυμο\" στη φόρμα"}. {"You need a client that supports x:data and CAPTCHA to register","Χρειάζεστε ένα x:data και CAPTCHA ικανό πελάτη για εγγραφή"}. {"You need a client that supports x:data to register the nickname","Χρειάζεστε ένα x:data ικανό πελάτη για εγγραφή με ψευδώνυμο"}. {"You need an x:data capable client to configure mod_irc settings","Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε το mod_irc"}. {"You need an x:data capable client to search","Χρειάζεστε ένα x:data ικανό πελάτη για αναζήτηση"}. {"Your active privacy list has denied the routing of this stanza.","Ο ενεργός κατάλογος απορρήτου, έχει αρνηθεί τη δρομολόγηση αυτής της στροφής (stanza)."}. {"Your contact offline message queue is full. The message has been discarded.","Η μνήμη χωρίς σύνδεση μήνυματών είναι πλήρης. Το μήνυμα έχει απορριφθεί."}. {"Your Jabber account was successfully created.","Ο Jabber λογαριασμός σας δημιουργήθηκε με επιτυχία."}. {"Your Jabber account was successfully deleted.","Ο Jabber λογαριασμός σας διαγράφηκε με επιτυχία."}. {"Your messages to ~s are being blocked. To unblock them, visit ~s","Τα μηνύματά σας πρως ~s είναι αποκλεισμένα. Για αποδεσμεύση, επισκεφθείτε ~s"}. ejabberd-18.01/priv/msgs/pt-br.po0000644000232200023220000017307113225664356017171 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "Last-Translator: Victor Rodrigues\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Portuguese (Brazil)\n" "X-Additional-Translator: Otávio Fernandes\n" "X-Additional-Translator: Renato Botelho\n" "X-Additional-Translator: Lucius Curado\n" "X-Additional-Translator: Felipe Brito Vasconcellos\n" "X-Additional-Translator: Victor Hugo dos Santos\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " mudou o assunto para: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Se necessita senha para entrar nesta sala" #: ejabberd_oauth:448 msgid "Accept" msgstr "Aceito" #: mod_configure:1109 msgid "Access Configuration" msgstr "Configuração de Acesso" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Configuração da Lista de Controle de Acesso" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Listas de Controle de Acesso" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Regras de Acesso" #: mod_configure:1095 msgid "Access control lists" msgstr "Listas de Controle de Acesso" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Acesso negado pela política do serviço" #: mod_configure:1113 msgid "Access rules" msgstr "Regras de acesso" #: mod_configure:1772 msgid "Action on user" msgstr "Ação no usuário" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Adicionar ID jabber" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Adicionar novo" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Adicionar usuário" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administração" #: mod_configure:1767 msgid "Administration of " msgstr "Administração de " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Se necessita privilégios de administrador" #: mod_configure:507 msgid "All Users" msgstr "Todos os usuários" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Todas atividades" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Permitir a usuários modificar o assunto" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Permitir a usuários pesquisar informações sobre os demais" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Permitir a usuários envio de convites" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Permitir a usuários enviarem mensagens privadas" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Permitir mudança de apelido aos visitantes" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Permitir visitantes enviar mensagem privada para" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Permitir atualizações de status aos visitantes" #: mod_announce:611 msgid "Announcements" msgstr "Anúncios" #: mod_muc_log:476 msgid "April" msgstr "Abril" #: mod_muc_log:480 msgid "August" msgstr "Agosto" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Salvar cópia de segurança" #: mod_configure:584 msgid "Backup Management" msgstr "Gestão de Backup" #: ejabberd_web_admin:1979 msgid "Backup of ~p" msgstr "Backup de ~p" #: mod_configure:948 msgid "Backup to File at " msgstr "Salvar backup para arquivo em " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Formato incorreto" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Aniversário" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "CAPTCHA web page" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Tempo de CPU" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Mudar senha" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Alterar Senha do Usuário" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Caracteres não aceitos:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Caracteres não aceitos:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Caracteres não aceitos:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Configuração da sala de bate-papo modificada" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "A sala de chat está criada" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "A sala de chat está destruída" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "A sala de chat está iniciada" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "A sala de chat está parada" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Salas de Chat" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Escolha um nome de usuário e senha para registrar-se neste servidor" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Selecione módulos a parar" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Selecione o tipo de armazenamento das tabelas" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Aprovar esta assinatura." #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Cidade" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Comandos" #: mod_muc:461 msgid "Conference room does not exist" msgstr "A sala de conferência não existe" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Configuração" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Configuração para ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Recursos conectados:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parâmetros para as Conexões" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "País" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Base de dados" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Configuração de Tabelas de Base de dados em " #: ejabberd_web_admin:1941 msgid "Database Tables at ~p" msgstr "Tabelas da Base de dados em ~p" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Base de dados" #: mod_muc_log:484 msgid "December" msgstr "Dezembro" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Usuários padrões como participantes" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Remover os selecionados" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Deletar Usuário" #: mod_announce:629 msgid "Delete message of the day" msgstr "Apagar mensagem do dia" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Apagar a mensagem do dia em todos os hosts" #: mod_shared_roster:900 msgid "Description:" msgstr "Descrição:" #: mod_configure:889 msgid "Disc only copy" msgstr "Somente cópia em disco" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Grupos Exibidos:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" "Não revele a sua senha a ninguém, nem mesmo para o administrador deste " "servidor Jabber." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Exportar backup para texto em " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Exportar para arquivo texto" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Editar propriedades" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Você deve aprovar/desaprovar a requisição de voz." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Elementos" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "Email" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "a senha é" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Permitir criação de logs" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Codificação para o servidor ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Terminar Sessão do Usuário" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Introduza lista de {módulo, [opções]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Introduza o apelido que quer registrar" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Introduza o caminho do arquivo de backup" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Introduza o caminho para o diretório de fila do jabberd14" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Insira o caminho para a fila (arquivo) do jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Introduza caminho para o arquivo texto" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Insira o texto que você vê" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Insira o nome de usuário e codificações que você deseja usar para conectar-" "se aos servidores de IRC. Depois, presione 'Next' ('Próximo') para exibir " "mais campos que devem ser preenchidos. Ao final, pressione " "'Complete' ('Completar') para salvar a configuração." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Insira o nome de usuário, codificações, portas e senhas que você deseja para " "usar nos servidores IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Servidor Jabber em Erlang" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Erro" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Exemplo: [{\"irc.teste.net\", \"koi8-r\"}, 6667, \"senha\"}, {\"dominio.foo." "net\", \"iso8859-1\", 7000}, {\"irc.servidordeteste.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "Exportar todas as tabelas como SQL para um arquivo:" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exportar todos os dados de todos os usuários no servidor, para arquivos " "formato PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exportar dados dos usuários em um host, para arquivos PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Não foi possível extrair o JID (Jabber ID) da requisição de voz" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Sobrenome" #: mod_muc_log:474 msgid "February" msgstr "Fevereiro" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Preencha o formulário para buscar usuários Jabber. Agrega * ao final de um " "campo para buscar sub-palavras." #: mod_muc_log:467 msgid "Friday" msgstr "Sexta" #: mod_offline:691 msgid "From" msgstr "De" #: mod_configure:723 msgid "From ~s" msgstr "De ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Nome completo" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Obter Número de Usuários Online" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Obter Número de Usuários Registrados" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Obter a Data do Último Login" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Obter Senha do Usuário" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Obter Estatísticas do Usuário" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Nome do meio" #: mod_shared_roster:923 msgid "Group " msgstr "Grupo " #: mod_roster:916 msgid "Groups" msgstr "Grupos" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Máquina" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "Endereços IP" #: mod_irc:439 msgid "IRC Transport" msgstr "Transporte IRC" #: mod_irc:496 msgid "IRC Username" msgstr "Usuário IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "Canal IRC (não coloque o #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Nó não encontrado" #: mod_irc:673 msgid "IRC server" msgstr "Servidor IRC" #: mod_irc:756 msgid "IRC settings" msgstr "Configurações do IRC" #: mod_irc:766 msgid "IRC username" msgstr "Usuário IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Se você não conseguir ver o CAPTCHA aqui, visite a web page." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Se você deseja especificar portas diferentes, senhas ou codifações para " "servidores de IRC, complete esta lista com os valores no formato: " "'{\"servidor IRC\", \"codificação\", porta, \"senha\"}'. Por padrão, este " "serviço usa a codificação \"~s\", porta \"~p\", e senha em branco (vazia)" #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Importar diretório" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Importar arquivo" #: mod_configure:982 msgid "Import User from File at " msgstr "Importar usuário a partir do arquivo em " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importar usuários de arquivos jabberd14 (spool files)" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importar usuários a partir do diretório em " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importar dados dos usuários de uma fila jabberd14:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importar usuários de um arquivo PIEFXIS (XEP-0227): " #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importar dados dos usuários de um diretório-fila jabberd14:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Tipo de mensagem incorreto" #: ejabberd_web_admin:1586 msgid "Incoming s2s Connections:" msgstr "Conexões que entram de s2s" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Senha incorreta" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Senha incorreta" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Requisições de voz estão desabilitadas nesta conferência" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" "Não é permitido o envio de mensagens de erro a esta sala. O membro (~s) " "enviou uma mensagem de erro (~s) e foi desconectado (\"kicked\")." #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Não é permitido enviar mensagens privadas" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Não é permitido enviar mensagens privadas do tipo \"groupchat\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Impedir o envio de mensagens privadas para a sala" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Registros de Contas Jabber" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "ID Jabber" #: mod_muc_log:473 msgid "January" msgstr "Janeiro" #: mod_irc:665 msgid "Join IRC channel" msgstr "Juntar-se ao canal IRC" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Aqui! Juntar-se ao canal IRC." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Entrar no canal IRC, neste ID Jabber: ~s" #: mod_muc_log:479 msgid "July" msgstr "Julho" #: mod_muc_log:478 msgid "June" msgstr "Junho" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Última atividade" #: mod_configure:1646 msgid "Last login" msgstr "Último login" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Último mês" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Último ano" #: mod_configure:941 msgid "List of modules to start" msgstr "Listas de módulos para inicializar" #: mod_muc_admin:373 msgid "List of rooms" msgstr "Lista de salas" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Portas abertas" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Portas abertas em " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Script de atualização low level" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Tornar pública a lista de participantes" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Tornar protegida a senha da sala" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Tornar sala apenas para membros" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Tornar a sala moderada" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Tornar sala protegida à senha" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Tornar sala persistente" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Tornar sala pública possível de ser encontrada" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "Usuário IRC" #: mod_muc_log:475 msgid "March" msgstr "Março" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Número máximo de participantes" #: mod_muc_log:477 msgid "May" msgstr "Maio" #: mod_shared_roster:907 msgid "Members:" msgstr "Membros:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Necessitas ser membro desta sala para poder entrar" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Memorize a sua senha, ou escreva-a em um papel e guarde-o em um lugar " "seguro. Jabber não é uma maneira automatizada para recuperar a sua senha, se " "você a esquecer eventualmente." #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Memória" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Corpo da mensagem" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Nome do meio" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Se necessita privilégios de moderador" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Módulos atualizados" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Módulo" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Módulos" #: ejabberd_web_admin:2168 msgid "Modules at ~p" msgstr "Módulos em ~p" #: mod_muc_log:463 msgid "Monday" msgstr "Segunda" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "Chat multi-usuário" #: mod_multicast:267 msgid "Multicast" msgstr "Multicast" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Nome" #: mod_shared_roster:896 msgid "Name:" msgstr "Nome:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nunca" #: mod_register_web:377 msgid "New Password:" msgstr "Nova Senha:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Apelido" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registro do apelido em " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "O apelido ~s não existe na sala" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Nó não encontrado" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Nenhum dado" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Nenhum corpo de texto fornecido para anunciar mensagem" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Nó não encontrado" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Nó não encontrado" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Nó não encontrado" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Nó não encontrado" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 msgid "Node ~p" msgstr "Nó ~p" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Nós" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nenhum" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Não encontrado" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "Novembro" #: mod_configure:1212 msgid "Number of online users" msgstr "Número de usuários online" #: mod_configure:1202 msgid "Number of registered users" msgstr "Número de usuários registrados" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Outubro" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Mensagens offline" #: mod_offline:761 msgid "Offline Messages:" msgstr "Mensagens offline" #: mod_register_web:373 msgid "Old Password:" msgstr "Senha Antiga:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Conectado" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Usuários conectados" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Usuários online" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 msgid "Only members may query archives of this room" msgstr "Somente os membros podem procurar nos arquivos desta sala" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "" "Somente os moderadores e os participamentes podem alterar o assunto desta " "sala" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Somente os moderadores podem alterar o assunto desta sala" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Somente moderadores podem aprovar requisições de voz" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Somente os ocupantes podem enviar mensagens à sala" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Somente os ocupantes podem enviar consultas à sala" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "" "Apenas administradores possuem permissão para enviar mensagens de serviço" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Opções" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Nome da organização" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Departamento/Unidade" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Conexões que partam de s2s" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Conexões que partem de s2s" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Se requer privilégios de proprietário da sala" #: mod_offline:693 msgid "Packet" msgstr "Pacote" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Senha" #: mod_configure:1130 msgid "Password Verification" msgstr "Verificação de Senha" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Verificação de Senha" #: mod_irc:822 msgid "Password ~b" msgstr "Senha ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Senha:" #: mod_configure:998 msgid "Path to Dir" msgstr "Caminho para o diretório" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Caminho do arquivo" #: mod_roster:915 msgid "Pending" msgstr "Pendente" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Período: " #: mod_muc_admin:369 msgid "Permanent rooms" msgstr "Salas permanentes" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Observe que tais opções farão backup apenas da base de dados Mnesia. Caso " "você esteja utilizando o modulo ODBC, você precisará fazer backup de sua " "base de dados SQL separadamente." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Por favor, espere antes de enviar uma nova requisição de voz" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Porta" #: mod_irc:828 msgid "Port ~b" msgstr "Porta ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Porta" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "PubSub requisição de assinante" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publicação de Tópico" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Nesta sala não se permite consultas aos membros da sala" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Cópias na RAM e disco rígido" #: mod_configure:889 msgid "RAM copy" msgstr "Cópia em RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Erro de chamada RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Intocado" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Deletar realmente a mensagem do dia?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "O receptor não está na sala de conferência" #: mod_register_web:275 msgid "Register" msgstr "Registrar" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Registrar uma conta Jabber" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Usuários Registrados" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Usuários registrados" #: mod_muc_admin:370 msgid "Registered nicknames" msgstr "Usuários registrados" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registro em mod_irc para " #: mod_configure:889 msgid "Remote copy" msgstr "Cópia remota" #: mod_roster:963 msgid "Remove" msgstr "Remover" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Remover Todas as Mensagens Offline" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Remover usuário" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Substituído por nova conexão" #: mod_configure:1675 msgid "Resources" msgstr "Recursos" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Reiniciar" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Reiniciar Serviço" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Restaurar" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Restaurar backup a partir do arquivo em " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Restaurar backup binário após reinicialização do ejabberd (requer menos " "memória):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Restaurar backup binário imediatamente" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Restaurar backup formato texto imediatamente:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Configuração de salas" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Número de participantes" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Sala não pode ser criada devido à política do serviço" #: mod_muc_log:1064 msgid "Room description" msgstr "Descrição da Sala" #: mod_muc_log:1027 msgid "Room title" msgstr "Título da sala" #: mod_roster:1082 msgid "Roster" msgstr "Lista de contatos" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Lista de contatos de " #: mod_configure:1671 msgid "Roster size" msgstr "Tamanho da Lista" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Nós em execução" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Sábado" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "O CAPTCHA é inválido." #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Verificação de Script" #: mod_vcard:453 msgid "Search Results for " msgstr "Resultados de pesquisa para " #: mod_vcard:427 msgid "Search users in " msgstr "Procurar usuários em " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Enviar anúncio a todos os usuárions online" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Enviar anúncio a todos usuários online em todas as máquinas" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Enviar anúncio a todos os usuários" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Enviar aviso para todos os usuários em todos os hosts" #: mod_muc_log:481 msgid "September" msgstr "Setembro" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Servidor ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 msgid "Server:" msgstr "Servidor:" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Definir mensagem do dia e enviar a todos usuários online" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Definir mensagem do dia em todos os hosts e enviar para os usuários online" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Grupos Shared Roster" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Mostrar Tabela Integral" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Mostrar Tabela Ordinária" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Parar Serviço" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Alguns clientes jabber podem salvar a sua senha no seu computador. Use o " "recurso somente se você considera este computador seguro o suficiente." #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Iniciar" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Iniciar módulos" #: mod_configure:936 msgid "Start Modules at " msgstr "Iniciar módulos em " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Estatísticas" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Estatísticas de ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Parar" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Parar módulos" #: mod_configure:918 msgid "Stop Modules at " msgstr "Parar módulos em " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Nós parados" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Tipo de armazenamento" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Armazenar backup binário:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Armazenar backup em texto:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Assunto" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Enviar" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Submetido" #: mod_roster:914 msgid "Subscription" msgstr "Subscrição" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Domingo" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "O apelido (nick) já está sendo utilizado" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "O apelido já está registrado por outra pessoa" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "O CAPTCHA é inválido." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "A verificação do CAPTCHA falhou" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "Senha considerada fraca'" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "A senha da sua conta Jabber foi mudada com sucesso." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Houve um erro ao mudar a senha: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Houve um erro ao criar esta conta: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Houve um erro ao deletar esta conta: " #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Não é 'case insensitive': macbeth é o mesmo que MacBeth e ainda Macbeth. " #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Esta pagina aceita criações de novas contas Jabber neste servidor. O seu JID " "(Identificador Jabber) será da seguinte forma: 'usuário@servidor'. Por " "favor, leia cuidadosamente as instruções para preencher corretamente os " "campos." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "Esta página aceita para deletar uma conta Jabber neste servidor." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Essa sala não é anônima" #: mod_muc_log:466 msgid "Thursday" msgstr "Quinta" #: mod_offline:690 msgid "Time" msgstr "Tempo" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Intervalo (Tempo)" #: mod_offline:692 msgid "To" msgstr "Para" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Para ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Número excessivo de requisições para o CAPTCHA" #: mod_proxy65_service:223 #, fuzzy msgid "Too many active bytestreams" msgstr "número excessivo de instâncias sem confirmação" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "número excessivo de instâncias sem confirmação" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Requisições de voz estão desabilitadas nesta conferência" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 msgid "Total rooms" msgstr "Salas no total" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Limite de banda excedido" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transações abortadas:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transações salvas:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transações de log:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transações reiniciadas:" #: mod_muc_log:464 msgid "Tuesday" msgstr "Terça" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Impossível gerar um CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Não Autorizado" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Deletar registro" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Deletar conta Jabber" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Atualizar" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Atualizar mensagem do dia (não enviar)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Atualizar a mensagem do dia em todos os host (não enviar)" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Plano de Atualização" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Script de atualização" #: ejabberd_web_admin:2267 msgid "Update ~p" msgstr "Atualizar ~p" #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Uptime:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "É obrigatório uso de STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "É obrigatório uso de STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Usuário" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Gerenciamento de Usuários" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Nó não encontrado" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 msgid "User ~s" msgstr "Usuário ~s" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "Usuário:" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Usuários" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Últimas atividades dos usuários" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Usuários não estão autorizados a registrar contas imediatamente" #: mod_roster:954 msgid "Validate" msgstr "Validar" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Hosts virtuais" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "Nesta sala, os visitantes não podem mudar seus apelidos" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "Os visitantes não podem enviar mensagens a todos os ocupantes" #: mod_muc_room:3879 msgid "Voice request" msgstr "Requisição de voz" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Requisições de voz estão desabilitadas nesta conferência" #: mod_muc_log:465 msgid "Wednesday" msgstr "Quarta" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Mais tarde você pode alterar a sua senha usando um cliente Jabber." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Você foi banido desta sala" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Você deve completar o campo \"Apelido\" no formulário" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "" "Você precisa de um cliente com suporte de x:data para poder registrar o " "apelido" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "" "Você precisa de um cliente com suporte a x:data para registrar o seu apelido" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "" "Necessitas um cliente com suporte de x:data para configurar as opções de " "mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Necessitas um cliente com suporte de x:data para poder buscar" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Não é permitido enviar mensagens privadas" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Sua conta jabber foi criada com sucesso." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Sua conta Jabber foi deletada com sucesso." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Sua lista de privacidade ativa negou o roteamento desta instância." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "Sua fila de mensagens offline esta cheia. Sua mensagem foi descartada" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" "Suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "Módulo de IRC para ejabberd" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "Módulo de MUC para ejabberd" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "ejabberd Multicast service" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "Módulo para Publicar Tópicos do ejabberd" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "Modulo ejabberd SOCKS5 Bytestreams" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "Módulo vCard para ejabberd" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "foi banido" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "foi removido" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "foi desconectado porque o sistema foi desligado" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "foi desconectado porque por afiliação inválida" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" "foi desconectado porque a política da sala mudou, só membros são permitidos" #: mod_muc_log:410 msgid "is now known as" msgstr "é agora conhecido como" #: mod_muc_log:370 msgid "joins the room" msgstr "Entrar na sala" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "Sair da sala" #: mod_muc_room:3856 msgid "private, " msgstr "privado, " #: mod_muc_room:3955 msgid "the password is" msgstr "a senha é" #: mod_vcard:292 msgid "vCard User Search" msgstr "Busca de Usuário vCard" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "Configuração da Regra de Acesso ~s" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s convidou você para a sala ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s's Fila de Mensagens Offline" #~ msgid "No resource provided" #~ msgstr "Nenhum recurso foi informado" #~ msgid "Server" #~ msgstr "Servidor" #~ msgid "" #~ "Too many (~p) failed authentications from this IP address (~s). The " #~ "address will be unblocked at ~s UTC" #~ msgstr "" #~ "Número excessivo (~p) de tentativas falhas de autenticação (~s). O " #~ "endereço será desbloqueado às ~s UTC" #~ msgid "Please specify file size." #~ msgstr "Por favor informe o tamanho do arquivo." #~ msgid "Please specify file name." #~ msgstr "Por favor informe o nome do arquivo." #~ msgid "This IP address is blacklisted in ~s" #~ msgstr "Este endereço IP está bloqueado em ~s" #~ msgid "Empty Rooms" #~ msgstr "Salas vazias" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "O Jabber ID ~s não es válido" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Afiliação não válida: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Cargo (role) é não válido: ~s" #~ msgid "No limit" #~ msgstr "Ilimitado" #~ msgid "Present real Jabber IDs to" #~ msgstr "Tornar o Jabber ID real visível por" #~ msgid "moderators only" #~ msgstr "apenas moderadores" #~ msgid "anyone" #~ msgstr "qualquer um" #~ msgid "Roles for which Presence is Broadcasted" #~ msgstr "Para quem a presença será notificada" #~ msgid "Moderator" #~ msgstr "Moderador" #~ msgid "Participant" #~ msgstr "Participante" #~ msgid "Visitor" #~ msgstr "Visitante" #~ msgid "nobody" #~ msgstr "ninguém" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Permitir aos visitantes o envio de requisições de voz" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "O intervalo mínimo entre requisições de voz (em segundos)" #~ msgid "Enable message archiving" #~ msgstr "Habilitar arquivamento de mensagens" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Excluir IDs Jabber de serem submetidos ao CAPTCHA" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Necessitas um cliente com suporte de x:data para configurar a sala" #~ msgid "Number of occupants" #~ msgstr "Número de participantes" #~ msgid "User JID" #~ msgstr "Usuário JID" #~ msgid "Grant voice to this person?" #~ msgstr "Dar voz a esta pessoa?" #~ msgid "Node ID" #~ msgstr "ID do Tópico" #~ msgid "Subscriber Address" #~ msgstr "Endereço dos Assinantes" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Autorizar este Jabber ID para a inscrição neste tópico pubsub?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Enviar payloads junto com as notificações de eventos" #~ msgid "Deliver event notifications" #~ msgstr "Entregar as notificações de evento" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Notificar assinantes a configuração do nó mudar" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Notificar assinantes quando o nó for eliminado se elimine" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Notificar assinantes quando itens forem eliminados do nó" #~ msgid "Persist items to storage" #~ msgstr "Persistir elementos ao armazenar" #~ msgid "A friendly name for the node" #~ msgstr "Um nome familiar para o nó" #~ msgid "Max # of items to persist" #~ msgstr "Máximo # de elementos que persistem" #~ msgid "Whether to allow subscriptions" #~ msgstr "Permitir subscrições" #~ msgid "Specify the access model" #~ msgstr "Especificar os modelos de acesso" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Listar grupos autorizados" #~ msgid "Specify the publisher model" #~ msgstr "Especificar o modelo do publicante" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Descartar todos os itens quando o publicante principal estiver offline" #~ msgid "Specify the event message type" #~ msgstr "Especificar o tipo de mensagem para o evento" #~ msgid "Max payload size in bytes" #~ msgstr "Máximo tamanho do payload em bytes" #~ msgid "When to send the last published item" #~ msgstr "Quando enviar o último tópico publicado" #~ msgid "Only deliver notifications to available users" #~ msgstr "Somente enviar notificações aos usuários disponíveis" #~ msgid "The collections with which a node is affiliated" #~ msgstr "As coleções com as quais o nó está relacionado" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Preencha campos para buscar usuários Jabber que concordem" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Servidores que partem de s2s" #~ msgid "Delete" #~ msgstr "Eliminar" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "" #~ "Este participante foi desconectado da sala de chat por ter enviado uma " #~ "mensagem de erro." #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Este participante foi desconectado da sala de chat por ter enviado uma " #~ "mensagem de erro para outro usuário." #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Este participante foi desconectado da sala de chat por ter enviado uma " #~ "notificação errônea de presença." ejabberd-18.01/priv/msgs/th.po0000644000232200023220000021516013225664356016554 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.0-alpha\n" "Last-Translator: EQHO Communications (Thailand) Ltd. - http://www.eqho.com\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Thai (ภาษาไทย)\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr " ตั้งหัวข้อว่า: " #: mod_muc_room:1893 #, fuzzy msgid "A password is required to enter this room" msgstr "ต้องใส่รหัสผ่านเพื่อเข้าห้องนี้" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "การกำหนดค่าการเข้าถึง" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "การกำหนดค่ารายการควบคุมการเข้าถึง" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "รายการควบคุมการเข้าถึง" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "กฎการเข้าถึง" #: mod_configure:1095 msgid "Access control lists" msgstr "รายการควบคุมการเข้าถึง" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "การเข้าถึงถูกปฏิเสธโดยนโยบายการบริการ" #: mod_configure:1113 msgid "Access rules" msgstr "กฎการเข้าถึง" #: mod_configure:1772 msgid "Action on user" msgstr "การดำเนินการกับผู้ใช้" #: mod_roster:982 msgid "Add Jabber ID" msgstr "เพิ่ม Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "เพิ่มผู้ใช้ใหม่" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "เพิ่มผู้ใช้" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "การดูแล" #: mod_configure:1767 msgid "Administration of " msgstr "การดูแล " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "ต้องมีสิทธิพิเศษของผู้ดูแลระบบ" #: mod_configure:507 msgid "All Users" msgstr "ผู้ใช้ทั้งหมด" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "กิจกรรมทั้งหมด" #: mod_muc_log:1046 #, fuzzy msgid "Allow users to change the subject" msgstr "อนุญาตให้ผู้ใช้เปลี่ยนหัวข้อได้" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "อนุญาตให้ผู้ใช้ถามคำถามกับผู้ใช้คนอื่นๆ ได้" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว" #: mod_muc_log:1057 #, fuzzy msgid "Allow visitors to change nickname" msgstr "อนุญาตให้ผู้ใช้เปลี่ยนหัวข้อได้" #: mod_muc_log:1050 #, fuzzy msgid "Allow visitors to send private messages to" msgstr "อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว" #: mod_muc_log:1059 #, fuzzy msgid "Allow visitors to send status text in presence updates" msgstr "อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว" #: mod_announce:611 msgid "Announcements" msgstr "ประกาศ" #: mod_muc_log:476 msgid "April" msgstr "เมษายน" #: mod_muc_log:480 msgid "August" msgstr "สิงหาคม" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "การสำรองข้อมูล " #: mod_configure:584 msgid "Backup Management" msgstr "การจัดการข้อมูลสำรอง" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "การสำรองข้อมูล" #: mod_configure:948 msgid "Backup to File at " msgstr "สำรองไฟล์ข้อมูลที่" #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "รูปแบบที่ไม่ถูกต้อง" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "วันเกิด" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "เวลาการทำงานของ CPU:" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "เปลี่ยนรหัสผ่าน" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "เปลี่ยนรหัสผ่านของผู้ใช้" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "รหัสผ่านคือ" #: mod_muc_room:2764 msgid "Changing role/affiliation is not allowed" msgstr "" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "มีการปรับเปลี่ยนการกำหนดค่าของห้องสนทนา" #: mod_muc_log:453 #, fuzzy msgid "Chatroom is created" msgstr "ห้องสนทนา" #: mod_muc_log:455 #, fuzzy msgid "Chatroom is destroyed" msgstr "ห้องสนทนา" #: mod_muc_log:457 #, fuzzy msgid "Chatroom is started" msgstr "ห้องสนทนา" #: mod_muc_log:459 #, fuzzy msgid "Chatroom is stopped" msgstr "ห้องสนทนา" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "ห้องสนทนา" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "เลือกชื่อผู้ใช้และรหัสผ่านเพื่อลงทะเบียนกับเซิร์ฟเวอร์นี้" #: mod_configure:920 msgid "Choose modules to stop" msgstr "เลือกโมดูลเพื่อหยุดการทำงาน" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "เลือกชนิดการจัดเก็บของตาราง" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "เลือกว่าจะอนุมัติการสมัครเข้าใช้งานของเอนทิตี้นี้หรือไม่" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "เมือง" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "คำสั่ง" #: mod_muc:461 msgid "Conference room does not exist" msgstr "ไม่มีห้องประชุม" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "การกำหนดค่า" #: mod_muc_room:3163 #, fuzzy msgid "Configuration of room ~s" msgstr "การกำหนดค่าสำหรับ " #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "ทรัพยากรที่เชื่อมต่อ:" #: mod_irc:526 msgid "Connections parameters" msgstr "" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "ประเทศ" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "ฐานข้อมูล" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "การกำหนดค่าตารางฐานข้อมูลที่" #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "ตารางฐานข้อมูลที่" #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "ฐานข้อมูล" #: mod_muc_log:484 msgid "December" msgstr "ธันวาคม" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "ผู้ใช้เริ่มต้นเป็นผู้เข้าร่วม" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "ลบข้อความที่เลือก" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "ลบผู้ใช้" #: mod_announce:629 msgid "Delete message of the day" msgstr "ลบข้อความของวัน" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "ลบข้อความของวันบนโฮสต์ทั้งหมด" #: mod_shared_roster:900 msgid "Description:" msgstr "รายละเอียด:" #: mod_configure:889 msgid "Disc only copy" msgstr "คัดลอกเฉพาะดิสก์" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "กลุ่มที่แสดง:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "" #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "ถ่ายโอนการสำรองข้อมูลไปยังไฟล์ข้อความที่" #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "ถ่ายโอนข้อมูลไปยังไฟล์ข้อความ" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "แก้ไขคุณสมบัติ" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "" #: ejabberd_web_admin:1953 msgid "Elements" msgstr "" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "อีเมล" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "รหัสผ่านคือ" #: mod_muc_log:1055 msgid "Enable logging" msgstr "เปิดใช้งานการบันทึก" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "สิ้นสุดเซสชันของผู้ใช้" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "ป้อนรายการของ {โมดูล, [ตัวเลือก]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "ป้อนชื่อเล่นที่คุณต้องการลงทะเบียน" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "ป้อนพาธเพื่อสำรองไฟล์ข้อมูล" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "ป้อนพาธไปยัง jabberd14 spool dir" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "ป้อนพาธไปยังไฟล์เก็บพักข้อมูล jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "ป้อนพาธของไฟล์ข้อความ" #: ejabberd_captcha:70 #, fuzzy msgid "Enter the text you see" msgstr "ป้อนพาธของไฟล์ข้อความ" #: mod_irc:759 #, fuzzy msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "ป้อนชื่อผู้ใช้และการเข้ารหัสที่คุณต้องการใช้สำหรับเชื่อมต่อกับเซิร์ฟเวอร์ IRC" #: mod_irc:540 #, fuzzy msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "ป้อนชื่อผู้ใช้และการเข้ารหัสที่คุณต้องการใช้สำหรับเชื่อมต่อกับเซิร์ฟเวอร์ IRC" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "" #: mod_irc:520 #, fuzzy msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "ตัวอย่าง: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", " "\"iso8859-1\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "" #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "นามสกุล" #: mod_muc_log:474 msgid "February" msgstr "กุมภาพันธ์" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "กรอกข้อมูลในแบบฟอร์มเพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน (ใส่เครื่องหมาย * " "ที่ท้ายสุดของฟิลด์เพื่อจับคู่กับสตริงย่อย)" #: mod_muc_log:467 msgid "Friday" msgstr "วันศุกร์" #: mod_offline:691 msgid "From" msgstr "จาก" #: mod_configure:723 msgid "From ~s" msgstr "จาก ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "ชื่อเต็ม" #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "แสดงจำนวนผู้ใช้ออนไลน์" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "แสดงจำนวนผู้ใช้ที่ลงทะเบียน" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "แสดงเวลาเข้าสู่ระบบครั้งล่าสุดของผู้ใช้" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "ขอรับรหัสผ่านของผู้ใช้" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "แสดงสถิติของผู้ใช้" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "ชื่อกลาง" #: mod_shared_roster:923 msgid "Group " msgstr "กลุ่ม" #: mod_roster:916 msgid "Groups" msgstr "กลุ่ม" #: ejabberd_web_admin:1365 msgid "Host" msgstr "โฮสต์" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "" #: mod_configure:1673 msgid "IP addresses" msgstr "ที่อยู่ IP" #: mod_irc:439 msgid "IRC Transport" msgstr "การส่ง IRC" #: mod_irc:496 msgid "IRC Username" msgstr "ชื่อผู้ใช้ IRC" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "ไม่พบโหนด" #: mod_irc:673 #, fuzzy msgid "IRC server" msgstr "ชื่อผู้ใช้ IRC" #: mod_irc:756 msgid "IRC settings" msgstr "" #: mod_irc:766 #, fuzzy msgid "IRC username" msgstr "ชื่อผู้ใช้ IRC" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "" #: mod_irc:503 #, fuzzy msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "ถ้าคุณต้องการระบุการเข้ารหัสที่ต่างกันสำหรับเซิร์ฟเวอร์ IRC ให้กรอกค่าโดยใช้รูปแบบ '{\"irc " "server\", \"encoding\"}' ลงในรายการ การบริการนี้ใช้การเข้ารหัสในรูปแบบ \"~s\" " "โดยค่าดีฟอลต์ " #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "อิมพอร์ตไดเร็กทอรี" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "อิมพอร์ตไฟล์" #: mod_configure:982 msgid "Import User from File at " msgstr "อิมพอร์ตผู้ใช้จากไฟล์ที่" #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "อิมพอร์ตผู้ใช้จาก Dir ที่" #: ejabberd_web_admin:2100 #, fuzzy msgid "Import user data from jabberd14 spool file:" msgstr "อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "" #: ejabberd_web_admin:2111 #, fuzzy msgid "Import users data from jabberd14 spool directory:" msgstr "อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "ประเภทข้อความไม่เหมาะสม" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "การเชื่อมต่อ s2s ขาออก:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "รหัสผ่านไม่ถูกต้อง" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "รหัสผ่านไม่ถูกต้อง" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 #, fuzzy msgid "It is not allowed to send private messages" msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยัง \"กลุ่มสนทนา\"" #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "มกราคม" #: mod_irc:665 msgid "Join IRC channel" msgstr "" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "" #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "" #: mod_muc_log:479 msgid "July" msgstr "กรกฎาคม" #: mod_muc_log:478 msgid "June" msgstr "มิถุนายน" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "กิจกรรมล่าสุด" #: mod_configure:1646 msgid "Last login" msgstr "การเข้าสู่ระบบครั้งล่าสุด" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "เดือนที่แล้ว" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "ปีที่แล้ว" #: mod_configure:941 msgid "List of modules to start" msgstr "รายการของโมดูลที่จะเริ่มการทำงาน" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "พอร์ทฟัง" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "พอร์ทฟังที่" #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "อัพเดตสคริปต์ระดับต่ำ" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "สร้างรายการผู้เข้าร่วมสำหรับใช้งานโดยบุคคลทั่วไป" #: mod_muc_log:1062 #, fuzzy msgid "Make room CAPTCHA protected" msgstr "สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "สร้างห้องสำหรับสมาชิกเท่านั้น" #: mod_muc_log:1042 #, fuzzy msgid "Make room moderated" msgstr "สร้างเป็นห้องถาวร" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "สร้างเป็นห้องถาวร" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "สร้างเป็นห้องที่บุคคลทั่วไปสามารถค้นหาได้" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "ชื่อผู้ใช้ IRC" #: mod_muc_log:475 msgid "March" msgstr "มีนาคม" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "จำนวนผู้ครอบครองห้องสูงสุด" #: mod_muc_log:477 msgid "May" msgstr "พฤษภาคม" #: mod_shared_roster:907 msgid "Members:" msgstr "สมาชิก:" #: mod_muc_room:1833 #, fuzzy msgid "Membership is required to enter this room" msgstr "ต้องเป็นสมาชิกจึงจะสามารถเข้าห้องนี้ได้" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" #: ejabberd_web_admin:1954 msgid "Memory" msgstr "หน่วยความจำ" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "เนื้อหาของข้อความ" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "ชื่อกลาง" #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "ต้องมีสิทธิพิเศษของผู้ดูแลการสนทนา" #: ejabberd_web_admin:2279 #, fuzzy msgid "Modified modules" msgstr "โมดูลที่อัพเดต" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "โมดูล" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "โมดูล" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "โมดูลที่ " #: mod_muc_log:463 msgid "Monday" msgstr "วันจันทร์" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "ชื่อ" #: mod_shared_roster:896 msgid "Name:" msgstr "ชื่อ:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "ไม่เคย" #: mod_register_web:377 #, fuzzy msgid "New Password:" msgstr "รหัสผ่าน:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "ชื่อเล่น" #: mod_muc:722 msgid "Nickname Registration at " msgstr "การลงทะเบียนชื่อเล่นที่ " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "ไม่มีชื่อเล่น ~s อยู่ในห้องนี้" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "ไม่พบโหนด" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "ไม่มีข้อมูล" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "ไม่ได้ป้อนเนื้อหาสำหรับข้อความที่ประกาศ" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "ไม่พบโหนด" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "ไม่พบโหนด" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "ไม่พบโหนด" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "ไม่พบโหนด" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "โหนด " #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "โหนด" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "ไม่มี" #: ejabberd_web_admin:1033 #, fuzzy msgid "Not Found" msgstr "ไม่พบโหนด" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "พฤศจิกายน" #: mod_configure:1212 msgid "Number of online users" msgstr "จำนวนผู้ใช้ออนไลน์" #: mod_configure:1202 msgid "Number of registered users" msgstr "จำนวนผู้ใช้ที่ลงทะเบียน" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "ตกลง" #: mod_muc_log:482 msgid "October" msgstr "ตุลาคม" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "ข้อความออฟไลน์" #: mod_offline:761 msgid "Offline Messages:" msgstr "ข้อความออฟไลน์:" #: mod_register_web:373 #, fuzzy msgid "Old Password:" msgstr "รหัสผ่าน:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "ออนไลน์" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "ผู้ใช้ออนไลน์" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "ผู้ใช้ออนไลน์:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "ผู้ดูแลการสนทนาเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" #: mod_muc_room:773 #, fuzzy msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "ผู้ดูแลการสนทนาและผู้เข้าร่วมเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" #: mod_muc_room:778 #, fuzzy msgid "Only moderators are allowed to change the subject in this room" msgstr "ผู้ดูแลการสนทนาเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" #: mod_muc_room:917 #, fuzzy msgid "Only moderators can approve voice requests" msgstr "อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความไปยังห้องประชุม" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งกระทู้ถามไปยังห้องประชุม" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "ผู้ดูแลด้านการบริการเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความการบริการ" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "ตัวเลือก" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "ชื่อองค์กร" #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "หน่วยขององค์กร" #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "การเชื่อมต่อ s2s ขาออก" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "การเชื่อมต่อ s2s ขาออก:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "ต้องมีสิทธิพิเศษของเจ้าของ" #: mod_offline:693 msgid "Packet" msgstr "แพ็กเก็ต" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "รหัสผ่าน" #: mod_configure:1130 msgid "Password Verification" msgstr "การตรวจสอบรหัสผ่าน" #: mod_register_web:268 mod_register_web:381 #, fuzzy msgid "Password Verification:" msgstr "การตรวจสอบรหัสผ่าน" #: mod_irc:822 #, fuzzy msgid "Password ~b" msgstr "รหัสผ่าน" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "รหัสผ่าน:" #: mod_configure:998 msgid "Path to Dir" msgstr "พาธไปยัง Dir" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "พาธของไฟล์ข้อมูล" #: mod_roster:915 msgid "Pending" msgstr "ค้างอยู่" #: ejabberd_web_admin:994 msgid "Period: " msgstr "ระยะเวลา:" #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "ออกจากห้อง" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 #, fuzzy msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "โปรดทราบว่าตัวเลือกเหล่านี้จะสำรองข้อมูลในฐานข้อมูล builtin Mnesia เท่านั้น หากคุณใช้โมดูล " "ODBC คุณต้องสำรองข้อมูลของฐานข้อมูล SQL แยกต่างหากด้วย" #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "พอร์ท" #: mod_irc:828 #, fuzzy msgid "Port ~b" msgstr "พอร์ท" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 #, fuzzy msgid "Protocol" msgstr "พอร์ท" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "คำร้องขอของผู้สมัครเข้าใช้งาน PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "เผยแพร่-สมัครเข้าใช้งาน" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "ห้องนี้ไม่อนุญาตให้ส่งกระทู้ถามถึงสมาชิกในห้องประชุม" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "คัดลอก RAM และดิสก์" #: mod_configure:889 msgid "RAM copy" msgstr "คัดลอก RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "ข้อผิดพลาดจากการเรียกใช้ RPC" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "ข้อมูลดิบ" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "แน่ใจว่าต้องการลบข้อความของวันหรือไม่" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "ผู้รับไม่ได้อยู่ในห้องประชุม" #: mod_register_web:275 #, fuzzy msgid "Register" msgstr "บัญชีรายชื่อ" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "ผู้ใช้ที่ลงทะเบียน" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "ผู้ใช้ที่ลงทะเบียน:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "ผู้ใช้ที่ลงทะเบียน" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "การลงทะเบียนใน mod_irc สำหรับ" #: mod_configure:889 msgid "Remote copy" msgstr "คัดลอกระยะไกล" #: mod_roster:963 msgid "Remove" msgstr "ลบ" #: mod_offline:765 #, fuzzy msgid "Remove All Offline Messages" msgstr "ข้อความออฟไลน์" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "ลบผู้ใช้" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "แทนที่ด้วยการเชื่อมต่อใหม่" #: mod_configure:1675 msgid "Resources" msgstr "ทรัพยากร" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "เริ่มต้นใหม่" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "เริ่มต้นการบริการใหม่อีกครั้ง" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "การคืนค่า" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "คืนค่าการสำรองข้อมูลจากไฟล์ที่" #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "คืนค่าข้อมูลสำรองแบบไบนารีหลังจากที่ ejabberd ถัดไปเริ่มการทำงานใหม่ (ใช้หน่วยความจำน้อยลง):" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "คืนค่าข้อมูลสำรองแบบไบนารีโดยทันที:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "คืนค่าข้อมูลสำรองที่เป็นข้อความธรรมดาโดยทันที:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "การกำหนดค่าห้องสนทนา" #: mod_muc_log:892 #, fuzzy msgid "Room Occupants" msgstr "จำนวนผู้ครอบครองห้อง" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "การสร้างห้องสนทนาถูกปฏิเสธโดยนโยบายการบริการ" #: mod_muc_log:1064 #, fuzzy msgid "Room description" msgstr "รายละเอียด:" #: mod_muc_log:1027 msgid "Room title" msgstr "ชื่อห้อง" #: mod_roster:1082 msgid "Roster" msgstr "บัญชีรายชื่อ" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "บัญชีรายชื่อของ " #: mod_configure:1671 msgid "Roster size" msgstr "ขนาดของบัญชีรายชื่อ" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "โหนดที่ทำงาน" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "วันเสาร์" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 msgid "Scan failed" msgstr "" #: ejabberd_web_admin:2282 msgid "Script check" msgstr "ตรวจสอบคริปต์" #: mod_vcard:453 msgid "Search Results for " msgstr "ผลการค้นหาสำหรับ " #: mod_vcard:427 msgid "Search users in " msgstr "ค้นหาผู้ใช้ใน " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมด" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมดบนโฮสต์ทั้งหมด" #: mod_announce:613 msgid "Send announcement to all users" msgstr "ส่งประกาศถึงผู้ใช้ทั้งหมด" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "ส่งประกาศถึงผู้ใช้ทั้งหมดบนโฮสต์ทั้งหมด" #: mod_muc_log:481 msgid "September" msgstr "กันยายน" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 #, fuzzy msgid "Server:" msgstr "ไม่เคย" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "ตั้งค่าข้อความของวันและส่งถึงผู้ใช้ออนไลน์" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "ตั้งค่าข้อความของวันบนโฮสต์ทั้งหมดและส่งถึงผู้ใช้ออนไลน์" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "กลุ่มบัญชีรายชื่อที่ใช้งานร่วมกัน" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "แสดงตารางรวม" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "แสดงตารางทั่วไป" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "ปิดการบริการ" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "เริ่ม" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "เริ่มโมดูล" #: mod_configure:936 msgid "Start Modules at " msgstr "เริ่มโมดูลที่" #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "สถิติ" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "สถิติของ ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "หยุด" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "หยุดโมดูล" #: mod_configure:918 msgid "Stop Modules at " msgstr "หยุดโมดูลที่" #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "โหนดที่หยุด" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "ชนิดที่เก็บข้อมูล" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "จัดเก็บข้อมูลสำรองแบบไบนารี:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "จัดเก็บข้อมูลสำรองที่เป็นข้อความธรรมดา:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "หัวเรื่อง" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "ส่ง" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "ส่งแล้ว" #: mod_roster:914 msgid "Subscription" msgstr "การสมัครสมาชิก" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "วันอาทิตย์" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 #, fuzzy msgid "That nickname is already in use by another occupant" msgstr "ชื่อเล่นถูกใช้งานอยู่โดยผู้ครอบครองห้อง" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 #, fuzzy msgid "That nickname is registered by another person" msgstr "ชื่อเล่นถูกลงทะเบียนใช้งานโดยบุคคลอื่น" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "" #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 #, fuzzy msgid "The password is too weak" msgstr "รหัสผ่านคือ" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "" #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "" #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "" #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "ห้องนี้ไม่ปิดบังชื่อ" #: mod_muc_log:466 msgid "Thursday" msgstr "วันพฤหัสบดี" #: mod_offline:690 msgid "Time" msgstr "เวลา" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "การหน่วงเวลา" #: mod_offline:692 msgid "To" msgstr "ถึง" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "ถึง ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 msgid "Too many users in this conference" msgstr "" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "ห้องสนทนา" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "อัตราของปริมาณการเข้าใช้เกินขีดจำกัด" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "ทรานแซกชันที่ถูกยกเลิก:" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "ทรานแซกชันที่ได้รับมอบหมาย:" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "ทรานแซกชันที่บันทึก:" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "ทรานแซกชันที่เริ่มทำงานใหม่อีกครั้ง:" #: mod_muc_log:464 msgid "Tuesday" msgstr "วันอังคาร" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "อัพเดต" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "อัพเดตข้อความของวัน (ไม่ต้องส่ง)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "อัพเดตข้อความของวันบนโฮสต์ทั้งหมด (ไม่ต้องส่ง) " #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "แผนการอัพเดต" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "อัพเดตสคริปต์" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "อัพเดต " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "เวลาการทำงานต่อเนื่อง:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "ต้องใช้ STARTTLS" #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "ต้องใช้ STARTTLS" #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "ผู้ใช้" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "การจัดการผู้ใช้" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "ไม่พบโหนด" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "ผู้ใช้" #: mod_register_web:230 mod_register_web:366 mod_register_web:476 #, fuzzy msgid "Username:" msgstr "ชื่อผู้ใช้ IRC" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "ผู้ใช้" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "กิจกรรมล่าสุดของผู้ใช้" #: mod_register:375 #, fuzzy msgid "Users are not allowed to register accounts so quickly" msgstr "ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด" #: mod_roster:954 msgid "Validate" msgstr "ตรวจสอบ" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "โฮสต์เสมือน" #: mod_muc_room:992 #, fuzzy msgid "Visitors are not allowed to change their nicknames in this room" msgstr "ผู้ดูแลการสนทนาเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด" #: mod_muc_room:3879 msgid "Voice request" msgstr "" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "" #: mod_muc_log:465 msgid "Wednesday" msgstr "วันพุธ" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "" #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "คุณถูกสั่งห้ามไมให้เข้าห้องนี้" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "คุณต้องกรอกฟิลด์ \"Nickname\" ในแบบฟอร์ม" #: mod_register:222 #, fuzzy msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อลงทะเบียนชื่อเล่น" #: mod_muc:731 #, fuzzy msgid "You need a client that supports x:data to register the nickname" msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อลงทะเบียนชื่อเล่น" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดการตั้งค่า mod_irc" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อค้นหา" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "" #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "" #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "" #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "ลำดับข้อความออฟไลน์ของผู้ที่ติดต่อของคุณเต็มแล้ว ข้อความถูกลบทิ้งแล้ว" #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC module" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC module" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe module" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams module" #: ejabberd_web_admin:311 ejabberd_web_admin:343 #, fuzzy msgid "ejabberd Web Admin" msgstr "เว็บอินเทอร์เฟซของ ejabberd" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard module" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "ถูกสั่งห้าม" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "ถูกไล่ออก" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "" #: mod_muc_log:410 msgid "is now known as" msgstr "ซึ่งรู้จักกันในชื่อ" #: mod_muc_log:370 msgid "joins the room" msgstr "เข้าห้องสนทนานี้" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "ออกจากห้อง" #: mod_muc_room:3856 msgid "private, " msgstr "ส่วนตัว, " #: mod_muc_room:3955 msgid "the password is" msgstr "รหัสผ่านคือ" #: mod_vcard:292 msgid "vCard User Search" msgstr "ค้นหาผู้ใช้ vCard " #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s การกำหนดค่ากฎการเข้าถึง" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s เชิญคุณเข้าร่วมสนทนาในห้อง ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s's ลำดับข้อความออฟไลน์" #~ msgid "No resource provided" #~ msgstr "ไม่ได้ระบุข้อมูล" #, fuzzy #~ msgid "Server" #~ msgstr "ไม่เคย" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s ไม่ถูกต้อง" #~ msgid "Invalid affiliation: ~s" #~ msgstr "การเข้าร่วมที่ไม่ถูกต้อง: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "บทบาทไม่ถูกต้อง: ~s" #~ msgid "No limit" #~ msgstr "ไม่จำกัด" #~ msgid "Present real Jabber IDs to" #~ msgstr "แสดง Jabber IDs ที่ถูกต้องแก่" #~ msgid "moderators only" #~ msgstr "สำหรับผู้ดูแลการสนทนาเท่านั้น" #~ msgid "anyone" #~ msgstr "ทุกคน" #, fuzzy #~ msgid "Moderator" #~ msgstr "สำหรับผู้ดูแลการสนทนาเท่านั้น" #, fuzzy #~ msgid "Allow visitors to send voice requests" #~ msgstr "อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดค่าห้องสนทนา " #~ msgid "Number of occupants" #~ msgstr "จำนวนผู้ครอบครองห้อง" #, fuzzy #~ msgid "User JID" #~ msgstr "ผู้ใช้" #~ msgid "Node ID" #~ msgstr "ID โหนด" #~ msgid "Subscriber Address" #~ msgstr "ที่อยู่ของผู้สมัคร" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "อนุญาตให้ Jabber ID นี้เข้าร่วมเป็นสมาชิกของโหนด pubsub หรือไม่" #~ msgid "Deliver payloads with event notifications" #~ msgstr "ส่งส่วนของข้อมูล (payload) พร้อมกับการแจ้งเตือนเหตุการณ์" #~ msgid "Deliver event notifications" #~ msgstr "ส่งการแจ้งเตือนเหตุการณ์" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "แจ้งเตือนผู้สมัครสมาชิกเมื่อการกำหนดค่าโหนดเปลี่ยนแปลง" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "แจ้งเตือนผู้สมัครสมาชิกเมื่อโหนดถูกลบ" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "แจ้งเตือนผู้สมัครสมาชิกเมื่อรายการถูกลบออกจากโหนด" #~ msgid "Persist items to storage" #~ msgstr "ยืนยันรายการที่จะจัดเก็บ" #~ msgid "Max # of items to persist" #~ msgstr "จำนวนสูงสุดของรายการที่ยืนยัน" #~ msgid "Whether to allow subscriptions" #~ msgstr "อนุญาตให้เข้าร่วมเป็นสมาชิกหรือไม่" #~ msgid "Specify the access model" #~ msgstr "ระบุโมเดลการเข้าถึง" #~ msgid "Specify the publisher model" #~ msgstr "ระบุโมเดลผู้เผยแพร่" #, fuzzy #~ msgid "Specify the event message type" #~ msgstr "ระบุโมเดลการเข้าถึง" #~ msgid "Max payload size in bytes" #~ msgstr "ขนาดสูงสุดของส่วนของข้อมูล (payload) มีหน่วยเป็นไบต์" #~ msgid "When to send the last published item" #~ msgstr "เวลาที่ส่งรายการที่เผยแพร่ครั้งล่าสุด" #~ msgid "Only deliver notifications to available users" #~ msgstr "ส่งการแจ้งเตือนถึงผู้ใช้ที่สามารถติดต่อได้เท่านั้น" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "กรอกข้อมูลลงในฟิลด์เพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน" #~ msgid "Outgoing s2s Servers:" #~ msgstr "เซิร์ฟเวอร์ s2s ขาออก:" #~ msgid "Delete" #~ msgstr "ลบ" #~ msgid "Encodings" #~ msgstr "การเข้ารหัส" #~ msgid "(Raw)" #~ msgstr "(ข้อมูลดิบ)" #~ msgid "Specified nickname is already registered" #~ msgstr "ชื่อเล่นที่ระบุได้รับการลงได้ทะเบียนแล้ว" #~ msgid "Size" #~ msgstr "ขนาด" #~ msgid "Roster groups that may subscribe (if access model is roster)" #~ msgstr "กลุ่มบัญชีรายชื่อที่อาจจะสมัครเป็นสมาชิก (ถ้าโมเดลการเข้าถึงคือบัญชีรายชื่อ)" ejabberd-18.01/priv/msgs/sk.po0000644000232200023220000017101013225664356016551 0ustar debalancedebalancemsgid "" msgstr "" "Project-Id-Version: 2.1.x\n" "PO-Revision-Date: 2012-04-29 18:25+0000\n" "Last-Translator: Marek Bečka \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Language: Slovak (slovenčina)\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" "X-Additional-Translator: Juraj Michalek\n" "X-Additional-Translator: SkLUG\n" #: mod_muc_log:413 mod_muc_log:752 msgid " has set the subject to: " msgstr "zmenil(a) tému na: " #: mod_muc_room:1893 msgid "A password is required to enter this room" msgstr "Pre vstup do miestnosti je potrebné heslo" #: ejabberd_oauth:448 msgid "Accept" msgstr "" #: mod_configure:1109 msgid "Access Configuration" msgstr "Konfigurácia prístupu" #: mod_configure:1091 msgid "Access Control List Configuration" msgstr "Konfigurácia zoznamu prístupových oprávnení (ACL)" #: ejabberd_web_admin:758 ejabberd_web_admin:797 mod_configure:185 #: mod_configure:514 msgid "Access Control Lists" msgstr "Zoznamy prístupových oprávnení (ACL)" #: ejabberd_web_admin:863 ejabberd_web_admin:896 mod_configure:187 #: mod_configure:515 msgid "Access Rules" msgstr "Prístupové pravidlá" #: mod_configure:1095 msgid "Access control lists" msgstr "Zoznamy prístupových oprávnení (ACL)" #: ejabberd_c2s:414 ejabberd_c2s:661 ejabberd_s2s:372 ejabberd_service:202 #: mod_announce:255 mod_announce:273 mod_announce:275 mod_announce:277 #: mod_announce:279 mod_announce:281 mod_announce:283 mod_announce:285 #: mod_announce:287 mod_announce:289 mod_announce:291 mod_announce:351 #: mod_announce:353 mod_announce:355 mod_announce:357 mod_announce:359 #: mod_announce:361 mod_announce:363 mod_announce:365 mod_announce:367 #: mod_announce:369 mod_announce:420 mod_announce:846 mod_configure:206 #: mod_configure:219 mod_configure:220 mod_configure:221 mod_configure:222 #: mod_configure:224 mod_configure:225 mod_configure:226 mod_configure:227 #: mod_configure:229 mod_configure:231 mod_configure:233 mod_configure:235 #: mod_configure:237 mod_configure:239 mod_configure:241 mod_configure:243 #: mod_configure:245 mod_configure:247 mod_configure:249 mod_configure:251 #: mod_configure:253 mod_configure:255 mod_configure:257 mod_configure:259 #: mod_configure:261 mod_configure:263 mod_configure:265 mod_configure:267 #: mod_configure:313 mod_configure:435 mod_configure:780 mod_configure:782 #: mod_configure:784 mod_configure:786 mod_configure:788 mod_configure:790 #: mod_configure:792 mod_configure:794 mod_configure:1740 mod_http_upload:545 #: mod_irc:265 mod_legacy_auth:136 mod_muc:401 mod_proxy65_service:186 #: mod_proxy65_service:235 mod_register:130 mod_register:273 mod_register:325 #: mod_register:326 mod_roster:187 mod_s2s_dialback:323 msgid "Access denied by service policy" msgstr "Prístup bol zamietnutý nastavením služby" #: mod_configure:1113 msgid "Access rules" msgstr "Prístupové pravidlá" #: mod_configure:1772 msgid "Action on user" msgstr "Operácia aplikovaná na užívateľa" #: mod_roster:982 msgid "Add Jabber ID" msgstr "Pridať Jabber ID" #: ejabberd_web_admin:1277 mod_shared_roster:825 msgid "Add New" msgstr "Pridať nový" #: ejabberd_web_admin:1444 mod_configure:166 mod_configure:521 #: mod_configure:1118 msgid "Add User" msgstr "Pridať používateľa" #: ejabberd_web_admin:687 ejabberd_web_admin:698 msgid "Administration" msgstr "Administrácia" #: mod_configure:1767 msgid "Administration of " msgstr "Administrácia " #: mod_muc_room:2528 msgid "Administrator privileges required" msgstr "Sú potrebné práva administrátora" #: mod_configure:507 msgid "All Users" msgstr "Všetci užívatelia" #: ejabberd_web_admin:1010 msgid "All activity" msgstr "Všetky aktivity" #: mod_muc_log:1046 msgid "Allow users to change the subject" msgstr "Povoliť užívateľom meniť tému" #: mod_muc_log:1052 msgid "Allow users to query other users" msgstr "Povoliť užívateľom dotazovať sa informácie o iných užívateľoch" #: mod_muc_log:1054 msgid "Allow users to send invites" msgstr "Povoliť používateľom posielanie pozvánok" #: mod_muc_log:1048 msgid "Allow users to send private messages" msgstr "Povoliť užívateľom odosielať súkromné správy" #: mod_muc_log:1057 msgid "Allow visitors to change nickname" msgstr "Návštevníci môžu meniť prezývky" #: mod_muc_log:1050 msgid "Allow visitors to send private messages to" msgstr "Povoliť užívateľom odosielať súkromné správy" #: mod_muc_log:1059 msgid "Allow visitors to send status text in presence updates" msgstr "Návštevníci môžu posielať textové informácie v stavových správach" #: mod_announce:611 msgid "Announcements" msgstr "Oznámenia" #: mod_muc_log:476 msgid "April" msgstr "Apríl" #: mod_muc_log:480 msgid "August" msgstr "August" #: mod_pubsub:1842 msgid "Automatic node creation is not enabled" msgstr "" #: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617 msgid "Backup" msgstr "Zálohovať" #: mod_configure:584 msgid "Backup Management" msgstr "Správa zálohovania" #: ejabberd_web_admin:1979 #, fuzzy msgid "Backup of ~p" msgstr "Záloha " #: mod_configure:948 msgid "Backup to File at " msgstr "Záloha do súboru na " #: ejabberd_web_admin:763 ejabberd_web_admin:802 ejabberd_web_admin:868 #: ejabberd_web_admin:901 ejabberd_web_admin:937 ejabberd_web_admin:1422 #: ejabberd_web_admin:1705 ejabberd_web_admin:1861 ejabberd_web_admin:2145 #: ejabberd_web_admin:2174 mod_roster:972 mod_shared_roster:831 #: mod_shared_roster:926 msgid "Bad format" msgstr "Zlý formát" #: mod_vcard_ldap:334 mod_vcard_ldap:347 mod_vcard_mnesia:105 #: mod_vcard_mnesia:119 mod_vcard_sql:160 mod_vcard_sql:174 msgid "Birthday" msgstr "Dátum narodenia" #: mod_legacy_auth:102 msgid "Both the username and the resource are required" msgstr "" #: mod_proxy65_service:226 msgid "Bytestream already activated" msgstr "" #: ejabberd_captcha:135 msgid "CAPTCHA web page" msgstr "Webová stránka CAPTCHA" #: ejabberd_web_admin:2207 msgid "CPU Time:" msgstr "Čas procesoru" #: mod_privacy:334 msgid "Cannot remove active list" msgstr "" #: mod_privacy:341 msgid "Cannot remove default list" msgstr "" #: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353 #: mod_register_web:361 mod_register_web:386 msgid "Change Password" msgstr "Zmeniť heslo" #: mod_configure:174 mod_configure:528 msgid "Change User Password" msgstr "Zmeniť heslo užívateľa" #: mod_register:295 #, fuzzy msgid "Changing password is not allowed" msgstr "Nepovolené znaky:" #: mod_muc_room:2764 #, fuzzy msgid "Changing role/affiliation is not allowed" msgstr "Nepovolené znaky:" #: mod_register_web:239 msgid "Characters not allowed:" msgstr "Nepovolené znaky:" #: mod_muc_log:358 mod_muc_log:367 msgid "Chatroom configuration modified" msgstr "Nastavenie diskusnej miestnosti bolo zmenené" #: mod_muc_log:453 msgid "Chatroom is created" msgstr "Diskusná miestnosť je vytvorená" #: mod_muc_log:455 msgid "Chatroom is destroyed" msgstr "Diskusná miestnosť je zrušená" #: mod_muc_log:457 msgid "Chatroom is started" msgstr "Diskusná miestnosť je obnovená" #: mod_muc_log:459 msgid "Chatroom is stopped" msgstr "Diskusná miestnosť je pozastavená" #: mod_muc:525 mod_muc_admin:440 msgid "Chatrooms" msgstr "Diskusné miestnosti" #: mod_register:211 msgid "Choose a username and password to register with this server" msgstr "Zvolte meno užívateľa a heslo pre registráciu na tomto servere" #: mod_configure:920 msgid "Choose modules to stop" msgstr "Vyberte moduly, ktoré majú byť zastavené" #: mod_configure:881 msgid "Choose storage type of tables" msgstr "Vyberte typ úložiska pre tabuľky" #: mod_pubsub:1338 msgid "Choose whether to approve this entity's subscription." msgstr "Zvolte, či chcete povoliť toto odoberanie" #: mod_vcard_ldap:336 mod_vcard_ldap:349 mod_vcard_mnesia:107 #: mod_vcard_mnesia:121 mod_vcard_sql:162 mod_vcard_sql:176 msgid "City" msgstr "Mesto" #: mod_adhoc:118 mod_adhoc:147 mod_adhoc:163 mod_adhoc:179 msgid "Commands" msgstr "Príkazy" #: mod_muc:461 msgid "Conference room does not exist" msgstr "Diskusná miestnosť neexistuje" #: mod_configure:129 mod_configure:286 mod_configure:306 mod_configure:504 msgid "Configuration" msgstr "Konfigurácia" #: mod_muc_room:3163 msgid "Configuration of room ~s" msgstr "Konfigurácia miestnosti ~s" #: ejabberd_web_admin:1711 msgid "Connected Resources:" msgstr "Pripojené zdroje:" #: mod_irc:526 msgid "Connections parameters" msgstr "Parametre spojenia" #: mod_vcard_ldap:335 mod_vcard_ldap:348 mod_vcard_mnesia:106 #: mod_vcard_mnesia:120 mod_vcard_sql:161 mod_vcard_sql:175 msgid "Country" msgstr "Krajina" #: ejabberd_web_admin:1866 mod_configure:139 mod_configure:580 msgid "Database" msgstr "Databáza" #: mod_configure:879 msgid "Database Tables Configuration at " msgstr "Konfigurácia databázových tabuliek " #: ejabberd_web_admin:1941 #, fuzzy msgid "Database Tables at ~p" msgstr "Databázové tabuľky na " #: mod_blocking:267 mod_carboncopy:137 mod_irc:486 mod_irc:574 mod_irc:739 #: mod_last:215 mod_mam:501 mod_muc:749 mod_offline:291 mod_offline:582 #: mod_privacy:186 mod_privacy:204 mod_privacy:298 mod_privacy:314 #: mod_privacy:347 mod_privacy:364 mod_private:103 mod_private:110 #: mod_proxy65_service:231 mod_pubsub:3506 mod_pubsub:3513 mod_pubsub:3573 #: mod_pubsub:3598 mod_pubsub:3604 mod_pubsub:3607 mod_vcard:226 #: node_flat_sql:801 nodetree_tree_sql:128 nodetree_tree_sql:142 #: nodetree_tree_sql:257 #, fuzzy msgid "Database failure" msgstr "Databáza" #: mod_muc_log:484 msgid "December" msgstr "December" #: mod_muc_log:1044 msgid "Default users as participants" msgstr "Užívatelia sú implicitne členmi" #: ejabberd_web_admin:811 ejabberd_web_admin:911 mod_offline:703 #: mod_shared_roster:839 msgid "Delete Selected" msgstr "Zmazať vybrané" #: mod_configure:168 mod_configure:522 mod_configure:1135 msgid "Delete User" msgstr "Vymazať užívateľa" #: mod_announce:629 msgid "Delete message of the day" msgstr "Zmazať správu dňa" #: mod_announce:631 msgid "Delete message of the day on all hosts" msgstr "Zmazať správu dňa na všetkých serveroch" #: mod_shared_roster:900 msgid "Description:" msgstr "Popis:" #: mod_configure:889 msgid "Disc only copy" msgstr "Len kópia disku" #: mod_shared_roster:914 msgid "Displayed Groups:" msgstr "Zobrazené skupiny:" #: mod_register_web:251 msgid "" "Don't tell your password to anybody, not even the administrators of the " "Jabber server." msgstr "Nevyzrádzajte heslo nikomu, ani administrátorom tohoto Jabber servera." #: mod_configure:971 msgid "Dump Backup to Text File at " msgstr "Uložiť zálohu do textového súboru na " #: mod_configure:154 mod_configure:621 msgid "Dump to Text File" msgstr "Uložiť do textového súboru" #: mod_roster:180 msgid "Duplicated groups are not allowed by RFC6121" msgstr "" #: mod_configure:1776 msgid "Edit Properties" msgstr "Editovať vlastnosti" #: mod_muc_room:3881 msgid "Either approve or decline the voice request." msgstr "Povolte alebo zamietnite žiadosť o Voice." #: ejabberd_web_admin:1953 msgid "Elements" msgstr "Prvky" #: mod_vcard_ldap:337 mod_vcard_ldap:350 mod_vcard_mnesia:108 #: mod_vcard_mnesia:122 mod_vcard_sql:163 mod_vcard_sql:177 msgid "Email" msgstr "E-mail" #: mod_register:292 #, fuzzy msgid "Empty password" msgstr "heslo je" #: mod_muc_log:1055 msgid "Enable logging" msgstr "Zapnúť zaznamenávanie histórie" #: mod_push:252 msgid "Enabling push without 'node' attribute is not supported" msgstr "" #: mod_irc:834 msgid "Encoding for server ~b" msgstr "Kódovanie pre server ~b" #: mod_configure:170 mod_configure:524 mod_configure:1145 msgid "End User Session" msgstr "Ukončiť reláciu užívateľa" #: mod_configure:938 msgid "Enter list of {Module, [Options]}" msgstr "Vložte zoznam modulov {Modul, [Parametre]}" #: mod_muc:723 msgid "Enter nickname you want to register" msgstr "Zadajte prezývku, ktorú chcete registrovať" #: mod_configure:950 mod_configure:962 msgid "Enter path to backup file" msgstr "Zadajte cestu k súboru so zálohou" #: mod_configure:996 msgid "Enter path to jabberd14 spool dir" msgstr "Zadajte cestu k jabberd14 spool adresáru" #: mod_configure:985 msgid "Enter path to jabberd14 spool file" msgstr "Zadajte cestu k spool súboru jabberd14" #: mod_configure:974 msgid "Enter path to text file" msgstr "Zadajte cestu k textovému súboru" #: ejabberd_captcha:70 msgid "Enter the text you see" msgstr "Zadajte zobrazený text" #: mod_irc:759 msgid "" "Enter username and encodings you wish to use for connecting to IRC servers. " "Press 'Next' to get more fields to fill in. Press 'Complete' to save " "settings." msgstr "" "Vložte meno používateľa a kódovanie, ktoré chcete používať pri pripojení na " "IRC servery. Kliknutím na tlačítko 'Ďalej' môžete zadať niektoré ďalšie " "hodnoty. Pomocou 'Ukončiť ' uložíte nastavenia." #: mod_irc:540 msgid "" "Enter username, encodings, ports and passwords you wish to use for " "connecting to IRC servers" msgstr "" "Vložte meno používateľa, kódovanie, porty a heslo ktoré chcete používať pri " "pripojení na IRC server" #: mod_vcard:201 msgid "Erlang Jabber Server" msgstr "Erlang Jabber Server" #: ejabberd_web_admin:1976 ejabberd_web_admin:2147 msgid "Error" msgstr "Chyba" #: mod_irc:520 msgid "" "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." msgstr "" "Príklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." "net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." #: ejabberd_web_admin:2084 msgid "Export all tables as SQL queries to a file:" msgstr "" #: ejabberd_web_admin:2056 msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" msgstr "" "Exportovať dáta všetkých uživateľov na serveri do súborov PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2068 msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" msgstr "" "Exportovať dáta uživateľov na hostitelovi do súborov PIEFXIS (XEP-0227):" #: mod_delegation:275 msgid "External component failure" msgstr "" #: mod_delegation:283 msgid "External component timeout" msgstr "" #: mod_proxy65_service:218 msgid "Failed to activate bytestream" msgstr "" #: mod_muc_room:910 msgid "Failed to extract JID from your voice request approval" msgstr "Nepodarilo sa nájsť JID v súhlase o Voice." #: mod_delegation:257 msgid "Failed to map delegated namespace to external component" msgstr "" #: mod_http_upload:602 msgid "Failed to parse HTTP response" msgstr "" #: mod_irc:426 msgid "Failed to parse chanserv" msgstr "" #: mod_muc_room:3297 msgid "Failed to process option '~s'" msgstr "" #: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103 #: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172 msgid "Family Name" msgstr "Priezvisko" #: mod_muc_log:474 msgid "February" msgstr "Február" #: mod_http_upload:555 msgid "File larger than ~w bytes" msgstr "" #: mod_vcard:437 msgid "" "Fill in the form to search for any matching Jabber User (Add * to the end of " "field to match substring)" msgstr "" "Pre vyhľadanie Jabber používateľa vyplňte formulár (pridajte znak * na " "koniec, pre vyhľadanie podreťazca)" #: mod_muc_log:467 msgid "Friday" msgstr "Piatok" #: mod_offline:691 msgid "From" msgstr "Od" #: mod_configure:723 msgid "From ~s" msgstr "Od ~s" #: mod_vcard_ldap:329 mod_vcard_ldap:342 mod_vcard_mnesia:100 #: mod_vcard_mnesia:114 mod_vcard_sql:155 mod_vcard_sql:169 msgid "Full Name" msgstr "Celé meno: " #: mod_configure:183 mod_configure:536 msgid "Get Number of Online Users" msgstr "Zobraziť počet pripojených užívateľov" #: mod_configure:180 mod_configure:534 msgid "Get Number of Registered Users" msgstr "Zobraziť počet registrovaných užívateľov" #: mod_configure:176 mod_configure:530 mod_configure:1179 msgid "Get User Last Login Time" msgstr "Zobraziť čas posledného prihlásenia" #: mod_configure:172 mod_configure:526 mod_configure:1155 mod_configure:1165 msgid "Get User Password" msgstr "Zobraziť heslo užívateľa" #: mod_configure:178 mod_configure:532 mod_configure:1188 msgid "Get User Statistics" msgstr "Zobraziť štatistiku užívateľa" #: mod_vcard_ldap:330 mod_vcard_ldap:343 #, fuzzy msgid "Given Name" msgstr "Prostredné meno: " #: mod_shared_roster:923 msgid "Group " msgstr "Skupina " #: mod_roster:916 msgid "Groups" msgstr "Skupiny" #: ejabberd_web_admin:1365 msgid "Host" msgstr "Server" #: mod_s2s_dialback:325 msgid "Host unknown" msgstr "" #: ejabberd_web_admin:2463 msgid "IP" msgstr "IP" #: mod_configure:1673 msgid "IP addresses" msgstr "IP adresa" #: mod_irc:439 msgid "IRC Transport" msgstr "IRC Transport" #: mod_irc:496 msgid "IRC Username" msgstr "IRC prezývka" #: mod_irc:669 msgid "IRC channel (don't put the first #)" msgstr "IRC kanál (bez počiatočnej #)" #: mod_irc:417 #, fuzzy msgid "IRC connection not found" msgstr "Uzol nenájdený" #: mod_irc:673 msgid "IRC server" msgstr "IRC server" #: mod_irc:756 msgid "IRC settings" msgstr "Nastavania IRC" #: mod_irc:766 msgid "IRC username" msgstr "IRC prezývka" #: ejabberd_captcha:126 msgid "If you don't see the CAPTCHA image here, visit the web page." msgstr "Pokiaľ nevidíte obrázok CAPTCHA, navštívte webovú stránku." #: mod_irc:503 msgid "" "If you want to specify different ports, passwords, encodings for IRC " "servers, fill this list with values in format '{\"irc server\", \"encoding" "\", port, \"password\"}'. By default this service use \"~s\" encoding, port " "~p, empty password." msgstr "" "Ak chcete zadať iné porty, heslá a kódovania pre IRC servery, vyplnte zoznam " "s hodnotami vo formáte '{\"irc server\",\"kódovanie\", \"port\", \"heslo" "\"}'. Predvolenéi hodnoty pre túto službu sú: kódovanie \"~s\", port ~p a " "žiadne heslo." #: mod_configure:160 mod_configure:634 msgid "Import Directory" msgstr "Import adresára" #: mod_configure:157 mod_configure:632 msgid "Import File" msgstr "Import súboru" #: mod_configure:982 msgid "Import User from File at " msgstr "Importovať užívateľa zo súboru na " #: mod_configure:586 msgid "Import Users From jabberd14 Spool Files" msgstr "Importovať užívateľov z jabberd14 spool súborov" #: mod_configure:993 msgid "Import Users from Dir at " msgstr "Importovať užívateľov z adresára na " #: ejabberd_web_admin:2100 msgid "Import user data from jabberd14 spool file:" msgstr "Importovať dáta užívateľov z jabberd14 spool súboru:" #: ejabberd_web_admin:2043 msgid "Import users data from a PIEFXIS file (XEP-0227):" msgstr "Importovat dáta užívateľov zo súboru PIEFXIS (XEP-0227):" #: ejabberd_web_admin:2111 msgid "Import users data from jabberd14 spool directory:" msgstr "Importovať dáta užívateľov z jabberd14 spool adresára:" #: xmpp_stream_in:983 msgid "Improper 'from' attribute" msgstr "" #: xmpp_stream_in:477 msgid "Improper 'to' attribute" msgstr "" #: ejabberd_service:189 msgid "Improper domain part of 'from' attribute" msgstr "" #: mod_muc_room:260 msgid "Improper message type" msgstr "Nesprávny typ správy" #: ejabberd_web_admin:1586 #, fuzzy msgid "Incoming s2s Connections:" msgstr "Odchádzajúce s2s spojenia:" #: mod_muc_room:3669 mod_register:187 msgid "Incorrect CAPTCHA submit" msgstr "" #: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256 #, fuzzy msgid "Incorrect data form" msgstr "Nesprávne heslo" #: mod_muc_room:1943 mod_register:300 mod_register:350 msgid "Incorrect password" msgstr "Nesprávne heslo" #: mod_irc:585 msgid "Incorrect value in data form" msgstr "" #: mod_adhoc:276 msgid "Incorrect value of 'action' attribute" msgstr "" #: mod_configure:1806 msgid "Incorrect value of 'action' in data form" msgstr "" #: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419 #: mod_configure:1439 msgid "Incorrect value of 'path' in data form" msgstr "" #: mod_irc:331 msgid "Incorrect value of 'type' attribute" msgstr "" #: mod_privilege:100 msgid "Insufficient privilege" msgstr "" #: mod_privilege:286 msgid "Invalid 'from' attribute in forwarded message" msgstr "" #: mod_privilege:300 msgid "Invalid element" msgstr "" #: mod_muc_room:3926 #, fuzzy msgid "Invitations are not allowed in this conference" msgstr "Žiadosti o Voice nie sú povolené v tejto konferencii" #: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046 msgid "" "It is not allowed to send error messages to the room. The participant (~s) " "has sent an error message (~s) and got kicked from the room" msgstr "" #: mod_muc_room:418 mod_muc_room:429 msgid "It is not allowed to send private messages" msgstr "Nieje povolené posielať súkromné správy" #: mod_muc_room:388 msgid "It is not allowed to send private messages of type \"groupchat\"" msgstr "Nie je dovolené odoslanie súkromnej správy typu \"Skupinová správa\" " #: mod_muc_room:244 msgid "It is not allowed to send private messages to the conference" msgstr "Nie je povolené odosielať súkromné správy do konferencie" #: mod_register_web:181 mod_register_web:189 msgid "Jabber Account Registration" msgstr "Registrácia jabber účtu" #: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159 #: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600 #: mod_configure:1644 mod_configure:1669 mod_roster:912 mod_vcard_mnesia:113 #: mod_vcard_sql:168 msgid "Jabber ID" msgstr "Jabber ID" #: mod_muc_log:473 msgid "January" msgstr "Január" #: mod_irc:665 msgid "Join IRC channel" msgstr "Pripojit IRC kanál" #: mod_irc:689 msgid "Join the IRC channel here." msgstr "Propojiť IRC kanál sem." #: mod_irc:690 msgid "Join the IRC channel in this Jabber ID: ~s" msgstr "Pripojit IRC kanál k tomuto Jabber ID: ~s" #: mod_muc_log:479 msgid "July" msgstr "Júl" #: mod_muc_log:478 msgid "June" msgstr "Jún" #: ejabberd_web_admin:1488 ejabberd_web_admin:1715 msgid "Last Activity" msgstr "Posledná aktivita" #: mod_configure:1646 msgid "Last login" msgstr "Posledné prihlásenie" #: ejabberd_web_admin:1007 msgid "Last month" msgstr "Posledný mesiac" #: ejabberd_web_admin:1008 msgid "Last year" msgstr "Posledný rok" #: mod_configure:941 msgid "List of modules to start" msgstr "Zoznam modulov, ktoré majú byť spustené" #: mod_muc_admin:373 msgid "List of rooms" msgstr "" #: ejabberd_web_admin:1869 msgid "Listened Ports" msgstr "Otvorené portov" #: ejabberd_web_admin:2139 msgid "Listened Ports at " msgstr "Otvorené porty na " #: ejabberd_web_admin:2281 msgid "Low level update script" msgstr "Nízkoúrovňový aktualizačný skript" #: mod_muc_log:1033 msgid "Make participants list public" msgstr "Nastaviť zoznam zúčastnených ako verejný" #: mod_muc_log:1062 msgid "Make room CAPTCHA protected" msgstr "Chrániť miestnosť systémom CAPTCHA" #: mod_muc_log:1040 msgid "Make room members-only" msgstr "Nastaviť miestnosť len pre členov" #: mod_muc_log:1042 msgid "Make room moderated" msgstr "Nastaviť miestnosť ako moderovanú" #: mod_muc_log:1035 msgid "Make room password protected" msgstr "Chrániť miestnosť heslom" #: mod_muc_log:1029 msgid "Make room persistent" msgstr "Nastaviť miestnosť ako trvalú" #: mod_muc_log:1031 msgid "Make room public searchable" msgstr "Nastaviť miestnosť ako verejne prehľadávateľnú" #: mod_register:317 #, fuzzy msgid "Malformed username" msgstr "IRC prezývka" #: mod_muc_log:475 msgid "March" msgstr "Marec" #: mod_muc_log:1068 msgid "Maximum Number of Occupants" msgstr "Počet účastníkov" #: mod_muc_log:477 msgid "May" msgstr "Máj" #: mod_shared_roster:907 msgid "Members:" msgstr "Členovia:" #: mod_muc_room:1833 msgid "Membership is required to enter this room" msgstr "Pre vstup do miestnosti je potrebné byť členom" #: mod_register_web:262 msgid "" "Memorize your password, or write it in a paper placed in a safe place. In " "Jabber there isn't an automated way to recover your password if you forget " "it." msgstr "" "Zapamätajte si heslo alebo si ho zapíšte na papier. Jabber neposkytuje " "automatickú funkciu ako zistiť zabudnuté heslo. " #: ejabberd_web_admin:1954 msgid "Memory" msgstr "Pamäť" #: mod_announce:520 mod_configure:1039 mod_configure:1079 msgid "Message body" msgstr "Telo správy" #: mod_privilege:291 msgid "Message not found in forwarded payload" msgstr "" #: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102 #: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171 msgid "Middle Name" msgstr "Prostredné meno: " #: mod_irc:704 msgid "Missing 'channel' or 'server' in the data form" msgstr "" #: xmpp_stream_in:990 msgid "Missing 'from' attribute" msgstr "" #: xmpp_stream_in:472 xmpp_stream_in:993 msgid "Missing 'to' attribute" msgstr "" #: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798 msgid "Moderator privileges required" msgstr "Sú potrebné práva moderátora" #: ejabberd_web_admin:2279 msgid "Modified modules" msgstr "Modifikované moduly" #: ejabberd_web_admin:2465 ejabberd_web_admin:2620 msgid "Module" msgstr "Modul" #: gen_iq_handler:153 msgid "Module failed to handle the query" msgstr "" #: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595 msgid "Modules" msgstr "Moduly" #: ejabberd_web_admin:2168 #, fuzzy msgid "Modules at ~p" msgstr "Moduly na " #: mod_muc_log:463 msgid "Monday" msgstr "Pondelok" #: mod_muc_admin:346 mod_muc_admin:349 mod_muc_admin:365 mod_muc_admin:439 msgid "Multi-User Chat" msgstr "" #: mod_multicast:267 msgid "Multicast" msgstr "" #: mod_roster:195 msgid "Multiple elements are not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115 #: mod_vcard_sql:156 mod_vcard_sql:170 msgid "Name" msgstr "Meno" #: mod_shared_roster:896 msgid "Name:" msgstr "Meno:" #: mod_muc_room:2696 msgid "Neither 'jid' nor 'nick' attribute found" msgstr "" #: mod_muc_room:2518 mod_muc_room:2701 msgid "Neither 'role' nor 'affiliation' attribute found" msgstr "" #: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629 msgid "Never" msgstr "Nikdy" #: mod_register_web:377 msgid "New Password:" msgstr "Nové heslo:" #: mod_roster:913 mod_vcard_ldap:333 mod_vcard_ldap:346 mod_vcard_mnesia:104 #: mod_vcard_mnesia:118 mod_vcard_sql:159 mod_vcard_sql:173 msgid "Nickname" msgstr "Prezývka" #: mod_muc:722 msgid "Nickname Registration at " msgstr "Registrácia prezývky na " #: mod_muc_room:2713 msgid "Nickname ~s does not exist in the room" msgstr "Prezývka ~s v miestnosti neexistuje" #: mod_configure:1496 msgid "No 'access' found in data form" msgstr "" #: mod_configure:1455 msgid "No 'acls' found in data form" msgstr "" #: mod_muc_room:3075 msgid "No 'affiliation' attribute found" msgstr "" #: mod_muc_room:2505 #, fuzzy msgid "No 'item' element found" msgstr "Uzol nenájdený" #: mod_configure:1280 msgid "No 'modules' found in data form" msgstr "" #: mod_configure:1799 msgid "No 'password' found in data form" msgstr "" #: mod_register:148 msgid "No 'password' found in this query" msgstr "" #: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413 #: mod_configure:1433 msgid "No 'path' found in data form" msgstr "" #: mod_muc_room:3922 msgid "No 'to' attribute found in the invitation" msgstr "" #: ejabberd_web_admin:1767 msgid "No Data" msgstr "Žiadne dáta" #: ejabberd_local:181 msgid "No available resource found" msgstr "" #: mod_announce:575 msgid "No body provided for announce message" msgstr "Správa neobsahuje text" #: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289 #, fuzzy msgid "No data form found" msgstr "Uzol nenájdený" #: mod_disco:224 mod_vcard:282 msgid "No features available" msgstr "" #: mod_adhoc:239 msgid "No hook has processed this command" msgstr "" #: mod_last:218 msgid "No info about last activity found" msgstr "" #: mod_blocking:99 msgid "No items found in this query" msgstr "" #: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110 #: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580 #: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207 msgid "No module is handling this query" msgstr "" #: mod_pubsub:1541 msgid "No node specified" msgstr "" #: mod_pubsub:1426 msgid "No pending subscriptions found" msgstr "" #: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344 msgid "No privacy list with this name found" msgstr "" #: mod_private:96 msgid "No private data found in this query" msgstr "" #: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254 #: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377 #: mod_configure:1408 mod_configure:1428 mod_stats:93 #, fuzzy msgid "No running node found" msgstr "Uzol nenájdený" #: mod_disco:252 mod_vcard:265 msgid "No services available" msgstr "" #: mod_stats:101 msgid "No statistics found for this item" msgstr "" #: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255 msgid "Node already exists" msgstr "" #: nodetree_tree_sql:99 #, fuzzy msgid "Node index not found" msgstr "Uzol nenájdený" #: ejabberd_web_admin:1046 mod_irc:303 mod_irc:366 mod_muc:532 mod_muc:680 #: nodetree_dag:78 nodetree_dag:102 nodetree_dag:118 nodetree_dag:142 #: nodetree_dag:229 nodetree_tree:74 nodetree_tree:80 nodetree_tree_sql:130 #: nodetree_tree_sql:144 msgid "Node not found" msgstr "Uzol nenájdený" #: ejabberd_web_admin:1857 ejabberd_web_admin:1882 #, fuzzy msgid "Node ~p" msgstr "Uzol" #: mod_vcard:385 msgid "Nodeprep has failed" msgstr "" #: ejabberd_web_admin:1837 msgid "Nodes" msgstr "Uzly" #: ejabberd_web_admin:1622 ejabberd_web_admin:1818 ejabberd_web_admin:1828 #: ejabberd_web_admin:2238 mod_roster:907 msgid "None" msgstr "Nič" #: ejabberd_web_admin:1033 msgid "Not Found" msgstr "Nebol nájdený" #: mod_disco:296 mod_disco:370 mod_last:159 msgid "Not subscribed" msgstr "" #: mod_muc_log:483 msgid "November" msgstr "November" #: mod_configure:1212 msgid "Number of online users" msgstr "Počet online užívateľov" #: mod_configure:1202 msgid "Number of registered users" msgstr "Počet registrovaných užívateľov" #: ejabberd_web_admin:2000 ejabberd_web_admin:2010 ejabberd_web_admin:2021 #: ejabberd_web_admin:2030 ejabberd_web_admin:2040 ejabberd_web_admin:2053 #: ejabberd_web_admin:2065 ejabberd_web_admin:2081 ejabberd_web_admin:2097 #: ejabberd_web_admin:2108 ejabberd_web_admin:2118 msgid "OK" msgstr "OK" #: mod_muc_log:482 msgid "October" msgstr "Október" #: ejabberd_web_admin:1487 msgid "Offline Messages" msgstr "Offline správy" #: mod_offline:761 msgid "Offline Messages:" msgstr "Offline správy" #: mod_register_web:373 msgid "Old Password:" msgstr "Staré heslo:" #: ejabberd_web_admin:1524 ejabberd_web_admin:1698 mod_configure:1639 msgid "Online" msgstr "Online" #: ejabberd_web_admin:974 ejabberd_web_admin:1367 mod_configure:506 msgid "Online Users" msgstr "Online užívatelia" #: ejabberd_web_admin:1580 ejabberd_web_admin:1599 ejabberd_web_admin:2211 msgid "Online Users:" msgstr "Online používatelia:" #: mod_carboncopy:141 msgid "Only or tags are allowed" msgstr "" #: mod_privacy:154 msgid "Only element is allowed in this query" msgstr "" #: mod_mam:379 #, fuzzy msgid "Only members may query archives of this room" msgstr "Len moderátori majú povolené meniť tému miestnosti" #: mod_muc_room:773 msgid "" "Only moderators and participants are allowed to change the subject in this " "room" msgstr "Len moderátori a zúčastnený majú povolené meniť tému tejto miestnosti" #: mod_muc_room:778 msgid "Only moderators are allowed to change the subject in this room" msgstr "Len moderátori majú povolené meniť tému miestnosti" #: mod_muc_room:917 msgid "Only moderators can approve voice requests" msgstr "Len moderátori môžu schváliť žiadosť o Voice" #: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989 msgid "Only occupants are allowed to send messages to the conference" msgstr "Len členovia majú povolené zasielať správy do konferencie" #: mod_muc_room:457 msgid "Only occupants are allowed to send queries to the conference" msgstr "Len členovia majú povolené dotazovať sa o konferencii" #: mod_muc:422 msgid "Only service administrators are allowed to send service messages" msgstr "Iba správcovia služby majú povolené odosielanie servisných správ" #: ejabberd_web_admin:2466 ejabberd_web_admin:2621 msgid "Options" msgstr "Nastavenia" #: mod_vcard_ldap:338 mod_vcard_ldap:351 mod_vcard_mnesia:109 #: mod_vcard_mnesia:123 mod_vcard_sql:164 mod_vcard_sql:178 msgid "Organization Name" msgstr "Meno organizácie: " #: mod_vcard_ldap:339 mod_vcard_ldap:352 mod_vcard_mnesia:110 #: mod_vcard_mnesia:124 mod_vcard_sql:165 mod_vcard_sql:179 msgid "Organization Unit" msgstr "Organizačná jednotka: " #: mod_configure:508 msgid "Outgoing s2s Connections" msgstr "Odchádzajúce s2s spojenia" #: ejabberd_web_admin:1583 msgid "Outgoing s2s Connections:" msgstr "Odchádzajúce s2s spojenia:" #: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302 #: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184 #: mod_pubsub:2383 mod_pubsub:2464 mod_pubsub:3063 mod_pubsub:3206 msgid "Owner privileges required" msgstr "Sú vyžadované práva vlastníka" #: mod_offline:693 msgid "Packet" msgstr "Paket" #: mod_irc:578 msgid "Parse error" msgstr "" #: mod_configure:1299 mod_configure:1468 mod_configure:1513 msgid "Parse failed" msgstr "" #: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126 #: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036 #: mod_register:229 msgid "Password" msgstr "Heslo" #: mod_configure:1130 msgid "Password Verification" msgstr "Overenie hesla" #: mod_register_web:268 mod_register_web:381 msgid "Password Verification:" msgstr "Overenie hesla" #: mod_irc:822 msgid "Password ~b" msgstr "Heslo ~b" #: ejabberd_web_admin:1713 mod_register_web:245 mod_register_web:483 msgid "Password:" msgstr "Heslo:" #: mod_configure:998 msgid "Path to Dir" msgstr "Cesta k adresáru" #: mod_configure:952 mod_configure:964 mod_configure:976 mod_configure:987 msgid "Path to File" msgstr "Cesta k súboru" #: mod_roster:915 msgid "Pending" msgstr "Čakajúce" #: ejabberd_web_admin:994 msgid "Period: " msgstr "Čas:" #: mod_muc_admin:369 #, fuzzy msgid "Permanent rooms" msgstr "odišiel(a) z miestnosti" #: mod_adhoc:168 mod_adhoc:259 msgid "Ping" msgstr "Ping" #: mod_ping:180 msgid "Ping query is incorrect" msgstr "" #: ejabberd_web_admin:1983 msgid "" "Please note that these options will only backup the builtin Mnesia database. " "If you are using the ODBC module, you also need to backup your SQL database " "separately." msgstr "" "Prosím, berte na vedomie, že tieto nastavenia zázálohujú iba zabudovnú " "Mnesia databázu. Ak používate ODBC modul, musíte zálohovať vašu SQL databázu " "separátne." #: mod_muc_room:878 msgid "Please, wait for a while before sending new voice request" msgstr "Prosím počkate, predtým než pošlete novú žiadosť o Voice" #: mod_adhoc:274 msgid "Pong" msgstr "Pong" #: ejabberd_web_admin:2463 msgid "Port" msgstr "Port" #: mod_irc:828 msgid "Port ~b" msgstr "Port ~b" #: mod_roster:173 msgid "Possessing 'ask' attribute is not allowed by RFC6121" msgstr "" #: ejabberd_web_admin:2464 msgid "Protocol" msgstr "Protokol" #: mod_pubsub:1335 msgid "PubSub subscriber request" msgstr "Žiadosť odberateľa PubSub" #: mod_pubsub:970 msgid "Publish-Subscribe" msgstr "Publish-Subscribe" #: node_dag:81 msgid "Publishing items to collection node is not allowed" msgstr "" #: mod_muc_room:462 msgid "Queries to the conference members are not allowed in this room" msgstr "Dotazovať sa o členoch nie je v tejto miestnosti povolené" #: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146 #: mod_private:118 mod_roster:163 mod_sic:90 msgid "Query to another users is forbidden" msgstr "" #: mod_configure:889 msgid "RAM and disc copy" msgstr "Kópia RAM a disku" #: mod_configure:889 msgid "RAM copy" msgstr "Kópia RAM" #: ejabberd_web_admin:1890 msgid "RPC Call Error" msgstr "Chyba RPC volania" #: ejabberd_web_admin:806 ejabberd_web_admin:905 msgid "Raw" msgstr "Surové dáta" #: mod_announce:511 msgid "Really delete message of the day?" msgstr "Skutočne zmazať správu dňa?" #: mod_muc_room:395 mod_muc_room:443 msgid "Recipient is not in the conference room" msgstr "Príjemca sa nenachádza v konferenčnej miestnosti" #: mod_register_web:275 msgid "Register" msgstr "Zoznam kontaktov" #: mod_register_web:192 mod_register_web:210 mod_register_web:218 msgid "Register a Jabber account" msgstr "Zaregistrovať Jabber účet" #: ejabberd_web_admin:1366 msgid "Registered Users" msgstr "Registrovaní používatelia" #: ejabberd_web_admin:1577 ejabberd_web_admin:1596 msgid "Registered Users:" msgstr "Registrovaní používatelia:" #: mod_muc_admin:370 #, fuzzy msgid "Registered nicknames" msgstr "Registrovaní používatelia" #: mod_irc:535 msgid "Registration in mod_irc for " msgstr "Registrácia do mod_irc na" #: mod_configure:889 msgid "Remote copy" msgstr "Vzdialená kópia" #: mod_roster:963 msgid "Remove" msgstr "Odstrániť" #: mod_offline:765 msgid "Remove All Offline Messages" msgstr "Odstrániť všetky offline správy" #: ejabberd_web_admin:1720 mod_configure:1779 msgid "Remove User" msgstr "Odstrániť užívateľa" #: ejabberd_sm:404 msgid "Replaced by new connection" msgstr "Nahradené novým spojením" #: mod_configure:1675 msgid "Resources" msgstr "Zdroje" #: ejabberd_web_admin:1876 ejabberd_web_admin:2494 ejabberd_web_admin:2638 msgid "Restart" msgstr "Reštart" #: mod_configure:162 mod_configure:588 mod_configure:1009 msgid "Restart Service" msgstr "Reštartovať službu" #: mod_configure:151 mod_configure:619 msgid "Restore" msgstr "Obnoviť" #: mod_configure:959 msgid "Restore Backup from File at " msgstr "Obnoviť zálohu zo súboru na " #: ejabberd_web_admin:2013 msgid "" "Restore binary backup after next ejabberd restart (requires less memory):" msgstr "" "Obnoviť binárnu zálohu pri nasledujúcom reštarte ejabberd (vyžaduje menej " "pamäte)" #: ejabberd_web_admin:2003 msgid "Restore binary backup immediately:" msgstr "Okamžite obnoviť binárnu zálohu:" #: ejabberd_web_admin:2033 msgid "Restore plain text backup immediately:" msgstr "Okamžite obnoviť zálohu z textového súboru:" #: mod_muc_log:872 msgid "Room Configuration" msgstr "Nastavenia miestnosti" #: mod_muc_log:892 msgid "Room Occupants" msgstr "Ľudí v miestnosti" #: mod_muc:455 msgid "Room creation is denied by service policy" msgstr "Vytváranie miestnosti nie je povolené" #: mod_muc_log:1064 msgid "Room description" msgstr "Popis miestnosti" #: mod_muc_log:1027 msgid "Room title" msgstr "Názov miestnosti" #: mod_roster:1082 msgid "Roster" msgstr "Zoznam kontaktov" #: mod_roster:334 msgid "Roster module has failed" msgstr "" #: mod_roster:968 msgid "Roster of " msgstr "Zoznam kontaktov " #: mod_configure:1671 msgid "Roster size" msgstr "Počet kontaktov v zozname" #: ejabberd_web_admin:1838 mod_configure:510 msgid "Running Nodes" msgstr "Bežiace uzly" #: xmpp_stream_in:541 xmpp_stream_in:549 msgid "SASL negotiation is not allowed in this state" msgstr "" #: mod_muc_log:468 msgid "Saturday" msgstr "Sobota" #: mod_irc:582 msgid "Scan error" msgstr "" #: mod_configure:1303 mod_configure:1472 mod_configure:1517 #, fuzzy msgid "Scan failed" msgstr "Platná CAPTCHA." #: ejabberd_web_admin:2282 msgid "Script check" msgstr "Kontrola skriptu" #: mod_vcard:453 msgid "Search Results for " msgstr "Hľadať výsledky pre " #: mod_vcard:427 msgid "Search users in " msgstr "Hľadať užívateľov v " #: mod_announce:617 msgid "Send announcement to all online users" msgstr "Odoslať zoznam všetkým online používateľom" #: mod_announce:619 mod_configure:1032 mod_configure:1072 msgid "Send announcement to all online users on all hosts" msgstr "Odoslať oznam všetkým online používateľom na všetkých serveroch" #: mod_announce:613 msgid "Send announcement to all users" msgstr "Odoslať oznam všetkým používateľom" #: mod_announce:615 msgid "Send announcement to all users on all hosts" msgstr "Poslať oznámenie všetkým užívateľom na všetkých serveroch" #: mod_muc_log:481 msgid "September" msgstr "September" #: mod_irc_connection:648 msgid "Server Connect Failed" msgstr "" #: ejabberd_s2s:369 msgid "Server connections to local subdomains are forbidden" msgstr "" #: mod_irc:842 msgid "Server ~b" msgstr "Server ~b" #: mod_register_web:242 mod_register_web:370 mod_register_web:480 #, fuzzy msgid "Server:" msgstr "Server ~b" #: mod_announce:621 msgid "Set message of the day and send to online users" msgstr "Nastaviť správu dňa a odoslať ju online používateľom" #: mod_announce:623 msgid "Set message of the day on all hosts and send to online users" msgstr "" "Nastaviť správu dňa na všetkých serveroch a poslať ju online užívateľom" #: mod_shared_roster:784 mod_shared_roster:826 mod_shared_roster:920 msgid "Shared Roster Groups" msgstr "Skupiny pre zdieľaný zoznam kontaktov" #: ejabberd_web_admin:1016 msgid "Show Integral Table" msgstr "Zobraziť kompletnú tabuľku" #: ejabberd_web_admin:1013 msgid "Show Ordinary Table" msgstr "Zobraziť bežnú tabuľku" #: mod_configure:164 mod_configure:590 mod_configure:1049 msgid "Shut Down Service" msgstr "Vypnúť službu" #: mod_register_web:258 msgid "" "Some Jabber clients can store your password in the computer, but you should " "do this only in your personal computer for safety reasons." msgstr "" "Niektorí Jabber klenti môžu ukladať heslá v počítači. Používajte túto " "funkciu len ak veríte, že sú tam v bezpečí. " #: ejabberd_web_admin:2518 ejabberd_web_admin:2654 msgid "Start" msgstr "Štart" #: mod_configure:142 mod_configure:605 msgid "Start Modules" msgstr "Spustiť moduly" #: mod_configure:936 msgid "Start Modules at " msgstr "Spustiť moduly na " #: ejabberd_web_admin:1023 ejabberd_web_admin:1871 mod_muc_admin:366 msgid "Statistics" msgstr "Štatistiky" #: ejabberd_web_admin:2199 msgid "Statistics of ~p" msgstr "Štatistiky ~p" #: ejabberd_web_admin:1878 ejabberd_web_admin:2498 ejabberd_web_admin:2642 msgid "Stop" msgstr "Zastaviť" #: mod_configure:145 mod_configure:607 msgid "Stop Modules" msgstr "Zastaviť moduly" #: mod_configure:918 msgid "Stop Modules at " msgstr "Zastaviť moduly na " #: ejabberd_web_admin:1839 mod_configure:511 msgid "Stopped Nodes" msgstr "Zastavené uzly" #: ejabberd_web_admin:1952 msgid "Storage Type" msgstr "Typ úložiska" #: ejabberd_web_admin:1993 msgid "Store binary backup:" msgstr "Uložiť binárnu zálohu:" #: ejabberd_web_admin:2023 msgid "Store plain text backup:" msgstr "Uložiť zálohu do textového súboru:" #: mod_announce:516 mod_configure:1036 mod_configure:1076 msgid "Subject" msgstr "Predmet" #: ejabberd_web_admin:774 ejabberd_web_admin:814 ejabberd_web_admin:879 #: ejabberd_web_admin:944 ejabberd_web_admin:1963 mod_shared_roster:933 msgid "Submit" msgstr "Odoslať" #: ejabberd_web_admin:762 ejabberd_web_admin:801 ejabberd_web_admin:867 #: ejabberd_web_admin:900 ejabberd_web_admin:936 ejabberd_web_admin:1421 #: ejabberd_web_admin:1704 ejabberd_web_admin:1860 ejabberd_web_admin:1894 #: ejabberd_web_admin:1974 ejabberd_web_admin:2144 ejabberd_web_admin:2173 #: ejabberd_web_admin:2270 mod_offline:681 mod_roster:971 mod_shared_roster:830 #: mod_shared_roster:925 msgid "Submitted" msgstr "Odoslané" #: mod_roster:914 msgid "Subscription" msgstr "Prihlásenie" #: mod_muc_room:3708 msgid "Subscriptions are not allowed" msgstr "" #: mod_muc_log:469 msgid "Sunday" msgstr "Nedeľa" #: mod_muc_room:998 mod_muc_room:1843 mod_muc_room:3737 msgid "That nickname is already in use by another occupant" msgstr "Prezývka je už používaná iným členom" #: mod_muc:745 mod_muc_room:1004 mod_muc_room:1852 mod_muc_room:3740 msgid "That nickname is registered by another person" msgstr "Prezývka je už zaregistrovaná inou osobou" #: ejabberd_captcha:256 msgid "The CAPTCHA is valid." msgstr "Platná CAPTCHA." #: mod_muc_room:653 mod_muc_room:3672 mod_register:190 msgid "The CAPTCHA verification has failed" msgstr "Overenie pomocou CAPTCHA zlihalo" #: mod_muc_room:302 msgid "The feature requested is not supported by the conference" msgstr "" #: mod_register:308 mod_register:366 msgid "The password contains unacceptable characters" msgstr "" #: mod_register:311 mod_register:370 msgid "The password is too weak" msgstr "heslo je" #: mod_register_web:141 msgid "The password of your Jabber account was successfully changed." msgstr "Heslo k Jabber účtu bolo úspešne zmenené." #: mod_register:160 mod_vcard:219 msgid "The query is only allowed from local users" msgstr "" #: mod_roster:203 msgid "The query must not contain elements" msgstr "" #: mod_privacy:280 msgid "" "The stanza MUST contain only one element, one element, " "or one element" msgstr "" #: mod_register_web:146 msgid "There was an error changing the password: " msgstr "Pri zmene hesla nastala chyba: " #: mod_register_web:116 msgid "There was an error creating the account: " msgstr "Pri vytváraní účtu nastala chyba: " #: mod_register_web:130 msgid "There was an error deleting the account: " msgstr "Pri rušení účtu nastala chyba:" #: mod_register_web:236 msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." msgstr "" "Veľké a malé písmená sa nerozlišujú: macbeth je to isté ako MacBeth a " "Macbeth." #: mod_register_web:220 msgid "" "This page allows to create a Jabber account in this Jabber server. Your JID " "(Jabber IDentifier) will be of the form: username@server. Please read " "carefully the instructions to fill correctly the fields." msgstr "" "Táto stránka umožňuje refistrovať Jabber účet na tomto serveri. Vaše JID " "(Jabber IDentifikátor) bude vo formáte: užívateľ@server. Pozorne sledujte " "inštrukcie, aby ste údaje vypnili správne." #: mod_register_web:470 msgid "This page allows to unregister a Jabber account in this Jabber server." msgstr "" "Na tejto stránke si môžete zrušiť Jabber účet registrovaný na tomto serveri." #: mod_muc_log:1038 msgid "This room is not anonymous" msgstr "Táto miestnosť nie je anonymná" #: mod_muc_log:466 msgid "Thursday" msgstr "Štvrtok" #: mod_offline:690 msgid "Time" msgstr "Čas" #: mod_configure:1014 mod_configure:1054 msgid "Time delay" msgstr "Časový posun" #: mod_offline:692 msgid "To" msgstr "Pre" #: mod_register:215 msgid "To register, visit ~s" msgstr "" #: mod_configure:709 msgid "To ~s" msgstr "Pre ~s" #: ejabberd_oauth:439 msgid "Token TTL" msgstr "" #: xmpp_stream_in:463 msgid "Too long value of 'xml:lang' attribute" msgstr "" #: mod_muc_room:2541 mod_muc_room:3081 msgid "Too many elements" msgstr "" #: mod_privacy:164 msgid "Too many elements" msgstr "" #: mod_muc_room:1924 mod_register:240 msgid "Too many CAPTCHA requests" msgstr "Príliš veľa žiadostí o CAPTCHA" #: mod_proxy65_service:223 msgid "Too many active bytestreams" msgstr "" #: mod_stream_mgmt:205 msgid "Too many unacked stanzas" msgstr "" #: mod_muc_room:1802 #, fuzzy msgid "Too many users in this conference" msgstr "Žiadosti o Voice nie sú povolené v tejto konferencii" #: mod_register:355 msgid "Too many users registered" msgstr "" #: mod_muc_admin:368 #, fuzzy msgid "Total rooms" msgstr "Diskusné miestnosti" #: mod_muc_room:173 msgid "Traffic rate limit is exceeded" msgstr "Bol prekročený prenosový limit" #: ejabberd_web_admin:2219 msgid "Transactions Aborted:" msgstr "Transakcie zrušená" #: ejabberd_web_admin:2215 msgid "Transactions Committed:" msgstr "Transakcie potvrdená" #: ejabberd_web_admin:2227 msgid "Transactions Logged:" msgstr "Transakcie zaznamenaná" #: ejabberd_web_admin:2223 msgid "Transactions Restarted:" msgstr "Transakcie reštartovaná" #: mod_muc_log:464 msgid "Tuesday" msgstr "Utorok" #: mod_muc_room:1933 mod_register:244 msgid "Unable to generate a CAPTCHA" msgstr "Nepodarilo sa vygenerovat CAPTCHA" #: ejabberd_service:120 msgid "Unable to register route on existing local domain" msgstr "" #: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241 #: ejabberd_web_admin:253 msgid "Unauthorized" msgstr "Neautorizovaný" #: mod_announce:485 mod_configure:830 mod_configure:1758 msgid "Unexpected action" msgstr "" #: mod_register_web:488 msgid "Unregister" msgstr "Zrušiť účet" #: mod_register_web:197 mod_register_web:460 mod_register_web:468 msgid "Unregister a Jabber account" msgstr "Zrušiť Jabber účet" #: mod_mam:526 msgid "Unsupported element" msgstr "" #: mod_mix:119 msgid "Unsupported MIX query" msgstr "" #: ejabberd_web_admin:1872 ejabberd_web_admin:2285 msgid "Update" msgstr "Aktualizovať" #: mod_announce:625 msgid "Update message of the day (don't send)" msgstr "Aktualizovať správu dňa (neodosielať)" #: mod_announce:627 msgid "Update message of the day on all hosts (don't send)" msgstr "Upraviť správu dňa na všetkých serveroch" #: ejabberd_web_admin:2278 msgid "Update plan" msgstr "Aktualizovať plán" #: ejabberd_web_admin:2280 msgid "Update script" msgstr "Aktualizované skripty" #: ejabberd_web_admin:2267 #, fuzzy msgid "Update ~p" msgstr "Aktualizovať " #: ejabberd_web_admin:2203 msgid "Uptime:" msgstr "Uptime:" #: xmpp_stream_out:533 #, fuzzy msgid "Use of STARTTLS forbidden" msgstr "Je vyžadované použitie STARTTLS " #: xmpp_stream_in:573 xmpp_stream_out:527 xmpp_stream_out:603 msgid "Use of STARTTLS required" msgstr "Je vyžadované použitie STARTTLS " #: ejabberd_web_admin:1430 ejabberd_web_admin:1486 mod_register:225 #: mod_vcard_ldap:328 mod_vcard_mnesia:99 mod_vcard_sql:154 msgid "User" msgstr "Užívateľ" #: ejabberd_oauth:428 msgid "User (jid)" msgstr "" #: mod_configure:308 mod_configure:505 msgid "User Management" msgstr "Správa užívateľov" #: mod_register:345 msgid "User already exists" msgstr "" #: mod_echo:138 msgid "User part of JID in 'from' is empty" msgstr "" #: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106 #, fuzzy msgid "User session not found" msgstr "Uzol nenájdený" #: mod_stream_mgmt:561 mod_stream_mgmt:583 msgid "User session terminated" msgstr "" #: ejabberd_web_admin:1700 #, fuzzy msgid "User ~s" msgstr "Používateľ " #: mod_register_web:230 mod_register_web:366 mod_register_web:476 msgid "Username:" msgstr "IRC prezývka" #: ejabberd_web_admin:959 ejabberd_web_admin:967 msgid "Users" msgstr "Používatelia" #: ejabberd_web_admin:990 msgid "Users Last Activity" msgstr "Posledná aktivita používateľa" #: mod_register:375 msgid "Users are not allowed to register accounts so quickly" msgstr "Nieje dovolené vytvárať účty tak rýchlo po sebe" #: mod_roster:954 msgid "Validate" msgstr "Overiť" #: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802 #: mod_pubsub:895 mod_push:249 msgid "Value 'get' of 'type' attribute is not allowed" msgstr "" #: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270 #: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504 #: mod_muc:539 mod_muc:561 mod_muc:571 mod_muc_room:3597 mod_muc_room:3641 #: mod_proxy65_service:142 mod_proxy65_service:160 mod_proxy65_service:167 #: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93 #: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62 msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription:237 pubsub_subscription_sql:202 msgid "Value of '~s' should be boolean" msgstr "" #: pubsub_subscription:215 pubsub_subscription_sql:180 msgid "Value of '~s' should be datetime string" msgstr "" #: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174 #: pubsub_subscription_sql:192 msgid "Value of '~s' should be integer" msgstr "" #: ejabberd_web_admin:950 msgid "Virtual Hosts" msgstr "Virtuálne servery" #: mod_muc_room:992 msgid "Visitors are not allowed to change their nicknames in this room" msgstr "V tejto miestnosti nieje povolené meniť prezývky" #: mod_muc_room:785 msgid "Visitors are not allowed to send messages to all occupants" msgstr "" "Návštevníci nemajú povolené zasielať správy všetkým prihláseným do " "konferencie" #: mod_muc_room:3879 msgid "Voice request" msgstr "Žiadosť o Voice" #: mod_muc_room:885 msgid "Voice requests are disabled in this conference" msgstr "Žiadosti o Voice nie sú povolené v tejto konferencii" #: mod_muc_log:465 msgid "Wednesday" msgstr "Streda" #: mod_register_web:255 msgid "You can later change your password using a Jabber client." msgstr "Neskôr si heslo môžete zmeniť pomocou Jabber klienta." #: mod_muc_room:1830 msgid "You have been banned from this room" msgstr "Boli ste vylúčený z tejto miestnosti" #: mod_muc_room:1811 msgid "You have joined too many conferences" msgstr "" #: mod_muc:777 msgid "You must fill in field \"Nickname\" in the form" msgstr "Musíte vyplniť políčko \"Prezývka\" vo formulári" #: mod_register:222 msgid "You need a client that supports x:data and CAPTCHA to register" msgstr "Na registráciu prezývky potrebujete klienta podporujúceho z x:data" #: mod_muc:731 msgid "You need a client that supports x:data to register the nickname" msgstr "Na registráciu prezývky potrebujete klienta podporujúceho z x:data" #: mod_irc:547 msgid "You need an x:data capable client to configure mod_irc settings" msgstr "Pre konfiguráciu mod_irc potrebujete klienta podporujúceho x:data" #: mod_vcard:443 msgid "You need an x:data capable client to search" msgstr "Na vyhľadávanie potrebujete klienta podporujúceho x:data" #: mod_pubsub:1504 #, fuzzy msgid "You're not allowed to create nodes" msgstr "Nieje povolené posielať súkromné správy" #: mod_register_web:111 msgid "Your Jabber account was successfully created." msgstr "Jabber účet bol úspešne vytvorený." #: mod_register_web:125 msgid "Your Jabber account was successfully deleted." msgstr "Váš Jabber účet bol úspešne odstránený." #: ejabberd_c2s:651 ejabberd_c2s:811 msgid "Your active privacy list has denied the routing of this stanza." msgstr "Aktívny list súkromia zbránil v smerovaní tejto stanzy." #: mod_offline:576 msgid "" "Your contact offline message queue is full. The message has been discarded." msgstr "Fronta offline správ tohoto kontaktu je plná. Správa bola zahodená." #: ejabberd_captcha:103 msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" msgstr "Správa určená pre ~s bola zablokovaná. Oblokovať ju môžete na ~s" #: mod_irc:455 msgid "ejabberd IRC module" msgstr "ejabberd IRC modul" #: mod_muc:473 msgid "ejabberd MUC module" msgstr "ejabberd MUC modul" #: mod_multicast:272 msgid "ejabberd Multicast service" msgstr "" #: mod_pubsub:1067 msgid "ejabberd Publish-Subscribe module" msgstr "ejabberd Publish-Subscribe modul" #: mod_proxy65_service:170 msgid "ejabberd SOCKS5 Bytestreams module" msgstr "ejabberd SOCKS5 Bytestreams modul" #: ejabberd_web_admin:311 ejabberd_web_admin:343 msgid "ejabberd Web Admin" msgstr "ejabberd Web Admin" #: mod_vcard:239 msgid "ejabberd vCard module" msgstr "ejabberd vCard modul" #: mod_muc_log:380 mod_muc_log:383 msgid "has been banned" msgstr "bol(a) zablokovaný(á)" #: ejabberd_sm:407 mod_configure:1559 mod_muc_log:387 mod_muc_log:390 msgid "has been kicked" msgstr "bol(a) vyhodený(á) z miestnosti" #: mod_muc_log:405 msgid "has been kicked because of a system shutdown" msgstr "bol vyhodený(á) kvôli reštartu systému" #: mod_muc_log:395 msgid "has been kicked because of an affiliation change" msgstr "bol vyhodený(á) kvôli zmene priradenia" #: mod_muc_log:400 msgid "has been kicked because the room has been changed to members-only" msgstr "bol vyhodený(á), pretože miestnosť bola vyhradená len pre členov" #: mod_muc_log:410 msgid "is now known as" msgstr "sa premenoval(a) na" #: mod_muc_log:370 msgid "joins the room" msgstr "vstúpil(a) do miestnosti" #: mod_muc_log:373 mod_muc_log:376 msgid "leaves the room" msgstr "odišiel(a) z miestnosti" #: mod_muc_room:3856 msgid "private, " msgstr "súkromná, " #: mod_muc_room:3955 msgid "the password is" msgstr "heslo je" #: mod_vcard:292 msgid "vCard User Search" msgstr "Hľadať užívateľov vo vCard" #: ejabberd_web_admin:932 msgid "~s access rule configuration" msgstr "~s konfigurácia prístupového pravidla" #: mod_muc_room:3948 msgid "~s invites you to the room ~s" msgstr "~s Vás pozýva do miestnosti ~s" #: mod_offline:677 msgid "~s's Offline Messages Queue" msgstr "~s Offline správy" #~ msgid "No resource provided" #~ msgstr "Nebol poskytnutý žiadny zdroj" #, fuzzy #~ msgid "Server" #~ msgstr "Server ~b" #~ msgid "Jabber ID ~s is invalid" #~ msgstr "Jabber ID ~s je neplatné" #~ msgid "Invalid affiliation: ~s" #~ msgstr "Neplatné priradenie: ~s" #~ msgid "Invalid role: ~s" #~ msgstr "Neplatná rola: ~s" #~ msgid "No limit" #~ msgstr "Bez limitu" #~ msgid "Present real Jabber IDs to" #~ msgstr "Zobrazovať skutočné Jabber ID" #~ msgid "moderators only" #~ msgstr "moderátorom" #~ msgid "anyone" #~ msgstr "všetkým" #, fuzzy #~ msgid "Moderator" #~ msgstr "moderátorom" #~ msgid "nobody" #~ msgstr "nikto" #~ msgid "Allow visitors to send voice requests" #~ msgstr "Povoliť používateľom posielanie pozvánok" #~ msgid "Minimum interval between voice requests (in seconds)" #~ msgstr "Minimum interval between voice requests (in seconds)" #~ msgid "Exclude Jabber IDs from CAPTCHA challenge" #~ msgstr "Nepoužívať CAPTCHA pre nasledujúce Jabber ID" #~ msgid "You need an x:data capable client to configure room" #~ msgstr "Na konfiguráciu miestnosti potrebujete klienta podporujúceho x:data" #~ msgid "Number of occupants" #~ msgstr "Počet zúčastnených" #~ msgid "User JID" #~ msgstr "Používateľ " #~ msgid "Grant voice to this person?" #~ msgstr "Prideltiť Voice tejto osobe?" #~ msgid "Node ID" #~ msgstr "ID uzlu" #~ msgid "Subscriber Address" #~ msgstr "Adresa odberateľa" #~ msgid "Allow this Jabber ID to subscribe to this pubsub node?" #~ msgstr "Dovoliť tomuto Jabber ID odoberať PubSub uzol?" #~ msgid "Deliver payloads with event notifications" #~ msgstr "Doručiť náklad s upozornením na udalosť" #~ msgid "Deliver event notifications" #~ msgstr "Doručiť oznamy o udalosti" #~ msgid "Notify subscribers when the node configuration changes" #~ msgstr "Upozorniť prihlásených používateľov na zmenu nastavenia uzlu" #~ msgid "Notify subscribers when the node is deleted" #~ msgstr "Upozorniť prihlásených používateľov na zmazanie uzlu" #~ msgid "Notify subscribers when items are removed from the node" #~ msgstr "Upozorniť prihlásených používateľov na odstránenie položiek z uzlu" #~ msgid "Persist items to storage" #~ msgstr "Uložiť položky natrvalo do úložiska" #~ msgid "A friendly name for the node" #~ msgstr "Prístupný názov pre uzol" #~ msgid "Max # of items to persist" #~ msgstr "Maximálny počet položiek, ktoré je možné natrvalo uložiť" #~ msgid "Whether to allow subscriptions" #~ msgstr "Povoliť prihlasovanie" #~ msgid "Specify the access model" #~ msgstr "Uveďte model prístupu" #~ msgid "Roster groups allowed to subscribe" #~ msgstr "Skupiny kontaktov, ktoré môžu odoberať" #~ msgid "Specify the publisher model" #~ msgstr "Špecifikovať model publikovania" #~ msgid "Purge all items when the relevant publisher goes offline" #~ msgstr "" #~ "Odstrániť všetky relevantné položky, keď užívateľ prejde do módu offline" #~ msgid "Specify the event message type" #~ msgstr "Uveďte typ pre správu o udalosti" #~ msgid "Max payload size in bytes" #~ msgstr "Maximálny náklad v bajtoch" #~ msgid "When to send the last published item" #~ msgstr "Kedy odoslať posledne publikovanú položku" #~ msgid "Only deliver notifications to available users" #~ msgstr "Doručovať upozornenia len aktuálne prihláseným používateľom" #~ msgid "The collections with which a node is affiliated" #~ msgstr "Kolekcie asociované s uzlom" #~ msgid "Fill in fields to search for any matching Jabber User" #~ msgstr "Vyplnte políčka pre vyhľadávanie Jabber užívateľa" #~ msgid "Outgoing s2s Servers:" #~ msgstr "Odchádzajúce s2s servery:" #~ msgid "Delete" #~ msgstr "Zmazať" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message" #~ msgstr "Účastník bol vyhodený z miestnosti, pretože poslal chybovú správu" #~ msgid "" #~ "This participant is kicked from the room because he sent an error message " #~ "to another participant" #~ msgstr "" #~ "Účastník bol vyhodený z miestnosti, pretože poslal chybovú správu inému " #~ "účastníkovi" #~ msgid "" #~ "This participant is kicked from the room because he sent an error presence" #~ msgstr "" #~ "Účastník bol vyhodený z miestnosti, pretože poslal chybovú správu o stave" #~ msgid "Encodings" #~ msgstr "Kódovania" #~ msgid "(Raw)" #~ msgstr "(Raw)" #~ msgid "Specified nickname is already registered" #~ msgstr "Zadaná prezývka je už registrovaná" #~ msgid "Size" #~ msgstr "Veľkosť" #~ msgid "You must fill in field \"nick\" in the form" #~ msgstr "Musíte vyplniť políčko \"prezývka\" vo formulári" ejabberd-18.01/src/0000755000232200023220000000000013225664356014432 5ustar debalancedebalanceejabberd-18.01/src/xmpp_stream_pkix.erl0000644000232200023220000002065613225664356020541 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 13 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(xmpp_stream_pkix). %% API -export([authenticate/1, authenticate/2, get_cert_domains/1, format_error/1]). -include("xmpp.hrl"). -include_lib("public_key/include/public_key.hrl"). -include("XmppAddr.hrl"). -type cert() :: #'OTPCertificate'{}. %%%=================================================================== %%% API %%%=================================================================== -spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state()) -> {ok, binary()} | {error, atom(), binary()}. authenticate(State) -> authenticate(State, <<"">>). -spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state(), binary()) -> {ok, binary()} | {error, atom(), binary()}. authenticate(#{xmlns := ?NS_SERVER, socket := Socket} = State, Authzid) -> Peer = maps:get(remote_server, State, Authzid), case verify_cert(Socket) of {ok, Cert} -> case ejabberd_idna:domain_utf8_to_ascii(Peer) of false -> {error, idna_failed, Peer}; AsciiPeer -> case lists:any( fun(D) -> match_domain(AsciiPeer, D) end, get_cert_domains(Cert)) of true -> {ok, Peer}; false -> {error, hostname_mismatch, Peer} end end; {error, Reason} -> {error, Reason, Peer} end; authenticate(#{xmlns := ?NS_CLIENT, socket := Socket, lserver := LServer}, Authzid) -> JID = try jid:decode(Authzid) catch _:{bad_jid, <<>>} -> jid:make(LServer); _:{bad_jid, _} -> {error, invalid_authzid, Authzid} end, case JID of #jid{user = User} -> case verify_cert(Socket) of {ok, Cert} -> JIDs = get_xmpp_addrs(Cert), get_username(JID, JIDs, LServer); {error, Reason} -> {error, Reason, User} end; Err -> Err end. format_error(idna_failed) -> {'bad-protocol', <<"Remote domain is not an IDN hostname">>}; format_error(hostname_mismatch) -> {'not-authorized', <<"Certificate host name mismatch">>}; format_error(jid_mismatch) -> {'not-authorized', <<"Certificate JID mismatch">>}; format_error(get_cert_failed) -> {'bad-protocol', <<"Failed to get peer certificate">>}; format_error(invalid_authzid) -> {'invalid-authzid', <<"Malformed JID">>}; format_error(Other) -> {'not-authorized', erlang:atom_to_binary(Other, utf8)}. -spec get_cert_domains(cert()) -> [binary()]. get_cert_domains(Cert) -> TBSCert = Cert#'OTPCertificate'.tbsCertificate, {rdnSequence, Subject} = TBSCert#'OTPTBSCertificate'.subject, Extensions = TBSCert#'OTPTBSCertificate'.extensions, get_domain_from_subject(lists:flatten(Subject)) ++ get_domains_from_san(Extensions). %%%=================================================================== %%% Internal functions %%%=================================================================== -spec verify_cert(xmpp_socket:socket()) -> {ok, cert()} | {error, atom()}. verify_cert(Socket) -> case xmpp_socket:get_peer_certificate(Socket, otp) of {ok, Cert} -> case xmpp_socket:get_verify_result(Socket) of 0 -> {ok, Cert}; VerifyRes -> %% TODO: return atomic errors %% This should be improved in fast_tls Reason = fast_tls:get_cert_verify_string(VerifyRes, Cert), {error, erlang:binary_to_atom(Reason, utf8)} end; {error, _Reason} -> {error, get_cert_failed}; error -> {error, get_cert_failed} end. -spec get_domain_from_subject([#'AttributeTypeAndValue'{}]) -> [binary()]. get_domain_from_subject(AttrVals) -> case lists:keyfind(?'id-at-commonName', #'AttributeTypeAndValue'.type, AttrVals) of #'AttributeTypeAndValue'{value = {_, S}} -> try jid:decode(iolist_to_binary(S)) of #jid{luser = <<"">>, lresource = <<"">>, lserver = Domain} -> [Domain]; _ -> [] catch _:{bad_jid, _} -> [] end; _ -> [] end. -spec get_domains_from_san([#'Extension'{}] | asn1_NOVALUE) -> [binary()]. get_domains_from_san(Extensions) when is_list(Extensions) -> case lists:keyfind(?'id-ce-subjectAltName', #'Extension'.extnID, Extensions) of #'Extension'{extnValue = Vals} -> lists:flatmap( fun({dNSName, S}) -> [iolist_to_binary(S)]; ({otherName, AnotherName}) -> case decode_xmpp_addr(AnotherName) of {ok, #jid{luser = <<"">>, lresource = <<"">>, lserver = Domain}} -> case ejabberd_idna:domain_utf8_to_ascii(Domain) of false -> []; ASCIIDomain -> [ASCIIDomain] end; _ -> [] end; (_) -> [] end, Vals); _ -> [] end; get_domains_from_san(_) -> []. -spec decode_xmpp_addr(#'AnotherName'{}) -> {ok, jid()} | error. decode_xmpp_addr(#'AnotherName'{'type-id' = ?'id-on-xmppAddr', value = XmppAddr}) -> try 'XmppAddr':decode('XmppAddr', XmppAddr) of {ok, JIDStr} -> try {ok, jid:decode(iolist_to_binary(JIDStr))} catch _:{bad_jid, _} -> error end; _ -> error catch _:_ -> error end; decode_xmpp_addr(_) -> error. -spec get_xmpp_addrs(cert()) -> [jid()]. get_xmpp_addrs(Cert) -> TBSCert = Cert#'OTPCertificate'.tbsCertificate, case TBSCert#'OTPTBSCertificate'.extensions of Extensions when is_list(Extensions) -> case lists:keyfind(?'id-ce-subjectAltName', #'Extension'.extnID, Extensions) of #'Extension'{extnValue = Vals} -> lists:flatmap( fun({otherName, AnotherName}) -> case decode_xmpp_addr(AnotherName) of {ok, JID} -> [JID]; _ -> [] end; (_) -> [] end, Vals); _ -> [] end; _ -> [] end. match_domain(Domain, Domain) -> true; match_domain(Domain, Pattern) -> DLabels = str:tokens(Domain, <<".">>), PLabels = str:tokens(Pattern, <<".">>), match_labels(DLabels, PLabels). match_labels([], []) -> true; match_labels([], [_ | _]) -> false; match_labels([_ | _], []) -> false; match_labels([DL | DLabels], [PL | PLabels]) -> case lists:all(fun (C) -> $a =< C andalso C =< $z orelse $0 =< C andalso C =< $9 orelse C == $- orelse C == $* end, binary_to_list(PL)) of true -> Regexp = ejabberd_regexp:sh_to_awk(PL), case ejabberd_regexp:run(DL, Regexp) of match -> match_labels(DLabels, PLabels); nomatch -> false end; false -> false end. -spec get_username(jid(), [jid()], binary()) -> {ok, binary()} | {error, jid_mismatch, binary()}. get_username(#jid{user = User, lserver = LS}, _, LServer) when LS /= LServer -> %% The user provided JID from different domain {error, jid_mismatch, User}; get_username(#jid{user = <<>>}, [#jid{user = U, lserver = LS}], LServer) when U /= <<>> andalso LS == LServer -> %% The user didn't provide JID or username, and there is only %% one 'non-global' JID matching current domain {ok, U}; get_username(#jid{user = User, luser = LUser}, JIDs, LServer) when User /= <<>> -> %% The user provided username lists:foldl( fun(_, {ok, _} = OK) -> OK; (#jid{user = <<>>, lserver = LS}, _) when LS == LServer -> %% Found "global" JID in the certficate %% (i.e. in the form of 'domain.com') %% within current domain, so we force matching {ok, User}; (#jid{luser = LU, lserver = LS}, _) when LU == LUser, LS == LServer -> %% Found exact JID matching {ok, User}; (_, Err) -> Err end, {error, jid_mismatch, User}, JIDs); get_username(#jid{user = User}, _, _) -> %% Nothing from above is true {error, jid_mismatch, User}. ejabberd-18.01/src/mod_vcard.erl0000644000232200023220000005064713225664356017110 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_vcard.erl %%% Author : Alexey Shchepin %%% Purpose : Vcard management %%% Created : 2 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_vcard). -author('alexey@process-one.net'). -protocol({xep, 54, '1.2'}). -protocol({xep, 55, '1.3'}). -behaviour(gen_server). -behaviour(gen_mod). -export([start/2, stop/1, get_sm_features/5, process_local_iq/1, process_sm_iq/1, string2lower/1, remove_user/2, export/1, import_info/0, import/5, import_start/2, depends/2, process_search/1, process_vcard/1, get_vcard/2, disco_items/5, disco_features/5, disco_identity/5, vcard_iq_set/1, mod_opt_type/1, set_vcard/3, make_vcard_search/4]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_vcard.hrl"). -include("translate.hrl"). -define(JUD_MATCHES, 30). -define(VCARD_CACHE, vcard_cache). -callback init(binary(), gen_mod:opts()) -> any(). -callback stop(binary()) -> any(). -callback import(binary(), binary(), [binary()]) -> ok. -callback get_vcard(binary(), binary()) -> {ok, [xmlel()]} | error. -callback set_vcard(binary(), binary(), xmlel(), #vcard_search{}) -> {atomic, any()}. -callback search_fields(binary()) -> [{binary(), binary()}]. -callback search_reported(binary()) -> [{binary(), binary()}]. -callback search(binary(), [{binary(), [binary()]}], boolean(), infinity | pos_integer()) -> [{binary(), binary()}]. -callback remove_user(binary(), binary()) -> {atomic, any()}. -callback is_search_supported(binary()) -> boolean(). -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). -record(state, {hosts :: [binary()], server_host :: binary()}). %%==================================================================== %% gen_mod callbacks %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD, ?MODULE, process_sm_iq, IQDisc), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_iq_set, 50), MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"vjud.@HOST@">>), Search = gen_mod:get_opt(search, Opts, false), if Search -> lists:foreach( fun(MyHost) -> ejabberd_hooks:add( disco_local_items, MyHost, ?MODULE, disco_items, 100), ejabberd_hooks:add( disco_local_features, MyHost, ?MODULE, disco_features, 100), ejabberd_hooks:add( disco_local_identity, MyHost, ?MODULE, disco_identity, 100), gen_iq_handler:add_iq_handler( ejabberd_local, MyHost, ?NS_SEARCH, ?MODULE, process_search, IQDisc), gen_iq_handler:add_iq_handler( ejabberd_local, MyHost, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), gen_iq_handler:add_iq_handler( ejabberd_local, MyHost, ?NS_DISCO_ITEMS, mod_disco, process_local_iq_items, IQDisc), gen_iq_handler:add_iq_handler( ejabberd_local, MyHost, ?NS_DISCO_INFO, mod_disco, process_local_iq_info, IQDisc), case Mod:is_search_supported(Host) of false -> ?WARNING_MSG("vcard search functionality is " "not implemented for ~s backend", [gen_mod:db_type(Host, Opts, ?MODULE)]); true -> ejabberd_router:register_route(MyHost, Host) end end, MyHosts); true -> ok end, {ok, #state{hosts = MyHosts, server_host = Host}}. handle_call(_Call, _From, State) -> {noreply, State}. handle_cast(Cast, State) -> ?WARNING_MSG("unexpected cast: ~p", [Cast]), {noreply, State}. handle_info({route, Packet}, State) -> case catch do_route(Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); _ -> ok end, {noreply, State}; handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, #state{hosts = MyHosts, server_host = Host}) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_set, 50), Mod = gen_mod:db_mod(Host, ?MODULE), Mod:stop(Host), lists:foreach( fun(MyHost) -> ejabberd_router:unregister_route(MyHost), ejabberd_hooks:delete(disco_local_items, MyHost, ?MODULE, disco_items, 100), ejabberd_hooks:delete(disco_local_features, MyHost, ?MODULE, disco_features, 100), ejabberd_hooks:delete(disco_local_identity, MyHost, ?MODULE, disco_identity, 100), gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_SEARCH), gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO) end, MyHosts). code_change(_OldVsn, State, _Extra) -> {ok, State}. do_route(#iq{} = IQ) -> ejabberd_router:process_iq(IQ); do_route(_) -> ok. -spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]}, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | empty | {result, [binary()]}. get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> Acc; get_sm_features(Acc, _From, _To, Node, _Lang) -> case Node of <<"">> -> case Acc of {result, Features} -> {result, [?NS_DISCO_INFO, ?NS_VCARD | Features]}; empty -> {result, [?NS_DISCO_INFO, ?NS_VCARD]} end; _ -> Acc end. -spec process_local_iq(iq()) -> iq(). process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_local_iq(#iq{type = get, lang = Lang} = IQ) -> Desc = translate:translate(Lang, <<"Erlang Jabber Server">>), xmpp:make_iq_result( IQ, #vcard_temp{fn = <<"ejabberd">>, url = ?EJABBERD_URI, desc = <>, bday = <<"2002-11-16">>}). -spec process_sm_iq(iq()) -> iq(). process_sm_iq(#iq{type = set, lang = Lang, from = From} = IQ) -> #jid{lserver = LServer} = From, case lists:member(LServer, ?MYHOSTS) of true -> case ejabberd_hooks:run_fold(vcard_iq_set, LServer, IQ, []) of drop -> ignore; #stanza_error{} = Err -> xmpp:make_error(IQ, Err); _ -> xmpp:make_iq_result(IQ) end; false -> Txt = <<"The query is only allowed from local users">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)) end; process_sm_iq(#iq{type = get, from = From, to = To, lang = Lang} = IQ) -> #jid{luser = LUser, lserver = LServer} = To, case get_vcard(LUser, LServer) of error -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)); [] -> xmpp:make_iq_result(IQ, #vcard_temp{}); Els -> IQ#iq{type = result, to = From, from = To, sub_els = Els} end. -spec process_vcard(iq()) -> iq(). process_vcard(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_vcard(#iq{type = get, lang = Lang} = IQ) -> Desc = translate:translate(Lang, <<"ejabberd vCard module">>), xmpp:make_iq_result( IQ, #vcard_temp{fn = <<"ejabberd/mod_vcard">>, url = ?EJABBERD_URI, desc = <>}). -spec process_search(iq()) -> iq(). process_search(#iq{type = get, to = To, lang = Lang} = IQ) -> ServerHost = ejabberd_router:host_of_route(To#jid.lserver), xmpp:make_iq_result(IQ, mk_search_form(To, ServerHost, Lang)); process_search(#iq{type = set, to = To, lang = Lang, sub_els = [#search{xdata = #xdata{type = submit, fields = Fs}}]} = IQ) -> ServerHost = ejabberd_router:host_of_route(To#jid.lserver), ResultXData = search_result(Lang, To, ServerHost, Fs), xmpp:make_iq_result(IQ, #search{xdata = ResultXData}); process_search(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Incorrect data form">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)). -spec disco_items({error, stanza_error()} | {result, [disco_item()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [disco_item()]}. disco_items(empty, _From, _To, <<"">>, _Lang) -> {result, []}; disco_items(empty, _From, _To, _Node, Lang) -> {error, xmpp:err_item_not_found(<<"No services available">>, Lang)}; disco_items(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. disco_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> Acc; disco_features(Acc, _From, _To, <<"">>, _Lang) -> Features = case Acc of {result, Fs} -> Fs; empty -> [] end, {result, [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_VCARD, ?NS_SEARCH | Features]}; disco_features(empty, _From, _To, _Node, Lang) -> Txt = <<"No features available">>, {error, xmpp:err_item_not_found(Txt, Lang)}; disco_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. disco_identity(Acc, _From, To, <<"">>, Lang) -> Host = ejabberd_router:host_of_route(To#jid.lserver), Name = gen_mod:get_module_opt(Host, ?MODULE, name, ?T("vCard User Search")), [#identity{category = <<"directory">>, type = <<"user">>, name = translate:translate(Lang, Name)}|Acc]; disco_identity(Acc, _From, _To, _Node, _Lang) -> Acc. -spec get_vcard(binary(), binary()) -> [xmlel()] | error. get_vcard(LUser, LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Result = case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?VCARD_CACHE, {LUser, LServer}, fun() -> Mod:get_vcard(LUser, LServer) end); false -> Mod:get_vcard(LUser, LServer) end, case Result of {ok, Els} -> Els; error -> error end. -spec make_vcard_search(binary(), binary(), binary(), xmlel()) -> #vcard_search{}. make_vcard_search(User, LUser, LServer, VCARD) -> FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]), Family = fxml:get_path_s(VCARD, [{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]), Given = fxml:get_path_s(VCARD, [{elem, <<"N">>}, {elem, <<"GIVEN">>}, cdata]), Middle = fxml:get_path_s(VCARD, [{elem, <<"N">>}, {elem, <<"MIDDLE">>}, cdata]), Nickname = fxml:get_path_s(VCARD, [{elem, <<"NICKNAME">>}, cdata]), BDay = fxml:get_path_s(VCARD, [{elem, <<"BDAY">>}, cdata]), CTRY = fxml:get_path_s(VCARD, [{elem, <<"ADR">>}, {elem, <<"CTRY">>}, cdata]), Locality = fxml:get_path_s(VCARD, [{elem, <<"ADR">>}, {elem, <<"LOCALITY">>}, cdata]), EMail1 = fxml:get_path_s(VCARD, [{elem, <<"EMAIL">>}, {elem, <<"USERID">>}, cdata]), EMail2 = fxml:get_path_s(VCARD, [{elem, <<"EMAIL">>}, cdata]), OrgName = fxml:get_path_s(VCARD, [{elem, <<"ORG">>}, {elem, <<"ORGNAME">>}, cdata]), OrgUnit = fxml:get_path_s(VCARD, [{elem, <<"ORG">>}, {elem, <<"ORGUNIT">>}, cdata]), EMail = case EMail1 of <<"">> -> EMail2; _ -> EMail1 end, LFN = string2lower(FN), LFamily = string2lower(Family), LGiven = string2lower(Given), LMiddle = string2lower(Middle), LNickname = string2lower(Nickname), LBDay = string2lower(BDay), LCTRY = string2lower(CTRY), LLocality = string2lower(Locality), LEMail = string2lower(EMail), LOrgName = string2lower(OrgName), LOrgUnit = string2lower(OrgUnit), US = {LUser, LServer}, #vcard_search{us = US, user = {User, LServer}, luser = LUser, fn = FN, lfn = LFN, family = Family, lfamily = LFamily, given = Given, lgiven = LGiven, middle = Middle, lmiddle = LMiddle, nickname = Nickname, lnickname = LNickname, bday = BDay, lbday = LBDay, ctry = CTRY, lctry = LCTRY, locality = Locality, llocality = LLocality, email = EMail, lemail = LEMail, orgname = OrgName, lorgname = LOrgName, orgunit = OrgUnit, lorgunit = LOrgUnit}. -spec vcard_iq_set(iq()) -> iq() | {stop, stanza_error()}. vcard_iq_set(#iq{from = From, lang = Lang, sub_els = [VCard]} = IQ) -> #jid{user = User, lserver = LServer} = From, case set_vcard(User, LServer, VCard) of {error, badarg} -> %% Should not be here? Txt = <<"Nodeprep has failed">>, {stop, xmpp:err_internal_server_error(Txt, Lang)}; ok -> IQ end; vcard_iq_set(Acc) -> Acc. -spec set_vcard(binary(), binary(), xmlel() | vcard_temp()) -> {error, badarg} | ok. set_vcard(User, LServer, VCARD) -> case jid:nodeprep(User) of error -> {error, badarg}; LUser -> VCardEl = xmpp:encode(VCARD), VCardSearch = make_vcard_search(User, LUser, LServer, VCardEl), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:set_vcard(LUser, LServer, VCardEl, VCardSearch), ets_cache:delete(?VCARD_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)), ok end. -spec string2lower(binary()) -> binary(). string2lower(String) -> case stringprep:tolower(String) of Lower when is_binary(Lower) -> Lower; error -> str:to_lower(String) end. -spec mk_tfield(binary(), binary(), binary()) -> xdata_field(). mk_tfield(Label, Var, Lang) -> #xdata_field{type = 'text-single', label = translate:translate(Lang, Label), var = Var}. -spec mk_field(binary(), binary()) -> xdata_field(). mk_field(Var, Val) -> #xdata_field{var = Var, values = [Val]}. -spec mk_search_form(jid(), binary(), binary()) -> search(). mk_search_form(JID, ServerHost, Lang) -> Title = <<(translate:translate(Lang, <<"Search users in ">>))/binary, (jid:encode(JID))/binary>>, Mod = gen_mod:db_mod(ServerHost, ?MODULE), SearchFields = Mod:search_fields(ServerHost), Fs = [mk_tfield(Label, Var, Lang) || {Label, Var} <- SearchFields], X = #xdata{type = form, title = Title, instructions = [translate:translate( Lang, <<"Fill in the form to search for any matching " "Jabber User (Add * to the end of field " "to match substring)">>)], fields = Fs}, #search{instructions = translate:translate( Lang, <<"You need an x:data capable client to search">>), xdata = X}. -spec search_result(binary(), jid(), binary(), [xdata_field()]) -> xdata(). search_result(Lang, JID, ServerHost, XFields) -> Mod = gen_mod:db_mod(ServerHost, ?MODULE), Reported = [mk_tfield(Label, Var, Lang) || {Label, Var} <- Mod:search_reported(ServerHost)], #xdata{type = result, title = <<(translate:translate(Lang, <<"Search Results for ">>))/binary, (jid:encode(JID))/binary>>, reported = Reported, items = lists:map(fun (Item) -> item_to_field(Item) end, search(ServerHost, XFields))}. -spec item_to_field([{binary(), binary()}]) -> [xdata_field()]. item_to_field(Items) -> [mk_field(Var, Value) || {Var, Value} <- Items]. -spec search(binary(), [xdata_field()]) -> [binary()]. search(LServer, XFields) -> Data = [{Var, Vals} || #xdata_field{var = Var, values = Vals} <- XFields], Mod = gen_mod:db_mod(LServer, ?MODULE), AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE, allow_return_all, false), MaxMatch = gen_mod:get_module_opt(LServer, ?MODULE, matches, ?JUD_MATCHES), Mod:search(LServer, Data, AllowReturnAll, MaxMatch). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_user(LUser, LServer), ets_cache:delete(?VCARD_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)). -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> case use_cache(Mod, Host) of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?VCARD_CACHE, CacheOpts); false -> ets_cache:delete(?VCARD_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(Host); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. import_info() -> [{<<"vcard">>, 3}, {<<"vcard_search">>, 24}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). import(LServer, {sql, _}, DBType, Tab, L) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, Tab, L). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). depends(_Host, _Opts) -> []. mod_opt_type(allow_return_all) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun (L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(matches) -> fun (infinity) -> infinity; (I) when is_integer(I), I > 0 -> I end; mod_opt_type(search) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(search_all_hosts) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [allow_return_all, db_type, host, hosts, iqdisc, matches, search, search_all_hosts, cache_life_time, cache_size, use_cache, cache_missed, name]. ejabberd-18.01/src/mod_multicast.erl0000644000232200023220000010447713225664356020017 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_multicast.erl %%% Author : Badlop %%% Purpose : Extended Stanza Addressing (XEP-0033) support %%% Created : 29 May 2007 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_multicast). -author('badlop@process-one.net'). -protocol({xep, 33, '1.1'}). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3]). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, handle_cast/2, terminate/2, code_change/3]). -export([purge_loop/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("translate.hrl"). -include("xmpp.hrl"). -record(state, {lserver, lservice, access, service_limits}). -type state() :: #state{}. -record(multicastc, {rserver, response, ts}). %% ts: timestamp (in seconds) when the cache item was last updated -record(dest, {jid_string = none :: binary(), jid_jid :: jid(), type :: atom(), full_xml :: address()}). %% jid_string = string() %% jid_jid = jid() %% full_xml = xml() -record(group, {server, dests, multicast, others, addresses}). %% server = string() %% dests = [string()] %% multicast = {cached, local_server} | {cached, string()} | {cached, not_supported} | {obsolete, not_supported} | {obsolete, string()} | not_cached %% after being updated, possible values are: local | multicast_not_supported | {multicast_supported, string(), limits()} %% others = [xml()] %% packet = xml() -record(waiter, {awaiting, group, renewal = false, sender, packet, aattrs, addresses}). %% awaiting = {[Remote_service], Local_service, Type_awaiting} %% Remote_service = Local_service = string() %% Type_awaiting = info | items %% group = #group %% renewal = true | false %% sender = From %% packet = xml() %% aattrs = [xml()] -record(limits, {message, presence}). %% message = presence = integer() | infinite -record(service_limits, {local, remote}). %% All the elements are of type value() -define(VERSION_MULTICAST, <<"$Revision: 440 $ ">>). -define(PURGE_PROCNAME, ejabberd_mod_multicast_purgeloop). -define(MAXTIME_CACHE_POSITIVE, 86400). -define(MAXTIME_CACHE_NEGATIVE, 86400). -define(CACHE_PURGE_TIMER, 86400000). -define(DISCO_QUERY_TIMEOUT, 10000). -define(DEFAULT_LIMIT_LOCAL_MESSAGE, 100). -define(DEFAULT_LIMIT_LOCAL_PRESENCE, 100). -define(DEFAULT_LIMIT_REMOTE_MESSAGE, 20). -define(DEFAULT_LIMIT_REMOTE_PRESENCE, 20). start(LServerS, Opts) -> gen_mod:start_child(?MODULE, LServerS, Opts). stop(LServerS) -> gen_mod:stop_child(?MODULE, LServerS). reload(LServerS, NewOpts, OldOpts) -> Proc = gen_mod:get_module_proc(LServerS, ?MODULE), gen_server:cast(Proc, {reload, NewOpts, OldOpts}). %%==================================================================== %% gen_server callbacks %%==================================================================== init([LServerS, Opts]) -> process_flag(trap_exit, true), LServiceS = gen_mod:get_opt_host(LServerS, Opts, <<"multicast.@HOST@">>), Access = gen_mod:get_opt(access, Opts, all), SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts, [])), create_cache(), try_start_loop(), create_pool(), ejabberd_router_multicast:register_route(LServerS), ejabberd_router:register_route(LServiceS, LServerS), {ok, #state{lservice = LServiceS, lserver = LServerS, access = Access, service_limits = SLimits}}. handle_call(stop, _From, State) -> try_stop_loop(), {stop, normal, ok, State}. handle_cast({reload, NewOpts, NewOpts}, #state{lserver = LServerS, lservice = OldLServiceS} = State) -> Access = gen_mod:get_opt(access, NewOpts, all), SLimits = build_service_limit_record(gen_mod:get_opt(limits, NewOpts, [])), NewLServiceS = gen_mod:get_opt_host(LServerS, NewOpts, <<"multicast.@HOST@">>), if NewLServiceS /= OldLServiceS -> ejabberd_router:register_route(NewLServiceS, LServerS), ejabberd_router:unregister_route(OldLServiceS); true -> ok end, {noreply, State#state{lservice = NewLServiceS, access = Access, service_limits = SLimits}}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({route, #iq{} = Packet}, State) -> case catch handle_iq(Packet, State) of {'EXIT', Reason} -> ?ERROR_MSG("Error when processing IQ stanza: ~p", [Reason]); _ -> ok end, {noreply, State}; %% XEP33 allows only 'message' and 'presence' stanza type handle_info({route, Packet}, #state{lservice = LServiceS, lserver = LServerS, access = Access, service_limits = SLimits} = State) when ?is_stanza(Packet) -> route_untrusted(LServiceS, LServerS, Access, SLimits, Packet), {noreply, State}; %% Handle multicast packets sent by trusted local services handle_info({route_trusted, Destinations, Packet}, #state{lservice = LServiceS, lserver = LServerS} = State) -> From = xmpp:get_from(Packet), case catch route_trusted(LServiceS, LServerS, From, Destinations, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("Error in route_trusted: ~p", [Reason]); _ -> ok end, {noreply, State}; handle_info({get_host, Pid}, State) -> Pid ! {my_host, State#state.lservice}, {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, State) -> ejabberd_router_multicast:unregister_route(State#state.lserver), ejabberd_router:unregister_route(State#state.lservice), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%==================================================================== %%% Internal functions %%==================================================================== %%%------------------------ %%% IQ Request Processing %%%------------------------ handle_iq(Packet, State) -> try IQ = xmpp:decode_els(Packet), case process_iq(IQ, State) of {result, SubEl} -> ejabberd_router:route(xmpp:make_iq_result(Packet, SubEl)); {error, Error} -> ejabberd_router:route_error(Packet, Error); reply -> To = xmpp:get_to(IQ), LServiceS = jid:encode(To), case Packet#iq.type of result -> process_iqreply_result(LServiceS, IQ); error -> process_iqreply_error(LServiceS, IQ) end end catch _:{xmpp_codec, Why} -> Lang = xmpp:get_lang(Packet), Err = xmpp:err_bad_request(xmpp:io_format_error(Why), Lang), ejabberd_router:route_error(Packet, Err) end. -spec process_iq(iq(), state()) -> {result, xmpp_element()} | {error, stanza_error()} | reply. process_iq(#iq{type = get, lang = Lang, from = From, sub_els = [#disco_info{}]}, State) -> {result, iq_disco_info(From, Lang, State)}; process_iq(#iq{type = get, sub_els = [#disco_items{}]}, _) -> {result, #disco_items{}}; process_iq(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]}, _) -> {result, iq_vcard(Lang)}; process_iq(#iq{type = T}, _) when T == set; T == get -> {error, xmpp:err_service_unavailable()}; process_iq(_, _) -> reply. -define(FEATURE(Feat), Feat). iq_disco_info(From, Lang, State) -> Name = gen_mod:get_module_opt(State#state.lserver, ?MODULE, name, ?T("Multicast")), #disco_info{ identities = [#identity{category = <<"service">>, type = <<"multicast">>, name = translate:translate(Lang, Name)}], features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_VCARD, ?NS_ADDRESS], xdata = iq_disco_info_extras(From, State)}. iq_vcard(Lang) -> Desc = translate:translate(Lang, <<"ejabberd Multicast service">>), #vcard_temp{fn = <<"ejabberd/mod_multicast">>, url = ?EJABBERD_URI, desc = <>}. %%%------------------------- %%% Route %%%------------------------- route_trusted(LServiceS, LServerS, FromJID, Destinations, Packet) -> Packet_stripped = Packet, AAttrs = [], Delivereds = [], Dests2 = lists:map( fun(D) -> #dest{jid_string = jid:encode(D), jid_jid = D, type = bcc, full_xml = #address{type = bcc, jid = D}} end, Destinations), Groups = group_dests(Dests2), route_common(LServerS, LServiceS, FromJID, Groups, Delivereds, Packet_stripped, AAttrs). route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) -> try route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) catch adenied -> route_error(Packet, forbidden, <<"Access denied by service policy">>); eadsele -> route_error(Packet, bad_request, <<"No addresses element found">>); eadeles -> route_error(Packet, bad_request, <<"No address elements found">>); ewxmlns -> route_error(Packet, bad_request, <<"Wrong xmlns">>); etoorec -> route_error(Packet, not_acceptable, <<"Too many receiver fields were specified">>); edrelay -> route_error(Packet, forbidden, <<"Packet relay is denied by service policy">>); EType:EReason -> ?ERROR_MSG("Multicast unknown error: Type: ~p~nReason: ~p", [EType, EReason]), route_error(Packet, internal_server_error, <<"Unknown problem">>) end. route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) -> FromJID = xmpp:get_from(Packet), ok = check_access(LServerS, Access, FromJID), {ok, Packet_stripped, Addresses} = strip_addresses_element(Packet), {To_deliver, Delivereds} = split_addresses_todeliver(Addresses), Dests = convert_dest_record(To_deliver), {Dests2, Not_jids} = split_dests_jid(Dests), report_not_jid(FromJID, Packet, Not_jids), ok = check_limit_dests(SLimits, FromJID, Packet, Dests2), Groups = group_dests(Dests2), ok = check_relay(FromJID#jid.server, LServerS, Groups), route_common(LServerS, LServiceS, FromJID, Groups, Delivereds, Packet_stripped, []). -spec route_common(binary(), binary(), jid(), [#group{}], [address()], stanza(), list()) -> any(). route_common(LServerS, LServiceS, FromJID, Groups, Delivereds, Packet_stripped, AAttrs) -> Groups2 = look_cached_servers(LServerS, Groups), Groups3 = build_others_xml(Groups2), Groups4 = add_addresses(Delivereds, Groups3), AGroups = decide_action_groups(Groups4), act_groups(FromJID, Packet_stripped, AAttrs, LServiceS, AGroups). act_groups(FromJID, Packet_stripped, AAttrs, LServiceS, AGroups) -> [perform(FromJID, Packet_stripped, AAttrs, LServiceS, AGroup) || AGroup <- AGroups]. perform(From, Packet, AAttrs, _, {route_single, Group}) -> [route_packet(From, ToUser, Packet, AAttrs, Group#group.others, Group#group.addresses) || ToUser <- Group#group.dests]; perform(From, Packet, AAttrs, _, {{route_multicast, JID, RLimits}, Group}) -> route_packet_multicast(From, JID, Packet, AAttrs, Group#group.dests, Group#group.addresses, RLimits); perform(From, Packet, AAttrs, LServiceS, {{ask, Old_service, renewal}, Group}) -> send_query_info(Old_service, LServiceS), add_waiter(#waiter{awaiting = {[Old_service], LServiceS, info}, group = Group, renewal = true, sender = From, packet = Packet, aattrs = AAttrs, addresses = Group#group.addresses}); perform(_From, _Packet, _AAttrs, LServiceS, {{ask, LServiceS, _}, _Group}) -> ok; perform(From, Packet, AAttrs, LServiceS, {{ask, Server, not_renewal}, Group}) -> send_query_info(Server, LServiceS), add_waiter(#waiter{awaiting = {[Server], LServiceS, info}, group = Group, renewal = false, sender = From, packet = Packet, aattrs = AAttrs, addresses = Group#group.addresses}). %%%------------------------- %%% Check access permission %%%------------------------- check_access(LServerS, Access, From) -> case acl:match_rule(LServerS, Access, From) of allow -> ok; _ -> throw(adenied) end. %%%------------------------- %%% Strip 'addresses' XML element %%%------------------------- -spec strip_addresses_element(stanza()) -> {ok, stanza(), [address()]}. strip_addresses_element(Packet) -> case xmpp:get_subtag(Packet, #addresses{}) of #addresses{list = Addrs} -> PacketStripped = xmpp:remove_subtag(Packet, #addresses{}), {ok, PacketStripped, Addrs}; false -> throw(eadsele) end. %%%------------------------- %%% Split Addresses %%%------------------------- -spec split_addresses_todeliver([address()]) -> {[address()], [address()]}. split_addresses_todeliver(Addresses) -> lists:partition( fun(#address{delivered = true}) -> false; (#address{type = Type}) -> case Type of to -> true; cc -> true; bcc -> true; _ -> false end end, Addresses). %%%------------------------- %%% Check does not exceed limit of destinations %%%------------------------- -spec check_limit_dests(_, jid(), stanza(), [address()]) -> ok. check_limit_dests(SLimits, FromJID, Packet, Addresses) -> SenderT = sender_type(FromJID), Limits = get_slimit_group(SenderT, SLimits), Type_of_stanza = type_of_stanza(Packet), {_Type, Limit_number} = get_limit_number(Type_of_stanza, Limits), case length(Addresses) > Limit_number of false -> ok; true -> throw(etoorec) end. %%%------------------------- %%% Convert Destination XML to record %%%------------------------- -spec convert_dest_record([address()]) -> [#dest{}]. convert_dest_record(Addrs) -> lists:map( fun(#address{jid = undefined} = Addr) -> #dest{jid_string = none, full_xml = Addr}; (#address{jid = JID, type = Type} = Addr) -> #dest{jid_string = jid:encode(JID), jid_jid = JID, type = Type, full_xml = Addr} end, Addrs). %%%------------------------- %%% Split destinations by existence of JID %%% and send error messages for other dests %%%------------------------- -spec split_dests_jid([#dest{}]) -> {[#dest{}], [#dest{}]}. split_dests_jid(Dests) -> lists:partition(fun (Dest) -> case Dest#dest.jid_string of none -> false; _ -> true end end, Dests). -spec report_not_jid(jid(), stanza(), #dest{}) -> any(). report_not_jid(From, Packet, Dests) -> Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.full_xml)) || Dest <- Dests], [route_error(xmpp:set_from_to(Packet, From, From), jid_malformed, <<"This service can not process the address: ", D/binary>>) || D <- Dests2]. %%%------------------------- %%% Group destinations by their servers %%%------------------------- -spec group_dests([#dest{}]) -> [#group{}]. group_dests(Dests) -> D = lists:foldl(fun (Dest, Dict) -> ServerS = (Dest#dest.jid_jid)#jid.server, dict:append(ServerS, Dest, Dict) end, dict:new(), Dests), Keys = dict:fetch_keys(D), [#group{server = Key, dests = dict:fetch(Key, D)} || Key <- Keys]. %%%------------------------- %%% Look for cached responses %%%------------------------- look_cached_servers(LServerS, Groups) -> [look_cached(LServerS, Group) || Group <- Groups]. look_cached(LServerS, G) -> Maxtime_positive = (?MAXTIME_CACHE_POSITIVE), Maxtime_negative = (?MAXTIME_CACHE_NEGATIVE), Cached_response = search_server_on_cache(G#group.server, LServerS, {Maxtime_positive, Maxtime_negative}), G#group{multicast = Cached_response}. %%%------------------------- %%% Build delivered XML element %%%------------------------- build_others_xml(Groups) -> [Group#group{others = build_other_xml(Group#group.dests)} || Group <- Groups]. build_other_xml(Dests) -> lists:foldl(fun (Dest, R) -> XML = Dest#dest.full_xml, case Dest#dest.type of to -> [add_delivered(XML) | R]; cc -> [add_delivered(XML) | R]; bcc -> R; _ -> [XML | R] end end, [], Dests). -spec add_delivered(address()) -> address(). add_delivered(Addr) -> Addr#address{delivered = true}. %%%------------------------- %%% Add preliminary packets %%%------------------------- add_addresses(Delivereds, Groups) -> Ps = [Group#group.others || Group <- Groups], add_addresses2(Delivereds, Groups, [], [], Ps). add_addresses2(_, [], Res, _, []) -> Res; add_addresses2(Delivereds, [Group | Groups], Res, Pa, [Pi | Pz]) -> Addresses = lists:append([Delivereds] ++ Pa ++ Pz), Group2 = Group#group{addresses = Addresses}, add_addresses2(Delivereds, Groups, [Group2 | Res], [Pi | Pa], Pz). %%%------------------------- %%% Decide action groups %%%------------------------- decide_action_groups(Groups) -> [{decide_action_group(Group), Group} || Group <- Groups]. decide_action_group(Group) -> Server = Group#group.server, case Group#group.multicast of {cached, local_server} -> %% Send a copy of the packet to each local user on Dests route_single; {cached, not_supported} -> %% Send a copy of the packet to each remote user on Dests route_single; {cached, {multicast_supported, JID, RLimits}} -> {route_multicast, JID, RLimits}; {obsolete, {multicast_supported, Old_service, _RLimits}} -> {ask, Old_service, renewal}; {obsolete, not_supported} -> {ask, Server, not_renewal}; not_cached -> {ask, Server, not_renewal} end. %%%------------------------- %%% Route packet %%%------------------------- route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) -> Dests = case ToDest#dest.type of bcc -> []; _ -> [ToDest] end, route_packet2(From, ToDest#dest.jid_string, Dests, Packet, AAttrs, {Others, Addresses}). route_packet_multicast(From, ToS, Packet, AAttrs, Dests, Addresses, Limits) -> Type_of_stanza = type_of_stanza(Packet), {_Type, Limit_number} = get_limit_number(Type_of_stanza, Limits), Fragmented_dests = fragment_dests(Dests, Limit_number), [route_packet2(From, ToS, DFragment, Packet, AAttrs, Addresses) || DFragment <- Fragmented_dests]. -spec route_packet2(jid(), binary(), [#dest{}], stanza(), list(), [address()]) -> ok. route_packet2(From, ToS, Dests, Packet, _AAttrs, Addresses) -> Els = case append_dests(Dests, Addresses) of [] -> xmpp:get_els(Packet); ACs -> [#addresses{list = ACs}|xmpp:get_els(Packet)] end, Packet2 = xmpp:set_els(Packet, Els), ToJID = stj(ToS), ejabberd_router:route(xmpp:set_from_to(Packet2, From, ToJID)). -spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()]. append_dests(_Dests, {Others, Addresses}) -> Addresses++Others; append_dests([], Addresses) -> Addresses; append_dests([Dest | Dests], Addresses) -> append_dests(Dests, [Dest#dest.full_xml | Addresses]). %%%------------------------- %%% Check relay %%%------------------------- -spec check_relay(binary(), binary(), [#group{}]) -> ok. check_relay(RS, LS, Gs) -> case check_relay_required(RS, LS, Gs) of false -> ok; true -> throw(edrelay) end. -spec check_relay_required(binary(), binary(), [#group{}]) -> boolean(). check_relay_required(RServer, LServerS, Groups) -> case lists:suffix(str:tokens(LServerS, <<".">>), str:tokens(RServer, <<".">>)) of true -> false; false -> check_relay_required(LServerS, Groups) end. -spec check_relay_required(binary(), [#group{}]) -> boolean(). check_relay_required(LServerS, Groups) -> lists:any(fun (Group) -> Group#group.server /= LServerS end, Groups). %%%------------------------- %%% Check protocol support: Send request %%%------------------------- send_query_info(RServerS, LServiceS) -> case str:str(RServerS, <<"echo.">>) of 1 -> false; _ -> send_query(RServerS, LServiceS, #disco_info{}) end. send_query_items(RServerS, LServiceS) -> send_query(RServerS, LServiceS, #disco_items{}). -spec send_query(binary(), binary(), [disco_info()|disco_items()]) -> ok. send_query(RServerS, LServiceS, SubEl) -> Packet = #iq{from = stj(LServiceS), to = stj(RServerS), id = randoms:get_string(), type = get, sub_els = [SubEl]}, ejabberd_router:route(Packet). %%%------------------------- %%% Check protocol support: Receive response: Error %%%------------------------- process_iqreply_error(LServiceS, Packet) -> FromS = jts(xmpp:get_from(Packet)), case search_waiter(FromS, LServiceS, info) of {found_waiter, Waiter} -> received_awaiter(FromS, Waiter, LServiceS); _ -> ok end. %%%------------------------- %%% Check protocol support: Receive response: Disco %%%------------------------- -spec process_iqreply_result(binary(), iq()) -> any(). process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) -> case SubEl of #disco_info{} -> process_discoinfo_result(From, LServiceS, SubEl); #disco_items{} -> process_discoitems_result(From, LServiceS, SubEl); _ -> ok end. %%%------------------------- %%% Check protocol support: Receive response: Disco Info %%%------------------------- process_discoinfo_result(From, LServiceS, DiscoInfo) -> FromS = jts(From), case search_waiter(FromS, LServiceS, info) of {found_waiter, Waiter} -> process_discoinfo_result2(From, FromS, LServiceS, DiscoInfo, Waiter); _ -> ok end. process_discoinfo_result2(From, FromS, LServiceS, #disco_info{features = Feats} = DiscoInfo, Waiter) -> Multicast_support = lists:member(?NS_ADDRESS, Feats), Group = Waiter#waiter.group, RServer = Group#group.server, case Multicast_support of true -> SenderT = sender_type(From), RLimits = get_limits_xml(DiscoInfo, SenderT), add_response(RServer, {multicast_supported, FromS, RLimits}), FromM = Waiter#waiter.sender, DestsM = Group#group.dests, PacketM = Waiter#waiter.packet, AAttrsM = Waiter#waiter.aattrs, AddressesM = Waiter#waiter.addresses, RServiceM = FromS, route_packet_multicast(FromM, RServiceM, PacketM, AAttrsM, DestsM, AddressesM, RLimits), delo_waiter(Waiter); false -> case FromS of RServer -> send_query_items(FromS, LServiceS), delo_waiter(Waiter), add_waiter(Waiter#waiter{awaiting = {[FromS], LServiceS, items}, renewal = false}); %% We asked a component, and it does not support XEP33 _ -> received_awaiter(FromS, Waiter, LServiceS) end end. get_limits_xml(DiscoInfo, SenderT) -> LimitOpts = get_limits_els(DiscoInfo), build_remote_limit_record(LimitOpts, SenderT). -spec get_limits_els(disco_info()) -> [{atom(), integer()}]. get_limits_els(DiscoInfo) -> lists:flatmap( fun(#xdata{type = result} = X) -> get_limits_fields(X); (_) -> [] end, DiscoInfo#disco_info.xdata). -spec get_limits_fields(xdata()) -> [{atom(), integer()}]. get_limits_fields(X) -> {Head, Tail} = lists:partition( fun(#xdata_field{var = Var, type = Type}) -> Var == <<"FORM_TYPE">> andalso Type == hidden end, X#xdata.fields), case Head of [] -> []; _ -> get_limits_values(Tail) end. -spec get_limits_values([xdata_field()]) -> [{atom(), integer()}]. get_limits_values(Fields) -> lists:flatmap( fun(#xdata_field{var = Name, values = [Number]}) -> try [{binary_to_atom(Name, utf8), binary_to_integer(Number)}] catch _:badarg -> [] end; (_) -> [] end, Fields). %%%------------------------- %%% Check protocol support: Receive response: Disco Items %%%------------------------- process_discoitems_result(From, LServiceS, #disco_items{items = Items}) -> FromS = jts(From), case search_waiter(FromS, LServiceS, items) of {found_waiter, Waiter} -> List = lists:flatmap( fun(#disco_item{jid = #jid{luser = <<"">>, lresource = <<"">>} = J}) -> [J]; (_) -> [] end, Items), case List of [] -> received_awaiter(FromS, Waiter, LServiceS); _ -> [send_query_info(Item, LServiceS) || Item <- List], delo_waiter(Waiter), add_waiter(Waiter#waiter{awaiting = {List, LServiceS, info}, renewal = false}) end; _ -> ok end. %%%------------------------- %%% Check protocol support: Receive response: Received awaiter %%%------------------------- received_awaiter(JID, Waiter, LServiceS) -> {JIDs, LServiceS, _} = Waiter#waiter.awaiting, delo_waiter(Waiter), Group = Waiter#waiter.group, RServer = Group#group.server, case lists:delete(JID, JIDs) of [] -> case Waiter#waiter.renewal of false -> add_response(RServer, not_supported), From = Waiter#waiter.sender, Packet = Waiter#waiter.packet, AAttrs = Waiter#waiter.aattrs, Others = Group#group.others, Addresses = Waiter#waiter.addresses, [route_packet(From, ToUser, Packet, AAttrs, Others, Addresses) || ToUser <- Group#group.dests]; true -> send_query_info(RServer, LServiceS), add_waiter(Waiter#waiter{awaiting = {[RServer], LServiceS, info}, renewal = false}) end; JIDs2 -> add_waiter(Waiter#waiter{awaiting = {JIDs2, LServiceS, info}, renewal = false}) end. %%%------------------------- %%% Cache %%%------------------------- create_cache() -> ejabberd_mnesia:create(?MODULE, multicastc, [{ram_copies, [node()]}, {attributes, record_info(fields, multicastc)}]). add_response(RServer, Response) -> Secs = calendar:datetime_to_gregorian_seconds(calendar:local_time()), mnesia:dirty_write(#multicastc{rserver = RServer, response = Response, ts = Secs}). search_server_on_cache(RServer, LServerS, _Maxmins) when RServer == LServerS -> {cached, local_server}; search_server_on_cache(RServer, _LServerS, Maxmins) -> case look_server(RServer) of not_cached -> not_cached; {cached, Response, Ts} -> Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()), case is_obsolete(Response, Ts, Now, Maxmins) of false -> {cached, Response}; true -> {obsolete, Response} end end. look_server(RServer) -> case mnesia:dirty_read(multicastc, RServer) of [] -> not_cached; [M] -> {cached, M#multicastc.response, M#multicastc.ts} end. is_obsolete(Response, Ts, Now, {Max_pos, Max_neg}) -> Max = case Response of multicast_not_supported -> Max_neg; _ -> Max_pos end, Now - Ts > Max. %%%------------------------- %%% Purge cache %%%------------------------- purge() -> Maxmins_positive = (?MAXTIME_CACHE_POSITIVE), Maxmins_negative = (?MAXTIME_CACHE_NEGATIVE), Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()), purge(Now, {Maxmins_positive, Maxmins_negative}). purge(Now, Maxmins) -> F = fun () -> mnesia:foldl(fun (R, _) -> #multicastc{response = Response, ts = Ts} = R, case is_obsolete(Response, Ts, Now, Maxmins) of true -> mnesia:delete_object(R); false -> ok end end, none, multicastc) end, mnesia:transaction(F). %%%------------------------- %%% Purge cache loop %%%------------------------- try_start_loop() -> case lists:member(?PURGE_PROCNAME, registered()) of true -> ok; false -> start_loop() end, (?PURGE_PROCNAME) ! new_module. start_loop() -> register(?PURGE_PROCNAME, spawn(?MODULE, purge_loop, [0])), (?PURGE_PROCNAME) ! purge_now. try_stop_loop() -> (?PURGE_PROCNAME) ! try_stop. purge_loop(NM) -> receive purge_now -> purge(), timer:send_after(?CACHE_PURGE_TIMER, ?PURGE_PROCNAME, purge_now), purge_loop(NM); new_module -> purge_loop(NM + 1); try_stop when NM > 1 -> purge_loop(NM - 1); try_stop -> purge_loop_finished end. %%%------------------------- %%% Pool %%%------------------------- create_pool() -> catch begin ets:new(multicastp, [duplicate_bag, public, named_table, {keypos, 2}]), ets:give_away(multicastp, whereis(ejabberd), ok) end. add_waiter(Waiter) -> true = ets:insert(multicastp, Waiter). delo_waiter(Waiter) -> true = ets:delete_object(multicastp, Waiter). -spec search_waiter(binary(), binary(), info | items) -> {found_waiter, #waiter{}} | waiter_not_found. search_waiter(JID, LServiceS, Type) -> Rs = ets:foldl(fun (W, Res) -> {JIDs, LServiceS1, Type1} = W#waiter.awaiting, case lists:member(JID, JIDs) and (LServiceS == LServiceS1) and (Type1 == Type) of true -> Res ++ [W]; false -> Res end end, [], multicastp), case Rs of [R | _] -> {found_waiter, R}; [] -> waiter_not_found end. %%%------------------------- %%% Limits: utils %%%------------------------- %% Type definitions for data structures related with XEP33 limits %% limit() = {Name, Value} %% Name = atom() %% Value = {Type, Number} %% Type = default | custom %% Number = integer() | infinite list_of_limits(local) -> [{message, ?DEFAULT_LIMIT_LOCAL_MESSAGE}, {presence, ?DEFAULT_LIMIT_LOCAL_PRESENCE}]; list_of_limits(remote) -> [{message, ?DEFAULT_LIMIT_REMOTE_MESSAGE}, {presence, ?DEFAULT_LIMIT_REMOTE_PRESENCE}]. build_service_limit_record(LimitOpts) -> LimitOptsL = get_from_limitopts(LimitOpts, local), LimitOptsR = get_from_limitopts(LimitOpts, remote), {service_limits, build_limit_record(LimitOptsL, local), build_limit_record(LimitOptsR, remote)}. get_from_limitopts(LimitOpts, SenderT) -> case lists:keyfind(SenderT, 1, LimitOpts) of false -> []; {SenderT, Result} -> Result end. build_remote_limit_record(LimitOpts, SenderT) -> build_limit_record(LimitOpts, SenderT). build_limit_record(LimitOpts, SenderT) -> Limits = [get_limit_value(Name, Default, LimitOpts) || {Name, Default} <- list_of_limits(SenderT)], list_to_tuple([limits | Limits]). get_limit_value(Name, Default, LimitOpts) -> case lists:keysearch(Name, 1, LimitOpts) of {value, {Name, Number}} -> {custom, Number}; false -> {default, Default} end. type_of_stanza(Stanza) -> element(1, Stanza). get_limit_number(message, Limits) -> Limits#limits.message; get_limit_number(presence, Limits) -> Limits#limits.presence. get_slimit_group(local, SLimits) -> SLimits#service_limits.local; get_slimit_group(remote, SLimits) -> SLimits#service_limits.remote. fragment_dests(Dests, Limit_number) -> {R, _} = lists:foldl(fun (Dest, {Res, Count}) -> case Count of Limit_number -> Head2 = [Dest], {[Head2 | Res], 0}; _ -> [Head | Tail] = Res, Head2 = [Dest | Head], {[Head2 | Tail], Count + 1} end end, {[[]], 0}, Dests), R. %%%------------------------- %%% Limits: XEP-0128 Service Discovery Extensions %%%------------------------- %% Some parts of code are borrowed from mod_muc_room.erl -define(RFIELDT(Type, Var, Val), #xdata_field{type = Type, var = Var, values = [Val]}). -define(RFIELDV(Var, Val), #xdata_field{var = Var, values = [Val]}). iq_disco_info_extras(From, State) -> SenderT = sender_type(From), Service_limits = State#state.service_limits, case iq_disco_info_extras2(SenderT, Service_limits) of [] -> []; List_limits_xmpp -> #xdata{type = result, fields = [?RFIELDT(hidden, <<"FORM_TYPE">>, ?NS_ADDRESS) | List_limits_xmpp]} end. sender_type(From) -> Local_hosts = (?MYHOSTS), case lists:member(From#jid.lserver, Local_hosts) of true -> local; false -> remote end. iq_disco_info_extras2(SenderT, SLimits) -> Limits = get_slimit_group(SenderT, SLimits), Stanza_types = [message, presence], lists:foldl(fun (Type_of_stanza, R) -> case get_limit_number(Type_of_stanza, Limits) of {custom, Number} -> [?RFIELDV((to_binary(Type_of_stanza)), (to_binary(Number))) | R]; {default, _} -> R end end, [], Stanza_types). to_binary(A) -> list_to_binary(hd(io_lib:format("~p", [A]))). %%%------------------------- %%% Error report %%%------------------------- route_error(Packet, ErrType, ErrText) -> Lang = xmpp:get_lang(Packet), Err = make_reply(ErrType, Lang, ErrText), ejabberd_router:route_error(Packet, Err). make_reply(bad_request, Lang, ErrText) -> xmpp:err_bad_request(ErrText, Lang); make_reply(jid_malformed, Lang, ErrText) -> xmpp:err_jid_malformed(ErrText, Lang); make_reply(not_acceptable, Lang, ErrText) -> xmpp:err_not_acceptable(ErrText, Lang); make_reply(internal_server_error, Lang, ErrText) -> xmpp:err_internal_server_error(ErrText, Lang); make_reply(forbidden, Lang, ErrText) -> xmpp:err_forbidden(ErrText, Lang). stj(String) -> jid:decode(String). jts(String) -> jid:encode(String). depends(_Host, _Opts) -> []. mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type({limits, Type}) when (Type == local) or (Type == remote) -> fun(L) -> lists:map( fun ({message, infinite} = O) -> O; ({presence, infinite} = O) -> O; ({message, I} = O) when is_integer(I) -> O; ({presence, I} = O) when is_integer(I) -> O end, L) end; mod_opt_type(_) -> [access, host, {limits, local}, {limits, remote}, name]. ejabberd-18.01/src/ejabberd_acme.erl0000644000232200023220000011564413225664356017674 0ustar debalancedebalance-module (ejabberd_acme). -behaviour(gen_server). -behavior(ejabberd_config). %% ejabberdctl commands -export([get_certificates/1, renew_certificates/0, list_certificates/1, revoke_certificate/1]). %% Command Options Validity -export([is_valid_account_opt/1, is_valid_verbose_opt/1, is_valid_domain_opt/1, is_valid_revoke_cert/1]). %% Key Related -export([generate_key/0, to_public/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([start_link/0, opt_type/1, register_certfiles/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_commands.hrl"). -include("ejabberd_acme.hrl"). -include_lib("public_key/include/public_key.hrl"). -define(DEFAULT_CONFIG_CONTACT, <<"mailto:example-admin@example.com">>). -define(DEFAULT_CONFIG_CA_URL, "https://acme-v01.api.letsencrypt.org"). -record(state, {}). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> case filelib:ensure_dir(filename:join(acme_certs_dir(), "foo")) of ok -> ejabberd_hooks:add(config_reloaded, ?MODULE, register_certfiles, 40), ejabberd_commands:register_commands(get_commands_spec()), register_certfiles(), {ok, #state{}}; {error, Why} -> ?CRITICAL_MSG("Failed to create directory ~s: ~s", [acme_certs_dir(), file:format_error(Why)]), {stop, Why} end. handle_call(_Request, _From, State) -> {stop, {unexpected_call, _Request, _From}, State}. handle_cast(_Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [_Msg]), {noreply, State}. handle_info(_Info, State) -> ?WARNING_MSG("unexpected info: ~p", [_Info]), {noreply, State}. terminate(_Reason, _State) -> ejabberd_hooks:delete(config_reloaded, ?MODULE, register_certfiles, 40), ejabberd_commands:unregister_commands(get_commands_spec()). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Command Functions %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Check Validity of command options %% -spec is_valid_account_opt(string()) -> boolean(). is_valid_account_opt("old-account") -> true; is_valid_account_opt("new-account") -> true; is_valid_account_opt(_) -> false. -spec is_valid_verbose_opt(string()) -> boolean(). is_valid_verbose_opt("plain") -> true; is_valid_verbose_opt("verbose") -> true; is_valid_verbose_opt(_) -> false. %% TODO: Make this check more complicated -spec is_valid_domain_opt(string()) -> boolean(). is_valid_domain_opt("all") -> true; is_valid_domain_opt(DomainString) -> case parse_domain_string(DomainString) of [] -> false; _SeparatedDomains -> true end. -spec is_valid_revoke_cert(string()) -> boolean(). is_valid_revoke_cert(DomainOrFile) -> lists:prefix("file:", DomainOrFile) orelse lists:prefix("domain:", DomainOrFile). %% Commands get_commands_spec() -> [#ejabberd_commands{name = get_certificates, tags = [acme], desc = "Gets certificates for all or the specified " "domains {all|domain1;domain2;...}.", module = ?MODULE, function = get_certificates, args_desc = ["Domains for which to acquire a certificate"], args_example = ["all | www.example.com;www.example1.net"], args = [{domains, string}], result = {certificates, string}}, #ejabberd_commands{name = renew_certificates, tags = [acme], desc = "Renews all certificates that are close to expiring", module = ?MODULE, function = renew_certificates, args = [], result = {certificates, string}}, #ejabberd_commands{name = list_certificates, tags = [acme], desc = "Lists all curently handled certificates and " "their respective domains in {plain|verbose} format", module = ?MODULE, function = list_certificates, args_desc = ["Whether to print the whole certificate " "or just some metadata. " "Possible values: plain | verbose"], args = [{option, string}], result = {certificates, {list, {certificate, string}}}}, #ejabberd_commands{name = revoke_certificate, tags = [acme], desc = "Revokes the selected certificate", module = ?MODULE, function = revoke_certificate, args_desc = ["The domain or file (in pem format) of " "the certificate in question " "{domain:Domain | file:File}"], args = [{domain_or_file, string}], result = {res, restuple}}]. %% %% Get Certificate %% -spec get_certificates(domains_opt()) -> string() | {'error', _}. get_certificates(Domains) -> case is_valid_domain_opt(Domains) of true -> try CAUrl = get_config_ca_url(), get_certificates0(CAUrl, Domains) catch throw:Throw -> Throw; E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), {error, get_certificates} end; false -> io_lib:format("Invalid domains: ~p", [Domains]) end. -spec get_certificates0(url(), domains_opt()) -> string(). get_certificates0(CAUrl, Domains) -> %% Check if an account exists or create another one {ok, _AccId, PrivateKey} = retrieve_or_create_account(CAUrl), get_certificates1(CAUrl, Domains, PrivateKey). -spec retrieve_or_create_account(url()) -> {'ok', string(), jose_jwk:key()}. retrieve_or_create_account(CAUrl) -> case read_account_persistent() of none -> create_save_new_account(CAUrl); {ok, AccId, CAUrl, PrivateKey} -> {ok, AccId, PrivateKey}; {ok, _AccId, _, _PrivateKey} -> create_save_new_account(CAUrl) end. -spec get_certificates1(url(), domains_opt(), jose_jwk:key()) -> string(). get_certificates1(CAUrl, "all", PrivateKey) -> Hosts = get_config_hosts(), get_certificates2(CAUrl, PrivateKey, Hosts); get_certificates1(CAUrl, DomainString, PrivateKey) -> Domains = parse_domain_string(DomainString), Hosts = [list_to_bitstring(D) || D <- Domains], get_certificates2(CAUrl, PrivateKey, Hosts). -spec get_certificates2(url(), jose_jwk:key(), [bitstring()]) -> string(). get_certificates2(CAUrl, PrivateKey, Hosts) -> %% Get a certificate for each host PemCertKeys = [get_certificate(CAUrl, Host, PrivateKey) || Host <- Hosts], %% Save Certificates SavedCerts = [save_certificate(Cert) || Cert <- PemCertKeys], %% Format the result to send back to ejabberdctl format_get_certificates_result(SavedCerts). -spec format_get_certificates_result([{'ok', bitstring(), _} | {'error', bitstring(), _}]) -> string(). format_get_certificates_result(Certs) -> Cond = lists:all(fun(Cert) -> not is_error(Cert) end, Certs), %% FormattedCerts = string:join([format_get_certificate(C) || C <- Certs], "\n"), FormattedCerts = str:join([format_get_certificate(C) || C <- Certs], $\n), case Cond of true -> Result = io_lib:format("Success:~n~s", [FormattedCerts]), lists:flatten(Result); _ -> Result = io_lib:format("Error with one or more certificates~n~s", [FormattedCerts]), lists:flatten(Result) end. -spec format_get_certificate({'ok', bitstring(), _} | {'error', bitstring(), _}) -> string(). format_get_certificate({ok, Domain, saved}) -> io_lib:format(" Certificate for domain: \"~s\" acquired and saved", [Domain]); format_get_certificate({ok, Domain, not_found}) -> io_lib:format(" Certificate for domain: \"~s\" not found, so it was not renewed", [Domain]); format_get_certificate({ok, Domain, no_expire}) -> io_lib:format(" Certificate for domain: \"~s\" is not close to expiring", [Domain]); format_get_certificate({error, Domain, Reason}) -> io_lib:format(" Error for domain: \"~s\", with reason: \'~s\'", [Domain, Reason]). -spec get_certificate(url(), bitstring(), jose_jwk:key()) -> {'ok', bitstring(), pem()} | {'error', bitstring(), _}. get_certificate(CAUrl, DomainName, PrivateKey) -> try AllSubDomains = find_all_sub_domains(DomainName), lists:foreach( fun(Domain) -> {ok, _Authz} = create_new_authorization(CAUrl, Domain, PrivateKey) end, [DomainName|AllSubDomains]), create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) catch throw:Throw -> Throw; E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), {error, DomainName, get_certificate} end. -spec create_save_new_account(url()) -> {'ok', string(), jose_jwk:key()} | no_return(). create_save_new_account(CAUrl) -> %% Get contact from configuration file Contact = get_config_contact(), %% Generate a Key PrivateKey = generate_key(), %% Create a new account {ok, Id} = create_new_account(CAUrl, Contact, PrivateKey), %% Write Persistent Data ok = write_account_persistent({Id, CAUrl, PrivateKey}), {ok, Id, PrivateKey}. %% TODO: %% Find a way to ask the user if he accepts the TOS -spec create_new_account(url(), bitstring(), jose_jwk:key()) -> {'ok', string()} | no_return(). create_new_account(CAUrl, Contact, PrivateKey) -> try {ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl), Req0 = [{ <<"contact">>, [Contact]}], {ok, {TOS, Account}, Nonce1} = ejabberd_acme_comm:new_account(Dirs, PrivateKey, Req0, Nonce0), {<<"id">>, AccIdInt} = lists:keyfind(<<"id">>, 1, Account), AccId = integer_to_list(AccIdInt), Req1 = [{ <<"agreement">>, list_to_bitstring(TOS)}], {ok, _Account2, _Nonce2} = ejabberd_acme_comm:update_account({CAUrl, AccId}, PrivateKey, Req1, Nonce1), {ok, AccId} catch E:R -> ?ERROR_MSG("Error: ~p creating an account for contact: ~p", [{E,R}, Contact]), throw({error,create_new_account}) end. -spec create_new_authorization(url(), bitstring(), jose_jwk:key()) -> {'ok', proplist()} | no_return(). create_new_authorization(CAUrl, DomainName, PrivateKey) -> acme_challenge:register_hooks(DomainName), try {ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl), Req0 = [{<<"identifier">>, {[{<<"type">>, <<"dns">>}, {<<"value">>, DomainName}]}}, {<<"existing">>, <<"accept">>}], {ok, {AuthzUrl, Authz}, Nonce1} = ejabberd_acme_comm:new_authz(Dirs, PrivateKey, Req0, Nonce0), {ok, AuthzId} = location_to_id(AuthzUrl), Challenges = get_challenges(Authz), {ok, ChallengeUrl, KeyAuthz} = acme_challenge:solve_challenge(<<"http-01">>, Challenges, PrivateKey), {ok, ChallengeId} = location_to_id(ChallengeUrl), Req3 = [{<<"type">>, <<"http-01">>},{<<"keyAuthorization">>, KeyAuthz}], {ok, _SolvedChallenge, _Nonce2} = ejabberd_acme_comm:complete_challenge( {CAUrl, AuthzId, ChallengeId}, PrivateKey, Req3, Nonce1), {ok, AuthzValid, _Nonce} = ejabberd_acme_comm:get_authz_until_valid({CAUrl, AuthzId}), {ok, AuthzValid} catch E:R -> ?ERROR_MSG("Error: ~p getting an authorization for domain: ~p~n", [{E,R}, DomainName]), throw({error, DomainName, authorization}) after acme_challenge:unregister_hooks(DomainName) end. -spec create_new_certificate(url(), {bitstring(), [bitstring()]}, jose_jwk:key()) -> {ok, bitstring(), pem()}. create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) -> try {ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl), CSRSubject = [{commonName, bitstring_to_list(DomainName)}], SANs = [{dNSName, SAN} || SAN <- AllSubDomains], {CSR, CSRKey} = make_csr(CSRSubject, SANs), {NotBefore, NotAfter} = not_before_not_after(), Req = [{<<"csr">>, CSR}, {<<"notBefore">>, NotBefore}, {<<"NotAfter">>, NotAfter} ], {ok, {IssuerCertLink, Certificate}, _Nonce1} = ejabberd_acme_comm:new_cert(Dirs, PrivateKey, Req, Nonce0), DecodedCert = public_key:pkix_decode_cert(list_to_binary(Certificate), plain), PemEntryCert = public_key:pem_entry_encode('Certificate', DecodedCert), {ok, IssuerCert, _Nonce2} = ejabberd_acme_comm:get_issuer_cert(IssuerCertLink), DecodedIssuerCert = public_key:pkix_decode_cert(list_to_binary(IssuerCert), plain), PemEntryIssuerCert = public_key:pem_entry_encode('Certificate', DecodedIssuerCert), {_, CSRKeyKey} = jose_jwk:to_key(CSRKey), PemEntryKey = public_key:pem_entry_encode('ECPrivateKey', CSRKeyKey), PemCertKey = public_key:pem_encode([PemEntryKey, PemEntryCert, PemEntryIssuerCert]), {ok, DomainName, PemCertKey} catch E:R -> ?ERROR_MSG("Error: ~p getting an authorization for domain: ~p~n", [{E,R}, DomainName]), throw({error, DomainName, certificate}) end. -spec ensure_account_exists(url()) -> {ok, string(), jose_jwk:key()}. ensure_account_exists(CAUrl) -> case read_account_persistent() of none -> ?ERROR_MSG("No existing account", []), throw({error, no_old_account}); {ok, AccId, CAUrl, PrivateKey} -> {ok, AccId, PrivateKey}; {ok, _AccId, OtherCAUrl, _PrivateKey} -> ?ERROR_MSG("Account is connected to another CA: ~s", [OtherCAUrl]), throw({error, account_in_other_CA}) end. %% %% Renew Certificates %% -spec renew_certificates() -> string() | {'error', _}. renew_certificates() -> try CAUrl = get_config_ca_url(), renew_certificates0(CAUrl) catch throw:Throw -> Throw; E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), {error, get_certificates} end. -spec renew_certificates0(url()) -> string(). renew_certificates0(CAUrl) -> %% Get the current account {ok, _AccId, PrivateKey} = ensure_account_exists(CAUrl), %% Find all hosts that we have certificates for Certs = read_certificates_persistent(), %% Get a certificate for each host PemCertKeys = [renew_certificate(CAUrl, Cert, PrivateKey) || Cert <- Certs], %% Save Certificates SavedCerts = [save_renewed_certificate(Cert) || Cert <- PemCertKeys], %% Format the result to send back to ejabberdctl format_get_certificates_result(SavedCerts). -spec renew_certificate(url(), {bitstring(), data_cert()}, jose_jwk:key()) -> {'ok', bitstring(), _} | {'error', bitstring(), _}. renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) -> case cert_to_expire(Cert) of true -> get_certificate(CAUrl, DomainName, PrivateKey); false -> {ok, DomainName, no_expire} end. -spec cert_to_expire({bitstring(), data_cert()}) -> boolean(). cert_to_expire({_DomainName, #data_cert{pem = Pem}}) -> Certificate = pem_to_certificate(Pem), Validity = get_utc_validity(Certificate), %% 30 days before expiration close_to_expire(Validity, 30). -spec close_to_expire(string(), integer()) -> boolean(). close_to_expire(Validity, Days) -> {ValidDate, _ValidTime} = utc_string_to_datetime(Validity), ValidDays = calendar:date_to_gregorian_days(ValidDate), {CurrentDate, _CurrentTime} = calendar:universal_time(), CurrentDays = calendar:date_to_gregorian_days(CurrentDate), CurrentDays > ValidDays - Days. %% %% List Certificates %% -spec list_certificates(verbose_opt()) -> [string()] | [any()] | {error, _}. list_certificates(Verbose) -> case is_valid_verbose_opt(Verbose) of true -> try list_certificates0(Verbose) catch throw:Throw -> Throw; E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), {error, list_certificates} end; false -> String = io_lib:format("Invalid verbose option: ~p", [Verbose]), {invalid_option, String} end. -spec list_certificates0(verbose_opt()) -> [string()] | [any()]. list_certificates0(Verbose) -> Certs = read_certificates_persistent(), [format_certificate(DataCert, Verbose) || {_Key, DataCert} <- Certs]. %% TODO: Make this cleaner and more robust -spec format_certificate(data_cert(), verbose_opt()) -> string(). format_certificate(DataCert, Verbose) -> #data_cert{ domain = DomainName, pem = PemCert, path = Path } = DataCert, try Certificate = pem_to_certificate(PemCert), %% Find the commonName _CommonName = get_commonName(Certificate), %% Find the notAfter date NotAfter = get_notAfter(Certificate), %% Find the subjectAltNames SANs = get_subjectAltNames(Certificate), case Verbose of "plain" -> format_certificate_plain(DomainName, SANs, NotAfter, Path); "verbose" -> format_certificate_verbose(DomainName, SANs, NotAfter, PemCert) end catch E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), fail_format_certificate(DomainName) end. -spec format_certificate_plain(bitstring(), [string()], {expired | ok, string()}, string()) -> string(). format_certificate_plain(DomainName, SANs, NotAfter, Path) -> Result = lists:flatten(io_lib:format( " Domain: ~s~n" "~s" " ~s~n" " Path: ~s", [DomainName, lists:flatten([io_lib:format(" SAN: ~s~n", [SAN]) || SAN <- SANs]), format_validity(NotAfter), Path])), Result. -spec format_certificate_verbose(bitstring(), [string()], {expired | ok, string()}, bitstring()) -> string(). format_certificate_verbose(DomainName, SANs, NotAfter, PemCert) -> Result = lists:flatten(io_lib:format( " Domain: ~s~n" "~s" " ~s~n" " Certificate In PEM format: ~n~s", [DomainName, lists:flatten([io_lib:format(" SAN: ~s~n", [SAN]) || SAN <- SANs]), format_validity(NotAfter), PemCert])), Result. -spec format_validity({'expired' | 'ok', string()}) -> string(). format_validity({expired, NotAfter}) -> io_lib:format("Expired at: ~s UTC", [NotAfter]); format_validity({ok, NotAfter}) -> io_lib:format("Valid until: ~s UTC", [NotAfter]). -spec fail_format_certificate(bitstring()) -> string(). fail_format_certificate(DomainName) -> Result = lists:flatten(io_lib:format( " Domain: ~s~n" " Failed to format Certificate", [DomainName])), Result. -spec get_commonName(#'Certificate'{}) -> string(). get_commonName(#'Certificate'{tbsCertificate = TbsCertificate}) -> #'TBSCertificate'{ subject = {rdnSequence, SubjectList} } = TbsCertificate, %% TODO: Not the best way to find the commonName ShallowSubjectList = [Attribute || [Attribute] <- SubjectList], {_, _, CommonName} = lists:keyfind(attribute_oid(commonName), 2, ShallowSubjectList), %% TODO: Remove the length-encoding from the commonName before returning it CommonName. -spec get_notAfter(#'Certificate'{}) -> {expired | ok, string()}. get_notAfter(Certificate) -> UtcTime = get_utc_validity(Certificate), %% TODO: Find a library function to decode utc time [Y1,Y2,MO1,MO2,D1,D2,H1,H2,MI1,MI2,S1,S2,$Z] = UtcTime, YEAR = case list_to_integer([Y1,Y2]) >= 50 of true -> "19" ++ [Y1,Y2]; _ -> "20" ++ [Y1,Y2] end, NotAfter = lists:flatten(io_lib:format("~s-~s-~s ~s:~s:~s", [YEAR, [MO1,MO2], [D1,D2], [H1,H2], [MI1,MI2], [S1,S2]])), case close_to_expire(UtcTime, 0) of true -> {expired, NotAfter}; false -> {ok, NotAfter} end. -spec get_subjectAltNames(#'Certificate'{}) -> [string()]. get_subjectAltNames(#'Certificate'{tbsCertificate = TbsCertificate}) -> #'TBSCertificate'{ extensions = Exts } = TbsCertificate, EncodedSANs = [Val || #'Extension'{extnID = Oid, extnValue = Val} <- Exts, Oid =:= attribute_oid(subjectAltName)], lists:flatmap( fun(EncSAN) -> SANs0 = public_key:der_decode('SubjectAltName', EncSAN), [Name || {dNSName, Name} <- SANs0] end, EncodedSANs). -spec get_utc_validity(#'Certificate'{}) -> string(). get_utc_validity(#'Certificate'{tbsCertificate = TbsCertificate}) -> #'TBSCertificate'{ validity = Validity } = TbsCertificate, #'Validity'{notAfter = {utcTime, UtcTime}} = Validity, UtcTime. %% %% Revoke Certificate %% revoke_certificate(DomainOrFile) -> case is_valid_revoke_cert(DomainOrFile) of true -> revoke_certificates(DomainOrFile); false -> String = io_lib:format("Bad argument: ~s", [DomainOrFile]), {invalid_argument, String} end. -spec revoke_certificates(string()) -> {ok, deleted} | {error, _}. revoke_certificates(DomainOrFile) -> try CAUrl = get_config_ca_url(), revoke_certificate0(CAUrl, DomainOrFile) catch throw:Throw -> Throw; E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), {error, revoke_certificate} end. -spec revoke_certificate0(url(), string()) -> {ok, deleted}. revoke_certificate0(CAUrl, DomainOrFile) -> ParsedCert = parse_revoke_cert_argument(DomainOrFile), revoke_certificate1(CAUrl, ParsedCert). -spec revoke_certificate1(url(), {domain, bitstring()} | {file, file:filename()}) -> {ok, deleted}. revoke_certificate1(CAUrl, {domain, Domain}) -> case domain_certificate_exists(Domain) of {Domain, Cert = #data_cert{pem=PemCert}} -> ok = revoke_certificate2(CAUrl, PemCert), ok = remove_certificate_persistent(Cert), {ok, deleted}; false -> ?ERROR_MSG("Certificate for domain: ~p not found", [Domain]), throw({error, not_found}) end; revoke_certificate1(CAUrl, {file, File}) -> case file:read_file(File) of {ok, Pem} -> ok = revoke_certificate2(CAUrl, Pem), {ok, deleted}; {error, Reason} -> ?ERROR_MSG("Error: ~p reading pem certificate-key file: ~p", [Reason, File]), throw({error, Reason}) end. -spec revoke_certificate2(url(), pem()) -> ok. revoke_certificate2(CAUrl, PemEncodedCert) -> {Certificate, CertPrivateKey} = prepare_certificate_revoke(PemEncodedCert), {ok, Dirs, Nonce} = ejabberd_acme_comm:directory(CAUrl), Req = [{<<"certificate">>, Certificate}], {ok, [], _Nonce1} = ejabberd_acme_comm:revoke_cert(Dirs, CertPrivateKey, Req, Nonce), ok. -spec parse_revoke_cert_argument(string()) -> {domain, bitstring()} | {file, file:filename()}. parse_revoke_cert_argument([$f, $i, $l, $e, $:|File]) -> {file, File}; parse_revoke_cert_argument([$d, $o, $m, $a, $i, $n, $: | Domain]) -> {domain, list_to_bitstring(Domain)}. -spec prepare_certificate_revoke(pem()) -> {bitstring(), jose_jwk:key()}. prepare_certificate_revoke(PemEncodedCert) -> PemList = public_key:pem_decode(PemEncodedCert), PemCertEnc = lists:keyfind('Certificate', 1, PemList), PemCert = public_key:pem_entry_decode(PemCertEnc), DerCert = public_key:der_encode('Certificate', PemCert), Base64Cert = base64url:encode(DerCert), {ok, Key} = find_private_key_in_pem(PemEncodedCert), {Base64Cert, Key}. -spec domain_certificate_exists(bitstring()) -> {bitstring(), data_cert()} | false. domain_certificate_exists(Domain) -> Certs = read_certificates_persistent(), lists:keyfind(Domain, 1, Certs). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Certificate Request Functions %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% For now we accept only generating a key of %% specific type for signing the csr -spec make_csr(proplist(), [{dNSName, bitstring()}]) -> {binary(), jose_jwk:key()}. make_csr(Attributes, SANs) -> Key = generate_key(), {_, KeyKey} = jose_jwk:to_key(Key), KeyPub = to_public(Key), try SubPKInfoAlgo = subject_pk_info_algo(KeyPub), {ok, RawBinPubKey} = raw_binary_public_key(KeyPub), SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey), {ok, Subject} = attributes_from_list(Attributes), ExtensionRequest = extension_request(SANs), CRI = certificate_request_info(SubPKInfo, Subject, ExtensionRequest), {ok, EncodedCRI} = der_encode( 'CertificationRequestInfo', CRI), SignedCRI = public_key:sign(EncodedCRI, 'sha256', KeyKey), SignatureAlgo = signature_algo(Key, 'sha256'), CSR = certification_request(CRI, SignatureAlgo, SignedCRI), {ok, DerCSR} = der_encode( 'CertificationRequest', CSR), Result = base64url:encode(DerCSR), {Result, Key} catch _:{badmatch, {error, bad_public_key}} -> {error, bad_public_key}; _:{badmatch, {error, bad_attributes}} -> {error, bad_public_key}; _:{badmatch, {error, der_encode}} -> {error, der_encode} end. subject_pk_info_algo(_KeyPub) -> #'SubjectPublicKeyInfoAlgorithm'{ algorithm = ?'id-ecPublicKey', parameters = {asn1_OPENTYPE,<<6,8,42,134,72,206,61,3,1,7>>} }. subject_pk_info(Algo, RawBinPubKey) -> #'SubjectPublicKeyInfo-PKCS-10'{ algorithm = Algo, subjectPublicKey = RawBinPubKey }. extension(SANs) -> #'Extension'{ extnID = attribute_oid(subjectAltName), critical = false, extnValue = public_key:der_encode('SubjectAltName', SANs)}. extension_request(SANs) -> #'AttributePKCS-10'{ type = ?'pkcs-9-at-extensionRequest', values = [{'asn1_OPENTYPE', public_key:der_encode( 'ExtensionRequest', [extension(SANs)])}] }. certificate_request_info(SubPKInfo, Subject, ExtensionRequest) -> #'CertificationRequestInfo'{ version = 0, subject = Subject, subjectPKInfo = SubPKInfo, attributes = [ExtensionRequest] }. signature_algo(_Key, _Hash) -> #'CertificationRequest_signatureAlgorithm'{ algorithm = ?'ecdsa-with-SHA256', parameters = asn1_NOVALUE }. certification_request(CRI, SignatureAlgo, SignedCRI) -> #'CertificationRequest'{ certificationRequestInfo = CRI, signatureAlgorithm = SignatureAlgo, signature = SignedCRI }. raw_binary_public_key(KeyPub) -> try {_, RawPubKey} = jose_jwk:to_key(KeyPub), {{_, RawBinPubKey}, _} = RawPubKey, {ok, RawBinPubKey} catch _:_ -> ?ERROR_MSG("Bad public key: ~p~n", [KeyPub]), {error, bad_public_key} end. der_encode(Type, Term) -> try {ok, public_key:der_encode(Type, Term)} catch _:_ -> ?ERROR_MSG("Cannot DER encode: ~p, with asn1type: ~p", [Term, Type]), {error, der_encode} end. %% %% Attributes Parser %% attributes_from_list(Attrs) -> ParsedAttrs = [attribute_parser_fun(Attr) || Attr <- Attrs], case lists:any(fun is_error/1, ParsedAttrs) of true -> {error, bad_attributes}; false -> {ok, {rdnSequence, [[PAttr] || PAttr <- ParsedAttrs]}} end. attribute_parser_fun({AttrName, AttrVal}) -> try #'AttributeTypeAndValue'{ type = attribute_oid(AttrName), %% TODO: Check if every attribute should be encoded as %% common name. Actually it doesn't matter in %% practice. Only in theory in order to have cleaner code. value = public_key:der_encode('X520CommonName', {printableString, AttrVal}) %% value = length_bitstring(list_to_bitstring(AttrVal)) } catch _:_ -> ?ERROR_MSG("Bad attribute: ~p~n", [{AttrName, AttrVal}]), {error, bad_attributes} end. -spec attribute_oid(atom()) -> tuple() | no_return(). attribute_oid(commonName) -> ?'id-at-commonName'; attribute_oid(countryName) -> ?'id-at-countryName'; attribute_oid(stateOrProvinceName) -> ?'id-at-stateOrProvinceName'; attribute_oid(localityName) -> ?'id-at-localityName'; attribute_oid(organizationName) -> ?'id-at-organizationName'; attribute_oid(subjectAltName) -> ?'id-ce-subjectAltName'; attribute_oid(_) -> error(bad_attributes). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Useful funs %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec location_to_id(url()) -> {ok, string()} | {error, not_found}. location_to_id(Url0) -> Url = string:strip(Url0, right, $/), case string:rchr(Url, $/) of 0 -> ?ERROR_MSG("Couldn't find id in url: ~p~n", [Url]), {error, not_found}; Ind -> {ok, string:sub_string(Url, Ind+1)} end. -spec get_challenges(proplist()) -> [{proplist()}]. get_challenges(Body) -> {<<"challenges">>, Challenges} = proplists:lookup(<<"challenges">>, Body), Challenges. -spec not_before_not_after() -> {binary(), binary()}. not_before_not_after() -> {Date, Time} = calendar:universal_time(), NotBefore = encode_calendar_datetime({Date, Time}), %% The certificate will be valid for 90 Days after today AfterDate = add_days_to_date(90, Date), NotAfter = encode_calendar_datetime({AfterDate, Time}), {NotBefore, NotAfter}. -spec to_public(jose_jwk:key()) -> jose_jwk:key(). to_public(PrivateKey) -> jose_jwk:to_public(PrivateKey). -spec pem_to_certificate(pem()) -> #'Certificate'{}. pem_to_certificate(Pem) -> PemList = public_key:pem_decode(Pem), PemEntryCert = lists:keyfind('Certificate', 1, PemList), Certificate = public_key:pem_entry_decode(PemEntryCert), Certificate. -spec add_days_to_date(integer(), calendar:date()) -> calendar:date(). add_days_to_date(Days, Date) -> Date1 = calendar:date_to_gregorian_days(Date), calendar:gregorian_days_to_date(Date1 + Days). -spec encode_calendar_datetime(calendar:datetime()) -> binary(). encode_calendar_datetime({{Year, Month, Day}, {Hour, Minute, Second}}) -> list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT" "~2..0B:~2..0B:~2..0BZ", [Year, Month, Day, Hour, Minute, Second])). %% TODO: Find a better and more robust way to parse the utc string -spec utc_string_to_datetime(string()) -> calendar:datetime(). utc_string_to_datetime(UtcString) -> try [Y1,Y2,MO1,MO2,D1,D2,H1,H2,MI1,MI2,S1,S2,$Z] = UtcString, Year = list_to_integer("20" ++ [Y1,Y2]), Month = list_to_integer([MO1, MO2]), Day = list_to_integer([D1,D2]), Hour = list_to_integer([H1,H2]), Minute = list_to_integer([MI1,MI2]), Second = list_to_integer([S1,S2]), {{Year, Month, Day}, {Hour, Minute, Second}} catch _:_ -> ?ERROR_MSG("Unable to parse UTC string", []), throw({error, utc_string_to_datetime}) end. -spec find_private_key_in_pem(pem()) -> {ok, jose_jwk:key()} | false. find_private_key_in_pem(Pem) -> PemList = public_key:pem_decode(Pem), case find_private_key_in_pem1(private_key_types(), PemList) of false -> false; PemKey -> Key = public_key:pem_entry_decode(PemKey), JoseKey = jose_jwk:from_key(Key), {ok, JoseKey} end. -spec find_private_key_in_pem1([public_key:pki_asn1_type()], [public_key:pem_entry()]) -> public_key:pem_entry() | false. find_private_key_in_pem1([], _PemList) -> false; find_private_key_in_pem1([Type|Types], PemList) -> case lists:keyfind(Type, 1, PemList) of false -> find_private_key_in_pem1(Types, PemList); Key -> Key end. -spec parse_domain_string(string()) -> [string()]. parse_domain_string(DomainString) -> string:tokens(DomainString, ";"). -spec private_key_types() -> [public_key:pki_asn1_type()]. private_key_types() -> ['RSAPrivateKey', 'DSAPrivateKey', 'ECPrivateKey']. -spec find_all_sub_domains(bitstring()) -> [bitstring()]. find_all_sub_domains(DomainName) -> AllRoutes = ejabberd_router:get_all_routes(), DomainLen = size(DomainName), [Route || Route <- AllRoutes, binary:longest_common_suffix([DomainName, Route]) =:= DomainLen]. -spec is_error(_) -> boolean(). is_error({error, _}) -> true; is_error({error, _, _}) -> true; is_error(_) -> false. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Handle the persistent data structure %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec data_empty() -> []. data_empty() -> []. %% %% Account %% -spec data_get_account(acme_data()) -> {ok, list(), url(), jose_jwk:key()} | none. data_get_account(Data) -> case lists:keyfind(account, 1, Data) of {account, #data_acc{id = AccId, ca_url = CAUrl, key = PrivateKey}} -> {ok, AccId, CAUrl, PrivateKey}; false -> none end. -spec data_set_account(acme_data(), {list(), url(), jose_jwk:key()}) -> acme_data(). data_set_account(Data, {AccId, CAUrl, PrivateKey}) -> NewAcc = {account, #data_acc{id = AccId, ca_url = CAUrl, key = PrivateKey}}, lists:keystore(account, 1, Data, NewAcc). %% %% Certificates %% -spec data_get_certificates(acme_data()) -> data_certs(). data_get_certificates(Data) -> case lists:keyfind(certs, 1, Data) of {certs, Certs} -> Certs; false -> [] end. -spec data_set_certificates(acme_data(), data_certs()) -> acme_data(). data_set_certificates(Data, NewCerts) -> lists:keystore(certs, 1, Data, {certs, NewCerts}). %% ATM we preserve one certificate for each domain -spec data_add_certificate(acme_data(), data_cert()) -> acme_data(). data_add_certificate(Data, DataCert = #data_cert{domain=Domain}) -> Certs = data_get_certificates(Data), NewCerts = lists:keystore(Domain, 1, Certs, {Domain, DataCert}), data_set_certificates(Data, NewCerts). -spec data_remove_certificate(acme_data(), data_cert()) -> acme_data(). data_remove_certificate(Data, _DataCert = #data_cert{domain=Domain}) -> Certs = data_get_certificates(Data), NewCerts = lists:keydelete(Domain, 1, Certs), data_set_certificates(Data, NewCerts). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Handle Config and Persistence Files %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec persistent_file() -> file:filename(). persistent_file() -> AcmeDir = acme_certs_dir(), filename:join(AcmeDir, "acme.DAT"). %% The persistent file should be read and written only by its owner -spec file_mode() -> 384. file_mode() -> 8#600. -spec read_persistent() -> {ok, acme_data()} | no_return(). read_persistent() -> case file:read_file(persistent_file()) of {ok, Binary} -> {ok, binary_to_term(Binary)}; {error, enoent} -> create_persistent(), {ok, data_empty()}; {error, Reason} -> ?ERROR_MSG("Error: ~p reading acme data file", [Reason]), throw({error, Reason}) end. -spec write_persistent(acme_data()) -> ok | no_return(). write_persistent(Data) -> Binary = term_to_binary(Data), case file:write_file(persistent_file(), Binary) of ok -> ok; {error, Reason} -> ?ERROR_MSG("Error: ~p writing acme data file", [Reason]), throw({error, Reason}) end. -spec create_persistent() -> ok | no_return(). create_persistent() -> Binary = term_to_binary(data_empty()), case file:write_file(persistent_file(), Binary) of ok -> case file:change_mode(persistent_file(), file_mode()) of ok -> ok; {error, Reason} -> ?ERROR_MSG("Error: ~p changing acme data file mode", [Reason]), throw({error, Reason}) end; {error, Reason} -> ?ERROR_MSG("Error: ~p creating acme data file", [Reason]), throw({error, Reason}) end. -spec write_account_persistent({list(), url(), jose_jwk:key()}) -> ok | no_return(). write_account_persistent({AccId, CAUrl, PrivateKey}) -> {ok, Data} = read_persistent(), NewData = data_set_account(Data, {AccId, CAUrl, PrivateKey}), ok = write_persistent(NewData). -spec read_account_persistent() -> {ok, list(), url(), jose_jwk:key()} | none. read_account_persistent() -> {ok, Data} = read_persistent(), data_get_account(Data). -spec read_certificates_persistent() -> data_certs(). read_certificates_persistent() -> {ok, Data} = read_persistent(), data_get_certificates(Data). -spec add_certificate_persistent(data_cert()) -> ok. add_certificate_persistent(DataCert) -> {ok, Data} = read_persistent(), NewData = data_add_certificate(Data, DataCert), ok = write_persistent(NewData). -spec remove_certificate_persistent(data_cert()) -> ok. remove_certificate_persistent(DataCert) -> {ok, Data} = read_persistent(), NewData = data_remove_certificate(Data, DataCert), ok = write_persistent(NewData). -spec save_certificate({ok, bitstring(), binary()} | {error, _, _}) -> {ok, bitstring(), saved} | {error, bitstring(), _}. save_certificate({error, _, _} = Error) -> Error; save_certificate({ok, DomainName, Cert}) -> try CertDir = acme_certs_dir(), DomainString = bitstring_to_list(DomainName), CertificateFile = filename:join([CertDir, DomainString ++ ".pem"]), %% TODO: At some point do the following using a Transaction so %% that there is no certificate saved if it cannot be added in %% certificate persistent storage write_cert(CertificateFile, Cert, DomainName), ok = ejabberd_pkix:add_certfile(CertificateFile), DataCert = #data_cert{ domain = DomainName, pem = Cert, path = CertificateFile }, add_certificate_persistent(DataCert), {ok, DomainName, saved} catch throw:Throw -> Throw; E:R -> ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]), {error, DomainName, saving} end. -spec save_renewed_certificate({ok, bitstring(), _} | {error, _, _}) -> {ok, bitstring(), _} | {error, bitstring(), _}. save_renewed_certificate({error, _, _} = Error) -> Error; save_renewed_certificate({ok, _, no_expire} = Cert) -> Cert; save_renewed_certificate({ok, DomainName, Cert}) -> save_certificate({ok, DomainName, Cert}). -spec register_certfiles() -> ok. register_certfiles() -> Dir = acme_certs_dir(), Paths = filelib:wildcard(filename:join(Dir, "*.pem")), lists:foreach( fun(Path) -> ejabberd_pkix:add_certfile(Path) end, Paths). -spec write_cert(file:filename(), binary(), bitstring()) -> {ok, bitstring(), saved}. write_cert(CertificateFile, Cert, DomainName) -> case file:write_file(CertificateFile, Cert) of ok -> case file:change_mode(CertificateFile, file_mode()) of ok -> ok; {error, Why} -> ?WARNING_MSG("Failed to change mode of file ~s: ~s", [CertificateFile, file:format_error(Why)]) end, {ok, DomainName, saved}; {error, Reason} -> ?ERROR_MSG("Error: ~p saving certificate at file: ~p", [Reason, CertificateFile]), throw({error, DomainName, saving}) end. -spec get_config_acme() -> acme_config(). get_config_acme() -> case ejabberd_config:get_option(acme, undefined) of undefined -> ?WARNING_MSG("No acme configuration has been specified", []), %% throw({error, configuration}); []; Acme -> Acme end. -spec get_config_contact() -> bitstring(). get_config_contact() -> Acme = get_config_acme(), case lists:keyfind(contact, 1, Acme) of {contact, Contact} -> Contact; false -> ?WARNING_MSG("No contact has been specified in configuration", []), ?DEFAULT_CONFIG_CONTACT %% throw({error, configuration_contact}) end. -spec get_config_ca_url() -> url(). get_config_ca_url() -> Acme = get_config_acme(), case lists:keyfind(ca_url, 1, Acme) of {ca_url, CAUrl} -> CAUrl; false -> ?ERROR_MSG("No CA url has been specified in configuration", []), ?DEFAULT_CONFIG_CA_URL %% throw({error, configuration_ca_url}) end. -spec get_config_hosts() -> [bitstring()]. get_config_hosts() -> case ejabberd_config:get_option(hosts, undefined) of undefined -> ?ERROR_MSG("No hosts have been specified in configuration", []), throw({error, configuration_hosts}); Hosts -> Hosts end. -spec acme_certs_dir() -> file:filename(). acme_certs_dir() -> filename:join(ejabberd_pkix:certs_dir(), "acme"). generate_key() -> jose_jwk:generate_key({ec, secp256r1}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Option Parsing Code %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec opt_type(acme) -> fun((acme_config()) -> (acme_config())); (atom()) -> [atom()]. opt_type(acme) -> fun(L) -> lists:map( fun({ca_url, URL}) -> URL1 = binary_to_list(URL), {ok, _} = http_uri:parse(URL1), {ca_url, URL1}; ({contact, Contact}) -> [<<_, _/binary>>, <<_, _/binary>>] = binary:split(Contact, <<":">>), {contact, Contact} end, L) end; opt_type(_) -> [acme]. ejabberd-18.01/src/elixir_logger_backend.erl0000644000232200023220000001040313225664356021436 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : elixir_logger_backend.erl %%% Author : Mickael Remond %%% Purpose : This module bridges lager logs to Elixir Logger. %%% Created : 9 March 2016 by Mickael Remond %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(elixir_logger_backend). -behaviour(gen_event). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). -record(state, {level = debug}). init(Opts) -> Level = proplists:get_value(level, Opts, debug), State = #state{level = Level}, {ok, State}. %% @private handle_event({log, LagerMsg}, State) -> #{mode := Mode, truncate := Truncate, level := MinLevel, utc_log := UTCLog} = 'Elixir.Logger.Config':'__data__'(), MsgLevel = severity_to_level(lager_msg:severity(LagerMsg)), case {lager_util:is_loggable(LagerMsg, lager_util:level_to_num(State#state.level), ?MODULE), 'Elixir.Logger':compare_levels(MsgLevel, MinLevel)} of {_, lt}-> {ok, State}; {true, _} -> Metadata = normalize_pid(lager_msg:metadata(LagerMsg)), Message = 'Elixir.Logger.Utils':truncate(lager_msg:message(LagerMsg), Truncate), Timestamp = timestamp(lager_msg:timestamp(LagerMsg), UTCLog), GroupLeader = case proplists:get_value(pid, Metadata, self()) of Pid when is_pid(Pid) -> erlang:process_info(self(), group_leader); _ -> {group_leader, self()} end, notify(Mode, {MsgLevel, GroupLeader, {'Elixir.Logger', Message, Timestamp, Metadata}}), {ok, State}; _ -> {ok, State} end; handle_event(_Msg, State) -> {ok, State}. %% @private %% TODO Handle loglevels handle_call(get_loglevel, State) -> {ok, lager_util:config_to_mask(State#state.level), State}; handle_call({set_loglevel, Config}, State) -> {ok, ok, State#state{level = Config}}. %% @private handle_info(_Msg, State) -> {ok, State}. %% @private terminate(_Reason, _State) -> ok. %% @private code_change(_OldVsn, State, _Extra) -> {ok, State}. notify(sync, Msg) -> gen_event:sync_notify('Elixir.Logger', Msg); notify(async, Msg) -> gen_event:notify('Elixir.Logger', Msg). normalize_pid(Metadata) -> case proplists:get_value(pid, Metadata) of Pid when is_pid(Pid) -> Metadata; Pid when is_list(Pid) -> M1 = proplists:delete(pid, Metadata), case catch erlang:list_to_pid(Pid) of {'EXIT', _} -> M1; PidAsPid -> [{pid, PidAsPid}|M1] end; _ -> proplists:delete(pid, Metadata) end. %% Return timestamp with milliseconds timestamp(Time, UTCLog) -> {_, _, Micro} = p1_time_compat:timestamp(), {Date, {Hours, Minutes, Seconds}} = case UTCLog of true -> calendar:now_to_universal_time(Time); false -> calendar:now_to_local_time(Time) end, {Date, {Hours, Minutes, Seconds, Micro div 1000}}. severity_to_level(debug) -> debug; severity_to_level(info) -> info; severity_to_level(notice) -> info; severity_to_level(warning) -> warn; severity_to_level(error) -> error; severity_to_level(critical) -> error; severity_to_level(alert) -> error; severity_to_level(emergency) -> error. ejabberd-18.01/src/pubsub_index.erl0000644000232200023220000000411513225664356017626 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pubsub_index.erl %%% Author : Christophe Romain %%% Purpose : Provide uniq integer index for pubsub node %%% Created : 30 Apr 2009 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %% important note: %% new/1 and free/2 MUST be called inside a transaction bloc -module(pubsub_index). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, new/1, free/2]). init(_Host, _ServerHost, _Opts) -> ejabberd_mnesia:create(?MODULE, pubsub_index, [{disc_copies, [node()]}, {attributes, record_info(fields, pubsub_index)}]). new(Index) -> case mnesia:read({pubsub_index, Index}) of [I] -> case I#pubsub_index.free of [] -> Id = I#pubsub_index.last + 1, mnesia:write(I#pubsub_index{last = Id}), Id; [Id | Free] -> mnesia:write(I#pubsub_index{free = Free}), Id end; _ -> mnesia:write(#pubsub_index{index = Index, last = 1, free = []}), 1 end. free(Index, Id) -> case mnesia:read({pubsub_index, Index}) of [I] -> Free = I#pubsub_index.free, mnesia:write(I#pubsub_index{free = [Id | Free]}); _ -> ok end. ejabberd-18.01/src/mod_carboncopy_sql.erl0000644000232200023220000000573513225664356021025 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 29 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_carboncopy_sql). -behaviour(mod_carboncopy). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/2, enable/4, disable/3, list/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(Host, _Opts) -> clean_table(Host). enable(LUser, LServer, LResource, NS) -> NodeS = erlang:atom_to_binary(node(), latin1), case ?SQL_UPSERT(LServer, "carboncopy", ["!username=%(LUser)s", "!server_host=%(LServer)s", "!resource=%(LResource)s", "namespace=%(NS)s", "node=%(NodeS)s"]) of ok -> ok; _Err -> {error, db_failure} end. disable(LUser, LServer, LResource) -> case ejabberd_sql:sql_query( LServer, ?SQL("delete from carboncopy where username=%(LUser)s " "and %(LServer)H and resource=%(LResource)s")) of {updated, _} -> ok; _Err -> {error, db_failure} end. list(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(resource)s, @(namespace)s, @(node)s from carboncopy " "where username=%(LUser)s and %(LServer)H")) of {selected, Rows} -> {ok, [{Resource, NS, binary_to_atom(Node, latin1)} || {Resource, NS, Node} <- Rows]}; _Err -> {error, db_failure} end. %%%=================================================================== %%% Internal functions %%%=================================================================== clean_table(LServer) -> NodeS = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'carboncopy' table...", []), case ejabberd_sql:sql_query( LServer, ?SQL("delete from carboncopy where node=%(NodeS)s")) of {updated, _} -> ok; Err -> ?ERROR_MSG("failed to clean 'carboncopy' table: ~p", [Err]), {error, db_failure} end. ejabberd-18.01/src/ejabberd_bosh.erl0000644000232200023220000010604513225664356017715 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_bosh.erl %%% Author : Evgeniy Khramtsov %%% Purpose : Manage BOSH sockets %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_bosh). -protocol({xep, 124, '1.11'}). -protocol({xep, 206, '1.4'}). -behaviour(p1_fsm). %% API -export([start/2, start/3, start_link/3]). -export([send_xml/2, setopts/2, controlling_process/2, migrate/3, become_controller/2, reset_stream/1, change_shaper/2, monitor/1, close/1, sockname/1, peername/1, process_request/3, send/2, change_controller/2]). %% gen_fsm callbacks -export([init/1, wait_for_session/2, wait_for_session/3, active/2, active/3, handle_event/3, print_state/1, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -include("bosh.hrl"). %%-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). -endif. -define(BOSH_VERSION, <<"1.11">>). -define(NS_BOSH, <<"urn:xmpp:xbosh">>). -define(NS_HTTP_BIND, <<"http://jabber.org/protocol/httpbind">>). -define(DEFAULT_MAXPAUSE, 120). -define(DEFAULT_WAIT, 300). -define(DEFAULT_HOLD, 1). -define(DEFAULT_POLLING, 2). -define(DEFAULT_INACTIVITY, 30). -define(MAX_SHAPED_REQUESTS_QUEUE_LEN, 1000). -define(SEND_TIMEOUT, 15000). -type bosh_socket() :: {http_bind, pid(), {inet:ip_address(), inet:port_number()}}. -export_type([bosh_socket/0]). -record(state, {host = <<"">> :: binary(), sid = <<"">> :: binary(), el_ibuf :: p1_queue:queue(), el_obuf :: p1_queue:queue(), shaper_state = none :: shaper:shaper(), c2s_pid :: pid() | undefined, xmpp_ver = <<"">> :: binary(), inactivity_timer :: reference() | undefined, wait_timer :: reference() | undefined, wait_timeout = ?DEFAULT_WAIT :: timeout(), inactivity_timeout = ?DEFAULT_INACTIVITY :: timeout(), prev_rid = 0 :: non_neg_integer(), prev_key = <<"">> :: binary(), prev_poll :: erlang:timestamp() | undefined, max_concat = unlimited :: unlimited | non_neg_integer(), responses = gb_trees:empty() :: ?TGB_TREE, receivers = gb_trees:empty() :: ?TGB_TREE, shaped_receivers :: p1_queue:queue(), ip :: inet:ip_address(), max_requests = 1 :: non_neg_integer()}). -record(body, {http_reason = <<"">> :: binary(), attrs = [] :: [{any(), any()}], els = [] :: [fxml_stream:xml_stream_el()], size = 0 :: non_neg_integer()}). start(#body{attrs = Attrs} = Body, IP, SID) -> XMPPDomain = get_attr(to, Attrs), SupervisorProc = gen_mod:get_module_proc(XMPPDomain, mod_bosh), case catch supervisor:start_child(SupervisorProc, [Body, IP, SID]) of {ok, Pid} -> {ok, Pid}; {'EXIT', {noproc, _}} -> check_bosh_module(XMPPDomain), {error, module_not_loaded}; Err -> ?ERROR_MSG("Failed to start BOSH session: ~p", [Err]), {error, Err} end. start(StateName, State) -> p1_fsm:start_link(?MODULE, [StateName, State], ?FSMOPTS). start_link(Body, IP, SID) -> p1_fsm:start_link(?MODULE, [Body, IP, SID], ?FSMOPTS). send({http_bind, FsmRef, IP}, Packet) -> send_xml({http_bind, FsmRef, IP}, Packet). send_xml({http_bind, FsmRef, _IP}, Packet) -> case catch p1_fsm:sync_send_all_state_event(FsmRef, {send_xml, Packet}, ?SEND_TIMEOUT) of {'EXIT', {timeout, _}} -> {error, timeout}; {'EXIT', _} -> {error, einval}; Res -> Res end. setopts({http_bind, FsmRef, _IP}, Opts) -> case lists:member({active, once}, Opts) of true -> p1_fsm:send_all_state_event(FsmRef, {activate, self()}); _ -> case lists:member({active, false}, Opts) of true -> case catch p1_fsm:sync_send_all_state_event(FsmRef, deactivate_socket) of {'EXIT', _} -> {error, einval}; Res -> Res end; _ -> ok end end. controlling_process(_Socket, _Pid) -> ok. become_controller(FsmRef, C2SPid) -> p1_fsm:send_all_state_event(FsmRef, {become_controller, C2SPid}). change_controller({http_bind, FsmRef, _IP}, C2SPid) -> become_controller(FsmRef, C2SPid). reset_stream({http_bind, _FsmRef, _IP} = Socket) -> Socket. change_shaper({http_bind, FsmRef, _IP}, Shaper) -> p1_fsm:send_all_state_event(FsmRef, {change_shaper, Shaper}). monitor({http_bind, FsmRef, _IP}) -> erlang:monitor(process, FsmRef). close({http_bind, FsmRef, _IP}) -> catch p1_fsm:sync_send_all_state_event(FsmRef, close). sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}. peername({http_bind, _FsmRef, IP}) -> {ok, IP}. migrate(FsmRef, Node, After) when node(FsmRef) == node() -> catch erlang:send_after(After, FsmRef, {migrate, Node}); migrate(_FsmRef, _Node, _After) -> ok. process_request(Data, IP, Type) -> Opts1 = ejabberd_c2s_config:get_c2s_limits(), Opts = case Type of xml -> [{xml_socket, true} | Opts1]; json -> Opts1 end, MaxStanzaSize = case lists:keysearch(max_stanza_size, 1, Opts) of {value, {_, Size}} -> Size; _ -> infinity end, PayloadSize = iolist_size(Data), if PayloadSize > MaxStanzaSize -> http_error(403, <<"Request Too Large">>, Type); true -> case decode_body(Data, PayloadSize, Type) of {ok, #body{attrs = Attrs} = Body} -> SID = get_attr(sid, Attrs), To = get_attr(to, Attrs), if SID == <<"">>, To == <<"">> -> bosh_response_with_msg(#body{http_reason = <<"Missing 'to' attribute">>, attrs = [{type, <<"terminate">>}, {condition, <<"improper-addressing">>}]}, Type, Body); SID == <<"">> -> case start(Body, IP, make_sid()) of {ok, Pid} -> process_request(Pid, Body, IP, Type); _Err -> bosh_response_with_msg(#body{http_reason = <<"Failed to start BOSH session">>, attrs = [{type, <<"terminate">>}, {condition, <<"internal-server-error">>}]}, Type, Body) end; true -> case mod_bosh:find_session(SID) of {ok, Pid} -> process_request(Pid, Body, IP, Type); error -> bosh_response_with_msg(#body{http_reason = <<"Session ID mismatch">>, attrs = [{type, <<"terminate">>}, {condition, <<"item-not-found">>}]}, Type, Body) end end; {error, Reason} -> http_error(400, Reason, Type) end end. process_request(Pid, Req, _IP, Type) -> case catch p1_fsm:sync_send_event(Pid, Req, infinity) of #body{} = Resp -> bosh_response(Resp, Type); {'EXIT', {Reason, _}} when Reason == noproc; Reason == normal -> bosh_response(#body{http_reason = <<"BOSH session not found">>, attrs = [{type, <<"terminate">>}, {condition, <<"item-not-found">>}]}, Type); {'EXIT', _} -> bosh_response(#body{http_reason = <<"Unexpected error">>, attrs = [{type, <<"terminate">>}, {condition, <<"internal-server-error">>}]}, Type) end. init([#body{attrs = Attrs}, IP, SID]) -> Opts1 = ejabberd_c2s_config:get_c2s_limits(), Opts2 = [{xml_socket, true} | Opts1], Shaper = none, ShaperState = shaper:new(Shaper), Socket = make_socket(self(), IP), XMPPVer = get_attr('xmpp:version', Attrs), XMPPDomain = get_attr(to, Attrs), {InBuf, Opts} = case gen_mod:get_module_opt( XMPPDomain, mod_bosh, prebind, false) of true -> JID = make_random_jid(XMPPDomain), {buf_new(XMPPDomain), [{jid, JID} | Opts2]}; false -> {buf_in([make_xmlstreamstart(XMPPDomain, XMPPVer)], buf_new(XMPPDomain)), Opts2} end, xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket, [{receiver, self()}|Opts]), Inactivity = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_inactivity, ?DEFAULT_INACTIVITY), MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat, unlimited), ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN), State = #state{host = XMPPDomain, sid = SID, ip = IP, xmpp_ver = XMPPVer, el_ibuf = InBuf, max_concat = MaxConcat, el_obuf = buf_new(XMPPDomain), inactivity_timeout = Inactivity, shaped_receivers = ShapedReceivers, shaper_state = ShaperState}, NewState = restart_inactivity_timer(State), mod_bosh:open_session(SID, self()), {ok, wait_for_session, NewState}; init([StateName, State]) -> mod_bosh:open_session(State#state.sid, self()), case State#state.c2s_pid of C2SPid when is_pid(C2SPid) -> NewSocket = make_socket(self(), State#state.ip), C2SPid ! {change_socket, NewSocket}, NewState = restart_inactivity_timer(State), {ok, StateName, NewState}; _ -> {stop, normal} end. wait_for_session(_Event, State) -> ?ERROR_MSG("unexpected event in 'wait_for_session': ~p", [_Event]), {next_state, wait_for_session, State}. wait_for_session(#body{attrs = Attrs} = Req, From, State) -> RID = get_attr(rid, Attrs), ?DEBUG("got request:~n** RequestID: ~p~n** Request: " "~p~n** From: ~p~n** State: ~p", [RID, Req, From, State]), Wait = min(get_attr(wait, Attrs, undefined), ?DEFAULT_WAIT), Hold = min(get_attr(hold, Attrs, undefined), ?DEFAULT_HOLD), NewKey = get_attr(newkey, Attrs), Type = get_attr(type, Attrs), Requests = Hold + 1, {PollTime, Polling} = if Wait == 0, Hold == 0 -> {p1_time_compat:timestamp(), [{polling, ?DEFAULT_POLLING}]}; true -> {undefined, []} end, MaxPause = gen_mod:get_module_opt(State#state.host, mod_bosh, max_pause, ?DEFAULT_MAXPAUSE), Resp = #body{attrs = [{sid, State#state.sid}, {wait, Wait}, {ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING}, {inactivity, State#state.inactivity_timeout}, {hold, Hold}, {'xmpp:restartlogic', true}, {requests, Requests}, {secure, true}, {maxpause, MaxPause}, {'xmlns:xmpp', ?NS_BOSH}, {'xmlns:stream', ?NS_STREAM}, {from, State#state.host} | Polling]}, {ShaperState, _} = shaper:update(State#state.shaper_state, Req#body.size), State1 = State#state{wait_timeout = Wait, prev_rid = RID, prev_key = NewKey, prev_poll = PollTime, shaper_state = ShaperState, max_requests = Requests}, Els = maybe_add_xmlstreamend(Req#body.els, Type), State2 = route_els(State1, Els), {State3, RespEls} = get_response_els(State2), State4 = stop_inactivity_timer(State3), case RespEls of [] -> State5 = restart_wait_timer(State4), Receivers = gb_trees:insert(RID, {From, Resp}, State5#state.receivers), {next_state, active, State5#state{receivers = Receivers}}; _ -> reply_next_state(State4, Resp#body{els = RespEls}, RID, From) end; wait_for_session(_Event, _From, State) -> ?ERROR_MSG("unexpected sync event in 'wait_for_session': ~p", [_Event]), {reply, {error, badarg}, wait_for_session, State}. active({#body{} = Body, From}, State) -> active1(Body, From, State); active(_Event, State) -> ?ERROR_MSG("unexpected event in 'active': ~p", [_Event]), {next_state, active, State}. active(#body{attrs = Attrs, size = Size} = Req, From, State) -> ?DEBUG("got request:~n** Request: ~p~n** From: " "~p~n** State: ~p", [Req, From, State]), {ShaperState, Pause} = shaper:update(State#state.shaper_state, Size), State1 = State#state{shaper_state = ShaperState}, if Pause > 0 -> TRef = start_shaper_timer(Pause), try p1_queue:in({TRef, From, Req}, State1#state.shaped_receivers) of Q -> State2 = stop_inactivity_timer(State1), {next_state, active, State2#state{shaped_receivers = Q}} catch error:full -> cancel_timer(TRef), RID = get_attr(rid, Attrs), reply_stop(State1, #body{http_reason = <<"Too many requests">>, attrs = [{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"policy-violation">>}]}, From, RID) end; true -> active1(Req, From, State1) end; active(_Event, _From, State) -> ?ERROR_MSG("unexpected sync event in 'active': ~p", [_Event]), {reply, {error, badarg}, active, State}. active1(#body{attrs = Attrs} = Req, From, State) -> RID = get_attr(rid, Attrs), Key = get_attr(key, Attrs), IsValidKey = is_valid_key(State#state.prev_key, Key), IsOveractivity = is_overactivity(State#state.prev_poll), Type = get_attr(type, Attrs), if RID > State#state.prev_rid + State#state.max_requests -> reply_stop(State, #body{http_reason = <<"Request ID is out of range">>, attrs = [{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"item-not-found">>}]}, From, RID); RID > State#state.prev_rid + 1 -> State1 = restart_inactivity_timer(State), Receivers = gb_trees:insert(RID, {From, Req}, State1#state.receivers), {next_state, active, State1#state{receivers = Receivers}}; RID =< State#state.prev_rid -> %% TODO: do we need to check 'key' here? It seems so... case gb_trees:lookup(RID, State#state.responses) of {value, PrevBody} -> {next_state, active, do_reply(State, From, PrevBody, RID)}; none -> State1 = drop_holding_receiver(State), State2 = stop_inactivity_timer(State1), State3 = restart_wait_timer(State2), Receivers = gb_trees:insert(RID, {From, Req}, State3#state.receivers), {next_state, active, State3#state{receivers = Receivers}} end; not IsValidKey -> reply_stop(State, #body{http_reason = <<"Session key mismatch">>, attrs = [{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"item-not-found">>}]}, From, RID); IsOveractivity -> reply_stop(State, #body{http_reason = <<"Too many requests">>, attrs = [{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"policy-violation">>}]}, From, RID); true -> State1 = stop_inactivity_timer(State), State2 = stop_wait_timer(State1), Els = case get_attr('xmpp:restart', Attrs, false) of true -> XMPPDomain = get_attr(to, Attrs, State#state.host), XMPPVer = get_attr('xmpp:version', Attrs, State#state.xmpp_ver), [make_xmlstreamstart(XMPPDomain, XMPPVer)]; false -> Req#body.els end, State3 = route_els(State2, maybe_add_xmlstreamend(Els, Type)), {State4, RespEls} = get_response_els(State3), NewKey = get_attr(newkey, Attrs, Key), Pause = get_attr(pause, Attrs, undefined), NewPoll = case State#state.prev_poll of undefined -> undefined; _ -> p1_time_compat:timestamp() end, State5 = State4#state{prev_poll = NewPoll, prev_key = NewKey}, if Type == <<"terminate">> -> reply_stop(State5, #body{http_reason = <<"Session close">>, attrs = [{<<"type">>, <<"terminate">>}], els = RespEls}, From, RID); Pause /= undefined -> State6 = drop_holding_receiver(State5), State7 = restart_inactivity_timer(State6, Pause), InBuf = buf_in(RespEls, State7#state.el_ibuf), {next_state, active, State7#state{prev_rid = RID, el_ibuf = InBuf}}; RespEls == [] -> State6 = drop_holding_receiver(State5), State7 = stop_inactivity_timer(State6), State8 = restart_wait_timer(State7), Receivers = gb_trees:insert(RID, {From, #body{}}, State8#state.receivers), {next_state, active, State8#state{prev_rid = RID, receivers = Receivers}}; true -> State6 = drop_holding_receiver(State5), reply_next_state(State6#state{prev_rid = RID}, #body{els = RespEls}, RID, From) end end. handle_event({become_controller, C2SPid}, StateName, State) -> State1 = route_els(State#state{c2s_pid = C2SPid}), {next_state, StateName, State1}; handle_event({change_shaper, Shaper}, StateName, State) -> NewShaperState = shaper:new(Shaper), {next_state, StateName, State#state{shaper_state = NewShaperState}}; handle_event(_Event, StateName, State) -> ?ERROR_MSG("unexpected event in '~s': ~p", [StateName, _Event]), {next_state, StateName, State}. handle_sync_event({send_xml, {xmlstreamstart, _, _} = El}, _From, StateName, State) when State#state.xmpp_ver >= <<"1.0">> -> OutBuf = buf_in([El], State#state.el_obuf), {reply, ok, StateName, State#state{el_obuf = OutBuf}}; handle_sync_event({send_xml, El}, _From, StateName, State) -> OutBuf = buf_in([El], State#state.el_obuf), State1 = State#state{el_obuf = OutBuf}, case gb_trees:lookup(State1#state.prev_rid, State1#state.receivers) of {value, {From, Body}} -> {State2, Els} = get_response_els(State1), {reply, ok, StateName, reply(State2, Body#body{els = Els}, State2#state.prev_rid, From)}; none -> State2 = case p1_queue:out(State1#state.shaped_receivers) of {{value, {TRef, From, Body}}, Q} -> cancel_timer(TRef), p1_fsm:send_event(self(), {Body, From}), State1#state{shaped_receivers = Q}; _ -> State1 end, {reply, ok, StateName, State2} end; handle_sync_event(close, _From, _StateName, State) -> {stop, normal, State}; handle_sync_event(deactivate_socket, _From, StateName, StateData) -> {reply, ok, StateName, StateData#state{c2s_pid = undefined}}; handle_sync_event(_Event, _From, StateName, State) -> ?ERROR_MSG("unexpected sync event in '~s': ~p", [StateName, _Event]), {reply, {error, badarg}, StateName, State}. handle_info({timeout, TRef, wait_timeout}, StateName, #state{wait_timer = TRef} = State) -> {next_state, StateName, drop_holding_receiver(State)}; handle_info({timeout, TRef, inactive}, _StateName, #state{inactivity_timer = TRef} = State) -> {stop, normal, State}; handle_info({timeout, TRef, shaper_timeout}, StateName, State) -> case p1_queue:out(State#state.shaped_receivers) of {{value, {TRef, From, Req}}, Q} -> p1_fsm:send_event(self(), {Req, From}), {next_state, StateName, State#state{shaped_receivers = Q}}; {{value, _}, _} -> ?ERROR_MSG("shaper_timeout mismatch:~n** TRef: ~p~n** " "State: ~p", [TRef, State]), {stop, normal, State}; _ -> {next_state, StateName, State} end; handle_info({migrate, Node}, StateName, State) -> if Node /= node() -> NewState = bounce_receivers(State, migrated), {migrate, NewState, {Node, ?MODULE, start, [StateName, NewState]}, 0}; true -> {next_state, StateName, State} end; handle_info(_Info, StateName, State) -> ?ERROR_MSG("unexpected info:~n** Msg: ~p~n** StateName: ~p", [_Info, StateName]), {next_state, StateName, State}. terminate({migrated, ClonePid}, _StateName, State) -> ?INFO_MSG("Migrating session \"~s\" (c2s_pid = " "~p) to ~p on node ~p", [State#state.sid, State#state.c2s_pid, ClonePid, node(ClonePid)]), mod_bosh:close_session(State#state.sid); terminate(_Reason, _StateName, State) -> mod_bosh:close_session(State#state.sid), case State#state.c2s_pid of C2SPid when is_pid(C2SPid) -> p1_fsm:send_event(C2SPid, closed); _ -> ok end, bounce_receivers(State, closed), bounce_els_from_obuf(State). code_change(_OldVsn, StateName, State, _Extra) -> {ok, StateName, State}. print_state(State) -> State. route_els(#state{el_ibuf = Buf, c2s_pid = C2SPid} = State) -> NewBuf = p1_queue:dropwhile( fun(El) -> p1_fsm:send_event(C2SPid, El), true end, Buf), State#state{el_ibuf = NewBuf}. route_els(State, Els) -> case State#state.c2s_pid of C2SPid when is_pid(C2SPid) -> lists:foreach(fun (El) -> p1_fsm:send_event(C2SPid, El) end, Els), State; _ -> InBuf = buf_in(Els, State#state.el_ibuf), State#state{el_ibuf = InBuf} end. get_response_els(#state{el_obuf = OutBuf, max_concat = MaxConcat} = State) -> {Els, NewOutBuf} = buf_out(OutBuf, MaxConcat), {State#state{el_obuf = NewOutBuf}, Els}. reply(State, Body, RID, From) -> State1 = restart_inactivity_timer(State), Receivers = gb_trees:delete_any(RID, State1#state.receivers), State2 = do_reply(State1, From, Body, RID), case catch gb_trees:take_smallest(Receivers) of {NextRID, {From1, Req}, Receivers1} when NextRID == RID + 1 -> p1_fsm:send_event(self(), {Req, From1}), State2#state{receivers = Receivers1}; _ -> State2#state{receivers = Receivers} end. reply_next_state(State, Body, RID, From) -> State1 = restart_inactivity_timer(State), Receivers = gb_trees:delete_any(RID, State1#state.receivers), State2 = do_reply(State1, From, Body, RID), case catch gb_trees:take_smallest(Receivers) of {NextRID, {From1, Req}, Receivers1} when NextRID == RID + 1 -> active(Req, From1, State2#state{receivers = Receivers1}); _ -> {next_state, active, State2#state{receivers = Receivers}} end. reply_stop(State, Body, From, RID) -> {stop, normal, do_reply(State, From, Body, RID)}. drop_holding_receiver(State) -> RID = State#state.prev_rid, case gb_trees:lookup(RID, State#state.receivers) of {value, {From, Body}} -> State1 = restart_inactivity_timer(State), Receivers = gb_trees:delete_any(RID, State1#state.receivers), State2 = State1#state{receivers = Receivers}, do_reply(State2, From, Body, RID); none -> State end. do_reply(State, From, Body, RID) -> ?DEBUG("send reply:~n** RequestID: ~p~n** Reply: " "~p~n** To: ~p~n** State: ~p", [RID, Body, From, State]), p1_fsm:reply(From, Body), Responses = gb_trees:delete_any(RID, State#state.responses), Responses1 = case gb_trees:size(Responses) of N when N < State#state.max_requests; N == 0 -> Responses; _ -> element(3, gb_trees:take_smallest(Responses)) end, Responses2 = gb_trees:insert(RID, Body, Responses1), State#state{responses = Responses2}. bounce_receivers(State, Reason) -> Receivers = gb_trees:to_list(State#state.receivers), ShapedReceivers = lists:map(fun ({_, From, #body{attrs = Attrs} = Body}) -> RID = get_attr(rid, Attrs), {RID, {From, Body}} end, p1_queue:to_list(State#state.shaped_receivers)), lists:foldl(fun ({RID, {From, Body}}, AccState) -> NewBody = if Reason == closed -> #body{http_reason = <<"Session closed">>, attrs = [{type, <<"terminate">>}, {condition, <<"other-request">>}]}; Reason == migrated -> Body#body{http_reason = <<"Session migrated">>} end, do_reply(AccState, From, NewBody, RID) end, State, Receivers ++ ShapedReceivers). bounce_els_from_obuf(State) -> p1_queue:foreach( fun({xmlstreamelement, El}) -> try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of Pkt when ?is_stanza(Pkt) -> case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of {#jid{}, #jid{}} -> ejabberd_router:route(Pkt); _ -> ok end; _ -> ok catch _:{xmpp_codec, _} -> ok end; (_) -> ok end, State#state.el_obuf). is_valid_key(<<"">>, <<"">>) -> true; is_valid_key(PrevKey, Key) -> str:sha(Key) == PrevKey. is_overactivity(undefined) -> false; is_overactivity(PrevPoll) -> PollPeriod = timer:now_diff(p1_time_compat:timestamp(), PrevPoll) div 1000000, if PollPeriod < (?DEFAULT_POLLING) -> true; true -> false end. make_xmlstreamstart(XMPPDomain, Version) -> VersionEl = case Version of <<"">> -> []; _ -> [{<<"version">>, Version}] end, {xmlstreamstart, <<"stream:stream">>, [{<<"to">>, XMPPDomain}, {<<"xmlns">>, ?NS_CLIENT}, {<<"xmlns:xmpp">>, ?NS_BOSH}, {<<"xmlns:stream">>, ?NS_STREAM} | VersionEl]}. maybe_add_xmlstreamend(Els, <<"terminate">>) -> Els ++ [{xmlstreamend, <<"stream:stream">>}]; maybe_add_xmlstreamend(Els, _) -> Els. encode_body(#body{attrs = Attrs, els = Els}, Type) -> Attrs1 = lists:map(fun ({K, V}) when is_atom(K) -> AmK = iolist_to_binary(atom_to_list(K)), case V of true -> {AmK, <<"true">>}; false -> {AmK, <<"false">>}; I when is_integer(I), I >= 0 -> {AmK, integer_to_binary(I)}; _ -> {AmK, V} end; ({K, V}) -> {K, V} end, Attrs), Attrs2 = [{<<"xmlns">>, ?NS_HTTP_BIND} | Attrs1], {Attrs3, XMLs} = lists:foldr(fun ({xmlstreamraw, XML}, {AttrsAcc, XMLBuf}) -> {AttrsAcc, [XML | XMLBuf]}; ({xmlstreamelement, #xmlel{name = <<"stream:error">>} = El}, {AttrsAcc, XMLBuf}) -> {[{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"remote-stream-error">>}, {<<"xmlns:stream">>, ?NS_STREAM} | AttrsAcc], [encode_element(El, Type) | XMLBuf]}; ({xmlstreamelement, #xmlel{name = <<"stream:features">>} = El}, {AttrsAcc, XMLBuf}) -> {lists:keystore(<<"xmlns:stream">>, 1, AttrsAcc, {<<"xmlns:stream">>, ?NS_STREAM}), [encode_element(El, Type) | XMLBuf]}; ({xmlstreamelement, #xmlel{name = Name, attrs = EAttrs} = El}, {AttrsAcc, XMLBuf}) when Name == <<"message">>; Name == <<"presence">>; Name == <<"iq">> -> NewAttrs = lists:keystore( <<"xmlns">>, 1, EAttrs, {<<"xmlns">>, ?NS_CLIENT}), NewEl = El#xmlel{attrs = NewAttrs}, {AttrsAcc, [encode_element(NewEl, Type) | XMLBuf]}; ({xmlstreamelement, El}, {AttrsAcc, XMLBuf}) -> {AttrsAcc, [encode_element(El, Type) | XMLBuf]}; ({xmlstreamend, _}, {AttrsAcc, XMLBuf}) -> {[{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"remote-stream-error">>} | AttrsAcc], XMLBuf}; ({xmlstreamstart, <<"stream:stream">>, SAttrs}, {AttrsAcc, XMLBuf}) -> StreamID = fxml:get_attr_s(<<"id">>, SAttrs), NewAttrs = case fxml:get_attr_s(<<"version">>, SAttrs) of <<"">> -> [{<<"authid">>, StreamID} | AttrsAcc]; V -> lists:keystore(<<"xmlns:xmpp">>, 1, [{<<"xmpp:version">>, V}, {<<"authid">>, StreamID} | AttrsAcc], {<<"xmlns:xmpp">>, ?NS_BOSH}) end, {NewAttrs, XMLBuf}; ({xmlstreamerror, _}, {AttrsAcc, XMLBuf}) -> {[{<<"type">>, <<"terminate">>}, {<<"condition">>, <<"remote-stream-error">>} | AttrsAcc], XMLBuf}; (_, Acc) -> Acc end, {Attrs2, []}, Els), case XMLs of [] when Type == xml -> [<<">, attrs_to_list(Attrs3), <<"/>">>]; _ when Type == xml -> [<<">, attrs_to_list(Attrs3), $>, XMLs, <<"">>] end. encode_element(El, xml) -> fxml:element_to_binary(El); encode_element(El, json) -> El. decode_body(Data, Size, Type) -> case decode(Data, Type) of #xmlel{name = <<"body">>, attrs = Attrs, children = Els} -> case attrs_to_body_attrs(Attrs) of {error, _} = Err -> Err; BodyAttrs -> case get_attr(rid, BodyAttrs) of <<"">> -> {error, <<"Missing \"rid\" attribute">>}; _ -> Els1 = lists:flatmap(fun (#xmlel{} = El) -> [{xmlstreamelement, El}]; (_) -> [] end, Els), {ok, #body{attrs = BodyAttrs, size = Size, els = Els1}} end end; #xmlel{} -> {error, <<"Unexpected payload">>}; _ when Type == xml -> {error, <<"XML is not well-formed">>}; _ when Type == json -> {error, <<"JSON is not well-formed">>} end. decode(Data, xml) -> fxml_stream:parse_element(Data). attrs_to_body_attrs(Attrs) -> lists:foldl(fun (_, {error, Reason}) -> {error, Reason}; ({Attr, Val}, Acc) -> try case Attr of <<"ver">> -> [{ver, Val} | Acc]; <<"xmpp:version">> -> [{'xmpp:version', Val} | Acc]; <<"type">> -> [{type, Val} | Acc]; <<"key">> -> [{key, Val} | Acc]; <<"newkey">> -> [{newkey, Val} | Acc]; <<"xmlns">> -> Val = (?NS_HTTP_BIND), Acc; <<"secure">> -> [{secure, to_bool(Val)} | Acc]; <<"xmpp:restart">> -> [{'xmpp:restart', to_bool(Val)} | Acc]; <<"to">> -> [{to, jid:nameprep(Val)} | Acc]; <<"wait">> -> [{wait, to_int(Val, 0)} | Acc]; <<"ack">> -> [{ack, to_int(Val, 0)} | Acc]; <<"sid">> -> [{sid, Val} | Acc]; <<"hold">> -> [{hold, to_int(Val, 0)} | Acc]; <<"rid">> -> [{rid, to_int(Val, 0)} | Acc]; <<"pause">> -> [{pause, to_int(Val, 0)} | Acc]; _ -> [{Attr, Val} | Acc] end catch _:_ -> {error, <<"Invalid \"", Attr/binary, "\" attribute">>} end end, [], Attrs). to_int(S, Min) -> case binary_to_integer(S) of I when I >= Min -> I; _ -> erlang:error(badarg) end. to_bool(<<"true">>) -> true; to_bool(<<"1">>) -> true; to_bool(<<"false">>) -> false; to_bool(<<"0">>) -> false. attrs_to_list(Attrs) -> [attr_to_list(A) || A <- Attrs]. attr_to_list({Name, Value}) -> [$\s, Name, $=, $', fxml:crypt(Value), $']. bosh_response(Body, Type) -> CType = case Type of xml -> ?CT_XML; json -> ?CT_JSON end, {200, Body#body.http_reason, ?HEADER(CType), encode_body(Body, Type)}. bosh_response_with_msg(Body, Type, RcvBody) -> ?DEBUG("send error reply:~p~n** Receiced body: ~p", [Body, RcvBody]), bosh_response(Body, Type). http_error(Status, Reason, Type) -> CType = case Type of xml -> ?CT_XML; json -> ?CT_JSON end, {Status, Reason, ?HEADER(CType), <<"">>}. make_sid() -> str:sha(randoms:get_string()). -compile({no_auto_import, [{min, 2}]}). min(undefined, B) -> B; min(A, B) -> erlang:min(A, B). check_bosh_module(XmppDomain) -> case gen_mod:is_loaded(XmppDomain, mod_bosh) of true -> ok; false -> ?ERROR_MSG("You are trying to use BOSH (HTTP Bind) " "in host ~p, but the module mod_bosh " "is not started in that host. Configure " "your BOSH client to connect to the correct " "host, or add your desired host to the " "configuration, or check your 'modules' " "section in your ejabberd configuration " "file.", [XmppDomain]) end. get_attr(Attr, Attrs) -> get_attr(Attr, Attrs, <<"">>). get_attr(Attr, Attrs, Default) -> case lists:keysearch(Attr, 1, Attrs) of {value, {_, Val}} -> Val; _ -> Default end. buf_new(Host) -> buf_new(Host, unlimited). buf_new(Host, Limit) -> QueueType = gen_mod:get_module_opt( Host, mod_bosh, queue_type, ejabberd_config:default_queue_type(Host)), p1_queue:new(QueueType, Limit). buf_in(Xs, Buf) -> lists:foldl(fun p1_queue:in/2, Buf, Xs). buf_out(Buf, Num) when is_integer(Num), Num > 0 -> buf_out(Buf, Num, []); buf_out(Buf, _) -> {p1_queue:to_list(Buf), p1_queue:clear(Buf)}. buf_out(Buf, 0, Els) -> {lists:reverse(Els), Buf}; buf_out(Buf, I, Els) -> case p1_queue:out(Buf) of {{value, El}, NewBuf} -> buf_out(NewBuf, I - 1, [El | Els]); {empty, _} -> buf_out(Buf, 0, Els) end. cancel_timer(TRef) when is_reference(TRef) -> p1_fsm:cancel_timer(TRef); cancel_timer(_) -> false. restart_timer(TRef, Timeout, Msg) -> cancel_timer(TRef), erlang:start_timer(timer:seconds(Timeout), self(), Msg). restart_inactivity_timer(#state{inactivity_timeout = Timeout} = State) -> restart_inactivity_timer(State, Timeout). restart_inactivity_timer(#state{inactivity_timer = TRef} = State, Timeout) -> NewTRef = restart_timer(TRef, Timeout, inactive), State#state{inactivity_timer = NewTRef}. stop_inactivity_timer(#state{inactivity_timer = TRef} = State) -> cancel_timer(TRef), State#state{inactivity_timer = undefined}. restart_wait_timer(#state{wait_timer = TRef, wait_timeout = Timeout} = State) -> NewTRef = restart_timer(TRef, Timeout, wait_timeout), State#state{wait_timer = NewTRef}. stop_wait_timer(#state{wait_timer = TRef} = State) -> cancel_timer(TRef), State#state{wait_timer = undefined}. start_shaper_timer(Timeout) -> erlang:start_timer(Timeout, self(), shaper_timeout). make_random_jid(Host) -> User = randoms:get_string(), jid:make(User, Host, randoms:get_string()). make_socket(Pid, IP) -> {http_bind, Pid, IP}. ejabberd-18.01/src/node_online.erl0000644000232200023220000001362313225664356017434 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_online.erl %%% Author : Christophe Romain %%% Purpose : Handle only online users, remove offline subscriptions and nodes %%% Created : 15 Dec 2015 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_online). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -include("jid.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). -export([user_offline/3]). init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts), ejabberd_hooks:add(sm_remove_connection_hook, ServerHost, ?MODULE, user_offline, 75), ok. terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost), ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost, ?MODULE, user_offline, 75), ok. -spec user_offline(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> _. user_offline(_SID, #jid{luser=LUser,lserver=LServer}, _Info) -> mod_pubsub:remove_user(LUser, LServer). options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, false}, {purge_offline, true}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, open}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, {presence_based_delivery, true}, {itemreply, none}]. features() -> node_flat:features(). create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Removed) -> node_flat:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). ejabberd-18.01/src/ejabberd_admin.erl0000644000232200023220000006751213225664356020057 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_admin.erl %%% Author : Mickael Remond %%% Purpose : Administrative functions and commands %%% Created : 7 May 2006 by Mickael Remond %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_admin). -author('mickael.remond@process-one.net'). -behaviour(gen_server). -export([start_link/0, %% Server status/0, reopen_log/0, rotate_log/0, set_loglevel/1, stop_kindly/2, send_service_message_all_mucs/2, registered_vhosts/0, reload_config/0, %% Cluster join_cluster/1, leave_cluster/1, list_cluster/0, %% Erlang update_list/0, update/1, %% Accounts register/3, unregister/2, registered_users/1, %% Migration jabberd1.4 import_file/1, import_dir/1, %% Purge DB delete_expired_messages/0, delete_old_messages/1, %% Mnesia set_master/1, backup_mnesia/1, restore_mnesia/1, dump_mnesia/1, dump_table/2, load_mnesia/1, install_fallback_mnesia/1, dump_to_textfile/1, dump_to_textfile/2, mnesia_change_nodename/4, restore/1, % Still used by some modules clear_cache/0, get_commands_spec/0 ]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_commands.hrl"). -record(state, {}). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> process_flag(trap_exit, true), ejabberd_commands:register_commands(get_commands_spec()), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ejabberd_commands:unregister_commands(get_commands_spec()). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%% %%% ejabberd commands %%% get_commands_spec() -> [ %% The commands status, stop and restart are implemented also in ejabberd_ctl %% They are defined here so that other interfaces can use them too #ejabberd_commands{name = status, tags = [server], desc = "Get status of the ejabberd server", module = ?MODULE, function = status, result_desc = "Result tuple", result_example = {ok, <<"The node ejabberd@localhost is started with status: started" "ejabberd X.X is running in that node">>}, args = [], result = {res, restuple}}, #ejabberd_commands{name = stop, tags = [server], desc = "Stop ejabberd gracefully", module = init, function = stop, args = [], result = {res, rescode}}, #ejabberd_commands{name = restart, tags = [server], desc = "Restart ejabberd gracefully", module = init, function = restart, args = [], result = {res, rescode}}, #ejabberd_commands{name = reopen_log, tags = [logs, server], desc = "Reopen the log files", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, #ejabberd_commands{name = rotate_log, tags = [logs, server], desc = "Rotate the log files", module = ?MODULE, function = rotate_log, args = [], result = {res, rescode}}, #ejabberd_commands{name = stop_kindly, tags = [server], desc = "Inform users and rooms, wait, and stop the server", longdesc = "Provide the delay in seconds, and the " "announcement quoted, for example: \n" "ejabberdctl stop_kindly 60 " "\\\"The server will stop in one minute.\\\"", module = ?MODULE, function = stop_kindly, args_desc = ["Seconds to wait", "Announcement to send, with quotes"], args_example = [60, <<"Server will stop now.">>], args = [{delay, integer}, {announcement, string}], result = {res, rescode}}, #ejabberd_commands{name = get_loglevel, tags = [logs, server], desc = "Get the current loglevel", module = ejabberd_logger, function = get, result_desc = "Tuple with the log level number, its keyword and description", result_example = {4, info, <<"Info">>}, args = [], result = {leveltuple, {tuple, [{levelnumber, integer}, {levelatom, atom}, {leveldesc, string} ]}}}, #ejabberd_commands{name = set_loglevel, tags = [logs, server], desc = "Set the loglevel (0 to 5)", module = ?MODULE, function = set_loglevel, args_desc = ["Integer of the desired logging level, between 1 and 5"], args_example = [5], result_desc = "The type of logger module used", result_example = lager, args = [{loglevel, integer}], result = {logger, atom}}, #ejabberd_commands{name = update_list, tags = [server], desc = "List modified modules that can be updated", module = ?MODULE, function = update_list, args = [], result_example = ["mod_configure", "mod_vcard"], result = {modules, {list, {module, string}}}}, #ejabberd_commands{name = update, tags = [server], desc = "Update the given module, or use the keyword: all", module = ?MODULE, function = update, args_example = ["mod_vcard"], args = [{module, string}], result = {res, restuple}}, #ejabberd_commands{name = register, tags = [accounts], desc = "Register a user", policy = admin, module = ?MODULE, function = register, args_desc = ["Username", "Local vhost served by ejabberd", "Password"], args_example = [<<"bob">>, <<"example.com">>, <<"SomEPass44">>], args = [{user, binary}, {host, binary}, {password, binary}], result = {res, restuple}}, #ejabberd_commands{name = unregister, tags = [accounts], desc = "Unregister a user", policy = admin, module = ?MODULE, function = unregister, args_desc = ["Username", "Local vhost served by ejabberd"], args_example = [<<"bob">>, <<"example.com">>], args = [{user, binary}, {host, binary}], result = {res, restuple}}, #ejabberd_commands{name = registered_users, tags = [accounts], desc = "List all registered users in HOST", module = ?MODULE, function = registered_users, args_desc = ["Local vhost"], args_example = [<<"example.com">>], result_desc = "List of registered accounts usernames", result_example = [<<"user1">>, <<"user2">>], args = [{host, binary}], result = {users, {list, {username, string}}}}, #ejabberd_commands{name = registered_vhosts, tags = [server], desc = "List all registered vhosts in SERVER", module = ?MODULE, function = registered_vhosts, result_desc = "List of available vhosts", result_example = [<<"example.com">>, <<"anon.example.com">>], args = [], result = {vhosts, {list, {vhost, string}}}}, #ejabberd_commands{name = reload_config, tags = [server], desc = "Reload config file in memory", module = ?MODULE, function = reload_config, args = [], result = {res, rescode}}, #ejabberd_commands{name = join_cluster, tags = [cluster], desc = "Join this node into the cluster handled by Node", module = ?MODULE, function = join_cluster, args_desc = ["Nodename of the node to join"], args_example = [<<"ejabberd1@machine7">>], args = [{node, binary}], result = {res, rescode}}, #ejabberd_commands{name = leave_cluster, tags = [cluster], desc = "Remove and shutdown Node from the running cluster", longdesc = "This command can be run from any running node of the cluster, " "even the node to be removed.", module = ?MODULE, function = leave_cluster, args_desc = ["Nodename of the node to kick from the cluster"], args_example = [<<"ejabberd1@machine8">>], args = [{node, binary}], result = {res, rescode}}, #ejabberd_commands{name = list_cluster, tags = [cluster], desc = "List nodes that are part of the cluster handled by Node", module = ?MODULE, function = list_cluster, result_example = [ejabberd1@machine7, ejabberd1@machine8], args = [], result = {nodes, {list, {node, atom}}}}, #ejabberd_commands{name = import_file, tags = [mnesia], desc = "Import user data from jabberd14 spool file", module = ?MODULE, function = import_file, args_desc = ["Full path to the jabberd14 spool file"], args_example = ["/var/lib/ejabberd/jabberd14.spool"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = import_dir, tags = [mnesia], desc = "Import users data from jabberd14 spool dir", module = ?MODULE, function = import_dir, args_desc = ["Full path to the jabberd14 spool directory"], args_example = ["/var/lib/ejabberd/jabberd14/"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = import_piefxis, tags = [mnesia], desc = "Import users data from a PIEFXIS file (XEP-0227)", module = ejabberd_piefxis, function = import_file, args_desc = ["Full path to the PIEFXIS file"], args_example = ["/var/lib/ejabberd/example.com.xml"], args = [{file, string}], result = {res, rescode}}, #ejabberd_commands{name = export_piefxis, tags = [mnesia], desc = "Export data of all users in the server to PIEFXIS files (XEP-0227)", module = ejabberd_piefxis, function = export_server, args_desc = ["Full path to a directory"], args_example = ["/var/lib/ejabberd/"], args = [{dir, string}], result = {res, rescode}}, #ejabberd_commands{name = export_piefxis_host, tags = [mnesia], desc = "Export data of users in a host to PIEFXIS files (XEP-0227)", module = ejabberd_piefxis, function = export_host, args_desc = ["Full path to a directory", "Vhost to export"], args_example = ["/var/lib/ejabberd/", "example.com"], args = [{dir, string}, {host, string}], result = {res, rescode}}, #ejabberd_commands{name = delete_mnesia, tags = [mnesia, sql], desc = "Delete elements in Mnesia database for a given vhost", module = ejd2sql, function = delete, args_desc = ["Vhost which content will be deleted in Mnesia database"], args_example = ["example.com"], args = [{host, string}], result = {res, rescode}}, #ejabberd_commands{name = convert_to_scram, tags = [sql], desc = "Convert the passwords in 'users' ODBC table to SCRAM", module = ejabberd_auth_sql, function = convert_to_scram, args_desc = ["Vhost which users' passwords will be scrammed"], args_example = ["example.com"], args = [{host, binary}], result = {res, rescode}}, #ejabberd_commands{name = import_prosody, tags = [mnesia, sql, riak], desc = "Import data from Prosody", module = prosody2ejabberd, function = from_dir, args_desc = ["Full path to the Prosody data directory"], args_example = ["/var/lib/prosody/datadump/"], args = [{dir, string}], result = {res, rescode}}, #ejabberd_commands{name = convert_to_yaml, tags = [config], desc = "Convert the input file from Erlang to YAML format", module = ejabberd_config, function = convert_to_yaml, args_desc = ["Full path to the original configuration file", "And full path to final file"], args_example = ["/etc/ejabberd/ejabberd.cfg", "/etc/ejabberd/ejabberd.yml"], args = [{in, string}, {out, string}], result = {res, rescode}}, #ejabberd_commands{name = delete_expired_messages, tags = [purge], desc = "Delete expired offline messages from database", module = ?MODULE, function = delete_expired_messages, args = [], result = {res, rescode}}, #ejabberd_commands{name = delete_old_messages, tags = [purge], desc = "Delete offline messages older than DAYS", module = ?MODULE, function = delete_old_messages, args_desc = ["Number of days"], args_example = [31], args = [{days, integer}], result = {res, rescode}}, #ejabberd_commands{name = export2sql, tags = [mnesia], desc = "Export virtual host information from Mnesia tables to SQL file", longdesc = "Configure the modules to use SQL, then call this command.", module = ejd2sql, function = export, args_desc = ["Vhost", "Full path to the destination SQL file"], args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"], args = [{host, string}, {file, string}], result = {res, rescode}}, #ejabberd_commands{name = set_master, tags = [mnesia], desc = "Set master node of the clustered Mnesia tables", longdesc = "If you provide as nodename \"self\", this " "node will be set as its own master.", module = ?MODULE, function = set_master, args_desc = ["Name of the erlang node that will be considered master of this node"], args_example = ["ejabberd@machine7"], args = [{nodename, string}], result = {res, restuple}}, #ejabberd_commands{name = mnesia_change_nodename, tags = [mnesia], desc = "Change the erlang node name in a backup file", module = ?MODULE, function = mnesia_change_nodename, args_desc = ["Name of the old erlang node", "Name of the new node", "Path to old backup file", "Path to the new backup file"], args_example = ["ejabberd@machine1", "ejabberd@machine2", "/var/lib/ejabberd/old.backup", "/var/lib/ejabberd/new.backup"], args = [{oldnodename, string}, {newnodename, string}, {oldbackup, string}, {newbackup, string}], result = {res, restuple}}, #ejabberd_commands{name = backup, tags = [mnesia], desc = "Store the database to backup file", module = ?MODULE, function = backup_mnesia, args_desc = ["Full path for the destination backup file"], args_example = ["/var/lib/ejabberd/database.backup"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = restore, tags = [mnesia], desc = "Restore the database from backup file", module = ?MODULE, function = restore_mnesia, args_desc = ["Full path to the backup file"], args_example = ["/var/lib/ejabberd/database.backup"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = dump, tags = [mnesia], desc = "Dump the database to a text file", module = ?MODULE, function = dump_mnesia, args_desc = ["Full path for the text file"], args_example = ["/var/lib/ejabberd/database.txt"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = dump_table, tags = [mnesia], desc = "Dump a table to a text file", module = ?MODULE, function = dump_table, args_desc = ["Full path for the text file", "Table name"], args_example = ["/var/lib/ejabberd/table-muc-registered.txt", "muc_registered"], args = [{file, string}, {table, string}], result = {res, restuple}}, #ejabberd_commands{name = load, tags = [mnesia], desc = "Restore the database from a text file", module = ?MODULE, function = load_mnesia, args_desc = ["Full path to the text file"], args_example = ["/var/lib/ejabberd/database.txt"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = install_fallback, tags = [mnesia], desc = "Install the database from a fallback file", module = ?MODULE, function = install_fallback_mnesia, args_desc = ["Full path to the fallback file"], args_example = ["/var/lib/ejabberd/database.fallback"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = clear_cache, tags = [server], desc = "Clear database cache on all nodes", module = ?MODULE, function = clear_cache, args = [], result = {res, rescode}} ]. %%% %%% Server management %%% status() -> {InternalStatus, ProvidedStatus} = init:get_status(), String1 = io_lib:format("The node ~p is ~p. Status: ~p", [node(), InternalStatus, ProvidedStatus]), {Is_running, String2} = case lists:keysearch(ejabberd, 1, application:which_applications()) of false -> {ejabberd_not_running, "ejabberd is not running in that node."}; {value, {_, _, Version}} -> {ok, io_lib:format("ejabberd ~s is running in that node", [Version])} end, {Is_running, String1 ++ String2}. reopen_log() -> ejabberd_hooks:run(reopen_log_hook, []), ejabberd_logger:reopen_log(). rotate_log() -> ejabberd_hooks:run(rotate_log_hook, []), ejabberd_logger:rotate_log(). set_loglevel(LogLevel) -> {module, Module} = ejabberd_logger:set(LogLevel), Module. %%% %%% Stop Kindly %%% stop_kindly(DelaySeconds, AnnouncementTextString) -> Subject = (str:format("Server stop in ~p seconds!", [DelaySeconds])), WaitingDesc = (str:format("Waiting ~p seconds", [DelaySeconds])), AnnouncementText = list_to_binary(AnnouncementTextString), Steps = [ {"Stopping ejabberd port listeners", ejabberd_listener, stop_listeners, []}, {"Sending announcement to connected users", mod_announce, send_announcement_to_all, [?MYNAME, Subject, AnnouncementText]}, {"Sending service message to MUC rooms", ejabberd_admin, send_service_message_all_mucs, [Subject, AnnouncementText]}, {WaitingDesc, timer, sleep, [DelaySeconds * 1000]}, {"Stopping ejabberd", application, stop, [ejabberd]}, {"Stopping Mnesia", mnesia, stop, []}, {"Stopping Erlang node", init, stop, []} ], NumberLast = length(Steps), TimestampStart = calendar:datetime_to_gregorian_seconds({date(), time()}), lists:foldl( fun({Desc, Mod, Func, Args}, NumberThis) -> SecondsDiff = calendar:datetime_to_gregorian_seconds({date(), time()}) - TimestampStart, io:format("[~p/~p ~ps] ~s... ", [NumberThis, NumberLast, SecondsDiff, Desc]), Result = (catch apply(Mod, Func, Args)), io:format("~p~n", [Result]), NumberThis+1 end, 1, Steps), ok. send_service_message_all_mucs(Subject, AnnouncementText) -> Message = str:format("~s~n~s", [Subject, AnnouncementText]), lists:foreach( fun(ServerHost) -> MUCHost = gen_mod:get_module_opt_host( ServerHost, mod_muc, <<"conference.@HOST@">>), mod_muc:broadcast_service_message(ServerHost, MUCHost, Message) end, ?MYHOSTS). %%% %%% ejabberd_update %%% update_list() -> {ok, _Dir, UpdatedBeams, _Script, _LowLevelScript, _Check} = ejabberd_update:update_info(), [atom_to_list(Beam) || Beam <- UpdatedBeams]. update("all") -> [update_module(ModStr) || ModStr <- update_list()], {ok, []}; update(ModStr) -> update_module(ModStr). update_module(ModuleNameBin) when is_binary(ModuleNameBin) -> update_module(binary_to_list(ModuleNameBin)); update_module(ModuleNameString) -> ModuleName = list_to_atom(ModuleNameString), case ejabberd_update:update([ModuleName]) of {ok, _Res} -> {ok, []}; {error, Reason} -> {error, Reason} end. %%% %%% Account management %%% register(User, Host, Password) -> case ejabberd_auth:try_register(User, Host, Password) of ok -> {ok, io_lib:format("User ~s@~s successfully registered", [User, Host])}; {error, exists} -> Msg = io_lib:format("User ~s@~s already registered", [User, Host]), {error, conflict, 10090, Msg}; {error, Reason} -> String = io_lib:format("Can't register user ~s@~s at node ~p: ~p", [User, Host, node(), Reason]), {error, cannot_register, 10001, String} end. unregister(User, Host) -> ejabberd_auth:remove_user(User, Host), {ok, ""}. registered_users(Host) -> Users = ejabberd_auth:get_users(Host), SUsers = lists:sort(Users), lists:map(fun({U, _S}) -> U end, SUsers). registered_vhosts() -> ?MYHOSTS. reload_config() -> ejabberd_config:reload_file(). %%% %%% Cluster management %%% join_cluster(NodeBin) -> ejabberd_cluster:join(list_to_atom(binary_to_list(NodeBin))). leave_cluster(NodeBin) -> ejabberd_cluster:leave(list_to_atom(binary_to_list(NodeBin))). list_cluster() -> ejabberd_cluster:get_nodes(). %%% %%% Migration management %%% import_file(Path) -> case jd2ejd:import_file(Path) of ok -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't import jabberd14 spool file ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_import_file, String} end. import_dir(Path) -> case jd2ejd:import_dir(Path) of ok -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't import jabberd14 spool dir ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_import_dir, String} end. %%% %%% Purge DB %%% delete_expired_messages() -> lists:foreach( fun(Host) -> {atomic, ok} = mod_offline:remove_expired_messages(Host) end, ?MYHOSTS). delete_old_messages(Days) -> lists:foreach( fun(Host) -> {atomic, _} = mod_offline:remove_old_messages(Days, Host) end, ?MYHOSTS). %%% %%% Mnesia management %%% set_master("self") -> set_master(node()); set_master(NodeString) when is_list(NodeString) -> set_master(list_to_atom(NodeString)); set_master(Node) when is_atom(Node) -> case mnesia:set_master_nodes([Node]) of ok -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't set master node ~p at node ~p:~n~p", [Node, node(), Reason]), {error, String} end. backup_mnesia(Path) -> case mnesia:backup(Path) of ok -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't store backup in ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_backup, String} end. restore_mnesia(Path) -> case ejabberd_admin:restore(Path) of {atomic, _} -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't restore backup from ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_restore, String}; {aborted,{no_exists,Table}} -> String = io_lib:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.", [filename:absname(Path), node(), Table]), {table_not_exists, String}; {aborted,enoent} -> String = io_lib:format("Can't restore backup from ~p at node ~p: File not found.", [filename:absname(Path), node()]), {file_not_found, String} end. %% Mnesia database restore %% This function is called from ejabberd_ctl, ejabberd_web_admin and %% mod_configure/adhoc restore(Path) -> mnesia:restore(Path, [{keep_tables,keep_tables()}, {default_op, skip_tables}]). %% This function return a list of tables that should be kept from a previous %% version backup. %% Obsolete tables or tables created by module who are no longer used are not %% restored and are ignored. keep_tables() -> lists:flatten([acl, passwd, config, keep_modules_tables()]). %% Returns the list of modules tables in use, according to the list of actually %% loaded modules keep_modules_tables() -> lists:map(fun(Module) -> module_tables(Module) end, gen_mod:loaded_modules(?MYNAME)). %% TODO: This mapping should probably be moved to a callback function in each %% module. %% Mapping between modules and their tables module_tables(mod_announce) -> [motd, motd_users]; module_tables(mod_irc) -> [irc_custom]; module_tables(mod_last) -> [last_activity]; module_tables(mod_muc) -> [muc_room, muc_registered]; module_tables(mod_offline) -> [offline_msg]; module_tables(mod_privacy) -> [privacy]; module_tables(mod_private) -> [private_storage]; module_tables(mod_pubsub) -> [pubsub_node]; module_tables(mod_roster) -> [roster]; module_tables(mod_shared_roster) -> [sr_group, sr_user]; module_tables(mod_vcard) -> [vcard, vcard_search]; module_tables(_Other) -> []. get_local_tables() -> Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)), Tabs = lists:filter( fun(T) -> case mnesia:table_info(T, storage_type) of disc_copies -> true; disc_only_copies -> true; _ -> false end end, Tabs1), Tabs. dump_mnesia(Path) -> Tabs = get_local_tables(), dump_tables(Path, Tabs). dump_table(Path, STable) -> Table = list_to_atom(STable), dump_tables(Path, [Table]). dump_tables(Path, Tables) -> case dump_to_textfile(Path, Tables) of ok -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't store dump in ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_dump, String} end. dump_to_textfile(File) -> Tabs = get_local_tables(), dump_to_textfile(File, Tabs). dump_to_textfile(File, Tabs) -> dump_to_textfile(mnesia:system_info(is_running), Tabs, file:open(File, [write])). dump_to_textfile(yes, Tabs, {ok, F}) -> Defs = lists:map( fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)}, {attributes, mnesia:table_info(T, attributes)}]} end, Tabs), io:format(F, "~p.~n", [{tables, Defs}]), lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs), file:close(F); dump_to_textfile(_, _, {ok, F}) -> file:close(F), {error, mnesia_not_running}; dump_to_textfile(_, _, {error, Reason}) -> {error, Reason}. dump_tab(F, T) -> W = mnesia:table_info(T, wild_pattern), {atomic,All} = mnesia:transaction( fun() -> mnesia:match_object(T, W, read) end), lists:foreach( fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All). load_mnesia(Path) -> case mnesia:load_textfile(Path) of {atomic, ok} -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't load dump in ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_load, String} end. install_fallback_mnesia(Path) -> case mnesia:install_fallback(Path) of ok -> {ok, ""}; {error, Reason} -> String = io_lib:format("Can't install fallback from ~p at node ~p: ~p", [filename:absname(Path), node(), Reason]), {cannot_fallback, String} end. mnesia_change_nodename(FromString, ToString, Source, Target) -> From = list_to_atom(FromString), To = list_to_atom(ToString), Switch = fun (Node) when Node == From -> io:format(" - Replacing nodename: '~p' with: '~p'~n", [From, To]), To; (Node) when Node == To -> %% throw({error, already_exists}); io:format(" - Node: '~p' will not be modified (it is already '~p')~n", [Node, To]), Node; (Node) -> io:format(" - Node: '~p' will not be modified (it is not '~p')~n", [Node, From]), Node end, Convert = fun ({schema, db_nodes, Nodes}, Acc) -> io:format(" +++ db_nodes ~p~n", [Nodes]), {[{schema, db_nodes, lists:map(Switch,Nodes)}], Acc}; ({schema, version, Version}, Acc) -> io:format(" +++ version: ~p~n", [Version]), {[{schema, version, Version}], Acc}; ({schema, cookie, Cookie}, Acc) -> io:format(" +++ cookie: ~p~n", [Cookie]), {[{schema, cookie, Cookie}], Acc}; ({schema, Tab, CreateList}, Acc) -> io:format("~n * Checking table: '~p'~n", [Tab]), Keys = [ram_copies, disc_copies, disc_only_copies], OptSwitch = fun({Key, Val}) -> case lists:member(Key, Keys) of true -> io:format(" + Checking key: '~p'~n", [Key]), {Key, lists:map(Switch, Val)}; false-> {Key, Val} end end, Res = {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc}, Res; (Other, Acc) -> {[Other], Acc} end, mnesia:traverse_backup(Source, Target, Convert, switched). clear_cache() -> Nodes = ejabberd_cluster:get_nodes(), lists:foreach(fun(T) -> ets_cache:clear(T, Nodes) end, ets_cache:all()). ejabberd-18.01/src/mod_vcard_ldap.erl0000644000232200023220000004052013225664356020075 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_vcard_ldap.erl %%% Author : Evgeny Khramtsov %%% Created : 29 Jul 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_vcard_ldap). -behaviour(gen_server). -behaviour(mod_vcard). %% API -export([start_link/2]). -export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4, remove_user/2, import/3, search_fields/1, search_reported/1, mod_opt_type/1]). -export([is_search_supported/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("eldap.hrl"). -include("xmpp.hrl"). -include("translate.hrl"). -define(PROCNAME, ejabberd_mod_vcard_ldap). -record(state, {serverhost = <<"">> :: binary(), myhosts = [] :: [binary()], eldap_id = <<"">> :: binary(), search = false :: boolean(), servers = [] :: [binary()], backups = [] :: [binary()], port = ?LDAP_PORT :: inet:port_number(), tls_options = [] :: list(), dn = <<"">> :: binary(), base = <<"">> :: binary(), password = <<"">> :: binary(), uids = [] :: [{binary()} | {binary(), binary()}], vcard_map = [] :: [{binary(), binary(), [binary()]}], vcard_map_attrs = [] :: [binary()], user_filter = <<"">> :: binary(), search_filter :: eldap:filter(), search_fields = [] :: [{binary(), binary()}], search_reported = [] :: [{binary(), binary()}], search_reported_attrs = [] :: [binary()], deref_aliases = never :: never | searching | finding | always, matches = 0 :: non_neg_integer()}). %%%=================================================================== %%% API %%%=================================================================== start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). init(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]}, transient, 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_backend_sup, ChildSpec). stop(Host) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), supervisor:terminate_child(ejabberd_backend_sup, Proc), supervisor:delete_child(ejabberd_backend_sup, Proc), ok. is_search_supported(_LServer) -> true. get_vcard(LUser, LServer) -> {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME), VCardMap = State#state.vcard_map, case find_ldap_user(LUser, State) of #eldap_entry{attributes = Attributes} -> VCard = ldap_attributes_to_vcard(Attributes, VCardMap, {LUser, LServer}), {ok, [xmpp:encode(VCard)]}; _ -> {ok, []} end. set_vcard(_LUser, _LServer, _VCard, _VCardSearch) -> {atomic, not_implemented}. search_fields(LServer) -> {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME), State#state.search_fields. search_reported(LServer) -> {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME), State#state.search_reported. search(LServer, Data, _AllowReturnAll, MaxMatch) -> {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME), Base = State#state.base, SearchFilter = State#state.search_filter, Eldap_ID = State#state.eldap_id, UIDs = State#state.uids, ReportedAttrs = State#state.search_reported_attrs, Filter = eldap:'and'([SearchFilter, eldap_utils:make_filter(Data, UIDs)]), case eldap_pool:search(Eldap_ID, [{base, Base}, {filter, Filter}, {limit, MaxMatch}, {deref_aliases, State#state.deref_aliases}, {attributes, ReportedAttrs}]) of #eldap_search_result{entries = E} -> search_items(E, State); _ -> [] end. search_items(Entries, State) -> LServer = State#state.serverhost, SearchReported = State#state.search_reported, VCardMap = State#state.vcard_map, UIDs = State#state.uids, Attributes = lists:map(fun (E) -> #eldap_entry{attributes = Attrs} = E, Attrs end, Entries), lists:flatmap( fun(Attrs) -> case eldap_utils:find_ldap_attrs(UIDs, Attrs) of {U, UIDAttrFormat} -> case eldap_utils:get_user_part(U, UIDAttrFormat) of {ok, Username} -> case ejabberd_auth:user_exists(Username, LServer) of true -> RFields = lists:map( fun({_, VCardName}) -> {VCardName, map_vcard_attr(VCardName, Attrs, VCardMap, {Username, ?MYNAME})} end, SearchReported), J = <>, [{<<"jid">>, J} | RFields]; _ -> [] end; _ -> [] end; <<"">> -> [] end end, Attributes). remove_user(_User, _Server) -> {atomic, not_implemented}. import(_, _, _) -> ok. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), State = parse_options(Host, Opts), eldap_pool:start_link(State#state.eldap_id, State#state.servers, State#state.backups, State#state.port, State#state.dn, State#state.password, State#state.tls_options), {ok, State}. handle_call(get_state, _From, State) -> {reply, {ok, State}, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== find_ldap_user(User, State) -> Base = State#state.base, RFC2254_Filter = State#state.user_filter, Eldap_ID = State#state.eldap_id, VCardAttrs = State#state.vcard_map_attrs, case eldap_filter:parse(RFC2254_Filter, [{<<"%u">>, User}]) of {ok, EldapFilter} -> case eldap_pool:search(Eldap_ID, [{base, Base}, {filter, EldapFilter}, {deref_aliases, State#state.deref_aliases}, {attributes, VCardAttrs}]) of #eldap_search_result{entries = [E | _]} -> E; _ -> false end; _ -> false end. ldap_attributes_to_vcard(Attributes, VCardMap, UD) -> Attrs = lists:map(fun ({VCardName, _, _}) -> {stringprep:tolower(VCardName), map_vcard_attr(VCardName, Attributes, VCardMap, UD)} end, VCardMap), lists:foldl(fun ldap_attribute_to_vcard/2, #vcard_temp{}, Attrs). -spec ldap_attribute_to_vcard({binary(), binary()}, vcard_temp()) -> vcard_temp(). ldap_attribute_to_vcard({Attr, Value}, V) -> Ts = V#vcard_temp.tel, Es = V#vcard_temp.email, N = case V#vcard_temp.n of undefined -> #vcard_name{}; _ -> V#vcard_temp.n end, O = case V#vcard_temp.org of undefined -> #vcard_org{}; _ -> V#vcard_temp.org end, A = case V#vcard_temp.adr of [] -> #vcard_adr{}; As -> hd(As) end, case Attr of <<"fn">> -> V#vcard_temp{fn = Value}; <<"nickname">> -> V#vcard_temp{nickname = Value}; <<"title">> -> V#vcard_temp{title = Value}; <<"bday">> -> V#vcard_temp{bday = Value}; <<"url">> -> V#vcard_temp{url = Value}; <<"desc">> -> V#vcard_temp{desc = Value}; <<"role">> -> V#vcard_temp{role = Value}; <<"tel">> -> V#vcard_temp{tel = [#vcard_tel{number = Value}|Ts]}; <<"email">> -> V#vcard_temp{email = [#vcard_email{userid = Value}|Es]}; <<"photo">> -> V#vcard_temp{photo = #vcard_photo{binval = Value}}; <<"family">> -> V#vcard_temp{n = N#vcard_name{family = Value}}; <<"given">> -> V#vcard_temp{n = N#vcard_name{given = Value}}; <<"middle">> -> V#vcard_temp{n = N#vcard_name{middle = Value}}; <<"orgname">> -> V#vcard_temp{org = O#vcard_org{name = Value}}; <<"orgunit">> -> V#vcard_temp{org = O#vcard_org{units = [Value]}}; <<"locality">> -> V#vcard_temp{adr = [A#vcard_adr{locality = Value}]}; <<"street">> -> V#vcard_temp{adr = [A#vcard_adr{street = Value}]}; <<"ctry">> -> V#vcard_temp{adr = [A#vcard_adr{ctry = Value}]}; <<"region">> -> V#vcard_temp{adr = [A#vcard_adr{region = Value}]}; <<"pcode">> -> V#vcard_temp{adr = [A#vcard_adr{pcode = Value}]}; _ -> V end. map_vcard_attr(VCardName, Attributes, Pattern, UD) -> Res = lists:filter(fun ({Name, _, _}) -> eldap_utils:case_insensitive_match(Name, VCardName) end, Pattern), case Res of [{_, Str, Attrs}] -> process_pattern(Str, UD, [eldap_utils:get_ldap_attr(X, Attributes) || X <- Attrs]); _ -> <<"">> end. process_pattern(Str, {User, Domain}, AttrValues) -> eldap_filter:do_sub(Str, [{<<"%u">>, User}, {<<"%d">>, Domain}] ++ [{<<"%s">>, V, 1} || V <- AttrValues]). default_vcard_map() -> [{<<"NICKNAME">>, <<"%u">>, []}, {<<"FN">>, <<"%s">>, [<<"displayName">>]}, {<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, {<<"GIVEN">>, <<"%s">>, [<<"givenName">>]}, {<<"MIDDLE">>, <<"%s">>, [<<"initials">>]}, {<<"ORGNAME">>, <<"%s">>, [<<"o">>]}, {<<"ORGUNIT">>, <<"%s">>, [<<"ou">>]}, {<<"CTRY">>, <<"%s">>, [<<"c">>]}, {<<"LOCALITY">>, <<"%s">>, [<<"l">>]}, {<<"STREET">>, <<"%s">>, [<<"street">>]}, {<<"REGION">>, <<"%s">>, [<<"st">>]}, {<<"PCODE">>, <<"%s">>, [<<"postalCode">>]}, {<<"TITLE">>, <<"%s">>, [<<"title">>]}, {<<"URL">>, <<"%s">>, [<<"labeleduri">>]}, {<<"DESC">>, <<"%s">>, [<<"description">>]}, {<<"TEL">>, <<"%s">>, [<<"telephoneNumber">>]}, {<<"EMAIL">>, <<"%s">>, [<<"mail">>]}, {<<"BDAY">>, <<"%s">>, [<<"birthDay">>]}, {<<"ROLE">>, <<"%s">>, [<<"employeeType">>]}, {<<"PHOTO">>, <<"%s">>, [<<"jpegPhoto">>]}]. default_search_fields() -> [{?T("User"), <<"%u">>}, {?T("Full Name"), <<"displayName">>}, {?T("Given Name"), <<"givenName">>}, {?T("Middle Name"), <<"initials">>}, {?T("Family Name"), <<"sn">>}, {?T("Nickname"), <<"%u">>}, {?T("Birthday"), <<"birthDay">>}, {?T("Country"), <<"c">>}, {?T("City"), <<"l">>}, {?T("Email"), <<"mail">>}, {?T("Organization Name"), <<"o">>}, {?T("Organization Unit"), <<"ou">>}]. default_search_reported() -> [{?T("Full Name"), <<"FN">>}, {?T("Given Name"), <<"FIRST">>}, {?T("Middle Name"), <<"MIDDLE">>}, {?T("Family Name"), <<"LAST">>}, {?T("Nickname"), <<"NICK">>}, {?T("Birthday"), <<"BDAY">>}, {?T("Country"), <<"CTRY">>}, {?T("City"), <<"LOCALITY">>}, {?T("Email"), <<"EMAIL">>}, {?T("Organization Name"), <<"ORGNAME">>}, {?T("Organization Unit"), <<"ORGUNIT">>}]. parse_options(Host, Opts) -> MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"vjud.@HOST@">>), Search = gen_mod:get_opt(search, Opts, false), Matches = gen_mod:get_opt(matches, Opts, 30), Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?PROCNAME)), Cfg = eldap_utils:get_config(Host, Opts), UIDsTemp = gen_mod:get_opt({ldap_uids, Host}, Opts, [{<<"uid">>, <<"%u">>}]), UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp), SubFilter = eldap_utils:generate_subfilter(UIDs), UserFilter = case gen_mod:get_opt({ldap_filter, Host}, Opts, <<"">>) of <<"">> -> SubFilter; F -> <<"(&", SubFilter/binary, F/binary, ")">> end, {ok, SearchFilter} = eldap_filter:parse(eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}])), VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts, default_vcard_map()), SearchFields = gen_mod:get_opt(ldap_search_fields, Opts, default_search_fields()), SearchReported = gen_mod:get_opt(ldap_search_reported, Opts, default_search_reported()), UIDAttrs = [UAttr || {UAttr, _} <- UIDs], VCardMapAttrs = lists:usort(lists:append([A || {_, _, A} <- VCardMap]) ++ UIDAttrs), SearchReportedAttrs = lists:usort(lists:flatmap(fun ({_, N}) -> case lists:keysearch(N, 1, VCardMap) of {value, {_, _, L}} -> L; _ -> [] end end, SearchReported) ++ UIDAttrs), #state{serverhost = Host, myhosts = MyHosts, eldap_id = Eldap_ID, search = Search, servers = Cfg#eldap_config.servers, backups = Cfg#eldap_config.backups, port = Cfg#eldap_config.port, tls_options = Cfg#eldap_config.tls_options, dn = Cfg#eldap_config.dn, password = Cfg#eldap_config.password, base = Cfg#eldap_config.base, deref_aliases = Cfg#eldap_config.deref_aliases, uids = UIDs, vcard_map = VCardMap, vcard_map_attrs = VCardMapAttrs, user_filter = UserFilter, search_filter = SearchFilter, search_fields = SearchFields, search_reported = SearchReported, search_reported_attrs = SearchReportedAttrs, matches = Matches}. mod_opt_type(ldap_filter) -> fun eldap_utils:check_filter/1; mod_opt_type(ldap_search_fields) -> fun (Ls) -> [{iolist_to_binary(S), iolist_to_binary(P)} || {S, P} <- Ls] end; mod_opt_type(ldap_search_reported) -> fun (Ls) -> [{iolist_to_binary(S), iolist_to_binary(P)} || {S, P} <- Ls] end; mod_opt_type(ldap_uids) -> fun (Us) -> lists:map(fun ({U, P}) -> {iolist_to_binary(U), iolist_to_binary(P)}; ({U}) -> {iolist_to_binary(U)}; (U) -> {iolist_to_binary(U)} end, lists:flatten(Us)) end; mod_opt_type(ldap_vcard_map) -> fun (Ls) -> lists:map(fun ({S, [{P, L}]}) -> {iolist_to_binary(S), iolist_to_binary(P), [iolist_to_binary(E) || E <- L]} end, Ls) end; mod_opt_type(deref_aliases) -> fun (never) -> never; (searching) -> searching; (finding) -> finding; (always) -> always end; mod_opt_type(ldap_backups) -> fun (L) -> [iolist_to_binary(H) || H <- L] end; mod_opt_type(ldap_base) -> fun iolist_to_binary/1; mod_opt_type(ldap_deref_aliases) -> fun (never) -> never; (searching) -> searching; (finding) -> finding; (always) -> always end; mod_opt_type(ldap_encrypt) -> fun (tls) -> tls; (starttls) -> starttls; (none) -> none end; mod_opt_type(ldap_password) -> fun iolist_to_binary/1; mod_opt_type(ldap_port) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(ldap_rootdn) -> fun iolist_to_binary/1; mod_opt_type(ldap_servers) -> fun (L) -> [iolist_to_binary(H) || H <- L] end; mod_opt_type(ldap_tls_cacertfile) -> fun misc:try_read_file/1; mod_opt_type(ldap_tls_certfile) -> fun ejabberd_pkix:try_certfile/1; mod_opt_type(ldap_tls_depth) -> fun (I) when is_integer(I), I >= 0 -> I end; mod_opt_type(ldap_tls_verify) -> fun (hard) -> hard; (soft) -> soft; (false) -> false end; mod_opt_type(_) -> [ldap_filter, ldap_search_fields, ldap_search_reported, ldap_uids, ldap_vcard_map, deref_aliases, ldap_backups, ldap_base, ldap_deref_aliases, ldap_encrypt, ldap_password, ldap_port, ldap_rootdn, ldap_servers, ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth, ldap_tls_verify]. ejabberd-18.01/src/cyrsasl.erl0000644000232200023220000001672713225664356016633 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : cyrsasl.erl %%% Author : Alexey Shchepin %%% Purpose : Cyrus SASL-like library %%% Created : 8 Mar 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(cyrsasl). -author('alexey@process-one.net'). -behaviour(gen_server). -export([start_link/0, register_mechanism/3, listmech/1, server_new/7, server_start/3, server_step/2, get_mech/1, format_error/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -record(state, {}). -record(sasl_mechanism, {mechanism = <<"">> :: mechanism() | '$1', module :: atom(), password_type = plain :: password_type() | '$2'}). -type(mechanism() :: binary()). -type(mechanisms() :: [mechanism(),...]). -type(password_type() :: plain | digest | scram). -type sasl_property() :: {username, binary()} | {authzid, binary()} | {mechanism, binary()} | {auth_module, atom()}. -type sasl_return() :: {ok, [sasl_property()]} | {ok, [sasl_property()], binary()} | {continue, binary(), sasl_state()} | {error, atom(), binary()}. -type(sasl_mechanism() :: #sasl_mechanism{}). -type error_reason() :: cyrsasl_digest:error_reason() | cyrsasl_oauth:error_reason() | cyrsasl_plain:error_reason() | cyrsasl_scram:error_reason() | unsupported_mechanism | nodeprep_failed | empty_username | aborted. -record(sasl_state, { service, myname, realm, get_password, check_password, check_password_digest, mech_name = <<"">>, mech_mod, mech_state }). -type sasl_state() :: #sasl_state{}. -export_type([mechanism/0, mechanisms/0, sasl_mechanism/0, error_reason/0, sasl_state/0, sasl_return/0, sasl_property/0]). -callback start(list()) -> any(). -callback stop() -> any(). -callback mech_new(binary(), fun(), fun(), fun()) -> any(). -callback mech_step(any(), binary()) -> sasl_return(). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> ets:new(sasl_mechanism, [named_table, public, {keypos, #sasl_mechanism.mechanism}]), cyrsasl_plain:start([]), cyrsasl_digest:start([]), cyrsasl_scram:start([]), cyrsasl_anonymous:start([]), cyrsasl_oauth:start([]), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> cyrsasl_plain:stop(), cyrsasl_digest:stop(), cyrsasl_scram:stop(), cyrsasl_anonymous:stop(), cyrsasl_oauth:stop(). code_change(_OldVsn, State, _Extra) -> {ok, State}. -spec format_error(mechanism() | sasl_state(), error_reason()) -> {atom(), binary()}. format_error(_, unsupported_mechanism) -> {'invalid-mechanism', <<"Unsupported mechanism">>}; format_error(_, nodeprep_failed) -> {'bad-protocol', <<"Nodeprep failed">>}; format_error(_, empty_username) -> {'bad-protocol', <<"Empty username">>}; format_error(_, aborted) -> {'aborted', <<"Aborted">>}; format_error(#sasl_state{mech_mod = Mod}, Reason) -> Mod:format_error(Reason); format_error(Mech, Reason) -> case ets:lookup(sasl_mechanism, Mech) of [#sasl_mechanism{module = Mod}] -> Mod:format_error(Reason); [] -> {'invalid-mechanism', <<"Unsupported mechanism">>} end. -spec register_mechanism(Mechanim :: mechanism(), Module :: module(), PasswordType :: password_type()) -> any(). register_mechanism(Mechanism, Module, PasswordType) -> ets:insert(sasl_mechanism, #sasl_mechanism{mechanism = Mechanism, module = Module, password_type = PasswordType}). check_credentials(_State, Props) -> User = proplists:get_value(authzid, Props, <<>>), case jid:nodeprep(User) of error -> {error, nodeprep_failed}; <<"">> -> {error, empty_username}; _LUser -> ok end. -spec listmech(Host ::binary()) -> Mechanisms::mechanisms(). listmech(Host) -> ets:select(sasl_mechanism, [{#sasl_mechanism{mechanism = '$1', password_type = '$2', _ = '_'}, case catch ejabberd_auth:store_type(Host) of external -> [{'==', '$2', plain}]; scram -> [{'/=', '$2', digest}]; {'EXIT', {undef, [{Module, store_type, []} | _]}} -> ?WARNING_MSG("~p doesn't implement the function store_type/0", [Module]), []; _Else -> [] end, ['$1']}]). -spec server_new(binary(), binary(), binary(), term(), fun(), fun(), fun()) -> sasl_state(). server_new(Service, ServerFQDN, UserRealm, _SecFlags, GetPassword, CheckPassword, CheckPasswordDigest) -> #sasl_state{service = Service, myname = ServerFQDN, realm = UserRealm, get_password = GetPassword, check_password = CheckPassword, check_password_digest = CheckPasswordDigest}. -spec server_start(sasl_state(), mechanism(), binary()) -> sasl_return(). server_start(State, Mech, ClientIn) -> case lists:member(Mech, listmech(State#sasl_state.myname)) of true -> case ets:lookup(sasl_mechanism, Mech) of [#sasl_mechanism{module = Module}] -> {ok, MechState} = Module:mech_new(State#sasl_state.myname, State#sasl_state.get_password, State#sasl_state.check_password, State#sasl_state.check_password_digest), server_step(State#sasl_state{mech_mod = Module, mech_name = Mech, mech_state = MechState}, ClientIn); _ -> {error, unsupported_mechanism, <<"">>} end; false -> {error, unsupported_mechanism, <<"">>} end. -spec server_step(sasl_state(), binary()) -> sasl_return(). server_step(State, ClientIn) -> Module = State#sasl_state.mech_mod, MechState = State#sasl_state.mech_state, case Module:mech_step(MechState, ClientIn) of {ok, Props} -> case check_credentials(State, Props) of ok -> {ok, Props}; {error, Error} -> {error, Error, <<"">>} end; {ok, Props, ServerOut} -> case check_credentials(State, Props) of ok -> {ok, Props, ServerOut}; {error, Error} -> {error, Error, <<"">>} end; {continue, ServerOut, NewMechState} -> {continue, ServerOut, State#sasl_state{mech_state = NewMechState}}; {error, Error, Username} -> {error, Error, Username}; {error, Error} -> {error, Error, <<"">>} end. -spec get_mech(sasl_state()) -> binary(). get_mech(#sasl_state{mech_name = Mech}) -> Mech. ejabberd-18.01/src/mod_vcard_sql.erl0000644000232200023220000002772113225664356017764 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_vcard_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_vcard_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_vcard). %% API -export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4, remove_user/2, search_fields/1, search_reported/1, import/3, export/1]). -export([is_search_supported/1]). -include("xmpp.hrl"). -include("mod_vcard.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -include("translate.hrl"). -ifdef(NEW_SQL_SCHEMA). -define(USE_NEW_SCHEMA, true). -else. -define(USE_NEW_SCHEMA, false). -endif. %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. stop(_Host) -> ok. is_search_supported(_LServer) -> true. get_vcard(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(vcard)s from vcard" " where username=%(LUser)s and %(LServer)H")) of {selected, [{SVCARD}]} -> case fxml_stream:parse_element(SVCARD) of {error, _Reason} -> error; VCARD -> {ok, [VCARD]} end; {selected, []} -> {ok, []}; _ -> error end. set_vcard(LUser, LServer, VCARD, #vcard_search{user = {User, _}, fn = FN, lfn = LFN, family = Family, lfamily = LFamily, given = Given, lgiven = LGiven, middle = Middle, lmiddle = LMiddle, nickname = Nickname, lnickname = LNickname, bday = BDay, lbday = LBDay, ctry = CTRY, lctry = LCTRY, locality = Locality, llocality = LLocality, email = EMail, lemail = LEMail, orgname = OrgName, lorgname = LOrgName, orgunit = OrgUnit, lorgunit = LOrgUnit}) -> SVCARD = fxml:element_to_binary(VCARD), ejabberd_sql:sql_transaction( LServer, fun() -> ?SQL_UPSERT(LServer, "vcard", ["!username=%(LUser)s", "!server_host=%(LServer)s", "vcard=%(SVCARD)s"]), ?SQL_UPSERT(LServer, "vcard_search", ["username=%(User)s", "!lusername=%(LUser)s", "!server_host=%(LServer)s", "fn=%(FN)s", "lfn=%(LFN)s", "family=%(Family)s", "lfamily=%(LFamily)s", "given=%(Given)s", "lgiven=%(LGiven)s", "middle=%(Middle)s", "lmiddle=%(LMiddle)s", "nickname=%(Nickname)s", "lnickname=%(LNickname)s", "bday=%(BDay)s", "lbday=%(LBDay)s", "ctry=%(CTRY)s", "lctry=%(LCTRY)s", "locality=%(Locality)s", "llocality=%(LLocality)s", "email=%(EMail)s", "lemail=%(LEMail)s", "orgname=%(OrgName)s", "lorgname=%(LOrgName)s", "orgunit=%(OrgUnit)s", "lorgunit=%(LOrgUnit)s"]) end). search(LServer, Data, AllowReturnAll, MaxMatch) -> MatchSpec = make_matchspec(LServer, Data), if (MatchSpec == <<"">>) and not AllowReturnAll -> []; true -> Limit = case MaxMatch of infinity -> <<"">>; Val -> [<<" LIMIT ">>, integer_to_binary(Val)] end, case catch ejabberd_sql:sql_query( LServer, [<<"select username, fn, family, given, " "middle, nickname, bday, ctry, " "locality, email, orgname, orgunit " "from vcard_search ">>, MatchSpec, Limit, <<";">>]) of {selected, [<<"username">>, <<"fn">>, <<"family">>, <<"given">>, <<"middle">>, <<"nickname">>, <<"bday">>, <<"ctry">>, <<"locality">>, <<"email">>, <<"orgname">>, <<"orgunit">>], Rs} when is_list(Rs) -> [row_to_item(LServer, R) || R <- Rs]; Error -> ?ERROR_MSG("~p", [Error]), [] end end. search_fields(_LServer) -> [{?T("User"), <<"user">>}, {?T("Full Name"), <<"fn">>}, {?T("Name"), <<"first">>}, {?T("Middle Name"), <<"middle">>}, {?T("Family Name"), <<"last">>}, {?T("Nickname"), <<"nick">>}, {?T("Birthday"), <<"bday">>}, {?T("Country"), <<"ctry">>}, {?T("City"), <<"locality">>}, {?T("Email"), <<"email">>}, {?T("Organization Name"), <<"orgname">>}, {?T("Organization Unit"), <<"orgunit">>}]. search_reported(_LServer) -> [{?T("Jabber ID"), <<"jid">>}, {?T("Full Name"), <<"fn">>}, {?T("Name"), <<"first">>}, {?T("Middle Name"), <<"middle">>}, {?T("Family Name"), <<"last">>}, {?T("Nickname"), <<"nick">>}, {?T("Birthday"), <<"bday">>}, {?T("Country"), <<"ctry">>}, {?T("City"), <<"locality">>}, {?T("Email"), <<"email">>}, {?T("Organization Name"), <<"orgname">>}, {?T("Organization Unit"), <<"orgunit">>}]. remove_user(LUser, LServer) -> ejabberd_sql:sql_transaction( LServer, fun() -> ejabberd_sql:sql_query_t( ?SQL("delete from vcard" " where username=%(LUser)s and %(LServer)H")), ejabberd_sql:sql_query_t( ?SQL("delete from vcard_search" " where lusername=%(LUser)s and %(LServer)H")) end). export(_Server) -> [{vcard, fun(Host, #vcard{us = {LUser, LServer}, vcard = VCARD}) when LServer == Host -> SVCARD = fxml:element_to_binary(VCARD), [?SQL("delete from vcard" " where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT("vcard", ["username=%(LUser)s", "server_host=%(LServer)s", "vcard=%(SVCARD)s"])]; (_Host, _R) -> [] end}, {vcard_search, fun(Host, #vcard_search{user = {User, LServer}, luser = LUser, fn = FN, lfn = LFN, family = Family, lfamily = LFamily, given = Given, lgiven = LGiven, middle = Middle, lmiddle = LMiddle, nickname = Nickname, lnickname = LNickname, bday = BDay, lbday = LBDay, ctry = CTRY, lctry = LCTRY, locality = Locality, llocality = LLocality, email = EMail, lemail = LEMail, orgname = OrgName, lorgname = LOrgName, orgunit = OrgUnit, lorgunit = LOrgUnit}) when LServer == Host -> [?SQL("delete from vcard_search" " where lusername=%(LUser)s and %(LServer)H;"), ?SQL_INSERT("vcard_search", ["username=%(User)s", "lusername=%(LUser)s", "server_host=%(LServer)s", "fn=%(FN)s", "lfn=%(LFN)s", "family=%(Family)s", "lfamily=%(LFamily)s", "given=%(Given)s", "lgiven=%(LGiven)s", "middle=%(Middle)s", "lmiddle=%(LMiddle)s", "nickname=%(Nickname)s", "lnickname=%(LNickname)s", "bday=%(BDay)s", "lbday=%(LBDay)s", "ctry=%(CTRY)s", "lctry=%(LCTRY)s", "locality=%(Locality)s", "llocality=%(LLocality)s", "email=%(EMail)s", "lemail=%(LEMail)s", "orgname=%(OrgName)s", "lorgname=%(LOrgName)s", "orgunit=%(OrgUnit)s", "lorgunit=%(LOrgUnit)s"])]; (_Host, _R) -> [] end}]. import(_, _, _) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== make_matchspec(LServer, Data) -> filter_fields(Data, <<"">>, LServer). filter_fields([], Match, LServer) -> case ?USE_NEW_SCHEMA of true -> SServer = ejabberd_sql:escape(LServer), case Match of <<"">> -> [<<"where server_host='">>, SServer, <<"'">>]; _ -> [<<" where server_host='">>, SServer, <<"' and ">>, Match] end; false -> case Match of <<"">> -> <<"">>; _ -> [<<" where ">>, Match] end end; filter_fields([{SVar, [Val]} | Ds], Match, LServer) when is_binary(Val) and (Val /= <<"">>) -> LVal = mod_vcard:string2lower(Val), NewMatch = case SVar of <<"user">> -> make_val(Match, <<"lusername">>, LVal); <<"fn">> -> make_val(Match, <<"lfn">>, LVal); <<"last">> -> make_val(Match, <<"lfamily">>, LVal); <<"first">> -> make_val(Match, <<"lgiven">>, LVal); <<"middle">> -> make_val(Match, <<"lmiddle">>, LVal); <<"nick">> -> make_val(Match, <<"lnickname">>, LVal); <<"bday">> -> make_val(Match, <<"lbday">>, LVal); <<"ctry">> -> make_val(Match, <<"lctry">>, LVal); <<"locality">> -> make_val(Match, <<"llocality">>, LVal); <<"email">> -> make_val(Match, <<"lemail">>, LVal); <<"orgname">> -> make_val(Match, <<"lorgname">>, LVal); <<"orgunit">> -> make_val(Match, <<"lorgunit">>, LVal); _ -> Match end, filter_fields(Ds, NewMatch, LServer); filter_fields([_ | Ds], Match, LServer) -> filter_fields(Ds, Match, LServer). make_val(Match, Field, Val) -> Condition = case str:suffix(<<"*">>, Val) of true -> Val1 = str:substr(Val, 1, byte_size(Val) - 1), SVal = <<(ejabberd_sql:escape( ejabberd_sql:escape_like_arg_circumflex( Val1)))/binary, "%">>, [Field, <<" LIKE '">>, SVal, <<"' ESCAPE '^'">>]; _ -> SVal = ejabberd_sql:escape(Val), [Field, <<" = '">>, SVal, <<"'">>] end, case Match of <<"">> -> Condition; _ -> [Match, <<" and ">>, Condition] end. row_to_item(LServer, [Username, FN, Family, Given, Middle, Nickname, BDay, CTRY, Locality, EMail, OrgName, OrgUnit]) -> [{<<"jid">>, <>}, {<<"fn">>, FN}, {<<"last">>, Family}, {<<"first">>, Given}, {<<"middle">>, Middle}, {<<"nick">>, Nickname}, {<<"bday">>, BDay}, {<<"ctry">>, CTRY}, {<<"locality">>, Locality}, {<<"email">>, EMail}, {<<"orgname">>, OrgName}, {<<"orgunit">>, OrgUnit}]. ejabberd-18.01/src/mod_caps_mnesia.erl0000644000232200023220000000564713225664356020273 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_caps_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_caps_mnesia). -behaviour(mod_caps). %% API -export([init/2, caps_read/2, caps_write/3, import/3]). -export([need_transform/1, transform/1]). -include("mod_caps.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, caps_features, [{disc_only_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, caps_features)}]). caps_read(_LServer, Node) -> case mnesia:dirty_read({caps_features, Node}) of [#caps_features{features = Features}] -> {ok, Features}; _ -> error end. caps_write(_LServer, Node, Features) -> mnesia:dirty_write(#caps_features{node_pair = Node, features = Features}). import(_LServer, NodePair, [I]) when is_integer(I) -> mnesia:dirty_write( #caps_features{node_pair = NodePair, features = I}); import(_LServer, NodePair, Features) -> mnesia:dirty_write( #caps_features{node_pair = NodePair, features = Features}). need_transform(#caps_features{node_pair = {N, P}, features = Fs}) -> case is_list(N) orelse is_list(P) orelse (is_list(Fs) andalso lists:any(fun is_list/1, Fs)) of true -> ?INFO_MSG("Mnesia table 'caps_features' will be " "converted to binary", []), true; false -> false end. transform(#caps_features{node_pair = {N, P}, features = Fs} = R) -> NewFs = if is_integer(Fs) -> Fs; true -> [iolist_to_binary(F) || F <- Fs] end, R#caps_features{node_pair = {iolist_to_binary(N), iolist_to_binary(P)}, features = NewFs}. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/mod_http_upload.erl0000644000232200023220000010033613225664356020323 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_http_upload.erl %%% Author : Holger Weiss %%% Purpose : HTTP File Upload (XEP-0363) %%% Created : 20 Aug 2015 by Holger Weiss %%% %%% %%% ejabberd, Copyright (C) 2015-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_http_upload). -author('holger@zedat.fu-berlin.de'). -protocol({xep, 363, '0.1'}). -define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds. -define(SLOT_TIMEOUT, 18000000). % 5 hours. -define(FORMAT(Error), file:format_error(Error)). -define(URL_ENC(URL), binary_to_list(ejabberd_http:url_encode(URL))). -define(ADDR_TO_STR(IP), ejabberd_config:may_hide_data(misc:ip_to_list(IP))). -define(STR_TO_INT(Str, B), binary_to_integer(iolist_to_binary(Str), B)). -define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>). -define(CONTENT_TYPES, [{<<".avi">>, <<"video/avi">>}, {<<".bmp">>, <<"image/bmp">>}, {<<".bz2">>, <<"application/x-bzip2">>}, {<<".gif">>, <<"image/gif">>}, {<<".gz">>, <<"application/x-gzip">>}, {<<".jpeg">>, <<"image/jpeg">>}, {<<".jpg">>, <<"image/jpeg">>}, {<<".mp3">>, <<"audio/mpeg">>}, {<<".mp4">>, <<"video/mp4">>}, {<<".mpeg">>, <<"video/mpeg">>}, {<<".mpg">>, <<"video/mpeg">>}, {<<".ogg">>, <<"application/ogg">>}, {<<".pdf">>, <<"application/pdf">>}, {<<".png">>, <<"image/png">>}, {<<".rtf">>, <<"application/rtf">>}, {<<".svg">>, <<"image/svg+xml">>}, {<<".tiff">>, <<"image/tiff">>}, {<<".txt">>, <<"text/plain">>}, {<<".wav">>, <<"audio/wav">>}, {<<".webp">>, <<"image/webp">>}, {<<".xz">>, <<"application/x-xz">>}, {<<".zip">>, <<"application/zip">>}]). -behaviour(gen_server). -behaviour(gen_mod). %% gen_mod/supervisor callbacks. -export([start/2, stop/1, depends/2, mod_opt_type/1]). %% gen_server callbacks. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% ejabberd_http callback. -export([process/2]). %% ejabberd_hooks callback. -export([remove_user/2]). %% Utility functions. -export([get_proc_name/2, expand_home/1, expand_host/2]). -include("ejabberd.hrl"). -include("ejabberd_http.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -include("translate.hrl"). -record(state, {server_host :: binary(), hosts :: [binary()], name :: binary(), access :: atom(), max_size :: pos_integer() | infinity, secret_length :: pos_integer(), jid_in_url :: sha1 | node, file_mode :: integer() | undefined, dir_mode :: integer() | undefined, docroot :: binary(), put_url :: binary(), get_url :: binary(), service_url :: binary() | undefined, thumbnail :: boolean(), custom_headers :: [{binary(), binary()}], slots = #{} :: map()}). -record(media_info, {type :: atom(), height :: integer(), width :: integer()}). -type state() :: #state{}. -type slot() :: [binary(), ...]. -type media_info() :: #media_info{}. %%-------------------------------------------------------------------- %% gen_mod/supervisor callbacks. %%-------------------------------------------------------------------- -spec start(binary(), gen_mod:opts()) -> {ok, pid()}. start(ServerHost, Opts) -> case gen_mod:get_opt(rm_on_unregister, Opts, true) of true -> ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50); false -> ok end, Proc = get_proc_name(ServerHost, ?MODULE), gen_mod:start_child(?MODULE, ServerHost, Opts, Proc). -spec stop(binary()) -> ok | {error, any()}. stop(ServerHost) -> case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister, true) of true -> ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50); false -> ok end, Proc = get_proc_name(ServerHost, ?MODULE), gen_mod:stop_child(Proc). -spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun (L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(max_size) -> fun(I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(secret_length) -> fun(I) when is_integer(I), I >= 8 -> I end; mod_opt_type(jid_in_url) -> fun(sha1) -> sha1; (node) -> node end; mod_opt_type(file_mode) -> fun(Mode) -> ?STR_TO_INT(Mode, 8) end; mod_opt_type(dir_mode) -> fun(Mode) -> ?STR_TO_INT(Mode, 8) end; mod_opt_type(docroot) -> fun iolist_to_binary/1; mod_opt_type(put_url) -> fun(<<"http://", _/binary>> = URL) -> URL; (<<"https://", _/binary>> = URL) -> URL end; mod_opt_type(get_url) -> fun(<<"http://", _/binary>> = URL) -> URL; (<<"https://", _/binary>> = URL) -> URL end; mod_opt_type(service_url) -> fun(<<"http://", _/binary>> = URL) -> URL; (<<"https://", _/binary>> = URL) -> URL end; mod_opt_type(custom_headers) -> fun(Headers) -> lists:map(fun({K, V}) -> {iolist_to_binary(K), iolist_to_binary(V)} end, Headers) end; mod_opt_type(rm_on_unregister) -> fun(B) when is_boolean(B) -> B end; mod_opt_type(thumbnail) -> fun(B) when is_boolean(B) -> B end; mod_opt_type(_) -> [host, hosts, name, access, max_size, secret_length, jid_in_url, file_mode, dir_mode, docroot, put_url, get_url, service_url, custom_headers, rm_on_unregister, thumbnail]. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> []. %%-------------------------------------------------------------------- %% gen_server callbacks. %%-------------------------------------------------------------------- -spec init(list()) -> {ok, state()}. init([ServerHost, Opts]) -> process_flag(trap_exit, true), Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"upload.@HOST@">>), Name = gen_mod:get_opt(name, Opts, ?T("HTTP File Upload")), Access = gen_mod:get_opt(access, Opts, local), MaxSize = gen_mod:get_opt(max_size, Opts, 104857600), SecretLength = gen_mod:get_opt(secret_length, Opts, 40), JIDinURL = gen_mod:get_opt(jid_in_url, Opts, sha1), DocRoot = gen_mod:get_opt(docroot, Opts, <<"@HOME@/upload">>), FileMode = gen_mod:get_opt(file_mode, Opts), DirMode = gen_mod:get_opt(dir_mode, Opts), PutURL = gen_mod:get_opt(put_url, Opts, <<"http://@HOST@:5444">>), GetURL = gen_mod:get_opt(get_url, Opts, PutURL), ServiceURL = gen_mod:get_opt(service_url, Opts), Thumbnail = gen_mod:get_opt(thumbnail, Opts, true), CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []), DocRoot1 = expand_home(str:strip(DocRoot, right, $/)), DocRoot2 = expand_host(DocRoot1, ServerHost), case DirMode of undefined -> ok; Mode -> file:change_mode(DocRoot2, Mode) end, case Thumbnail of true -> case misc:have_eimp() of false -> ?ERROR_MSG("ejabberd is built without graphics support, " "please rebuild it with --enable-graphics or " "set 'thumbnail: false' for module '~s' in " "ejabberd.yml", [?MODULE]); _ -> ok end; false -> ok end, lists:foreach( fun(Host) -> ejabberd_router:register_route(Host, ServerHost) end, Hosts), {ok, #state{server_host = ServerHost, hosts = Hosts, name = Name, access = Access, max_size = MaxSize, secret_length = SecretLength, jid_in_url = JIDinURL, file_mode = FileMode, dir_mode = DirMode, thumbnail = Thumbnail, docroot = DocRoot2, put_url = expand_host(str:strip(PutURL, right, $/), ServerHost), get_url = expand_host(str:strip(GetURL, right, $/), ServerHost), service_url = ServiceURL, custom_headers = CustomHeaders}}. -spec handle_call(_, {pid(), _}, state()) -> {reply, {ok, pos_integer(), binary(), pos_integer() | undefined, pos_integer() | undefined}, state()} | {reply, {error, atom()}, state()} | {noreply, state()}. handle_call({use_slot, Slot, Size}, _From, #state{file_mode = FileMode, dir_mode = DirMode, get_url = GetPrefix, thumbnail = Thumbnail, custom_headers = CustomHeaders, docroot = DocRoot} = State) -> case get_slot(Slot, State) of {ok, {Size, Timer}} -> timer:cancel(Timer), NewState = del_slot(Slot, State), Path = str:join([DocRoot | Slot], <<$/>>), {reply, {ok, Path, FileMode, DirMode, GetPrefix, Thumbnail, CustomHeaders}, NewState}; {ok, {_WrongSize, _Timer}} -> {reply, {error, size_mismatch}, State}; error -> {reply, {error, invalid_slot}, State} end; handle_call(get_conf, _From, #state{docroot = DocRoot, custom_headers = CustomHeaders} = State) -> {reply, {ok, DocRoot, CustomHeaders}, State}; handle_call(Request, From, State) -> ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]), {noreply, State}. -spec handle_cast(_, state()) -> {noreply, state()}. handle_cast(Request, State) -> ?ERROR_MSG("Got unexpected request: ~p", [Request]), {noreply, State}. -spec handle_info(timeout | _, state()) -> {noreply, state()}. handle_info({route, #iq{lang = Lang} = Packet}, State) -> try xmpp:decode_els(Packet) of IQ -> {Reply, NewState} = case process_iq(IQ, State) of R when is_record(R, iq) -> {R, State}; {R, S} -> {R, S}; not_request -> {none, State} end, if Reply /= none -> ejabberd_router:route(Reply); true -> ok end, {noreply, NewState} catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), {noreply, State} end; handle_info({slot_timed_out, Slot}, State) -> NewState = del_slot(Slot, State), {noreply, NewState}; handle_info(Info, State) -> ?ERROR_MSG("Got unexpected info: ~p", [Info]), {noreply, State}. -spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok. terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) -> ?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]), lists:foreach(fun ejabberd_router:unregister_route/1, Hosts). -spec code_change({down, _} | _, state(), _) -> {ok, state()}. code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) -> ?DEBUG("Updating HTTP upload process for ~s", [ServerHost]), {ok, State}. %%-------------------------------------------------------------------- %% ejabberd_http callback. %%-------------------------------------------------------------------- -spec process([binary()], #request{}) -> {pos_integer(), [{binary(), binary()}], binary()}. process(LocalPath, #request{method = Method, host = Host, ip = IP}) when length(LocalPath) < 3, Method == 'PUT' orelse Method == 'GET' orelse Method == 'HEAD' -> ?DEBUG("Rejecting ~s request from ~s for ~s: Too few path components", [Method, ?ADDR_TO_STR(IP), Host]), http_response(404); process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP, data = Data} = Request) -> {Proc, Slot} = parse_http_request(Request), case catch gen_server:call(Proc, {use_slot, Slot, byte_size(Data)}) of {ok, Path, FileMode, DirMode, GetPrefix, Thumbnail, CustomHeaders} -> ?DEBUG("Storing file from ~s for ~s: ~s", [?ADDR_TO_STR(IP), Host, Path]), case store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) of ok -> http_response(201, CustomHeaders); {ok, Headers, OutData} -> http_response(201, Headers ++ CustomHeaders, OutData); {error, Error} -> ?ERROR_MSG("Cannot store file ~s from ~s for ~s: ~p", [Path, ?ADDR_TO_STR(IP), Host, ?FORMAT(Error)]), http_response(500) end; {error, size_mismatch} -> ?INFO_MSG("Rejecting file from ~s for ~s: Unexpected size (~B)", [?ADDR_TO_STR(IP), Host, byte_size(Data)]), http_response(413); {error, invalid_slot} -> ?INFO_MSG("Rejecting file from ~s for ~s: Invalid slot", [?ADDR_TO_STR(IP), Host]), http_response(403); Error -> ?ERROR_MSG("Cannot handle PUT request from ~s for ~s: ~p", [?ADDR_TO_STR(IP), Host, Error]), http_response(500) end; process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request) when Method == 'GET'; Method == 'HEAD' -> {Proc, [_UserDir, _RandDir, FileName] = Slot} = parse_http_request(Request), case catch gen_server:call(Proc, get_conf) of {ok, DocRoot, CustomHeaders} -> Path = str:join([DocRoot | Slot], <<$/>>), case file:read_file(Path) of {ok, Data} -> ?INFO_MSG("Serving ~s to ~s", [Path, ?ADDR_TO_STR(IP)]), ContentType = guess_content_type(FileName), Headers1 = case ContentType of <<"image/", _SubType/binary>> -> []; <<"text/", _SubType/binary>> -> []; _ -> [{<<"Content-Disposition">>, <<"attachment; filename=", $", FileName/binary, $">>}] end, Headers2 = [{<<"Content-Type">>, ContentType} | Headers1], Headers3 = Headers2 ++ CustomHeaders, http_response(200, Headers3, Data); {error, eacces} -> ?INFO_MSG("Cannot serve ~s to ~s: Permission denied", [Path, ?ADDR_TO_STR(IP)]), http_response(403); {error, enoent} -> ?INFO_MSG("Cannot serve ~s to ~s: No such file", [Path, ?ADDR_TO_STR(IP)]), http_response(404); {error, eisdir} -> ?INFO_MSG("Cannot serve ~s to ~s: Is a directory", [Path, ?ADDR_TO_STR(IP)]), http_response(404); {error, Error} -> ?INFO_MSG("Cannot serve ~s to ~s: ~s", [Path, ?ADDR_TO_STR(IP), ?FORMAT(Error)]), http_response(500) end; Error -> ?ERROR_MSG("Cannot handle ~s request from ~s for ~s: ~p", [Method, ?ADDR_TO_STR(IP), Host, Error]), http_response(500) end; process(_LocalPath, #request{method = 'OPTIONS', host = Host, ip = IP} = Request) -> ?DEBUG("Responding to OPTIONS request from ~s for ~s", [?ADDR_TO_STR(IP), Host]), {Proc, _Slot} = parse_http_request(Request), case catch gen_server:call(Proc, get_conf) of {ok, _DocRoot, CustomHeaders} -> http_response(200, CustomHeaders); Error -> ?ERROR_MSG("Cannot handle OPTIONS request from ~s for ~s: ~p", [?ADDR_TO_STR(IP), Host, Error]), http_response(500) end; process(_LocalPath, #request{method = Method, host = Host, ip = IP}) -> ?DEBUG("Rejecting ~s request from ~s for ~s", [Method, ?ADDR_TO_STR(IP), Host]), http_response(405, [{<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>}]). %%-------------------------------------------------------------------- %% Exported utility functions. %%-------------------------------------------------------------------- -spec get_proc_name(binary(), atom()) -> atom(). get_proc_name(ServerHost, ModuleName) -> PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url, <<"http://@HOST@">>), {ok, {_Scheme, _UserInfo, Host, _Port, Path, _Query}} = http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))), ProcPrefix = list_to_binary(string:strip(Host ++ Path, right, $/)), gen_mod:get_module_proc(ProcPrefix, ModuleName). -spec expand_home(binary()) -> binary(). expand_home(Input) -> {ok, [[Home]]} = init:get_argument(home), misc:expand_keyword(<<"@HOME@">>, Input, Home). -spec expand_host(binary(), binary()) -> binary(). expand_host(Input, Host) -> misc:expand_keyword(<<"@HOST@">>, Input, Host). %%-------------------------------------------------------------------- %% Internal functions. %%-------------------------------------------------------------------- %% XMPP request handling. -spec process_iq(iq(), state()) -> {iq(), state()} | iq() | not_request. process_iq(#iq{type = get, lang = Lang, sub_els = [#disco_info{}]} = IQ, #state{server_host = ServerHost, name = Name}) -> AddInfo = ejabberd_hooks:run_fold(disco_info, ServerHost, [], [ServerHost, ?MODULE, <<"">>, <<"">>]), xmpp:make_iq_result(IQ, iq_disco_info(ServerHost, Lang, Name, AddInfo)); process_iq(#iq{type = get, sub_els = [#disco_items{}]} = IQ, _State) -> xmpp:make_iq_result(IQ, #disco_items{}); process_iq(#iq{type = get, sub_els = [#upload_request{filename = File, size = Size, 'content-type' = CType, xmlns = XMLNS}]} = IQ, State) -> process_slot_request(IQ, File, Size, CType, XMLNS, State); process_iq(#iq{type = get, sub_els = [#upload_request_0{filename = File, size = Size, 'content-type' = CType, xmlns = XMLNS}]} = IQ, State) -> process_slot_request(IQ, File, Size, CType, XMLNS, State); process_iq(#iq{type = T, lang = Lang} = IQ, _State) when T == get; T == set -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)); process_iq(#iq{}, _State) -> not_request. -spec process_slot_request(iq(), binary(), pos_integer(), binary(), binary(), state()) -> {iq(), state()} | iq(). process_slot_request(#iq{lang = Lang, from = From} = IQ, File, Size, CType, XMLNS, #state{server_host = ServerHost, access = Access} = State) -> case acl:match_rule(ServerHost, Access, From) of allow -> ContentType = yield_content_type(CType), case create_slot(State, From, File, Size, ContentType, Lang) of {ok, Slot} -> {ok, Timer} = timer:send_after(?SLOT_TIMEOUT, {slot_timed_out, Slot}), NewState = add_slot(Slot, Size, Timer, State), NewSlot = mk_slot(Slot, State, XMLNS), {xmpp:make_iq_result(IQ, NewSlot), NewState}; {ok, PutURL, GetURL} -> Slot = mk_slot(PutURL, GetURL, XMLNS), xmpp:make_iq_result(IQ, Slot); {error, Error} -> xmpp:make_error(IQ, Error) end; deny -> ?DEBUG("Denying HTTP upload slot request from ~s", [jid:encode(From)]), Txt = <<"Access denied by service policy">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)) end. -spec create_slot(state(), jid(), binary(), pos_integer(), binary(), binary()) -> {ok, slot()} | {ok, binary(), binary()} | {error, xmlel()}. create_slot(#state{service_url = undefined, max_size = MaxSize}, JID, File, Size, _ContentType, Lang) when MaxSize /= infinity, Size > MaxSize -> Text = {<<"File larger than ~w bytes">>, [MaxSize]}, ?INFO_MSG("Rejecting file ~s from ~s (too large: ~B bytes)", [File, jid:encode(JID), Size]), {error, xmpp:err_not_acceptable(Text, Lang)}; create_slot(#state{service_url = undefined, jid_in_url = JIDinURL, secret_length = SecretLength, server_host = ServerHost, docroot = DocRoot}, JID, File, Size, _ContentType, Lang) -> UserStr = make_user_string(JID, JIDinURL), UserDir = <>, case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow, [JID, UserDir, Size, Lang]) of allow -> RandStr = make_rand_string(SecretLength), FileStr = make_file_string(File), ?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)", [jid:encode(JID), File]), {ok, [UserStr, RandStr, FileStr]}; deny -> {error, xmpp:err_service_unavailable()}; #stanza_error{} = Error -> {error, Error} end; create_slot(#state{service_url = ServiceURL}, #jid{luser = U, lserver = S} = JID, File, Size, ContentType, Lang) -> Options = [{body_format, binary}, {full_result, false}], HttpOptions = [{timeout, ?SERVICE_REQUEST_TIMEOUT}], SizeStr = integer_to_binary(Size), GetRequest = binary_to_list(ServiceURL) ++ "?jid=" ++ ?URL_ENC(jid:encode({U, S, <<"">>})) ++ "&name=" ++ ?URL_ENC(File) ++ "&size=" ++ ?URL_ENC(SizeStr) ++ "&content_type=" ++ ?URL_ENC(ContentType), case httpc:request(get, {GetRequest, []}, HttpOptions, Options) of {ok, {Code, Body}} when Code >= 200, Code =< 299 -> case binary:split(Body, <<$\n>>, [global, trim]) of [<<"http", _/binary>> = PutURL, <<"http", _/binary>> = GetURL] -> ?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)", [jid:encode(JID), File]), {ok, PutURL, GetURL}; Lines -> ?ERROR_MSG("Can't parse data received for ~s from <~s>: ~p", [jid:encode(JID), ServiceURL, Lines]), Txt = <<"Failed to parse HTTP response">>, {error, xmpp:err_service_unavailable(Txt, Lang)} end; {ok, {402, _Body}} -> ?INFO_MSG("Got status code 402 for ~s from <~s>", [jid:encode(JID), ServiceURL]), {error, xmpp:err_resource_constraint()}; {ok, {403, _Body}} -> ?INFO_MSG("Got status code 403 for ~s from <~s>", [jid:encode(JID), ServiceURL]), {error, xmpp:err_not_allowed()}; {ok, {413, _Body}} -> ?INFO_MSG("Got status code 413 for ~s from <~s>", [jid:encode(JID), ServiceURL]), {error, xmpp:err_not_acceptable()}; {ok, {Code, _Body}} -> ?ERROR_MSG("Got unexpected status code for ~s from <~s>: ~B", [jid:encode(JID), ServiceURL, Code]), {error, xmpp:err_service_unavailable()}; {error, Reason} -> ?ERROR_MSG("Error requesting upload slot for ~s from <~s>: ~p", [jid:encode(JID), ServiceURL, Reason]), {error, xmpp:err_service_unavailable()} end. -spec add_slot(slot(), pos_integer(), timer:tref(), state()) -> state(). add_slot(Slot, Size, Timer, #state{slots = Slots} = State) -> NewSlots = maps:put(Slot, {Size, Timer}, Slots), State#state{slots = NewSlots}. -spec get_slot(slot(), state()) -> {ok, {pos_integer(), timer:tref()}} | error. get_slot(Slot, #state{slots = Slots}) -> maps:find(Slot, Slots). -spec del_slot(slot(), state()) -> state(). del_slot(Slot, #state{slots = Slots} = State) -> NewSlots = maps:remove(Slot, Slots), State#state{slots = NewSlots}. -spec mk_slot(slot(), state(), binary()) -> upload_slot(); (binary(), binary(), binary()) -> upload_slot(). mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) -> PutURL = str:join([PutPrefix | Slot], <<$/>>), GetURL = str:join([GetPrefix | Slot], <<$/>>), mk_slot(PutURL, GetURL, XMLNS); mk_slot(PutURL, GetURL, ?NS_HTTP_UPLOAD_0) -> #upload_slot_0{get = GetURL, put = PutURL, xmlns = ?NS_HTTP_UPLOAD_0}; mk_slot(PutURL, GetURL, XMLNS) -> #upload_slot{get = GetURL, put = PutURL, xmlns = XMLNS}. -spec make_user_string(jid(), sha1 | node) -> binary(). make_user_string(#jid{luser = U, lserver = S}, sha1) -> str:sha(<>); make_user_string(#jid{luser = U}, node) -> re:replace(U, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]). -spec make_file_string(binary()) -> binary(). make_file_string(File) -> re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]). -spec make_rand_string(non_neg_integer()) -> binary(). make_rand_string(Length) -> list_to_binary(make_rand_string([], Length)). -spec make_rand_string(string(), non_neg_integer()) -> string(). make_rand_string(S, 0) -> S; make_rand_string(S, N) -> make_rand_string([make_rand_char() | S], N - 1). -spec make_rand_char() -> char(). make_rand_char() -> map_int_to_char(randoms:uniform(0, 61)). -spec map_int_to_char(0..61) -> char(). map_int_to_char(N) when N =< 9 -> N + 48; % Digit. map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character. map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character. -spec yield_content_type(binary()) -> binary(). yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE; yield_content_type(Type) -> Type. -spec iq_disco_info(binary(), binary(), binary(), [xdata()]) -> disco_info(). iq_disco_info(Host, Lang, Name, AddInfo) -> Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size, 104857600) of infinity -> AddInfo; MaxSize -> MaxSizeStr = integer_to_binary(MaxSize), XData = lists:map( fun(NS) -> Fields = [#xdata_field{ type = hidden, var = <<"FORM_TYPE">>, values = [NS]}, #xdata_field{ var = <<"max-file-size">>, values = [MaxSizeStr]}], #xdata{type = result, fields = Fields} end, [?NS_HTTP_UPLOAD, ?NS_HTTP_UPLOAD_0]), XData ++ AddInfo end, #disco_info{identities = [#identity{category = <<"store">>, type = <<"file">>, name = translate:translate(Lang, Name)}], features = [?NS_HTTP_UPLOAD, ?NS_HTTP_UPLOAD_0, ?NS_HTTP_UPLOAD_OLD, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS], xdata = Form}. %% HTTP request handling. -spec parse_http_request(#request{}) -> {atom(), slot()}. parse_http_request(#request{host = Host, path = Path}) -> PrefixLength = length(Path) - 3, {ProcURL, Slot} = if PrefixLength > 0 -> Prefix = lists:sublist(Path, PrefixLength), {str:join([Host | Prefix], $/), lists:nthtail(PrefixLength, Path)}; true -> {Host, Path} end, {gen_mod:get_module_proc(ProcURL, ?MODULE), Slot}. -spec store_file(binary(), binary(), integer() | undefined, integer() | undefined, binary(), slot(), boolean()) -> ok | {ok, [{binary(), binary()}], binary()} | {error, term()}. store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) -> case do_store_file(Path, Data, FileMode, DirMode) of ok when Thumbnail -> case identify(Path, Data) of {ok, MediaInfo} -> case convert(Path, Data, MediaInfo) of {ok, OutPath, OutMediaInfo} -> [UserDir, RandDir | _] = Slot, FileName = filename:basename(OutPath), URL = str:join([GetPrefix, UserDir, RandDir, FileName], <<$/>>), ThumbEl = thumb_el(OutMediaInfo, URL), {ok, [{<<"Content-Type">>, <<"text/xml; charset=utf-8">>}], fxml:element_to_binary(ThumbEl)}; pass -> ok end; pass -> ok end; ok -> ok; Err -> Err end. -spec do_store_file(file:filename_all(), binary(), integer() | undefined, integer() | undefined) -> ok | {error, term()}. do_store_file(Path, Data, FileMode, DirMode) -> try ok = filelib:ensure_dir(Path), {ok, Io} = file:open(Path, [write, exclusive, raw]), Ok = file:write(Io, Data), ok = file:close(Io), if is_integer(FileMode) -> ok = file:change_mode(Path, FileMode); FileMode == undefined -> ok end, if is_integer(DirMode) -> RandDir = filename:dirname(Path), UserDir = filename:dirname(RandDir), ok = file:change_mode(RandDir, DirMode), ok = file:change_mode(UserDir, DirMode); DirMode == undefined -> ok end, ok = Ok % Raise an exception if file:write/2 failed. catch _:{badmatch, {error, Error}} -> {error, Error}; _:Error -> {error, Error} end. -spec guess_content_type(binary()) -> binary(). guess_content_type(FileName) -> mod_http_fileserver:content_type(FileName, ?DEFAULT_CONTENT_TYPE, ?CONTENT_TYPES). -spec http_response(100..599) -> {pos_integer(), [{binary(), binary()}], binary()}. http_response(Code) -> http_response(Code, []). -spec http_response(100..599, [{binary(), binary()}]) -> {pos_integer(), [{binary(), binary()}], binary()}. http_response(Code, ExtraHeaders) -> Message = <<(code_to_message(Code))/binary, $\n>>, http_response(Code, ExtraHeaders, Message). -spec http_response(100..599, [{binary(), binary()}], binary()) -> {pos_integer(), [{binary(), binary()}], binary()}. http_response(Code, ExtraHeaders, Body) -> Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of true -> ExtraHeaders; false -> [{<<"Content-Type">>, <<"text/plain">>} | ExtraHeaders] end, {Code, Headers, Body}. -spec code_to_message(100..599) -> binary(). code_to_message(201) -> <<"Upload successful.">>; code_to_message(403) -> <<"Forbidden.">>; code_to_message(404) -> <<"Not found.">>; code_to_message(405) -> <<"Method not allowed.">>; code_to_message(413) -> <<"File size doesn't match requested size.">>; code_to_message(500) -> <<"Internal server error.">>; code_to_message(_Code) -> <<"">>. %%-------------------------------------------------------------------- %% Image manipulation stuff. %%-------------------------------------------------------------------- -spec identify(binary(), binary()) -> {ok, media_info()} | pass. identify(Path, Data) -> case misc:have_eimp() of true -> case eimp:identify(Data) of {ok, Info} -> {ok, #media_info{ type = proplists:get_value(type, Info), width = proplists:get_value(width, Info), height = proplists:get_value(height, Info)}}; {error, Why} -> ?DEBUG("Cannot identify type of ~s: ~s", [Path, eimp:format_error(Why)]), pass end; false -> pass end. -spec convert(binary(), binary(), media_info()) -> {ok, binary(), media_info()} | pass. convert(Path, Data, #media_info{type = T, width = W, height = H} = Info) -> if W * H >= 25000000 -> ?DEBUG("The image ~s is more than 25 Mpix", [Path]), pass; W =< 300, H =< 300 -> {ok, Path, Info}; true -> Dir = filename:dirname(Path), Ext = atom_to_binary(T, latin1), FileName = <<(randoms:get_string())/binary, $., Ext/binary>>, OutPath = filename:join(Dir, FileName), {W1, H1} = if W > H -> {300, round(H*300/W)}; H > W -> {round(W*300/H), 300}; true -> {300, 300} end, OutInfo = #media_info{type = T, width = W1, height = H1}, case eimp:convert(Data, T, [{scale, {W1, H1}}]) of {ok, OutData} -> case file:write_file(OutPath, OutData) of ok -> {ok, OutPath, OutInfo}; {error, Why} -> ?ERROR_MSG("Failed to write to ~s: ~s", [OutPath, file:format_error(Why)]), pass end; {error, Why} -> ?ERROR_MSG("Failed to convert ~s to ~s: ~s", [Path, OutPath, eimp:format_error(Why)]), pass end end. -spec thumb_el(media_info(), binary()) -> xmlel(). thumb_el(#media_info{type = T, height = H, width = W}, URI) -> MimeType = <<"image/", (atom_to_binary(T, latin1))/binary>>, Thumb = #thumbnail{'media-type' = MimeType, uri = URI, height = H, width = W}, xmpp:encode(Thumb). %%-------------------------------------------------------------------- %% Remove user. %%-------------------------------------------------------------------- -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> ServerHost = jid:nameprep(Server), DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot, <<"@HOME@/upload">>), JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url, sha1), DocRoot1 = expand_host(expand_home(DocRoot), ServerHost), UserStr = make_user_string(jid:make(User, Server), JIDinURL), UserDir = str:join([DocRoot1, UserStr], <<$/>>), case del_tree(UserDir) of ok -> ?INFO_MSG("Removed HTTP upload directory of ~s@~s", [User, Server]); {error, enoent} -> ?DEBUG("Found no HTTP upload directory of ~s@~s", [User, Server]); {error, Error} -> ?ERROR_MSG("Cannot remove HTTP upload directory of ~s@~s: ~p", [User, Server, ?FORMAT(Error)]) end, ok. -spec del_tree(file:filename_all()) -> ok | {error, term()}. del_tree(Dir) when is_binary(Dir) -> del_tree(binary_to_list(Dir)); del_tree(Dir) -> try {ok, Entries} = file:list_dir(Dir), lists:foreach(fun(Path) -> case filelib:is_dir(Path) of true -> ok = del_tree(Path); false -> ok = file:delete(Path) end end, [Dir ++ "/" ++ Entry || Entry <- Entries]), ok = file:del_dir(Dir) catch _:{badmatch, {error, Error}} -> {error, Error}; _:Error -> {error, Error} end. ejabberd-18.01/src/scram.erl0000644000232200023220000000536213225664356016251 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : scram.erl %%% Author : Stephen Röttger %%% Purpose : SCRAM (RFC 5802) %%% Created : 7 Aug 2011 by Stephen Röttger %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(scram). -author('stephen.roettger@googlemail.com'). %% External exports %% ejabberd doesn't implement SASLPREP, so we use the similar RESOURCEPREP instead -export([salted_password/3, stored_key/1, server_key/1, server_signature/2, client_signature/2, client_key/1, client_key/2]). -spec salted_password(binary(), binary(), non_neg_integer()) -> binary(). salted_password(Password, Salt, IterationCount) -> hi(jid:resourceprep(Password), Salt, IterationCount). -spec client_key(binary()) -> binary(). client_key(SaltedPassword) -> sha_mac(SaltedPassword, <<"Client Key">>). -spec stored_key(binary()) -> binary(). stored_key(ClientKey) -> crypto:hash(sha, ClientKey). -spec server_key(binary()) -> binary(). server_key(SaltedPassword) -> sha_mac(SaltedPassword, <<"Server Key">>). -spec client_signature(binary(), binary()) -> binary(). client_signature(StoredKey, AuthMessage) -> sha_mac(StoredKey, AuthMessage). -spec client_key(binary(), binary()) -> binary(). client_key(ClientProof, ClientSignature) -> crypto:exor(ClientProof, ClientSignature). -spec server_signature(binary(), binary()) -> binary(). server_signature(ServerKey, AuthMessage) -> sha_mac(ServerKey, AuthMessage). hi(Password, Salt, IterationCount) -> U1 = sha_mac(Password, <>), crypto:exor(U1, hi_round(Password, U1, IterationCount - 1)). hi_round(Password, UPrev, 1) -> sha_mac(Password, UPrev); hi_round(Password, UPrev, IterationCount) -> U = sha_mac(Password, UPrev), crypto:exor(U, hi_round(Password, U, IterationCount - 1)). sha_mac(Key, Data) -> crypto:hmac(sha, Key, Data). ejabberd-18.01/src/node_pep_sql.erl0000644000232200023220000002126613225664356017615 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_pep_sql.erl %%% Author : Christophe Romain %%% Purpose : Standard PubSub PEP plugin with ODBC backend %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the pep PubSub plugin. %%%

PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.

-module(node_pep_sql). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -compile([{parse_transform, ejabberd_sql_pt}]). -include("pubsub.hrl"). -include("ejabberd_sql_pt.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, depends/3, get_entity_subscriptions_for_send_last/2, get_last_items/3]). depends(_Host, _ServerHost, _Opts) -> [{mod_caps, hard}]. init(Host, ServerHost, Opts) -> node_flat_sql:init(Host, ServerHost, Opts), ok. terminate(Host, ServerHost) -> node_flat_sql:terminate(Host, ServerHost), ok. options() -> [{sql, true}, {rsm, true} | node_pep:options()]. features() -> [<<"rsm">> | node_pep:features()]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat_sql:create_node(Nidx, Owner), {result, {default, broadcast}}. delete_node(Nodes) -> {result, {_, _, Result}} = node_flat_sql:delete_node(Nodes), {result, {default, Result}}. subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat_sql:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> case node_flat_sql:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of {error, Error} -> {error, Error}; {result, _} -> {result, []} end. publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat_sql:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat_sql:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat_sql:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat_sql:purge_node(Nidx, Owner). get_entity_affiliations(_Host, Owner) -> OwnerKey = jid:tolower(jid:remove_resource(Owner)), node_flat_sql:get_entity_affiliations(OwnerKey, Owner). get_node_affiliations(Nidx) -> node_flat_sql:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat_sql:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat_sql:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(_Host, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), HLike = <<"%@", (node_flat_sql:encode_host_like(element(2, SubKey)))/binary>>, GJ = node_flat_sql:encode_jid(GenKey), Query = case SubKey of GenKey -> GJLike = <<(node_flat_sql:encode_jid_like(GenKey))/binary, "/%">>, ?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n " "where i.nodeid = n.nodeid and " "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host like %(HLike)s escape '^'"); _ -> SJ = node_flat_sql:encode_jid(SubKey), ?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n " "where i.nodeid = n.nodeid and " "jid in (%(SJ)s,%(GJ)s) and host like %(HLike)s escape '^'") end, {result, case ejabberd_sql:sql_query_t(Query) of {selected, RItems} -> lists:foldl( fun({H, N, T, I, J, S}, Acc) -> O = node_flat_sql:decode_jid(H), Node = nodetree_tree_sql:raw_to_node(O, {N, <<"">>, T, I}), Jid = node_flat_sql:decode_jid(J), lists:foldl( fun({Sub, SubId}, Acc2) -> [{Node, Sub, SubId, Jid} | Acc2] end, Acc, node_flat_sql:decode_subscriptions(S)) end, [], RItems); _ -> [] end}. get_entity_subscriptions_for_send_last(_Host, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), HLike = <<"%@", (node_flat_sql:encode_host_like(element(2, SubKey)))/binary>>, GJ = node_flat_sql:encode_jid(GenKey), Query = case SubKey of GenKey -> GJLike = <<(node_flat_sql:encode_jid_like(GenKey))/binary, "/%">>, ?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n, pubsub_node_option o " "where i.nodeid = n.nodeid and n.nodeid = o.nodeid and " "name='send_last_published_item' and val='on_sub_and_presence' and " "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host like %(HLike)s escape '^'"); _ -> SJ = node_flat_sql:encode_jid(SubKey), ?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n, pubsub_node_option o " "where i.nodeid = n.nodeid and n.nodeid = o.nodeid and " "name='send_last_published_item' and val='on_sub_and_presence' and " "jid in (%(SJ)s,%(GJ)s) and host like %(HLike)s escape '^'") end, {result, case ejabberd_sql:sql_query_t(Query) of {selected, RItems} -> lists:foldl( fun ({H, N, T, I, J, S}, Acc) -> O = node_flat_sql:decode_jid(H), Node = nodetree_tree_sql:raw_to_node(O, {N, <<"">>, T, I}), Jid = node_flat_sql:decode_jid(J), lists:foldl( fun ({Sub, SubId}, Acc2) -> [{Node, Sub, SubId, Jid}| Acc2] end, Acc, node_flat_sql:decode_subscriptions(S)) end, [], RItems); _ -> [] end}. get_node_subscriptions(Nidx) -> node_flat_sql:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat_sql:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat_sql:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat_sql:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat_sql:get_states(Nidx). get_state(Nidx, JID) -> node_flat_sql:get_state(Nidx, JID). set_state(State) -> node_flat_sql:set_state(State). get_items(Nidx, From, RSM) -> node_flat_sql:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat_sql:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, JID, Count) -> node_flat_sql:get_last_items(Nidx, JID, Count). get_item(Nidx, ItemId) -> node_flat_sql:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat_sql:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat_sql:set_item(Item). get_item_name(Host, Node, Id) -> node_flat_sql:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat_sql:node_to_path(Node). path_to_node(Path) -> node_flat_sql:path_to_node(Path). ejabberd-18.01/src/mod_stream_mgmt.erl0000644000232200023220000006621513225664356020326 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Author : Holger Weiss %%% Created : 25 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_stream_mgmt). -behaviour(gen_mod). -author('holger@zedat.fu-berlin.de'). -protocol({xep, 198, '1.5.2'}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% hooks -export([c2s_stream_init/2, c2s_stream_started/2, c2s_stream_features/2, c2s_authenticated_packet/2, c2s_unauthenticated_packet/2, c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2, c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3, c2s_handle_recv/3]). %% adjust pending session timeout -export([get_resume_timeout/1, set_resume_timeout/2]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -include("p1_queue.hrl"). -define(is_sm_packet(Pkt), is_record(Pkt, sm_enable) or is_record(Pkt, sm_resume) or is_record(Pkt, sm_a) or is_record(Pkt, sm_r)). -type state() :: ejabberd_c2s:state(). %%%=================================================================== %%% API %%%=================================================================== start(Host, _Opts) -> ejabberd_hooks:add(c2s_init, ?MODULE, c2s_stream_init, 50), ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE, c2s_stream_started, 50), ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, c2s_stream_features, 50), ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, c2s_unauthenticated_packet, 50), ejabberd_hooks:add(c2s_unbinded_packet, Host, ?MODULE, c2s_unbinded_packet, 50), ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE, c2s_authenticated_packet, 50), ejabberd_hooks:add(c2s_handle_send, Host, ?MODULE, c2s_handle_send, 50), ejabberd_hooks:add(c2s_handle_recv, Host, ?MODULE, c2s_handle_recv, 50), ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:add(c2s_handle_call, Host, ?MODULE, c2s_handle_call, 50), ejabberd_hooks:add(c2s_closed, Host, ?MODULE, c2s_closed, 50), ejabberd_hooks:add(c2s_terminated, Host, ?MODULE, c2s_terminated, 50). stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of true -> ok; false -> ejabberd_hooks:delete(c2s_init, ?MODULE, c2s_stream_init, 50) end, ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE, c2s_stream_started, 50), ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, c2s_stream_features, 50), ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, c2s_unauthenticated_packet, 50), ejabberd_hooks:delete(c2s_unbinded_packet, Host, ?MODULE, c2s_unbinded_packet, 50), ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE, c2s_authenticated_packet, 50), ejabberd_hooks:delete(c2s_handle_send, Host, ?MODULE, c2s_handle_send, 50), ejabberd_hooks:delete(c2s_handle_recv, Host, ?MODULE, c2s_handle_recv, 50), ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:delete(c2s_handle_call, Host, ?MODULE, c2s_handle_call, 50), ejabberd_hooks:delete(c2s_closed, Host, ?MODULE, c2s_closed, 50), ejabberd_hooks:delete(c2s_terminated, Host, ?MODULE, c2s_terminated, 50). reload(_Host, _NewOpts, _OldOpts) -> ?WARNING_MSG("module ~s is reloaded, but new configuration will take " "effect for newly created client connections only", [?MODULE]). depends(_Host, _Opts) -> []. c2s_stream_init({ok, State}, Opts) -> MgmtOpts = lists:filter( fun({stream_management, _}) -> true; ({max_ack_queue, _}) -> true; ({resume_timeout, _}) -> true; ({max_resume_timeout, _}) -> true; ({ack_timeout, _}) -> true; ({resend_on_timeout, _}) -> true; ({queue_type, _}) -> true; (_) -> false end, Opts), {ok, State#{mgmt_options => MgmtOpts}}; c2s_stream_init(Acc, _Opts) -> Acc. c2s_stream_started(#{lserver := LServer, mgmt_options := Opts} = State, _StreamStart) -> State1 = maps:remove(mgmt_options, State), ResumeTimeout = get_resume_timeout(LServer, Opts), MaxResumeTimeout = get_max_resume_timeout(LServer, Opts, ResumeTimeout), State1#{mgmt_state => inactive, mgmt_queue_type => get_queue_type(LServer, Opts), mgmt_max_queue => get_max_ack_queue(LServer, Opts), mgmt_timeout => ResumeTimeout, mgmt_max_timeout => MaxResumeTimeout, mgmt_ack_timeout => get_ack_timeout(LServer, Opts), mgmt_resend => get_resend_on_timeout(LServer, Opts), mgmt_stanzas_in => 0, mgmt_stanzas_out => 0, mgmt_stanzas_req => 0}; c2s_stream_started(State, _StreamStart) -> State. c2s_stream_features(Acc, Host) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> [#feature_sm{xmlns = ?NS_STREAM_MGMT_2}, #feature_sm{xmlns = ?NS_STREAM_MGMT_3}|Acc]; false -> Acc end. c2s_unauthenticated_packet(State, Pkt) when ?is_sm_packet(Pkt) -> %% XEP-0198 says: "For client-to-server connections, the client MUST NOT %% attempt to enable stream management until after it has completed Resource %% Binding unless it is resuming a previous session". However, it also %% says: "Stream management errors SHOULD be considered recoverable", so we %% won't bail out. Err = #sm_failed{reason = 'unexpected-request', xmlns = ?NS_STREAM_MGMT_3}, {stop, send(State, Err)}; c2s_unauthenticated_packet(State, _Pkt) -> State. c2s_unbinded_packet(State, #sm_resume{} = Pkt) -> case handle_resume(State, Pkt) of {ok, ResumedState} -> {stop, ResumedState}; {error, State1} -> {stop, State1} end; c2s_unbinded_packet(State, Pkt) when ?is_sm_packet(Pkt) -> c2s_unauthenticated_packet(State, Pkt); c2s_unbinded_packet(State, _Pkt) -> State. c2s_authenticated_packet(#{mgmt_state := MgmtState} = State, Pkt) when ?is_sm_packet(Pkt) -> if MgmtState == pending; MgmtState == active -> {stop, perform_stream_mgmt(Pkt, State)}; true -> {stop, negotiate_stream_mgmt(Pkt, State)} end; c2s_authenticated_packet(State, Pkt) -> update_num_stanzas_in(State, Pkt). c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) -> Xmlns = xmpp:get_ns(El), if Xmlns == ?NS_STREAM_MGMT_2; Xmlns == ?NS_STREAM_MGMT_3 -> Txt = xmpp:io_format_error(Why), Err = #sm_failed{reason = 'bad-request', text = xmpp:mk_text(Txt, Lang), xmlns = Xmlns}, send(State, Err); true -> State end; c2s_handle_recv(State, _, _) -> State. c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod, lang := Lang} = State, Pkt, SendResult) when MgmtState == pending; MgmtState == active -> case Pkt of _ when ?is_stanza(Pkt) -> Meta = xmpp:get_meta(Pkt), case maps:get(mgmt_is_resent, Meta, false) of false -> case mgmt_queue_add(State, Pkt) of #{mgmt_max_queue := exceeded} = State1 -> State2 = State1#{mgmt_resend => false}, Err = xmpp:serr_policy_violation( <<"Too many unacked stanzas">>, Lang), send(State2, Err); State1 when SendResult == ok -> send_rack(State1); State1 -> State1 end; true -> State end; #stream_error{} -> case MgmtState of active -> State; pending -> Mod:stop(State#{stop_reason => {stream, {out, Pkt}}}) end; _ -> State end; c2s_handle_send(State, _Pkt, _Result) -> State. c2s_handle_call(#{sid := {Time, _}, mod := Mod, mgmt_queue := Queue} = State, {resume_session, Time}, From) -> State1 = State#{mgmt_queue => p1_queue:file_to_ram(Queue)}, Mod:reply(From, {resume, State1}), {stop, State#{mgmt_state => resumed}}; c2s_handle_call(#{mod := Mod} = State, {resume_session, _}, From) -> Mod:reply(From, {error, <<"Previous session not found">>}), {stop, State}; c2s_handle_call(State, _Call, _From) -> State. c2s_handle_info(#{mgmt_ack_timer := TRef, jid := JID, mod := Mod} = State, {timeout, TRef, ack_timeout}) -> ?DEBUG("Timed out waiting for stream management acknowledgement of ~s", [jid:encode(JID)]), State1 = Mod:close(State), {stop, transition_to_pending(State1)}; c2s_handle_info(#{mgmt_state := pending, mgmt_pending_timer := TRef, jid := JID, mod := Mod} = State, {timeout, TRef, pending_timeout}) -> ?DEBUG("Timed out waiting for resumption of stream for ~s", [jid:encode(JID)]), Txt = <<"Timed out waiting for stream resumption">>, Err = xmpp:serr_connection_timeout(Txt, ?MYLANG), Mod:stop(State#{mgmt_state => timeout, stop_reason => {stream, {out, Err}}}); c2s_handle_info(#{jid := JID} = State, {_Ref, {resume, OldState}}) -> %% This happens if the resume_session/1 request timed out; the new session %% now receives the late response. ?DEBUG("Received old session state for ~s after failed resumption", [jid:encode(JID)]), route_unacked_stanzas(OldState#{mgmt_resend => false}), State; c2s_handle_info(State, _) -> State. c2s_closed(State, {stream, _}) -> State; c2s_closed(#{mgmt_state := active} = State, _Reason) -> {stop, transition_to_pending(State)}; c2s_closed(State, _Reason) -> State. c2s_terminated(#{mgmt_state := resumed, jid := JID} = State, _Reason) -> ?INFO_MSG("Closing former stream of resumed session for ~s", [jid:encode(JID)]), bounce_message_queue(), {stop, State}; c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In, sid := SID, user := U, server := S, resource := R} = State, Reason) -> Result = case MgmtState of timeout -> Info = [{num_stanzas_in, In}], %% TODO: Usually, ejabberd_c2s:process_terminated/2 is %% called later in the hook chain. We swap the order so %% that the offline info won't be purged after we stored %% it. This should be fixed in a proper way. State1 = ejabberd_c2s:process_terminated(State, Reason), ejabberd_sm:set_offline_info(SID, U, S, R, Info), {stop, State1}; _ -> State end, route_unacked_stanzas(State), Result; c2s_terminated(State, _Reason) -> State. %%%=================================================================== %%% Adjust pending session timeout %%%=================================================================== -spec get_resume_timeout(state()) -> non_neg_integer(). get_resume_timeout(#{mgmt_timeout := Timeout}) -> Timeout. -spec set_resume_timeout(state(), non_neg_integer()) -> state(). set_resume_timeout(#{mgmt_timeout := Timeout} = State, Timeout) -> State; set_resume_timeout(State, Timeout) -> State1 = restart_pending_timer(State, Timeout), State1#{mgmt_timeout => Timeout}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec negotiate_stream_mgmt(xmpp_element(), state()) -> state(). negotiate_stream_mgmt(Pkt, State) -> Xmlns = xmpp:get_ns(Pkt), case Pkt of #sm_enable{} -> handle_enable(State#{mgmt_xmlns => Xmlns}, Pkt); _ when is_record(Pkt, sm_a); is_record(Pkt, sm_r); is_record(Pkt, sm_resume) -> Err = #sm_failed{reason = 'unexpected-request', xmlns = Xmlns}, send(State, Err); _ -> Err = #sm_failed{reason = 'bad-request', xmlns = Xmlns}, send(State, Err) end. -spec perform_stream_mgmt(xmpp_element(), state()) -> state(). perform_stream_mgmt(Pkt, #{mgmt_xmlns := Xmlns} = State) -> case xmpp:get_ns(Pkt) of Xmlns -> case Pkt of #sm_r{} -> handle_r(State); #sm_a{} -> handle_a(State, Pkt); _ when is_record(Pkt, sm_enable); is_record(Pkt, sm_resume) -> send(State, #sm_failed{reason = 'unexpected-request', xmlns = Xmlns}); _ -> send(State, #sm_failed{reason = 'bad-request', xmlns = Xmlns}) end; _ -> send(State, #sm_failed{reason = 'unsupported-version', xmlns = Xmlns}) end. -spec handle_enable(state(), sm_enable()) -> state(). handle_enable(#{mgmt_timeout := DefaultTimeout, mgmt_queue_type := QueueType, mgmt_max_timeout := MaxTimeout, mgmt_xmlns := Xmlns, jid := JID} = State, #sm_enable{resume = Resume, max = Max}) -> Timeout = if Resume == false -> 0; Max /= undefined, Max > 0, Max =< MaxTimeout -> Max; true -> DefaultTimeout end, Res = if Timeout > 0 -> ?INFO_MSG("Stream management with resumption enabled for ~s", [jid:encode(JID)]), #sm_enabled{xmlns = Xmlns, id = make_resume_id(State), resume = true, max = Timeout}; true -> ?INFO_MSG("Stream management without resumption enabled for ~s", [jid:encode(JID)]), #sm_enabled{xmlns = Xmlns} end, State1 = State#{mgmt_state => active, mgmt_queue => p1_queue:new(QueueType), mgmt_timeout => Timeout}, send(State1, Res). -spec handle_r(state()) -> state(). handle_r(#{mgmt_xmlns := Xmlns, mgmt_stanzas_in := H} = State) -> Res = #sm_a{xmlns = Xmlns, h = H}, send(State, Res). -spec handle_a(state(), sm_a()) -> state(). handle_a(State, #sm_a{h = H}) -> State1 = check_h_attribute(State, H), resend_rack(State1). -spec handle_resume(state(), sm_resume()) -> {ok, state()} | {error, state()}. handle_resume(#{user := User, lserver := LServer, lang := Lang, socket := Socket} = State, #sm_resume{h = H, previd = PrevID, xmlns = Xmlns}) -> R = case inherit_session_state(State, PrevID) of {ok, InheritedState} -> {ok, InheritedState, H}; {error, Err, InH} -> {error, #sm_failed{reason = 'item-not-found', text = xmpp:mk_text(Err, Lang), h = InH, xmlns = Xmlns}, Err}; {error, Err} -> {error, #sm_failed{reason = 'item-not-found', text = xmpp:mk_text(Err, Lang), xmlns = Xmlns}, Err} end, case R of {ok, #{jid := JID} = ResumedState, NumHandled} -> State1 = check_h_attribute(ResumedState, NumHandled), #{mgmt_xmlns := AttrXmlns, mgmt_stanzas_in := AttrH} = State1, AttrId = make_resume_id(State1), State2 = send(State1, #sm_resumed{xmlns = AttrXmlns, h = AttrH, previd = AttrId}), State3 = resend_unacked_stanzas(State2), State4 = send(State3, #sm_r{xmlns = AttrXmlns}), State5 = ejabberd_hooks:run_fold(c2s_session_resumed, LServer, State4, []), ?INFO_MSG("(~s) Resumed session for ~s", [xmpp_socket:pp(Socket), jid:encode(JID)]), {ok, State5}; {error, El, Msg} -> ?INFO_MSG("Cannot resume session for ~s@~s: ~s", [User, LServer, Msg]), {error, send(State, El)} end. -spec transition_to_pending(state()) -> state(). transition_to_pending(#{mgmt_state := active, mod := Mod, mgmt_timeout := 0} = State) -> Mod:stop(State); transition_to_pending(#{mgmt_state := active, jid := JID, lserver := LServer, mgmt_timeout := Timeout} = State) -> State1 = cancel_ack_timer(State), ?INFO_MSG("Waiting for resumption of stream for ~s", [jid:encode(JID)]), TRef = erlang:start_timer(timer:seconds(Timeout), self(), pending_timeout), State2 = State1#{mgmt_state => pending, mgmt_pending_timer => TRef}, ejabberd_hooks:run_fold(c2s_session_pending, LServer, State2, []); transition_to_pending(State) -> State. -spec check_h_attribute(state(), non_neg_integer()) -> state(). check_h_attribute(#{mgmt_stanzas_out := NumStanzasOut, jid := JID} = State, H) when H > NumStanzasOut -> ?DEBUG("~s acknowledged ~B stanzas, but only ~B were sent", [jid:encode(JID), H, NumStanzasOut]), mgmt_queue_drop(State#{mgmt_stanzas_out => H}, NumStanzasOut); check_h_attribute(#{mgmt_stanzas_out := NumStanzasOut, jid := JID} = State, H) -> ?DEBUG("~s acknowledged ~B of ~B stanzas", [jid:encode(JID), H, NumStanzasOut]), mgmt_queue_drop(State, H). -spec update_num_stanzas_in(state(), xmpp_element()) -> state(). update_num_stanzas_in(#{mgmt_state := MgmtState, mgmt_stanzas_in := NumStanzasIn} = State, El) when MgmtState == active; MgmtState == pending -> NewNum = case {xmpp:is_stanza(El), NumStanzasIn} of {true, 4294967295} -> 0; {true, Num} -> Num + 1; {false, Num} -> Num end, State#{mgmt_stanzas_in => NewNum}; update_num_stanzas_in(State, _El) -> State. -spec send_rack(state()) -> state(). send_rack(#{mgmt_ack_timer := _} = State) -> State; send_rack(#{mgmt_xmlns := Xmlns, mgmt_stanzas_out := NumStanzasOut, mgmt_ack_timeout := AckTimeout} = State) -> TRef = erlang:start_timer(AckTimeout, self(), ack_timeout), State1 = State#{mgmt_ack_timer => TRef, mgmt_stanzas_req => NumStanzasOut}, send(State1, #sm_r{xmlns = Xmlns}). -spec resend_rack(state()) -> state(). resend_rack(#{mgmt_ack_timer := _, mgmt_queue := Queue, mgmt_stanzas_out := NumStanzasOut, mgmt_stanzas_req := NumStanzasReq} = State) -> State1 = cancel_ack_timer(State), case NumStanzasReq < NumStanzasOut andalso not p1_queue:is_empty(Queue) of true -> send_rack(State1); false -> State1 end; resend_rack(State) -> State. -spec mgmt_queue_add(state(), xmpp_element()) -> state(). mgmt_queue_add(#{mgmt_stanzas_out := NumStanzasOut, mgmt_queue := Queue} = State, Pkt) -> NewNum = case NumStanzasOut of 4294967295 -> 0; Num -> Num + 1 end, Queue1 = p1_queue:in({NewNum, p1_time_compat:timestamp(), Pkt}, Queue), State1 = State#{mgmt_queue => Queue1, mgmt_stanzas_out => NewNum}, check_queue_length(State1). -spec mgmt_queue_drop(state(), non_neg_integer()) -> state(). mgmt_queue_drop(#{mgmt_queue := Queue} = State, NumHandled) -> NewQueue = p1_queue:dropwhile( fun({N, _T, _E}) -> N =< NumHandled end, Queue), State#{mgmt_queue => NewQueue}. -spec check_queue_length(state()) -> state(). check_queue_length(#{mgmt_max_queue := Limit} = State) when Limit == infinity; Limit == exceeded -> State; check_queue_length(#{mgmt_queue := Queue, mgmt_max_queue := Limit} = State) -> case p1_queue:len(Queue) > Limit of true -> State#{mgmt_max_queue => exceeded}; false -> State end. -spec resend_unacked_stanzas(state()) -> state(). resend_unacked_stanzas(#{mgmt_state := MgmtState, mgmt_queue := Queue, jid := JID} = State) when (MgmtState == active orelse MgmtState == pending orelse MgmtState == timeout) andalso ?qlen(Queue) > 0 -> ?DEBUG("Resending ~B unacknowledged stanza(s) to ~s", [p1_queue:len(Queue), jid:encode(JID)]), p1_queue:foldl( fun({_, Time, Pkt}, AccState) -> NewPkt = add_resent_delay_info(AccState, Pkt, Time), send(AccState, xmpp:put_meta(NewPkt, mgmt_is_resent, true)) end, State, Queue); resend_unacked_stanzas(State) -> State. -spec route_unacked_stanzas(state()) -> ok. route_unacked_stanzas(#{mgmt_state := MgmtState, mgmt_resend := MgmtResend, lang := Lang, user := User, jid := JID, lserver := LServer, mgmt_queue := Queue, resource := Resource} = State) when (MgmtState == active orelse MgmtState == pending orelse MgmtState == timeout) andalso ?qlen(Queue) > 0 -> ResendOnTimeout = case MgmtResend of Resend when is_boolean(Resend) -> Resend; if_offline -> case ejabberd_sm:get_user_resources(User, LServer) of [Resource] -> %% Same resource opened new session true; [] -> true; _ -> false end end, ?DEBUG("Re-routing ~B unacknowledged stanza(s) to ~s", [p1_queue:len(Queue), jid:encode(JID)]), p1_queue:foreach( fun({_, _Time, #presence{from = From}}) -> ?DEBUG("Dropping presence stanza from ~s", [jid:encode(From)]); ({_, _Time, #iq{} = El}) -> Txt = <<"User session terminated">>, ejabberd_router:route_error( El, xmpp:err_service_unavailable(Txt, Lang)); ({_, _Time, #message{from = From, meta = #{carbon_copy := true}}}) -> %% XEP-0280 says: "When a receiving server attempts to deliver a %% forked message, and that message bounces with an error for %% any reason, the receiving server MUST NOT forward that error %% back to the original sender." Resending such a stanza could %% easily lead to unexpected results as well. ?DEBUG("Dropping forwarded message stanza from ~s", [jid:encode(From)]); ({_, Time, #message{} = Msg}) -> case ejabberd_hooks:run_fold(message_is_archived, LServer, false, [State, Msg]) of true -> ?DEBUG("Dropping archived message stanza from ~s", [jid:encode(xmpp:get_from(Msg))]); false when ResendOnTimeout -> NewEl = add_resent_delay_info(State, Msg, Time), ejabberd_router:route(NewEl); false -> Txt = <<"User session terminated">>, ejabberd_router:route_error( Msg, xmpp:err_service_unavailable(Txt, Lang)) end; ({_, _Time, El}) -> %% Raw element of type 'error' resulting from a validation error %% We cannot pass it to the router, it will generate an error ?DEBUG("Do not route raw element from ack queue: ~p", [El]) end, Queue); route_unacked_stanzas(_State) -> ok. -spec inherit_session_state(state(), binary()) -> {ok, state()} | {error, binary()} | {error, binary(), non_neg_integer()}. inherit_session_state(#{user := U, server := S, mgmt_queue_type := QueueType} = State, ResumeID) -> case misc:base64_to_term(ResumeID) of {term, {R, Time}} -> case ejabberd_sm:get_session_pid(U, S, R) of none -> case ejabberd_sm:get_offline_info(Time, U, S, R) of none -> {error, <<"Previous session PID not found">>}; Info -> case proplists:get_value(num_stanzas_in, Info) of undefined -> {error, <<"Previous session timed out">>}; H -> {error, <<"Previous session timed out">>, H} end end; OldPID -> OldSID = {Time, OldPID}, try resume_session(OldSID, State) of {resume, #{mgmt_xmlns := Xmlns, mgmt_queue := Queue, mgmt_timeout := Timeout, mgmt_stanzas_in := NumStanzasIn, mgmt_stanzas_out := NumStanzasOut} = OldState} -> State1 = ejabberd_c2s:copy_state(State, OldState), Queue1 = case QueueType of ram -> Queue; _ -> p1_queue:ram_to_file(Queue) end, State2 = State1#{mgmt_xmlns => Xmlns, mgmt_queue => Queue1, mgmt_timeout => Timeout, mgmt_stanzas_in => NumStanzasIn, mgmt_stanzas_out => NumStanzasOut, mgmt_state => active}, ejabberd_sm:close_session(OldSID, U, S, R), State3 = ejabberd_c2s:open_session(State2), ejabberd_c2s:stop(OldPID), {ok, State3}; {error, Msg} -> {error, Msg} catch exit:{noproc, _} -> {error, <<"Previous session PID is dead">>}; exit:{timeout, _} -> {error, <<"Session state copying timed out">>} end end; _ -> {error, <<"Invalid 'previd' value">>} end. -spec resume_session({erlang:timestamp(), pid()}, state()) -> {resume, state()} | {error, binary()}. resume_session({Time, Pid}, _State) -> ejabberd_c2s:call(Pid, {resume_session, Time}, timer:seconds(15)). -spec make_resume_id(state()) -> binary(). make_resume_id(#{sid := {Time, _}, resource := Resource}) -> misc:term_to_base64({Resource, Time}). -spec add_resent_delay_info(state(), stanza(), erlang:timestamp()) -> stanza(); (state(), xmlel(), erlang:timestamp()) -> xmlel(). add_resent_delay_info(#{lserver := LServer}, El, Time) when is_record(El, message); is_record(El, presence) -> xmpp_util:add_delay_info(El, jid:make(LServer), Time, <<"Resent">>); add_resent_delay_info(_State, El, _Time) -> El. -spec send(state(), xmpp_element()) -> state(). send(#{mod := Mod} = State, Pkt) -> Mod:send(State, Pkt). -spec restart_pending_timer(state(), non_neg_integer()) -> state(). restart_pending_timer(#{mgmt_pending_timer := TRef} = State, NewTimeout) -> cancel_timer(TRef), NewTRef = erlang:start_timer(timer:seconds(NewTimeout), self(), pending_timeout), State#{mgmt_pending_timer => NewTRef}; restart_pending_timer(State, _NewTimeout) -> State. -spec cancel_ack_timer(state()) -> state(). cancel_ack_timer(#{mgmt_ack_timer := TRef} = State) -> cancel_timer(TRef), maps:remove(mgmt_ack_timer, State); cancel_ack_timer(State) -> State. -spec cancel_timer(reference()) -> ok. cancel_timer(TRef) -> case erlang:cancel_timer(TRef) of false -> receive {timeout, TRef, _} -> ok after 0 -> ok end; _ -> ok end. -spec bounce_message_queue() -> ok. bounce_message_queue() -> receive {route, Pkt} -> ejabberd_router:route(Pkt), bounce_message_queue() after 0 -> ok end. %%%=================================================================== %%% Configuration processing %%%=================================================================== get_max_ack_queue(Host, Opts) -> gen_mod:get_module_opt(Host, ?MODULE, max_ack_queue, gen_mod:get_opt(max_ack_queue, Opts, 5000)). get_resume_timeout(Host, Opts) -> gen_mod:get_module_opt(Host, ?MODULE, resume_timeout, gen_mod:get_opt(resume_timeout, Opts, 300)). get_max_resume_timeout(Host, Opts, ResumeTimeout) -> case gen_mod:get_module_opt(Host, ?MODULE, max_resume_timeout, gen_mod:get_opt(max_resume_timeout, Opts)) of undefined -> ResumeTimeout; Max when Max >= ResumeTimeout -> Max; _ -> ResumeTimeout end. get_ack_timeout(Host, Opts) -> case gen_mod:get_module_opt(Host, ?MODULE, ack_timeout, gen_mod:get_opt(ack_timeout, Opts, 60)) of infinity -> infinity; T -> timer:seconds(T) end. get_resend_on_timeout(Host, Opts) -> gen_mod:get_module_opt(Host, ?MODULE, resend_on_timeout, gen_mod:get_opt(resend_on_timeout, Opts, false)). get_queue_type(Host, Opts) -> case gen_mod:get_module_opt(Host, ?MODULE, queue_type, gen_mod:get_opt(queue_type, Opts)) of undefined -> ejabberd_config:default_queue_type(Host); Type -> Type end. mod_opt_type(max_ack_queue) -> fun(I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(resume_timeout) -> fun(I) when is_integer(I), I >= 0 -> I end; mod_opt_type(max_resume_timeout) -> fun(I) when is_integer(I), I >= 0 -> I end; mod_opt_type(ack_timeout) -> fun(I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(resend_on_timeout) -> fun(B) when is_boolean(B) -> B; (if_offline) -> if_offline end; mod_opt_type(queue_type) -> fun(ram) -> ram; (file) -> file end; mod_opt_type(_) -> [max_ack_queue, resume_timeout, max_resume_timeout, ack_timeout, resend_on_timeout, queue_type]. ejabberd-18.01/src/mod_push_keepalive.erl0000644000232200023220000002125313225664356021004 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_push_keepalive.erl %%% Author : Holger Weiss %%% Purpose : Keep pending XEP-0198 sessions alive with XEP-0357 %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% %%% ejabberd, Copyright (C) 2017-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_push_keepalive). -author('holger@zedat.fu-berlin.de'). -behavior(gen_mod). %% gen_mod callbacks. -export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]). %% ejabberd_hooks callbacks. -export([c2s_session_pending/1, c2s_session_resumed/1, c2s_copy_session/2, c2s_handle_cast/2, c2s_handle_info/2, c2s_stanza/3]). -include("logger.hrl"). -include("xmpp.hrl"). -define(PUSH_BEFORE_TIMEOUT_SECS, 120). -type c2s_state() :: ejabberd_c2s:state(). %%-------------------------------------------------------------------- %% gen_mod callbacks. %%-------------------------------------------------------------------- -spec start(binary(), gen_mod:opts()) -> ok. start(Host, Opts) -> case gen_mod:get_opt(wake_on_start, Opts, false) of true -> wake_all(Host); false -> ok end, register_hooks(Host). -spec stop(binary()) -> ok. stop(Host) -> unregister_hooks(Host). -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(wake_on_start, NewOpts, OldOpts, false) of {false, true, _} -> wake_all(Host); _ -> ok end, ok. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> [{mod_push, hard}, {mod_client_state, soft}, {mod_stream_mgmt, soft}]. -spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. mod_opt_type(resume_timeout) -> fun(I) when is_integer(I), I >= 0 -> I; (undefined) -> undefined end; mod_opt_type(wake_on_start) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(wake_on_timeout) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun(I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [resume_timeout, wake_on_start, wake_on_timeout, cache_life_time, cache_size, use_cache, cache_missed, iqdisc]. %%-------------------------------------------------------------------- %% Register/unregister hooks. %%-------------------------------------------------------------------- -spec register_hooks(binary()) -> ok. register_hooks(Host) -> ejabberd_hooks:add(c2s_session_pending, Host, ?MODULE, c2s_session_pending, 50), ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:add(c2s_handle_cast, Host, ?MODULE, c2s_handle_cast, 40), ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:add(c2s_handle_send, Host, ?MODULE, c2s_stanza, 50). -spec unregister_hooks(binary()) -> ok. unregister_hooks(Host) -> ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), ejabberd_hooks:delete(c2s_session_pending, Host, ?MODULE, c2s_session_pending, 50), ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:delete(c2s_handle_cast, Host, ?MODULE, c2s_handle_cast, 40), ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:delete(c2s_handle_send, Host, ?MODULE, c2s_stanza, 50). %%-------------------------------------------------------------------- %% Hook callbacks. %%-------------------------------------------------------------------- -spec c2s_stanza(c2s_state(), xmpp_element() | xmlel(), term()) -> c2s_state(). c2s_stanza(#{push_enabled := true, mgmt_state := pending} = State, _Pkt, _SendResult) -> maybe_restore_resume_timeout(State); c2s_stanza(State, _Pkt, _SendResult) -> State. -spec c2s_session_pending(c2s_state()) -> c2s_state(). c2s_session_pending(#{push_enabled := true, mgmt_queue := Queue} = State) -> case p1_queue:len(Queue) of 0 -> State1 = maybe_adjust_resume_timeout(State), maybe_start_wakeup_timer(State1); _ -> State end; c2s_session_pending(State) -> State. -spec c2s_session_resumed(c2s_state()) -> c2s_state(). c2s_session_resumed(#{push_enabled := true} = State) -> maybe_restore_resume_timeout(State); c2s_session_resumed(State) -> State. -spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state(). c2s_copy_session(State, #{push_enabled := true, push_resume_timeout := ResumeTimeout, push_wake_on_timeout := WakeOnTimeout}) -> State#{push_resume_timeout => ResumeTimeout, push_wake_on_timeout => WakeOnTimeout}; c2s_copy_session(State, _) -> State. -spec c2s_handle_cast(c2s_state(), any()) -> c2s_state(). c2s_handle_cast(#{lserver := LServer} = State, push_enable) -> ResumeTimeout = gen_mod:get_module_opt(LServer, ?MODULE, resume_timeout, 86400), WakeOnTimeout = gen_mod:get_module_opt(LServer, ?MODULE, wake_on_timeout, true), State#{push_resume_timeout => ResumeTimeout, push_wake_on_timeout => WakeOnTimeout}; c2s_handle_cast(State, push_disable) -> State1 = maps:remove(push_resume_timeout, State), maps:remove(push_wake_on_timeout, State1); c2s_handle_cast(State, _Msg) -> State. -spec c2s_handle_info(c2s_state(), any()) -> c2s_state() | {stop, c2s_state()}. c2s_handle_info(#{push_enabled := true, mgmt_state := pending, jid := JID} = State, {timeout, _, push_keepalive}) -> ?INFO_MSG("Waking ~s before session times out", [jid:encode(JID)]), mod_push:notify(State), {stop, State}; c2s_handle_info(State, _) -> State. %%-------------------------------------------------------------------- %% Internal functions. %%-------------------------------------------------------------------- -spec maybe_adjust_resume_timeout(c2s_state()) -> c2s_state(). maybe_adjust_resume_timeout(#{push_resume_timeout := undefined} = State) -> State; maybe_adjust_resume_timeout(#{push_resume_timeout := Timeout} = State) -> OrigTimeout = mod_stream_mgmt:get_resume_timeout(State), ?DEBUG("Adjusting resume timeout to ~B seconds", [Timeout]), State1 = mod_stream_mgmt:set_resume_timeout(State, Timeout), State1#{push_resume_timeout_orig => OrigTimeout}. -spec maybe_restore_resume_timeout(c2s_state()) -> c2s_state(). maybe_restore_resume_timeout(#{push_resume_timeout_orig := Timeout} = State) -> ?DEBUG("Restoring resume timeout to ~B seconds", [Timeout]), State1 = mod_stream_mgmt:set_resume_timeout(State, Timeout), maps:remove(push_resume_timeout_orig, State1); maybe_restore_resume_timeout(State) -> State. -spec maybe_start_wakeup_timer(c2s_state()) -> c2s_state(). maybe_start_wakeup_timer(#{push_wake_on_timeout := true, push_resume_timeout := ResumeTimeout} = State) when is_integer(ResumeTimeout), ResumeTimeout > ?PUSH_BEFORE_TIMEOUT_SECS -> WakeTimeout = ResumeTimeout - ?PUSH_BEFORE_TIMEOUT_SECS, ?DEBUG("Scheduling wake-up timer to fire in ~B seconds", [WakeTimeout]), erlang:start_timer(timer:seconds(WakeTimeout), self(), push_keepalive), State; maybe_start_wakeup_timer(State) -> State. -spec wake_all(binary()) -> ok | error. wake_all(LServer) -> ?INFO_MSG("Waking all push clients on ~s", [LServer]), Mod = gen_mod:db_mod(LServer, mod_push), case Mod:lookup_sessions(LServer) of {ok, Sessions} -> IgnoreResponse = fun(_) -> ok end, lists:foreach(fun({_, PushLJID, Node, XData}) -> mod_push:notify(LServer, PushLJID, Node, XData, IgnoreResponse) end, Sessions); error -> error end. ejabberd-18.01/src/ejabberd_sm.erl0000644000232200023220000010464113225664356017401 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_sm.erl %%% Author : Alexey Shchepin %%% Purpose : Session manager %%% Created : 24 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sm). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. -behaviour(?GEN_SERVER). %% API -export([start_link/0, stop/0, route/1, route/2, process_iq/1, open_session/5, open_session/6, close_session/4, check_in_subscription/6, bounce_offline_message/1, disconnect_removed_user/2, get_user_resources/2, get_user_present_resources/2, set_presence/7, unset_presence/6, close_session_unset_presence/5, set_offline_info/5, get_offline_info/4, dirty_get_sessions_list/0, dirty_get_my_sessions_list/0, get_vh_session_list/1, get_vh_session_number/1, get_vh_by_backend/1, register_iq_handler/5, unregister_iq_handler/2, force_update_presence/1, connected_users/0, connected_users_number/0, user_resources/2, kick_user/2, get_session_pid/3, get_session_sid/3, get_session_sids/2, get_user_info/2, get_user_info/3, get_user_ip/3, get_max_user_sessions/2, get_all_pids/0, is_existing_resource/3, get_commands_spec/0, c2s_handle_info/2, host_up/1, host_down/1, make_sid/0, clean_cache/1, config_reloaded/0 ]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_commands.hrl"). -include("ejabberd_sm.hrl"). -callback init() -> ok | {error, any()}. -callback set_session(#session{}) -> ok | {error, any()}. -callback delete_session(#session{}) -> ok | {error, any()}. -callback get_sessions() -> [#session{}]. -callback get_sessions(binary()) -> [#session{}]. -callback get_sessions(binary(), binary()) -> {ok, [#session{}]} | {error, any()}. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). -record(state, {}). %% default value for the maximum number of user connections -define(MAX_USER_SESSIONS, infinity). %%==================================================================== %% API %%==================================================================== -export_type([sid/0, info/0]). start_link() -> ?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []). -spec stop() -> ok. stop() -> supervisor:terminate_child(ejabberd_sup, ?MODULE), supervisor:delete_child(ejabberd_sup, ?MODULE), ok. -spec route(jid(), term()) -> ok. %% @doc route arbitrary term to c2s process(es) route(To, Term) -> case catch do_route(To, Term) of {'EXIT', Reason} -> ?ERROR_MSG("route ~p to ~p failed: ~p", [Term, To, Reason]); _ -> ok end. -spec route(stanza()) -> ok. route(Packet) -> #jid{lserver = LServer} = xmpp:get_to(Packet), case ejabberd_hooks:run_fold(sm_receive_packet, LServer, Packet, []) of drop -> ?DEBUG("hook dropped stanza:~n~s", [xmpp:pp(Packet)]); Packet1 -> try do_route(Packet1), ok catch E:R -> ?ERROR_MSG("failed to route packet:~n~s~nReason = ~p", [xmpp:pp(Packet1), {E, {R, erlang:get_stacktrace()}}]) end end. -spec open_session(sid(), binary(), binary(), binary(), prio(), info()) -> ok. open_session(SID, User, Server, Resource, Priority, Info) -> set_session(SID, User, Server, Resource, Priority, Info), check_for_sessions_to_replace(User, Server, Resource), JID = jid:make(User, Server, Resource), ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver, [SID, JID, Info]). -spec open_session(sid(), binary(), binary(), binary(), info()) -> ok. open_session(SID, User, Server, Resource, Info) -> open_session(SID, User, Server, Resource, undefined, Info). -spec close_session(sid(), binary(), binary(), binary()) -> ok. close_session(SID, User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), Sessions = get_sessions(Mod, LUser, LServer, LResource), Info = case lists:keyfind(SID, #session.sid, Sessions) of #session{info = I} = Session -> delete_session(Mod, Session), I; _ -> [] end, JID = jid:make(User, Server, Resource), ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver, [SID, JID, Info]). -spec check_in_subscription(boolean(), binary(), binary(), jid(), subscribe | subscribed | unsubscribe | unsubscribed, binary()) -> boolean() | {stop, false}. check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) -> case ejabberd_auth:user_exists(User, Server) of true -> Acc; false -> {stop, false} end. -spec bounce_offline_message({bounce, message()} | any()) -> any(). bounce_offline_message({bounce, #message{type = T} = Packet} = Acc) when T == chat; T == groupchat; T == normal -> Lang = xmpp:get_lang(Packet), Txt = <<"User session not found">>, Err = xmpp:err_service_unavailable(Txt, Lang), ejabberd_router:route_error(Packet, Err), {stop, Acc}; bounce_offline_message(Acc) -> Acc. -spec disconnect_removed_user(binary(), binary()) -> ok. disconnect_removed_user(User, Server) -> route(jid:make(User, Server), {exit, <<"User removed">>}). get_user_resources(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), Ss = online(get_sessions(Mod, LUser, LServer)), [element(3, S#session.usr) || S <- clean_session_list(Ss)]. -spec get_user_present_resources(binary(), binary()) -> [tuple()]. get_user_present_resources(LUser, LServer) -> Mod = get_sm_backend(LServer), Ss = online(get_sessions(Mod, LUser, LServer)), [{S#session.priority, element(3, S#session.usr)} || S <- clean_session_list(Ss), is_integer(S#session.priority)]. -spec get_user_ip(binary(), binary(), binary()) -> ip(). get_user_ip(User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), case online(get_sessions(Mod, LUser, LServer, LResource)) of [] -> undefined; Ss -> Session = lists:max(Ss), proplists:get_value(ip, Session#session.info) end. -spec get_user_info(binary(), binary()) -> [{binary(), info()}]. get_user_info(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), Ss = online(get_sessions(Mod, LUser, LServer)), [{LResource, [{node, node(Pid)}|Info]} || #session{usr = {_, _, LResource}, info = Info, sid = {_, Pid}} <- clean_session_list(Ss)]. -spec get_user_info(binary(), binary(), binary()) -> info() | offline. get_user_info(User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), case online(get_sessions(Mod, LUser, LServer, LResource)) of [] -> offline; Ss -> Session = lists:max(Ss), Node = node(element(2, Session#session.sid)), [{node, Node}|Session#session.info] end. -spec set_presence(sid(), binary(), binary(), binary(), prio(), presence(), info()) -> ok. set_presence(SID, User, Server, Resource, Priority, Presence, Info) -> set_session(SID, User, Server, Resource, Priority, Info), ejabberd_hooks:run(set_presence_hook, jid:nameprep(Server), [User, Server, Resource, Presence]). -spec unset_presence(sid(), binary(), binary(), binary(), binary(), info()) -> ok. unset_presence(SID, User, Server, Resource, Status, Info) -> set_session(SID, User, Server, Resource, undefined, Info), ejabberd_hooks:run(unset_presence_hook, jid:nameprep(Server), [User, Server, Resource, Status]). -spec close_session_unset_presence(sid(), binary(), binary(), binary(), binary()) -> ok. close_session_unset_presence(SID, User, Server, Resource, Status) -> close_session(SID, User, Server, Resource), ejabberd_hooks:run(unset_presence_hook, jid:nameprep(Server), [User, Server, Resource, Status]). -spec get_session_pid(binary(), binary(), binary()) -> none | pid(). get_session_pid(User, Server, Resource) -> case get_session_sid(User, Server, Resource) of {_, PID} -> PID; none -> none end. -spec get_session_sid(binary(), binary(), binary()) -> none | sid(). get_session_sid(User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), case online(get_sessions(Mod, LUser, LServer, LResource)) of [] -> none; Ss -> #session{sid = SID} = lists:max(Ss), SID end. -spec get_session_sids(binary(), binary()) -> [sid()]. get_session_sids(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), Sessions = online(get_sessions(Mod, LUser, LServer)), [SID || #session{sid = SID} <- Sessions]. -spec set_offline_info(sid(), binary(), binary(), binary(), info()) -> ok. set_offline_info(SID, User, Server, Resource, Info) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), set_session(SID, LUser, LServer, LResource, undefined, [offline | Info]). -spec get_offline_info(erlang:timestamp(), binary(), binary(), binary()) -> none | info(). get_offline_info(Time, User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), case get_sessions(Mod, LUser, LServer, LResource) of [#session{sid = {Time, _}, info = Info}] -> case proplists:get_bool(offline, Info) of true -> Info; false -> none end; _ -> none end. -spec dirty_get_sessions_list() -> [ljid()]. dirty_get_sessions_list() -> lists:flatmap( fun(Mod) -> [S#session.usr || S <- online(get_sessions(Mod))] end, get_sm_backends()). -spec dirty_get_my_sessions_list() -> [#session{}]. dirty_get_my_sessions_list() -> lists:flatmap( fun(Mod) -> [S || S <- online(get_sessions(Mod)), node(element(2, S#session.sid)) == node()] end, get_sm_backends()). -spec get_vh_session_list(binary()) -> [ljid()]. get_vh_session_list(Server) -> LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), [S#session.usr || S <- online(get_sessions(Mod, LServer))]. -spec get_all_pids() -> [pid()]. get_all_pids() -> lists:flatmap( fun(Mod) -> [element(2, S#session.sid) || S <- online(get_sessions(Mod))] end, get_sm_backends()). -spec get_vh_session_number(binary()) -> non_neg_integer(). get_vh_session_number(Server) -> LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), length(online(get_sessions(Mod, LServer))). -spec register_iq_handler(binary(), binary(), atom(), atom(), list()) -> ok. register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> ?GEN_SERVER:cast(?MODULE, {register_iq_handler, Host, XMLNS, Module, Fun, Opts}). -spec unregister_iq_handler(binary(), binary()) -> ok. unregister_iq_handler(Host, XMLNS) -> ?GEN_SERVER:cast(?MODULE, {unregister_iq_handler, Host, XMLNS}). %% Why the hell do we have so many similar kicks? c2s_handle_info(#{lang := Lang} = State, replaced) -> State1 = State#{replaced => true}, Err = xmpp:serr_conflict(<<"Replaced by new connection">>, Lang), {stop, ejabberd_c2s:send(State1, Err)}; c2s_handle_info(#{lang := Lang} = State, kick) -> Err = xmpp:serr_policy_violation(<<"has been kicked">>, Lang), c2s_handle_info(State, {kick, kicked_by_admin, Err}); c2s_handle_info(State, {kick, _Reason, Err}) -> {stop, ejabberd_c2s:send(State, Err)}; c2s_handle_info(#{lang := Lang} = State, {exit, Reason}) -> Err = xmpp:serr_conflict(Reason, Lang), {stop, ejabberd_c2s:send(State, Err)}; c2s_handle_info(State, _) -> State. -spec config_reloaded() -> ok. config_reloaded() -> init_cache(). %%==================================================================== %% gen_server callbacks %%==================================================================== init([]) -> process_flag(trap_exit, true), init_cache(), lists:foreach(fun(Mod) -> Mod:init() end, get_sm_backends()), clean_cache(), ets:new(sm_iqtable, [named_table, public, {read_concurrency, true}]), ejabberd_hooks:add(host_up, ?MODULE, host_up, 50), ejabberd_hooks:add(host_down, ?MODULE, host_down, 60), ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), lists:foreach(fun host_up/1, ?MYHOSTS), ejabberd_commands:register_commands(get_commands_spec()), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) -> ets:insert(sm_iqtable, {{Host, XMLNS}, Module, Function, Opts}), {noreply, State}; handle_cast({unregister_iq_handler, Host, XMLNS}, State) -> case ets:lookup(sm_iqtable, {Host, XMLNS}) of [{_, Module, Function, Opts}] -> gen_iq_handler:stop_iq_handler(Module, Function, Opts); _ -> ok end, ets:delete(sm_iqtable, {Host, XMLNS}), {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. handle_info({route, Packet}, State) -> route(Packet), {noreply, State}; handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> lists:foreach(fun host_down/1, ?MYHOSTS), ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50), ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60), ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50), ejabberd_commands:unregister_commands(get_commands_spec()), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -spec host_up(binary()) -> ok. host_up(Host) -> ejabberd_hooks:add(c2s_handle_info, Host, ejabberd_sm, c2s_handle_info, 50), ejabberd_hooks:add(roster_in_subscription, Host, ejabberd_sm, check_in_subscription, 20), ejabberd_hooks:add(offline_message_hook, Host, ejabberd_sm, bounce_offline_message, 100), ejabberd_hooks:add(remove_user, Host, ejabberd_sm, disconnect_removed_user, 100), ejabberd_c2s:host_up(Host). -spec host_down(binary()) -> ok. host_down(Host) -> Mod = get_sm_backend(Host), Err = case ejabberd_cluster:get_nodes() of [Node] when Node == node() -> xmpp:serr_system_shutdown(); _ -> xmpp:serr_reset() end, lists:foreach( fun(#session{sid = {_, Pid}}) when node(Pid) == node() -> ejabberd_c2s:send(Pid, Err), ejabberd_c2s:stop(Pid); (_) -> ok end, get_sessions(Mod, Host)), ejabberd_hooks:delete(c2s_handle_info, Host, ejabberd_sm, c2s_handle_info, 50), ejabberd_hooks:delete(roster_in_subscription, Host, ejabberd_sm, check_in_subscription, 20), ejabberd_hooks:delete(offline_message_hook, Host, ejabberd_sm, bounce_offline_message, 100), ejabberd_hooks:delete(remove_user, Host, ejabberd_sm, disconnect_removed_user, 100), ejabberd_c2s:host_down(Host). -spec set_session(sid(), binary(), binary(), binary(), prio(), info()) -> ok | {error, any()}. set_session(SID, User, Server, Resource, Priority, Info) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), US = {LUser, LServer}, USR = {LUser, LServer, LResource}, Mod = get_sm_backend(LServer), case Mod:set_session(#session{sid = SID, usr = USR, us = US, priority = Priority, info = Info}) of ok -> case use_cache(Mod, LServer) of true -> ets_cache:delete(?SM_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)); false -> ok end; {error, _} = Err -> Err end. -spec get_sessions(module()) -> [#session{}]. get_sessions(Mod) -> Mod:get_sessions(). -spec get_sessions(module(), binary()) -> [#session{}]. get_sessions(Mod, LServer) -> Mod:get_sessions(LServer). -spec get_sessions(module(), binary(), binary()) -> [#session{}]. get_sessions(Mod, LUser, LServer) -> case use_cache(Mod, LServer) of true -> case ets_cache:lookup( ?SM_CACHE, {LUser, LServer}, fun() -> case Mod:get_sessions(LUser, LServer) of {ok, Ss} when Ss /= [] -> {ok, Ss}; _ -> error end end) of {ok, Sessions} -> Sessions; error -> [] end; false -> case Mod:get_sessions(LUser, LServer) of {ok, Ss} -> Ss; _ -> [] end end. -spec get_sessions(module(), binary(), binary(), binary()) -> [#session{}]. get_sessions(Mod, LUser, LServer, LResource) -> Sessions = get_sessions(Mod, LUser, LServer), [S || S <- Sessions, element(3, S#session.usr) == LResource]. -spec delete_session(module(), #session{}) -> ok. delete_session(Mod, #session{usr = {LUser, LServer, _}} = Session) -> Mod:delete_session(Session), case use_cache(Mod, LServer) of true -> ets_cache:delete(?SM_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)); false -> ok end. -spec online([#session{}]) -> [#session{}]. online(Sessions) -> lists:filter(fun is_online/1, Sessions). -spec is_online(#session{}) -> boolean(). is_online(#session{info = Info}) -> not proplists:get_bool(offline, Info). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec do_route(jid(), term()) -> any(). do_route(#jid{lresource = <<"">>} = To, Term) -> lists:foreach( fun(R) -> do_route(jid:replace_resource(To, R), Term) end, get_user_resources(To#jid.user, To#jid.server)); do_route(To, Term) -> ?DEBUG("broadcasting ~p to ~s", [Term, jid:encode(To)]), {U, S, R} = jid:tolower(To), Mod = get_sm_backend(S), case online(get_sessions(Mod, U, S, R)) of [] -> ?DEBUG("dropping broadcast to unavailable resourse: ~p", [Term]); Ss -> Session = lists:max(Ss), Pid = element(2, Session#session.sid), ?DEBUG("sending to process ~p: ~p", [Pid, Term]), ejabberd_c2s:route(Pid, Term) end. -spec do_route(stanza()) -> any(). do_route(#presence{from = From, to = To, type = T, status = Status} = Packet) when T == subscribe; T == subscribed; T == unsubscribe; T == unsubscribed -> ?DEBUG("processing subscription:~n~s", [xmpp:pp(Packet)]), #jid{user = User, server = Server, luser = LUser, lserver = LServer} = To, Reason = if T == subscribe -> xmpp:get_text(Status); true -> <<"">> end, case is_privacy_allow(Packet) andalso ejabberd_hooks:run_fold( roster_in_subscription, LServer, false, [User, Server, From, T, Reason]) of true -> Mod = get_sm_backend(LServer), lists:foreach( fun(#session{sid = SID, usr = {_, _, R}, priority = Prio}) when is_integer(Prio) -> Pid = element(2, SID), Packet1 = Packet#presence{to = jid:replace_resource(To, R)}, ?DEBUG("sending to process ~p:~n~s", [Pid, xmpp:pp(Packet1)]), ejabberd_c2s:route(Pid, {route, Packet1}); (_) -> ok end, online(get_sessions(Mod, LUser, LServer))); false -> ok end; do_route(#presence{to = #jid{lresource = <<"">>} = To} = Packet) -> ?DEBUG("processing presence to bare JID:~n~s", [xmpp:pp(Packet)]), {LUser, LServer, _} = jid:tolower(To), lists:foreach( fun({_, R}) -> do_route(Packet#presence{to = jid:replace_resource(To, R)}) end, get_user_present_resources(LUser, LServer)); do_route(#message{to = #jid{lresource = <<"">>}, type = T} = Packet) -> ?DEBUG("processing message to bare JID:~n~s", [xmpp:pp(Packet)]), if T == chat; T == headline; T == normal -> route_message(Packet); true -> Lang = xmpp:get_lang(Packet), ErrTxt = <<"User session not found">>, Err = xmpp:err_service_unavailable(ErrTxt, Lang), ejabberd_router:route_error(Packet, Err) end; do_route(#iq{to = #jid{lresource = <<"">>}} = Packet) -> ?DEBUG("processing IQ to bare JID:~n~s", [xmpp:pp(Packet)]), process_iq(Packet); do_route(Packet) -> ?DEBUG("processing packet to full JID:~n~s", [xmpp:pp(Packet)]), To = xmpp:get_to(Packet), {LUser, LServer, LResource} = jid:tolower(To), Mod = get_sm_backend(LServer), case online(get_sessions(Mod, LUser, LServer, LResource)) of [] -> case Packet of #message{type = T} when T == chat; T == normal -> route_message(Packet); #message{type = T} when T == headline -> ?DEBUG("dropping headline to unavailable resource:~n~s", [xmpp:pp(Packet)]); #presence{} -> ?DEBUG("dropping presence to unavailable resource:~n~s", [xmpp:pp(Packet)]); _ -> Lang = xmpp:get_lang(Packet), ErrTxt = <<"User session not found">>, Err = xmpp:err_service_unavailable(ErrTxt, Lang), ejabberd_router:route_error(Packet, Err) end; Ss -> Session = lists:max(Ss), Pid = element(2, Session#session.sid), ?DEBUG("sending to process ~p:~n~s", [Pid, xmpp:pp(Packet)]), ejabberd_c2s:route(Pid, {route, Packet}) end. %% The default list applies to the user as a whole, %% and is processed if there is no active list set %% for the target session/resource to which a stanza is addressed, %% or if there are no current sessions for the user. -spec is_privacy_allow(stanza()) -> boolean(). is_privacy_allow(Packet) -> To = xmpp:get_to(Packet), LServer = To#jid.server, allow == ejabberd_hooks:run_fold( privacy_check_packet, LServer, allow, [To, Packet, in]). -spec route_message(message()) -> any(). route_message(#message{to = To, type = Type} = Packet) -> LUser = To#jid.luser, LServer = To#jid.lserver, PrioRes = get_user_present_resources(LUser, LServer), case catch lists:max(PrioRes) of {MaxPrio, MaxRes} when is_integer(MaxPrio), MaxPrio >= 0 -> lists:foreach(fun ({P, R}) when P == MaxPrio; (P >= 0) and (Type == headline) -> LResource = jid:resourceprep(R), Mod = get_sm_backend(LServer), case online(get_sessions(Mod, LUser, LServer, LResource)) of [] -> ok; % Race condition Ss -> Session = lists:max(Ss), Pid = element(2, Session#session.sid), ?DEBUG("sending to process ~p~n", [Pid]), LMaxRes = jid:resourceprep(MaxRes), Packet1 = maybe_mark_as_copy(Packet, LResource, LMaxRes, P, MaxPrio), ejabberd_c2s:route(Pid, {route, Packet1}) end; %% Ignore other priority: ({_Prio, _Res}) -> ok end, PrioRes); _ -> case ejabberd_auth:user_exists(LUser, LServer) andalso is_privacy_allow(Packet) of true -> ejabberd_hooks:run_fold(offline_message_hook, LServer, {bounce, Packet}, []); false -> Err = xmpp:err_service_unavailable(), ejabberd_router:route_error(Packet, Err) end end. -spec maybe_mark_as_copy(message(), binary(), binary(), integer(), integer()) -> message(). maybe_mark_as_copy(Packet, R, R, P, P) -> Packet; maybe_mark_as_copy(Packet, _, _, P, P) -> xmpp:put_meta(Packet, sm_copy, true); maybe_mark_as_copy(Packet, _, _, _, _) -> Packet. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec clean_session_list([#session{}]) -> [#session{}]. clean_session_list(Ss) -> clean_session_list(lists:keysort(#session.usr, Ss), []). -spec clean_session_list([#session{}], [#session{}]) -> [#session{}]. clean_session_list([], Res) -> Res; clean_session_list([S], Res) -> [S | Res]; clean_session_list([S1, S2 | Rest], Res) -> if S1#session.usr == S2#session.usr -> if S1#session.sid > S2#session.sid -> clean_session_list([S1 | Rest], Res); true -> clean_session_list([S2 | Rest], Res) end; true -> clean_session_list([S2 | Rest], [S1 | Res]) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% On new session, check if some existing connections need to be replace -spec check_for_sessions_to_replace(binary(), binary(), binary()) -> ok | replaced. check_for_sessions_to_replace(User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), check_existing_resources(LUser, LServer, LResource), check_max_sessions(LUser, LServer). -spec check_existing_resources(binary(), binary(), binary()) -> ok. check_existing_resources(LUser, LServer, LResource) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer, LResource), {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss), lists:foreach(fun(S) -> delete_session(Mod, S) end, OfflineSs), if OnlineSs == [] -> ok; true -> SIDs = [SID || #session{sid = SID} <- OnlineSs], MaxSID = lists:max(SIDs), lists:foreach(fun ({_, Pid} = S) when S /= MaxSID -> ejabberd_c2s:route(Pid, replaced); (_) -> ok end, SIDs) end. -spec is_existing_resource(binary(), binary(), binary()) -> boolean(). is_existing_resource(LUser, LServer, LResource) -> [] /= get_resource_sessions(LUser, LServer, LResource). -spec get_resource_sessions(binary(), binary(), binary()) -> [sid()]. get_resource_sessions(User, Server, Resource) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), [S#session.sid || S <- online(get_sessions(Mod, LUser, LServer, LResource))]. -spec check_max_sessions(binary(), binary()) -> ok | replaced. check_max_sessions(LUser, LServer) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer), {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss), MaxSessions = get_max_user_sessions(LUser, LServer), if length(OnlineSs) =< MaxSessions -> ok; true -> #session{sid = {_, Pid}} = lists:min(OnlineSs), ejabberd_c2s:route(Pid, replaced) end, if length(OfflineSs) =< MaxSessions -> ok; true -> delete_session(Mod, lists:min(OfflineSs)) end. %% Get the user_max_session setting %% This option defines the max number of time a given users are allowed to %% log in %% Defaults to infinity -spec get_max_user_sessions(binary(), binary()) -> infinity | non_neg_integer(). get_max_user_sessions(LUser, Host) -> case acl:match_rule(Host, max_user_sessions, jid:make(LUser, Host)) of Max when is_integer(Max) -> Max; infinity -> infinity; _ -> ?MAX_USER_SESSIONS end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec process_iq(iq()) -> any(). process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet) when T == get; T == set -> XMLNS = xmpp:get_ns(El), Host = To#jid.lserver, case ets:lookup(sm_iqtable, {Host, XMLNS}) of [{_, Module, Function, Opts}] -> gen_iq_handler:handle(Host, Module, Function, Opts, Packet); [] -> Txt = <<"No module is handling this query">>, Err = xmpp:err_service_unavailable(Txt, Lang), ejabberd_router:route_error(Packet, Err) end; process_iq(#iq{type = T, lang = Lang, sub_els = SubEls} = Packet) when T == get; T == set -> Txt = case SubEls of [] -> <<"No child elements found">>; _ -> <<"Too many child elements">> end, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err); process_iq(#iq{}) -> ok. -spec force_update_presence({binary(), binary()}) -> ok. force_update_presence({LUser, LServer}) -> Mod = get_sm_backend(LServer), Ss = online(get_sessions(Mod, LUser, LServer)), lists:foreach(fun (#session{sid = {_, Pid}}) -> ejabberd_c2s:resend_presence(Pid) end, Ss). -spec get_sm_backend(binary()) -> module(). get_sm_backend(Host) -> DBType = ejabberd_config:get_option( {sm_db_type, Host}, ejabberd_config:default_ram_db(Host, ?MODULE)), list_to_atom("ejabberd_sm_" ++ atom_to_list(DBType)). -spec get_sm_backends() -> [module()]. get_sm_backends() -> lists:usort([get_sm_backend(Host) || Host <- ?MYHOSTS]). -spec get_vh_by_backend(module()) -> [binary()]. get_vh_by_backend(Mod) -> lists:filter( fun(Host) -> get_sm_backend(Host) == Mod end, ?MYHOSTS). %%-------------------------------------------------------------------- %%% Cache stuff %%-------------------------------------------------------------------- -spec init_cache() -> ok. init_cache() -> case use_cache() of true -> ets_cache:new(?SM_CACHE, cache_opts()); false -> ets_cache:delete(?SM_CACHE) end. -spec cache_opts() -> [proplists:property()]. cache_opts() -> MaxSize = ejabberd_config:get_option( sm_cache_size, ejabberd_config:cache_size(global)), CacheMissed = ejabberd_config:get_option( sm_cache_missed, ejabberd_config:cache_missed(global)), LifeTime = case ejabberd_config:get_option( sm_cache_life_time, ejabberd_config:cache_life_time(global)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec clean_cache(node()) -> ok. clean_cache(Node) -> ets_cache:filter( ?SM_CACHE, fun(_, error) -> false; (_, {ok, Ss}) -> not lists:any( fun(#session{sid = {_, Pid}}) -> node(Pid) == Node end, Ss) end). -spec clean_cache() -> ok. clean_cache() -> ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]). -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, LServer) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(LServer); false -> ejabberd_config:get_option( {sm_use_cache, LServer}, ejabberd_config:use_cache(LServer)) end. -spec use_cache() -> boolean(). use_cache() -> lists:any( fun(Host) -> Mod = get_sm_backend(Host), use_cache(Mod, Host) end, ?MYHOSTS). -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, LServer) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(LServer); false -> ejabberd_cluster:get_nodes() end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% ejabberd commands get_commands_spec() -> [#ejabberd_commands{name = connected_users, tags = [session], desc = "List all established sessions", policy = admin, module = ?MODULE, function = connected_users, args = [], result_desc = "List of users sessions", result_example = [<<"user1@example.com">>, <<"user2@example.com">>], result = {connected_users, {list, {sessions, string}}}}, #ejabberd_commands{name = connected_users_number, tags = [session, stats], desc = "Get the number of established sessions", policy = admin, module = ?MODULE, function = connected_users_number, result_example = 2, args = [], result = {num_sessions, integer}}, #ejabberd_commands{name = user_resources, tags = [session], desc = "List user's connected resources", policy = admin, module = ?MODULE, function = user_resources, args = [{user, binary}, {host, binary}], args_desc = ["User name", "Server name"], args_example = [<<"user1">>, <<"example.com">>], result_example = [<<"tka1">>, <<"Gajim">>, <<"mobile-app">>], result = {resources, {list, {resource, string}}}}, #ejabberd_commands{name = kick_user, tags = [session], desc = "Disconnect user's active sessions", module = ?MODULE, function = kick_user, args = [{user, binary}, {host, binary}], args_desc = ["User name", "Server name"], args_example = [<<"user1">>, <<"example.com">>], result_desc = "Number of resources that were kicked", result_example = 3, result = {num_resources, integer}}]. -spec connected_users() -> [binary()]. connected_users() -> USRs = dirty_get_sessions_list(), SUSRs = lists:sort(USRs), lists:map(fun ({U, S, R}) -> <> end, SUSRs). connected_users_number() -> length(dirty_get_sessions_list()). user_resources(User, Server) -> Resources = get_user_resources(User, Server), lists:sort(Resources). kick_user(User, Server) -> Resources = get_user_resources(User, Server), lists:foreach( fun(Resource) -> PID = get_session_pid(User, Server, Resource), ejabberd_c2s:route(PID, kick) end, Resources), length(Resources). make_sid() -> {p1_time_compat:unique_timestamp(), self()}. -spec opt_type(sm_db_type) -> fun((atom()) -> atom()); (sm_use_cache) -> fun((boolean()) -> boolean()); (sm_cache_missed) -> fun((boolean()) -> boolean()); (sm_cache_size) -> fun((timeout()) -> timeout()); (sm_cache_life_time) -> fun((timeout()) -> timeout()); (atom()) -> [atom()]. opt_type(sm_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; opt_type(O) when O == sm_use_cache; O == sm_cache_missed -> fun(B) when is_boolean(B) -> B end; opt_type(O) when O == sm_cache_size; O == sm_cache_life_time -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; opt_type(_) -> [sm_db_type, sm_use_cache, sm_cache_size, sm_cache_missed, sm_cache_life_time]. ejabberd-18.01/src/xmpp_stream_in.erl0000644000232200023220000012002113225664356020157 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 26 Nov 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(xmpp_stream_in). -define(GEN_SERVER, p1_server). -behaviour(?GEN_SERVER). -protocol({rfc, 6120}). -protocol({xep, 114, '1.6'}). %% API -export([start/3, start_link/3, call/3, cast/2, reply/2, stop/1, send/2, close/1, close/2, send_error/3, establish/1, get_transport/1, change_shaper/2, set_timeout/2, format_error/1]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3]). %%-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). -endif. -include("xmpp.hrl"). -type state() :: map(). -type stop_reason() :: {stream, reset | {in | out, stream_error()}} | {tls, inet:posix() | atom() | binary()} | {socket, inet:posix() | atom()} | internal_failure. -export_type([state/0, stop_reason/0]). -callback init(list()) -> {ok, state()} | {error, term()} | ignore. -callback handle_cast(term(), state()) -> state(). -callback handle_call(term(), term(), state()) -> state(). -callback handle_info(term(), state()) -> state(). -callback terminate(term(), state()) -> any(). -callback code_change(term(), state(), term()) -> {ok, state()} | {error, term()}. -callback handle_stream_start(stream_start(), state()) -> state(). -callback handle_stream_established(state()) -> state(). -callback handle_stream_end(stop_reason(), state()) -> state(). -callback handle_cdata(binary(), state()) -> state(). -callback handle_unauthenticated_packet(xmpp_element(), state()) -> state(). -callback handle_authenticated_packet(xmpp_element(), state()) -> state(). -callback handle_unbinded_packet(xmpp_element(), state()) -> state(). -callback handle_auth_success(binary(), binary(), module(), state()) -> state(). -callback handle_auth_failure(binary(), binary(), binary(), state()) -> state(). -callback handle_send(xmpp_element(), ok | {error, inet:posix()}, state()) -> state(). -callback handle_recv(fxml:xmlel(), xmpp_element() | {error, term()}, state()) -> state(). -callback handle_timeout(state()) -> state(). -callback get_password_fun(state()) -> fun(). -callback check_password_fun(state()) -> fun(). -callback check_password_digest_fun(state()) -> fun(). -callback bind(binary(), state()) -> {ok, state()} | {error, stanza_error(), state()}. -callback compress_methods(state()) -> [binary()]. -callback tls_options(state()) -> [proplists:property()]. -callback tls_required(state()) -> boolean(). -callback tls_verify(state()) -> boolean(). -callback tls_enabled(state()) -> boolean(). -callback sasl_mechanisms([cyrsasl:mechanism()], state()) -> [cyrsasl:mechanism()]. -callback unauthenticated_stream_features(state()) -> [xmpp_element()]. -callback authenticated_stream_features(state()) -> [xmpp_element()]. %% All callbacks are optional -optional_callbacks([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, handle_stream_start/2, handle_stream_established/1, handle_stream_end/2, handle_cdata/2, handle_authenticated_packet/2, handle_unauthenticated_packet/2, handle_unbinded_packet/2, handle_auth_success/4, handle_auth_failure/4, handle_send/3, handle_recv/3, handle_timeout/1, get_password_fun/1, check_password_fun/1, check_password_digest_fun/1, bind/2, compress_methods/1, tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1, sasl_mechanisms/2, unauthenticated_stream_features/1, authenticated_stream_features/1]). %%%=================================================================== %%% API %%%=================================================================== start(Mod, Args, Opts) -> ?GEN_SERVER:start(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS). start_link(Mod, Args, Opts) -> ?GEN_SERVER:start_link(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS). call(Ref, Msg, Timeout) -> ?GEN_SERVER:call(Ref, Msg, Timeout). cast(Ref, Msg) -> ?GEN_SERVER:cast(Ref, Msg). reply(Ref, Reply) -> ?GEN_SERVER:reply(Ref, Reply). -spec stop(pid()) -> ok; (state()) -> no_return(). stop(Pid) when is_pid(Pid) -> cast(Pid, stop); stop(#{owner := Owner} = State) when Owner == self() -> terminate(normal, State), exit(normal); stop(_) -> erlang:error(badarg). -spec send(pid(), xmpp_element()) -> ok; (state(), xmpp_element()) -> state(). send(Pid, Pkt) when is_pid(Pid) -> cast(Pid, {send, Pkt}); send(#{owner := Owner} = State, Pkt) when Owner == self() -> send_pkt(State, Pkt); send(_, _) -> erlang:error(badarg). -spec close(pid()) -> ok; (state()) -> state(). close(Pid) when is_pid(Pid) -> close(Pid, closed); close(#{owner := Owner} = State) when Owner == self() -> close_socket(State); close(_) -> erlang:error(badarg). -spec close(pid(), atom()) -> ok. close(Pid, Reason) -> cast(Pid, {close, Reason}). -spec establish(state()) -> state(). establish(State) -> process_stream_established(State). -spec set_timeout(state(), non_neg_integer() | infinity) -> state(). set_timeout(#{owner := Owner} = State, Timeout) when Owner == self() -> case Timeout of infinity -> State#{stream_timeout => infinity}; _ -> Time = p1_time_compat:monotonic_time(milli_seconds), State#{stream_timeout => {Timeout, Time}} end; set_timeout(_, _) -> erlang:error(badarg). get_transport(#{socket := Socket, owner := Owner}) when Owner == self() -> xmpp_socket:get_transport(Socket); get_transport(_) -> erlang:error(badarg). -spec change_shaper(state(), shaper:shaper()) -> state(). change_shaper(#{socket := Socket, owner := Owner} = State, Shaper) when Owner == self() -> Socket1 = xmpp_socket:change_shaper(Socket, Shaper), State#{socket => Socket1}; change_shaper(_, _) -> erlang:error(badarg). -spec format_error(stop_reason()) -> binary(). format_error({socket, Reason}) -> format("Connection failed: ~s", [format_inet_error(Reason)]); format_error({stream, reset}) -> <<"Stream reset by peer">>; format_error({stream, {in, #stream_error{reason = Reason, text = Txt}}}) -> format("Stream closed by peer: ~s", [format_stream_error(Reason, Txt)]); format_error({stream, {out, #stream_error{reason = Reason, text = Txt}}}) -> format("Stream closed by us: ~s", [format_stream_error(Reason, Txt)]); format_error({tls, Reason}) -> format("TLS failed: ~s", [format_tls_error(Reason)]); format_error(internal_failure) -> <<"Internal server error">>; format_error(Err) -> format("Unrecognized error: ~w", [Err]). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([Module, {_SockMod, Socket}, Opts]) -> Encrypted = proplists:get_bool(tls, Opts), SocketMonitor = xmpp_socket:monitor(Socket), case xmpp_socket:peername(Socket) of {ok, IP} -> Time = p1_time_compat:monotonic_time(milli_seconds), State = #{owner => self(), mod => Module, socket => Socket, socket_monitor => SocketMonitor, stream_timeout => {timer:seconds(30), Time}, stream_direction => in, stream_id => new_id(), stream_state => wait_for_stream, stream_header_sent => false, stream_restarted => false, stream_compressed => false, stream_encrypted => Encrypted, stream_version => {1,0}, stream_authenticated => false, xmlns => ?NS_CLIENT, lang => <<"">>, user => <<"">>, server => <<"">>, resource => <<"">>, lserver => <<"">>, ip => IP}, case try Module:init([State, Opts]) catch _:undef -> {ok, State} end of {ok, State1} when not Encrypted -> {_, State2, Timeout} = noreply(State1), {ok, State2, Timeout}; {ok, State1} when Encrypted -> TLSOpts = try Module:tls_options(State1) catch _:undef -> [] end, case xmpp_socket:starttls(Socket, TLSOpts) of {ok, TLSSocket} -> State2 = State1#{socket => TLSSocket}, {_, State3, Timeout} = noreply(State2), {ok, State3, Timeout}; {error, Reason} -> {stop, Reason} end; {error, Reason} -> {stop, Reason}; ignore -> ignore end; {error, _Reason} -> ignore end. handle_cast({send, Pkt}, State) -> noreply(send_pkt(State, Pkt)); handle_cast(stop, State) -> {stop, normal, State}; handle_cast({close, Reason}, State) -> State1 = close_socket(State), noreply( case is_disconnected(State) of true -> State1; false -> process_stream_end({socket, Reason}, State) end); handle_cast(Cast, #{mod := Mod} = State) -> noreply(try Mod:handle_cast(Cast, State) catch _:undef -> State end). handle_call(Call, From, #{mod := Mod} = State) -> noreply(try Mod:handle_call(Call, From, State) catch _:undef -> State end). handle_info({'$gen_event', {xmlstreamstart, Name, Attrs}}, #{stream_state := wait_for_stream, xmlns := XMLNS, lang := MyLang} = State) -> El = #xmlel{name = Name, attrs = Attrs}, noreply( try xmpp:decode(El, XMLNS, []) of #stream_start{} = Pkt -> State1 = send_header(State, Pkt), case is_disconnected(State1) of true -> State1; false -> process_stream(Pkt, State1) end; _ -> State1 = send_header(State), case is_disconnected(State1) of true -> State1; false -> send_pkt(State1, xmpp:serr_invalid_xml()) end catch _:{xmpp_codec, Why} -> State1 = send_header(State), case is_disconnected(State1) of true -> State1; false -> Txt = xmpp:io_format_error(Why), Lang = select_lang(MyLang, xmpp:get_lang(El)), Err = xmpp:serr_invalid_xml(Txt, Lang), send_pkt(State1, Err) end end); handle_info({'$gen_event', {xmlstreamend, _}}, State) -> noreply(process_stream_end({stream, reset}, State)); handle_info({'$gen_event', closed}, State) -> noreply(process_stream_end({socket, closed}, State)); handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) -> State1 = send_header(State), noreply( case is_disconnected(State1) of true -> State1; false -> Err = case Reason of <<"XML stanza is too big">> -> xmpp:serr_policy_violation(Reason, Lang); {_, Txt} -> xmpp:serr_not_well_formed(Txt, Lang) end, send_pkt(State1, Err) end); handle_info({'$gen_event', El}, #{stream_state := wait_for_stream} = State) -> error_logger:error_msg("unexpected event from XML driver: ~p; " "xmlstreamstart was expected", [El]), State1 = send_header(State), noreply( case is_disconnected(State1) of true -> State1; false -> send_pkt(State1, xmpp:serr_invalid_xml()) end); handle_info({'$gen_event', {xmlstreamelement, El}}, #{xmlns := NS, mod := Mod} = State) -> noreply( try xmpp:decode(El, NS, [ignore_els]) of Pkt -> State1 = try Mod:handle_recv(El, Pkt, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> process_element(Pkt, State1) end catch _:{xmpp_codec, Why} -> State1 = try Mod:handle_recv(El, {error, Why}, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> process_invalid_xml(State1, El, Why) end end); handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}}, #{mod := Mod} = State) -> noreply(try Mod:handle_cdata(Data, State) catch _:undef -> State end); handle_info(timeout, #{mod := Mod} = State) -> Disconnected = is_disconnected(State), noreply(try Mod:handle_timeout(State) catch _:undef when not Disconnected -> send_pkt(State, xmpp:serr_connection_timeout()); _:undef -> stop(State) end); handle_info({'DOWN', MRef, _Type, _Object, _Info}, #{socket_monitor := MRef} = State) -> noreply(process_stream_end({socket, closed}, State)); handle_info({tcp, _, Data}, #{socket := Socket} = State) -> noreply( case xmpp_socket:recv(Socket, Data) of {ok, NewSocket} -> State#{socket => NewSocket}; {error, Reason} when is_atom(Reason) -> process_stream_end({socket, Reason}, State); {error, Reason} -> %% TODO: make fast_tls return atoms process_stream_end({tls, Reason}, State) end); handle_info({tcp_closed, _}, State) -> handle_info({'$gen_event', closed}, State); handle_info({tcp_error, _, Reason}, State) -> noreply(process_stream_end({socket, Reason}, State)); handle_info(Info, #{mod := Mod} = State) -> noreply(try Mod:handle_info(Info, State) catch _:undef -> State end). terminate(Reason, #{mod := Mod} = State) -> case get(already_terminated) of true -> State; _ -> put(already_terminated, true), try Mod:terminate(Reason, State) catch _:undef -> ok end, send_trailer(State) end. code_change(OldVsn, #{mod := Mod} = State, Extra) -> Mod:code_change(OldVsn, State, Extra). %%%=================================================================== %%% Internal functions %%%=================================================================== -spec noreply(state()) -> {noreply, state(), non_neg_integer() | infinity}. noreply(#{stream_timeout := infinity} = State) -> {noreply, State, infinity}; noreply(#{stream_timeout := {MSecs, StartTime}} = State) -> CurrentTime = p1_time_compat:monotonic_time(milli_seconds), Timeout = max(0, MSecs - CurrentTime + StartTime), {noreply, State, Timeout}. -spec new_id() -> binary(). new_id() -> randoms:get_string(). -spec is_disconnected(state()) -> boolean(). is_disconnected(#{stream_state := StreamState}) -> StreamState == disconnected. -spec process_invalid_xml(state(), fxml:xmlel(), term()) -> state(). process_invalid_xml(#{lang := MyLang} = State, El, Reason) -> case xmpp:is_stanza(El) of true -> Txt = xmpp:io_format_error(Reason), Lang = select_lang(MyLang, xmpp:get_lang(El)), send_error(State, El, xmpp:err_bad_request(Txt, Lang)); false -> case {xmpp:get_name(El), xmpp:get_ns(El)} of {Tag, ?NS_SASL} when Tag == <<"auth">>; Tag == <<"response">>; Tag == <<"abort">> -> Txt = xmpp:io_format_error(Reason), Err = #sasl_failure{reason = 'malformed-request', text = xmpp:mk_text(Txt, MyLang)}, send_pkt(State, Err); {<<"starttls">>, ?NS_TLS} -> send_pkt(State, #starttls_failure{}); {<<"compress">>, ?NS_COMPRESS} -> Err = #compress_failure{reason = 'setup-failed'}, send_pkt(State, Err); _ -> %% Maybe add something more? State end end. -spec process_stream_end(stop_reason(), state()) -> state(). process_stream_end(_, #{stream_state := disconnected} = State) -> State; process_stream_end(Reason, #{mod := Mod} = State) -> State1 = State#{stream_timeout => infinity, stream_state => disconnected}, try Mod:handle_stream_end(Reason, State1) catch _:undef -> stop(State1) end. -spec process_stream(stream_start(), state()) -> state(). process_stream(#stream_start{xmlns = XML_NS, stream_xmlns = STREAM_NS}, #{xmlns := NS} = State) when XML_NS /= NS; STREAM_NS /= ?NS_STREAM -> send_pkt(State, xmpp:serr_invalid_namespace()); process_stream(#stream_start{version = {N, _}}, State) when N > 1 -> send_pkt(State, xmpp:serr_unsupported_version()); process_stream(#stream_start{lang = Lang}, #{xmlns := ?NS_CLIENT, lang := DefaultLang} = State) when size(Lang) > 35 -> %% As stated in BCP47, 4.4.1: %% Protocols or specifications that specify limited buffer sizes for %% language tags MUST allow for language tags of at least 35 characters. %% Do not store long language tag to avoid possible DoS/flood attacks Txt = <<"Too long value of 'xml:lang' attribute">>, send_pkt(State, xmpp:serr_policy_violation(Txt, DefaultLang)); process_stream(#stream_start{to = undefined, version = Version} = StreamStart, #{lang := Lang, server := Server, xmlns := NS} = State) -> if Version < {1,0} andalso NS /= ?NS_COMPONENT -> %% Work-around for gmail servers To = jid:make(Server), process_stream(StreamStart#stream_start{to = To}, State); true -> Txt = <<"Missing 'to' attribute">>, send_pkt(State, xmpp:serr_improper_addressing(Txt, Lang)) end; process_stream(#stream_start{to = #jid{luser = U, lresource = R}}, #{lang := Lang} = State) when U /= <<"">>; R /= <<"">> -> Txt = <<"Improper 'to' attribute">>, send_pkt(State, xmpp:serr_improper_addressing(Txt, Lang)); process_stream(#stream_start{to = #jid{lserver = RemoteServer}} = StreamStart, #{xmlns := ?NS_COMPONENT, mod := Mod} = State) -> State1 = State#{remote_server => RemoteServer, stream_state => wait_for_handshake}, try Mod:handle_stream_start(StreamStart, State1) catch _:undef -> State1 end; process_stream(#stream_start{to = #jid{server = Server, lserver = LServer}, from = From} = StreamStart, #{stream_authenticated := Authenticated, stream_restarted := StreamWasRestarted, mod := Mod, xmlns := NS, resource := Resource, stream_encrypted := Encrypted} = State) -> State1 = if not StreamWasRestarted -> State#{server => Server, lserver => LServer}; true -> State end, State2 = case From of #jid{lserver = RemoteServer} when NS == ?NS_SERVER -> State1#{remote_server => RemoteServer}; _ -> State1 end, State3 = try Mod:handle_stream_start(StreamStart, State2) catch _:undef -> State2 end, case is_disconnected(State3) of true -> State3; false -> State4 = send_features(State3), case is_disconnected(State4) of true -> State4; false -> TLSRequired = is_starttls_required(State4), if not Authenticated and (TLSRequired and not Encrypted) -> State4#{stream_state => wait_for_starttls}; not Authenticated -> State4#{stream_state => wait_for_sasl_request}; (NS == ?NS_CLIENT) and (Resource == <<"">>) -> State4#{stream_state => wait_for_bind}; true -> process_stream_established(State4) end end end. -spec process_element(xmpp_element(), state()) -> state(). process_element(Pkt, #{stream_state := StateName, lang := Lang} = State) -> case Pkt of #starttls{} when StateName == wait_for_starttls; StateName == wait_for_sasl_request -> process_starttls(State); #starttls{} -> process_starttls_failure(unexpected_starttls_request, State); #sasl_auth{} when StateName == wait_for_starttls -> send_pkt(State, #sasl_failure{reason = 'encryption-required'}); #sasl_auth{} when StateName == wait_for_sasl_request -> process_sasl_request(Pkt, State); #sasl_auth{} when StateName == wait_for_sasl_response -> process_sasl_request(Pkt, maps:remove(sasl_state, State)); #sasl_auth{} -> Txt = <<"SASL negotiation is not allowed in this state">>, send_pkt(State, #sasl_failure{reason = 'not-authorized', text = xmpp:mk_text(Txt, Lang)}); #sasl_response{} when StateName == wait_for_starttls -> send_pkt(State, #sasl_failure{reason = 'encryption-required'}); #sasl_response{} when StateName == wait_for_sasl_response -> process_sasl_response(Pkt, State); #sasl_response{} -> Txt = <<"SASL negotiation is not allowed in this state">>, send_pkt(State, #sasl_failure{reason = 'not-authorized', text = xmpp:mk_text(Txt, Lang)}); #sasl_abort{} when StateName == wait_for_sasl_response -> process_sasl_abort(State); #sasl_abort{} -> send_pkt(State, #sasl_failure{reason = 'aborted'}); #sasl_success{} -> State; #compress{} -> process_compress(Pkt, State); #handshake{} when StateName == wait_for_handshake -> process_handshake(Pkt, State); #handshake{} -> State; #stream_error{} -> process_stream_end({stream, {in, Pkt}}, State); _ when StateName == wait_for_sasl_request; StateName == wait_for_handshake; StateName == wait_for_sasl_response -> process_unauthenticated_packet(Pkt, State); _ when StateName == wait_for_starttls -> Txt = <<"Use of STARTTLS required">>, Err = xmpp:serr_policy_violation(Txt, Lang), send_pkt(State, Err); _ when StateName == wait_for_bind -> process_bind(Pkt, State); _ when StateName == established -> process_authenticated_packet(Pkt, State) end. -spec process_unauthenticated_packet(xmpp_element(), state()) -> state(). process_unauthenticated_packet(Pkt, #{mod := Mod} = State) -> NewPkt = set_lang(Pkt, State), try Mod:handle_unauthenticated_packet(NewPkt, State) catch _:undef -> Err = xmpp:serr_not_authorized(), send(State, Err) end. -spec process_authenticated_packet(xmpp_element(), state()) -> state(). process_authenticated_packet(Pkt, #{mod := Mod} = State) -> Pkt1 = set_lang(Pkt, State), case set_from_to(Pkt1, State) of {ok, Pkt2} -> try Mod:handle_authenticated_packet(Pkt2, State) catch _:undef -> Err = xmpp:err_service_unavailable(), send_error(State, Pkt, Err) end; {error, Err} -> send_pkt(State, Err) end. -spec process_bind(xmpp_element(), state()) -> state(). process_bind(#iq{type = set, sub_els = [_]} = Pkt, #{xmlns := ?NS_CLIENT, mod := Mod, lang := MyLang} = State) -> try xmpp:try_subtag(Pkt, #bind{}) of #bind{resource = R} -> case Mod:bind(R, State) of {ok, #{user := U, server := S, resource := NewR} = State1} when NewR /= <<"">> -> Reply = #bind{jid = jid:make(U, S, NewR)}, State2 = send_pkt(State1, xmpp:make_iq_result(Pkt, Reply)), process_stream_established(State2); {error, #stanza_error{} = Err, State1} -> send_error(State1, Pkt, Err) end; _ -> try Mod:handle_unbinded_packet(Pkt, State) catch _:undef -> Err = xmpp:err_not_authorized(), send_error(State, Pkt, Err) end catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Lang = select_lang(MyLang, xmpp:get_lang(Pkt)), Err = xmpp:err_bad_request(Txt, Lang), send_error(State, Pkt, Err) end; process_bind(Pkt, #{mod := Mod} = State) -> try Mod:handle_unbinded_packet(Pkt, State) catch _:undef -> Err = xmpp:err_not_authorized(), send_error(State, Pkt, Err) end. -spec process_handshake(handshake(), state()) -> state(). process_handshake(#handshake{data = Digest}, #{mod := Mod, stream_id := StreamID, remote_server := RemoteServer} = State) -> GetPW = try Mod:get_password_fun(State) catch _:undef -> fun(_) -> {false, undefined} end end, AuthRes = case GetPW(<<"">>) of {false, _} -> false; {Password, _} -> str:sha(<>) == Digest end, case AuthRes of true -> State1 = try Mod:handle_auth_success( RemoteServer, <<"handshake">>, undefined, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> State2 = send_pkt(State1, #handshake{}), process_stream_established(State2) end; false -> State1 = try Mod:handle_auth_failure( RemoteServer, <<"handshake">>, <<"not authorized">>, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> send_pkt(State1, xmpp:serr_not_authorized()) end end. -spec process_stream_established(state()) -> state(). process_stream_established(#{stream_state := StateName} = State) when StateName == disconnected; StateName == established -> State; process_stream_established(#{mod := Mod} = State) -> State1 = State#{stream_authenticated => true, stream_state => established, stream_timeout => infinity}, try Mod:handle_stream_established(State1) catch _:undef -> State1 end. -spec process_compress(compress(), state()) -> state(). process_compress(#compress{}, #{stream_compressed := Compressed, stream_authenticated := Authenticated} = State) when Compressed or not Authenticated -> send_pkt(State, #compress_failure{reason = 'setup-failed'}); process_compress(#compress{methods = HisMethods}, #{socket := Socket, mod := Mod} = State) -> MyMethods = try Mod:compress_methods(State) catch _:undef -> [] end, CommonMethods = lists_intersection(MyMethods, HisMethods), case lists:member(<<"zlib">>, CommonMethods) of true -> case xmpp_socket:compress(Socket) of {ok, ZlibSocket} -> State1 = send_pkt(State, #compressed{}), case is_disconnected(State1) of true -> State1; false -> State1#{socket => ZlibSocket, stream_id => new_id(), stream_header_sent => false, stream_restarted => true, stream_state => wait_for_stream, stream_compressed => true} end; {error, _} -> Err = #compress_failure{reason = 'setup-failed'}, send_pkt(State, Err) end; false -> send_pkt(State, #compress_failure{reason = 'unsupported-method'}) end. -spec process_starttls(state()) -> state(). process_starttls(#{stream_encrypted := true} = State) -> process_starttls_failure(already_encrypted, State); process_starttls(#{socket := Socket, mod := Mod} = State) -> case is_starttls_available(State) of true -> TLSOpts = try Mod:tls_options(State) catch _:undef -> [] end, case xmpp_socket:starttls(Socket, TLSOpts) of {ok, TLSSocket} -> State1 = send_pkt(State, #starttls_proceed{}), case is_disconnected(State1) of true -> State1; false -> State1#{socket => TLSSocket, stream_id => new_id(), stream_header_sent => false, stream_restarted => true, stream_state => wait_for_stream, stream_encrypted => true} end; {error, Reason} -> process_starttls_failure(Reason, State) end; false -> process_starttls_failure(starttls_unsupported, State) end. -spec process_starttls_failure(term(), state()) -> state(). process_starttls_failure(Why, State) -> State1 = send_pkt(State, #starttls_failure{}), case is_disconnected(State1) of true -> State1; false -> process_stream_end({tls, Why}, State1) end. -spec process_sasl_request(sasl_auth(), state()) -> state(). process_sasl_request(#sasl_auth{mechanism = Mech, text = ClientIn}, #{mod := Mod, lserver := LServer} = State) -> State1 = State#{sasl_mech => Mech}, Mechs = get_sasl_mechanisms(State1), case lists:member(Mech, Mechs) of true when Mech == <<"EXTERNAL">> -> Res = case xmpp_stream_pkix:authenticate(State1, ClientIn) of {ok, Peer} -> {ok, [{auth_module, pkix}, {username, Peer}]}; {error, Reason, Peer} -> {error, Reason, Peer} end, process_sasl_result(Res, State1); true -> GetPW = try Mod:get_password_fun(State1) catch _:undef -> fun(_) -> false end end, CheckPW = try Mod:check_password_fun(State1) catch _:undef -> fun(_, _, _) -> false end end, CheckPWDigest = try Mod:check_password_digest_fun(State1) catch _:undef -> fun(_, _, _, _, _) -> false end end, SASLState = cyrsasl:server_new(<<"jabber">>, LServer, <<"">>, [], GetPW, CheckPW, CheckPWDigest), Res = cyrsasl:server_start(SASLState, Mech, ClientIn), process_sasl_result(Res, State1#{sasl_state => SASLState}); false -> process_sasl_result({error, unsupported_mechanism, <<"">>}, State1) end. -spec process_sasl_response(sasl_response(), state()) -> state(). process_sasl_response(#sasl_response{text = ClientIn}, #{sasl_state := SASLState} = State) -> SASLResult = cyrsasl:server_step(SASLState, ClientIn), process_sasl_result(SASLResult, State). -spec process_sasl_result(cyrsasl:sasl_return(), state()) -> state(). process_sasl_result({ok, Props}, State) -> process_sasl_success(Props, <<"">>, State); process_sasl_result({ok, Props, ServerOut}, State) -> process_sasl_success(Props, ServerOut, State); process_sasl_result({continue, ServerOut, NewSASLState}, State) -> process_sasl_continue(ServerOut, NewSASLState, State); process_sasl_result({error, Reason, User}, State) -> process_sasl_failure(Reason, User, State). -spec process_sasl_success([cyrsasl:sasl_property()], binary(), state()) -> state(). process_sasl_success(Props, ServerOut, #{socket := Socket, mod := Mod, sasl_mech := Mech} = State) -> User = identity(Props), AuthModule = proplists:get_value(auth_module, Props), Socket1 = xmpp_socket:reset_stream(Socket), State0 = State#{socket => Socket1}, State1 = send_pkt(State0, #sasl_success{text = ServerOut}), case is_disconnected(State1) of true -> State1; false -> State2 = try Mod:handle_auth_success(User, Mech, AuthModule, State1) catch _:undef -> State1 end, case is_disconnected(State2) of true -> State2; false -> State3 = maps:remove(sasl_state, maps:remove(sasl_mech, State2)), State3#{stream_id => new_id(), stream_authenticated => true, stream_header_sent => false, stream_restarted => true, stream_state => wait_for_stream, user => User} end end. -spec process_sasl_continue(binary(), cyrsasl:sasl_state(), state()) -> state(). process_sasl_continue(ServerOut, NewSASLState, State) -> State1 = State#{sasl_state => NewSASLState, stream_state => wait_for_sasl_response}, send_pkt(State1, #sasl_challenge{text = ServerOut}). -spec process_sasl_failure(atom(), binary(), state()) -> state(). process_sasl_failure(Err, User, #{mod := Mod, sasl_mech := Mech, lang := Lang} = State) -> {Reason, Text} = format_sasl_error(Mech, Err), State1 = send_pkt(State, #sasl_failure{reason = Reason, text = xmpp:mk_text(Text, Lang)}), case is_disconnected(State1) of true -> State1; false -> State2 = try Mod:handle_auth_failure(User, Mech, Text, State1) catch _:undef -> State1 end, State3 = maps:remove(sasl_state, maps:remove(sasl_mech, State2)), State3#{stream_state => wait_for_sasl_request} end. -spec process_sasl_abort(state()) -> state(). process_sasl_abort(State) -> process_sasl_failure(aborted, <<"">>, State). -spec send_features(state()) -> state(). send_features(#{stream_version := {1,0}, stream_encrypted := Encrypted} = State) -> TLSRequired = is_starttls_required(State), Features = if TLSRequired and not Encrypted -> get_tls_feature(State); true -> get_sasl_feature(State) ++ get_compress_feature(State) ++ get_tls_feature(State) ++ get_bind_feature(State) ++ get_session_feature(State) ++ get_other_features(State) end, send_pkt(State, #stream_features{sub_els = Features}); send_features(State) -> %% clients and servers from stone age State. -spec get_sasl_mechanisms(state()) -> [cyrsasl:mechanism()]. get_sasl_mechanisms(#{stream_encrypted := Encrypted, mod := Mod, xmlns := NS, lserver := LServer} = State) -> Mechs = if NS == ?NS_CLIENT -> cyrsasl:listmech(LServer); true -> [] end, TLSVerify = try Mod:tls_verify(State) catch _:undef -> false end, Mechs1 = if Encrypted andalso (TLSVerify orelse NS == ?NS_SERVER) -> [<<"EXTERNAL">>|Mechs]; true -> Mechs end, try Mod:sasl_mechanisms(Mechs1, State) catch _:undef -> Mechs1 end. -spec get_sasl_feature(state()) -> [sasl_mechanisms()]. get_sasl_feature(#{stream_authenticated := false, stream_encrypted := Encrypted} = State) -> TLSRequired = is_starttls_required(State), if Encrypted or not TLSRequired -> Mechs = get_sasl_mechanisms(State), [#sasl_mechanisms{list = Mechs}]; true -> [] end; get_sasl_feature(_) -> []. -spec get_compress_feature(state()) -> [compression()]. get_compress_feature(#{stream_compressed := false, mod := Mod, stream_authenticated := true} = State) -> try Mod:compress_methods(State) of [] -> []; Ms -> [#compression{methods = Ms}] catch _:undef -> [] end; get_compress_feature(_) -> []. -spec get_tls_feature(state()) -> [starttls()]. get_tls_feature(#{stream_authenticated := false, stream_encrypted := false} = State) -> case is_starttls_available(State) of true -> TLSRequired = is_starttls_required(State), [#starttls{required = TLSRequired}]; false -> [] end; get_tls_feature(_) -> []. -spec get_bind_feature(state()) -> [bind()]. get_bind_feature(#{xmlns := ?NS_CLIENT, stream_authenticated := true, resource := <<"">>}) -> [#bind{}]; get_bind_feature(_) -> []. -spec get_session_feature(state()) -> [xmpp_session()]. get_session_feature(#{xmlns := ?NS_CLIENT, stream_authenticated := true, resource := <<"">>}) -> [#xmpp_session{optional = true}]; get_session_feature(_) -> []. -spec get_other_features(state()) -> [xmpp_element()]. get_other_features(#{stream_authenticated := Auth, mod := Mod} = State) -> try if Auth -> Mod:authenticated_stream_features(State); true -> Mod:unauthenticated_stream_features(State) end catch _:undef -> [] end. -spec is_starttls_available(state()) -> boolean(). is_starttls_available(#{mod := Mod} = State) -> try Mod:tls_enabled(State) catch _:undef -> true end. -spec is_starttls_required(state()) -> boolean(). is_starttls_required(#{mod := Mod} = State) -> try Mod:tls_required(State) catch _:undef -> false end. -spec set_from_to(xmpp_element(), state()) -> {ok, xmpp_element()} | {error, stream_error()}. set_from_to(Pkt, _State) when not ?is_stanza(Pkt) -> {ok, Pkt}; set_from_to(Pkt, #{user := U, server := S, resource := R, lang := Lang, xmlns := ?NS_CLIENT}) -> JID = jid:make(U, S, R), From = case xmpp:get_from(Pkt) of undefined -> JID; F -> F end, if JID#jid.luser == From#jid.luser andalso JID#jid.lserver == From#jid.lserver andalso (JID#jid.lresource == From#jid.lresource orelse From#jid.lresource == <<"">>) -> To = case xmpp:get_to(Pkt) of undefined -> jid:make(U, S); T -> T end, {ok, xmpp:set_from_to(Pkt, JID, To)}; true -> Txt = <<"Improper 'from' attribute">>, {error, xmpp:serr_invalid_from(Txt, Lang)} end; set_from_to(Pkt, #{lang := Lang}) -> From = xmpp:get_from(Pkt), To = xmpp:get_to(Pkt), if From == undefined -> Txt = <<"Missing 'from' attribute">>, {error, xmpp:serr_improper_addressing(Txt, Lang)}; To == undefined -> Txt = <<"Missing 'to' attribute">>, {error, xmpp:serr_improper_addressing(Txt, Lang)}; true -> {ok, Pkt} end. -spec send_header(state()) -> state(). send_header(#{stream_version := Version} = State) -> send_header(State, #stream_start{version = Version}). -spec send_header(state(), stream_start()) -> state(). send_header(#{stream_id := StreamID, stream_version := MyVersion, stream_header_sent := false, lang := MyLang, xmlns := NS} = State, #stream_start{to = HisTo, from = HisFrom, lang = HisLang, version = HisVersion}) -> Lang = select_lang(MyLang, HisLang), NS_DB = if NS == ?NS_SERVER -> ?NS_SERVER_DIALBACK; true -> <<"">> end, Version = case HisVersion of undefined -> undefined; {0,_} -> HisVersion; _ -> MyVersion end, StreamStart = #stream_start{version = Version, lang = Lang, xmlns = NS, stream_xmlns = ?NS_STREAM, db_xmlns = NS_DB, id = StreamID, to = HisFrom, from = HisTo}, State1 = State#{lang => Lang, stream_version => Version, stream_header_sent => true}, case socket_send(State1, StreamStart) of ok -> State1; {error, Why} -> process_stream_end({socket, Why}, State1) end; send_header(State, _) -> State. -spec send_pkt(state(), xmpp_element() | xmlel()) -> state(). send_pkt(#{mod := Mod} = State, Pkt) -> Result = socket_send(State, Pkt), State1 = try Mod:handle_send(Pkt, Result, State) catch _:undef -> State end, case Result of _ when is_record(Pkt, stream_error) -> process_stream_end({stream, {out, Pkt}}, State1); ok -> State1; {error, Why} -> process_stream_end({socket, Why}, State1) end. -spec send_error(state(), xmpp_element() | xmlel(), stanza_error()) -> state(). send_error(State, Pkt, Err) -> case xmpp:is_stanza(Pkt) of true -> case xmpp:get_type(Pkt) of result -> State; error -> State; <<"result">> -> State; <<"error">> -> State; _ -> ErrPkt = xmpp:make_error(Pkt, Err), send_pkt(State, ErrPkt) end; false -> State end. -spec send_trailer(state()) -> state(). send_trailer(State) -> socket_send(State, trailer), close_socket(State). -spec socket_send(state(), xmpp_element() | xmlel() | trailer) -> ok | {error, inet:posix()}. socket_send(#{socket := Sock, stream_state := StateName, xmlns := NS, stream_header_sent := true}, Pkt) -> case Pkt of trailer -> xmpp_socket:send_trailer(Sock); #stream_start{} when StateName /= disconnected -> xmpp_socket:send_header(Sock, xmpp:encode(Pkt)); _ when StateName /= disconnected -> xmpp_socket:send_element(Sock, xmpp:encode(Pkt, NS)); _ -> {error, closed} end; socket_send(_, _) -> {error, closed}. -spec close_socket(state()) -> state(). close_socket(#{socket := Socket} = State) -> xmpp_socket:close(Socket), State#{stream_timeout => infinity, stream_state => disconnected}. -spec select_lang(binary(), binary()) -> binary(). select_lang(Lang, <<"">>) -> Lang; select_lang(_, Lang) -> Lang. -spec set_lang(xmpp_element(), state()) -> xmpp_element(). set_lang(Pkt, #{lang := MyLang, xmlns := ?NS_CLIENT}) when ?is_stanza(Pkt) -> HisLang = xmpp:get_lang(Pkt), Lang = select_lang(MyLang, HisLang), xmpp:set_lang(Pkt, Lang); set_lang(Pkt, _) -> Pkt. -spec format_inet_error(atom()) -> string(). format_inet_error(closed) -> "connection closed"; format_inet_error(Reason) -> case inet:format_error(Reason) of "unknown POSIX error" -> atom_to_list(Reason); Txt -> Txt end. -spec format_stream_error(atom() | 'see-other-host'(), [text()]) -> string(). format_stream_error(Reason, Txt) -> Slogan = case Reason of undefined -> "no reason"; #'see-other-host'{} -> "see-other-host"; _ -> atom_to_list(Reason) end, case xmpp:get_text(Txt) of <<"">> -> Slogan; Data -> binary_to_list(Data) ++ " (" ++ Slogan ++ ")" end. -spec format_sasl_error(cyrsasl:mechanism(), atom()) -> {atom(), binary()}. format_sasl_error(<<"EXTERNAL">>, Err) -> xmpp_stream_pkix:format_error(Err); format_sasl_error(Mech, Err) -> cyrsasl:format_error(Mech, Err). -spec format_tls_error(atom() | binary()) -> list(). format_tls_error(Reason) when is_atom(Reason) -> format_inet_error(Reason); format_tls_error(Reason) -> Reason. -spec format(io:format(), list()) -> binary(). format(Fmt, Args) -> iolist_to_binary(io_lib:format(Fmt, Args)). -spec lists_intersection(list(), list()) -> list(). lists_intersection(L1, L2) -> lists:filter( fun(E) -> lists:member(E, L2) end, L1). -spec identity([cyrsasl:sasl_property()]) -> binary(). identity(Props) -> case proplists:get_value(authzid, Props, <<>>) of <<>> -> proplists:get_value(username, Props, <<>>); AuthzId -> AuthzId end. ejabberd-18.01/src/randoms.erl0000644000232200023220000000403113225664356016577 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : randoms.erl %%% Author : Alexey Shchepin %%% Purpose : Random generation number wrapper %%% Created : 13 Dec 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(randoms). -author('alexey@process-one.net'). -export([get_string/0, uniform/0, uniform/1, uniform/2, bytes/1, round_robin/1]). -define(THRESHOLD, 16#10000000000000000). -ifdef(RAND_UNIFORM). get_string() -> R = rand:uniform(?THRESHOLD), integer_to_binary(R). uniform() -> rand:uniform(). uniform(N) -> rand:uniform(N). uniform(N, M) -> rand:uniform(M-N+1) + N-1. -else. get_string() -> R = crypto:rand_uniform(0, ?THRESHOLD), integer_to_binary(R). uniform() -> crypto:rand_uniform(0, ?THRESHOLD)/?THRESHOLD. uniform(N) -> crypto:rand_uniform(1, N+1). uniform(N, M) -> crypto:rand_uniform(N, M+1). -endif. -ifdef(STRONG_RAND_BYTES). bytes(N) -> crypto:strong_rand_bytes(N). -else. bytes(N) -> crypto:rand_bytes(N). -endif. -spec round_robin(pos_integer()) -> non_neg_integer(). round_robin(N) -> p1_time_compat:unique_integer([monotonic, positive]) rem N. ejabberd-18.01/src/ejabberd_http_ws.erl0000644000232200023220000003616113225664356020453 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_websocket.erl %%% Author : Eric Cestari %%% Purpose : XMPP Websocket support %%% Created : 09-10-2010 by Eric Cestari %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_http_ws). -behaviour(ejabberd_config). -author('ecestari@process-one.net'). -behaviour(p1_fsm). -export([start/1, start_link/1, init/1, handle_event/3, handle_sync_event/4, code_change/4, handle_info/3, terminate/3, send_xml/2, setopts/2, sockname/1, peername/1, controlling_process/2, become_controller/2, monitor/1, reset_stream/1, close/1, change_shaper/2, socket_handoff/6, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -define(PING_INTERVAL, 60). -define(WEBSOCKET_TIMEOUT, 300). -record(state, {socket :: ws_socket(), ping_interval = ?PING_INTERVAL :: non_neg_integer(), ping_timer = make_ref() :: reference(), pong_expected = false :: boolean(), timeout = ?WEBSOCKET_TIMEOUT :: non_neg_integer(), timer = make_ref() :: reference(), input = [] :: list(), waiting_input = false :: false | pid(), last_receiver = self() :: pid(), ws :: {#ws{}, pid()}, rfc_compilant = undefined :: boolean() | undefined}). %-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). -endif. -type ws_socket() :: {http_ws, pid(), {inet:ip_address(), inet:port_number()}}. -export_type([ws_socket/0]). start(WS) -> p1_fsm:start(?MODULE, [WS], ?FSMOPTS). start_link(WS) -> p1_fsm:start_link(?MODULE, [WS], ?FSMOPTS). send_xml({http_ws, FsmRef, _IP}, Packet) -> case catch p1_fsm:sync_send_all_state_event(FsmRef, {send_xml, Packet}, 15000) of {'EXIT', {timeout, _}} -> {error, timeout}; {'EXIT', _} -> {error, einval}; Res -> Res end. setopts({http_ws, FsmRef, _IP}, Opts) -> case lists:member({active, once}, Opts) of true -> p1_fsm:send_all_state_event(FsmRef, {activate, self()}); _ -> ok end. sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}. peername({http_ws, _FsmRef, IP}) -> {ok, IP}. controlling_process(_Socket, _Pid) -> ok. become_controller(FsmRef, C2SPid) -> p1_fsm:send_all_state_event(FsmRef, {activate, C2SPid}). close({http_ws, FsmRef, _IP}) -> catch p1_fsm:sync_send_all_state_event(FsmRef, close). monitor({http_ws, FsmRef, _IP}) -> erlang:monitor(process, FsmRef). reset_stream({http_ws, _FsmRef, _IP} = Socket) -> Socket. change_shaper({http_ws, _FsmRef, _IP}, _Shaper) -> %% TODO??? ok. socket_handoff(LocalPath, Request, Socket, SockMod, Buf, Opts) -> ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod, Buf, Opts, ?MODULE, fun get_human_html_xmlel/0). %%% Internal init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) -> SOpts = lists:filtermap(fun({stream_management, _}) -> true; ({max_ack_queue, _}) -> true; ({ack_timeout, _}) -> true; ({resume_timeout, _}) -> true; ({max_resume_timeout, _}) -> true; ({resend_on_timeout, _}) -> true; (_) -> false end, HOpts), Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts, PingInterval = ejabberd_config:get_option( {websocket_ping_interval, ?MYNAME}, ?PING_INTERVAL) * 1000, WSTimeout = ejabberd_config:get_option( {websocket_timeout, ?MYNAME}, ?WEBSOCKET_TIMEOUT) * 1000, Socket = {http_ws, self(), IP}, ?DEBUG("Client connected through websocket ~p", [Socket]), xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket, [{receiver, self()}|Opts]), Timer = erlang:start_timer(WSTimeout, self(), []), {ok, loop, #state{socket = Socket, timeout = WSTimeout, timer = Timer, ws = WS, ping_interval = PingInterval}}. handle_event({activate, From}, StateName, StateData) -> case StateData#state.input of [] -> {next_state, StateName, StateData#state{waiting_input = From}}; Input -> Receiver = From, lists:foreach(fun(I) when is_binary(I)-> Receiver ! {tcp, StateData#state.socket, I}; (I2) -> Receiver ! {tcp, StateData#state.socket, [I2]} end, Input), {next_state, StateName, StateData#state{input = [], waiting_input = false, last_receiver = Receiver}} end. handle_sync_event({send_xml, Packet}, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant = R} = StateData) -> Packet2 = case {case R of undefined -> true; V -> V end, Packet} of {true, {xmlstreamstart, _, Attrs}} -> Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], {xmlstreamelement, #xmlel{name = <<"open">>, attrs = Attrs2}}; {true, {xmlstreamend, _}} -> {xmlstreamelement, #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}}; {true, {xmlstreamraw, <<"\r\n\r\n">>}} -> % cdata ping skip; {true, {xmlstreamelement, #xmlel{name=Name2} = El2}} -> El3 = case Name2 of <<"stream:", _/binary>> -> fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2); _ -> case fxml:get_tag_attr_s(<<"xmlns">>, El2) of <<"">> -> fxml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2); _ -> El2 end end, {xmlstreamelement , El3}; _ -> Packet end, case Packet2 of {xmlstreamstart, Name, Attrs3} -> B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}), WsPid ! {send, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>}; {xmlstreamend, Name} -> WsPid ! {send, <<"">>}; {xmlstreamelement, El} -> WsPid ! {send, fxml:element_to_binary(El)}; {xmlstreamraw, Bin} -> WsPid ! {send, Bin}; {xmlstreamcdata, Bin2} -> WsPid ! {send, Bin2}; skip -> ok end, SN2 = case Packet2 of {xmlstreamelement, #xmlel{name = <<"close">>}} -> stream_end_sent; _ -> StateName end, {reply, ok, SN2, StateData}; handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant = true} = StateData) when StateName /= stream_end_sent -> Close = #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}, WsPid ! {send, fxml:element_to_binary(Close)}, {stop, normal, StateData}; handle_sync_event(close, _From, _StateName, StateData) -> {stop, normal, StateData}. handle_info(closed, _StateName, StateData) -> {stop, normal, StateData}; handle_info({received, Packet}, StateName, StateDataI) -> {StateData, Parsed} = parse(StateDataI, Packet), SD = case StateData#state.waiting_input of false -> Input = StateData#state.input ++ if is_binary(Parsed) -> [Parsed]; true -> Parsed end, StateData#state{input = Input}; Receiver -> Receiver ! {tcp, StateData#state.socket, Parsed}, setup_timers(StateData#state{waiting_input = false, last_receiver = Receiver}) end, {next_state, StateName, SD}; handle_info(PingPong, StateName, StateData) when PingPong == ping orelse PingPong == pong -> StateData2 = setup_timers(StateData), {next_state, StateName, StateData2#state{pong_expected = false}}; handle_info({timeout, Timer, _}, _StateName, #state{timer = Timer} = StateData) -> ?DEBUG("Closing websocket connection from hitting inactivity timeout", []), {stop, normal, StateData}; handle_info({timeout, Timer, _}, StateName, #state{ping_timer = Timer, ws = {_, WsPid}} = StateData) -> case StateData#state.pong_expected of false -> cancel_timer(StateData#state.ping_timer), PingTimer = erlang:start_timer(StateData#state.ping_interval, self(), []), WsPid ! {ping, <<>>}, {next_state, StateName, StateData#state{ping_timer = PingTimer, pong_expected = true}}; true -> ?DEBUG("Closing websocket connection from missing pongs", []), {stop, normal, StateData} end; handle_info(_, StateName, StateData) -> {next_state, StateName, StateData}. code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}. terminate(_Reason, _StateName, StateData) -> case StateData#state.waiting_input of false -> ok; Receiver -> ?DEBUG("C2S Pid : ~p", [Receiver]), Receiver ! {tcp_closed, StateData#state.socket} end, ok. setup_timers(StateData) -> cancel_timer(StateData#state.timer), Timer = erlang:start_timer(StateData#state.timeout, self(), []), cancel_timer(StateData#state.ping_timer), PingTimer = case StateData#state.ping_interval of 0 -> StateData#state.ping_timer; V -> erlang:start_timer(V, self(), []) end, StateData#state{timer = Timer, ping_timer = PingTimer, pong_expected = false}. cancel_timer(Timer) -> erlang:cancel_timer(Timer), receive {timeout, Timer, _} -> ok after 0 -> ok end. get_human_html_xmlel() -> Heading = <<"ejabberd ", (misc:atom_to_binary(?MODULE))/binary>>, #xmlel{name = <<"html">>, attrs = [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}], children = [#xmlel{name = <<"head">>, attrs = [], children = [#xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, Heading}]}]}, #xmlel{name = <<"body">>, attrs = [], children = [#xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, Heading}]}, #xmlel{name = <<"p">>, attrs = [], children = [{xmlcdata, <<"An implementation of ">>}, #xmlel{name = <<"a">>, attrs = [{<<"href">>, <<"http://tools.ietf.org/html/rfc6455">>}], children = [{xmlcdata, <<"WebSocket protocol">>}]}]}, #xmlel{name = <<"p">>, attrs = [], children = [{xmlcdata, <<"This web page is only informative. To " "use WebSocket connection you need a Jabber/XMPP " "client that supports it.">>}]}]}]}. parse(#state{rfc_compilant = C} = State, Data) -> case C of undefined -> P = fxml_stream:new(self()), P2 = fxml_stream:parse(P, Data), fxml_stream:close(P2), case parsed_items([]) of error -> {State#state{rfc_compilant = true}, <<"parse error">>}; [] -> {State#state{rfc_compilant = true}, <<"parse error">>}; [{xmlstreamstart, <<"open">>, _} | _] -> parse(State#state{rfc_compilant = true}, Data); _ -> parse(State#state{rfc_compilant = false}, Data) end; true -> El = fxml_stream:parse_element(Data), case El of #xmlel{name = <<"open">>, attrs = Attrs} -> Attrs2 = [{<<"xmlns:stream">>, ?NS_STREAM}, {<<"xmlns">>, <<"jabber:client">>} | lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], {State, [{xmlstreamstart, <<"stream:stream">>, Attrs2}]}; #xmlel{name = <<"close">>} -> {State, [{xmlstreamend, <<"stream:stream">>}]}; {error, _} -> {State, <<"parse error">>}; _ -> {State, [El]} end; false -> {State, Data} end. parsed_items(List) -> receive {'$gen_event', El} when element(1, El) == xmlel; element(1, El) == xmlstreamstart; element(1, El) == xmlstreamelement; element(1, El) == xmlstreamend -> parsed_items([El | List]); {'$gen_event', {xmlstreamerror, _}} -> error after 0 -> lists:reverse(List) end. opt_type(websocket_ping_interval) -> fun (I) when is_integer(I), I >= 0 -> I end; opt_type(websocket_timeout) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(_) -> [websocket_ping_interval, websocket_timeout]. ejabberd-18.01/src/mod_shared_roster_ldap.erl0000644000232200023220000005506713225664356021656 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_shared_roster_ldap.erl %%% Author : Realloc %%% Marcin Owsiany %%% Evgeniy Khramtsov %%% Description : LDAP shared roster management %%% Created : 5 Mar 2005 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_shared_roster_ldap). -behaviour(ejabberd_config). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([get_user_roster/2, get_jid_info/4, process_item/2, in_subscription/6, out_subscription/4, mod_opt_type/1, opt_type/1, depends/2, transform_module_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_roster.hrl"). -include("eldap.hrl"). -define(USER_CACHE, shared_roster_ldap_user_cache). -define(GROUP_CACHE, shared_roster_ldap_group_cache). -define(LDAP_SEARCH_TIMEOUT, 5). %% Timeout for LDAP search queries in seconds -define(INVALID_SETTING_MSG, "~s is not properly set! ~s will not function."). -record(state, {host = <<"">> :: binary(), eldap_id = <<"">> :: binary(), servers = [] :: [binary()], backups = [] :: [binary()], port = ?LDAP_PORT :: inet:port_number(), tls_options = [] :: list(), dn = <<"">> :: binary(), base = <<"">> :: binary(), password = <<"">> :: binary(), uid = <<"">> :: binary(), deref_aliases = never :: never | searching | finding | always, group_attr = <<"">> :: binary(), group_desc = <<"">> :: binary(), user_desc = <<"">> :: binary(), user_uid = <<"">> :: binary(), uid_format = <<"">> :: binary(), uid_format_re = <<"">> :: binary(), filter = <<"">> :: binary(), ufilter = <<"">> :: binary(), rfilter = <<"">> :: binary(), gfilter = <<"">> :: binary(), auth_check = true :: boolean()}). -record(group_info, {desc, members}). %%==================================================================== %% API %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(Host, NewOpts, _OldOpts) -> case init_cache(Host, NewOpts) of true -> ets_cache:setopts(?USER_CACHE, cache_opts(Host, NewOpts)), ets_cache:setopts(?GROUP_CACHE, cache_opts(Host, NewOpts)); false -> ok end, Proc = gen_mod:get_module_proc(Host, ?MODULE), gen_server:cast(Proc, {set_state, parse_options(Host, NewOpts)}). depends(_Host, _Opts) -> [{mod_roster, hard}]. %%-------------------------------------------------------------------- %% Hooks %%-------------------------------------------------------------------- -spec get_user_roster([#roster{}], {binary(), binary()}) -> [#roster{}]. get_user_roster(Items, {U, S} = US) -> SRUsers = get_user_to_groups_map(US, true), {NewItems1, SRUsersRest} = lists:mapfoldl(fun (Item, SRUsers1) -> {_, _, {U1, S1, _}} = Item#roster.usj, US1 = {U1, S1}, case dict:find(US1, SRUsers1) of {ok, GroupNames} -> {Item#roster{subscription = both, groups = Item#roster.groups ++ GroupNames, ask = none}, dict:erase(US1, SRUsers1)}; error -> {Item, SRUsers1} end end, SRUsers, Items), SRItems = [#roster{usj = {U, S, {U1, S1, <<"">>}}, us = US, jid = {U1, S1, <<"">>}, name = get_user_name(U1, S1), subscription = both, ask = none, groups = GroupNames} || {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)], SRItems ++ NewItems1. %% This function in use to rewrite the roster entries when moving or renaming %% them in the user contact list. -spec process_item(#roster{}, binary()) -> #roster{}. process_item(RosterItem, _Host) -> USFrom = RosterItem#roster.us, {User, Server, _Resource} = RosterItem#roster.jid, USTo = {User, Server}, Map = get_user_to_groups_map(USFrom, false), case dict:find(USTo, Map) of error -> RosterItem; {ok, []} -> RosterItem; {ok, GroupNames} when RosterItem#roster.subscription == remove -> RosterItem#roster{subscription = both, ask = none, groups = GroupNames}; _ -> RosterItem#roster{subscription = both, ask = none} end. -spec get_jid_info({subscription(), [binary()]}, binary(), binary(), jid()) -> {subscription(), [binary()]}. get_jid_info({Subscription, Groups}, User, Server, JID) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), US = {LUser, LServer}, {U1, S1, _} = jid:tolower(JID), US1 = {U1, S1}, SRUsers = get_user_to_groups_map(US, false), case dict:find(US1, SRUsers) of {ok, GroupNames} -> NewGroups = if Groups == [] -> GroupNames; true -> Groups end, {both, NewGroups}; error -> {Subscription, Groups} end. -spec in_subscription(boolean(), binary(), binary(), jid(), subscribe | subscribed | unsubscribe | unsubscribed, binary()) -> boolean(). in_subscription(Acc, User, Server, JID, Type, _Reason) -> process_subscription(in, User, Server, JID, Type, Acc). -spec out_subscription( binary(), binary(), jid(), subscribed | unsubscribed | subscribe | unsubscribe) -> boolean(). out_subscription(User, Server, JID, Type) -> process_subscription(out, User, Server, JID, Type, false). process_subscription(Direction, User, Server, JID, _Type, Acc) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), US = {LUser, LServer}, {U1, S1, _} = jid:tolower(jid:remove_resource(JID)), US1 = {U1, S1}, DisplayedGroups = get_user_displayed_groups(US), SRUsers = lists:usort(lists:flatmap(fun (Group) -> get_group_users(LServer, Group) end, DisplayedGroups)), case lists:member(US1, SRUsers) of true -> case Direction of in -> {stop, false}; out -> stop end; false -> Acc end. %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), State = parse_options(Host, Opts), init_cache(Host, Opts), ejabberd_hooks:add(roster_get, Host, ?MODULE, get_user_roster, 70), ejabberd_hooks:add(roster_in_subscription, Host, ?MODULE, in_subscription, 30), ejabberd_hooks:add(roster_out_subscription, Host, ?MODULE, out_subscription, 30), ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE, get_jid_info, 70), ejabberd_hooks:add(roster_process_item, Host, ?MODULE, process_item, 50), eldap_pool:start_link(State#state.eldap_id, State#state.servers, State#state.backups, State#state.port, State#state.dn, State#state.password, State#state.tls_options), {ok, State}. handle_call(get_state, _From, State) -> {reply, {ok, State}, State}; handle_call(_Request, _From, State) -> {reply, {error, badarg}, State}. handle_cast({set_state, NewState}, _State) -> {noreply, NewState}; handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, State) -> Host = State#state.host, ejabberd_hooks:delete(roster_get, Host, ?MODULE, get_user_roster, 70), ejabberd_hooks:delete(roster_in_subscription, Host, ?MODULE, in_subscription, 30), ejabberd_hooks:delete(roster_out_subscription, Host, ?MODULE, out_subscription, 30), ejabberd_hooks:delete(roster_get_jid_info, Host, ?MODULE, get_jid_info, 70), ejabberd_hooks:delete(roster_process_item, Host, ?MODULE, process_item, 50). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- get_user_to_groups_map({_, Server} = US, SkipUS) -> DisplayedGroups = get_user_displayed_groups(US), lists:foldl(fun (Group, Dict1) -> GroupName = get_group_name(Server, Group), lists:foldl(fun (Contact, Dict) -> if SkipUS, Contact == US -> Dict; true -> dict:append(Contact, GroupName, Dict) end end, Dict1, get_group_users(Server, Group)) end, dict:new(), DisplayedGroups). eldap_search(State, FilterParseArgs, AttributesList) -> case apply(eldap_filter, parse, FilterParseArgs) of {ok, EldapFilter} -> case eldap_pool:search(State#state.eldap_id, [{base, State#state.base}, {filter, EldapFilter}, {timeout, ?LDAP_SEARCH_TIMEOUT}, {deref_aliases, State#state.deref_aliases}, {attributes, AttributesList}]) of #eldap_search_result{entries = Es} -> %% A result with entries. Return their list. Es; _ -> %% Something else. Pretend we got no results. [] end; _ -> %% Filter parsing failed. Pretend we got no results. [] end. get_user_displayed_groups({User, Host}) -> {ok, State} = eldap_utils:get_state(Host, ?MODULE), GroupAttr = State#state.group_attr, Entries = eldap_search(State, [eldap_filter:do_sub(State#state.rfilter, [{<<"%u">>, User}])], [GroupAttr]), Reply = lists:flatmap(fun (#eldap_entry{attributes = Attrs}) -> case Attrs of [{GroupAttr, ValuesList}] -> ValuesList; _ -> [] end end, Entries), lists:usort(Reply). get_group_users(Host, Group) -> {ok, State} = eldap_utils:get_state(Host, ?MODULE), case ets_cache:lookup(?GROUP_CACHE, {Group, Host}, fun () -> search_group_info(State, Group) end) of {ok, #group_info{members = Members}} when Members /= undefined -> Members; _ -> [] end. get_group_name(Host, Group) -> {ok, State} = eldap_utils:get_state(Host, ?MODULE), case ets_cache:lookup(?GROUP_CACHE, {Group, Host}, fun () -> search_group_info(State, Group) end) of {ok, #group_info{desc = GroupName}} when GroupName /= undefined -> GroupName; _ -> Group end. get_user_name(User, Host) -> {ok, State} = eldap_utils:get_state(Host, ?MODULE), case ets_cache:lookup(?USER_CACHE, {User, Host}, fun () -> search_user_name(State, User) end) of {ok, UserName} -> UserName; error -> User end. search_group_info(State, Group) -> Extractor = case State#state.uid_format_re of <<"">> -> fun (UID) -> catch eldap_utils:get_user_part(UID, State#state.uid_format) end; _ -> fun (UID) -> catch get_user_part_re(UID, State#state.uid_format_re) end end, AuthChecker = case State#state.auth_check of true -> fun ejabberd_auth:user_exists/2; _ -> fun (_U, _S) -> true end end, case eldap_search(State, [eldap_filter:do_sub(State#state.gfilter, [{<<"%g">>, Group}])], [State#state.group_attr, State#state.group_desc, State#state.uid]) of [] -> error; LDAPEntries -> {GroupDesc, MembersLists} = lists:foldl(fun(Entry, Acc) -> extract_members(State, Extractor, AuthChecker, Entry, Acc) end, {Group, []}, LDAPEntries), {ok, #group_info{desc = GroupDesc, members = lists:usort(lists:flatten(MembersLists))}} end. extract_members(State, Extractor, AuthChecker, #eldap_entry{attributes = Attrs}, {DescAcc, JIDsAcc}) -> Host = State#state.host, case {eldap_utils:get_ldap_attr(State#state.group_attr, Attrs), eldap_utils:get_ldap_attr(State#state.group_desc, Attrs), lists:keysearch(State#state.uid, 1, Attrs)} of {ID, Desc, {value, {GroupMemberAttr, Members}}} when ID /= <<"">>, GroupMemberAttr == State#state.uid -> JIDs = lists:foldl(fun({ok, UID}, L) -> PUID = jid:nodeprep(UID), case PUID of error -> L; _ -> case AuthChecker(PUID, Host) of true -> [{PUID, Host} | L]; _ -> L end end; (_, L) -> L end, [], lists:map(Extractor, Members)), {Desc, [JIDs | JIDsAcc]}; _ -> {DescAcc, JIDsAcc} end. search_user_name(State, User) -> case eldap_search(State, [eldap_filter:do_sub(State#state.ufilter, [{<<"%u">>, User}])], [State#state.user_desc, State#state.user_uid]) of [#eldap_entry{attributes = Attrs} | _] -> case {eldap_utils:get_ldap_attr(State#state.user_uid, Attrs), eldap_utils:get_ldap_attr(State#state.user_desc, Attrs)} of {UID, Desc} when UID /= <<"">> -> {ok, Desc}; _ -> error end; [] -> error end. %% Getting User ID part by regex pattern get_user_part_re(String, Pattern) -> case catch re:run(String, Pattern) of {match, Captured} -> {First, Len} = lists:nth(2, Captured), Result = str:sub_string(String, First + 1, First + Len), {ok, Result}; _ -> {error, badmatch} end. parse_options(Host, Opts) -> Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)), Cfg = eldap_utils:get_config(Host, Opts), GroupAttr = gen_mod:get_opt(ldap_groupattr, Opts, <<"cn">>), GroupDesc = gen_mod:get_opt(ldap_groupdesc, Opts, GroupAttr), UserDesc = gen_mod:get_opt(ldap_userdesc, Opts, <<"cn">>), UserUID = gen_mod:get_opt(ldap_useruid, Opts, <<"cn">>), UIDAttr = gen_mod:get_opt(ldap_memberattr, Opts, <<"memberUid">>), UIDAttrFormat = gen_mod:get_opt(ldap_memberattr_format, Opts, <<"%u">>), UIDAttrFormatRe = gen_mod:get_opt(ldap_memberattr_format_re, Opts, <<"">>), AuthCheck = gen_mod:get_opt(ldap_auth_check, Opts, true), ConfigFilter = gen_mod:get_opt({ldap_filter, Host}, Opts, <<"">>), ConfigUserFilter = gen_mod:get_opt({ldap_ufilter, Host}, Opts, <<"">>), ConfigGroupFilter = gen_mod:get_opt({ldap_gfilter, Host}, Opts, <<"">>), RosterFilter = gen_mod:get_opt({ldap_rfilter, Host}, Opts, <<"">>), SubFilter = <<"(&(", UIDAttr/binary, "=", UIDAttrFormat/binary, ")(", GroupAttr/binary, "=%g))">>, UserSubFilter = case ConfigUserFilter of <<"">> -> eldap_filter:do_sub(SubFilter, [{<<"%g">>, <<"*">>}]); UString -> UString end, GroupSubFilter = case ConfigGroupFilter of <<"">> -> eldap_filter:do_sub(SubFilter, [{<<"%u">>, <<"*">>}]); GString -> GString end, Filter = case ConfigFilter of <<"">> -> SubFilter; _ -> <<"(&", SubFilter/binary, ConfigFilter/binary, ")">> end, UserFilter = case ConfigFilter of <<"">> -> UserSubFilter; _ -> <<"(&", UserSubFilter/binary, ConfigFilter/binary, ")">> end, GroupFilter = case ConfigFilter of <<"">> -> GroupSubFilter; _ -> <<"(&", GroupSubFilter/binary, ConfigFilter/binary, ")">> end, #state{host = Host, eldap_id = Eldap_ID, servers = Cfg#eldap_config.servers, backups = Cfg#eldap_config.backups, port = Cfg#eldap_config.port, tls_options = Cfg#eldap_config.tls_options, dn = Cfg#eldap_config.dn, password = Cfg#eldap_config.password, base = Cfg#eldap_config.base, deref_aliases = Cfg#eldap_config.deref_aliases, uid = UIDAttr, group_attr = GroupAttr, group_desc = GroupDesc, user_desc = UserDesc, user_uid = UserUID, uid_format = UIDAttrFormat, uid_format_re = UIDAttrFormatRe, filter = Filter, ufilter = UserFilter, rfilter = RosterFilter, gfilter = GroupFilter, auth_check = AuthCheck}. init_cache(Host, Opts) -> UseCache = use_cache(Host, Opts), case UseCache of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?USER_CACHE, CacheOpts), ets_cache:new(?GROUP_CACHE, CacheOpts); false -> ets_cache:delete(?USER_CACHE), ets_cache:delete(?GROUP_CACHE) end, UseCache. use_cache(Host, Opts) -> gen_mod:get_opt(use_cache, Opts, ejabberd_config:use_cache(Host)). cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt(cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt(cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt(cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. transform_module_options(Opts) -> lists:map( fun({ldap_group_cache_size, I}) -> ?WARNING_MSG("Option 'ldap_group_cache_size' is deprecated, " "use 'cache_size' instead", []), {cache_size, I}; ({ldap_user_cache_size, I}) -> ?WARNING_MSG("Option 'ldap_user_cache_size' is deprecated, " "use 'cache_size' instead", []), {cache_size, I}; ({ldap_group_cache_validity, Secs}) -> ?WARNING_MSG("Option 'ldap_group_cache_validity' is deprecated, " "use 'cache_life_time' instead", []), {cache_life_time, Secs}; ({ldap_user_cache_validity, Secs}) -> ?WARNING_MSG("Option 'ldap_user_cache_validity' is deprecated, " "use 'cache_life_time' instead", []), {cache_life_time, Secs}; (Opt) -> Opt end, Opts). mod_opt_type(deref_aliases) -> fun (never) -> never; (searching) -> searching; (finding) -> finding; (always) -> always end; mod_opt_type(ldap_backups) -> fun (L) -> [iolist_to_binary(H) || H <- L] end; mod_opt_type(ldap_base) -> fun iolist_to_binary/1; mod_opt_type(ldap_deref_aliases) -> fun (never) -> never; (searching) -> searching; (finding) -> finding; (always) -> always end; mod_opt_type(ldap_encrypt) -> fun (tls) -> tls; (starttls) -> starttls; (none) -> none end; mod_opt_type(ldap_password) -> fun iolist_to_binary/1; mod_opt_type(ldap_port) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(ldap_rootdn) -> fun iolist_to_binary/1; mod_opt_type(ldap_servers) -> fun (L) -> [iolist_to_binary(H) || H <- L] end; mod_opt_type(ldap_tls_cacertfile) -> fun misc:try_read_file/1; mod_opt_type(ldap_tls_certfile) -> fun ejabberd_pkix:try_certfile/1; mod_opt_type(ldap_tls_depth) -> fun (I) when is_integer(I), I >= 0 -> I end; mod_opt_type(ldap_tls_verify) -> fun (hard) -> hard; (soft) -> soft; (false) -> false end; mod_opt_type(ldap_auth_check) -> fun (on) -> true; (off) -> false; (false) -> false; (true) -> true end; mod_opt_type(ldap_filter) -> fun eldap_utils:check_filter/1; mod_opt_type(ldap_gfilter) -> fun eldap_utils:check_filter/1; mod_opt_type(O) when O == cache_size; O == cache_life_time -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(ldap_groupattr) -> fun iolist_to_binary/1; mod_opt_type(ldap_groupdesc) -> fun iolist_to_binary/1; mod_opt_type(ldap_memberattr) -> fun iolist_to_binary/1; mod_opt_type(ldap_memberattr_format) -> fun iolist_to_binary/1; mod_opt_type(ldap_memberattr_format_re) -> fun (S) -> Re = iolist_to_binary(S), {ok, MP} = re:compile(Re), MP end; mod_opt_type(ldap_rfilter) -> fun eldap_utils:check_filter/1; mod_opt_type(ldap_ufilter) -> fun eldap_utils:check_filter/1; mod_opt_type(ldap_userdesc) -> fun iolist_to_binary/1; mod_opt_type(ldap_useruid) -> fun iolist_to_binary/1; mod_opt_type(_) -> [ldap_auth_check, ldap_filter, ldap_gfilter, ldap_groupattr, ldap_groupdesc, ldap_memberattr, ldap_memberattr_format, ldap_memberattr_format_re, ldap_rfilter, ldap_ufilter, ldap_userdesc, ldap_useruid, deref_aliases, ldap_backups, ldap_base, ldap_deref_aliases, ldap_encrypt, ldap_password, ldap_port, ldap_rootdn, ldap_servers, ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth, ldap_tls_verify, use_cache, cache_missed, cache_size, cache_life_time]. opt_type(ldap_gfilter) -> fun eldap_utils:check_filter/1; opt_type(ldap_rfilter) -> fun eldap_utils:check_filter/1; opt_type(ldap_ufilter) -> fun eldap_utils:check_filter/1; opt_type(_) -> [ldap_gfilter, ldap_rfilter, ldap_ufilter]. ejabberd-18.01/src/ejabberd_iq.erl0000644000232200023220000001311313225664356017364 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_iq.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 10 Nov 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_iq). -behaviour(gen_server). %% API -export([start_link/0, route/4, dispatch/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("xmpp.hrl"). -include("logger.hrl"). -record(state, {expire = infinity :: timeout()}). -type state() :: #state{}. %%%=================================================================== %%% API %%%=================================================================== start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec route(iq(), atom() | pid(), term(), non_neg_integer()) -> ok. route(#iq{type = T} = IQ, Proc, Ctx, Timeout) when T == set; T == get -> Expire = current_time() + Timeout, Rnd = randoms:get_string(), ID = encode_id(Expire, Rnd), ets:insert(?MODULE, {{Expire, Rnd}, Proc, Ctx}), gen_server:cast(?MODULE, {restart_timer, Expire}), ejabberd_router:route(IQ#iq{id = ID}). -spec dispatch(iq()) -> boolean(). dispatch(#iq{type = T, id = ID} = IQ) when T == error; T == result -> case decode_id(ID) of {ok, Expire, Rnd, Node} -> ejabberd_cluster:send({?MODULE, Node}, {route, IQ, {Expire, Rnd}}); error -> false end; dispatch(_) -> false. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> ets:new(?MODULE, [named_table, ordered_set, public]), {ok, #state{}}. handle_call(Request, From, State) -> {stop, {unexpected_call, Request, From}, State}. handle_cast({restart_timer, Expire}, State) -> State1 = State#state{expire = min(Expire, State#state.expire)}, noreply(State1); handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), noreply(State). handle_info({route, IQ, Key}, State) -> case ets:lookup(?MODULE, Key) of [{_, Proc, Ctx}] -> callback(Proc, IQ, Ctx), ets:delete(?MODULE, Key); [] -> ok end, noreply(State); handle_info(timeout, State) -> Expire = clean(ets:first(?MODULE)), noreply(State#state{expire = Expire}); handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p", [Info]), noreply(State). terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec current_time() -> non_neg_integer(). current_time() -> p1_time_compat:system_time(milli_seconds). -spec clean({non_neg_integer(), binary()} | '$end_of_table') -> non_neg_integer() | infinity. clean({Expire, _} = Key) -> case current_time() of Time when Time >= Expire -> case ets:lookup(?MODULE, Key) of [{_, Proc, Ctx}] -> callback(Proc, timeout, Ctx), ets:delete(?MODULE, Key); [] -> ok end, clean(ets:next(?MODULE, Key)); _ -> Expire end; clean('$end_of_table') -> infinity. -spec noreply(state()) -> {noreply, state()} | {noreply, state(), non_neg_integer()}. noreply(#state{expire = Expire} = State) -> case Expire of infinity -> {noreply, State}; _ -> Timeout = max(0, Expire - current_time()), {noreply, State, Timeout} end. -spec encode_id(non_neg_integer(), binary()) -> binary(). encode_id(Expire, Rnd) -> ExpireBin = integer_to_binary(Expire), Node = atom_to_binary(node(), utf8), CheckSum = calc_checksum(<>), <<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>. -spec decode_id(binary()) -> {ok, non_neg_integer(), binary(), atom()} | error. decode_id(<<"rr-", ID/binary>>) -> try [ExpireBin, Tail] = binary:split(ID, <<"-">>), [Rnd, Rest] = binary:split(Tail, <<"-">>), [CheckSum, NodeBin] = binary:split(Rest, <<"-">>), CheckSum = calc_checksum(<>), Node = erlang:binary_to_existing_atom(NodeBin, utf8), Expire = binary_to_integer(ExpireBin), {ok, Expire, Rnd, Node} catch _:{badmatch, _} -> error end; decode_id(_) -> error. -spec calc_checksum(binary()) -> binary(). calc_checksum(Data) -> Key = ejabberd_config:get_option(shared_key), base64:encode(crypto:hash(sha, <>)). -spec callback(atom() | pid(), #iq{} | timeout, term()) -> any(). callback(undefined, IQRes, Fun) -> Fun(IQRes); callback(Proc, IQRes, Ctx) -> Proc ! {iq_reply, IQRes, Ctx}. ejabberd-18.01/src/cyrsasl_oauth.erl0000644000232200023220000000715613225664356020027 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : cyrsasl_oauth.erl %%% Author : Alexey Shchepin %%% Purpose : X-OAUTH2 SASL mechanism %%% Created : 17 Sep 2015 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(cyrsasl_oauth). -author('alexey@process-one.net'). -export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]). -behaviour(cyrsasl). -record(state, {host}). -type error_reason() :: parser_failed | not_authorized. -export_type([error_reason/0]). start(_Opts) -> cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain). stop() -> ok. -spec format_error(error_reason()) -> {atom(), binary()}. format_error(parser_failed) -> {'bad-protocol', <<"Response decoding failed">>}; format_error(not_authorized) -> {'not-authorized', <<"Invalid token">>}. mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) -> {ok, #state{host = Host}}. mech_step(State, ClientIn) -> case prepare(ClientIn) of [AuthzId, User, Token] -> case ejabberd_oauth:check_token( User, State#state.host, [<<"sasl_auth">>], Token) of true -> {ok, [{username, User}, {authzid, AuthzId}, {auth_module, ejabberd_oauth}]}; _ -> {error, not_authorized, User} end; _ -> {error, parser_failed} end. prepare(ClientIn) -> case parse(ClientIn) of [<<"">>, UserMaybeDomain, Token] -> case parse_domain(UserMaybeDomain) of %% login@domainpwd [User, _Domain] -> [User, User, Token]; %% loginpwd [User] -> [User, User, Token] end; %% login@domainloginpwd [AuthzId, User, Token] -> case parse_domain(AuthzId) of %% login@domainloginpwd [AuthzUser, _Domain] -> [AuthzUser, User, Token]; %% loginloginpwd [AuthzUser] -> [AuthzUser, User, Token] end; _ -> error end. parse(S) -> parse1(binary_to_list(S), "", []). parse1([0 | Cs], S, T) -> parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T); %parse1([], [], T) -> % lists:reverse(T); parse1([], S, T) -> lists:reverse([list_to_binary(lists:reverse(S)) | T]). parse_domain(S) -> parse_domain1(binary_to_list(S), "", []). parse_domain1([$@ | Cs], S, T) -> parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); parse_domain1([C | Cs], S, T) -> parse_domain1(Cs, [C | S], T); parse_domain1([], S, T) -> lists:reverse([list_to_binary(lists:reverse(S)) | T]). ejabberd-18.01/src/ejabberd_sql.erl0000644000232200023220000011254413225664356017562 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_sql.erl %%% Author : Alexey Shchepin %%% Purpose : Serve SQL connection %%% Created : 8 Dec 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sql). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -behaviour(p1_fsm). %% External exports -export([start/1, start_link/2, sql_query/2, sql_query_t/1, sql_transaction/2, sql_bloc/2, abort/1, restart/1, sql_query_to_iolist/1, escape/1, standard_escape/1, escape_like/1, escape_like_arg/1, escape_like_arg_circumflex/1, to_bool/1, sqlite_db/1, sqlite_file/1, encode_term/1, decode_term/1, odbc_config/0, freetds_config/0, odbcinst_config/0, init_mssql/1, keep_alive/2]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, print_state/1, code_change/4]). -export([connecting/2, connecting/3, session_established/2, session_established/3, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -record(state, {db_ref = self() :: pid(), db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql, db_version = undefined :: undefined | non_neg_integer(), start_interval = 0 :: non_neg_integer(), host = <<"">> :: binary(), pending_requests :: p1_queue:queue()}). -define(STATE_KEY, ejabberd_sql_state). -define(NESTING_KEY, ejabberd_sql_nesting_level). -define(TOP_LEVEL_TXN, 0). -define(PGSQL_PORT, 5432). -define(MYSQL_PORT, 3306). -define(MSSQL_PORT, 1433). -define(MAX_TRANSACTION_RESTARTS, 10). -define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]). -define(PREPARE_KEY, ejabberd_sql_prepare). %%-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). -endif. %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host) -> p1_fsm:start(ejabberd_sql, [Host], fsm_limit_opts() ++ (?FSMOPTS)). start_link(Host, StartInterval) -> p1_fsm:start_link(ejabberd_sql, [Host, StartInterval], fsm_limit_opts() ++ (?FSMOPTS)). -type sql_query() :: [sql_query() | binary()] | #sql_query{} | fun(() -> any()) | fun((atom(), _) -> any()). -type sql_query_result() :: {updated, non_neg_integer()} | {error, binary()} | {selected, [binary()], [[binary()]]} | {selected, [any()]}. -spec sql_query(binary(), sql_query()) -> sql_query_result(). sql_query(Host, Query) -> check_error(sql_call(Host, {sql_query, Query}), Query). %% SQL transaction based on a list of queries %% This function automatically -spec sql_transaction(binary(), [sql_query()] | fun(() -> any())) -> {atomic, any()} | {aborted, any()}. sql_transaction(Host, Queries) when is_list(Queries) -> F = fun () -> lists:foreach(fun (Query) -> sql_query_t(Query) end, Queries) end, sql_transaction(Host, F); %% SQL transaction, based on a erlang anonymous function (F = fun) sql_transaction(Host, F) when is_function(F) -> sql_call(Host, {sql_transaction, F}). %% SQL bloc, based on a erlang anonymous function (F = fun) sql_bloc(Host, F) -> sql_call(Host, {sql_bloc, F}). sql_call(Host, Msg) -> case get(?STATE_KEY) of undefined -> case ejabberd_sql_sup:get_random_pid(Host) of none -> {error, <<"Unknown Host">>}; Pid -> sync_send_event(Pid,{sql_cmd, Msg, p1_time_compat:monotonic_time(milli_seconds)}, query_timeout(Host)) end; _State -> nested_op(Msg) end. keep_alive(Host, PID) -> sync_send_event(PID, {sql_cmd, {sql_query, ?KEEPALIVE_QUERY}, p1_time_compat:monotonic_time(milli_seconds)}, query_timeout(Host)). sync_send_event(Pid, Msg, Timeout) -> try p1_fsm:sync_send_event(Pid, Msg, Timeout) catch _:{Reason, {p1_fsm, _, _}} -> {error, Reason} end. -spec sql_query_t(sql_query()) -> sql_query_result(). %% This function is intended to be used from inside an sql_transaction: sql_query_t(Query) -> QRes = sql_query_internal(Query), case QRes of {error, Reason} -> throw({aborted, Reason}); Rs when is_list(Rs) -> case lists:keysearch(error, 1, Rs) of {value, {error, Reason}} -> throw({aborted, Reason}); _ -> QRes end; _ -> QRes end. abort(Reason) -> exit(Reason). restart(Reason) -> throw({aborted, Reason}). -spec escape_char(char()) -> binary(). escape_char($\000) -> <<"\\0">>; escape_char($\n) -> <<"\\n">>; escape_char($\t) -> <<"\\t">>; escape_char($\b) -> <<"\\b">>; escape_char($\r) -> <<"\\r">>; escape_char($') -> <<"''">>; escape_char($") -> <<"\\\"">>; escape_char($\\) -> <<"\\\\">>; escape_char(C) -> <>. -spec escape(binary()) -> binary(). escape(S) -> << <<(escape_char(Char))/binary>> || <> <= S >>. %% Escape character that will confuse an SQL engine %% Percent and underscore only need to be escaped for pattern matching like %% statement escape_like(S) when is_binary(S) -> << <<(escape_like(C))/binary>> || <> <= S >>; escape_like($%) -> <<"\\%">>; escape_like($_) -> <<"\\_">>; escape_like($\\) -> <<"\\\\\\\\">>; escape_like(C) when is_integer(C), C >= 0, C =< 255 -> escape_char(C). escape_like_arg(S) when is_binary(S) -> << <<(escape_like_arg(C))/binary>> || <> <= S >>; escape_like_arg($%) -> <<"\\%">>; escape_like_arg($_) -> <<"\\_">>; escape_like_arg($\\) -> <<"\\\\">>; escape_like_arg(C) when is_integer(C), C >= 0, C =< 255 -> <>. escape_like_arg_circumflex(S) when is_binary(S) -> << <<(escape_like_arg_circumflex(C))/binary>> || <> <= S >>; escape_like_arg_circumflex($%) -> <<"^%">>; escape_like_arg_circumflex($_) -> <<"^_">>; escape_like_arg_circumflex($^) -> <<"^^">>; escape_like_arg_circumflex($[) -> <<"^[">>; % For MSSQL escape_like_arg_circumflex($]) -> <<"^]">>; escape_like_arg_circumflex(C) when is_integer(C), C >= 0, C =< 255 -> <>. to_bool(<<"t">>) -> true; to_bool(<<"true">>) -> true; to_bool(<<"1">>) -> true; to_bool(true) -> true; to_bool(1) -> true; to_bool(_) -> false. encode_term(Term) -> escape(list_to_binary( erl_prettypr:format(erl_syntax:abstract(Term), [{paper, 65535}, {ribbon, 65535}]))). decode_term(Bin) -> Str = binary_to_list(<>), {ok, Tokens, _} = erl_scan:string(Str), {ok, Term} = erl_parse:parse_term(Tokens), Term. -spec sqlite_db(binary()) -> atom(). sqlite_db(Host) -> list_to_atom("ejabberd_sqlite_" ++ binary_to_list(Host)). -spec sqlite_file(binary()) -> string(). sqlite_file(Host) -> case ejabberd_config:get_option({sql_database, Host}) of undefined -> {ok, Cwd} = file:get_cwd(), filename:join([Cwd, "sqlite", atom_to_list(node()), binary_to_list(Host), "ejabberd.db"]); File -> binary_to_list(File) end. %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- init([Host, StartInterval]) -> process_flag(trap_exit, true), case ejabberd_config:get_option({sql_keepalive_interval, Host}) of undefined -> ok; KeepaliveInterval -> timer:apply_interval(KeepaliveInterval * 1000, ?MODULE, keep_alive, [Host, self()]) end, [DBType | _] = db_opts(Host), p1_fsm:send_event(self(), connect), ejabberd_sql_sup:add_pid(Host, self()), QueueType = case ejabberd_config:get_option({sql_queue_type, Host}) of undefined -> ejabberd_config:default_queue_type(Host); Type -> Type end, {ok, connecting, #state{db_type = DBType, host = Host, pending_requests = p1_queue:new(QueueType, max_fsm_queue()), start_interval = StartInterval}}. connecting(connect, #state{host = Host} = State) -> ConnectRes = case db_opts(Host) of [mysql | Args] -> apply(fun mysql_connect/8, Args); [pgsql | Args] -> apply(fun pgsql_connect/8, Args); [sqlite | Args] -> apply(fun sqlite_connect/1, Args); [mssql | Args] -> apply(fun odbc_connect/2, Args); [odbc | Args] -> apply(fun odbc_connect/2, Args) end, case ConnectRes of {ok, Ref} -> erlang:monitor(process, Ref), lists:foreach( fun({{?PREPARE_KEY, _} = Key, _}) -> erase(Key); (_) -> ok end, get()), PendingRequests = p1_queue:dropwhile( fun(Req) -> p1_fsm:send_event(self(), Req), true end, State#state.pending_requests), State1 = State#state{db_ref = Ref, pending_requests = PendingRequests}, State2 = get_db_version(State1), {next_state, session_established, State2}; {error, Reason} -> ?INFO_MSG("~p connection failed:~n** Reason: ~p~n** " "Retry after: ~p seconds", [State#state.db_type, Reason, State#state.start_interval div 1000]), p1_fsm:send_event_after(State#state.start_interval, connect), {next_state, connecting, State} end; connecting(Event, State) -> ?WARNING_MSG("unexpected event in 'connecting': ~p", [Event]), {next_state, connecting, State}. connecting({sql_cmd, {sql_query, ?KEEPALIVE_QUERY}, _Timestamp}, From, State) -> p1_fsm:reply(From, {error, <<"SQL connection failed">>}), {next_state, connecting, State}; connecting({sql_cmd, Command, Timestamp} = Req, From, State) -> ?DEBUG("queuing pending request while connecting:~n\t~p", [Req]), PendingRequests = try p1_queue:in({sql_cmd, Command, From, Timestamp}, State#state.pending_requests) catch error:full -> Q = p1_queue:dropwhile( fun({sql_cmd, _, To, _Timestamp}) -> p1_fsm:reply( To, {error, <<"SQL connection failed">>}), true end, State#state.pending_requests), p1_queue:in({sql_cmd, Command, From, Timestamp}, Q) end, {next_state, connecting, State#state{pending_requests = PendingRequests}}; connecting(Request, {Who, _Ref}, State) -> ?WARNING_MSG("unexpected call ~p from ~p in 'connecting'", [Request, Who]), {reply, {error, badarg}, connecting, State}. session_established({sql_cmd, Command, Timestamp}, From, State) -> run_sql_cmd(Command, From, State, Timestamp); session_established(Request, {Who, _Ref}, State) -> ?WARNING_MSG("unexpected call ~p from ~p in 'session_establ" "ished'", [Request, Who]), {reply, {error, badarg}, session_established, State}. session_established({sql_cmd, Command, From, Timestamp}, State) -> run_sql_cmd(Command, From, State, Timestamp); session_established(Event, State) -> ?WARNING_MSG("unexpected event in 'session_established': ~p", [Event]), {next_state, session_established, State}. handle_event(_Event, StateName, State) -> {next_state, StateName, State}. handle_sync_event(_Event, _From, StateName, State) -> {reply, {error, badarg}, StateName, State}. code_change(_OldVsn, StateName, State, _Extra) -> {ok, StateName, State}. %% We receive the down signal when we loose the MySQL connection (we are %% monitoring the connection) handle_info({'DOWN', _MonitorRef, process, _Pid, _Info}, _StateName, State) -> p1_fsm:send_event(self(), connect), {next_state, connecting, State}; handle_info(Info, StateName, State) -> ?WARNING_MSG("unexpected info in ~p: ~p", [StateName, Info]), {next_state, StateName, State}. terminate(_Reason, _StateName, State) -> ejabberd_sql_sup:remove_pid(State#state.host, self()), case State#state.db_type of mysql -> catch p1_mysql_conn:stop(State#state.db_ref); sqlite -> catch sqlite3:close(sqlite_db(State#state.host)); _ -> ok end, ok. %%---------------------------------------------------------------------- %% Func: print_state/1 %% Purpose: Prepare the state to be printed on error log %% Returns: State to print %%---------------------------------------------------------------------- print_state(State) -> State. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- run_sql_cmd(Command, From, State, Timestamp) -> QueryTimeout = query_timeout(State#state.host), case p1_time_compat:monotonic_time(milli_seconds) - Timestamp of Age when Age < QueryTimeout -> put(?NESTING_KEY, ?TOP_LEVEL_TXN), put(?STATE_KEY, State), abort_on_driver_error(outer_op(Command), From); Age -> ?ERROR_MSG("Database was not available or too slow, " "discarding ~p milliseconds old request~n~p~n", [Age, Command]), {next_state, session_established, State} end. %% Only called by handle_call, only handles top level operations. %% @spec outer_op(Op) -> {error, Reason} | {aborted, Reason} | {atomic, Result} outer_op({sql_query, Query}) -> sql_query_internal(Query); outer_op({sql_transaction, F}) -> outer_transaction(F, ?MAX_TRANSACTION_RESTARTS, <<"">>); outer_op({sql_bloc, F}) -> execute_bloc(F). %% Called via sql_query/transaction/bloc from client code when inside a %% nested operation nested_op({sql_query, Query}) -> sql_query_internal(Query); nested_op({sql_transaction, F}) -> NestingLevel = get(?NESTING_KEY), if NestingLevel =:= (?TOP_LEVEL_TXN) -> outer_transaction(F, ?MAX_TRANSACTION_RESTARTS, <<"">>); true -> inner_transaction(F) end; nested_op({sql_bloc, F}) -> execute_bloc(F). %% Never retry nested transactions - only outer transactions inner_transaction(F) -> PreviousNestingLevel = get(?NESTING_KEY), case get(?NESTING_KEY) of ?TOP_LEVEL_TXN -> {backtrace, T} = process_info(self(), backtrace), ?ERROR_MSG("inner transaction called at outer txn " "level. Trace: ~s", [T]), erlang:exit(implementation_faulty); _N -> ok end, put(?NESTING_KEY, PreviousNestingLevel + 1), Result = (catch F()), put(?NESTING_KEY, PreviousNestingLevel), case Result of {aborted, Reason} -> {aborted, Reason}; {'EXIT', Reason} -> {'EXIT', Reason}; {atomic, Res} -> {atomic, Res}; Res -> {atomic, Res} end. outer_transaction(F, NRestarts, _Reason) -> PreviousNestingLevel = get(?NESTING_KEY), case get(?NESTING_KEY) of ?TOP_LEVEL_TXN -> ok; _N -> {backtrace, T} = process_info(self(), backtrace), ?ERROR_MSG("outer transaction called at inner txn " "level. Trace: ~s", [T]), erlang:exit(implementation_faulty) end, sql_query_internal([<<"begin;">>]), put(?NESTING_KEY, PreviousNestingLevel + 1), Result = (catch F()), put(?NESTING_KEY, PreviousNestingLevel), case Result of {aborted, Reason} when NRestarts > 0 -> sql_query_internal([<<"rollback;">>]), outer_transaction(F, NRestarts - 1, Reason); {aborted, Reason} when NRestarts =:= 0 -> ?ERROR_MSG("SQL transaction restarts exceeded~n** " "Restarts: ~p~n** Last abort reason: " "~p~n** Stacktrace: ~p~n** When State " "== ~p", [?MAX_TRANSACTION_RESTARTS, Reason, erlang:get_stacktrace(), get(?STATE_KEY)]), sql_query_internal([<<"rollback;">>]), {aborted, Reason}; {'EXIT', Reason} -> sql_query_internal([<<"rollback;">>]), {aborted, Reason}; Res -> sql_query_internal([<<"commit;">>]), {atomic, Res} end. execute_bloc(F) -> case catch F() of {aborted, Reason} -> {aborted, Reason}; {'EXIT', Reason} -> {aborted, Reason}; Res -> {atomic, Res} end. execute_fun(F) when is_function(F, 0) -> F(); execute_fun(F) when is_function(F, 2) -> State = get(?STATE_KEY), F(State#state.db_type, State#state.db_version). sql_query_internal([{_, _} | _] = Queries) -> State = get(?STATE_KEY), case select_sql_query(Queries, State) of undefined -> {error, <<"no matching query for the current DBMS found">>}; Query -> sql_query_internal(Query) end; sql_query_internal(#sql_query{} = Query) -> State = get(?STATE_KEY), Res = try case State#state.db_type of odbc -> generic_sql_query(Query); mssql -> mssql_sql_query(Query); pgsql -> Key = {?PREPARE_KEY, Query#sql_query.hash}, case get(Key) of undefined -> case pgsql_prepare(Query, State) of {ok, _, _, _} -> put(Key, prepared); {error, Error} -> ?ERROR_MSG("PREPARE failed for SQL query " "at ~p: ~p", [Query#sql_query.loc, Error]), put(Key, ignore) end; _ -> ok end, case get(Key) of prepared -> pgsql_execute_sql_query(Query, State); _ -> generic_sql_query(Query) end; mysql -> generic_sql_query(Query); sqlite -> sqlite_sql_query(Query) end catch Class:Reason -> ST = erlang:get_stacktrace(), ?ERROR_MSG("Internal error while processing SQL query: ~p", [{Class, Reason, ST}]), {error, <<"internal error">>} end, case Res of {error, <<"No SQL-driver information available.">>} -> {updated, 0}; _Else -> Res end; sql_query_internal(F) when is_function(F) -> case catch execute_fun(F) of {'EXIT', Reason} -> {error, Reason}; Res -> Res end; sql_query_internal(Query) -> State = get(?STATE_KEY), ?DEBUG("SQL: \"~s\"", [Query]), QueryTimeout = query_timeout(State#state.host), Res = case State#state.db_type of odbc -> to_odbc(odbc:sql_query(State#state.db_ref, [Query], QueryTimeout - 1000)); mssql -> to_odbc(odbc:sql_query(State#state.db_ref, [Query], QueryTimeout - 1000)); pgsql -> pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query, QueryTimeout - 1000)); mysql -> R = mysql_to_odbc(p1_mysql_conn:squery(State#state.db_ref, [Query], self(), [{timeout, QueryTimeout - 1000}, {result_type, binary}])), %% ?INFO_MSG("MySQL, Received result~n~p~n", [R]), R; sqlite -> Host = State#state.host, sqlite_to_odbc(Host, sqlite3:sql_exec(sqlite_db(Host), Query)) end, case Res of {error, <<"No SQL-driver information available.">>} -> {updated, 0}; _Else -> Res end. select_sql_query(Queries, State) -> select_sql_query( Queries, State#state.db_type, State#state.db_version, undefined). select_sql_query([], _Type, _Version, undefined) -> undefined; select_sql_query([], _Type, _Version, Query) -> Query; select_sql_query([{any, Query} | _], _Type, _Version, _) -> Query; select_sql_query([{Type, Query} | _], Type, _Version, _) -> Query; select_sql_query([{{Type, _Version1}, Query1} | Rest], Type, undefined, _) -> select_sql_query(Rest, Type, undefined, Query1); select_sql_query([{{Type, Version1}, Query1} | Rest], Type, Version, Query) -> if Version >= Version1 -> Query1; true -> select_sql_query(Rest, Type, Version, Query) end; select_sql_query([{_, _} | Rest], Type, Version, Query) -> select_sql_query(Rest, Type, Version, Query). generic_sql_query(SQLQuery) -> sql_query_format_res( sql_query_internal(generic_sql_query_format(SQLQuery)), SQLQuery). generic_sql_query_format(SQLQuery) -> Args = (SQLQuery#sql_query.args)(generic_escape()), (SQLQuery#sql_query.format_query)(Args). generic_escape() -> #sql_escape{string = fun(X) -> <<"'", (escape(X))/binary, "'">> end, integer = fun(X) -> misc:i2l(X) end, boolean = fun(true) -> <<"1">>; (false) -> <<"0">> end }. sqlite_sql_query(SQLQuery) -> sql_query_format_res( sql_query_internal(sqlite_sql_query_format(SQLQuery)), SQLQuery). sqlite_sql_query_format(SQLQuery) -> Args = (SQLQuery#sql_query.args)(sqlite_escape()), (SQLQuery#sql_query.format_query)(Args). sqlite_escape() -> #sql_escape{string = fun(X) -> <<"'", (standard_escape(X))/binary, "'">> end, integer = fun(X) -> misc:i2l(X) end, boolean = fun(true) -> <<"1">>; (false) -> <<"0">> end }. standard_escape(S) -> << <<(case Char of $' -> << "''" >>; _ -> << Char >> end)/binary>> || <> <= S >>. mssql_sql_query(SQLQuery) -> sqlite_sql_query(SQLQuery). pgsql_prepare(SQLQuery, State) -> Escape = #sql_escape{_ = fun(X) -> X end}, N = length((SQLQuery#sql_query.args)(Escape)), Args = [<<$$, (integer_to_binary(I))/binary>> || I <- lists:seq(1, N)], Query = (SQLQuery#sql_query.format_query)(Args), pgsql:prepare(State#state.db_ref, SQLQuery#sql_query.hash, Query). pgsql_execute_escape() -> #sql_escape{string = fun(X) -> X end, integer = fun(X) -> [misc:i2l(X)] end, boolean = fun(true) -> "1"; (false) -> "0" end }. pgsql_execute_sql_query(SQLQuery, State) -> Args = (SQLQuery#sql_query.args)(pgsql_execute_escape()), ExecuteRes = pgsql:execute(State#state.db_ref, SQLQuery#sql_query.hash, Args), % {T, ExecuteRes} = % timer:tc(pgsql, execute, [State#state.db_ref, SQLQuery#sql_query.hash, Args]), % io:format("T ~s ~p~n", [SQLQuery#sql_query.hash, T]), Res = pgsql_execute_to_odbc(ExecuteRes), sql_query_format_res(Res, SQLQuery). sql_query_format_res({selected, _, Rows}, SQLQuery) -> Res = lists:flatmap( fun(Row) -> try [(SQLQuery#sql_query.format_res)(Row)] catch Class:Reason -> ST = erlang:get_stacktrace(), ?ERROR_MSG("Error while processing " "SQL query result: ~p~n" "row: ~p", [{Class, Reason, ST}, Row]), [] end end, Rows), {selected, Res}; sql_query_format_res(Res, _SQLQuery) -> Res. sql_query_to_iolist(SQLQuery) -> generic_sql_query_format(SQLQuery). %% Generate the OTP callback return tuple depending on the driver result. abort_on_driver_error({error, <<"query timed out">>} = Reply, From) -> p1_fsm:reply(From, Reply), {stop, timeout, get(?STATE_KEY)}; abort_on_driver_error({error, <<"Failed sending data on socket", _/binary>>} = Reply, From) -> p1_fsm:reply(From, Reply), {stop, closed, get(?STATE_KEY)}; abort_on_driver_error(Reply, From) -> p1_fsm:reply(From, Reply), {next_state, session_established, get(?STATE_KEY)}. %% == pure ODBC code %% part of init/1 %% Open an ODBC database connection odbc_connect(SQLServer, Timeout) -> ejabberd:start_app(odbc), odbc:connect(binary_to_list(SQLServer), [{scrollable_cursors, off}, {tuple_row, off}, {timeout, Timeout}, {binary_strings, on}]). %% == Native SQLite code %% part of init/1 %% Open a database connection to SQLite sqlite_connect(Host) -> File = sqlite_file(Host), case filelib:ensure_dir(File) of ok -> case sqlite3:open(sqlite_db(Host), [{file, File}]) of {ok, Ref} -> sqlite3:sql_exec( sqlite_db(Host), "pragma foreign_keys = on"), {ok, Ref}; {error, {already_started, Ref}} -> {ok, Ref}; {error, Reason} -> {error, Reason} end; Err -> Err end. %% Convert SQLite query result to Erlang ODBC result formalism sqlite_to_odbc(Host, ok) -> {updated, sqlite3:changes(sqlite_db(Host))}; sqlite_to_odbc(Host, {rowid, _}) -> {updated, sqlite3:changes(sqlite_db(Host))}; sqlite_to_odbc(_Host, [{columns, Columns}, {rows, TRows}]) -> Rows = [lists:map( fun(I) when is_integer(I) -> integer_to_binary(I); (B) -> B end, tuple_to_list(Row)) || Row <- TRows], {selected, [list_to_binary(C) || C <- Columns], Rows}; sqlite_to_odbc(_Host, {error, _Code, Reason}) -> {error, Reason}; sqlite_to_odbc(_Host, _) -> {updated, undefined}. %% == Native PostgreSQL code %% part of init/1 %% Open a database connection to PostgreSQL pgsql_connect(Server, Port, DB, Username, Password, ConnectTimeout, Transport, SSLOpts) -> case pgsql:connect([{host, Server}, {database, DB}, {user, Username}, {password, Password}, {port, Port}, {transport, Transport}, {connect_timeout, ConnectTimeout}, {as_binary, true}|SSLOpts]) of {ok, Ref} -> pgsql:squery(Ref, [<<"alter database \"">>, DB, <<"\" set ">>, <<"standard_conforming_strings='off';">>]), pgsql:squery(Ref, [<<"set standard_conforming_strings to 'off';">>]), {ok, Ref}; Err -> Err end. %% Convert PostgreSQL query result to Erlang ODBC result formalism pgsql_to_odbc({ok, PGSQLResult}) -> case PGSQLResult of [Item] -> pgsql_item_to_odbc(Item); Items -> [pgsql_item_to_odbc(Item) || Item <- Items] end. pgsql_item_to_odbc({<<"SELECT", _/binary>>, Rows, Recs}) -> {selected, [element(1, Row) || Row <- Rows], Recs}; pgsql_item_to_odbc({<<"FETCH", _/binary>>, Rows, Recs}) -> {selected, [element(1, Row) || Row <- Rows], Recs}; pgsql_item_to_odbc(<<"INSERT ", OIDN/binary>>) -> [_OID, N] = str:tokens(OIDN, <<" ">>), {updated, binary_to_integer(N)}; pgsql_item_to_odbc(<<"DELETE ", N/binary>>) -> {updated, binary_to_integer(N)}; pgsql_item_to_odbc(<<"UPDATE ", N/binary>>) -> {updated, binary_to_integer(N)}; pgsql_item_to_odbc({error, Error}) -> {error, Error}; pgsql_item_to_odbc(_) -> {updated, undefined}. pgsql_execute_to_odbc({ok, {<<"SELECT", _/binary>>, Rows}}) -> {selected, [], [[Field || {_, Field} <- Row] || Row <- Rows]}; pgsql_execute_to_odbc({ok, {'INSERT', N}}) -> {updated, N}; pgsql_execute_to_odbc({ok, {'DELETE', N}}) -> {updated, N}; pgsql_execute_to_odbc({ok, {'UPDATE', N}}) -> {updated, N}; pgsql_execute_to_odbc({error, Error}) -> {error, Error}; pgsql_execute_to_odbc(_) -> {updated, undefined}. %% == Native MySQL code %% part of init/1 %% Open a database connection to MySQL mysql_connect(Server, Port, DB, Username, Password, ConnectTimeout, _, _) -> case p1_mysql_conn:start(binary_to_list(Server), Port, binary_to_list(Username), binary_to_list(Password), binary_to_list(DB), ConnectTimeout, fun log/3) of {ok, Ref} -> p1_mysql_conn:fetch( Ref, [<<"set names 'utf8mb4' collate 'utf8mb4_bin';">>], self()), {ok, Ref}; Err -> Err end. %% Convert MySQL query result to Erlang ODBC result formalism mysql_to_odbc({updated, MySQLRes}) -> {updated, p1_mysql:get_result_affected_rows(MySQLRes)}; mysql_to_odbc({data, MySQLRes}) -> mysql_item_to_odbc(p1_mysql:get_result_field_info(MySQLRes), p1_mysql:get_result_rows(MySQLRes)); mysql_to_odbc({error, MySQLRes}) when is_binary(MySQLRes) -> {error, MySQLRes}; mysql_to_odbc({error, MySQLRes}) when is_list(MySQLRes) -> {error, list_to_binary(MySQLRes)}; mysql_to_odbc({error, MySQLRes}) -> {error, p1_mysql:get_result_reason(MySQLRes)}; mysql_to_odbc(ok) -> ok. %% When tabular data is returned, convert it to the ODBC formalism mysql_item_to_odbc(Columns, Recs) -> {selected, [element(2, Column) || Column <- Columns], Recs}. to_odbc({selected, Columns, Recs}) -> Rows = [lists:map( fun(I) when is_integer(I) -> integer_to_binary(I); (B) -> B end, Row) || Row <- Recs], {selected, [list_to_binary(C) || C <- Columns], Rows}; to_odbc({error, Reason}) when is_list(Reason) -> {error, list_to_binary(Reason)}; to_odbc(Res) -> Res. get_db_version(#state{db_type = pgsql} = State) -> case pgsql:squery(State#state.db_ref, <<"select current_setting('server_version_num')">>) of {ok, [{_, _, [[SVersion]]}]} -> case catch binary_to_integer(SVersion) of Version when is_integer(Version) -> State#state{db_version = Version}; Error -> ?WARNING_MSG("error getting pgsql version: ~p", [Error]), State end; Res -> ?WARNING_MSG("error getting pgsql version: ~p", [Res]), State end; get_db_version(State) -> State. log(Level, Format, Args) -> case Level of debug -> ?DEBUG(Format, Args); normal -> ?INFO_MSG(Format, Args); error -> ?ERROR_MSG(Format, Args) end. db_opts(Host) -> Type = ejabberd_config:get_option({sql_type, Host}, odbc), Server = ejabberd_config:get_option({sql_server, Host}, <<"localhost">>), Timeout = timer:seconds( ejabberd_config:get_option({sql_connect_timeout, Host}, 5)), Transport = case ejabberd_config:get_option({sql_ssl, Host}, false) of false -> tcp; true -> ssl end, warn_if_ssl_unsupported(Transport, Type), case Type of odbc -> [odbc, Server, Timeout]; sqlite -> [sqlite, Host]; _ -> Port = ejabberd_config:get_option( {sql_port, Host}, case Type of mssql -> ?MSSQL_PORT; mysql -> ?MYSQL_PORT; pgsql -> ?PGSQL_PORT end), DB = ejabberd_config:get_option({sql_database, Host}, <<"ejabberd">>), User = ejabberd_config:get_option({sql_username, Host}, <<"ejabberd">>), Pass = ejabberd_config:get_option({sql_password, Host}, <<"">>), SSLOpts = get_ssl_opts(Transport, Host), case Type of mssql -> [mssql, <<"DSN=", Host/binary, ";UID=", User/binary, ";PWD=", Pass/binary>>, Timeout]; _ -> [Type, Server, Port, DB, User, Pass, Timeout, Transport, SSLOpts] end end. warn_if_ssl_unsupported(tcp, _) -> ok; warn_if_ssl_unsupported(ssl, pgsql) -> ok; warn_if_ssl_unsupported(ssl, Type) -> ?WARNING_MSG("SSL connection is not supported for ~s", [Type]). get_ssl_opts(ssl, Host) -> Opts1 = case ejabberd_config:get_option({sql_ssl_certfile, Host}) of undefined -> []; CertFile -> [{certfile, CertFile}] end, Opts2 = case ejabberd_config:get_option({sql_ssl_cafile, Host}) of undefined -> Opts1; CAFile -> [{cacertfile, CAFile}|Opts1] end, case ejabberd_config:get_option({sql_ssl_verify, Host}, false) of true -> case lists:keymember(cacertfile, 1, Opts2) of true -> [{verify, verify_peer}|Opts2]; false -> ?WARNING_MSG("SSL verification is enabled for " "SQL connection, but option " "'sql_ssl_cafile' is not set; " "verification will be disabled", []), Opts2 end; false -> Opts2 end; get_ssl_opts(tcp, _) -> []. init_mssql(Host) -> Server = ejabberd_config:get_option({sql_server, Host}, <<"localhost">>), Port = ejabberd_config:get_option({sql_port, Host}, ?MSSQL_PORT), DB = ejabberd_config:get_option({sql_database, Host}, <<"ejabberd">>), FreeTDS = io_lib:fwrite("[~s]~n" "\thost = ~s~n" "\tport = ~p~n" "\ttds version = 7.1~n", [Host, Server, Port]), ODBCINST = io_lib:fwrite("[freetds]~n" "Description = MSSQL connection~n" "Driver = libtdsodbc.so~n" "Setup = libtdsS.so~n" "UsageCount = 1~n" "FileUsage = 1~n", []), ODBCINI = io_lib:fwrite("[~s]~n" "Description = MS SQL~n" "Driver = freetds~n" "Servername = ~s~n" "Database = ~s~n" "Port = ~p~n", [Host, Host, DB, Port]), ?DEBUG("~s:~n~s", [freetds_config(), FreeTDS]), ?DEBUG("~s:~n~s", [odbcinst_config(), ODBCINST]), ?DEBUG("~s:~n~s", [odbc_config(), ODBCINI]), case filelib:ensure_dir(freetds_config()) of ok -> try ok = file:write_file(freetds_config(), FreeTDS, [append]), ok = file:write_file(odbcinst_config(), ODBCINST), ok = file:write_file(odbc_config(), ODBCINI, [append]), os:putenv("ODBCSYSINI", tmp_dir()), os:putenv("FREETDS", freetds_config()), os:putenv("FREETDSCONF", freetds_config()), ok catch error:{badmatch, {error, Reason} = Err} -> ?ERROR_MSG("failed to create temporary files in ~s: ~s", [tmp_dir(), file:format_error(Reason)]), Err end; {error, Reason} = Err -> ?ERROR_MSG("failed to create temporary directory ~s: ~s", [tmp_dir(), file:format_error(Reason)]), Err end. tmp_dir() -> case os:type() of {win32, _} -> filename:join([os:getenv("HOME"), "conf"]); _ -> filename:join(["/tmp", "ejabberd"]) end. odbc_config() -> filename:join(tmp_dir(), "odbc.ini"). freetds_config() -> filename:join(tmp_dir(), "freetds.conf"). odbcinst_config() -> filename:join(tmp_dir(), "odbcinst.ini"). max_fsm_queue() -> proplists:get_value(max_queue, fsm_limit_opts(), unlimited). fsm_limit_opts() -> ejabberd_config:fsm_limit_opts([]). query_timeout(LServer) -> timer:seconds( ejabberd_config:get_option({sql_query_timeout, LServer}, 60)). check_error({error, Why} = Err, _Query) when Why == killed -> Err; check_error({error, Why} = Err, #sql_query{} = Query) -> ?ERROR_MSG("SQL query '~s' at ~p failed: ~p", [Query#sql_query.hash, Query#sql_query.loc, Why]), Err; check_error({error, Why} = Err, Query) -> case catch iolist_to_binary(Query) of SQuery when is_binary(SQuery) -> ?ERROR_MSG("SQL query '~s' failed: ~p", [SQuery, Why]); _ -> ?ERROR_MSG("SQL query ~p failed: ~p", [Query, Why]) end, Err; check_error(Result, _Query) -> Result. -spec opt_type(sql_database) -> fun((binary()) -> binary()); (sql_keepalive_interval) -> fun((pos_integer()) -> pos_integer()); (sql_password) -> fun((binary()) -> binary()); (sql_port) -> fun((0..65535) -> 0..65535); (sql_server) -> fun((binary()) -> binary()); (sql_username) -> fun((binary()) -> binary()); (sql_ssl) -> fun((boolean()) -> boolean()); (sql_ssl_verify) -> fun((boolean()) -> boolean()); (sql_ssl_certfile) -> fun((boolean()) -> boolean()); (sql_ssl_cafile) -> fun((boolean()) -> boolean()); (sql_query_timeout) -> fun((pos_integer()) -> pos_integer()); (sql_connect_timeout) -> fun((pos_integer()) -> pos_integer()); (sql_queue_type) -> fun((ram | file) -> ram | file); (atom()) -> [atom()]. opt_type(sql_database) -> fun iolist_to_binary/1; opt_type(sql_keepalive_interval) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(sql_password) -> fun iolist_to_binary/1; opt_type(sql_port) -> fun (P) when is_integer(P), P > 0, P < 65536 -> P end; opt_type(sql_server) -> fun iolist_to_binary/1; opt_type(sql_username) -> fun iolist_to_binary/1; opt_type(sql_ssl) -> fun(B) when is_boolean(B) -> B end; opt_type(sql_ssl_verify) -> fun(B) when is_boolean(B) -> B end; opt_type(sql_ssl_certfile) -> fun ejabberd_pkix:try_certfile/1; opt_type(sql_ssl_cafile) -> fun misc:try_read_file/1; opt_type(sql_query_timeout) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(sql_connect_timeout) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(sql_queue_type) -> fun(ram) -> ram; (file) -> file end; opt_type(_) -> [sql_database, sql_keepalive_interval, sql_password, sql_port, sql_server, sql_username, sql_ssl, sql_ssl_verify, sql_ssl_certfile, sql_ssl_cafile, sql_queue_type, sql_query_timeout, sql_connect_timeout]. ejabberd-18.01/src/ejabberd_auth.erl0000644000232200023220000006260213225664356017723 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_auth.erl %%% Author : Alexey Shchepin %%% Purpose : Authentification %%% Created : 23 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_auth). -behaviour(gen_server). -behaviour(ejabberd_config). -author('alexey@process-one.net'). %% External exports -export([start_link/0, host_up/1, host_down/1, config_reloaded/0, set_password/3, check_password/4, check_password/6, check_password_with_authmodule/4, check_password_with_authmodule/6, try_register/3, get_users/0, get_users/1, password_to_scram/1, get_users/2, import_info/0, count_users/1, import/5, import_start/2, count_users/2, get_password/2, get_password_s/2, get_password_with_authmodule/2, user_exists/2, user_exists_in_other_modules/3, remove_user/2, remove_user/3, plain_password_required/1, store_type/1, entropy/1, backend_type/1, password_format/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([auth_modules/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -define(AUTH_CACHE, auth_cache). -define(SALT_LENGTH, 16). -record(state, {host_modules = #{} :: map()}). -type password() :: binary() | #scram{}. -type digest_fun() :: fun((binary()) -> binary()). -export_type([password/0]). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- -type opts() :: [{prefix, binary()} | {from, integer()} | {to, integer()} | {limit, integer()} | {offset, integer()}]. -callback start(binary()) -> any(). -callback stop(binary()) -> any(). -callback plain_password_required(binary()) -> boolean(). -callback store_type(binary()) -> plain | external | scram. -callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}. -callback remove_user(binary(), binary()) -> ok | {error, any()}. -callback user_exists(binary(), binary()) -> boolean() | {error, atom()}. -callback check_password(binary(), binary(), binary(), binary()) -> boolean(). -callback try_register(binary(), binary(), password()) -> ok | {error, atom()}. -callback get_users(binary(), opts()) -> [{binary(), binary()}]. -callback count_users(binary(), opts()) -> number(). -callback get_password(binary(), binary()) -> {ok, password()} | error. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> boolean(). -optional_callbacks([set_password/3, remove_user/2, user_exists/2, check_password/4, try_register/3, get_users/2, count_users/2, get_password/2, use_cache/1, cache_nodes/1]). -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> ejabberd_hooks:add(host_up, ?MODULE, host_up, 30), ejabberd_hooks:add(host_down, ?MODULE, host_down, 80), ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 40), HostModules = lists:foldl( fun(Host, Acc) -> Modules = auth_modules(Host), maps:put(Host, Modules, Acc) end, #{}, ?MYHOSTS), lists:foreach( fun({Host, Modules}) -> start(Host, Modules) end, maps:to_list(HostModules)), init_cache(HostModules), {ok, #state{host_modules = HostModules}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast({host_up, Host}, #state{host_modules = HostModules} = State) -> Modules = auth_modules(Host), start(Host, Modules), NewHostModules = maps:put(Host, Modules, HostModules), init_cache(NewHostModules), {noreply, State#state{host_modules = NewHostModules}}; handle_cast({host_down, Host}, #state{host_modules = HostModules} = State) -> Modules = maps:get(Host, HostModules, []), stop(Host, Modules), NewHostModules = maps:remove(Host, HostModules), init_cache(NewHostModules), {noreply, State#state{host_modules = NewHostModules}}; handle_cast(config_reloaded, #state{host_modules = HostModules} = State) -> NewHostModules = lists:foldl( fun(Host, Acc) -> OldModules = maps:get(Host, HostModules, []), NewModules = auth_modules(Host), start(Host, NewModules -- OldModules), stop(Host, OldModules -- NewModules), maps:put(Host, NewModules, Acc) end, HostModules, ?MYHOSTS), init_cache(NewHostModules), {noreply, State#state{host_modules = NewHostModules}}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, State) -> ejabberd_hooks:delete(host_up, ?MODULE, start, 30), ejabberd_hooks:delete(host_down, ?MODULE, stop, 80), ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 40), lists:foreach( fun({Host, Modules}) -> stop(Host, Modules) end, maps:to_list(State#state.host_modules)). code_change(_OldVsn, State, _Extra) -> {ok, State}. start(Host, Modules) -> lists:foreach(fun(M) -> M:start(Host) end, Modules). stop(Host, Modules) -> lists:foreach(fun(M) -> M:stop(Host) end, Modules). host_up(Host) -> gen_server:cast(?MODULE, {host_up, Host}). host_down(Host) -> gen_server:cast(?MODULE, {host_down, Host}). config_reloaded() -> gen_server:cast(?MODULE, config_reloaded). -spec plain_password_required(binary()) -> boolean(). plain_password_required(Server) -> lists:any(fun (M) -> M:plain_password_required(Server) end, auth_modules(Server)). -spec store_type(binary()) -> plain | scram | external. store_type(Server) -> lists:foldl( fun(_, external) -> external; (M, scram) -> case M:store_type(Server) of external -> external; _ -> scram end; (M, plain) -> M:store_type(Server) end, plain, auth_modules(Server)). -spec check_password(binary(), binary(), binary(), binary()) -> boolean(). check_password(User, AuthzId, Server, Password) -> check_password(User, AuthzId, Server, Password, <<"">>, undefined). -spec check_password(binary(), binary(), binary(), binary(), binary(), digest_fun() | undefined) -> boolean(). check_password(User, AuthzId, Server, Password, Digest, DigestGen) -> case check_password_with_authmodule( User, AuthzId, Server, Password, Digest, DigestGen) of {true, _AuthModule} -> true; false -> false end. -spec check_password_with_authmodule(binary(), binary(), binary(), binary()) -> false | {true, atom()}. check_password_with_authmodule(User, AuthzId, Server, Password) -> check_password_with_authmodule( User, AuthzId, Server, Password, <<"">>, undefined). -spec check_password_with_authmodule( binary(), binary(), binary(), binary(), binary(), digest_fun() | undefined) -> false | {true, atom()}. check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGen) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> lists:foldl( fun(Mod, false) -> case db_check_password( LUser, AuthzId, LServer, Password, Digest, DigestGen, Mod) of true -> {true, Mod}; false -> false end; (_, Acc) -> Acc end, false, auth_modules(LServer)); _ -> false end. -spec set_password(binary(), binary(), password()) -> ok | {error, atom()}. set_password(User, Server, Password) -> case validate_credentials(User, Server, Password) of {ok, LUser, LServer} -> lists:foldl( fun(M, {error, _}) -> db_set_password(LUser, LServer, Password, M); (_, ok) -> ok end, {error, not_allowed}, auth_modules(LServer)); Err -> Err end. -spec try_register(binary(), binary(), password()) -> ok | {error, atom()}. try_register(User, Server, Password) -> case validate_credentials(User, Server, Password) of {ok, LUser, LServer} -> case user_exists(LUser, LServer) of true -> {error, exists}; false -> case ejabberd_router:is_my_host(LServer) of true -> case lists:foldl( fun(_, ok) -> ok; (Mod, _) -> db_try_register( LUser, LServer, Password, Mod) end, {error, not_allowed}, auth_modules(LServer)) of ok -> ejabberd_hooks:run( register_user, LServer, [LUser, LServer]); {error, _} = Err -> Err end; false -> {error, not_allowed} end end; Err -> Err end. -spec get_users() -> [{binary(), binary()}]. get_users() -> lists:flatmap( fun({Host, Mod}) -> db_get_users(Host, [], Mod) end, auth_modules()). -spec get_users(binary()) -> [{binary(), binary()}]. get_users(Server) -> get_users(Server, []). -spec get_users(binary(), opts()) -> [{binary(), binary()}]. get_users(Server, Opts) -> case jid:nameprep(Server) of error -> []; LServer -> lists:flatmap( fun(M) -> db_get_users(LServer, Opts, M) end, auth_modules(LServer)) end. -spec count_users(binary()) -> non_neg_integer(). count_users(Server) -> count_users(Server, []). -spec count_users(binary(), opts()) -> non_neg_integer(). count_users(Server, Opts) -> case jid:nameprep(Server) of error -> 0; LServer -> lists:sum( lists:map( fun(M) -> db_count_users(LServer, Opts, M) end, auth_modules(LServer))) end. -spec get_password(binary(), binary()) -> false | password(). get_password(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> case lists:foldl( fun(M, error) -> db_get_password(LUser, LServer, M); (_M, Acc) -> Acc end, error, auth_modules(LServer)) of {ok, Password} -> Password; error -> false end; _ -> false end. -spec get_password_s(binary(), binary()) -> password(). get_password_s(User, Server) -> case get_password(User, Server) of false -> <<"">>; Password -> Password end. -spec get_password_with_authmodule(binary(), binary()) -> {false | password(), module()}. get_password_with_authmodule(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> case lists:foldl( fun(M, {error, _}) -> {db_get_password(LUser, LServer, M), M}; (_M, Acc) -> Acc end, {error, undefined}, auth_modules(LServer)) of {{ok, Password}, Module} -> {Password, Module}; {error, Module} -> {false, Module} end; _ -> {false, undefined} end. -spec user_exists(binary(), binary()) -> boolean(). user_exists(_User, <<"">>) -> false; user_exists(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> lists:any( fun(M) -> case db_user_exists(LUser, LServer, M) of {error, _} -> false; Else -> Else end end, auth_modules(LServer)); _ -> false end. -spec user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe. user_exists_in_other_modules(Module, User, Server) -> user_exists_in_other_modules_loop( auth_modules(Server) -- [Module], User, Server). user_exists_in_other_modules_loop([], _User, _Server) -> false; user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> case db_user_exists(User, Server, AuthModule) of true -> true; false -> user_exists_in_other_modules_loop(AuthModules, User, Server); {error, _} -> maybe end. -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> lists:foreach( fun(Mod) -> db_remove_user(LUser, LServer, Mod) end, auth_modules(LServer)), ejabberd_hooks:run(remove_user, LServer, [LUser, LServer]); _Err -> ok end. -spec remove_user(binary(), binary(), password()) -> ok | {error, atom()}. remove_user(User, Server, Password) -> case validate_credentials(User, Server, Password) of {ok, LUser, LServer} -> case lists:foldl( fun (_, ok) -> ok; (Mod, _) -> case db_check_password( LUser, <<"">>, LServer, Password, <<"">>, undefined, Mod) of true -> db_remove_user(LUser, LServer, Mod); false -> {error, not_allowed} end end, {error, not_allowed}, auth_modules(Server)) of ok -> ejabberd_hooks:run( remove_user, LServer, [LUser, LServer]); Err -> Err end; Err -> Err end. %% @doc Calculate informational entropy. -spec entropy(iodata()) -> float(). entropy(B) -> case binary_to_list(B) of "" -> 0.0; S -> Set = lists:foldl(fun (C, [Digit, Printable, LowLetter, HiLetter, Other]) -> if C >= $a, C =< $z -> [Digit, Printable, 26, HiLetter, Other]; C >= $0, C =< $9 -> [9, Printable, LowLetter, HiLetter, Other]; C >= $A, C =< $Z -> [Digit, Printable, LowLetter, 26, Other]; C >= 33, C =< 126 -> [Digit, 33, LowLetter, HiLetter, Other]; true -> [Digit, Printable, LowLetter, HiLetter, 128] end end, [0, 0, 0, 0, 0], S), length(S) * math:log(lists:sum(Set)) / math:log(2) end. -spec backend_type(atom()) -> atom(). backend_type(Mod) -> case atom_to_list(Mod) of "ejabberd_auth_" ++ T -> list_to_atom(T); _ -> Mod end. -spec password_format(binary() | global) -> plain | scram. password_format(LServer) -> ejabberd_config:get_option({auth_password_format, LServer}, plain). %%%---------------------------------------------------------------------- %%% Backend calls %%%---------------------------------------------------------------------- db_try_register(User, Server, Password, Mod) -> case erlang:function_exported(Mod, try_register, 3) of true -> Password1 = case Mod:store_type(Server) of scram -> password_to_scram(Password); _ -> Password end, case use_cache(Mod, Server) of true -> case ets_cache:update( ?AUTH_CACHE, {User, Server}, {ok, Password}, fun() -> Mod:try_register(User, Server, Password1) end, cache_nodes(Mod, Server)) of {ok, _} -> ok; {error, _} = Err -> Err end; false -> Mod:try_register(User, Server, Password1) end; false -> {error, not_allowed} end. db_set_password(User, Server, Password, Mod) -> case erlang:function_exported(Mod, set_password, 3) of true -> Password1 = case Mod:store_type(Server) of scram -> password_to_scram(Password); _ -> Password end, case use_cache(Mod, Server) of true -> case ets_cache:update( ?AUTH_CACHE, {User, Server}, {ok, Password}, fun() -> Mod:set_password(User, Server, Password1) end, cache_nodes(Mod, Server)) of {ok, _} -> ok; {error, _} = Err -> Err end; false -> Mod:set_password(User, Server, Password1) end; false -> {error, not_allowed} end. db_get_password(User, Server, Mod) -> UseCache = use_cache(Mod, Server), case erlang:function_exported(Mod, get_password, 2) of false when UseCache -> ets_cache:lookup(?AUTH_CACHE, {User, Server}); false -> error; true when UseCache -> ets_cache:lookup( ?AUTH_CACHE, {User, Server}, fun() -> Mod:get_password(User, Server) end); true -> Mod:get_password(User, Server) end. db_user_exists(User, Server, Mod) -> case db_get_password(User, Server, Mod) of {ok, _} -> true; error -> case Mod:store_type(Server) of external -> Mod:user_exists(User, Server); _ -> false end end. db_check_password(User, AuthzId, Server, ProvidedPassword, Digest, DigestFun, Mod) -> case db_get_password(User, Server, Mod) of {ok, ValidPassword} -> match_passwords(ProvidedPassword, ValidPassword, Digest, DigestFun); error -> case {Mod:store_type(Server), use_cache(Mod, Server)} of {external, true} -> case ets_cache:update( ?AUTH_CACHE, {User, Server}, {ok, ProvidedPassword}, fun() -> case Mod:check_password( User, AuthzId, Server, ProvidedPassword) of true -> {ok, ProvidedPassword}; false -> error end end, cache_nodes(Mod, Server)) of {ok, _} -> true; error -> false end; {external, false} -> Mod:check_password(User, AuthzId, Server, ProvidedPassword); _ -> false end end. db_remove_user(User, Server, Mod) -> case erlang:function_exported(Mod, remove_user, 2) of true -> case Mod:remove_user(User, Server) of ok -> case use_cache(Mod, Server) of true -> ets_cache:delete(?AUTH_CACHE, {User, Server}, cache_nodes(Mod, Server)); false -> ok end; {error, _} = Err -> Err end; false -> {error, not_allowed} end. db_get_users(Server, Opts, Mod) -> case erlang:function_exported(Mod, get_users, 2) of true -> Mod:get_users(Server, Opts); false -> case use_cache(Mod, Server) of true -> ets_cache:fold( fun({User, S}, {ok, _}, Users) when S == Server -> [{User, Server}|Users]; (_, _, Users) -> Users end, [], ?AUTH_CACHE); false -> [] end end. db_count_users(Server, Opts, Mod) -> case erlang:function_exported(Mod, count_users, 2) of true -> Mod:count_users(Server, Opts); false -> case use_cache(Mod, Server) of true -> ets_cache:fold( fun({_, S}, {ok, _}, Num) when S == Server -> Num + 1; (_, _, Num) -> Num end, 0, ?AUTH_CACHE); false -> 0 end end. %%%---------------------------------------------------------------------- %%% SCRAM stuff %%%---------------------------------------------------------------------- is_password_scram_valid(Password, Scram) -> case jid:resourceprep(Password) of error -> false; _ -> IterationCount = Scram#scram.iterationcount, Salt = base64:decode(Scram#scram.salt), SaltedPassword = scram:salted_password(Password, Salt, IterationCount), StoredKey = scram:stored_key(scram:client_key(SaltedPassword)), base64:decode(Scram#scram.storedkey) == StoredKey end. password_to_scram(Password) -> password_to_scram(Password, ?SCRAM_DEFAULT_ITERATION_COUNT). password_to_scram(#scram{} = Password, _IterationCount) -> Password; password_to_scram(Password, IterationCount) -> Salt = randoms:bytes(?SALT_LENGTH), SaltedPassword = scram:salted_password(Password, Salt, IterationCount), StoredKey = scram:stored_key(scram:client_key(SaltedPassword)), ServerKey = scram:server_key(SaltedPassword), #scram{storedkey = base64:encode(StoredKey), serverkey = base64:encode(ServerKey), salt = base64:encode(Salt), iterationcount = IterationCount}. %%%---------------------------------------------------------------------- %%% Cache stuff %%%---------------------------------------------------------------------- -spec init_cache(map()) -> ok. init_cache(HostModules) -> case use_cache(HostModules) of true -> ets_cache:new(?AUTH_CACHE, cache_opts()); false -> ets_cache:delete(?AUTH_CACHE) end. -spec cache_opts() -> [proplists:property()]. cache_opts() -> MaxSize = ejabberd_config:get_option( auth_cache_size, ejabberd_config:cache_size(global)), CacheMissed = ejabberd_config:get_option( auth_cache_missed, ejabberd_config:cache_missed(global)), LifeTime = case ejabberd_config:get_option( auth_cache_life_time, ejabberd_config:cache_life_time(global)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(map()) -> boolean(). use_cache(HostModules) -> lists:any( fun({Host, Modules}) -> lists:any(fun(Module) -> use_cache(Module, Host) end, Modules) end, maps:to_list(HostModules)). -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, LServer) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(LServer); false -> ejabberd_config:get_option( {auth_use_cache, LServer}, ejabberd_config:use_cache(LServer)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, LServer) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(LServer); false -> ejabberd_cluster:get_nodes() end. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- -spec auth_modules() -> [{binary(), module()}]. auth_modules() -> lists:flatmap( fun(Host) -> [{Host, Mod} || Mod <- auth_modules(Host)] end, ?MYHOSTS). -spec auth_modules(binary()) -> [module()]. auth_modules(Server) -> LServer = jid:nameprep(Server), Default = ejabberd_config:default_db(LServer, ?MODULE), Methods = ejabberd_config:get_option({auth_method, LServer}, [Default]), [ejabberd:module_name([<<"ejabberd">>, <<"auth">>, misc:atom_to_binary(M)]) || M <- Methods]. -spec match_passwords(password(), password(), binary(), digest_fun() | undefined) -> boolean(). match_passwords(Password, #scram{} = Scram, <<"">>, undefined) -> is_password_scram_valid(Password, Scram); match_passwords(Password, #scram{} = Scram, Digest, DigestFun) -> StoredKey = base64:decode(Scram#scram.storedkey), DigRes = if Digest /= <<"">> -> Digest == DigestFun(StoredKey); true -> false end, if DigRes -> true; true -> StoredKey == Password andalso Password /= <<"">> end; match_passwords(ProvidedPassword, ValidPassword, <<"">>, undefined) -> ProvidedPassword == ValidPassword andalso ProvidedPassword /= <<"">>; match_passwords(ProvidedPassword, ValidPassword, Digest, DigestFun) -> DigRes = if Digest /= <<"">> -> Digest == DigestFun(ValidPassword); true -> false end, if DigRes -> true; true -> ValidPassword == ProvidedPassword andalso ProvidedPassword /= <<"">> end. -spec validate_credentials(binary(), binary()) -> {ok, binary(), binary()} | {error, invalid_jid}. validate_credentials(User, Server) -> validate_credentials(User, Server, #scram{}). -spec validate_credentials(binary(), binary(), password()) -> {ok, binary(), binary()} | {error, invalid_jid | invalid_password}. validate_credentials(_User, _Server, <<"">>) -> {error, invalid_password}; validate_credentials(User, Server, Password) -> case jid:nodeprep(User) of error -> {error, invalid_jid}; LUser -> case jid:nameprep(Server) of error -> {error, invalid_jid}; LServer -> if is_record(Password, scram) -> {ok, LUser, LServer}; true -> case jid:resourceprep(Password) of error -> {error, invalid_password}; _ -> {ok, LUser, LServer} end end end end. import_info() -> [{<<"users">>, 3}]. import_start(_LServer, mnesia) -> ejabberd_auth_mnesia:init_db(); import_start(_LServer, _) -> ok. import(Server, {sql, _}, mnesia, <<"users">>, Fields) -> ejabberd_auth_mnesia:import(Server, Fields); import(Server, {sql, _}, riak, <<"users">>, Fields) -> ejabberd_auth_riak:import(Server, Fields); import(_LServer, {sql, _}, sql, <<"users">>, _) -> ok. -spec opt_type(auth_method) -> fun((atom() | [atom()]) -> [atom()]); (auth_password_format) -> fun((plain | scram) -> plain | scram); (auth_use_cache) -> fun((boolean()) -> boolean()); (auth_cache_missed) -> fun((boolean()) -> boolean()); (auth_cache_life_time) -> fun((timeout()) -> timeout()); (auth_cache_size) -> fun((timeout()) -> timeout()); (atom()) -> [atom()]. opt_type(auth_method) -> fun (V) when is_list(V) -> lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V); (V) -> [ejabberd_config:v_db(?MODULE, V)] end; opt_type(auth_password_format) -> fun (plain) -> plain; (scram) -> scram end; opt_type(auth_use_cache) -> fun(B) when is_boolean(B) -> B end; opt_type(auth_cache_missed) -> fun(B) when is_boolean(B) -> B end; opt_type(auth_cache_life_time) -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; opt_type(auth_cache_size) -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; opt_type(_) -> [auth_method, auth_password_format, auth_use_cache, auth_cache_missed, auth_cache_life_time, auth_cache_size]. ejabberd-18.01/src/ejabberd_router_riak.erl0000644000232200023220000000536013225664356021306 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 15 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_router_riak). -behaviour(ejabberd_router). %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, get_all_routes/0]). -include("logger.hrl"). -include("ejabberd_router.hrl"). %%%=================================================================== %%% API %%%=================================================================== init() -> clean_table(). register_route(Domain, ServerHost, LocalHint, _, Pid) -> ejabberd_riak:put(#route{domain = Domain, server_host = ServerHost, local_hint = LocalHint, pid = Pid}, route_schema(), [{i, {Domain, Pid}}, {'2i', [{<<"route">>, Domain}]}]). unregister_route(Domain, _, Pid) -> ejabberd_riak:delete(route, {Domain, Pid}). find_routes(Domain) -> ejabberd_riak:get_by_index(route, route_schema(), <<"route">>, Domain). get_all_routes() -> case ejabberd_riak:get(route, route_schema()) of {ok, Routes} -> {ok, lists:flatmap( fun(#route{domain = D, server_host = S}) when D /= S -> [D]; (_) -> [] end, Routes)}; Err -> Err end. %%%=================================================================== %%% Internal functions %%%=================================================================== route_schema() -> {record_info(fields, route), #route{}}. clean_table() -> ?DEBUG("Cleaning Riak 'route' table...", []), case ejabberd_riak:get(route, route_schema()) of {ok, Routes} -> lists:foreach( fun(#route{domain = Domain, pid = Pid}) -> ejabberd_riak:delete(route, {Domain, Pid}) end, Routes); {error, Err} -> ?ERROR_MSG("failed to clean Riak 'route' table: ~p", [Err]), Err end. ejabberd-18.01/src/node_hometree.erl0000644000232200023220000001404613225664356017760 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_hometree.erl %%% Author : Christophe Romain %%% Purpose : Standard tree ordered node plugin %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_hometree). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts), Owner = mod_pubsub:service_jid(Host), mod_pubsub:create_node(Host, ServerHost, <<"/home">>, Owner, <<"hometree">>), mod_pubsub:create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, Owner, <<"hometree">>), ok. terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost). options() -> node_flat:options(). features() -> node_flat:features(). %% @doc Checks if the current user has the permission to create the requested node %%

In hometree node, the permission is decided by the place in the %% hierarchy where the user is creating the node. The access parameter is also %% checked. This parameter depends on the value of the %% access_createnode ACL value in ejabberd config file.

%%

This function also check that node can be created as a children of its %% parent node

create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) -> LOwner = jid:tolower(Owner), {User, Server, _Resource} = LOwner, Allowed = case LOwner of {<<"">>, Host, <<"">>} -> true; % pubsub service always allowed _ -> case acl:match_rule(ServerHost, Access, LOwner) of allow -> case node_to_path(Node) of [<<"home">>, Server, User | _] -> true; _ -> false end; _ -> false end end, {result, Allowed}. create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Nodes) -> node_flat:delete_node(Nodes). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). %% @doc

Return the path of the node.

node_to_path(Node) -> str:tokens(Node, <<"/">>). path_to_node([]) -> <<>>; path_to_node(Path) -> iolist_to_binary(str:join([<<"">> | Path], <<"/">>)). ejabberd-18.01/src/ejabberd_router.erl0000644000232200023220000003703113225664356020300 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_router.erl %%% Author : Alexey Shchepin %%% Purpose : Main router %%% Created : 27 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_router). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. -behaviour(?GEN_SERVER). %% API -export([route/1, route_error/2, route_iq/2, route_iq/3, route_iq/4, register_route/2, register_route/3, register_route/4, register_routes/1, host_of_route/1, process_iq/1, unregister_route/1, unregister_route/2, unregister_routes/1, get_all_routes/0, is_my_route/1, is_my_host/1, clean_cache/1, config_reloaded/0, get_backend/0]). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, opt_type/1]). %% Deprecated functions -export([route/3, route_error/4]). -deprecated([{route, 3}, {route_error, 4}]). %% This value is used in SIP and Megaco for a transaction lifetime. -define(IQ_TIMEOUT, 32000). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_router.hrl"). -include("xmpp.hrl"). -callback init() -> any(). -callback register_route(binary(), binary(), local_hint(), undefined | pos_integer(), pid()) -> ok | {error, term()}. -callback unregister_route(binary(), undefined | pos_integer(), pid()) -> ok | {error, term()}. -callback find_routes(binary()) -> {ok, [#route{}]} | {error, any()}. -callback get_all_routes() -> {ok, [binary()]} | {error, any()}. -record(state, {}). %%==================================================================== %% API %%==================================================================== start_link() -> ?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []). -spec route(stanza()) -> ok. route(Packet) -> try do_route(Packet) catch E:R -> ?ERROR_MSG("failed to route packet:~n~s~nReason = ~p", [xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}]) end. -spec route(jid(), jid(), xmlel() | stanza()) -> ok. route(#jid{} = From, #jid{} = To, #xmlel{} = El) -> try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of Pkt -> route(From, To, Pkt) catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode xml element ~p when " "routing from ~s to ~s: ~s", [El, jid:encode(From), jid:encode(To), xmpp:format_error(Why)]) end; route(#jid{} = From, #jid{} = To, Packet) -> case catch do_route(xmpp:set_from_to(Packet, From, To)) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, To, Packet}]); _ -> ok end. -spec route_error(stanza(), stanza_error()) -> ok. route_error(Packet, Err) -> Type = xmpp:get_type(Packet), if Type == error; Type == result -> ok; true -> route(xmpp:make_error(Packet, Err)) end. %% Route the error packet only if the originating packet is not an error itself. %% RFC3920 9.3.1 -spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok; (jid(), jid(), stanza(), stanza_error()) -> ok. route_error(From, To, #xmlel{} = ErrPacket, #xmlel{} = OrigPacket) -> #xmlel{attrs = Attrs} = OrigPacket, case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of false -> route(From, To, ErrPacket); true -> ok end; route_error(From, To, Packet, #stanza_error{} = Err) -> Type = xmpp:get_type(Packet), if Type == error; Type == result -> ok; true -> route(From, To, xmpp:make_error(Packet, Err)) end. -spec route_iq(iq(), fun((iq() | timeout) -> any())) -> ok. route_iq(IQ, Fun) -> route_iq(IQ, Fun, undefined, ?IQ_TIMEOUT). -spec route_iq(iq(), term(), pid() | atom()) -> ok. route_iq(IQ, State, Proc) -> route_iq(IQ, State, Proc, ?IQ_TIMEOUT). -spec route_iq(iq(), term(), pid() | atom(), undefined | non_neg_integer()) -> ok. route_iq(IQ, State, Proc, undefined) -> route_iq(IQ, State, Proc, ?IQ_TIMEOUT); route_iq(IQ, State, Proc, Timeout) -> ejabberd_iq:route(IQ, Proc, State, Timeout). -spec register_route(binary(), binary()) -> ok. register_route(Domain, ServerHost) -> register_route(Domain, ServerHost, undefined). -spec register_route(binary(), binary(), local_hint() | undefined) -> ok. register_route(Domain, ServerHost, LocalHint) -> register_route(Domain, ServerHost, LocalHint, self()). -spec register_route(binary(), binary(), local_hint() | undefined, pid()) -> ok. register_route(Domain, ServerHost, LocalHint, Pid) -> case {jid:nameprep(Domain), jid:nameprep(ServerHost)} of {error, _} -> erlang:error({invalid_domain, Domain}); {_, error} -> erlang:error({invalid_domain, ServerHost}); {LDomain, LServerHost} -> Mod = get_backend(), case Mod:register_route(LDomain, LServerHost, LocalHint, get_component_number(LDomain), Pid) of ok -> ?DEBUG("Route registered: ~s", [LDomain]), ejabberd_hooks:run(route_registered, [LDomain]), delete_cache(Mod, LDomain); {error, Err} -> ?ERROR_MSG("Failed to register route ~s: ~p", [LDomain, Err]) end end. -spec register_routes([{binary(), binary()}]) -> ok. register_routes(Domains) -> lists:foreach(fun ({Domain, ServerHost}) -> register_route(Domain, ServerHost) end, Domains). -spec unregister_route(binary()) -> ok. unregister_route(Domain) -> unregister_route(Domain, self()). -spec unregister_route(binary(), pid()) -> ok. unregister_route(Domain, Pid) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> Mod = get_backend(), case Mod:unregister_route( LDomain, get_component_number(LDomain), Pid) of ok -> ?DEBUG("Route unregistered: ~s", [LDomain]), ejabberd_hooks:run(route_unregistered, [LDomain]), delete_cache(Mod, LDomain); {error, Err} -> ?ERROR_MSG("Failed to unregister route ~s: ~p", [LDomain, Err]) end end. -spec unregister_routes([binary()]) -> ok. unregister_routes(Domains) -> lists:foreach(fun (Domain) -> unregister_route(Domain) end, Domains). -spec find_routes(binary()) -> [#route{}]. find_routes(Domain) -> Mod = get_backend(), case use_cache(Mod) of true -> case ets_cache:lookup( ?ROUTES_CACHE, {route, Domain}, fun() -> case Mod:find_routes(Domain) of {ok, Rs} when Rs /= [] -> {ok, Rs}; _ -> error end end) of {ok, Rs} -> Rs; error -> [] end; false -> case Mod:find_routes(Domain) of {ok, Rs} -> Rs; _ -> [] end end. -spec get_all_routes() -> [binary()]. get_all_routes() -> Mod = get_backend(), case use_cache(Mod) of true -> case ets_cache:lookup( ?ROUTES_CACHE, routes, fun() -> case Mod:get_all_routes() of {ok, Rs} when Rs /= [] -> {ok, Rs}; _ -> error end end) of {ok, Rs} -> Rs; error -> [] end; false -> case Mod:get_all_routes() of {ok, Rs} -> Rs; _ -> [] end end. -spec host_of_route(binary()) -> binary(). host_of_route(Domain) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> case find_routes(LDomain) of [#route{server_host = ServerHost}|_] -> ServerHost; _ -> erlang:error({unregistered_route, Domain}) end end. -spec is_my_route(binary()) -> boolean(). is_my_route(Domain) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> find_routes(LDomain) /= [] end. -spec is_my_host(binary()) -> boolean(). is_my_host(Domain) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> case find_routes(LDomain) of [#route{server_host = LDomain}|_] -> true; _ -> false end end. -spec process_iq(iq()) -> any(). process_iq(#iq{to = To} = IQ) -> if To#jid.luser == <<"">> -> ejabberd_local:process_iq(IQ); true -> ejabberd_sm:process_iq(IQ) end. -spec config_reloaded() -> ok. config_reloaded() -> Mod = get_backend(), init_cache(Mod). %%==================================================================== %% gen_server callbacks %%==================================================================== init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), Mod = get_backend(), init_cache(Mod), Mod:init(), clean_cache(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({route, Packet}, State) -> route(Packet), {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -spec do_route(stanza()) -> ok. do_route(OrigPacket) -> ?DEBUG("route:~n~s", [xmpp:pp(OrigPacket)]), case ejabberd_hooks:run_fold(filter_packet, OrigPacket, []) of drop -> ok; Packet -> case ejabberd_iq:dispatch(Packet) of true -> ok; false -> To = xmpp:get_to(Packet), LDstDomain = To#jid.lserver, case find_routes(LDstDomain) of [] -> ejabberd_s2s:route(Packet); [Route] -> do_route(Packet, Route); Routes -> From = xmpp:get_from(Packet), balancing_route(From, To, Packet, Routes) end, ok end end. -spec do_route(stanza(), #route{}) -> any(). do_route(Pkt, #route{local_hint = LocalHint, pid = Pid}) when is_pid(Pid) -> case LocalHint of {apply, Module, Function} when node(Pid) == node() -> Module:Function(Pkt); _ -> Pid ! {route, Pkt} end; do_route(_Pkt, _Route) -> ok. -spec balancing_route(jid(), jid(), stanza(), [#route{}]) -> any(). balancing_route(From, To, Packet, Rs) -> LDstDomain = To#jid.lserver, Value = get_domain_balancing(From, To, LDstDomain), case get_component_number(LDstDomain) of undefined -> case [R || R <- Rs, node(R#route.pid) == node()] of [] -> R = lists:nth(erlang:phash(Value, length(Rs)), Rs), do_route(Packet, R); LRs -> R = lists:nth(erlang:phash(Value, length(LRs)), LRs), do_route(Packet, R) end; _ -> SRs = lists:ukeysort(#route.local_hint, Rs), R = lists:nth(erlang:phash(Value, length(SRs)), SRs), do_route(Packet, R) end. -spec get_component_number(binary()) -> pos_integer() | undefined. get_component_number(LDomain) -> ejabberd_config:get_option({domain_balancing_component_number, LDomain}). -spec get_domain_balancing(jid(), jid(), binary()) -> any(). get_domain_balancing(From, To, LDomain) -> case ejabberd_config:get_option({domain_balancing, LDomain}) of undefined -> p1_time_compat:system_time(); random -> p1_time_compat:system_time(); source -> jid:tolower(From); destination -> jid:tolower(To); bare_source -> jid:remove_resource(jid:tolower(From)); bare_destination -> jid:remove_resource(jid:tolower(To)) end. -spec get_backend() -> module(). get_backend() -> DBType = ejabberd_config:get_option( router_db_type, ejabberd_config:default_ram_db(?MODULE)), list_to_atom("ejabberd_router_" ++ atom_to_list(DBType)). -spec cache_nodes(module()) -> [node()]. cache_nodes(Mod) -> case erlang:function_exported(Mod, cache_nodes, 0) of true -> Mod:cache_nodes(); false -> ejabberd_cluster:get_nodes() end. -spec use_cache(module()) -> boolean(). use_cache(Mod) -> case erlang:function_exported(Mod, use_cache, 0) of true -> Mod:use_cache(); false -> ejabberd_config:get_option( router_use_cache, ejabberd_config:use_cache(global)) end. -spec delete_cache(module(), binary()) -> ok. delete_cache(Mod, Domain) -> case use_cache(Mod) of true -> ets_cache:delete(?ROUTES_CACHE, {route, Domain}, cache_nodes(Mod)), ets_cache:delete(?ROUTES_CACHE, routes, cache_nodes(Mod)); false -> ok end. -spec init_cache(module()) -> ok. init_cache(Mod) -> case use_cache(Mod) of true -> ets_cache:new(?ROUTES_CACHE, cache_opts()); false -> ets_cache:delete(?ROUTES_CACHE) end. -spec cache_opts() -> [proplists:property()]. cache_opts() -> MaxSize = ejabberd_config:get_option( router_cache_size, ejabberd_config:cache_size(global)), CacheMissed = ejabberd_config:get_option( router_cache_missed, ejabberd_config:cache_missed(global)), LifeTime = case ejabberd_config:get_option( router_cache_life_time, ejabberd_config:cache_life_time(global)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec clean_cache(node()) -> ok. clean_cache(Node) -> ets_cache:filter( ?ROUTES_CACHE, fun(_, error) -> false; (routes, _) -> false; ({route, _}, {ok, Rs}) -> not lists:any( fun(#route{pid = Pid}) -> node(Pid) == Node end, Rs) end). -spec clean_cache() -> ok. clean_cache() -> ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]). -type domain_balancing() :: random | source | destination | bare_source | bare_destination. -spec opt_type(domain_balancing) -> fun((domain_balancing()) -> domain_balancing()); (domain_balancing_component_number) -> fun((pos_integer()) -> pos_integer()); (router_db_type) -> fun((atom()) -> atom()); (router_use_cache) -> fun((boolean()) -> boolean()); (router_cache_missed) -> fun((boolean()) -> boolean()); (router_cache_size) -> fun((timeout()) -> timeout()); (router_cache_life_time) -> fun((timeout()) -> timeout()); (atom()) -> [atom()]. opt_type(domain_balancing) -> fun (random) -> random; (source) -> source; (destination) -> destination; (bare_source) -> bare_source; (bare_destination) -> bare_destination end; opt_type(domain_balancing_component_number) -> fun (N) when is_integer(N), N > 1 -> N end; opt_type(router_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; opt_type(O) when O == router_use_cache; O == router_cache_missed -> fun(B) when is_boolean(B) -> B end; opt_type(O) when O == router_cache_size; O == router_cache_life_time -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; opt_type(_) -> [domain_balancing, domain_balancing_component_number, router_db_type, router_use_cache, router_cache_size, router_cache_missed, router_cache_life_time]. ejabberd-18.01/src/pubsub_migrate.erl0000644000232200023220000004006113225664356020147 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pubsub_migrate.erl %%% Author : Christophe Romain %%% Purpose : Migration/Upgrade code put out of mod_pubsub %%% Created : 26 Jul 2014 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(pubsub_migrate). -include("pubsub.hrl"). -include("logger.hrl"). -export([update_node_database/2, update_state_database/2]). -export([update_item_database/2, update_lastitem_database/2]). update_item_database(_Host, _ServerHost) -> convert_list_items(). update_node_database(Host, ServerHost) -> mnesia:del_table_index(pubsub_node, type), mnesia:del_table_index(pubsub_node, parentid), case catch mnesia:table_info(pubsub_node, attributes) of [host_node, host_parent, info] -> ?INFO_MSG("Upgrading pubsub nodes table...", []), F = fun () -> {Result, LastIdx} = lists:foldl(fun ({pubsub_node, NodeId, ParentId, {nodeinfo, Items, Options, Entities}}, {RecList, NodeIdx}) -> ItemsList = lists:foldl(fun ({item, IID, Publisher, Payload}, Acc) -> C = {unknown, Publisher}, M = {p1_time_compat:timestamp(), Publisher}, mnesia:write(#pubsub_item{itemid = {IID, NodeIdx}, creation = C, modification = M, payload = Payload}), [{Publisher, IID} | Acc] end, [], Items), Owners = dict:fold(fun (JID, {entity, Aff, Sub}, Acc) -> UsrItems = lists:foldl(fun ({P, I}, IAcc) -> case P of JID -> [I | IAcc]; _ -> IAcc end end, [], ItemsList), mnesia:write({pubsub_state, {JID, NodeIdx}, UsrItems, Aff, Sub}), case Aff of owner -> [JID | Acc]; _ -> Acc end end, [], Entities), mnesia:delete({pubsub_node, NodeId}), {[#pubsub_node{nodeid = NodeId, id = NodeIdx, parents = [element(2, ParentId)], owners = Owners, options = Options} | RecList], NodeIdx + 1} end, {[], 1}, mnesia:match_object({pubsub_node, {Host, '_'}, '_', '_'})), mnesia:write(#pubsub_index{index = node, last = LastIdx, free = []}), Result end, {atomic, NewRecords} = mnesia:transaction(F), {atomic, ok} = mnesia:delete_table(pubsub_node), {atomic, ok} = ejabberd_mnesia:create(?MODULE, pubsub_node, [{disc_copies, [node()]}, {attributes, record_info(fields, pubsub_node)}]), FNew = fun () -> lists:foreach(fun (Record) -> mnesia:write(Record) end, NewRecords) end, case mnesia:transaction(FNew) of {atomic, Result} -> ?INFO_MSG("Pubsub nodes table upgraded: ~p", [Result]); {aborted, Reason} -> ?ERROR_MSG("Problem upgrading Pubsub nodes table:~n~p", [Reason]) end; [nodeid, parentid, type, owners, options] -> F = fun ({pubsub_node, NodeId, {_, Parent}, Type, Owners, Options}) -> #pubsub_node{nodeid = NodeId, id = 0, parents = [Parent], type = Type, owners = Owners, options = Options} end, mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]), FNew = fun () -> LastIdx = lists:foldl(fun (#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) -> mnesia:write(PubsubNode#pubsub_node{id = NodeIdx}), lists:foreach(fun (#pubsub_state{stateid = StateId} = State) -> {JID, _} = StateId, mnesia:delete({pubsub_state, StateId}), mnesia:write(State#pubsub_state{stateid = {JID, NodeIdx}}) end, mnesia:match_object(#pubsub_state{stateid = {'_', NodeId}, _ = '_'})), lists:foreach(fun (#pubsub_item{itemid = ItemId} = Item) -> {IID, _} = ItemId, {M1, M2} = Item#pubsub_item.modification, {C1, C2} = Item#pubsub_item.creation, mnesia:delete({pubsub_item, ItemId}), mnesia:write(Item#pubsub_item{itemid = {IID, NodeIdx}, modification = {M2, M1}, creation = {C2, C1}}) end, mnesia:match_object(#pubsub_item{itemid = {'_', NodeId}, _ = '_'})), NodeIdx + 1 end, 1, mnesia:match_object({pubsub_node, {Host, '_'}, '_', '_', '_', '_', '_'}) ++ mnesia:match_object({pubsub_node, {{'_', ServerHost, '_'}, '_'}, '_', '_', '_', '_', '_'})), mnesia:write(#pubsub_index{index = node, last = LastIdx, free = []}) end, case mnesia:transaction(FNew) of {atomic, Result} -> rename_default_nodeplugin(), ?INFO_MSG("Pubsub nodes table upgraded: ~p", [Result]); {aborted, Reason} -> ?ERROR_MSG("Problem upgrading Pubsub nodes table:~n~p", [Reason]) end; [nodeid, id, parent, type, owners, options] -> F = fun ({pubsub_node, NodeId, Id, Parent, Type, Owners, Options}) -> #pubsub_node{nodeid = NodeId, id = Id, parents = [Parent], type = Type, owners = Owners, options = Options} end, mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]), rename_default_nodeplugin(); _ -> ok end, convert_list_nodes(). rename_default_nodeplugin() -> lists:foreach(fun (Node) -> mnesia:dirty_write(Node#pubsub_node{type = <<"hometree">>}) end, mnesia:dirty_match_object(#pubsub_node{type = <<"default">>, _ = '_'})). update_state_database(_Host, _ServerHost) -> % useless starting from ejabberd 17.04 % case catch mnesia:table_info(pubsub_state, attributes) of % [stateid, nodeidx, items, affiliation, subscriptions] -> % ?INFO_MSG("Upgrading pubsub states table...", []), % F = fun ({pubsub_state, {{U,S,R}, NodeID}, _NodeIdx, Items, Aff, Sub}, Acc) -> % JID = {U,S,R}, % Subs = case Sub of % none -> % []; % [] -> % []; % _ -> % SubID = pubsub_subscription:make_subid(), % [{Sub, SubID}] % end, % NewState = #pubsub_state{stateid = {JID, NodeID}, % items = Items, % affiliation = Aff, % subscriptions = Subs}, % [NewState | Acc] % end, % {atomic, NewRecs} = mnesia:transaction(fun mnesia:foldl/3, % [F, [], pubsub_state]), % {atomic, ok} = mnesia:delete_table(pubsub_state), % {atomic, ok} = ejabberd_mnesia:create(?MODULE, pubsub_state, % [{disc_copies, [node()]}, % {attributes, record_info(fields, pubsub_state)}]), % FNew = fun () -> % lists:foreach(fun mnesia:write/1, NewRecs) % end, % case mnesia:transaction(FNew) of % {atomic, Result} -> % ?INFO_MSG("Pubsub states table upgraded: ~p", % [Result]); % {aborted, Reason} -> % ?ERROR_MSG("Problem upgrading Pubsub states table:~n~p", % [Reason]) % end; % _ -> % ok % end, convert_list_subscriptions(), convert_list_states(). update_lastitem_database(_Host, _ServerHost) -> convert_list_lasts(). %% binarization from old 2.1.x convert_list_items() -> convert_list_records( pubsub_item, record_info(fields, pubsub_item), fun(#pubsub_item{itemid = {I, _}}) -> I end, fun(#pubsub_item{itemid = {I, Nidx}, creation = {C, CKey}, modification = {M, MKey}, payload = Els} = R) -> R#pubsub_item{itemid = {bin(I), Nidx}, creation = {C, binusr(CKey)}, modification = {M, binusr(MKey)}, payload = [fxml:to_xmlel(El) || El<-Els]} end). convert_list_states() -> convert_list_records( pubsub_state, record_info(fields, pubsub_state), fun(#pubsub_state{stateid = {{U,_,_}, _}}) -> U end, fun(#pubsub_state{stateid = {U, Nidx}, items = Is, affiliation = A, subscriptions = Ss} = R) -> R#pubsub_state{stateid = {binusr(U), Nidx}, items = [bin(I) || I<-Is], affiliation = A, subscriptions = [{S,bin(Sid)} || {S,Sid}<-Ss]} end). convert_list_nodes() -> convert_list_records( pubsub_node, record_info(fields, pubsub_node), fun(#pubsub_node{nodeid = {{U,_,_}, _}}) -> U; (#pubsub_node{nodeid = {H, _}}) -> H end, fun(#pubsub_node{nodeid = {H, N}, id = I, parents = Ps, type = T, owners = Os, options = Opts} = R) -> R#pubsub_node{nodeid = {binhost(H), bin(N)}, id = I, parents = [bin(P) || P<-Ps], type = bin(T), owners = [binusr(O) || O<-Os], options = Opts} end). convert_list_subscriptions() -> [convert_list_records( pubsub_subscription, record_info(fields, pubsub_subscription), fun(#pubsub_subscription{subid = I}) -> I end, fun(#pubsub_subscription{subid = I, options = Opts} = R) -> R#pubsub_subscription{subid = bin(I), options = Opts} end) || lists:member(pubsub_subscription, mnesia:system_info(tables))]. convert_list_lasts() -> convert_list_records( pubsub_last_item, record_info(fields, pubsub_last_item), fun(#pubsub_last_item{itemid = I}) -> I end, fun(#pubsub_last_item{itemid = I, nodeid = Nidx, creation = {C, CKey}, payload = Payload} = R) -> R#pubsub_last_item{itemid = bin(I), nodeid = Nidx, creation = {C, binusr(CKey)}, payload = fxml:to_xmlel(Payload)} end). %% internal tools convert_list_records(Tab, Fields, DetectFun, ConvertFun) -> case mnesia:table_info(Tab, attributes) of Fields -> convert_table_to_binary( Tab, Fields, set, DetectFun, ConvertFun); _ -> ?INFO_MSG("Recreating ~p table", [Tab]), mnesia:transform_table(Tab, ignore, Fields), convert_list_records(Tab, Fields, DetectFun, ConvertFun) end. binhost({U,S,R}) -> binusr({U,S,R}); binhost(L) -> bin(L). binusr({U,S,R}) -> {bin(U), bin(S), bin(R)}. bin(L) -> iolist_to_binary(L). %% The code should be updated to support new ejabberd_mnesia %% transform functions (i.e. need_transform/1 and transform/1) convert_table_to_binary(Tab, Fields, Type, DetectFun, ConvertFun) -> case is_table_still_list(Tab, DetectFun) of true -> ?INFO_MSG("Converting '~s' table from strings to binaries.", [Tab]), TmpTab = list_to_atom(atom_to_list(Tab) ++ "_tmp_table"), catch mnesia:delete_table(TmpTab), case ejabberd_mnesia:create(?MODULE, TmpTab, [{disc_only_copies, [node()]}, {type, Type}, {local_content, true}, {record_name, Tab}, {attributes, Fields}]) of {atomic, ok} -> mnesia:transform_table(Tab, ignore, Fields), case mnesia:transaction( fun() -> mnesia:write_lock_table(TmpTab), mnesia:foldl( fun(R, _) -> NewR = ConvertFun(R), mnesia:dirty_write(TmpTab, NewR) end, ok, Tab) end) of {atomic, ok} -> mnesia:clear_table(Tab), case mnesia:transaction( fun() -> mnesia:write_lock_table(Tab), mnesia:foldl( fun(R, _) -> mnesia:dirty_write(R) end, ok, TmpTab) end) of {atomic, ok} -> mnesia:delete_table(TmpTab); Err -> report_and_stop(Tab, Err) end; Err -> report_and_stop(Tab, Err) end; Err -> report_and_stop(Tab, Err) end; false -> ok end. is_table_still_list(Tab, DetectFun) -> is_table_still_list(Tab, DetectFun, mnesia:dirty_first(Tab)). is_table_still_list(_Tab, _DetectFun, '$end_of_table') -> false; is_table_still_list(Tab, DetectFun, Key) -> Rs = mnesia:dirty_read(Tab, Key), Res = lists:foldl(fun(_, true) -> true; (_, false) -> false; (R, _) -> case DetectFun(R) of '$next' -> '$next'; El -> is_list(El) end end, '$next', Rs), case Res of true -> true; false -> false; '$next' -> is_table_still_list(Tab, DetectFun, mnesia:dirty_next(Tab, Key)) end. report_and_stop(Tab, Err) -> ErrTxt = lists:flatten( io_lib:format( "Failed to convert '~s' table to binary: ~p", [Tab, Err])), ?CRITICAL_MSG(ErrTxt, []), timer:sleep(1000), halt(string:substr(ErrTxt, 1, 199)). ejabberd-18.01/src/mod_http_fileserver.erl0000644000232200023220000004604213225664356021210 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_http_fileserver.erl %%% Author : Massimiliano Mirra %%% Purpose : Simple file server plugin for embedded ejabberd web server %%% Created : %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_http_fileserver). -author('mmirra@process-one.net'). -behaviour(gen_mod). -behaviour(gen_server). %% gen_mod callbacks -export([start/2, stop/1, reload/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% request_handlers callbacks -export([process/2]). %% utility for other http modules -export([content_type/3]). -export([reopen_log/0, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). -include_lib("kernel/include/file.hrl"). -record(state, {host, docroot, accesslog, accesslogfd, directory_indices, custom_headers, default_content_type, content_types = [], user_access = none}). %% Response is {DataSize, Code, [{HeaderKey, HeaderValue}], Data} -define(HTTP_ERR_FILE_NOT_FOUND, {-1, 404, [], <<"Not found">>}). -define(REQUEST_AUTH_HEADERS, [{<<"WWW-Authenticate">>, <<"Basic realm=\"ejabberd\"">>}]). -define(HTTP_ERR_FORBIDDEN, {-1, 403, [], <<"Forbidden">>}). -define(HTTP_ERR_REQUEST_AUTH, {-1, 401, ?REQUEST_AUTH_HEADERS, <<"Unauthorized">>}). -define(HTTP_ERR_HOST_UNKNOWN, {-1, 410, [], <<"Host unknown">>}). -define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>). -define(DEFAULT_CONTENT_TYPES, [{<<".css">>, <<"text/css">>}, {<<".gif">>, <<"image/gif">>}, {<<".html">>, <<"text/html">>}, {<<".jar">>, <<"application/java-archive">>}, {<<".jpeg">>, <<"image/jpeg">>}, {<<".jpg">>, <<"image/jpeg">>}, {<<".js">>, <<"text/javascript">>}, {<<".png">>, <<"image/png">>}, {<<".svg">>, <<"image/svg+xml">>}, {<<".txt">>, <<"text/plain">>}, {<<".xml">>, <<"application/xml">>}, {<<".xpi">>, <<"application/x-xpinstall">>}, {<<".xul">>, <<"application/vnd.mozilla.xul+xml">>}]). %%==================================================================== %% gen_mod callbacks %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(Host, NewOpts, OldOpts) -> Proc = get_proc_name(Host), gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). depends(_Host, _Opts) -> []. %%==================================================================== %% gen_server callbacks %%==================================================================== %%-------------------------------------------------------------------- %% Function: init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- init([Host, Opts]) -> try initialize(Host, Opts) of State -> process_flag(trap_exit, true), {ok, State} catch throw:Reason -> {stop, Reason} end. initialize(Host, Opts) -> DocRoot = gen_mod:get_opt(docroot, Opts), check_docroot_defined(DocRoot, Host), DRInfo = check_docroot_exists(DocRoot), check_docroot_is_dir(DRInfo, DocRoot), check_docroot_is_readable(DRInfo, DocRoot), AccessLog = gen_mod:get_opt(accesslog, Opts), AccessLogFD = try_open_log(AccessLog, Host), DirectoryIndices = gen_mod:get_opt(directory_indices, Opts, []), CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []), DefaultContentType = gen_mod:get_opt(default_content_type, Opts, ?DEFAULT_CONTENT_TYPE), UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts, []), UserAccess = case UserAccess0 of [] -> none; _ -> dict:from_list(UserAccess0) end, ContentTypes = build_list_content_types( gen_mod:get_opt(content_types, Opts, []), ?DEFAULT_CONTENT_TYPES), ?DEBUG("known content types: ~s", [str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes], <<", ">>)]), #state{host = Host, accesslog = AccessLog, accesslogfd = AccessLogFD, docroot = DocRoot, directory_indices = DirectoryIndices, custom_headers = CustomHeaders, default_content_type = DefaultContentType, content_types = ContentTypes, user_access = UserAccess}. %% @spec (AdminCTs::[CT], Default::[CT]) -> [CT] %% where CT = {Extension::string(), Value} %% Value = string() | undefined %% @doc Return a unified list without duplicates. %% Elements of AdminCTs have more priority. %% If a CT is declared as 'undefined', then it is not included in the result. build_list_content_types(AdminCTsUnsorted, DefaultCTsUnsorted) -> AdminCTs = lists:ukeysort(1, AdminCTsUnsorted), DefaultCTs = lists:ukeysort(1, DefaultCTsUnsorted), CTsUnfiltered = lists:ukeymerge(1, AdminCTs, DefaultCTs), [{Extension, Value} || {Extension, Value} <- CTsUnfiltered, Value /= undefined]. check_docroot_defined(DocRoot, Host) -> case DocRoot of undefined -> throw({undefined_docroot_option, Host}); _ -> ok end. check_docroot_exists(DocRoot) -> case filelib:ensure_dir(filename:join(DocRoot, "foo")) of ok -> case file:read_file_info(DocRoot) of {error, Reason} -> throw({error_access_docroot, DocRoot, Reason}); {ok, FI} -> FI end; {error, Reason} -> throw({error_access_docroot, DocRoot, Reason}) end. check_docroot_is_dir(DRInfo, DocRoot) -> case DRInfo#file_info.type of directory -> ok; _ -> throw({docroot_not_directory, DocRoot}) end. check_docroot_is_readable(DRInfo, DocRoot) -> case DRInfo#file_info.access of read -> ok; read_write -> ok; _ -> throw({docroot_not_readable, DocRoot}) end. try_open_log(undefined, _Host) -> undefined; try_open_log(FN, _Host) -> FD = try open_log(FN) of FD1 -> FD1 catch throw:{cannot_open_accesslog, FN, Reason} -> ?ERROR_MSG("Cannot open access log file: ~p~nReason: ~p", [FN, Reason]), undefined end, ejabberd_hooks:add(reopen_log_hook, ?MODULE, reopen_log, 50), FD. %%-------------------------------------------------------------------- %% Function: handle_call(Request, From, State) -> {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({serve, LocalPath, Auth, RHeaders}, _From, State) -> IfModifiedSince = case find_header('If-Modified-Since', RHeaders, bad_date) of bad_date -> bad_date; Val -> httpd_util:convert_request_date(binary_to_list(Val)) end, Reply = serve(LocalPath, Auth, State#state.docroot, State#state.directory_indices, State#state.custom_headers, State#state.default_content_type, State#state.content_types, State#state.user_access, IfModifiedSince), {reply, Reply, State}; handle_call(_Request, _From, State) -> {reply, ok, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({add_to_log, FileSize, Code, Request}, State) -> add_to_log(State#state.accesslogfd, FileSize, Code, Request), {noreply, State}; handle_cast(reopen_log, State) -> FD2 = reopen_log(State#state.accesslog, State#state.accesslogfd), {noreply, State#state{accesslogfd = FD2}}; handle_cast({reload, Host, NewOpts, _OldOpts}, OldState) -> try initialize(Host, NewOpts) of NewState -> FD = reopen_log(NewState#state.accesslog, OldState#state.accesslogfd), {noreply, NewState#state{accesslogfd = FD}} catch throw:_ -> {noreply, OldState} end; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- terminate(_Reason, State) -> close_log(State#state.accesslogfd), %% TODO: unregister the hook gracefully %% ejabberd_hooks:delete(reopen_log_hook, State#state.host, ?MODULE, reopen_log, 50), ok. %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%==================================================================== %% request_handlers callbacks %%==================================================================== %% @spec (LocalPath, Request) -> {HTTPCode::integer(), [Header], Page::string()} %% @doc Handle an HTTP request. %% LocalPath is the part of the requested URL path that is "local to the module". %% Returns the page to be sent back to the client and/or HTTP status code. process(LocalPath, #request{host = Host, auth = Auth, headers = RHeaders} = Request) -> ?DEBUG("Requested ~p", [LocalPath]), try VHost = ejabberd_router:host_of_route(Host), {FileSize, Code, Headers, Contents} = gen_server:call(get_proc_name(VHost), {serve, LocalPath, Auth, RHeaders}), add_to_log(FileSize, Code, Request#request{host = VHost}), {Code, Headers, Contents} catch _:{Why, _} when Why == noproc; Why == invalid_domain; Why == unregistered_route -> ?DEBUG("Received an HTTP request with Host: ~s, " "but couldn't find the related " "ejabberd virtual host", [Host]), {FileSize1, Code1, Headers1, Contents1} = ?HTTP_ERR_HOST_UNKNOWN, add_to_log(FileSize1, Code1, Request#request{host = ?MYNAME}), {Code1, Headers1, Contents1} end. serve(LocalPath, Auth, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentType, ContentTypes, UserAccess, IfModifiedSince) -> CanProceed = case {UserAccess, Auth} of {none, _} -> true; {_, {User, Pass}} -> case dict:find(User, UserAccess) of {ok, Pass} -> true; _ -> false end; _ -> false end, case CanProceed of false -> ?HTTP_ERR_REQUEST_AUTH; true -> FileName = filename:join(filename:split(DocRoot) ++ LocalPath), case file:read_file_info(FileName) of {error, enoent} -> ?HTTP_ERR_FILE_NOT_FOUND; {error, enotdir} -> ?HTTP_ERR_FILE_NOT_FOUND; {error, eacces} -> ?HTTP_ERR_FORBIDDEN; {ok, #file_info{type = directory}} -> serve_index(FileName, DirectoryIndices, CustomHeaders, DefaultContentType, ContentTypes); {ok, #file_info{mtime = MTime} = FileInfo} -> case calendar:local_time_to_universal_time_dst(MTime) of [IfModifiedSince | _] -> serve_not_modified(FileInfo, FileName, CustomHeaders); _ -> serve_file(FileInfo, FileName, CustomHeaders, DefaultContentType, ContentTypes) end end; _ -> ?HTTP_ERR_FORBIDDEN end. %% Troll through the directory indices attempting to find one which %% works, if none can be found, return a 404. serve_index(_FileName, [], _CH, _DefaultContentType, _ContentTypes) -> ?HTTP_ERR_FILE_NOT_FOUND; serve_index(FileName, [Index | T], CH, DefaultContentType, ContentTypes) -> IndexFileName = filename:join([FileName] ++ [Index]), case file:read_file_info(IndexFileName) of {error, _Error} -> serve_index(FileName, T, CH, DefaultContentType, ContentTypes); {ok, #file_info{type = directory}} -> serve_index(FileName, T, CH, DefaultContentType, ContentTypes); {ok, FileInfo} -> serve_file(FileInfo, IndexFileName, CH, DefaultContentType, ContentTypes) end. serve_not_modified(FileInfo, FileName, CustomHeaders) -> ?DEBUG("Delivering not modified: ~s", [FileName]), {0, 304, [{<<"Server">>, <<"ejabberd">>}, {<<"Last-Modified">>, last_modified(FileInfo)} | CustomHeaders], <<>>}. %% Assume the file exists if we got this far and attempt to read it in %% and serve it up. serve_file(FileInfo, FileName, CustomHeaders, DefaultContentType, ContentTypes) -> ?DEBUG("Delivering: ~s", [FileName]), ContentType = content_type(FileName, DefaultContentType, ContentTypes), {ok, FileContents} = file:read_file(FileName), {FileInfo#file_info.size, 200, [{<<"Server">>, <<"ejabberd">>}, {<<"Last-Modified">>, last_modified(FileInfo)}, {<<"Content-Type">>, ContentType} | CustomHeaders], FileContents}. %%---------------------------------------------------------------------- %% Log file %%---------------------------------------------------------------------- open_log(FN) -> case file:open(FN, [append]) of {ok, FD} -> FD; {error, Reason} -> throw({cannot_open_accesslog, FN, Reason}) end. close_log(FD) -> file:close(FD). reopen_log(undefined, undefined) -> ok; reopen_log(FN, FD) -> close_log(FD), open_log(FN). reopen_log() -> lists:foreach( fun(Host) -> gen_server:cast(get_proc_name(Host), reopen_log) end, ?MYHOSTS). add_to_log(FileSize, Code, Request) -> gen_server:cast(get_proc_name(Request#request.host), {add_to_log, FileSize, Code, Request}). add_to_log(undefined, _FileSize, _Code, _Request) -> ok; add_to_log(File, FileSize, Code, Request) -> {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(), IP = ip_to_string(element(1, Request#request.ip)), Path = join(Request#request.path, "/"), Query = case stringify_query(Request#request.q) of <<"">> -> ""; String -> [$? | String] end, UserAgent = find_header('User-Agent', Request#request.headers, "-"), Referer = find_header('Referer', Request#request.headers, "-"), %% Pseudo Combined Apache log format: %% 127.0.0.1 - - [28/Mar/2007:18:41:55 +0200] "GET / HTTP/1.1" 302 303 "-" "tsung" %% TODO some fields are harcoded/missing: %% The date/time integers should have always 2 digits. For example day "7" should be "07" %% Month should be 3*letter, not integer 1..12 %% Missing time zone = (`+' | `-') 4*digit %% Missing protocol version: HTTP/1.1 %% For reference: http://httpd.apache.org/docs/2.2/logs.html io:format(File, "~s - - [~p/~p/~p:~p:~p:~p] \"~s /~s~s\" ~p ~p ~p ~p~n", [IP, Day, Month, Year, Hour, Minute, Second, Request#request.method, Path, Query, Code, FileSize, Referer, UserAgent]). stringify_query(Q) -> stringify_query(Q, []). stringify_query([], Res) -> join(lists:reverse(Res), "&"); stringify_query([{nokey, _B} | Q], Res) -> stringify_query(Q, Res); stringify_query([{A, B} | Q], Res) -> stringify_query(Q, [join([A,B], "=") | Res]). find_header(Header, Headers, Default) -> case lists:keysearch(Header, 1, Headers) of {value, {_, Value}} -> Value; false -> Default end. %%---------------------------------------------------------------------- %% Utilities %%---------------------------------------------------------------------- get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?MODULE). join([], _) -> <<"">>; join([E], _) -> E; join([H | T], Separator) -> [H2 | T2] = case is_binary(H) of true -> [binary_to_list(I)||I<-[H|T]]; false -> [H | T] end, Res=lists:foldl(fun(E, Acc) -> lists:concat([Acc, Separator, E]) end, H2, T2), case is_binary(H) of true -> list_to_binary(Res); false -> Res end. content_type(Filename, DefaultContentType, ContentTypes) -> Extension = str:to_lower(filename:extension(Filename)), case lists:keysearch(Extension, 1, ContentTypes) of {value, {_, ContentType}} -> ContentType; false -> DefaultContentType end. last_modified(FileInfo) -> Then = FileInfo#file_info.mtime, httpd_util:rfc1123_date(Then). %% Convert IP address tuple to string representation. Accepts either %% IPv4 or IPv6 address tuples. ip_to_string(Address) when size(Address) == 4 -> join(tuple_to_list(Address), "."); ip_to_string(Address) when size(Address) == 8 -> Parts = lists:map(fun (Int) -> io_lib:format("~.16B", [Int]) end, tuple_to_list(Address)), string:to_lower(lists:flatten(join(Parts, ":"))). mod_opt_type(accesslog) -> fun iolist_to_binary/1; mod_opt_type(content_types) -> fun(L) when is_list(L) -> lists:map( fun({K, V}) -> {iolist_to_binary(K), iolist_to_binary(V)} end, L) end; mod_opt_type(custom_headers) -> fun (L) when is_list(L) -> L end; mod_opt_type(default_content_type) -> fun iolist_to_binary/1; mod_opt_type(directory_indices) -> fun (L) when is_list(L) -> L end; mod_opt_type(docroot) -> fun (A) -> A end; mod_opt_type(must_authenticate_with) -> fun (L) when is_list(L) -> lists:map(fun(UP) when is_binary(UP) -> [K, V] = binary:split(UP, <<":">>), {K, V} end, L) end; mod_opt_type(_) -> [accesslog, content_types, custom_headers, default_content_type, directory_indices, docroot, must_authenticate_with]. ejabberd-18.01/src/mod_shared_roster_riak.erl0000644000232200023220000001311313225664356021646 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_shared_roster_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_shared_roster_riak). -behaviour(mod_shared_roster). %% API -export([init/2, list_groups/1, groups_with_opts/1, create_group/3, delete_group/2, get_group_opts/2, set_group_opts/3, get_user_groups/2, get_group_explicit_users/2, get_user_displayed_groups/3, is_user_in_group/3, add_user_to_group/3, remove_user_from_group/3, import/3]). -include("mod_roster.hrl"). -include("mod_shared_roster.hrl"). -include("xmpp.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. list_groups(Host) -> case ejabberd_riak:get_keys_by_index(sr_group, <<"host">>, Host) of {ok, Gs} -> [G || {G, _} <- Gs]; _ -> [] end. groups_with_opts(Host) -> case ejabberd_riak:get_by_index(sr_group, sr_group_schema(), <<"host">>, Host) of {ok, Rs} -> [{G, O} || #sr_group{group_host = {G, _}, opts = O} <- Rs]; _ -> [] end. create_group(Host, Group, Opts) -> {atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host}, opts = Opts}, sr_group_schema(), [{'2i', [{<<"host">>, Host}]}])}. delete_group(Host, Group) -> try ok = ejabberd_riak:delete(sr_group, {Group, Host}), ok = ejabberd_riak:delete_by_index(sr_user, <<"group_host">>, {Group, Host}), {atomic, ok} catch _:{badmatch, Err} -> {atomic, Err} end. get_group_opts(Host, Group) -> case ejabberd_riak:get(sr_group, sr_group_schema(), {Group, Host}) of {ok, #sr_group{opts = Opts}} -> Opts; _ -> error end. set_group_opts(Host, Group, Opts) -> {atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host}, opts = Opts}, sr_group_schema(), [{'2i', [{<<"host">>, Host}]}])}. get_user_groups(US, Host) -> case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of {ok, Rs} -> [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; _ -> [] end. get_group_explicit_users(Host, Group) -> case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"group_host">>, {Group, Host}) of {ok, Rs} -> [R#sr_user.us || R <- Rs]; _ -> [] end. get_user_displayed_groups(LUser, LServer, GroupsOpts) -> case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, {LUser, LServer}) of {ok, Rs} -> [{Group, proplists:get_value(Group, GroupsOpts, [])} || #sr_user{group_host = {Group, _}} <- Rs]; _ -> [] end. is_user_in_group(US, Group, Host) -> case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of {ok, Rs} -> lists:any( fun(#sr_user{group_host = {G, H}}) -> (Group == G) and (Host == H) end, Rs); _Err -> false end. add_user_to_group(Host, US, Group) -> {atomic, ejabberd_riak:put( #sr_user{us = US, group_host = {Group, Host}}, sr_user_schema(), [{i, {US, {Group, Host}}}, {'2i', [{<<"us">>, US}, {<<"group_host">>, {Group, Host}}]}])}. remove_user_from_group(Host, US, Group) -> {atomic, ejabberd_riak:delete(sr_group, {US, {Group, Host}})}. import(LServer, <<"sr_group">>, [Group, SOpts, _TimeStamp]) -> G = #sr_group{group_host = {Group, LServer}, opts = ejabberd_sql:decode_term(SOpts)}, ejabberd_riak:put(G, sr_group_schema(), [{'2i', [{<<"host">>, LServer}]}]); import(LServer, <<"sr_user">>, [SJID, Group|_]) -> #jid{luser = U, lserver = S} = jid:decode(SJID), User = #sr_user{us = {U, S}, group_host = {Group, LServer}}, ejabberd_riak:put(User, sr_user_schema(), [{i, {{U, S}, {Group, LServer}}}, {'2i', [{<<"us">>, {U, S}}, {<<"group_host">>, {Group, LServer}}]}]). %%%=================================================================== %%% Internal functions %%%=================================================================== sr_group_schema() -> {record_info(fields, sr_group), #sr_group{}}. sr_user_schema() -> {record_info(fields, sr_user), #sr_user{}}. ejabberd-18.01/src/mod_muc_log.erl0000644000232200023220000007702713225664356017437 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_muc_log.erl %%% Author : Badlop@process-one.net %%% Purpose : MUC room logging %%% Created : 12 Mar 2006 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_muc_log). -protocol({xep, 334, '0.2'}). -author('badlop@process-one.net'). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, transform_module_options/1, check_access_log/2, add_to_log/5]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_muc_room.hrl"). -define(T(Text), translate:translate(Lang, Text)). -record(room, {jid, title, subject, subject_author, config}). -define(PLAINTEXT_CO, <<"ZZCZZ">>). -define(PLAINTEXT_IN, <<"ZZIZZ">>). -define(PLAINTEXT_OUT, <<"ZZOZZ">>). -record(logstate, {host, out_dir, dir_type, dir_name, file_format, file_permissions, css_file, access, lang, timezone, spam_prevention, top_link}). %%==================================================================== %% API %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(Host, NewOpts, _OldOpts) -> Proc = get_proc_name(Host), gen_server:cast(Proc, {reload, NewOpts}). add_to_log(Host, Type, Data, Room, Opts) -> gen_server:cast(get_proc_name(Host), {add_to_log, Type, Data, Room, Opts}). check_access_log(Host, From) -> case catch gen_server:call(get_proc_name(Host), {check_access_log, Host, From}) of {'EXIT', _Error} -> deny; Res -> Res end. transform_module_options(Opts) -> lists:map( fun({top_link, {S1, S2}}) -> {top_link, [{S1, S2}]}; (Opt) -> Opt end, Opts). depends(_Host, _Opts) -> [{mod_muc, hard}]. %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), {ok, init_state(Host, Opts)}. handle_call({check_access_log, ServerHost, FromJID}, _From, State) -> Reply = acl:match_rule(ServerHost, State#logstate.access, FromJID), {reply, Reply, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}. handle_cast({reload, Opts}, #logstate{host = Host}) -> {noreply, init_state(Host, Opts)}; handle_cast({add_to_log, Type, Data, Room, Opts}, State) -> case catch add_to_log2(Type, Data, Room, Opts, State) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); _ -> ok end, {noreply, State}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- init_state(Host, Opts) -> OutDir = gen_mod:get_opt(outdir, Opts, <<"www/muc">>), DirType = gen_mod:get_opt(dirtype, Opts, subdirs), DirName = gen_mod:get_opt(dirname, Opts, room_jid), FileFormat = gen_mod:get_opt(file_format, Opts, html), FilePermissions = gen_mod:get_opt(file_permissions, Opts, {644, 33}), CSSFile = gen_mod:get_opt(cssfile, Opts, false), AccessLog = gen_mod:get_opt(access_log, Opts, muc_admin), Timezone = gen_mod:get_opt(timezone, Opts, local), Top_link = gen_mod:get_opt(top_link, Opts, {<<"/">>, <<"Home">>}), NoFollow = gen_mod:get_opt(spam_prevention, Opts, true), Lang = ejabberd_config:get_lang(Host), #logstate{host = Host, out_dir = OutDir, dir_type = DirType, dir_name = DirName, file_format = FileFormat, css_file = CSSFile, file_permissions = FilePermissions, access = AccessLog, lang = Lang, timezone = Timezone, spam_prevention = NoFollow, top_link = Top_link}. add_to_log2(text, {Nick, Packet}, Room, Opts, State) -> case has_no_permanent_store_hint(Packet) of false -> case {Packet#message.subject, Packet#message.body} of {[], []} -> ok; {[], Body} -> Message = {body, xmpp:get_text(Body)}, add_message_to_log(Nick, Message, Room, Opts, State); {Subj, _} -> Message = {subject, xmpp:get_text(Subj)}, add_message_to_log(Nick, Message, Room, Opts, State) end; true -> ok end; add_to_log2(roomconfig_change, _Occupants, Room, Opts, State) -> add_message_to_log(<<"">>, roomconfig_change, Room, Opts, State); add_to_log2(roomconfig_change_enabledlogging, Occupants, Room, Opts, State) -> add_message_to_log(<<"">>, {roomconfig_change, Occupants}, Room, Opts, State); add_to_log2(room_existence, NewStatus, Room, Opts, State) -> add_message_to_log(<<"">>, {room_existence, NewStatus}, Room, Opts, State); add_to_log2(nickchange, {OldNick, NewNick}, Room, Opts, State) -> add_message_to_log(NewNick, {nickchange, OldNick}, Room, Opts, State); add_to_log2(join, Nick, Room, Opts, State) -> add_message_to_log(Nick, join, Room, Opts, State); add_to_log2(leave, {Nick, Reason}, Room, Opts, State) -> case Reason of <<"">> -> add_message_to_log(Nick, leave, Room, Opts, State); _ -> add_message_to_log(Nick, {leave, Reason}, Room, Opts, State) end; add_to_log2(kickban, {Nick, Reason, Code}, Room, Opts, State) -> add_message_to_log(Nick, {kickban, Code, Reason}, Room, Opts, State). %%---------------------------------------------------------------------- %% Core build_filename_string(TimeStamp, OutDir, RoomJID, DirType, DirName, FileFormat) -> {{Year, Month, Day}, _Time} = TimeStamp, {Dir, Filename, Rel} = case DirType of subdirs -> SYear = (str:format("~4..0w", [Year])), SMonth = (str:format("~2..0w", [Month])), SDay = (str:format("~2..0w", [Day])), {fjoin([SYear, SMonth]), SDay, <<"../..">>}; plain -> Date = (str:format("~4..0w-~2..0w-~2..0w", [Year, Month, Day])), {<<"">>, Date, <<".">>} end, RoomString = case DirName of room_jid -> RoomJID; room_name -> get_room_name(RoomJID) end, Extension = case FileFormat of html -> <<".html">>; plaintext -> <<".txt">> end, Fd = fjoin([OutDir, RoomString, Dir]), Fn = fjoin([Fd, <>]), Fnrel = fjoin([Rel, Dir, <>]), {Fd, Fn, Fnrel}. get_room_name(RoomJID) -> JID = jid:decode(RoomJID), JID#jid.user. %% calculate day before get_timestamp_daydiff(TimeStamp, Daydiff) -> {Date1, HMS} = TimeStamp, Date2 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(Date1) + Daydiff), {Date2, HMS}. %% Try to close the previous day log, if it exists close_previous_log(Fn, Images_dir, FileFormat) -> case file:read_file_info(Fn) of {ok, _} -> {ok, F} = file:open(Fn, [append]), write_last_lines(F, Images_dir, FileFormat), file:close(F); _ -> ok end. write_last_lines(_, _, plaintext) -> ok; write_last_lines(F, Images_dir, _FileFormat) -> fw(F, <<"
">>), fw(F, <<" \"Powered">>, [Images_dir]), fw(F, <<" \"Powered">>, [Images_dir]), fw(F, <<"">>), fw(F, <<" ">>, [Images_dir]), fw(F, <<" \"Valid">>, [Images_dir]), fw(F, <<"
">>). set_filemode(Fn, {FileMode, FileGroup}) -> ok = file:change_mode(Fn, list_to_integer(integer_to_list(FileMode), 8)), ok = file:change_group(Fn, FileGroup). htmlize_nick(Nick1, html) -> htmlize(<<"<", Nick1/binary, ">">>, html); htmlize_nick(Nick1, plaintext) -> htmlize(<>, plaintext). add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> #logstate{out_dir = OutDir, dir_type = DirType, dir_name = DirName, file_format = FileFormat, file_permissions = FilePermissions, css_file = CSSFile, lang = Lang, timezone = Timezone, spam_prevention = NoFollow, top_link = TopLink} = State, Room = get_room_info(RoomJID, Opts), Nick = htmlize(Nick1, FileFormat), Nick2 = htmlize_nick(Nick1, FileFormat), Now = p1_time_compat:timestamp(), TimeStamp = case Timezone of local -> calendar:now_to_local_time(Now); universal -> calendar:now_to_universal_time(Now) end, {Fd, Fn, _Dir} = build_filename_string(TimeStamp, OutDir, Room#room.jid, DirType, DirName, FileFormat), {Date, Time} = TimeStamp, case file:read_file_info(Fn) of {ok, _} -> {ok, F} = file:open(Fn, [append]); {error, enoent} -> make_dir_rec(Fd), {ok, F} = file:open(Fn, [append]), catch set_filemode(Fn, FilePermissions), Datestring = get_dateweek(Date, Lang), TimeStampYesterday = get_timestamp_daydiff(TimeStamp, -1), {_FdYesterday, FnYesterday, DatePrev} = build_filename_string(TimeStampYesterday, OutDir, Room#room.jid, DirType, DirName, FileFormat), TimeStampTomorrow = get_timestamp_daydiff(TimeStamp, 1), {_FdTomorrow, _FnTomorrow, DateNext} = build_filename_string(TimeStampTomorrow, OutDir, Room#room.jid, DirType, DirName, FileFormat), HourOffset = calc_hour_offset(TimeStamp), put_header(F, Room, Datestring, CSSFile, Lang, HourOffset, DatePrev, DateNext, TopLink, FileFormat), Images_dir = fjoin([OutDir, <<"images">>]), file:make_dir(Images_dir), create_image_files(Images_dir), Images_url = case DirType of subdirs -> <<"../../../images">>; plain -> <<"../images">> end, close_previous_log(FnYesterday, Images_url, FileFormat) end, Text = case Message of roomconfig_change -> RoomConfig = roomconfig_to_string(Room#room.config, Lang, FileFormat), put_room_config(F, RoomConfig, Lang, FileFormat), io_lib:format("~s
", [?T(<<"Chatroom configuration modified">>)]); {roomconfig_change, Occupants} -> RoomConfig = roomconfig_to_string(Room#room.config, Lang, FileFormat), put_room_config(F, RoomConfig, Lang, FileFormat), RoomOccupants = roomoccupants_to_string(Occupants, FileFormat), put_room_occupants(F, RoomOccupants, Lang, FileFormat), io_lib:format("~s
", [?T(<<"Chatroom configuration modified">>)]); join -> io_lib:format("~s ~s
", [Nick, ?T(<<"joins the room">>)]); leave -> io_lib:format("~s ~s
", [Nick, ?T(<<"leaves the room">>)]); {leave, Reason} -> io_lib:format("~s ~s: ~s
", [Nick, ?T(<<"leaves the room">>), htmlize(Reason, NoFollow, FileFormat)]); {kickban, <<"301">>, <<"">>} -> io_lib:format("~s ~s
", [Nick, ?T(<<"has been banned">>)]); {kickban, <<"301">>, Reason} -> io_lib:format("~s ~s: ~s
", [Nick, ?T(<<"has been banned">>), htmlize(Reason, FileFormat)]); {kickban, <<"307">>, <<"">>} -> io_lib:format("~s ~s
", [Nick, ?T(<<"has been kicked">>)]); {kickban, <<"307">>, Reason} -> io_lib:format("~s ~s: ~s
", [Nick, ?T(<<"has been kicked">>), htmlize(Reason, FileFormat)]); {kickban, <<"321">>, <<"">>} -> io_lib:format("~s ~s
", [Nick, ?T(<<"has been kicked because of an affiliation " "change">>)]); {kickban, <<"322">>, <<"">>} -> io_lib:format("~s ~s
", [Nick, ?T(<<"has been kicked because the room has " "been changed to members-only">>)]); {kickban, <<"332">>, <<"">>} -> io_lib:format("~s ~s
", [Nick, ?T(<<"has been kicked because of a system " "shutdown">>)]); {nickchange, OldNick} -> io_lib:format("~s ~s ~s
", [htmlize(OldNick, FileFormat), ?T(<<"is now known as">>), Nick]); {subject, T} -> io_lib:format("~s~s~s
", [Nick, ?T(<<" has set the subject to: ">>), htmlize(T, NoFollow, FileFormat)]); {body, T} -> case {ejabberd_regexp:run(T, <<"^/me ">>), Nick} of {_, <<"">>} -> io_lib:format("~s
", [htmlize(T, NoFollow, FileFormat)]); {match, _} -> io_lib:format("~s ~s
", [Nick, str:substr(htmlize(T, FileFormat), 5)]); {nomatch, _} -> io_lib:format("~s ~s
", [Nick2, htmlize(T, NoFollow, FileFormat)]) end; {room_existence, RoomNewExistence} -> io_lib:format("~s
", [get_room_existence_string(RoomNewExistence, Lang)]) end, {Hour, Minute, Second} = Time, STime = io_lib:format("~2..0w:~2..0w:~2..0w", [Hour, Minute, Second]), {_, _, Microsecs} = Now, STimeUnique = io_lib:format("~s.~w", [STime, Microsecs]), catch fw(F, list_to_binary( io_lib:format("[~s] ", [STimeUnique, STimeUnique, STimeUnique, STime]) ++ Text), FileFormat), file:close(F), ok. %%---------------------------------------------------------------------- %% Utilities get_room_existence_string(created, Lang) -> ?T(<<"Chatroom is created">>); get_room_existence_string(destroyed, Lang) -> ?T(<<"Chatroom is destroyed">>); get_room_existence_string(started, Lang) -> ?T(<<"Chatroom is started">>); get_room_existence_string(stopped, Lang) -> ?T(<<"Chatroom is stopped">>). get_dateweek(Date, Lang) -> Weekday = case calendar:day_of_the_week(Date) of 1 -> ?T(<<"Monday">>); 2 -> ?T(<<"Tuesday">>); 3 -> ?T(<<"Wednesday">>); 4 -> ?T(<<"Thursday">>); 5 -> ?T(<<"Friday">>); 6 -> ?T(<<"Saturday">>); 7 -> ?T(<<"Sunday">>) end, {Y, M, D} = Date, Month = case M of 1 -> ?T(<<"January">>); 2 -> ?T(<<"February">>); 3 -> ?T(<<"March">>); 4 -> ?T(<<"April">>); 5 -> ?T(<<"May">>); 6 -> ?T(<<"June">>); 7 -> ?T(<<"July">>); 8 -> ?T(<<"August">>); 9 -> ?T(<<"September">>); 10 -> ?T(<<"October">>); 11 -> ?T(<<"November">>); 12 -> ?T(<<"December">>) end, list_to_binary( case Lang of <<"en">> -> io_lib:format("~s, ~s ~w, ~w", [Weekday, Month, D, Y]); <<"es">> -> io_lib:format("~s ~w de ~s de ~w", [Weekday, D, Month, Y]); _ -> io_lib:format("~s, ~w ~s ~w", [Weekday, D, Month, Y]) end). make_dir_rec(Dir) -> filelib:ensure_dir(<>). %% {ok, F1}=file:open("valid-xhtml10.png", [read]). %% {ok, F1b}=file:read(F1, 1000000). %% c("../../ejabberd/src/jlib.erl"). %% base64:encode(F1b). create_image_files(Images_dir) -> Filenames = [<<"powered-by-ejabberd.png">>, <<"powered-by-erlang.png">>, <<"valid-xhtml10.png">>, <<"vcss.png">>], lists:foreach( fun(Filename) -> Src = filename:join([misc:img_dir(), Filename]), Dst = fjoin([Images_dir, Filename]), case file:copy(Src, Dst) of {ok, _} -> ok; {error, Why} -> ?ERROR_MSG("Failed to copy ~s to ~s", [Src, Dst, file:format_error(Why)]) end end, Filenames). fw(F, S) -> fw(F, S, [], html). fw(F, S, O) when is_list(O) -> fw(F, S, O, html); fw(F, S, FileFormat) when is_atom(FileFormat) -> fw(F, S, [], FileFormat). fw(F, S, O, FileFormat) -> S1 = (str:format(binary_to_list(S) ++ "~n", O)), S2 = case FileFormat of html -> S1; plaintext -> S1a = ejabberd_regexp:greplace(S1, <<"<[^<^>]*>">>, <<"">>), S1x = ejabberd_regexp:greplace(S1a, ?PLAINTEXT_CO, <<"~~">>), S1y = ejabberd_regexp:greplace(S1x, ?PLAINTEXT_IN, <<"<">>), ejabberd_regexp:greplace(S1y, ?PLAINTEXT_OUT, <<">">>) end, file:write(F, S2). put_header(_, _, _, _, _, _, _, _, _, plaintext) -> ok; put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_link, FileFormat) -> fw(F, <<"">>), fw(F, <<"">>, [Lang, Lang]), fw(F, <<"">>), fw(F, <<"">>), fw(F, <<"~s - ~s">>, [htmlize(Room#room.title), Date]), put_header_css(F, CSSFile), put_header_script(F), fw(F, <<"">>), fw(F, <<"">>), {Top_url, Top_text} = Top_link, fw(F, <<"">>, [Top_url, Top_text]), fw(F, <<"
~s
">>, [htmlize(Room#room.title)]), fw(F, <<"~s" "">>, [Room#room.jid, Room#room.jid]), fw(F, <<"
~s" "< " "^ >">>, [Date, Date_prev, Date_next]), case {htmlize(Room#room.subject_author), htmlize(Room#room.subject)} of {<<"">>, <<"">>} -> ok; {SuA, Su} -> fw(F, <<"
~s~s~s
">>, [SuA, ?T(<<" has set the subject to: ">>), Su]) end, RoomConfig = roomconfig_to_string(Room#room.config, Lang, FileFormat), put_room_config(F, RoomConfig, Lang, FileFormat), Occupants = get_room_occupants(Room#room.jid), RoomOccupants = roomoccupants_to_string(Occupants, FileFormat), put_room_occupants(F, RoomOccupants, Lang, FileFormat), Time_offset_str = case Hour_offset < 0 of true -> io_lib:format("~p", [Hour_offset]); false -> io_lib:format("+~p", [Hour_offset]) end, fw(F, <<"
GMT~s
">>, [Time_offset_str]). put_header_css(F, false) -> fw(F, <<"">>); put_header_css(F, CSSFile) -> fw(F, <<"">>, [CSSFile]). put_header_script(F) -> fw(F, <<"">>). put_room_config(_F, _RoomConfig, _Lang, plaintext) -> ok; put_room_config(F, RoomConfig, Lang, _FileFormat) -> {_, Now2, _} = p1_time_compat:timestamp(), fw(F, <<"
">>), fw(F, <<"
~s
">>, [Now2, ?T(<<"Room Configuration">>)]), fw(F, <<"

~s
">>, [Now2, RoomConfig]), fw(F, <<"
">>). put_room_occupants(_F, _RoomOccupants, _Lang, plaintext) -> ok; put_room_occupants(F, RoomOccupants, Lang, _FileFormat) -> {_, Now2, _} = p1_time_compat:timestamp(), %% htmlize %% The default behaviour is to ignore the nofollow spam prevention on links %% (NoFollow=false) fw(F, <<"
">>), fw(F, <<"
~s
">>, [Now2, ?T(<<"Room Occupants">>)]), fw(F, <<"

~s
">>, [Now2, RoomOccupants]), fw(F, <<"
">>). htmlize(S1) -> htmlize(S1, html). htmlize(S1, plaintext) -> ejabberd_regexp:greplace(S1, <<"~">>, ?PLAINTEXT_CO); htmlize(S1, FileFormat) -> htmlize(S1, false, FileFormat). %% The NoFollow parameter tell if the spam prevention should be applied to the link found %% true means 'apply nofollow on links'. htmlize(S0, _NoFollow, plaintext) -> S1 = ejabberd_regexp:greplace(S0, <<"~">>, ?PLAINTEXT_CO), S1x = ejabberd_regexp:greplace(S1, <<"<">>, ?PLAINTEXT_IN), ejabberd_regexp:greplace(S1x, <<">">>, ?PLAINTEXT_OUT); htmlize(S1, NoFollow, _FileFormat) -> S2_list = str:tokens(S1, <<"\n">>), lists:foldl(fun (Si, Res) -> Si2 = htmlize2(Si, NoFollow), case Res of <<"">> -> Si2; _ -> <", Si2/binary>> end end, <<"">>, S2_list). htmlize2(S1, NoFollow) -> %% Regexp link %% Add the nofollow rel attribute when required S2 = ejabberd_regexp:greplace(S1, <<"\\&">>, <<"\\&">>), S3 = ejabberd_regexp:greplace(S2, <<"<">>, <<"\\<">>), S4 = ejabberd_regexp:greplace(S3, <<">">>, <<"\\>">>), S5 = ejabberd_regexp:greplace(S4, <<"((http|https|ftp)://|(mailto|xmpp):)[^] " ")'\"}]+">>, link_regexp(NoFollow)), S6 = ejabberd_regexp:greplace(S5, <<" ">>, <<"\\ \\ ">>), S7 = ejabberd_regexp:greplace(S6, <<"\\t">>, <<"\\ \\ \\ \\ ">>), S8 = ejabberd_regexp:greplace(S7, <<"~">>, <<"~~">>), ejabberd_regexp:greplace(S8, <<226, 128, 174>>, <<"[RLO]">>). link_regexp(false) -> <<"&">>; link_regexp(true) -> <<"&">>. get_room_info(RoomJID, Opts) -> Title = case lists:keysearch(title, 1, Opts) of {value, {_, T}} -> T; false -> <<"">> end, Subject = case lists:keysearch(subject, 1, Opts) of {value, {_, S}} -> xmpp:get_text(S); false -> <<"">> end, SubjectAuthor = case lists:keysearch(subject_author, 1, Opts) of {value, {_, SA}} -> SA; false -> <<"">> end, #room{jid = jid:encode(RoomJID), title = Title, subject = Subject, subject_author = SubjectAuthor, config = Opts}. roomconfig_to_string(Options, Lang, FileFormat) -> Title = case lists:keysearch(title, 1, Options) of {value, Tuple} -> [Tuple]; false -> [] end, Os1 = lists:keydelete(title, 1, Options), Os2 = lists:sort(Os1), Options2 = Title ++ Os2, lists:foldl(fun ({Opt, Val}, R) -> case get_roomconfig_text(Opt, Lang) of undefined -> R; OptText -> R2 = case Val of false -> <<"
", OptText/binary, "
">>; true -> <<"
", OptText/binary, "
">>; <<"">> -> <<"
", OptText/binary, "
">>; T -> case Opt of password -> <<"
", OptText/binary, "
">>; max_users -> <<"
", OptText/binary, ": \"", (htmlize(integer_to_binary(T), FileFormat))/binary, "\"
">>; title -> <<"
", OptText/binary, ": \"", (htmlize(T, FileFormat))/binary, "\"
">>; description -> <<"
", OptText/binary, ": \"", (htmlize(T, FileFormat))/binary, "\"
">>; allow_private_messages_from_visitors -> <<"
", OptText/binary, ": \"", (htmlize(?T(misc:atom_to_binary(T)), FileFormat))/binary, "\"
">>; _ -> <<"\"", T/binary, "\"">> end end, <> end end, <<"">>, Options2). get_roomconfig_text(title, Lang) -> ?T(<<"Room title">>); get_roomconfig_text(persistent, Lang) -> ?T(<<"Make room persistent">>); get_roomconfig_text(public, Lang) -> ?T(<<"Make room public searchable">>); get_roomconfig_text(public_list, Lang) -> ?T(<<"Make participants list public">>); get_roomconfig_text(password_protected, Lang) -> ?T(<<"Make room password protected">>); get_roomconfig_text(password, Lang) -> ?T(<<"Password">>); get_roomconfig_text(anonymous, Lang) -> ?T(<<"This room is not anonymous">>); get_roomconfig_text(members_only, Lang) -> ?T(<<"Make room members-only">>); get_roomconfig_text(moderated, Lang) -> ?T(<<"Make room moderated">>); get_roomconfig_text(members_by_default, Lang) -> ?T(<<"Default users as participants">>); get_roomconfig_text(allow_change_subj, Lang) -> ?T(<<"Allow users to change the subject">>); get_roomconfig_text(allow_private_messages, Lang) -> ?T(<<"Allow users to send private messages">>); get_roomconfig_text(allow_private_messages_from_visitors, Lang) -> ?T(<<"Allow visitors to send private messages to">>); get_roomconfig_text(allow_query_users, Lang) -> ?T(<<"Allow users to query other users">>); get_roomconfig_text(allow_user_invites, Lang) -> ?T(<<"Allow users to send invites">>); get_roomconfig_text(logging, Lang) -> ?T(<<"Enable logging">>); get_roomconfig_text(allow_visitor_nickchange, Lang) -> ?T(<<"Allow visitors to change nickname">>); get_roomconfig_text(allow_visitor_status, Lang) -> ?T(<<"Allow visitors to send status text in " "presence updates">>); get_roomconfig_text(captcha_protected, Lang) -> ?T(<<"Make room CAPTCHA protected">>); get_roomconfig_text(description, Lang) -> ?T(<<"Room description">>); %% get_roomconfig_text(subject, Lang) -> "Subject"; %% get_roomconfig_text(subject_author, Lang) -> "Subject author"; get_roomconfig_text(max_users, Lang) -> ?T(<<"Maximum Number of Occupants">>); get_roomconfig_text(_, _) -> undefined. %% Users = [{JID, Nick, Role}] roomoccupants_to_string(Users, _FileFormat) -> Res = [role_users_to_string(RoleS, Users1) || {RoleS, Users1} <- group_by_role(Users), Users1 /= []], iolist_to_binary([<<"
">>, Res, <<"
">>]). group_by_role(Users) -> {Ms, Ps, Vs, Ns} = lists:foldl(fun ({JID, Nick, moderator}, {Mod, Par, Vis, Non}) -> {[{JID, Nick}] ++ Mod, Par, Vis, Non}; ({JID, Nick, participant}, {Mod, Par, Vis, Non}) -> {Mod, [{JID, Nick}] ++ Par, Vis, Non}; ({JID, Nick, visitor}, {Mod, Par, Vis, Non}) -> {Mod, Par, [{JID, Nick}] ++ Vis, Non}; ({JID, Nick, none}, {Mod, Par, Vis, Non}) -> {Mod, Par, Vis, [{JID, Nick}] ++ Non} end, {[], [], [], []}, Users), case Ms of [] -> []; _ -> [{<<"Moderator">>, Ms}] end ++ case Ms of [] -> []; _ -> [{<<"Participant">>, Ps}] end ++ case Ms of [] -> []; _ -> [{<<"Visitor">>, Vs}] end ++ case Ms of [] -> []; _ -> [{<<"None">>, Ns}] end. role_users_to_string(RoleS, Users) -> SortedUsers = lists:keysort(2, Users), UsersString = << <">> || {_JID, Nick} <- SortedUsers >>, <>. get_room_occupants(RoomJIDString) -> RoomJID = jid:decode(RoomJIDString), RoomName = RoomJID#jid.luser, MucService = RoomJID#jid.lserver, StateData = get_room_state(RoomName, MucService), [{U#user.jid, U#user.nick, U#user.role} || {_, U} <- (?DICT):to_list(StateData#state.users)]. -spec get_room_state(binary(), binary()) -> mod_muc_room:state(). get_room_state(RoomName, MucService) -> case mod_muc:find_online_room(RoomName, MucService) of {ok, RoomPid} -> get_room_state(RoomPid); error -> #state{} end. -spec get_room_state(pid()) -> mod_muc_room:state(). get_room_state(RoomPid) -> {ok, R} = p1_fsm:sync_send_all_state_event(RoomPid, get_state), R. get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?MODULE). calc_hour_offset(TimeHere) -> TimeZero = calendar:universal_time(), TimeHereHour = calendar:datetime_to_gregorian_seconds(TimeHere) div 3600, TimeZeroHour = calendar:datetime_to_gregorian_seconds(TimeZero) div 3600, TimeHereHour - TimeZeroHour. fjoin(FileList) -> list_to_binary(filename:join([binary_to_list(File) || File <- FileList])). has_no_permanent_store_hint(Packet) -> xmpp:has_subtag(Packet, #hint{type = 'no-store'}) orelse xmpp:has_subtag(Packet, #hint{type = 'no-storage'}) orelse xmpp:has_subtag(Packet, #hint{type = 'no-permanent-store'}) orelse xmpp:has_subtag(Packet, #hint{type = 'no-permanent-storage'}). mod_opt_type(access_log) -> fun acl:access_rules_validator/1; mod_opt_type(cssfile) -> fun misc:try_read_file/1; mod_opt_type(dirname) -> fun (room_jid) -> room_jid; (room_name) -> room_name end; mod_opt_type(dirtype) -> fun (subdirs) -> subdirs; (plain) -> plain end; mod_opt_type(file_format) -> fun (html) -> html; (plaintext) -> plaintext end; mod_opt_type(file_permissions) -> fun (SubOpts) -> {proplists:get_value(mode, SubOpts, 644), proplists:get_value(group, SubOpts, 33)} end; mod_opt_type({file_permissions, mode}) -> fun(I) when is_integer(I), I>=0 -> I end; mod_opt_type({file_permissions, group}) -> fun(I) when is_integer(I), I>=0 -> I end; mod_opt_type(outdir) -> fun iolist_to_binary/1; mod_opt_type(spam_prevention) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(timezone) -> fun (local) -> local; (universal) -> universal end; mod_opt_type(top_link) -> fun ([{S1, S2}]) -> {iolist_to_binary(S1), iolist_to_binary(S2)} end; mod_opt_type(_) -> [access_log, cssfile, dirname, dirtype, file_format, {file_permissions, mode}, {file_permissions, group}, outdir, spam_prevention, timezone, top_link]. ejabberd-18.01/src/prosody2ejabberd.erl0000644000232200023220000004047413225664356020407 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : prosody2ejabberd.erl %%% Author : Evgeny Khramtsov %%% Created : 20 Jan 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(prosody2ejabberd). %% API -export([from_dir/1]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -include("mod_roster.hrl"). -include("mod_offline.hrl"). -include("mod_privacy.hrl"). %%%=================================================================== %%% API %%%=================================================================== from_dir(ProsodyDir) -> case file:list_dir(ProsodyDir) of {ok, HostDirs} -> lists:foreach( fun(HostDir) -> Host = list_to_binary(HostDir), lists:foreach( fun(SubDir) -> Path = filename:join( [ProsodyDir, HostDir, SubDir]), convert_dir(Path, Host, SubDir) end, ["vcard", "accounts", "roster", "private", "config", "offline", "privacy", "pep", "pubsub"]) end, HostDirs); {error, Why} = Err -> ?ERROR_MSG("failed to list ~s: ~s", [ProsodyDir, file:format_error(Why)]), Err end. %%%=================================================================== %%% Internal functions %%%=================================================================== convert_dir(Path, Host, Type) -> case file:list_dir(Path) of {ok, Files} -> lists:foreach( fun(File) -> FilePath = filename:join(Path, File), case Type of "pep" -> case filelib:is_dir(FilePath) of true -> JID = list_to_binary(File ++ "@" ++ Host), convert_dir(FilePath, JID, "pubsub"); false -> ok end; _ -> case eval_file(FilePath) of {ok, Data} -> Name = iolist_to_binary(filename:rootname(File)), convert_data(url_decode(Host), Type, url_decode(Name), Data); Err -> Err end end end, Files); {error, enoent} -> ok; {error, Why} = Err -> ?ERROR_MSG("failed to list ~s: ~s", [Path, file:format_error(Why)]), Err end. eval_file(Path) -> case file:read_file(Path) of {ok, Data} -> State0 = luerl:init(), State1 = luerl:set_table([item], fun([X], State) -> {[X], State} end, State0), NewData = case filename:extension(Path) of ".list" -> <<"return {", Data/binary, "};">>; _ -> Data end, case luerl:eval(NewData, State1) of {ok, _} = Res -> Res; {error, Why} = Err -> ?ERROR_MSG("failed to eval ~s: ~p", [Path, Why]), Err end; {error, Why} = Err -> ?ERROR_MSG("failed to read file ~s: ~s", [Path, file:format_error(Why)]), Err end. maybe_get_scram_auth(Data) -> case proplists:get_value(<<"iteration_count">>, Data, no_ic) of IC when is_float(IC) -> %% A float like 4096.0 is read #scram{ storedkey = misc:hex_to_base64(proplists:get_value(<<"stored_key">>, Data, <<"">>)), serverkey = misc:hex_to_base64(proplists:get_value(<<"server_key">>, Data, <<"">>)), salt = base64:encode(proplists:get_value(<<"salt">>, Data, <<"">>)), iterationcount = round(IC) }; _ -> <<"">> end. convert_data(Host, "accounts", User, [Data]) -> Password = case proplists:get_value(<<"password">>, Data, no_pass) of no_pass -> maybe_get_scram_auth(Data); Pass when is_binary(Pass) -> Pass end, case ejabberd_auth:try_register(User, Host, Password) of ok -> ok; Err -> ?ERROR_MSG("failed to register user ~s@~s: ~p", [User, Host, Err]), Err end; convert_data(Host, "roster", User, [Data]) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Host), Rosters = lists:flatmap( fun({<<"pending">>, L}) -> convert_pending_item(LUser, LServer, L); ({S, L}) when is_binary(S) -> convert_roster_item(LUser, LServer, S, L); (_) -> [] end, Data), lists:foreach(fun mod_roster:set_roster/1, Rosters); convert_data(Host, "private", User, [Data]) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Host), PrivData = lists:flatmap( fun({_TagXMLNS, Raw}) -> case deserialize(Raw) of [El] -> XMLNS = fxml:get_tag_attr_s(<<"xmlns">>, El), [{XMLNS, El}]; _ -> [] end end, Data), mod_private:set_data(LUser, LServer, PrivData); convert_data(Host, "vcard", User, [Data]) -> LServer = jid:nameprep(Host), case deserialize(Data) of [VCard] -> mod_vcard:set_vcard(User, LServer, VCard); _ -> ok end; convert_data(_Host, "config", _User, [Data]) -> RoomJID = jid:decode(proplists:get_value(<<"jid">>, Data, <<"">>)), Config = proplists:get_value(<<"_data">>, Data, []), RoomCfg = convert_room_config(Data), case proplists:get_bool(<<"persistent">>, Config) of true when RoomJID /= error -> mod_muc:store_room(?MYNAME, RoomJID#jid.lserver, RoomJID#jid.luser, RoomCfg); _ -> ok end; convert_data(Host, "offline", User, [Data]) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Host), lists:foreach( fun({_, RawXML}) -> case deserialize(RawXML) of [El] -> Msg = el_to_offline_msg(LUser, LServer, El), ok = mod_offline:store_offline_msg(Msg); _ -> ok end end, Data); convert_data(Host, "privacy", User, [Data]) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Host), Lists = proplists:get_value(<<"lists">>, Data, []), Priv = #privacy{ us = {LUser, LServer}, default = proplists:get_value(<<"default">>, Data, none), lists = lists:flatmap( fun({Name, Vals}) -> Items = proplists:get_value(<<"items">>, Vals, []), case lists:map(fun convert_privacy_item/1, Items) of [] -> []; ListItems -> [{Name, ListItems}] end end, Lists)}, mod_privacy:set_list(Priv); convert_data(HostStr, "pubsub", Node, [Data]) -> case decode_pubsub_host(HostStr) of Host when is_binary(Host); is_tuple(Host) -> Type = node_type(Host), NodeData = convert_node_config(HostStr, Data), DefaultConfig = mod_pubsub:config(Host, default_node_config, []), Owner = proplists:get_value(owner, NodeData), Options = lists:foldl( fun({_Opt, undefined}, Acc) -> Acc; ({Opt, Val}, Acc) -> lists:keystore(Opt, 1, Acc, {Opt, Val}) end, DefaultConfig, proplists:get_value(options, NodeData)), case mod_pubsub:tree_action(Host, create_node, [Host, Node, Type, Owner, Options, []]) of {ok, Nidx} -> case mod_pubsub:node_action(Host, Type, create_node, [Nidx, Owner]) of {result, _} -> Access = open, % always allow subscriptions proplists:get_value(access_model, Options), Publish = open, % always allow publications proplists:get_value(publish_model, Options), MaxItems = proplists:get_value(max_items, Options), Affiliations = proplists:get_value(affiliations, NodeData), Subscriptions = proplists:get_value(subscriptions, NodeData), Items = proplists:get_value(items, NodeData), [mod_pubsub:node_action(Host, Type, set_affiliation, [Nidx, Entity, Aff]) || {Entity, Aff} <- Affiliations, Entity =/= Owner], [mod_pubsub:node_action(Host, Type, subscribe_node, [Nidx, jid:make(Entity), Entity, Access, never, [], [], []]) || Entity <- Subscriptions], [mod_pubsub:node_action(Host, Type, publish_item, [Nidx, Publisher, Publish, MaxItems, ItemId, Payload, []]) || {ItemId, Publisher, Payload} <- Items]; Error -> Error end; Error -> ?ERROR_MSG("failed to import pubsub node ~s on ~p:~n~p", [Node, Host, NodeData]), Error end; Error -> ?ERROR_MSG("failed to import pubsub node: ~p", [Error]), Error end; convert_data(_Host, _Type, _User, _Data) -> ok. convert_pending_item(LUser, LServer, LuaList) -> lists:flatmap( fun({S, true}) -> try jid:decode(S) of J -> LJID = jid:tolower(J), [#roster{usj = {LUser, LServer, LJID}, us = {LUser, LServer}, jid = LJID, ask = in}] catch _:{bad_jid, _} -> [] end; (_) -> [] end, LuaList). convert_roster_item(LUser, LServer, JIDstring, LuaList) -> try jid:decode(JIDstring) of JID -> LJID = jid:tolower(JID), InitR = #roster{usj = {LUser, LServer, LJID}, us = {LUser, LServer}, jid = LJID}, Roster = lists:foldl( fun({<<"groups">>, Val}, R) -> Gs = lists:flatmap( fun({G, true}) -> [G]; (_) -> [] end, Val), R#roster{groups = Gs}; ({<<"subscription">>, Sub}, R) -> R#roster{subscription = misc:binary_to_atom(Sub)}; ({<<"ask">>, <<"subscribe">>}, R) -> R#roster{ask = out}; ({<<"name">>, Name}, R) -> R#roster{name = Name} end, InitR, LuaList), [Roster] catch _:{bad_jid, _} -> [] end. convert_room_affiliations(Data) -> lists:flatmap( fun({J, Aff}) -> try jid:decode(J) of #jid{luser = U, lserver = S} -> [{{U, S, <<>>}, misc:binary_to_atom(Aff)}] catch _:{bad_jid, _} -> [] end end, proplists:get_value(<<"_affiliations">>, Data, [])). convert_room_config(Data) -> Config = proplists:get_value(<<"_data">>, Data, []), Pass = case proplists:get_value(<<"password">>, Config, <<"">>) of <<"">> -> []; Password -> [{password_protected, true}, {password, Password}] end, Subj = try jid:decode( proplists:get_value( <<"subject_from">>, Config, <<"">>)) of #jid{lresource = Nick} when Nick /= <<"">> -> [{subject, proplists:get_value(<<"subject">>, Config, <<"">>)}, {subject_author, Nick}] catch _:{bad_jid, _} -> [] end, Anonymous = case proplists:get_value(<<"whois">>, Config, <<"moderators">>) of <<"moderators">> -> true; _ -> false end, [{affiliations, convert_room_affiliations(Data)}, {allow_change_subj, proplists:get_bool(<<"changesubject">>, Config)}, {description, proplists:get_value(<<"description">>, Config, <<"">>)}, {members_only, proplists:get_bool(<<"members_only">>, Config)}, {moderated, proplists:get_bool(<<"moderated">>, Config)}, {anonymous, Anonymous}] ++ Pass ++ Subj. convert_privacy_item({_, Item}) -> Action = proplists:get_value(<<"action">>, Item, <<"allow">>), Order = proplists:get_value(<<"order">>, Item, 0), T = misc:binary_to_atom(proplists:get_value(<<"type">>, Item, <<"none">>)), V = proplists:get_value(<<"value">>, Item, <<"">>), MatchIQ = proplists:get_bool(<<"iq">>, Item), MatchMsg = proplists:get_bool(<<"message">>, Item), MatchPresIn = proplists:get_bool(<<"presence-in">>, Item), MatchPresOut = proplists:get_bool(<<"presence-out">>, Item), MatchAll = if (MatchIQ == false) and (MatchMsg == false) and (MatchPresIn == false) and (MatchPresOut == false) -> true; true -> false end, {Type, Value} = try case T of none -> {T, none}; group -> {T, V}; jid -> {T, jid:tolower(jid:decode(V))}; subscription -> {T, misc:binary_to_atom(V)} end catch _:_ -> {none, none} end, #listitem{type = Type, value = Value, action = misc:binary_to_atom(Action), order = erlang:trunc(Order), match_all = MatchAll, match_iq = MatchIQ, match_message = MatchMsg, match_presence_in = MatchPresIn, match_presence_out = MatchPresOut}. url_decode(Encoded) -> url_decode(Encoded, <<>>). url_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) -> Hex = list_to_integer([Hi, Lo], 16), url_decode(Tail, <>); url_decode(<>, Acc) -> url_decode(Tail, <>); url_decode(<<>>, Acc) -> Acc. decode_pubsub_host(Host) -> try jid:decode(Host) of #jid{luser = <<>>, lserver = LServer} -> LServer; #jid{luser = LUser, lserver = LServer} -> {LUser, LServer, <<>>} catch _:{bad_jid, _} -> bad_jid end. node_type({_U, _S, _R}) -> <<"pep">>; node_type(Host) -> hd(mod_pubsub:plugins(Host)). max_items(Config, Default) -> case round(proplists:get_value(<<"max_items">>, Config, Default)) of I when I =< 0 -> Default; I -> I end. convert_node_affiliations(Data) -> lists:flatmap( fun({J, Aff}) -> try jid:decode(J) of JID -> [{JID, misc:binary_to_atom(Aff)}] catch _:{bad_jid, _} -> [] end end, proplists:get_value(<<"affiliations">>, Data, [])). convert_node_subscriptions(Data) -> lists:flatmap( fun({J, true}) -> try jid:decode(J) of JID -> [jid:tolower(JID)] catch _:{bad_jid, _} -> [] end; (_) -> [] end, proplists:get_value(<<"subscribers">>, Data, [])). convert_node_items(Host, Data) -> Authors = proplists:get_value(<<"data_author">>, Data, []), lists:flatmap( fun({ItemId, Item}) -> try jid:decode(proplists:get_value(ItemId, Authors, Host)) of JID -> [El] = deserialize(Item), [{ItemId, JID, El#xmlel.children}] catch _:{bad_jid, _} -> [] end end, proplists:get_value(<<"data">>, Data, [])). convert_node_config(Host, Data) -> Config = proplists:get_value(<<"config">>, Data, []), [{affiliations, convert_node_affiliations(Data)}, {subscriptions, convert_node_subscriptions(Data)}, {owner, jid:decode(proplists:get_value(<<"creator">>, Config, Host))}, {items, convert_node_items(Host, Data)}, {options, [ {deliver_notifications, proplists:get_value(<<"deliver_notifications">>, Config, true)}, {deliver_payloads, proplists:get_value(<<"deliver_payloads">>, Config, true)}, {persist_items, proplists:get_value(<<"persist_items">>, Config, true)}, {max_items, max_items(Config, 10)}, {access_model, misc:binary_to_atom(proplists:get_value(<<"access_model">>, Config, <<"open">>))}, {publish_model, misc:binary_to_atom(proplists:get_value(<<"publish_model">>, Config, <<"publishers">>))}, {title, proplists:get_value(<<"title">>, Config, <<"">>)} ]} ]. el_to_offline_msg(LUser, LServer, #xmlel{attrs = Attrs} = El) -> try TS = xmpp_util:decode_timestamp( fxml:get_attr_s(<<"stamp">>, Attrs)), Attrs1 = lists:filter( fun({<<"stamp">>, _}) -> false; ({<<"stamp_legacy">>, _}) -> false; (_) -> true end, Attrs), El1 = El#xmlel{attrs = Attrs1}, case xmpp:decode(El1, ?NS_CLIENT, [ignore_els]) of #message{from = #jid{} = From, to = #jid{} = To} = Packet -> [#offline_msg{ us = {LUser, LServer}, timestamp = TS, expire = never, from = From, to = To, packet = Packet}]; _ -> [] end catch _:{bad_timestamp, _} -> []; _:{bad_jid, _} -> []; _:{xmpp_codec, _} -> [] end. deserialize(L) -> deserialize(L, #xmlel{}, []). deserialize([{<<"attr">>, Attrs}|T], El, Acc) -> deserialize(T, El#xmlel{attrs = Attrs ++ El#xmlel.attrs}, Acc); deserialize([{<<"name">>, Name}|T], El, Acc) -> deserialize(T, El#xmlel{name = Name}, Acc); deserialize([{_, S}|T], #xmlel{children = Els} = El, Acc) when is_binary(S) -> deserialize(T, El#xmlel{children = [{xmlcdata, S}|Els]}, Acc); deserialize([{_, L}|T], #xmlel{children = Els} = El, Acc) when is_list(L) -> deserialize(T, El#xmlel{children = deserialize(L) ++ Els}, Acc); deserialize([], #xmlel{children = Els} = El, Acc) -> [El#xmlel{children = lists:reverse(Els)}|Acc]. ejabberd-18.01/src/mod_service_log.erl0000644000232200023220000000562313225664356020304 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_service_log.erl %%% Author : Alexey Shchepin %%% Purpose : Copy user messages to logger service %%% Created : 24 Aug 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_service_log). -author('alexey@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, log_user_send/1, log_user_receive/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, _Opts) -> ejabberd_hooks:add(user_send_packet, Host, ?MODULE, log_user_send, 50), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, log_user_receive, 50), ok. stop(Host) -> ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, log_user_send, 50), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, log_user_receive, 50), ok. depends(_Host, _Opts) -> []. -spec log_user_send({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. log_user_send({Packet, C2SState}) -> From = xmpp:get_from(Packet), log_packet(Packet, From#jid.lserver), {Packet, C2SState}. -spec log_user_receive({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. log_user_receive({Packet, C2SState}) -> To = xmpp:get_to(Packet), log_packet(Packet, To#jid.lserver), {Packet, C2SState}. -spec log_packet(stanza(), binary()) -> ok. log_packet(Packet, Host) -> Loggers = gen_mod:get_module_opt(Host, ?MODULE, loggers, []), ForwardedMsg = #message{from = jid:make(Host), id = randoms:get_string(), sub_els = [#forwarded{ xml_els = [xmpp:encode(Packet)]}]}, lists:foreach( fun(Logger) -> ejabberd_router:route(xmpp:set_to(ForwardedMsg, jid:make(Logger))) end, Loggers). mod_opt_type(loggers) -> fun (L) -> lists:map(fun (S) -> B = iolist_to_binary(S), N = jid:nameprep(B), if N /= error -> N end end, L) end; mod_opt_type(_) -> [loggers]. ejabberd-18.01/src/jlib.erl0000644000232200023220000007372113225664356016070 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : jlib.erl %%% Author : Alexey Shchepin %%% Purpose : General XMPP library. %%% Created : 23 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(jlib). -author('alexey@process-one.net'). -protocol({xep, 59, '1.0'}). -protocol({xep, 82, '1.1'}). -protocol({xep, 203, '2.0'}). -compile({no_auto_import, [atom_to_binary/2, binary_to_integer/1, integer_to_binary/1]}). %% The following functions are deprected: use functions from misc.erl -export([tolower/1, term_to_base64/1, base64_to_term/1, decode_base64/1, encode_base64/1, ip_to_list/1, hex_to_bin/1, hex_to_base64/1, expand_keyword/3, atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1, l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1]). %% The following functions are used by gen_iq_handler.erl for providing backward %% compatibility and must not be used in other parts of the code %% Use xmpp:decode() and xmpp:encode() instead -export([iq_query_info/1, iq_to_xml/1]). %% The following functions are deprecated and will be removed soon %% Use functions from xmpp.erl and xmpp_util.erl instead -export([make_result_iq_reply/1, make_error_reply/3, make_error_reply/2, make_error_element/2, make_correct_from_to_attrs/3, replace_from_to_attrs/3, replace_from_to/3, replace_from_attrs/2, replace_from/2, remove_attr/2, get_iq_namespace/1, iq_query_or_response_info/1, is_iq_request_type/1, parse_xdata_submit/1, unwrap_carbon/1, is_standalone_chat_state/1, add_delay_info/3, add_delay_info/4, timestamp_to_legacy/1, timestamp_to_iso_basic/1, timestamp_to_iso/2, now_to_utc_string/1, now_to_local_string/1, datetime_string_to_timestamp/1, rsm_encode/1, rsm_encode/2, rsm_decode/1, binary_to_integer/1, binary_to_integer/2, integer_to_binary/1, integer_to_binary/2]). %% The following functions are deprecated and will be removed soon %% Use corresponding functions from jid.erl instead -export([make_jid/3, make_jid/1, split_jid/1, string_to_jid/1, jid_to_string/1, is_nodename/1, nodeprep/1, nameprep/1, resourceprep/1, jid_tolower/1, jid_remove_resource/1, jid_replace_resource/2]). -deprecated([{make_jid, '_'}, {split_jid, 1}, {string_to_jid, 1}, {jid_to_string, 1}, {is_nodename, 1}, {nodeprep, 1}, {nameprep, 1}, {resourceprep, 1}, {jid_tolower, 1}, {jid_remove_resource, 1}, {jid_replace_resource, 2}, {add_delay_info, 3}, {add_delay_info, 4}, {make_result_iq_reply, 1}, {make_error_reply, 3}, {make_error_reply, 2}, {make_error_element, 2}, {make_correct_from_to_attrs, 3}, {replace_from_to_attrs, 3}, {replace_from_to, 3}, {replace_from_attrs, 2}, {replace_from, 2}, {remove_attr, 2}, {get_iq_namespace, 1}, {iq_query_or_response_info, 1}, {is_iq_request_type, 1}, {parse_xdata_submit, 1}, {unwrap_carbon, 1}, {is_standalone_chat_state, 1}, {timestamp_to_legacy, 1}, {timestamp_to_iso_basic, 1}, {timestamp_to_iso, 2}, {now_to_utc_string, 1}, {now_to_local_string, 1}, {datetime_string_to_timestamp, 1}, {rsm_encode, 1}, {rsm_encode, 2}, {rsm_decode, 1}, {binary_to_integer, 1}, {binary_to_integer, 2}, {integer_to_binary, 1}, {integer_to_binary, 2}, {tolower, 1}, {term_to_base64, 1}, {base64_to_term, 1}, {decode_base64, 1}, {encode_base64, 1}, {ip_to_list, 1}, {hex_to_bin, 1}, {hex_to_base64, 1}, {expand_keyword, 3}, {atom_to_binary, 1}, {binary_to_atom, 1}, {tuple_to_binary, 1}, {l2i, 1}, {i2l, 1}, {i2l, 2}, {expr_to_term, 1}, {term_to_expr, 1}]). -include("ejabberd.hrl"). -include("jlib.hrl"). %send_iq(From, To, ID, SubTags) -> % ok. -spec make_result_iq_reply(xmlel()) -> xmlel(). make_result_iq_reply(#xmlel{name = Name, attrs = Attrs, children = SubTags}) -> NewAttrs = make_result_iq_reply_attrs(Attrs), #xmlel{name = Name, attrs = NewAttrs, children = SubTags}. -spec make_result_iq_reply_attrs([attr()]) -> [attr()]. make_result_iq_reply_attrs(Attrs) -> To = fxml:get_attr(<<"to">>, Attrs), From = fxml:get_attr(<<"from">>, Attrs), Attrs1 = lists:keydelete(<<"to">>, 1, Attrs), Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1), Attrs3 = case To of {value, ToVal} -> [{<<"from">>, ToVal} | Attrs2]; _ -> Attrs2 end, Attrs4 = case From of {value, FromVal} -> [{<<"to">>, FromVal} | Attrs3]; _ -> Attrs3 end, Attrs5 = lists:keydelete(<<"type">>, 1, Attrs4), Attrs6 = [{<<"type">>, <<"result">>} | Attrs5], Attrs6. -spec make_error_reply(xmlel(), binary(), binary()) -> xmlel(). make_error_reply(#xmlel{name = Name, attrs = Attrs, children = SubTags}, Code, Desc) -> NewAttrs = make_error_reply_attrs(Attrs), #xmlel{name = Name, attrs = NewAttrs, children = SubTags ++ [#xmlel{name = <<"error">>, attrs = [{<<"code">>, Code}], children = [{xmlcdata, Desc}]}]}. -spec make_error_reply(xmlel(), xmlel()) -> xmlel(). make_error_reply(#xmlel{name = Name, attrs = Attrs, children = SubTags}, Error) -> NewAttrs = make_error_reply_attrs(Attrs), #xmlel{name = Name, attrs = NewAttrs, children = SubTags ++ [Error]}. -spec make_error_reply_attrs([attr()]) -> [attr()]. make_error_reply_attrs(Attrs) -> To = fxml:get_attr(<<"to">>, Attrs), From = fxml:get_attr(<<"from">>, Attrs), Attrs1 = lists:keydelete(<<"to">>, 1, Attrs), Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1), Attrs3 = case To of {value, ToVal} -> [{<<"from">>, ToVal} | Attrs2]; _ -> Attrs2 end, Attrs4 = case From of {value, FromVal} -> [{<<"to">>, FromVal} | Attrs3]; _ -> Attrs3 end, Attrs5 = lists:keydelete(<<"type">>, 1, Attrs4), Attrs6 = [{<<"type">>, <<"error">>} | Attrs5], Attrs6. -spec make_error_element(binary(), binary()) -> xmlel(). make_error_element(Code, Desc) -> #xmlel{name = <<"error">>, attrs = [{<<"code">>, Code}], children = [{xmlcdata, Desc}]}. -spec make_correct_from_to_attrs(binary(), binary(), [attr()]) -> [attr()]. make_correct_from_to_attrs(From, To, Attrs) -> Attrs1 = lists:keydelete(<<"from">>, 1, Attrs), Attrs2 = case fxml:get_attr(<<"to">>, Attrs) of {value, _} -> Attrs1; _ -> [{<<"to">>, To} | Attrs1] end, Attrs3 = [{<<"from">>, From} | Attrs2], Attrs3. -spec replace_from_to_attrs(binary(), binary(), [attr()]) -> [attr()]. replace_from_to_attrs(From, To, Attrs) -> Attrs1 = lists:keydelete(<<"to">>, 1, Attrs), Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1), Attrs3 = [{<<"to">>, To} | Attrs2], Attrs4 = [{<<"from">>, From} | Attrs3], Attrs4. -spec replace_from_to(jid(), jid(), xmlel()) -> xmlel(). replace_from_to(From, To, #xmlel{name = Name, attrs = Attrs, children = Els}) -> NewAttrs = replace_from_to_attrs(jid:encode(From), jid:encode(To), Attrs), #xmlel{name = Name, attrs = NewAttrs, children = Els}. -spec replace_from_attrs(binary(), [attr()]) -> [attr()]. replace_from_attrs(From, Attrs) -> Attrs1 = lists:keydelete(<<"from">>, 1, Attrs), [{<<"from">>, From} | Attrs1]. -spec replace_from(jid(), xmlel()) -> xmlel(). replace_from(From, #xmlel{name = Name, attrs = Attrs, children = Els}) -> NewAttrs = replace_from_attrs(jid:encode(From), Attrs), #xmlel{name = Name, attrs = NewAttrs, children = Els}. -spec remove_attr(binary(), xmlel()) -> xmlel(). remove_attr(Attr, #xmlel{name = Name, attrs = Attrs, children = Els}) -> NewAttrs = lists:keydelete(Attr, 1, Attrs), #xmlel{name = Name, attrs = NewAttrs, children = Els}. -spec make_jid(binary(), binary(), binary()) -> jid() | error. make_jid(User, Server, Resource) -> jid:make(User, Server, Resource). -spec make_jid({binary(), binary(), binary()}) -> jid() | error. make_jid({User, Server, Resource}) -> jid:make({User, Server, Resource}). %% This is the reverse of make_jid/1 -spec split_jid(jid()) -> {binary(), binary(), binary()} | error. split_jid(J) -> jid:split(J). -spec string_to_jid(binary()) -> jid() | error. string_to_jid(S) -> try jid:decode(S) catch _:_ -> error end. -spec jid_to_string(jid() | ljid()) -> binary(). jid_to_string(J) -> jid:encode(J). -spec is_nodename(binary()) -> boolean(). is_nodename(Node) -> jid:is_nodename(Node). %tolower_c(C) when C >= $A, C =< $Z -> % C + 32; %tolower_c(C) -> % C. -define(LOWER(Char), if Char >= $A, Char =< $Z -> Char + 32; true -> Char end). %tolower(S) -> % lists:map(fun tolower_c/1, S). %tolower(S) -> % [?LOWER(Char) || Char <- S]. -spec tolower(binary()) -> binary(). tolower(B) -> iolist_to_binary(tolower_s(binary_to_list(B))). tolower_s([C | Cs]) -> if C >= $A, C =< $Z -> [C + 32 | tolower_s(Cs)]; true -> [C | tolower_s(Cs)] end; tolower_s([]) -> []. %tolower([C | Cs]) when C >= $A, C =< $Z -> % [C + 32 | tolower(Cs)]; %tolower([C | Cs]) -> % [C | tolower(Cs)]; %tolower([]) -> % []. -spec nodeprep(binary()) -> binary() | error. nodeprep(S) -> jid:nodeprep(S). -spec nameprep(binary()) -> binary() | error. nameprep(S) -> jid:nameprep(S). -spec resourceprep(binary()) -> binary() | error. resourceprep(S) -> jid:resourceprep(S). -spec jid_tolower(jid() | ljid()) -> error | ljid(). jid_tolower(J) -> jid:tolower(J). -spec jid_remove_resource(jid()) -> jid(); (ljid()) -> ljid(). jid_remove_resource(J) -> jid:remove_resource(J). -spec jid_replace_resource(jid(), binary()) -> error | jid(). jid_replace_resource(JID, Resource) -> jid:replace_resource(JID, Resource). -spec get_iq_namespace(xmlel()) -> binary(). get_iq_namespace(#xmlel{name = <<"iq">>, children = Els}) -> case fxml:remove_cdata(Els) of [#xmlel{attrs = Attrs}] -> fxml:get_attr_s(<<"xmlns">>, Attrs); _ -> <<"">> end; get_iq_namespace(_) -> <<"">>. %% -spec iq_query_info(Xmlel :: xmlel()) -> iq_request() | 'reply' | 'invalid' | 'not_iq'. %% @spec (xmlelement()) -> iq() | reply | invalid | not_iq iq_query_info(El) -> iq_info_internal(El, request). %% -spec iq_query_or_response_info(Xmlel :: xmlel()) -> iq_request() | iq_reply() | 'reply' | 'invalid' | 'not_iq'. iq_query_or_response_info(El) -> iq_info_internal(El, any). iq_info_internal(#xmlel{name = <<"iq">>, attrs = Attrs, children = Els}, Filter) -> ID = fxml:get_attr_s(<<"id">>, Attrs), Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs), {Type, Class} = case fxml:get_attr_s(<<"type">>, Attrs) of <<"set">> -> {set, request}; <<"get">> -> {get, request}; <<"result">> -> {result, reply}; <<"error">> -> {error, reply}; _ -> {invalid, invalid} end, if Type == invalid -> invalid; Class == request; Filter == any -> FilteredEls = fxml:remove_cdata(Els), {XMLNS, SubEl} = case {Class, FilteredEls} of {request, [#xmlel{attrs = Attrs2}]} -> {fxml:get_attr_s(<<"xmlns">>, Attrs2), hd(FilteredEls)}; {reply, _} -> NonErrorEls = [El || #xmlel{name = SubName} = El <- FilteredEls, SubName /= <<"error">>], {case NonErrorEls of [NonErrorEl] -> fxml:get_tag_attr_s(<<"xmlns">>, NonErrorEl); _ -> <<"">> end, FilteredEls}; _ -> {<<"">>, []} end, if XMLNS == <<"">>, Class == request -> invalid; true -> #iq{id = ID, type = Type, xmlns = XMLNS, lang = Lang, sub_el = SubEl} end; Class == reply, Filter /= any -> reply end; iq_info_internal(_, _) -> not_iq. -spec is_iq_request_type(set | get | result | error) -> boolean(). is_iq_request_type(set) -> true; is_iq_request_type(get) -> true; is_iq_request_type(_) -> false. iq_type_to_string(set) -> <<"set">>; iq_type_to_string(get) -> <<"get">>; iq_type_to_string(result) -> <<"result">>; iq_type_to_string(error) -> <<"error">>. -spec iq_to_xml(IQ :: iq()) -> xmlel(). iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) -> Children = if is_list(SubEl) -> SubEl; true -> [SubEl] end, if ID /= <<"">> -> #xmlel{name = <<"iq">>, attrs = [{<<"id">>, ID}, {<<"type">>, iq_type_to_string(Type)}], children = Children}; true -> #xmlel{name = <<"iq">>, attrs = [{<<"type">>, iq_type_to_string(Type)}], children = Children} end. -spec parse_xdata_submit(El :: xmlel()) -> [{Var::binary(), Values::[binary()]}] | 'invalid'. parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) -> case fxml:get_attr_s(<<"type">>, Attrs) of <<"submit">> -> lists:reverse(parse_xdata_fields(Els, [])); <<"form">> -> %% This is a workaround to accept Psi's wrong forms lists:reverse(parse_xdata_fields(Els, [])); _ -> invalid end. -spec parse_xdata_fields(Xmlels :: [xmlel() | cdata()], Res :: [{Var::binary(), Values :: [binary()]}]) -> [{Var::binary(), Values::[binary()]}]. parse_xdata_fields([], Res) -> Res; parse_xdata_fields([#xmlel{name = <<"field">>, attrs = Attrs, children = SubEls} | Els], Res) -> case fxml:get_attr_s(<<"var">>, Attrs) of <<>> -> parse_xdata_fields(Els, Res); Var -> Field = {Var, lists:reverse(parse_xdata_values(SubEls, []))}, parse_xdata_fields(Els, [Field | Res]) end; parse_xdata_fields([_ | Els], Res) -> parse_xdata_fields(Els, Res). -spec parse_xdata_values(Xmlels :: [xmlel() | cdata()], Res :: [binary()]) -> [binary()]. parse_xdata_values([], Res) -> Res; parse_xdata_values([#xmlel{name = <<"value">>, children = SubEls} | Els], Res) -> Val = fxml:get_cdata(SubEls), parse_xdata_values(Els, [Val | Res]); parse_xdata_values([_ | Els], Res) -> parse_xdata_values(Els, Res). -spec rsm_decode(iq() | xmlel()) -> none | rsm_in(). rsm_decode(#iq{sub_el = SubEl}) -> rsm_decode(SubEl); rsm_decode(#xmlel{} = SubEl) -> case fxml:get_subtag(SubEl, <<"set">>) of false -> none; #xmlel{name = <<"set">>, children = SubEls} -> lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls) end. rsm_parse_element(#xmlel{name = <<"max">>, attrs = []} = Elem, RsmIn) -> CountStr = fxml:get_tag_cdata(Elem), {Count, _} = str:to_integer(CountStr), RsmIn#rsm_in{max = Count}; rsm_parse_element(#xmlel{name = <<"before">>, attrs = []} = Elem, RsmIn) -> UID = fxml:get_tag_cdata(Elem), RsmIn#rsm_in{direction = before, id = UID}; rsm_parse_element(#xmlel{name = <<"after">>, attrs = []} = Elem, RsmIn) -> UID = fxml:get_tag_cdata(Elem), RsmIn#rsm_in{direction = aft, id = UID}; rsm_parse_element(#xmlel{name = <<"index">>, attrs = []} = Elem, RsmIn) -> IndexStr = fxml:get_tag_cdata(Elem), {Index, _} = str:to_integer(IndexStr), RsmIn#rsm_in{index = Index}; rsm_parse_element(_, RsmIn) -> RsmIn. -spec rsm_encode(iq(), rsm_out()) -> iq(). rsm_encode(#iq{sub_el = SubEl} = IQ, RsmOut) -> Set = #xmlel{name = <<"set">>, attrs = [{<<"xmlns">>, ?NS_RSM}], children = lists:reverse(rsm_encode_out(RsmOut))}, #xmlel{name = Name, attrs = Attrs, children = SubEls} = SubEl, New = #xmlel{name = Name, attrs = Attrs, children = [Set | SubEls]}, IQ#iq{sub_el = New}. -spec rsm_encode(none | rsm_out()) -> [xmlel()]. rsm_encode(none) -> []; rsm_encode(RsmOut) -> [#xmlel{name = <<"set">>, attrs = [{<<"xmlns">>, ?NS_RSM}], children = lists:reverse(rsm_encode_out(RsmOut))}]. rsm_encode_out(#rsm_out{count = Count, index = Index, first = First, last = Last}) -> El = rsm_encode_first(First, Index, []), El2 = rsm_encode_last(Last, El), rsm_encode_count(Count, El2). rsm_encode_first(undefined, undefined, Arr) -> Arr; rsm_encode_first(First, undefined, Arr) -> [#xmlel{name = <<"first">>, attrs = [], children = [{xmlcdata, First}]} | Arr]; rsm_encode_first(First, Index, Arr) -> [#xmlel{name = <<"first">>, attrs = [{<<"index">>, i2l(Index)}], children = [{xmlcdata, First}]} | Arr]. rsm_encode_last(undefined, Arr) -> Arr; rsm_encode_last(Last, Arr) -> [#xmlel{name = <<"last">>, attrs = [], children = [{xmlcdata, Last}]} | Arr]. rsm_encode_count(undefined, Arr) -> Arr; rsm_encode_count(Count, Arr) -> [#xmlel{name = <<"count">>, attrs = [], children = [{xmlcdata, i2l(Count)}]} | Arr]. -spec unwrap_carbon(xmlel()) -> xmlel(). unwrap_carbon(#xmlel{name = <<"message">>} = Stanza) -> case unwrap_carbon(Stanza, <<"sent">>) of #xmlel{} = Payload -> Payload; false -> case unwrap_carbon(Stanza, <<"received">>) of #xmlel{} = Payload -> Payload; false -> Stanza end end; unwrap_carbon(Stanza) -> Stanza. -spec unwrap_carbon(xmlel(), binary()) -> xmlel() | false. unwrap_carbon(Stanza, Direction) -> case fxml:get_subtag(Stanza, Direction) of #xmlel{name = Direction, attrs = Attrs} = El -> case fxml:get_attr_s(<<"xmlns">>, Attrs) of NS when NS == ?NS_CARBONS_2; NS == ?NS_CARBONS_1 -> case fxml:get_subtag_with_xmlns(El, <<"forwarded">>, ?NS_FORWARD) of #xmlel{children = Els} -> case fxml:remove_cdata(Els) of [#xmlel{} = Payload] -> Payload; _ -> false end; false -> false end; _NS -> false end; false -> false end. -spec is_standalone_chat_state(xmlel()) -> boolean(). is_standalone_chat_state(Stanza) -> case unwrap_carbon(Stanza) of #xmlel{name = <<"message">>, children = Els} -> IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY], Stripped = [El || #xmlel{name = Name, attrs = Attrs} = El <- Els, not lists:member(fxml:get_attr_s(<<"xmlns">>, Attrs), IgnoreNS), Name /= <<"thread">>], Stripped == []; #xmlel{} -> false end. -spec add_delay_info(xmlel(), jid() | ljid() | binary(), erlang:timestamp()) -> xmlel(). add_delay_info(El, From, Time) -> add_delay_info(El, From, Time, <<"">>). -spec add_delay_info(xmlel(), jid() | ljid() | binary(), erlang:timestamp(), binary()) -> xmlel(). add_delay_info(El, From, Time, Desc) -> DelayTag = create_delay_tag(Time, From, Desc), fxml:append_subtags(El, [DelayTag]). -spec create_delay_tag(erlang:timestamp(), jid() | ljid() | binary(), binary()) -> xmlel() | error. create_delay_tag(TimeStamp, FromJID, Desc) when is_tuple(FromJID) -> From = jid:encode(FromJID), Stamp = now_to_utc_string(TimeStamp, 3), Children = case Desc of <<"">> -> []; _ -> [{xmlcdata, Desc}] end, #xmlel{name = <<"delay">>, attrs = [{<<"xmlns">>, ?NS_DELAY}, {<<"from">>, From}, {<<"stamp">>, Stamp}], children = Children}; create_delay_tag(DateTime, Host, Desc) when is_binary(Host) -> FromJID = jid:make(Host), create_delay_tag(DateTime, FromJID, Desc). -type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc. %% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes} %% Hours = integer() %% Minutes = integer() -spec timestamp_to_iso(calendar:datetime(), tz()) -> {binary(), binary()}. %% This is the XEP-0082 date and time format %% http://xmpp.org/extensions/xep-0082.html timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}, Timezone) -> Timestamp_string = lists:flatten(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B", [Year, Month, Day, Hour, Minute, Second])), Timezone_string = case Timezone of utc -> "Z"; {Sign, {TZh, TZm}} -> io_lib:format("~s~2..0B:~2..0B", [Sign, TZh, TZm]); {TZh, TZm} -> Sign = case TZh >= 0 of true -> "+"; false -> "-" end, io_lib:format("~s~2..0B:~2..0B", [Sign, abs(TZh), TZm]) end, {iolist_to_binary(Timestamp_string), iolist_to_binary(Timezone_string)}. -spec timestamp_to_legacy(calendar:datetime()) -> binary(). %% This is the jabber legacy format %% http://xmpp.org/extensions/xep-0091.html#time timestamp_to_legacy({{Year, Month, Day}, {Hour, Minute, Second}}) -> (str:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B", [Year, Month, Day, Hour, Minute, Second])). -spec timestamp_to_iso_basic(calendar:datetime()) -> binary(). %% This is the ISO 8601 basic bormat timestamp_to_iso_basic({{Year, Month, Day}, {Hour, Minute, Second}}) -> (str:format("~4..0B~2..0B~2..0BT~2..0B~2..0B~2..0B", [Year, Month, Day, Hour, Minute, Second])). -spec now_to_utc_string(erlang:timestamp()) -> binary(). now_to_utc_string({MegaSecs, Secs, MicroSecs}) -> now_to_utc_string({MegaSecs, Secs, MicroSecs}, 6). -spec now_to_utc_string(erlang:timestamp(), 1..6) -> binary(). now_to_utc_string({MegaSecs, Secs, MicroSecs}, Precision) -> {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}), Max = round(math:pow(10, Precision)), case round(MicroSecs / math:pow(10, 6 - Precision)) of Max -> now_to_utc_string({MegaSecs, Secs + 1, 0}, Precision); FracOfSec -> (str:format("~4..0B-~2..0B-~2..0BT" "~2..0B:~2..0B:~2..0B.~*..0BZ", [Year, Month, Day, Hour, Minute, Second, Precision, FracOfSec])) end. -spec now_to_local_string(erlang:timestamp()) -> binary(). now_to_local_string({MegaSecs, Secs, MicroSecs}) -> LocalTime = calendar:now_to_local_time({MegaSecs, Secs, MicroSecs}), UTCTime = calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}), Seconds = calendar:datetime_to_gregorian_seconds(LocalTime) - calendar:datetime_to_gregorian_seconds(UTCTime), {{H, M, _}, Sign} = if Seconds < 0 -> {calendar:seconds_to_time(-Seconds), "-"}; true -> {calendar:seconds_to_time(Seconds), "+"} end, {{Year, Month, Day}, {Hour, Minute, Second}} = LocalTime, (str:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B.~6." ".0B~s~2..0B:~2..0B", [Year, Month, Day, Hour, Minute, Second, MicroSecs, Sign, H, M])). -spec datetime_string_to_timestamp(binary()) -> undefined | erlang:timestamp(). datetime_string_to_timestamp(TimeStr) -> case catch parse_datetime(TimeStr) of {'EXIT', _Err} -> undefined; TimeStamp -> TimeStamp end. parse_datetime(TimeStr) -> [Date, Time] = str:tokens(TimeStr, <<"T">>), D = parse_date(Date), {T, MS, TZH, TZM} = parse_time(Time), S = calendar:datetime_to_gregorian_seconds({D, T}), S1 = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}), Seconds = S - S1 - TZH * 60 * 60 - TZM * 60, {Seconds div 1000000, Seconds rem 1000000, MS}. % yyyy-mm-dd parse_date(Date) -> [Y, M, D] = str:tokens(Date, <<"-">>), Date1 = {binary_to_integer(Y), binary_to_integer(M), binary_to_integer(D)}, case calendar:valid_date(Date1) of true -> Date1; _ -> false end. % hh:mm:ss[.sss]TZD parse_time(Time) -> case str:str(Time, <<"Z">>) of 0 -> parse_time_with_timezone(Time); _ -> [T | _] = str:tokens(Time, <<"Z">>), {TT, MS} = parse_time1(T), {TT, MS, 0, 0} end. parse_time_with_timezone(Time) -> case str:str(Time, <<"+">>) of 0 -> case str:str(Time, <<"-">>) of 0 -> false; _ -> parse_time_with_timezone(Time, <<"-">>) end; _ -> parse_time_with_timezone(Time, <<"+">>) end. parse_time_with_timezone(Time, Delim) -> [T, TZ] = str:tokens(Time, Delim), {TZH, TZM} = parse_timezone(TZ), {TT, MS} = parse_time1(T), case Delim of <<"-">> -> {TT, MS, -TZH, -TZM}; <<"+">> -> {TT, MS, TZH, TZM} end. parse_timezone(TZ) -> [H, M] = str:tokens(TZ, <<":">>), {[H1, M1], true} = check_list([{H, 12}, {M, 60}]), {H1, M1}. parse_time1(Time) -> [HMS | T] = str:tokens(Time, <<".">>), MS = case T of [] -> 0; [Val] -> binary_to_integer(str:left(Val, 6, $0)) end, [H, M, S] = str:tokens(HMS, <<":">>), {[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]), {{H1, M1, S1}, MS}. check_list(List) -> lists:mapfoldl(fun ({L, N}, B) -> V = binary_to_integer(L), if (V >= 0) and (V =< N) -> {V, B}; true -> {false, false} end end, true, List). % % Base64 stuff (based on httpd_util.erl) % -spec term_to_base64(term()) -> binary(). term_to_base64(Term) -> encode_base64(term_to_binary(Term)). -spec base64_to_term(binary()) -> {term, term()} | error. base64_to_term(Base64) -> case catch binary_to_term(decode_base64(Base64), [safe]) of {'EXIT', _} -> error; Term -> {term, Term} end. -spec decode_base64(binary()) -> binary(). decode_base64(S) -> case catch binary:last(S) of C when C == $\n; C == $\s -> decode_base64(binary:part(S, 0, byte_size(S) - 1)); _ -> decode_base64_bin(S, <<>>) end. take_without_spaces(Bin, Count) -> take_without_spaces(Bin, Count, <<>>). take_without_spaces(Bin, 0, Acc) -> {Acc, Bin}; take_without_spaces(<<>>, _, Acc) -> {Acc, <<>>}; take_without_spaces(<<$\s, Tail/binary>>, Count, Acc) -> take_without_spaces(Tail, Count, Acc); take_without_spaces(<<$\t, Tail/binary>>, Count, Acc) -> take_without_spaces(Tail, Count, Acc); take_without_spaces(<<$\n, Tail/binary>>, Count, Acc) -> take_without_spaces(Tail, Count, Acc); take_without_spaces(<<$\r, Tail/binary>>, Count, Acc) -> take_without_spaces(Tail, Count, Acc); take_without_spaces(<>, Count, Acc) -> take_without_spaces(Tail, Count-1, <>). decode_base64_bin(<<>>, Acc) -> Acc; decode_base64_bin(Bin, Acc) -> case take_without_spaces(Bin, 4) of {<>, _} -> <>; {<>, _} -> <>; {<>, Tail} -> Acc2 = <>, decode_base64_bin(Tail, Acc2); _ -> <<"">> end. d(X) when X >= $A, X =< $Z -> X - 65; d(X) when X >= $a, X =< $z -> X - 71; d(X) when X >= $0, X =< $9 -> X + 4; d($+) -> 62; d($/) -> 63; d(_) -> 63. %% Convert Erlang inet IP to list -spec encode_base64(binary()) -> binary(). encode_base64(Data) -> encode_base64_bin(Data, <<>>). encode_base64_bin(<>, Acc) -> encode_base64_bin(Tail, <>); encode_base64_bin(<>, Acc) -> <>; encode_base64_bin(<>, Acc) -> <>; encode_base64_bin(<<>>, Acc) -> Acc. e(X) when X >= 0, X < 26 -> X + 65; e(X) when X > 25, X < 52 -> X + 71; e(X) when X > 51, X < 62 -> X - 4; e(62) -> $+; e(63) -> $/; e(X) -> exit({bad_encode_base64_token, X}). -spec ip_to_list(inet:ip_address() | undefined | {inet:ip_address(), inet:port_number()}) -> binary(). ip_to_list({IP, _Port}) -> ip_to_list(IP); %% This function clause could use inet_parse too: ip_to_list(undefined) -> <<"unknown">>; ip_to_list(IP) -> list_to_binary(inet_parse:ntoa(IP)). -spec hex_to_bin(binary()) -> binary(). hex_to_bin(Hex) -> hex_to_bin(binary_to_list(Hex), []). -spec hex_to_bin(list(), list()) -> binary(). hex_to_bin([], Acc) -> list_to_binary(lists:reverse(Acc)); hex_to_bin([H1, H2 | T], Acc) -> {ok, [V], []} = io_lib:fread("~16u", [H1, H2]), hex_to_bin(T, [V | Acc]). -spec hex_to_base64(binary()) -> binary(). hex_to_base64(Hex) -> encode_base64(hex_to_bin(Hex)). -spec expand_keyword(binary(), binary(), binary()) -> binary(). expand_keyword(Keyword, Input, Replacement) -> Parts = binary:split(Input, Keyword, [global]), str:join(Parts, Replacement). binary_to_atom(Bin) -> erlang:binary_to_atom(Bin, utf8). binary_to_integer(Bin) -> erlang:binary_to_integer(Bin). binary_to_integer(Bin, Base) -> erlang:binary_to_integer(Bin, Base). integer_to_binary(I) -> erlang:integer_to_binary(I). integer_to_binary(I, Base) -> erlang:integer_to_binary(I, Base). tuple_to_binary(T) -> iolist_to_binary(tuple_to_list(T)). atom_to_binary(A) -> erlang:atom_to_binary(A, utf8). expr_to_term(Expr) -> Str = binary_to_list(<>), {ok, Tokens, _} = erl_scan:string(Str), {ok, Term} = erl_parse:parse_term(Tokens), Term. term_to_expr(Term) -> list_to_binary(io_lib:print(Term)). l2i(I) when is_integer(I) -> I; l2i(L) when is_binary(L) -> binary_to_integer(L). i2l(I) when is_integer(I) -> integer_to_binary(I); i2l(L) when is_binary(L) -> L. i2l(I, N) when is_integer(I) -> i2l(i2l(I), N); i2l(L, N) when is_binary(L) -> case str:len(L) of N -> L; C when C > N -> L; _ -> i2l(<<$0, L/binary>>, N) end. ejabberd-18.01/src/ejabberd_ctl.erl0000644000232200023220000007226013225664356017545 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_ctl.erl %%% Author : Alexey Shchepin %%% Purpose : ejabberd command line admin tool %%% Created : 11 Jan 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @headerfile "ejabberd_ctl.hrl" %%% @doc Management of ejabberdctl commands and frontend to ejabberd commands. %%% %%% An ejabberdctl command is an abstract function identified by a %%% name, with a defined number of calling arguments, that can be %%% defined in any Erlang module and executed using ejabberdctl %%% administration script. %%% %%% Note: strings cannot have blankspaces %%% %%% Does not support commands that have arguments with ctypes: list, tuple %%% %%% TODO: Update the guide %%% TODO: Mention this in the release notes %%% Note: the commands with several words use now the underline: _ %%% It is still possible to call the commands with dash: - %%% but this is deprecated, and may be removed in a future version. -module(ejabberd_ctl). -behaviour(ejabberd_config). -behaviour(gen_server). -author('alexey@process-one.net'). -export([start/0, start_link/0, process/1, process2/2, register_commands/3, unregister_commands/3, opt_type/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd_ctl.hrl"). -include("ejabberd_commands.hrl"). -include("ejabberd.hrl"). -include("logger.hrl"). -define(DEFAULT_VERSION, 1000000). -record(state, {}). %%----------------------------- %% Module %%----------------------------- start() -> [SNode, Timeout, Args] = case init:get_plain_arguments() of [SNode2, "--no-timeout" | Args2] -> [SNode2, infinity, Args2]; [SNode3 | Args3] -> [SNode3, 60000, Args3]; _ -> print_usage(?DEFAULT_VERSION), halt(?STATUS_USAGE) end, SNode1 = case string:tokens(SNode, "@") of [_Node, _Server] -> SNode; _ -> case net_kernel:longnames() of true -> lists:flatten([SNode, "@", inet_db:gethostname(), ".", inet_db:res_option(domain)]); false -> lists:flatten([SNode, "@", inet_db:gethostname()]); _ -> SNode end end, Node = list_to_atom(SNode1), Status = case rpc:call(Node, ?MODULE, process, [Args], Timeout) of {badrpc, Reason} -> print("Failed RPC connection to the node ~p: ~p~n", [Node, Reason]), %% TODO: show minimal start help ?STATUS_BADRPC; {invalid_version, V} -> print("Invalid API version number: ~p~n", [V]), ?STATUS_ERROR; S -> S end, halt(Status). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> ets:new(ejabberd_ctl_cmds, [named_table, set, public]), ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%----------------------------- %% ejabberdctl Command managment %%----------------------------- register_commands(CmdDescs, Module, Function) -> ets:insert(ejabberd_ctl_cmds, CmdDescs), ejabberd_hooks:add(ejabberd_ctl_process, Module, Function, 50), ok. unregister_commands(CmdDescs, Module, Function) -> lists:foreach(fun(CmdDesc) -> ets:delete_object(ejabberd_ctl_cmds, CmdDesc) end, CmdDescs), ejabberd_hooks:delete(ejabberd_ctl_process, Module, Function, 50), ok. %%----------------------------- %% Process %%----------------------------- -spec process([string()]) -> non_neg_integer(). process(Args) -> process(Args, ?DEFAULT_VERSION). -spec process([string()], non_neg_integer()) -> non_neg_integer(). %% The commands status, stop and restart are defined here to ensure %% they are usable even if ejabberd is completely stopped. process(["status"], _Version) -> {InternalStatus, ProvidedStatus} = init:get_status(), print("The node ~p is ~p with status: ~p~n", [node(), InternalStatus, ProvidedStatus]), case lists:keysearch(ejabberd, 1, application:which_applications()) of false -> EjabberdLogPath = ejabberd_logger:get_log_path(), print("ejabberd is not running in that node~n" "Check for error messages: ~s~n" "or other files in that directory.~n", [EjabberdLogPath]), ?STATUS_ERROR; {value, {_, _, Version}} -> print("ejabberd ~s is running in that node~n", [Version]), ?STATUS_SUCCESS end; process(["stop"], _Version) -> %%ejabberd_cover:stop(), init:stop(), ?STATUS_SUCCESS; process(["restart"], _Version) -> init:restart(), ?STATUS_SUCCESS; process(["mnesia"], _Version) -> print("~p~n", [mnesia:system_info(all)]), ?STATUS_SUCCESS; process(["mnesia", "info"], _Version) -> mnesia:info(), ?STATUS_SUCCESS; process(["mnesia", Arg], _Version) -> case catch mnesia:system_info(list_to_atom(Arg)) of {'EXIT', Error} -> print("Error: ~p~n", [Error]); Return -> print("~p~n", [Return]) end, ?STATUS_SUCCESS; %% The arguments --long and --dual are not documented because they are %% automatically selected depending in the number of columns of the shell process(["help" | Mode], Version) -> {MaxC, ShCode} = get_shell_info(), case Mode of [] -> print_usage(dual, MaxC, ShCode, Version), ?STATUS_USAGE; ["--dual"] -> print_usage(dual, MaxC, ShCode, Version), ?STATUS_USAGE; ["--long"] -> print_usage(long, MaxC, ShCode, Version), ?STATUS_USAGE; ["--tags"] -> print_usage_tags(MaxC, ShCode, Version), ?STATUS_SUCCESS; ["--tags", Tag] -> print_usage_tags(Tag, MaxC, ShCode, Version), ?STATUS_SUCCESS; ["help"] -> print_usage_help(MaxC, ShCode), ?STATUS_SUCCESS; [CmdString | _] -> CmdStringU = ejabberd_regexp:greplace( list_to_binary(CmdString), <<"-">>, <<"_">>), print_usage_commands2(binary_to_list(CmdStringU), MaxC, ShCode, Version), ?STATUS_SUCCESS end; process(["--version", Arg | Args], _) -> Version = try list_to_integer(Arg) catch _:_ -> throw({invalid_version, Arg}) end, process(Args, Version); process(Args, Version) -> AccessCommands = get_accesscommands(), {String, Code} = process2(Args, AccessCommands, Version), case String of [] -> ok; _ -> io:format("~s~n", [String]) end, Code. %% @spec (Args::[string()], AccessCommands) -> {String::string(), Code::integer()} process2(Args, AccessCommands) -> process2(Args, AccessCommands, ?DEFAULT_VERSION). %% @spec (Args::[string()], AccessCommands, Version) -> {String::string(), Code::integer()} process2(["--auth", User, Server, Pass | Args], AccessCommands, Version) -> process2(Args, AccessCommands, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass), true}, Version); process2(Args, AccessCommands, Version) -> process2(Args, AccessCommands, noauth, Version). process2(Args, AccessCommands, Auth, Version) -> case try_run_ctp(Args, Auth, AccessCommands, Version) of {String, wrong_command_arguments} when is_list(String) -> io:format(lists:flatten(["\n" | String]++["\n"])), [CommandString | _] = Args, process(["help" | [CommandString]], Version), {lists:flatten(String), ?STATUS_ERROR}; {String, Code} when is_list(String) and is_integer(Code) -> {lists:flatten(String), Code}; String when is_list(String) -> {lists:flatten(String), ?STATUS_SUCCESS}; Code when is_integer(Code) -> {"", Code}; Other -> {"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR} end. get_accesscommands() -> ejabberd_config:get_option(ejabberdctl_access_commands, []). %%----------------------------- %% Command calling %%----------------------------- %% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} try_run_ctp(Args, Auth, AccessCommands, Version) -> try ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of false when Args /= [] -> try_call_command(Args, Auth, AccessCommands, Version); false -> print_usage(Version), {"", ?STATUS_USAGE}; Status -> {"", Status} catch exit:Why -> print_usage(Version), {io_lib:format("Error in ejabberd ctl process: ~p", [Why]), ?STATUS_USAGE}; Error:Why -> %% In this case probably ejabberd is not started, so let's show Status process(["status"], Version), print("~n", []), {io_lib:format("Error in ejabberd ctl process: '~p' ~p", [Error, Why]), ?STATUS_USAGE} end. %% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} try_call_command(Args, Auth, AccessCommands, Version) -> try call_command(Args, Auth, AccessCommands, Version) of {error, command_unknown} -> {io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR}; {error, wrong_command_arguments} -> {"Error: wrong arguments", ?STATUS_ERROR}; Res -> Res catch throw:Error -> {io_lib:format("~p", [Error]), ?STATUS_ERROR}; A:Why -> Stack = erlang:get_stacktrace(), {io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR} end. %% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, ErrorType} call_command([CmdString | Args], Auth, _AccessCommands, Version) -> CmdStringU = ejabberd_regexp:greplace( list_to_binary(CmdString), <<"-">>, <<"_">>), Command = list_to_atom(binary_to_list(CmdStringU)), case ejabberd_commands:get_command_format(Command, Auth, Version) of {error, command_unknown} -> {error, command_unknown}; {ArgsFormat, ResultFormat} -> case (catch format_args(Args, ArgsFormat)) of ArgsFormatted when is_list(ArgsFormatted) -> CI = case Auth of {U, S, _, _} -> #{usr => {U, S, <<"">>}, caller_host => S}; _ -> #{} end, CI2 = CI#{caller_module => ?MODULE}, Result = ejabberd_commands:execute_command2(Command, ArgsFormatted, CI2, Version), format_result(Result, ResultFormat); {'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} -> {NumCompa, TextCompa} = case {length(A1), length(A2)} of {L1, L2} when L1 < L2 -> {L2-L1, "less argument"}; {L1, L2} when L1 > L2 -> {L1-L2, "more argument"} end, {io_lib:format("Error: the command ~p requires ~p ~s.", [CmdString, NumCompa, TextCompa]), wrong_command_arguments} end end. %%----------------------------- %% Format arguments %%----------------------------- format_args(Args, ArgsFormat) -> lists:foldl( fun({{_ArgName, ArgFormat}, Arg}, Res) -> Formatted = format_arg(Arg, ArgFormat), Res ++ [Formatted] end, [], lists:zip(ArgsFormat, Args)). format_arg(Arg, integer) -> format_arg2(Arg, "~d"); format_arg(Arg, binary) -> unicode:characters_to_binary(Arg, utf8); format_arg("", string) -> ""; format_arg(Arg, string) -> NumChars = integer_to_list(length(Arg)), Parse = "~" ++ NumChars ++ "c", format_arg2(Arg, Parse). format_arg2(Arg, Parse)-> {ok, [Arg2], _RemainingArguments} = io_lib:fread(Parse, Arg), Arg2. %%----------------------------- %% Format result %%----------------------------- format_result({error, ErrorAtom}, _) -> {io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)}; %% An error should always be allowed to return extended error to help with API. %% Extended error is of the form: %% {error, type :: atom(), code :: int(), Desc :: string()} format_result({error, ErrorAtom, Code, _Msg}, _) -> {io_lib:format("Error: ~p", [ErrorAtom]), make_status(Code)}; format_result(Atom, {_Name, atom}) -> io_lib:format("~p", [Atom]); format_result(Int, {_Name, integer}) -> io_lib:format("~p", [Int]); format_result([A|_]=String, {_Name, string}) when is_list(String) and is_integer(A) -> io_lib:format("~s", [String]); format_result(Binary, {_Name, string}) when is_binary(Binary) -> io_lib:format("~s", [binary_to_list(Binary)]); format_result(Atom, {_Name, string}) when is_atom(Atom) -> io_lib:format("~s", [atom_to_list(Atom)]); format_result(Integer, {_Name, string}) when is_integer(Integer) -> io_lib:format("~s", [integer_to_list(Integer)]); format_result(Other, {_Name, string}) -> io_lib:format("~p", [Other]); format_result(Code, {_Name, rescode}) -> make_status(Code); format_result({Code, Text}, {_Name, restuple}) -> {io_lib:format("~s", [Text]), make_status(Code)}; %% The result is a list of something: [something()] format_result([], {_Name, {list, _ElementsDef}}) -> ""; format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> %% Start formatting the first element [format_result(FirstElement, ElementsDef) | %% If there are more elements, put always first a newline character lists:map( fun(Element) -> ["\n" | format_result(Element, ElementsDef)] end, Elements)]; %% The result is a tuple with several elements: {something1(), something2(),...} %% NOTE: the elements in the tuple are separated with tabular characters, %% if a string is empty, it will be difficult to notice in the shell, %% maybe a different separation character should be used, like ;;? format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}) -> ElementsList = tuple_to_list(ElementsTuple), [{FirstE, FirstD} | ElementsAndDef] = lists:zip(ElementsList, ElementsDef), [format_result(FirstE, FirstD) | lists:map( fun({Element, ElementDef}) -> ["\t" | format_result(Element, ElementDef)] end, ElementsAndDef)]; format_result(404, {_Name, _}) -> make_status(not_found). make_status(ok) -> ?STATUS_SUCCESS; make_status(true) -> ?STATUS_SUCCESS; make_status(Code) when is_integer(Code), Code > 255 -> ?STATUS_ERROR; make_status(Code) when is_integer(Code), Code > 0 -> Code; make_status(_Error) -> ?STATUS_ERROR. get_list_commands(Version) -> try ejabberd_commands:list_commands(Version) of Commands -> [tuple_command_help(Command) || {N,_,_}=Command <- Commands, %% Don't show again those commands, because they are already %% announced by ejabberd_ctl itself N /= status, N /= stop, N /= restart] catch exit:_ -> [] end. %% Return: {string(), [string()], string()} tuple_command_help({Name, _Args, Desc}) -> {Args, _} = ejabberd_commands:get_command_format(Name, admin), Arguments = [atom_to_list(ArgN) || {ArgN, _ArgF} <- Args], Prepend = case is_supported_args(Args) of true -> ""; false -> "*" end, CallString = atom_to_list(Name), {CallString, Arguments, Prepend ++ Desc}. is_supported_args(Args) -> lists:all( fun({_Name, Format}) -> (Format == integer) or (Format == string) or (Format == binary) end, Args). get_list_ctls() -> case catch ets:tab2list(ejabberd_ctl_cmds) of {'EXIT', _} -> []; Cs -> [{NameArgs, [], Desc} || {NameArgs, Desc} <- Cs] end. %%----------------------------- %% Print help %%----------------------------- %% Bold -define(B1, "\e[1m"). -define(B2, "\e[22m"). -define(B(S), case ShCode of true -> [?B1, S, ?B2]; false -> S end). %% Underline -define(U1, "\e[4m"). -define(U2, "\e[24m"). -define(U(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end). print_usage(Version) -> {MaxC, ShCode} = get_shell_info(), print_usage(dual, MaxC, ShCode, Version). print_usage(HelpMode, MaxC, ShCode, Version) -> AllCommands = [ {"status", [], "Get ejabberd status"}, {"stop", [], "Stop ejabberd"}, {"restart", [], "Restart ejabberd"}, {"help", ["[--tags [tag] | com?*]"], "Show help (try: ejabberdctl help help)"}, {"mnesia", ["[info]"], "show information of Mnesia system"}] ++ get_list_commands(Version) ++ get_list_ctls(), print( ["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--version ", ?U("api_version"), "] ", ?U("command"), " [", ?U("options"), "]\n" "\n" "Available commands in this ejabberd node:\n"], []), print_usage_commands(HelpMode, MaxC, ShCode, AllCommands), print( ["\n" "Examples:\n" " ejabberdctl restart\n" " ejabberdctl --node ejabberd@host restart\n"], []). print_usage_commands(HelpMode, MaxC, ShCode, Commands) -> CmdDescsSorted = lists:keysort(1, Commands), %% What is the length of the largest command? {CmdArgsLenDescsSorted, Lens} = lists:mapfoldl( fun({Cmd, Args, Desc}, Lengths) -> Len = length(Cmd) + lists:foldl(fun(Arg, R) -> R + 1 + length(Arg) end, 0, Args), {{Cmd, Args, Len, Desc}, [Len | Lengths]} end, [], CmdDescsSorted), MaxCmdLen = case Lens of [] -> 80; _ -> lists:max(Lens) end, %% For each command in the list of commands %% Convert its definition to a line FmtCmdDescs = format_command_lines(CmdArgsLenDescsSorted, MaxCmdLen, MaxC, ShCode, HelpMode), print([FmtCmdDescs], []). %% Get some info about the shell: %% how many columns of width %% and guess if it supports text formatting codes. get_shell_info() -> %% This function was introduced in OTP R12B-0 try io:columns() of {ok, C} -> {C-2, true}; {error, enotsup} -> {78, false} catch _:_ -> {78, false} end. %% Split this command description in several lines of proper length prepare_description(DescInit, MaxC, Desc) -> Words = string:tokens(Desc, " "), prepare_long_line(DescInit, MaxC, Words). prepare_long_line(DescInit, MaxC, Words) -> MaxSegmentLen = MaxC - DescInit, MarginString = lists:duplicate(DescInit, $\s), % Put spaces [FirstSegment | MoreSegments] = split_desc_segments(MaxSegmentLen, Words), MoreSegmentsMixed = mix_desc_segments(MarginString, MoreSegments), [FirstSegment | MoreSegmentsMixed]. mix_desc_segments(MarginString, Segments) -> [["\n", MarginString, Segment] || Segment <- Segments]. split_desc_segments(MaxL, Words) -> join(MaxL, Words). %% Join words in a segment, %% but stop adding to a segment if adding this word would pass L join(L, Words) -> join(L, Words, 0, [], []). join(_Len, [], _CurSegLen, CurSeg, AllSegs) -> lists:reverse([CurSeg | AllSegs]); join(Len, [Word | Tail], CurSegLen, CurSeg, AllSegs) -> WordLen = length(Word), SegSize = WordLen + CurSegLen + 1, {NewCurSeg, NewAllSegs, NewCurSegLen} = if SegSize < Len -> {[CurSeg, " ", Word], AllSegs, SegSize}; true -> {Word, [CurSeg | AllSegs], WordLen} end, NewLen = case string:str(Word, "\n") of 0 -> NewCurSegLen; _ -> 0 end, join(Len, Tail, NewLen, NewCurSeg, NewAllSegs). format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) when MaxC - MaxCmdLen < 40 -> %% If the space available for descriptions is too narrow, enforce long help mode format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, long); format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) -> lists:map( fun({Cmd, Args, CmdArgsL, Desc}) -> DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc), [" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], string:chars($\s, MaxCmdLen - CmdArgsL + 1), DescFmt, "\n"] end, CALD); format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) -> lists:map( fun({Cmd, Args, _CmdArgsL, Desc}) -> DescFmt = prepare_description(8, MaxC, Desc), ["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ", DescFmt, "\n"] end, CALD). %%----------------------------- %% Print Tags %%----------------------------- print_usage_tags(MaxC, ShCode, Version) -> print("Available tags and commands:", []), TagsCommands = ejabberd_commands:get_tags_commands(Version), lists:foreach( fun({Tag, Commands} = _TagCommands) -> print(["\n\n ", ?B(Tag), "\n "], []), Words = lists:sort(Commands), Desc = prepare_long_line(5, MaxC, Words), print(Desc, []) end, TagsCommands), print("\n\n", []). print_usage_tags(Tag, MaxC, ShCode, Version) -> print(["Available commands with tag ", ?B(Tag), ":", "\n"], []), HelpMode = long, TagsCommands = ejabberd_commands:get_tags_commands(Version), CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of {value, {Tag, CNs}} -> CNs; false -> [] end, CommandsList = lists:map( fun(NameString) -> C = ejabberd_commands:get_command_definition( list_to_atom(NameString), Version), #ejabberd_commands{name = Name, args = Args, desc = Desc} = C, tuple_command_help({Name, Args, Desc}) end, CommandsNames), print_usage_commands(HelpMode, MaxC, ShCode, CommandsList), print("\n", []). %%----------------------------- %% Print usage of 'help' command %%----------------------------- print_usage_help(MaxC, ShCode) -> LongDesc = ["The special 'help' ejabberdctl command provides help of ejabberd commands.\n\n" "The format is:\n ", ?B("ejabberdctl"), " ", ?B("help"), " [", ?B("--tags"), " ", ?U("[tag]"), " | ", ?U("com?*"), "]\n\n" "The optional arguments:\n" " ",?B("--tags")," Show all tags and the names of commands in each tag\n" " ",?B("--tags"), " ", ?U("tag")," Show description of commands in this tag\n" " ",?U("command")," Show detailed description of the command\n" " ",?U("com?*")," Show detailed description of commands that match this glob.\n" " You can use ? to match a simple character,\n" " and * to match several characters.\n" "\n", "Some example usages:\n", " ejabberdctl help\n", " ejabberdctl help --tags\n", " ejabberdctl help --tags accounts\n", " ejabberdctl help register\n", " ejabberdctl help regist*\n", "\n", "Please note that 'ejabberdctl help' shows all ejabberd commands,\n", "even those that cannot be used in the shell with ejabberdctl.\n", "Those commands can be identified because the description starts with: *"], ArgsDef = [], C = #ejabberd_commands{ desc = "Show help of ejabberd commands", longdesc = lists:flatten(LongDesc), args = ArgsDef, result = {help, string}}, print_usage_command("help", C, MaxC, ShCode). %%----------------------------- %% Print usage command %%----------------------------- %% @spec (CmdSubString::string(), MaxC::integer(), ShCode::boolean()) -> ok print_usage_commands2(CmdSubString, MaxC, ShCode, Version) -> %% Get which command names match this substring AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)], Cmds = filter_commands(AllCommandsNames, CmdSubString), case Cmds of [] -> io:format("Error: no command found that match: ~p~n", [CmdSubString]); _ -> print_usage_commands3(lists:sort(Cmds), MaxC, ShCode, Version) end. print_usage_commands3(Cmds, MaxC, ShCode, Version) -> %% Then for each one print it lists:mapfoldl( fun(Cmd, Remaining) -> print_usage_command(Cmd, MaxC, ShCode, Version), case Remaining > 1 of true -> print([" ", lists:duplicate(MaxC, 126), " \n"], []); false -> ok end, {ok, Remaining-1} end, length(Cmds), Cmds). filter_commands(All, SubString) -> case lists:member(SubString, All) of true -> [SubString]; false -> filter_commands_regexp(All, SubString) end. filter_commands_regexp(All, Glob) -> RegExp = ejabberd_regexp:sh_to_awk(list_to_binary(Glob)), lists:filter( fun(Command) -> case ejabberd_regexp:run(list_to_binary(Command), RegExp) of match -> true; nomatch -> false end end, All). %% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok print_usage_command(Cmd, MaxC, ShCode, Version) -> Name = list_to_atom(Cmd), case ejabberd_commands:get_command_definition(Name, Version) of command_not_found -> io:format("Error: command ~p not known.~n", [Cmd]); C -> print_usage_command2(Cmd, C, MaxC, ShCode) end. print_usage_command2(Cmd, C, MaxC, ShCode) -> #ejabberd_commands{ tags = TagsAtoms, desc = Desc, longdesc = LongDesc, result = ResultDef} = C, NameFmt = [" ", ?B("Command Name"), ": ", Cmd, "\n"], %% Initial indentation of result is 13 = length(" Arguments: ") {ArgsDef, _} = ejabberd_commands:get_command_format( C#ejabberd_commands.name, admin), Args = [format_usage_ctype(ArgDef, 13) || ArgDef <- ArgsDef], ArgsMargin = lists:duplicate(13, $\s), ArgsListFmt = case Args of [] -> "\n"; _ -> [ [Arg, "\n", ArgsMargin] || Arg <- Args] end, ArgsFmt = [" ", ?B("Arguments"), ": ", ArgsListFmt], %% Initial indentation of result is 11 = length(" Returns: ") ResultFmt = format_usage_ctype(ResultDef, 11), ReturnsFmt = [" ",?B("Returns"),": ", ResultFmt], XmlrpcFmt = "", %%+++ [" ",?B("XML-RPC"),": ", format_usage_xmlrpc(ArgsDef, ResultDef), "\n\n"], TagsFmt = [" ",?B("Tags"),": ", prepare_long_line(8, MaxC, [atom_to_list(TagA) || TagA <- TagsAtoms])], DescFmt = [" ",?B("Description"),": ", prepare_description(15, MaxC, Desc)], LongDescFmt = case LongDesc of "" -> ""; _ -> ["", prepare_description(0, MaxC, LongDesc), "\n\n"] end, NoteEjabberdctl = case is_supported_args(ArgsDef) of true -> ""; false -> [" ", ?B("Note:"), " This command cannot be executed using ejabberdctl. Try ejabberd_xmlrpc.\n\n"] end, print(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt, NoteEjabberdctl], []). format_usage_ctype(Type, _Indentation) when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> io_lib:format("~p", [Type]); format_usage_ctype({Name, Type}, _Indentation) when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> io_lib:format("~p::~p", [Name, Type]); format_usage_ctype({Name, {list, ElementDef}}, Indentation) -> NameFmt = atom_to_list(Name), Indentation2 = Indentation + length(NameFmt) + 4, ElementFmt = format_usage_ctype(ElementDef, Indentation2), [NameFmt, "::[ ", ElementFmt, " ]"]; format_usage_ctype({Name, {tuple, ElementsDef}}, Indentation) -> NameFmt = atom_to_list(Name), Indentation2 = Indentation + length(NameFmt) + 4, ElementsFmt = format_usage_tuple(ElementsDef, Indentation2), [NameFmt, "::{ " | ElementsFmt]. format_usage_tuple([], _Indentation) -> []; format_usage_tuple([ElementDef], Indentation) -> [format_usage_ctype(ElementDef, Indentation) , " }"]; format_usage_tuple([ElementDef | ElementsDef], Indentation) -> ElementFmt = format_usage_ctype(ElementDef, Indentation), MarginString = lists:duplicate(Indentation, $\s), % Put spaces [ElementFmt, ",\n", MarginString, format_usage_tuple(ElementsDef, Indentation)]. print(Format, Args) -> io:format(lists:flatten(Format), Args). %%----------------------------- %% Command managment %%----------------------------- %%+++ %% Struct(Integer res) create_account(Struct(String user, String server, String password)) %%format_usage_xmlrpc(ArgsDef, ResultDef) -> %% ["aaaa bbb ccc"]. -spec opt_type(ejabberdctl_access_commands) -> fun((list()) -> list()); (atom()) -> [atom()]. opt_type(ejabberdctl_access_commands) -> fun (V) when is_list(V) -> V end; opt_type(_) -> [ejabberdctl_access_commands]. ejabberd-18.01/src/ejabberd.erl0000644000232200023220000001322513225664356016677 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd.erl %%% Author : Alexey Shchepin %%% Purpose : ejabberd wrapper: start / stop %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd). -author('alexey@process-one.net'). -protocol({xep, 4, '2.9'}). -protocol({xep, 86, '1.0'}). -protocol({xep, 106, '1.1'}). -protocol({xep, 170, '1.0'}). -protocol({xep, 205, '1.0'}). -protocol({xep, 212, '1.0'}). -protocol({xep, 216, '1.0'}). -protocol({xep, 243, '1.0'}). -protocol({xep, 270, '1.0'}). -export([start/0, stop/0, start_app/1, start_app/2, get_pid_file/0, check_app/1, module_name/1]). -include("logger.hrl"). start() -> %%ejabberd_cover:start(), application:start(ejabberd). stop() -> application:stop(ejabberd). %%ejabberd_cover:stop(). %% @spec () -> false | string() get_pid_file() -> case os:getenv("EJABBERD_PID_PATH") of false -> false; "" -> false; Path -> Path end. start_app(App) -> start_app(App, temporary). start_app(App, Type) -> StartFlag = not is_loaded(), start_app(App, Type, StartFlag). check_app(App) -> StartFlag = not is_loaded(), spawn(fun() -> check_app_modules(App, StartFlag) end), ok. is_loaded() -> Apps = application:which_applications(), lists:keymember(ejabberd, 1, Apps). start_app(App, Type, StartFlag) when not is_list(App) -> start_app([App], Type, StartFlag); start_app([App|Apps], Type, StartFlag) -> case application:start(App,Type) of ok -> spawn(fun() -> check_app_modules(App, StartFlag) end), start_app(Apps, Type, StartFlag); {error, {already_started, _}} -> start_app(Apps, Type, StartFlag); {error, {not_started, DepApp}} -> case lists:member(DepApp, [App|Apps]) of true -> Reason = io_lib:format( "failed to start application '~p': " "circular dependency on '~p' detected", [App, DepApp]), exit_or_halt(Reason, StartFlag); false -> start_app([DepApp,App|Apps], Type, StartFlag) end; Err -> Reason = io_lib:format("failed to start application '~p': ~p", [App, Err]), exit_or_halt(Reason, StartFlag) end; start_app([], _Type, _StartFlag) -> ok. check_app_modules(App, StartFlag) -> sleep(5000), case application:get_key(App, modules) of {ok, Mods} -> lists:foreach( fun(Mod) -> case code:which(Mod) of non_existing -> File = get_module_file(App, Mod), Reason = io_lib:format( "couldn't find module ~s " "needed for application '~p'", [File, App]), exit_or_halt(Reason, StartFlag); _ -> sleep(10) end end, Mods); _ -> %% No modules? This is strange ok end. exit_or_halt(Reason, StartFlag) -> ?CRITICAL_MSG(Reason, []), if StartFlag -> %% Wait for the critical message is written in the console/log timer:sleep(1000), halt(string:substr(lists:flatten(Reason), 1, 199)); true -> erlang:error(application_start_failed) end. sleep(N) -> timer:sleep(randoms:uniform(N)). get_module_file(App, Mod) -> BaseName = atom_to_list(Mod), case code:lib_dir(App, ebin) of {error, _} -> BaseName; Dir -> filename:join([Dir, BaseName ++ ".beam"]) end. module_name([Dir, _, <> | _] = Mod) when H >= 65, H =< 90 -> Module = str:join([elixir_name(M) || M<-tl(Mod)], <<>>), Prefix = case elixir_name(Dir) of <<"Ejabberd">> -> <<"Elixir.Ejabberd.">>; Lib -> <<"Elixir.Ejabberd.", Lib/binary, ".">> end, misc:binary_to_atom(<>); module_name([<<"ejabberd">> | _] = Mod) -> Module = str:join([erlang_name(M) || M<-Mod], $_), misc:binary_to_atom(Module); module_name(Mod) when is_list(Mod) -> Module = str:join([erlang_name(M) || M<-tl(Mod)], $_), misc:binary_to_atom(Module). elixir_name(Atom) when is_atom(Atom) -> elixir_name(misc:atom_to_binary(Atom)); elixir_name(<>) when H >= 65, H =< 90 -> <>; elixir_name(<>) -> <<(H-32), T/binary>>. erlang_name(Atom) when is_atom(Atom) -> misc:atom_to_binary(Atom); erlang_name(Bin) when is_binary(Bin) -> Bin. ejabberd-18.01/src/mod_irc_sql.erl0000644000232200023220000000745013225664356017437 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_irc_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_irc_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_irc). %% API -export([init/2, get_data/3, set_data/4, import/1, import/2, export/1]). -include("jid.hrl"). -include("mod_irc.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. get_data(LServer, Host, From) -> SJID = jid:encode(jid:tolower(jid:remove_resource(From))), case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(data)s from irc_custom" " where jid=%(SJID)s and host=%(Host)s and %(LServer)H")) of {selected, [{SData}]} -> mod_irc:data_to_binary(From, ejabberd_sql:decode_term(SData)); {'EXIT', _} -> error; {selected, _} -> empty end. set_data(LServer, Host, From, Data) -> SJID = jid:encode(jid:tolower(jid:remove_resource(From))), SData = misc:term_to_expr(Data), F = fun () -> ?SQL_UPSERT_T( "irc_custom", ["!jid=%(SJID)s", "!host=%(Host)s", "server_host=%(LServer)s", "data=%(SData)s"]), ok end, ejabberd_sql:sql_transaction(LServer, F). export(_Server) -> [{irc_custom, fun(Host, #irc_custom{us_host = {{U, S}, IRCHost}, data = Data}) -> case str:suffix(Host, IRCHost) of true -> SJID = jid:encode(jid:make(U, S)), LServer = ejabberd_router:host_of_route(IRCHost), SData = misc:term_to_expr(Data), [?SQL("delete from irc_custom" " where jid=%(SJID)s and host=%(IRCHost)s and %(LServer)H;"), ?SQL_INSERT( "irc_custom", ["jid=%(SJID)s", "host=%(Host)s", "server_host=%(LServer)s", "data=%(SData)s"])]; false -> [] end end}]. import(_LServer) -> [{<<"select jid, host, data from irc_custom;">>, fun([SJID, IRCHost, SData]) -> #jid{luser = U, lserver = S} = jid:decode(SJID), Data = ejabberd_sql:decode_term(SData), #irc_custom{us_host = {{U, S}, IRCHost}, data = Data} end}]. import(_, _) -> pass. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/ejabberd_auth_riak.erl0000644000232200023220000000713213225664356020726 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_auth_riak.erl %%% Author : Evgeniy Khramtsov %%% Purpose : Authentification via Riak %%% Created : 12 Nov 2012 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_auth_riak). -compile([{parse_transform, ejabberd_sql_pt}]). -author('alexey@process-one.net'). -behaviour(ejabberd_auth). %% External exports -export([start/1, stop/1, set_password/3, try_register/3, get_users/2, count_users/2, get_password/2, remove_user/2, store_type/1, export/1, import/2, plain_password_required/1]). -export([passwd_schema/0]). -include("ejabberd.hrl"). -include("ejabberd_sql_pt.hrl"). -include("ejabberd_auth.hrl"). start(_Host) -> ok. stop(_Host) -> ok. plain_password_required(Server) -> store_type(Server) == scram. store_type(Server) -> ejabberd_auth:password_format(Server). passwd_schema() -> {record_info(fields, passwd), #passwd{}}. set_password(User, Server, Password) -> ejabberd_riak:put(#passwd{us = {User, Server}, password = Password}, passwd_schema(), [{'2i', [{<<"host">>, Server}]}]). try_register(User, Server, Password) -> US = {User, Server}, case ejabberd_riak:get(passwd, passwd_schema(), US) of {error, notfound} -> ejabberd_riak:put(#passwd{us = US, password = Password}, passwd_schema(), [{'2i', [{<<"host">>, Server}]}]); {ok, _} -> {error, exists}; {error, _} = Err -> Err end. get_users(Server, _) -> case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, Server) of {ok, Users} -> Users; _ -> [] end. count_users(Server, _) -> case ejabberd_riak:count_by_index(passwd, <<"host">>, Server) of {ok, N} -> N; _ -> 0 end. get_password(User, Server) -> case ejabberd_riak:get(passwd, passwd_schema(), {User, Server}) of {ok, Password} -> {ok, Password}; {error, _} -> error end. remove_user(User, Server) -> ejabberd_riak:delete(passwd, {User, Server}). export(_Server) -> [{passwd, fun(Host, #passwd{us = {LUser, LServer}, password = Password}) when LServer == Host -> [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(Password)s"])]; (_Host, _R) -> [] end}]. import(LServer, [LUser, Password, _TimeStamp]) -> Passwd = #passwd{us = {LUser, LServer}, password = Password}, ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]). ejabberd-18.01/src/mod_http_api.erl0000644000232200023220000004653213225664356017617 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_http_api.erl %%% Author : Christophe romain %%% Purpose : Implements REST API for ejabberd using JSON data %%% Created : 15 Sep 2014 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %% Example config: %% %% in ejabberd_http listener %% request_handlers: %% "/api": mod_http_api %% %% To use a specific API version N, add a vN element in the URL path: %% in ejabberd_http listener %% request_handlers: %% "/api/v2": mod_http_api %% %% Access rights are defined with: %% commands_admin_access: configure %% commands: %% - add_commands: user %% %% %% add_commands allow exporting a class of commands, from %% open: methods is not risky and can be called by without any access check %% restricted (default): the same, but will appear only in ejabberdctl list. %% admin – auth is required with XMLRPC and HTTP API and checked for admin priviledges, works as usual in ejabberdctl. %% user - can be used through XMLRPC and HTTP API, even by user. Only admin can use the commands for other users. %% %% Then to perform an action, send a POST request to the following URL: %% http://localhost:5280/api/ %% %% It's also possible to enable unrestricted access to some commands from group %% of IP addresses by using option `admin_ip_access` by having fragment like %% this in configuration file: %% modules: %% mod_http_api: %% admin_ip_access: admin_ip_access_rule %%... %% access: %% admin_ip_access_rule: %% admin_ip_acl: %% - command1 %% - command2 %% %% use `all` to give access to all commands %%... %% acl: %% admin_ip_acl: %% ip: %% - "127.0.0.1/8" -module(mod_http_api). -author('cromain@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). -define(DEFAULT_API_VERSION, 0). -define(CT_PLAIN, {<<"Content-Type">>, <<"text/plain">>}). -define(CT_XML, {<<"Content-Type">>, <<"text/xml; charset=utf-8">>}). -define(CT_JSON, {<<"Content-Type">>, <<"application/json">>}). -define(AC_ALLOW_ORIGIN, {<<"Access-Control-Allow-Origin">>, <<"*">>}). -define(AC_ALLOW_METHODS, {<<"Access-Control-Allow-Methods">>, <<"GET, POST, OPTIONS">>}). -define(AC_ALLOW_HEADERS, {<<"Access-Control-Allow-Headers">>, <<"Content-Type, Authorization, X-Admin">>}). -define(AC_MAX_AGE, {<<"Access-Control-Max-Age">>, <<"86400">>}). -define(OPTIONS_HEADER, [?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS, ?AC_ALLOW_HEADERS, ?AC_MAX_AGE]). -define(HEADER(CType), [CType, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]). %% ------------------- %% Module control %% ------------------- start(_Host, _Opts) -> ejabberd_access_permissions:register_permission_addon(?MODULE, fun permission_addon/0), ok. stop(_Host) -> ejabberd_access_permissions:unregister_permission_addon(?MODULE), ok. reload(Host, NewOpts, _OldOpts) -> stop(Host), start(Host, NewOpts). depends(_Host, _Opts) -> []. %% ---------- %% basic auth %% ---------- extract_auth(#request{auth = HTTPAuth, ip = {IP, _}}) -> Info = case HTTPAuth of {SJID, Pass} -> try jid:decode(SJID) of #jid{luser = User, lserver = Server} -> case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of true -> #{usr => {User, Server, <<"">>}, caller_server => Server}; false -> {error, invalid_auth} end catch _:{bad_jid, _} -> {error, invalid_auth} end; {oauth, Token, _} -> case ejabberd_oauth:check_token(Token) of {ok, {U, S}, Scope} -> #{usr => {U, S, <<"">>}, oauth_scope => Scope, caller_server => S}; {false, Reason} -> {error, Reason} end; _ -> #{} end, case Info of Map when is_map(Map) -> Map#{caller_module => ?MODULE, ip => IP}; _ -> ?DEBUG("Invalid auth data: ~p", [Info]), Info end; extract_auth(#request{ip = IP}) -> #{ip => IP, caller_module => ?MODULE}. %% ------------------ %% command processing %% ------------------ %process(Call, Request) -> % ?DEBUG("~p~n~p", [Call, Request]), ok; process(_, #request{method = 'POST', data = <<>>}) -> ?DEBUG("Bad Request: no data", []), badrequest_response(<<"Missing POST data">>); process([Call], #request{method = 'POST', data = Data, ip = IPPort} = Req) -> Version = get_api_version(Req), try Args = extract_args(Data), log(Call, Args, IPPort), perform_call(Call, Args, Req, Version) catch %% TODO We need to refactor to remove redundant error return formatting throw:{error, unknown_command} -> json_format({404, 44, <<"Command not found.">>}); _:{error,{_,invalid_json}} = _Err -> ?DEBUG("Bad Request: ~p", [_Err]), badrequest_response(<<"Invalid JSON input">>); _:_Error -> ?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]), badrequest_response() end; process([Call], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) -> Version = get_api_version(Req), try Args = case Data of [{nokey, <<>>}] -> []; _ -> Data end, log(Call, Args, IP), perform_call(Call, Args, Req, Version) catch %% TODO We need to refactor to remove redundant error return formatting throw:{error, unknown_command} -> json_format({404, 44, <<"Command not found.">>}); _:_Error -> ?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]), badrequest_response() end; process([_Call], #request{method = 'OPTIONS', data = <<>>}) -> {200, ?OPTIONS_HEADER, []}; process(_, #request{method = 'OPTIONS'}) -> {400, ?OPTIONS_HEADER, []}; process(_Path, Request) -> ?DEBUG("Bad Request: no handler ~p", [Request]), json_error(400, 40, <<"Missing command name.">>). perform_call(Command, Args, Req, Version) -> case catch binary_to_existing_atom(Command, utf8) of Call when is_atom(Call) -> case extract_auth(Req) of {error, expired} -> invalid_token_response(); {error, not_found} -> invalid_token_response(); {error, invalid_auth} -> unauthorized_response(); {error, _} -> unauthorized_response(); Auth when is_map(Auth) -> Result = handle(Call, Auth, Args, Version), json_format(Result) end; _ -> json_error(404, 40, <<"Endpoint not found.">>) end. %% Be tolerant to make API more easily usable from command-line pipe. extract_args(<<"\n">>) -> []; extract_args(Data) -> case jiffy:decode(Data) of List when is_list(List) -> List; {List} when is_list(List) -> List; Other -> [Other] end. % get API version N from last "vN" element in URL path get_api_version(#request{path = Path}) -> get_api_version(lists:reverse(Path)); get_api_version([<<"v", String/binary>> | Tail]) -> case catch binary_to_integer(String) of N when is_integer(N) -> N; _ -> get_api_version(Tail) end; get_api_version([_Head | Tail]) -> get_api_version(Tail); get_api_version([]) -> ?DEFAULT_API_VERSION. %% ---------------- %% command handlers %% ---------------- %% TODO Check accept types of request before decided format of reply. % generic ejabberd command handler handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> case ejabberd_commands:get_command_format(Call, Auth, Version) of {ArgsSpec, _} when is_list(ArgsSpec) -> Args2 = [{misc:binary_to_atom(Key), Value} || {Key, Value} <- Args], Spec = lists:foldr( fun ({Key, binary}, Acc) -> [{Key, <<>>}|Acc]; ({Key, string}, Acc) -> [{Key, ""}|Acc]; ({Key, integer}, Acc) -> [{Key, 0}|Acc]; ({Key, {list, _}}, Acc) -> [{Key, []}|Acc]; ({Key, atom}, Acc) -> [{Key, undefined}|Acc] end, [], ArgsSpec), try handle2(Call, Auth, match(Args2, Spec), Version) catch throw:not_found -> {404, <<"not_found">>}; throw:{not_found, Why} when is_atom(Why) -> {404, misc:atom_to_binary(Why)}; throw:{not_found, Msg} -> {404, iolist_to_binary(Msg)}; throw:not_allowed -> {401, <<"not_allowed">>}; throw:{not_allowed, Why} when is_atom(Why) -> {401, misc:atom_to_binary(Why)}; throw:{not_allowed, Msg} -> {401, iolist_to_binary(Msg)}; throw:{error, account_unprivileged} -> {403, 31, <<"Command need to be run with admin priviledge.">>}; throw:{error, access_rules_unauthorized} -> {403, 32, <<"AccessRules: Account does not have the right to perform the operation.">>}; throw:{invalid_parameter, Msg} -> {400, iolist_to_binary(Msg)}; throw:{error, Why} when is_atom(Why) -> {400, misc:atom_to_binary(Why)}; throw:{error, Msg} -> {400, iolist_to_binary(Msg)}; throw:Error when is_atom(Error) -> {400, misc:atom_to_binary(Error)}; throw:Msg when is_list(Msg); is_binary(Msg) -> {400, iolist_to_binary(Msg)}; _Error -> ?ERROR_MSG("REST API Error: ~p ~p", [_Error, erlang:get_stacktrace()]), {500, <<"internal_error">>} end; {error, Msg} -> ?ERROR_MSG("REST API Error: ~p", [Msg]), {400, Msg}; _Error -> ?ERROR_MSG("REST API Error: ~p", [_Error]), {400, <<"Error">>} end. handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> {ArgsF, _ResultF} = ejabberd_commands:get_command_format(Call, Auth, Version), ArgsFormatted = format_args(Args, ArgsF), case ejabberd_commands:execute_command2(Call, ArgsFormatted, Auth, Version) of {error, Error} -> throw(Error); Res -> format_command_result(Call, Auth, Res, Version) end. get_elem_delete(A, L) -> case proplists:get_all_values(A, L) of [Value] -> {Value, proplists:delete(A, L)}; [_, _ | _] -> %% Crash reporting the error exit({duplicated_attribute, A, L}); [] -> %% Report the error and then force a crash exit({attribute_not_found, A, L}) end. format_args(Args, ArgsFormat) -> {ArgsRemaining, R} = lists:foldl(fun ({ArgName, ArgFormat}, {Args1, Res}) -> {ArgValue, Args2} = get_elem_delete(ArgName, Args1), Formatted = format_arg(ArgValue, ArgFormat), {Args2, Res ++ [Formatted]} end, {Args, []}, ArgsFormat), case ArgsRemaining of [] -> R; L when is_list(L) -> exit({additional_unused_args, L}) end. format_arg({Elements}, {list, {_ElementDefName, {tuple, [{_Tuple1N, Tuple1S}, {_Tuple2N, Tuple2S}]} = Tuple}}) when is_list(Elements) andalso (Tuple1S == binary orelse Tuple1S == string) -> lists:map(fun({F1, F2}) -> {format_arg(F1, Tuple1S), format_arg(F2, Tuple2S)}; ({Val}) when is_list(Val) -> format_arg({Val}, Tuple) end, Elements); format_arg(Elements, {list, {_ElementDefName, {list, _} = ElementDefFormat}}) when is_list(Elements) -> [{format_arg(Element, ElementDefFormat)} || Element <- Elements]; format_arg(Elements, {list, {_ElementDefName, ElementDefFormat}}) when is_list(Elements) -> [format_arg(Element, ElementDefFormat) || Element <- Elements]; format_arg({[{Name, Value}]}, {tuple, [{_Tuple1N, Tuple1S}, {_Tuple2N, Tuple2S}]}) when Tuple1S == binary; Tuple1S == string -> {format_arg(Name, Tuple1S), format_arg(Value, Tuple2S)}; format_arg({Elements}, {tuple, ElementsDef}) when is_list(Elements) -> F = lists:map(fun({TElName, TElDef}) -> case lists:keyfind(atom_to_binary(TElName, latin1), 1, Elements) of {_, Value} -> format_arg(Value, TElDef); _ when TElDef == binary; TElDef == string -> <<"">>; _ -> ?ERROR_MSG("missing field ~p in tuple ~p", [TElName, Elements]), throw({invalid_parameter, io_lib:format("Missing field ~w in tuple ~w", [TElName, Elements])}) end end, ElementsDef), list_to_tuple(F); format_arg(Elements, {list, ElementsDef}) when is_list(Elements) and is_atom(ElementsDef) -> [format_arg(Element, ElementsDef) || Element <- Elements]; format_arg(Arg, integer) when is_integer(Arg) -> Arg; format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg); format_arg(Arg, binary) when is_binary(Arg) -> Arg; format_arg(Arg, string) when is_list(Arg) -> Arg; format_arg(Arg, string) when is_binary(Arg) -> binary_to_list(Arg); format_arg(undefined, binary) -> <<>>; format_arg(undefined, string) -> ""; format_arg(Arg, Format) -> ?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]), throw({invalid_parameter, io_lib:format("Arg ~w is not in format ~w", [Arg, Format])}). process_unicode_codepoints(Str) -> iolist_to_binary(lists:map(fun(X) when X > 255 -> unicode:characters_to_binary([X]); (Y) -> Y end, Str)). %% ---------------- %% internal helpers %% ---------------- match(Args, Spec) -> [{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec]. format_command_result(Cmd, Auth, Result, Version) -> {_, ResultFormat} = ejabberd_commands:get_command_format(Cmd, Auth, Version), case {ResultFormat, Result} of {{_, rescode}, V} when V == true; V == ok -> {200, 0}; {{_, rescode}, _} -> {200, 1}; {_, {error, ErrorAtom, Code, Msg}} -> format_error_result(ErrorAtom, Code, Msg); {{_, restuple}, {V, Text}} when V == true; V == ok -> {200, iolist_to_binary(Text)}; {{_, restuple}, {ErrorAtom, Msg}} -> format_error_result(ErrorAtom, 0, Msg); {{_, {list, _}}, _V} -> {_, L} = format_result(Result, ResultFormat), {200, L}; {{_, {tuple, _}}, _V} -> {_, T} = format_result(Result, ResultFormat), {200, T}; _ -> {200, {[format_result(Result, ResultFormat)]}} end. format_result(Atom, {Name, atom}) -> {misc:atom_to_binary(Name), misc:atom_to_binary(Atom)}; format_result(Int, {Name, integer}) -> {misc:atom_to_binary(Name), Int}; format_result([String | _] = StringList, {Name, string}) when is_list(String) -> Binarized = iolist_to_binary(string:join(StringList, "\n")), {misc:atom_to_binary(Name), Binarized}; format_result(String, {Name, string}) -> {misc:atom_to_binary(Name), iolist_to_binary(String)}; format_result(Code, {Name, rescode}) -> {misc:atom_to_binary(Name), Code == true orelse Code == ok}; format_result({Code, Text}, {Name, restuple}) -> {misc:atom_to_binary(Name), {[{<<"res">>, Code == true orelse Code == ok}, {<<"text">>, iolist_to_binary(Text)}]}}; format_result(Code, {Name, restuple}) -> {misc:atom_to_binary(Name), {[{<<"res">>, Code == true orelse Code == ok}, {<<"text">>, <<"">>}]}}; format_result(Els, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) -> {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}}; format_result(Els, {Name, {list, Def}}) -> {misc:atom_to_binary(Name), [element(2, format_result(El, Def)) || El <- Els]}; format_result(Tuple, {_Name, {tuple, [{_, atom}, ValFmt]}}) -> {Name2, Val} = Tuple, {_, Val2} = format_result(Val, ValFmt), {misc:atom_to_binary(Name2), Val2}; format_result(Tuple, {Name, {tuple, Def}}) -> Els = lists:zip(tuple_to_list(Tuple), Def), {misc:atom_to_binary(Name), {[format_result(El, ElDef) || {El, ElDef} <- Els]}}; format_result(404, {_Name, _}) -> "not_found". format_error_result(conflict, Code, Msg) -> {409, Code, iolist_to_binary(Msg)}; format_error_result(_ErrorAtom, Code, Msg) -> {500, Code, iolist_to_binary(Msg)}. unauthorized_response() -> json_error(401, 10, <<"You are not authorized to call this command.">>). invalid_token_response() -> json_error(401, 10, <<"Oauth Token is invalid or expired.">>). %% outofscope_response() -> %% json_error(401, 11, <<"Token does not grant usage to command required scope.">>). badrequest_response() -> badrequest_response(<<"400 Bad Request">>). badrequest_response(Body) -> json_response(400, jiffy:encode(Body)). json_format({Code, Result}) -> json_response(Code, jiffy:encode(Result)); json_format({HTMLCode, JSONErrorCode, Message}) -> json_error(HTMLCode, JSONErrorCode, Message). json_response(Code, Body) when is_integer(Code) -> {Code, ?HEADER(?CT_JSON), Body}. %% HTTPCode, JSONCode = integers %% message is binary json_error(HTTPCode, JSONCode, Message) -> {HTTPCode, ?HEADER(?CT_JSON), jiffy:encode({[{<<"status">>, <<"error">>}, {<<"code">>, JSONCode}, {<<"message">>, Message}]}) }. log(Call, Args, {Addr, Port}) -> AddrS = misc:ip_to_list({Addr, Port}), ?INFO_MSG("API call ~s ~p from ~s:~p", [Call, hide_sensitive_args(Args), AddrS, Port]); log(Call, Args, IP) -> ?INFO_MSG("API call ~s ~p (~p)", [Call, hide_sensitive_args(Args), IP]). hide_sensitive_args(Args=[_H|_T]) -> lists:map( fun({<<"password">>, Password}) -> {<<"password">>, ejabberd_config:may_hide_data(Password)}; ({<<"newpass">>,NewPassword}) -> {<<"newpass">>, ejabberd_config:may_hide_data(NewPassword)}; (E) -> E end, Args); hide_sensitive_args(NonListArgs) -> NonListArgs. permission_addon() -> Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access, none), Rules = acl:resolve_access(Access, global), R = case Rules of all -> [{[{allow, all}], {all, []}}]; none -> []; _ -> lists:filtermap( fun({V, AclRules}) when V == all; V == [all]; V == [allow]; V == allow -> {true, {[{allow, AclRules}], {all, []}}}; ({List, AclRules}) when is_list(List) -> {true, {[{allow, AclRules}], {List, []}}}; (_) -> false end, Rules) end, {_, Res} = lists:foldl( fun({R2, L2}, {Idx, Acc}) -> {Idx+1, [{<<"'mod_http_api admin_ip_access' option compatibility shim ", (integer_to_binary(Idx))/binary>>, {[?MODULE], [{access, R2}], L2}} | Acc]} end, {1, []}, R), Res. mod_opt_type(admin_ip_access) -> fun acl:access_rules_validator/1; mod_opt_type(_) -> [admin_ip_access]. ejabberd-18.01/src/mod_last.erl0000644000232200023220000002720513225664356016746 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_last.erl %%% Author : Alexey Shchepin %%% Purpose : jabber:iq:last support (XEP-0012) %%% Created : 24 Oct 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_last). -author('alexey@process-one.net'). -protocol({xep, 12, '2.0'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_local_iq/1, export/1, process_sm_iq/1, on_presence_update/4, import_info/0, import/5, import_start/2, store_last_info/4, get_last_info/2, remove_user/2, mod_opt_type/1, register_user/2, depends/2, privacy_check_packet/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_privacy.hrl"). -include("mod_last.hrl"). -define(LAST_CACHE, last_activity_cache). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), #last_activity{}) -> ok | pass. -callback get_last(binary(), binary()) -> {ok, {non_neg_integer(), binary()}} | error | {error, any()}. -callback store_last_info(binary(), binary(), non_neg_integer(), binary()) -> ok | {error, any()}. -callback remove_user(binary(), binary()) -> any(). -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST, ?MODULE, process_sm_iq, IQDisc), ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE, privacy_check_packet, 30), ejabberd_hooks:add(register_user, Host, ?MODULE, register_user, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(unset_presence_hook, Host, ?MODULE, on_presence_update, 50). stop(Host) -> ejabberd_hooks:delete(register_user, Host, ?MODULE, register_user, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:delete(unset_presence_hook, Host, ?MODULE, on_presence_update, 50), ejabberd_hooks:delete(privacy_check_packet, Host, ?MODULE, privacy_check_packet, 30), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, init_cache(NewMod, Host, NewOpts), case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST, ?MODULE, process_sm_iq, IQDisc); true -> ok end. %%% %%% Uptime of ejabberd node %%% -spec process_local_iq(iq()) -> iq(). process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_local_iq(#iq{type = get} = IQ) -> xmpp:make_iq_result(IQ, #last{seconds = get_node_uptime()}). -spec get_node_uptime() -> non_neg_integer(). %% @doc Get the uptime of the ejabberd node, expressed in seconds. %% When ejabberd is starting, ejabberd_config:start/0 stores the datetime. get_node_uptime() -> case ejabberd_config:get_option(node_start) of undefined -> trunc(element(1, erlang:statistics(wall_clock)) / 1000); Now -> p1_time_compat:system_time(seconds) - Now end. %%% %%% Serve queries about user last online %%% -spec process_sm_iq(iq()) -> iq(). process_sm_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_sm_iq(#iq{from = From, to = To, lang = Lang} = IQ) -> User = To#jid.luser, Server = To#jid.lserver, {Subscription, _Groups} = ejabberd_hooks:run_fold(roster_get_jid_info, Server, {none, []}, [User, Server, From]), if (Subscription == both) or (Subscription == from) or (From#jid.luser == To#jid.luser) and (From#jid.lserver == To#jid.lserver) -> Pres = xmpp:set_from_to(#presence{}, To, From), case ejabberd_hooks:run_fold(privacy_check_packet, Server, allow, [To, Pres, out]) of allow -> get_last_iq(IQ, User, Server); deny -> xmpp:make_error(IQ, xmpp:err_forbidden()) end; true -> Txt = <<"Not subscribed">>, xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang)) end. privacy_check_packet(allow, C2SState, #iq{from = From, to = To, type = T} = IQ, in) when T == get; T == set -> case xmpp:has_subtag(IQ, #last{}) of true -> #jid{luser = LUser, lserver = LServer} = To, {Sub, _} = ejabberd_hooks:run_fold( roster_get_jid_info, LServer, {none, []}, [LUser, LServer, From]), if Sub == from; Sub == both -> Pres = #presence{from = To, to = From}, case ejabberd_hooks:run_fold( privacy_check_packet, allow, [C2SState, Pres, out]) of allow -> allow; deny -> {stop, deny} end; true -> {stop, deny} end; false -> allow end; privacy_check_packet(Acc, _, _, _) -> Acc. -spec get_last(binary(), binary()) -> {ok, non_neg_integer(), binary()} | not_found | {error, any()}. get_last(LUser, LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Res = case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?LAST_CACHE, {LUser, LServer}, fun() -> Mod:get_last(LUser, LServer) end); false -> Mod:get_last(LUser, LServer) end, case Res of {ok, {TimeStamp, Status}} -> {ok, TimeStamp, Status}; error -> not_found; Err -> Err end. -spec get_last_iq(iq(), binary(), binary()) -> iq(). get_last_iq(#iq{lang = Lang} = IQ, LUser, LServer) -> case ejabberd_sm:get_user_resources(LUser, LServer) of [] -> case get_last(LUser, LServer) of {error, _Reason} -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)); not_found -> Txt = <<"No info about last activity found">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)); {ok, TimeStamp, Status} -> TimeStamp2 = p1_time_compat:system_time(seconds), Sec = TimeStamp2 - TimeStamp, xmpp:make_iq_result(IQ, #last{seconds = Sec, status = Status}) end; _ -> xmpp:make_iq_result(IQ, #last{seconds = 0}) end. -spec register_user(binary(), binary()) -> any(). register_user(User, Server) -> on_presence_update( User, Server, <<"RegisterResource">>, <<"Registered but didn't login">>). -spec on_presence_update(binary(), binary(), binary(), binary()) -> any(). on_presence_update(User, Server, _Resource, Status) -> TimeStamp = p1_time_compat:system_time(seconds), store_last_info(User, Server, TimeStamp, Status). -spec store_last_info(binary(), binary(), non_neg_integer(), binary()) -> any(). store_last_info(User, Server, TimeStamp, Status) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), case use_cache(Mod, LServer) of true -> ets_cache:update( ?LAST_CACHE, {LUser, LServer}, {ok, {TimeStamp, Status}}, fun() -> Mod:store_last_info(LUser, LServer, TimeStamp, Status) end, cache_nodes(Mod, LServer)); false -> Mod:store_last_info(LUser, LServer, TimeStamp, Status) end. -spec get_last_info(binary(), binary()) -> {ok, non_neg_integer(), binary()} | not_found. get_last_info(LUser, LServer) -> case get_last(LUser, LServer) of {error, _Reason} -> not_found; Res -> Res end. -spec remove_user(binary(), binary()) -> any(). remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_user(LUser, LServer), ets_cache:delete(?LAST_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)). -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> case use_cache(Mod, Host) of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?LAST_CACHE, CacheOpts); false -> ets_cache:delete(?LAST_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(Host); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. import_info() -> [{<<"last">>, 3}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). import(LServer, {sql, _}, DBType, <<"last">>, [LUser, TimeStamp, State]) -> TS = case TimeStamp of <<"">> -> 0; _ -> binary_to_integer(TimeStamp) end, LA = #last_activity{us = {LUser, LServer}, timestamp = TS, status = State}, Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, LA). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). depends(_Host, _Opts) -> []. mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [db_type, iqdisc, cache_life_time, cache_size, use_cache, cache_missed]. ejabberd-18.01/src/str.erl0000644000232200023220000001746613225664356015764 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : str.erl %%% Author : Evgeniy Khramtsov %%% Purpose : Provide binary string manipulations %%% Created : 23 Feb 2012 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(str). -author('ekhramtsov@process-one.net'). %% API -export([equal/2, concat/2, rchr/2, str/2, rstr/2, span/2, cspan/2, copies/2, words/1, words/2, sub_word/2, sub_word/3, strip/1, strip/2, len/1, tokens/2, left/2, left/3, right/2, right/3, centre/2, centre/3, sub_string/2, sub_string/3, to_upper/1, join/2, substr/2, chr/2, chars/3, chars/2, substr/3, strip/3, to_lower/1, to_float/1, prefix/2, suffix/2, format/2, to_integer/1, sha/1, to_hexlist/1]). %%%=================================================================== %%% API %%%=================================================================== -spec len(binary()) -> non_neg_integer(). len(B) -> byte_size(B). -spec equal(binary(), binary()) -> boolean(). equal(B1, B2) -> B1 == B2. -spec concat(binary(), binary()) -> binary(). concat(B1, B2) -> <>. -spec rchr(binary(), char()) -> non_neg_integer(). rchr(B, C) -> string:rchr(binary_to_list(B), C). -spec str(binary(), binary()) -> non_neg_integer(). str(B1, B2) -> case binary:match(B1, B2) of {R, _Len} -> R+1; _ -> 0 end. -spec rstr(binary(), binary()) -> non_neg_integer(). rstr(B1, B2) -> string:rstr(binary_to_list(B1), binary_to_list(B2)). -spec span(binary(), binary()) -> non_neg_integer(). span(B1, B2) -> string:span(binary_to_list(B1), binary_to_list(B2)). -spec cspan(binary(), binary()) -> non_neg_integer(). cspan(B1, B2) -> string:cspan(binary_to_list(B1), binary_to_list(B2)). -spec copies(binary(), non_neg_integer()) -> binary(). copies(B, N) -> binary:copy(B, N). -spec words(binary()) -> pos_integer(). words(B) -> string:words(binary_to_list(B)). -spec words(binary(), char()) -> pos_integer(). words(B, C) -> string:words(binary_to_list(B), C). -spec sub_word(binary(), integer()) -> binary(). sub_word(B, N) -> iolist_to_binary(string:sub_word(binary_to_list(B), N)). -spec sub_word(binary(), integer(), char()) -> binary(). sub_word(B, N, C) -> iolist_to_binary(string:sub_word(binary_to_list(B), N, C)). -spec strip(binary()) -> binary(). strip(B) -> iolist_to_binary(string:strip(binary_to_list(B))). -spec strip(binary(), both | left | right) -> binary(). strip(B, D) -> iolist_to_binary(string:strip(binary_to_list(B), D)). -spec left(binary(), non_neg_integer()) -> binary(). left(B, N) -> iolist_to_binary(string:left(binary_to_list(B), N)). -spec left(binary(), non_neg_integer(), char()) -> binary(). left(B, N, C) -> iolist_to_binary(string:left(binary_to_list(B), N, C)). -spec right(binary(), non_neg_integer()) -> binary(). right(B, N) -> iolist_to_binary(string:right(binary_to_list(B), N)). -spec right(binary(), non_neg_integer(), char()) -> binary(). right(B, N, C) -> iolist_to_binary(string:right(binary_to_list(B), N, C)). -spec centre(binary(), non_neg_integer()) -> binary(). centre(B, N) -> iolist_to_binary(string:centre(binary_to_list(B), N)). -spec centre(binary(), non_neg_integer(), char()) -> binary(). centre(B, N, C) -> iolist_to_binary(string:centre(binary_to_list(B), N, C)). -spec sub_string(binary(), pos_integer()) -> binary(). sub_string(B, N) -> iolist_to_binary(string:sub_string(binary_to_list(B), N)). -spec sub_string(binary(), pos_integer(), pos_integer()) -> binary(). sub_string(B, S, E) -> iolist_to_binary(string:sub_string(binary_to_list(B), S, E)). -spec to_upper(binary()) -> binary(); (char()) -> char(). to_upper(B) when is_binary(B) -> iolist_to_binary(string:to_upper(binary_to_list(B))); to_upper(C) -> string:to_upper(C). -spec join([binary()], binary() | char()) -> binary(). join(L, Sep) -> iolist_to_binary(join_s(L, Sep)). -spec substr(binary(), pos_integer()) -> binary(). substr(B, N) -> binary_part(B, N-1, byte_size(B)-N+1). -spec chr(binary(), char()) -> non_neg_integer(). chr(B, C) -> string:chr(binary_to_list(B), C). -spec chars(char(), non_neg_integer(), binary()) -> binary(). chars(C, N, B) -> iolist_to_binary(string:chars(C, N, binary_to_list(B))). -spec chars(char(), non_neg_integer()) -> binary(). chars(C, N) -> iolist_to_binary(string:chars(C, N)). -spec substr(binary(), pos_integer(), non_neg_integer()) -> binary(). substr(B, S, E) -> binary_part(B, S-1, E). -spec strip(binary(), both | left | right, char()) -> binary(). strip(B, D, C) -> iolist_to_binary(string:strip(binary_to_list(B), D, C)). -spec to_lower(binary()) -> binary(); (char()) -> char(). to_lower(B) when is_binary(B) -> iolist_to_binary(string:to_lower(binary_to_list(B))); to_lower(C) -> string:to_lower(C). -spec tokens(binary(), binary()) -> [binary()]. tokens(B1, B2) -> [iolist_to_binary(T) || T <- string:tokens(binary_to_list(B1), binary_to_list(B2))]. -spec to_float(binary()) -> {float(), binary()} | {error, no_float}. to_float(B) -> case string:to_float(binary_to_list(B)) of {error, R} -> {error, R}; {Float, Rest} -> {Float, iolist_to_binary(Rest)} end. -spec to_integer(binary()) -> {integer(), binary()} | {error, no_integer}. to_integer(B) -> case string:to_integer(binary_to_list(B)) of {error, R} -> {error, R}; {Int, Rest} -> {Int, iolist_to_binary(Rest)} end. -spec prefix(binary(), binary()) -> boolean(). prefix(Prefix, B) -> Size = byte_size(Prefix), case B of <> -> true; _ -> false end. -spec suffix(binary(), binary()) -> boolean(). suffix(B1, B2) -> lists:suffix(binary_to_list(B1), binary_to_list(B2)). -spec format(io:format(), list()) -> binary(). format(Format, Args) -> iolist_to_binary(io_lib:format(Format, Args)). -spec sha(binary()) -> binary(). sha(Text) -> Bin = crypto:hash(sha, Text), to_hexlist(Bin). -spec to_hexlist(binary()) -> binary(). to_hexlist(S) when is_list(S) -> to_hexlist(iolist_to_binary(S)); to_hexlist(Bin) when is_binary(Bin) -> << <<(digit_to_xchar(N div 16)), (digit_to_xchar(N rem 16))>> || <> <= Bin >>. %%%=================================================================== %%% Internal functions %%%=================================================================== join_s([], _Sep) -> []; join_s([H|T], Sep) -> [H, [[Sep, X] || X <- T]]. digit_to_xchar(D) when (D >= 0) and (D < 10) -> D + $0; digit_to_xchar(D) -> D + $a - 10. ejabberd-18.01/src/mod_offline_mnesia.erl0000644000232200023220000001553013225664356020757 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_offline_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_offline_mnesia). -behaviour(mod_offline). -export([init/2, store_message/1, pop_messages/2, remove_expired_messages/1, remove_old_messages/2, remove_user/2, read_message_headers/2, read_message/3, remove_message/3, read_all_messages/2, remove_all_messages/2, count_messages/2, import/1]). -export([need_transform/1, transform/1]). -include("xmpp.hrl"). -include("mod_offline.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, offline_msg, [{disc_only_copies, [node()]}, {type, bag}, {attributes, record_info(fields, offline_msg)}]). store_message(#offline_msg{packet = Pkt} = OffMsg) -> El = xmpp:encode(Pkt), mnesia:dirty_write(OffMsg#offline_msg{packet = El}). pop_messages(LUser, LServer) -> US = {LUser, LServer}, F = fun () -> Rs = mnesia:wread({offline_msg, US}), mnesia:delete({offline_msg, US}), Rs end, case mnesia:transaction(F) of {atomic, L} -> {ok, lists:keysort(#offline_msg.timestamp, L)}; {aborted, Reason} -> {error, Reason} end. remove_expired_messages(_LServer) -> TimeStamp = p1_time_compat:timestamp(), F = fun () -> mnesia:write_lock_table(offline_msg), mnesia:foldl(fun (Rec, _Acc) -> case Rec#offline_msg.expire of never -> ok; TS -> if TS < TimeStamp -> mnesia:delete_object(Rec); true -> ok end end end, ok, offline_msg) end, mnesia:transaction(F). remove_old_messages(Days, _LServer) -> S = p1_time_compat:system_time(seconds) - 60 * 60 * 24 * Days, MegaSecs1 = S div 1000000, Secs1 = S rem 1000000, TimeStamp = {MegaSecs1, Secs1, 0}, F = fun () -> mnesia:write_lock_table(offline_msg), mnesia:foldl(fun (#offline_msg{timestamp = TS} = Rec, _Acc) when TS < TimeStamp -> mnesia:delete_object(Rec); (_Rec, _Acc) -> ok end, ok, offline_msg) end, mnesia:transaction(F). remove_user(LUser, LServer) -> US = {LUser, LServer}, F = fun () -> mnesia:delete({offline_msg, US}) end, mnesia:transaction(F). read_message_headers(LUser, LServer) -> Msgs = mnesia:dirty_read({offline_msg, {LUser, LServer}}), Hdrs = lists:map( fun(#offline_msg{from = From, to = To, packet = Pkt, timestamp = TS}) -> Seq = now_to_integer(TS), {Seq, From, To, TS, Pkt} end, Msgs), lists:keysort(1, Hdrs). read_message(LUser, LServer, I) -> US = {LUser, LServer}, TS = integer_to_now(I), case mnesia:dirty_match_object( offline_msg, #offline_msg{us = US, timestamp = TS, _ = '_'}) of [Msg|_] -> {ok, Msg}; _ -> error end. remove_message(LUser, LServer, I) -> US = {LUser, LServer}, TS = integer_to_now(I), case mnesia:dirty_match_object( offline_msg, #offline_msg{us = US, timestamp = TS, _ = '_'}) of [] -> {error, notfound}; Msgs -> lists:foreach( fun(Msg) -> mnesia:dirty_delete_object(Msg) end, Msgs) end. read_all_messages(LUser, LServer) -> US = {LUser, LServer}, lists:keysort(#offline_msg.timestamp, mnesia:dirty_read({offline_msg, US})). remove_all_messages(LUser, LServer) -> US = {LUser, LServer}, F = fun () -> mnesia:write_lock_table(offline_msg), lists:foreach(fun (Msg) -> mnesia:delete_object(Msg) end, mnesia:dirty_read({offline_msg, US})) end, mnesia:transaction(F). count_messages(LUser, LServer) -> US = {LUser, LServer}, F = fun () -> count_mnesia_records(US) end, case catch mnesia:async_dirty(F) of I when is_integer(I) -> I; _ -> 0 end. import(#offline_msg{} = Msg) -> mnesia:dirty_write(Msg). need_transform(#offline_msg{us = {U, S}}) when is_list(U) orelse is_list(S) -> ?INFO_MSG("Mnesia table 'offline_msg' will be converted to binary", []), true; need_transform(_) -> false. transform(#offline_msg{us = {U, S}, from = From, to = To, packet = El} = R) -> R#offline_msg{us = {iolist_to_binary(U), iolist_to_binary(S)}, from = jid_to_binary(From), to = jid_to_binary(To), packet = fxml:to_xmlel(El)}. %%%=================================================================== %%% Internal functions %%%=================================================================== %% Return the number of records matching a given match expression. %% This function is intended to be used inside a Mnesia transaction. %% The count has been written to use the fewest possible memory by %% getting the record by small increment and by using continuation. -define(BATCHSIZE, 100). count_mnesia_records(US) -> MatchExpression = #offline_msg{us = US, _ = '_'}, case mnesia:select(offline_msg, [{MatchExpression, [], [[]]}], ?BATCHSIZE, read) of {Result, Cont} -> Count = length(Result), count_records_cont(Cont, Count); '$end_of_table' -> 0 end. count_records_cont(Cont, Count) -> case mnesia:select(Cont) of {Result, Cont} -> NewCount = Count + length(Result), count_records_cont(Cont, NewCount); '$end_of_table' -> Count end. jid_to_binary(#jid{user = U, server = S, resource = R, luser = LU, lserver = LS, lresource = LR}) -> #jid{user = iolist_to_binary(U), server = iolist_to_binary(S), resource = iolist_to_binary(R), luser = iolist_to_binary(LU), lserver = iolist_to_binary(LS), lresource = iolist_to_binary(LR)}. now_to_integer({MS, S, US}) -> (MS * 1000000 + S) * 1000000 + US. integer_to_now(Int) -> Secs = Int div 1000000, USec = Int rem 1000000, MSec = Secs div 1000000, Sec = Secs rem 1000000, {MSec, Sec, USec}. ejabberd-18.01/src/node_flat.erl0000644000232200023220000010516613225664356017102 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_flat.erl %%% Author : Christophe Romain %%% Purpose : Standard PubSub node plugin %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the default PubSub plugin. %%%

It is used as a default for all unknown PubSub node type. It can serve %%% as a developer basis and reference to build its own custom pubsub node %%% types.

%%%

PubSub plugin nodes are using the {@link gen_node} behaviour.

-module(node_flat). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -include("xmpp.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, can_fetch_item/2, is_subscribed/1, transform/1]). init(_Host, _ServerHost, _Opts) -> %pubsub_subscription:init(Host, ServerHost, Opts), ejabberd_mnesia:create(?MODULE, pubsub_state, [{disc_copies, [node()]}, {index, [nodeidx]}, {type, ordered_set}, {attributes, record_info(fields, pubsub_state)}]), ejabberd_mnesia:create(?MODULE, pubsub_item, [{disc_only_copies, [node()]}, {index, [nodeidx]}, {attributes, record_info(fields, pubsub_item)}]), ejabberd_mnesia:create(?MODULE, pubsub_orphan, [{disc_copies, [node()]}, {attributes, record_info(fields, pubsub_orphan)}]), ItemsFields = record_info(fields, pubsub_item), case mnesia:table_info(pubsub_item, attributes) of ItemsFields -> ok; _ -> mnesia:transform_table(pubsub_item, ignore, ItemsFields) end, ok. terminate(_Host, _ServerHost) -> ok. options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, {purge_offline, false}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, open}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, {presence_based_delivery, false}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"auto-create">>, <<"access-authorize">>, <<"delete-nodes">>, <<"delete-items">>, <<"get-pending">>, <<"instant-nodes">>, <<"manage-subscriptions">>, <<"modify-affiliations">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"multi-items">>, <<"publish">>, <<"publish-only-affiliation">>, <<"publish-options">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>, %%<<"subscription-options">>, <<"subscription-notifications">>]. %% @doc Checks if the current user has the permission to create the requested node %%

In flat node, any unused node name is allowed. The access parameter is also %% checked. This parameter depends on the value of the %% access_createnode ACL value in ejabberd config file.

create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> LOwner = jid:tolower(Owner), Allowed = case LOwner of {<<"">>, Host, <<"">>} -> true; % pubsub service always allowed _ -> acl:match_rule(ServerHost, Access, LOwner) =:= allow end, {result, Allowed}. create_node(Nidx, Owner) -> OwnerKey = jid:tolower(jid:remove_resource(Owner)), set_state(#pubsub_state{stateid = {OwnerKey, Nidx}, nodeidx = Nidx, affiliation = owner}), {result, {default, broadcast}}. delete_node(Nodes) -> Tr = fun (#pubsub_state{stateid = {J, _}, subscriptions = Ss}) -> lists:map(fun (S) -> {J, S} end, Ss) end, Reply = lists:map(fun (#pubsub_node{id = Nidx} = PubsubNode) -> {result, States} = get_states(Nidx), lists:foreach(fun (State) -> del_items(Nidx, State#pubsub_state.items), del_state(State#pubsub_state{items = []}) end, States), del_orphan_items(Nidx), {PubsubNode, lists:flatmap(Tr, States)} end, Nodes), {result, {default, broadcast, Reply}}. %% @doc

Accepts or rejects subcription requests on a PubSub node.

%%

The mechanism works as follow: %%

    %%
  • The main PubSub module prepares the subscription and passes the %% result of the preparation as a record.
  • %%
  • This function gets the prepared record and several other parameters and %% can decide to:
      %%
    • reject the subscription;
    • %%
    • allow it as is, letting the main module perform the database %% persistance;
    • %%
    • allow it, modifying the record. The main module will store the %% modified record;
    • %%
    • allow it, but perform the needed persistance operations.
    %%

%%

The selected behaviour depends on the return parameter: %%

    %%
  • {error, Reason}: an IQ error result will be returned. No %% subscription will actually be performed.
  • %%
  • true: Subscribe operation is allowed, based on the %% unmodified record passed in parameter SubscribeResult. If this %% parameter contains an error, no subscription will be performed.
  • %%
  • {true, PubsubState}: Subscribe operation is allowed, but %% the {@link mod_pubsub:pubsubState()} record returned replaces the value %% passed in parameter SubscribeResult.
  • %%
  • {true, done}: Subscribe operation is allowed, but the %% {@link mod_pubsub:pubsubState()} will be considered as already stored and %% no further persistance operation will be performed. This case is used, %% when the plugin module is doing the persistance by itself or when it want %% to completly disable persistance.
%%

%%

In the default plugin module, the record is unchanged.

subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, _Options) -> SubKey = jid:tolower(Subscriber), GenKey = jid:remove_resource(SubKey), Authorized = jid:tolower(jid:remove_resource(Sender)) == GenKey, GenState = get_state(Nidx, GenKey), SubState = case SubKey of GenKey -> GenState; _ -> get_state(Nidx, SubKey) end, Affiliation = GenState#pubsub_state.affiliation, Subscriptions = SubState#pubsub_state.subscriptions, Whitelisted = lists:member(Affiliation, [member, publisher, owner]), PendingSubscription = lists:any(fun ({pending, _}) -> true; (_) -> false end, Subscriptions), Owner = Affiliation == owner, if not Authorized -> {error, mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_invalid_jid())}; (Affiliation == outcast) or (Affiliation == publish_only) -> {error, xmpp:err_forbidden()}; PendingSubscription -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_pending_subscription())}; (AccessModel == presence) and (not PresenceSubscription) and (not Owner) -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and (not RosterGroup) and (not Owner) -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and (not Whitelisted) and (not Owner) -> {error, mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; %%ForbiddenAnonymous -> %% % Requesting entity is anonymous %% {error, xmpp:err_forbidden()}; true -> %%SubId = pubsub_subscription:add_subscription(Subscriber, Nidx, Options), {NewSub, SubId} = case Subscriptions of [{subscribed, Id}|_] -> {subscribed, Id}; [] -> Id = pubsub_subscription:make_subid(), Sub = case AccessModel of authorize -> pending; _ -> subscribed end, set_state(SubState#pubsub_state{subscriptions = [{Sub, Id} | Subscriptions]}), {Sub, Id} end, case {NewSub, SendLast} of {subscribed, never} -> {result, {default, subscribed, SubId}}; {subscribed, _} -> {result, {default, subscribed, SubId, send_last}}; {_, _} -> {result, {default, pending, SubId}} end end. %% @doc

Unsubscribe the Subscriber from the Node.

unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> SubKey = jid:tolower(Subscriber), GenKey = jid:remove_resource(SubKey), Authorized = jid:tolower(jid:remove_resource(Sender)) == GenKey, GenState = get_state(Nidx, GenKey), SubState = case SubKey of GenKey -> GenState; _ -> get_state(Nidx, SubKey) end, Subscriptions = lists:filter(fun ({_Sub, _SubId}) -> true; (_SubId) -> false end, SubState#pubsub_state.subscriptions), SubIdExists = case SubId of <<>> -> false; Binary when is_binary(Binary) -> true; _ -> false end, if %% Requesting entity is prohibited from unsubscribing entity not Authorized -> {error, xmpp:err_forbidden()}; %% Entity did not specify SubId %%SubId == "", ?? -> %% {error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")}; %% Invalid subscription identifier %%InvalidSubId -> %% {error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; %% Requesting entity is not a subscriber Subscriptions == [] -> {error, mod_pubsub:extended_error(xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())}; %% Subid supplied, so use that. SubIdExists -> Sub = first_in_list(fun ({_, S}) when S == SubId -> true; (_) -> false end, SubState#pubsub_state.subscriptions), case Sub of {value, S} -> delete_subscriptions(SubState, [S]), {result, default}; false -> {error, mod_pubsub:extended_error(xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())} end; %% Asking to remove all subscriptions to the given node SubId == all -> delete_subscriptions(SubState, Subscriptions), {result, default}; %% No subid supplied, but there's only one matching subscription length(Subscriptions) == 1 -> delete_subscriptions(SubState, Subscriptions), {result, default}; %% No subid and more than one possible subscription match. true -> {error, mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_subid_required())} end. delete_subscriptions(SubState, Subscriptions) -> NewSubs = lists:foldl(fun ({Subscription, SubId}, Acc) -> %%pubsub_subscription:delete_subscription(SubKey, Nidx, SubId), Acc -- [{Subscription, SubId}] end, SubState#pubsub_state.subscriptions, Subscriptions), case {SubState#pubsub_state.affiliation, NewSubs} of {none, []} -> del_state(SubState); _ -> set_state(SubState#pubsub_state{subscriptions = NewSubs}) end. %% @doc

Publishes the item passed as parameter.

%%

The mechanism works as follow: %%

    %%
  • The main PubSub module prepares the item to publish and passes the %% result of the preparation as a {@link mod_pubsub:pubsubItem()} record.
  • %%
  • This function gets the prepared record and several other parameters and can decide to:
      %%
    • reject the publication;
    • %%
    • allow the publication as is, letting the main module perform the database persistance;
    • %%
    • allow the publication, modifying the record. The main module will store the modified record;
    • %%
    • allow it, but perform the needed persistance operations.
    %%

%%

The selected behaviour depends on the return parameter: %%

    %%
  • {error, Reason}: an iq error result will be return. No %% publication is actually performed.
  • %%
  • true: Publication operation is allowed, based on the %% unmodified record passed in parameter Item. If the Item %% parameter contains an error, no subscription will actually be %% performed.
  • %%
  • {true, Item}: Publication operation is allowed, but the %% {@link mod_pubsub:pubsubItem()} record returned replaces the value passed %% in parameter Item. The persistance will be performed by the main %% module.
  • %%
  • {true, done}: Publication operation is allowed, but the %% {@link mod_pubsub:pubsubItem()} will be considered as already stored and %% no further persistance operation will be performed. This case is used, %% when the plugin module is doing the persistance by itself or when it want %% to completly disable persistance.
%%

%%

In the default plugin module, the record is unchanged.

publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, _PubOpts) -> SubKey = jid:tolower(Publisher), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), SubState = case SubKey of GenKey -> GenState; _ -> get_state(Nidx, SubKey) end, Affiliation = GenState#pubsub_state.affiliation, Subscribed = case PublishModel of subscribers -> is_subscribed(GenState#pubsub_state.subscriptions) orelse is_subscribed(SubState#pubsub_state.subscriptions); _ -> undefined end, if not ((PublishModel == open) or (PublishModel == publishers) and ((Affiliation == owner) or (Affiliation == publisher) or (Affiliation == publish_only)) or (Subscribed == true)) -> {error, xmpp:err_forbidden()}; true -> if MaxItems > 0 -> Now = p1_time_compat:timestamp(), case get_item(Nidx, ItemId) of {result, #pubsub_item{creation = {_, GenKey}} = OldItem} -> set_item(OldItem#pubsub_item{ modification = {Now, SubKey}, payload = Payload}), {result, {default, broadcast, []}}; {result, _} -> {error, xmpp:err_forbidden()}; _ -> Items = [ItemId | GenState#pubsub_state.items], {result, {NI, OI}} = remove_extra_items(Nidx, MaxItems, Items), set_state(GenState#pubsub_state{items = NI}), set_item(#pubsub_item{ itemid = {ItemId, Nidx}, nodeidx = Nidx, creation = {Now, GenKey}, modification = {Now, SubKey}, payload = Payload}), {result, {default, broadcast, OI}} end; true -> {result, {default, broadcast, []}} end end. %% @doc

This function is used to remove extra items, most notably when the %% maximum number of items has been reached.

%%

This function is used internally by the core PubSub module, as no %% permission check is performed.

%%

In the default plugin module, the oldest items are removed, but other %% rules can be used.

%%

If another PubSub plugin wants to delegate the item removal (and if the %% plugin is using the default pubsub storage), it can implements this function like this: %% ```remove_extra_items(Nidx, MaxItems, ItemIds) -> %% node_default:remove_extra_items(Nidx, MaxItems, ItemIds).'''

remove_extra_items(_Nidx, unlimited, ItemIds) -> {result, {ItemIds, []}}; remove_extra_items(Nidx, MaxItems, ItemIds) -> NewItems = lists:sublist(ItemIds, MaxItems), OldItems = lists:nthtail(length(NewItems), ItemIds), del_items(Nidx, OldItems), {result, {NewItems, OldItems}}. %% @doc

Triggers item deletion.

%%

Default plugin: The user performing the deletion must be the node owner %% or a publisher, or PublishModel being open.

delete_item(Nidx, Publisher, PublishModel, ItemId) -> SubKey = jid:tolower(Publisher), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), #pubsub_state{affiliation = Affiliation, items = Items} = GenState, Allowed = Affiliation == publisher orelse Affiliation == owner orelse (PublishModel == open andalso case get_item(Nidx, ItemId) of {result, #pubsub_item{creation = {_, GenKey}}} -> true; _ -> false end), if not Allowed -> {error, xmpp:err_forbidden()}; true -> case lists:member(ItemId, Items) of true -> del_item(Nidx, ItemId), set_state(GenState#pubsub_state{items = lists:delete(ItemId, Items)}), {result, {default, broadcast}}; false -> case Affiliation of owner -> {result, States} = get_states(Nidx), Records = States ++ mnesia:read({pubsub_orphan, Nidx}), lists:foldl(fun (#pubsub_state{items = RI} = S, Res) -> case lists:member(ItemId, RI) of true -> NI = lists:delete(ItemId, RI), del_item(Nidx, ItemId), mnesia:write(S#pubsub_state{items = NI}), {result, {default, broadcast}}; false -> Res end; (#pubsub_orphan{items = RI} = S, Res) -> case lists:member(ItemId, RI) of true -> NI = lists:delete(ItemId, RI), del_item(Nidx, ItemId), mnesia:write(S#pubsub_orphan{items = NI}), {result, {default, broadcast}}; false -> Res end end, {error, xmpp:err_item_not_found()}, Records); _ -> {error, xmpp:err_forbidden()} end end end. purge_node(Nidx, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), case GenState of #pubsub_state{affiliation = owner} -> {result, States} = get_states(Nidx), lists:foreach(fun (#pubsub_state{items = []}) -> ok; (#pubsub_state{items = Items} = S) -> del_items(Nidx, Items), set_state(S#pubsub_state{items = []}) end, States), del_orphan_items(Nidx), {result, {default, broadcast}}; _ -> {error, xmpp:err_forbidden()} end. %% @doc

Return the current affiliations for the given user

%%

The default module reads affiliations in the main Mnesia %% pubsub_state table. If a plugin stores its data in the same %% table, it should return an empty list, as the affiliation will be read by %% the default PubSub module. Otherwise, it should return its own affiliation, %% that will be added to the affiliation stored in the main %% pubsub_state table.

get_entity_affiliations(Host, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}), NodeTree = mod_pubsub:tree(Host), Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) -> case NodeTree:get_node(N) of #pubsub_node{nodeid = {Host, _}} = Node -> [{Node, A} | Acc]; _ -> Acc end end, [], States), {result, Reply}. get_node_affiliations(Nidx) -> {result, States} = get_states(Nidx), Tr = fun (#pubsub_state{stateid = {J, _}, affiliation = A}) -> {J, A} end, {result, lists:map(Tr, States)}. get_affiliation(Nidx, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), #pubsub_state{affiliation = Affiliation} = get_state(Nidx, GenKey), {result, Affiliation}. set_affiliation(Nidx, Owner, Affiliation) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), case {Affiliation, GenState#pubsub_state.subscriptions} of {none, []} -> del_state(GenState); _ -> set_state(GenState#pubsub_state{affiliation = Affiliation}) end. %% @doc

Return the current subscriptions for the given user

%%

The default module reads subscriptions in the main Mnesia %% pubsub_state table. If a plugin stores its data in the same %% table, it should return an empty list, as the affiliation will be read by %% the default PubSub module. Otherwise, it should return its own affiliation, %% that will be added to the affiliation stored in the main %% pubsub_state table.

get_entity_subscriptions(Host, Owner) -> {U, D, _} = SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), States = case SubKey of GenKey -> mnesia:match_object(#pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'}); _ -> mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}) ++ mnesia:match_object(#pubsub_state{stateid = {SubKey, '_'}, _ = '_'}) end, NodeTree = mod_pubsub:tree(Host), Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) -> case NodeTree:get_node(N) of #pubsub_node{nodeid = {Host, _}} = Node -> lists:foldl(fun ({Sub, SubId}, Acc2) -> [{Node, Sub, SubId, J} | Acc2] end, Acc, Ss); _ -> Acc end end, [], States), {result, Reply}. get_node_subscriptions(Nidx) -> {result, States} = get_states(Nidx), Tr = fun (#pubsub_state{stateid = {J, _}, subscriptions = Subscriptions}) -> lists:foldl(fun ({S, SubId}, Acc) -> [{J, S, SubId} | Acc] end, [], Subscriptions) end, {result, lists:flatmap(Tr, States)}. get_subscriptions(Nidx, Owner) -> SubKey = jid:tolower(Owner), SubState = get_state(Nidx, SubKey), {result, SubState#pubsub_state.subscriptions}. set_subscriptions(Nidx, Owner, Subscription, SubId) -> SubKey = jid:tolower(Owner), SubState = get_state(Nidx, SubKey), case {SubId, SubState#pubsub_state.subscriptions} of {_, []} -> case Subscription of none -> {error, mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_not_subscribed())}; _ -> new_subscription(Nidx, Owner, Subscription, SubState) end; {<<>>, [{_, SID}]} -> case Subscription of none -> unsub_with_subid(SubState, SID); _ -> replace_subscription({Subscription, SID}, SubState) end; {<<>>, [_ | _]} -> {error, mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_subid_required())}; _ -> case Subscription of none -> unsub_with_subid(SubState, SubId); _ -> replace_subscription({Subscription, SubId}, SubState) end end. replace_subscription(NewSub, SubState) -> NewSubs = replace_subscription(NewSub, SubState#pubsub_state.subscriptions, []), set_state(SubState#pubsub_state{subscriptions = NewSubs}). replace_subscription(_, [], Acc) -> Acc; replace_subscription({Sub, SubId}, [{_, SubId} | T], Acc) -> replace_subscription({Sub, SubId}, T, [{Sub, SubId} | Acc]). new_subscription(_Nidx, _Owner, Sub, SubState) -> %%SubId = pubsub_subscription:add_subscription(Owner, Nidx, []), SubId = pubsub_subscription:make_subid(), Subs = SubState#pubsub_state.subscriptions, set_state(SubState#pubsub_state{subscriptions = [{Sub, SubId} | Subs]}), {Sub, SubId}. unsub_with_subid(SubState, SubId) -> %%pubsub_subscription:delete_subscription(SubState#pubsub_state.stateid, Nidx, SubId), NewSubs = [{S, Sid} || {S, Sid} <- SubState#pubsub_state.subscriptions, SubId =/= Sid], case {NewSubs, SubState#pubsub_state.affiliation} of {[], none} -> del_state(SubState); _ -> set_state(SubState#pubsub_state{subscriptions = NewSubs}) end. %% @doc

Returns a list of Owner's nodes on Host with pending %% subscriptions.

get_pending_nodes(Host, Owner) -> GenKey = jid:remove_resource(jid:tolower(Owner)), States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, affiliation = owner, _ = '_'}), NodeIdxs = [Nidx || #pubsub_state{stateid = {_, Nidx}} <- States], NodeTree = mod_pubsub:tree(Host), Reply = mnesia:foldl(fun (#pubsub_state{stateid = {_, Nidx}} = S, Acc) -> case lists:member(Nidx, NodeIdxs) of true -> case get_nodes_helper(NodeTree, S) of {value, Node} -> [Node | Acc]; false -> Acc end; false -> Acc end end, [], pubsub_state), {result, Reply}. get_nodes_helper(NodeTree, #pubsub_state{stateid = {_, N}, subscriptions = Subs}) -> HasPending = fun ({pending, _}) -> true; (pending) -> true; (_) -> false end, case lists:any(HasPending, Subs) of true -> case NodeTree:get_node(N) of #pubsub_node{nodeid = {_, Node}} -> {value, Node}; _ -> false end; false -> false end. %% @doc Returns the list of stored states for a given node. %%

For the default PubSub module, states are stored in Mnesia database.

%%

We can consider that the pubsub_state table have been created by the main %% mod_pubsub module.

%%

PubSub plugins can store the states where they wants (for example in a %% relational database).

%%

If a PubSub plugin wants to delegate the states storage to the default node, %% they can implement this function like this: %% ```get_states(Nidx) -> %% node_default:get_states(Nidx).'''

get_states(Nidx) -> States = case catch mnesia:index_read(pubsub_state, Nidx, #pubsub_state.nodeidx) of List when is_list(List) -> List; _ -> [] end, {result, States}. %% @doc

Returns a state (one state list), given its reference.

get_state(Nidx, Key) -> StateId = {Key, Nidx}, case catch mnesia:read({pubsub_state, StateId}) of [State] when is_record(State, pubsub_state) -> State; _ -> #pubsub_state{stateid = StateId, nodeidx = Nidx} end. %% @doc

Write a state into database.

set_state(State) when is_record(State, pubsub_state) -> mnesia:write(State). %set_state(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}. %% @doc

Delete a state from database.

del_state(#pubsub_state{stateid = {Key, Nidx}, items = Items}) -> case Items of [] -> ok; _ -> Orphan = #pubsub_orphan{nodeid = Nidx, items = case mnesia:read({pubsub_orphan, Nidx}) of [#pubsub_orphan{items = ItemIds}] -> lists:usort(ItemIds++Items); _ -> Items end}, mnesia:write(Orphan) end, mnesia:delete({pubsub_state, {Key, Nidx}}). %% @doc Returns the list of stored items for a given node. %%

For the default PubSub module, items are stored in Mnesia database.

%%

We can consider that the pubsub_item table have been created by the main %% mod_pubsub module.

%%

PubSub plugins can store the items where they wants (for example in a %% relational database), or they can even decide not to persist any items.

get_items(Nidx, _From, undefined) -> RItems = lists:keysort(#pubsub_item.creation, mnesia:index_read(pubsub_item, Nidx, #pubsub_item.nodeidx)), {result, {RItems, undefined}}; get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex, 'after' = After, before = Before}) -> RItems = lists:keysort(#pubsub_item.creation, mnesia:index_read(pubsub_item, Nidx, #pubsub_item.nodeidx)), Count = length(RItems), Limit = case Max of undefined -> ?MAXITEMS; _ -> Max end, {Offset, ItemsPage} = case {IncIndex, Before, After} of {I, undefined, undefined} -> SubList = lists:nthtail(I, RItems), {I, lists:sublist(SubList, Limit)}; {_, <<>>, undefined} -> %% 2.5 Requesting the Last Page in a Result Set SubList = lists:reverse(RItems), {0, lists:sublist(SubList, Limit)}; {_, Stamp, undefined} -> BeforeNow = encode_stamp(Stamp), SubList = lists:dropwhile( fun(#pubsub_item{creation = {Now, _}}) -> Now >= BeforeNow end, lists:reverse(RItems)), {0, lists:sublist(SubList, Limit)}; {_, undefined, Stamp} -> AfterNow = encode_stamp(Stamp), SubList = lists:dropwhile( fun(#pubsub_item{creation = {Now, _}}) -> Now =< AfterNow end, RItems), {0, lists:sublist(SubList, Limit)} end, Rsm = rsm_page(Count, IncIndex, Offset, ItemsPage), {result, {ItemsPage, Rsm}}. get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM) -> SubKey = jid:tolower(JID), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), SubState = get_state(Nidx, SubKey), Affiliation = GenState#pubsub_state.affiliation, BareSubscriptions = GenState#pubsub_state.subscriptions, FullSubscriptions = SubState#pubsub_state.subscriptions, Whitelisted = can_fetch_item(Affiliation, BareSubscriptions) orelse can_fetch_item(Affiliation, FullSubscriptions), if %%SubId == "", ?? -> %% Entity has multiple subscriptions to the node but does not specify a subscription ID %{error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")}; %%InvalidSubId -> %% Entity is subscribed but specifies an invalid subscription ID %{error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; (Affiliation == outcast) or (Affiliation == publish_only) -> {error, xmpp:err_forbidden()}; (AccessModel == presence) and not PresenceSubscription -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and not RosterGroup -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and not Whitelisted -> {error, mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())}; (AccessModel == authorize) and not Whitelisted -> {error, xmpp:err_forbidden()}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; true -> get_items(Nidx, JID, RSM) end. get_last_items(Nidx, _From, Count) when Count > 0 -> Items = mnesia:index_read(pubsub_item, Nidx, #pubsub_item.nodeidx), LastItems = lists:reverse(lists:keysort(#pubsub_item.modification, Items)), {result, lists:sublist(LastItems, Count)}; get_last_items(_Nidx, _From, _Count) -> {result, []}. %% @doc

Returns an item (one item list), given its reference.

get_item(Nidx, ItemId) -> case mnesia:read({pubsub_item, {ItemId, Nidx}}) of [Item] when is_record(Item, pubsub_item) -> {result, Item}; _ -> {error, xmpp:err_item_not_found()} end. get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> SubKey = jid:tolower(JID), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), Affiliation = GenState#pubsub_state.affiliation, Subscriptions = GenState#pubsub_state.subscriptions, Whitelisted = can_fetch_item(Affiliation, Subscriptions), if %%SubId == "", ?? -> %% Entity has multiple subscriptions to the node but does not specify a subscription ID %{error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")}; %%InvalidSubId -> %% Entity is subscribed but specifies an invalid subscription ID %{error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; (Affiliation == outcast) or (Affiliation == publish_only) -> {error, xmpp:err_forbidden()}; (AccessModel == presence) and not PresenceSubscription -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and not RosterGroup -> {error, mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and not Whitelisted -> {error, mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())}; (AccessModel == authorize) and not Whitelisted -> {error, xmpp:err_forbidden()}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; true -> get_item(Nidx, ItemId) end. %% @doc

Write an item into database.

set_item(Item) when is_record(Item, pubsub_item) -> mnesia:write(Item). %set_item(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}. %% @doc

Delete an item from database.

del_item(Nidx, ItemId) -> mnesia:delete({pubsub_item, {ItemId, Nidx}}). del_items(Nidx, ItemIds) -> lists:foreach(fun (ItemId) -> del_item(Nidx, ItemId) end, ItemIds). del_orphan_items(Nidx) -> case mnesia:read({pubsub_orphan, Nidx}) of [#pubsub_orphan{items = ItemIds}] -> del_items(Nidx, ItemIds), mnesia:delete({pubsub_orphan, Nidx}); _ -> ok end. get_item_name(_Host, _Node, Id) -> Id. %% @doc

Return the path of the node. In flat it's just node id.

node_to_path(Node) -> [(Node)]. path_to_node(Path) -> case Path of % default slot [Node] -> iolist_to_binary(Node); % handle old possible entries, used when migrating database content to new format [Node | _] when is_binary(Node) -> iolist_to_binary(str:join([<<"">> | Path], <<"/">>)); % default case (used by PEP for example) _ -> iolist_to_binary(Path) end. can_fetch_item(owner, _) -> true; can_fetch_item(member, _) -> true; can_fetch_item(publisher, _) -> true; can_fetch_item(publish_only, _) -> false; can_fetch_item(outcast, _) -> false; can_fetch_item(none, Subscriptions) -> is_subscribed(Subscriptions). %can_fetch_item(_Affiliation, _Subscription) -> false. is_subscribed(Subscriptions) -> lists:any(fun ({subscribed, _SubId}) -> true; (_) -> false end, Subscriptions). first_in_list(_Pred, []) -> false; first_in_list(Pred, [H | T]) -> case Pred(H) of true -> {value, H}; _ -> first_in_list(Pred, T) end. rsm_page(Count, Index, Offset, Items) -> FirstItem = hd(Items), LastItem = lists:last(Items), First = decode_stamp(element(1, FirstItem#pubsub_item.creation)), Last = decode_stamp(element(1, LastItem#pubsub_item.creation)), #rsm_set{count = Count, index = Index, first = #rsm_first{index = Offset, data = First}, last = Last}. encode_stamp(Stamp) -> case catch xmpp_util:decode_timestamp(Stamp) of {MS,S,US} -> {MS,S,US}; _ -> Stamp end. decode_stamp(Stamp) -> case catch xmpp_util:encode_timestamp(Stamp) of TimeStamp when is_binary(TimeStamp) -> TimeStamp; _ -> Stamp end. transform({pubsub_state, {Id, Nidx}, Is, A, Ss}) -> {pubsub_state, {Id, Nidx}, Nidx, Is, A, Ss}; transform({pubsub_item, {Id, Nidx}, C, M, P}) -> {pubsub_item, {Id, Nidx}, Nidx, C, M, P}; transform(Rec) -> Rec. ejabberd-18.01/src/ELDAPv3.asn1db0000644000232200023220000010613413225664356016627 0ustar debalancedebalancecXM $bWLAhhd compresseddfalsehdmemorybhdownergd nonode@nohostwhdheirdnonehdnamed asn1_ELDAPv3hdsizea9hdnoded nonode@nohosthd named_tabledfalsehdtypedsethdkeyposahd protectiond protectedhd major_versionahd minor_versionahd extended_infojbWLAhdMatchingRuleAssertionhdtypedefdtrueadMatchingRuleAssertionhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypead matchingRulehdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajahd ComponentTypeadtypehdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajahd ComponentTypead matchValuehdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajahd ComponentTypead dnAttributeshdtypelhdtagdCONTEXTadIMPLICITajdBOOLEANjjdnohdDEFAULTdfalselhdCONTEXTajajjjdnobWLAhdAttributeValueAssertionhdtypedefdtruea?dAttributeValueAssertionhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypea@d attributeDeschdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeaAdassertionValuehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajajjjdnobWLAhdAttributeDescriptionhdtypedefdtruea0dAttributeDescriptionhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnobbWLAhdEXTERNALhdtypedefdfalsed undefineddEXTERNALhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefinedddirect-referencehdtypejdOBJECT IDENTIFIERjjdnodOPTIONALlhd UNIVERSALajd undefinedhd ComponentTyped undefineddindirect-referencehdtypejdINTEGERjjdnodOPTIONALlhd UNIVERSALajd undefinedhd ComponentTyped undefinedddata-value-descriptorhdtypejdObjectDescriptorjjdnodOPTIONALd undefinedd undefinedhd ComponentTyped undefineddencodinghdtypejhdCHOICElhd ComponentTyped undefineddsingle-ASN1-typehdtypelhdtagdCONTEXTadEXPLICITa jdANYjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTyped undefinedd octet-alignedhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTyped undefinedd arbitraryhdtypelhdtagdCONTEXTadIMPLICITajhd BIT STRINGjjjdnod mandatorylhdCONTEXTajd undefinedjjjdnod mandatoryd undefinedd undefinedjjjdnobWLAhdReferralhdtypedefdtrueadReferralhdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnotbWLAhdLDAPURLhdtypedefdtrueadLDAPURLhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnobWLAhdAttributeValuehdtypedefdtruea=dAttributeValuehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnobWLAhdExtendedResponsehdtypedefdtruebdExtendedResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeaLd resultCodehdtypelhdtagd UNIVERSALa dIMPLICITajhd ENUMERATEDl'hdsuccessahdoperationsErrorahd protocolErrorahdtimeLimitExceededahdsizeLimitExceededahd compareFalseahd compareTrueahdauthMethodNotSupportedahdstrongAuthRequiredahdreferrala hdadminLimitExceededa hdunavailableCriticalExtensiona hdconfidentialityRequireda hdsaslBindInProgressahdnoSuchAttributeahdundefinedAttributeTypeahdinappropriateMatchingahdconstraintViolationahdattributeOrValueExistsahdinvalidAttributeSyntaxahd noSuchObjecta hd aliasProblema!hdinvalidDNSyntaxa"hdaliasDereferencingProblema$hdinappropriateAuthenticationa0hdinvalidCredentialsa1hdinsufficientAccessRightsa2hdbusya3hd unavailablea4hdunwillingToPerforma5hd loopDetecta6hdnamingViolationa@hdobjectClassViolationaAhdnotAllowedOnNonLeafaBhdnotAllowedOnRDNaChdentryAlreadyExistsaDhdobjectClassModsProhibitedaEhdaffectsMultipleDSAsaGhdotheraPjjjdnod mandatorylhd UNIVERSALa jahd ComponentTypead matchedDNhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead errorMessagehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadreferralhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenceadELDAPv3dReferraljjdnodOPTIONALlhdCONTEXTajahd ComponentTypebd responseNamehdtypelhdtagdCONTEXTa dIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTa jahd ComponentTypebdresponsehdtypelhdtagdCONTEXTa dIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTa jajjjdnobWLAhdModifyDNResponsehdtypedefdtrueb dModifyDNResponsehdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferenceb dELDAPv3d LDAPResultjjdnobWLAhd AttributeTypehdtypedefdtruea.d AttributeTypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnoZbWLAhdPasswdModifyRequestValuehdtypedefdtrueb#dPasswdModifyRequestValuehdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeb$d userIdentityhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajahd ComponentTypeb%d oldPasswdhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajahd ComponentTypeb&d newPasswdhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajajjjdnobWLAhdCompareResponsehdtypedefdtruebdCompareResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferencebdELDAPv3d LDAPResultjjdno|bWLAhd DelRequesthdtypedefdtruead DelRequesthdtypelhdtagd APPLICATIONa dIMPLICITajd OCTET STRINGjjdno;bWLAhd AttributeListhdtypedefdtruead AttributeListhdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadtypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadvalshdtypelhdtagd UNIVERSALadIMPLICITa jhdSET OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnod mandatorylhd UNIVERSALajajjjdnojjdnobWLAhd AddRequesthdtypedefdtruead AddRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadentryhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead attributeshdtypelhdtagd UNIVERSALadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d AttributeListjjdnod mandatorylhd UNIVERSALajajjjdnoqbWLAhd BindRequesthdtypedefdtruead BindRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadversionhdtypelhdtagd UNIVERSALadIMPLICITajdINTEGERlhd ValueRangehaajjdnod mandatorylhd UNIVERSALajahd ComponentTypeadnamehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadauthenticationhdtypejhdExternaltypereferenced undefineddELDAPv3dAuthenticationChoicejjdnod mandatorylhdCONTEXTahdCONTEXTajajjjdnobWLAhdAttributeDescriptionListhdtypedefdtruea:dAttributeDescriptionListhdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnobWLAhdSubstringFilterhdtypedefdtrueadSubstringFilterhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadtypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead substringshdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypejhdCHOICElhd ComponentTypeadinitialhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadanyhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadfinalhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajd undefinedjjjdnojjdnod mandatorylhd UNIVERSALajajjjdnozbWLAhd LDAPStringhdtypedefdtruea&d LDAPStringhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnoEbWLAhdPasswdModifyResponseValuehdtypedefdtrueb(dPasswdModifyResponseValuehdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeb)d genPasswdhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajajjjdnobWLAhdSearchResultDonehdtypedefdtrueadSearchResultDonehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d LDAPResultjjdnobWLAhdAssertionValuehdtypedefdtrueaCdAssertionValuehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnobWLAhdAbandonRequesthdtypedefdtruebdAbandonRequesthdtypelhdtagd APPLICATIONadIMPLICITajdINTEGERlhd ValueRangehabjjdnobWLAhdCompareRequesthdtypedefdtruebdCompareRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypebdentryhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypebdavahdtypelhdtagd UNIVERSALadIMPLICITa jhdExternaltypereferencebdELDAPv3dAttributeValueAssertionjjdnod mandatorylhd UNIVERSALajajjjdnozbWLAhd UnbindRequesthdtypedefdtruead UnbindRequesthdtypelhdtagd APPLICATIONadIMPLICITajdNULLjjdnobWLAhd SearchRequesthdtypedefdtruead SearchRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypead baseObjecthdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadscopehdtypelhdtagd UNIVERSALa dIMPLICITajhd ENUMERATEDlhd baseObjectahd singleLevelahd wholeSubtreeajjjdnod mandatorylhd UNIVERSALa jahd ComponentTypead derefAliaseshdtypelhdtagd UNIVERSALa dIMPLICITajhd ENUMERATEDlhdneverDerefAliasesahdderefInSearchingahdderefFindingBaseObjahd derefAlwaysajjjdnod mandatorylhd UNIVERSALa jahd ComponentTypead sizeLimithdtypelhdtagd UNIVERSALadIMPLICITajdINTEGERlhd ValueRangehabjjdnod mandatorylhd UNIVERSALajahd ComponentTypead timeLimithdtypelhdtagd UNIVERSALadIMPLICITajdINTEGERlhd ValueRangehabjjdnod mandatorylhd UNIVERSALajahd ComponentTypead typesOnlyhdtypelhdtagd UNIVERSALadIMPLICITajdBOOLEANjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadfilterhdtypejhdExternaltypereferenced undefineddELDAPv3dFilterjjdnod mandatoryl hdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTahdCONTEXTa jahd ComponentTypead attributeshdtypelhdtagd UNIVERSALadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dAttributeDescriptionListjjdnod mandatorylhd UNIVERSALajajjjdnoZbWLAhdTYPE-IDENTIFIERhdclassdefdtrued undefineddTYPE-IDENTIFIERhd objectclasslhdfixedtypevaluefielddidhdtypehdtagd UNIVERSALadIMPLICITadOBJECT IDENTIFIERjjdnodUNIQUEd MANDATORYhd typefielddTyped MANDATORYjhd WITH SYNTAXlhdtypefieldreferencedTyped IDENTIFIEDdBYhdvaluefieldreferencedidjAbWLAhdControlhdtypedefdtrueadControlhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypead controlTypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead criticalityhdtypelhdtagd UNIVERSALadIMPLICITajdBOOLEANjjdnohdDEFAULTdfalselhd UNIVERSALajahd ComponentTypead controlValuehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhd UNIVERSALajajjjdnobWLAhdSearchResultEntryhdtypedefdtrueadSearchResultEntryhdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypead objectNamehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead attributeshdtypelhdtagd UNIVERSALadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dPartialAttributeListjjdnod mandatorylhd UNIVERSALajajjjdnoHbWLAhd EMBEDDED PDVhdtypedefdfalsed undefinedd EMBEDDED PDVhdtypelhdtagd UNIVERSALa dIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefineddidentificationhdtypejhdCHOICElhd ComponentTyped undefineddsyntaxeshdtypejhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefineddabstracthdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddtransferhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedjjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddsyntaxhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddpresentation-context-idhdtypejdINTEGERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddcontext-negotiationhdtypejhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefineddpresentation-context-idhdtypejdINTEGERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddtransfer-syntaxhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedjjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddtransfer-syntaxhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddfixedhdtypejdNULLjjdnod mandatoryd undefinedd undefinedjjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefinedd data-valuehdtypejd OCTET STRINGjjdnod mandatoryd undefinedd undefinedjjjdnobWLAhdModifyResponsehdtypedefdtrueadModifyResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenceadELDAPv3d LDAPResultjjdnobWLAhdSearchResultReferencehdtypedefdtrueadSearchResultReferencehdtypelhdtagd APPLICATIONadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnobWLAhd DelResponsehdtypedefdtruebd DelResponsehdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferencebdELDAPv3d LDAPResultjjdnobWLAhd AddResponsehdtypedefdtruead AddResponsehdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferenceadELDAPv3d LDAPResultjjdno2bWLAhd BindResponsehdtypedefdtruead BindResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeaLd resultCodehdtypelhdtagd UNIVERSALa dIMPLICITajhd ENUMERATEDl'hdsuccessahdoperationsErrorahd protocolErrorahdtimeLimitExceededahdsizeLimitExceededahd compareFalseahd compareTrueahdauthMethodNotSupportedahdstrongAuthRequiredahdreferrala hdadminLimitExceededa hdunavailableCriticalExtensiona hdconfidentialityRequireda hdsaslBindInProgressahdnoSuchAttributeahdundefinedAttributeTypeahdinappropriateMatchingahdconstraintViolationahdattributeOrValueExistsahdinvalidAttributeSyntaxahd noSuchObjecta hd aliasProblema!hdinvalidDNSyntaxa"hdaliasDereferencingProblema$hdinappropriateAuthenticationa0hdinvalidCredentialsa1hdinsufficientAccessRightsa2hdbusya3hd unavailablea4hdunwillingToPerforma5hd loopDetecta6hdnamingViolationa@hdobjectClassViolationaAhdnotAllowedOnNonLeafaBhdnotAllowedOnRDNaChdentryAlreadyExistsaDhdobjectClassModsProhibitedaEhdaffectsMultipleDSAsaGhdotheraPjjjdnod mandatorylhd UNIVERSALa jahd ComponentTypead matchedDNhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead errorMessagehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadreferralhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dReferraljjdnodOPTIONALlhdCONTEXTajahd ComponentTypeadserverSaslCredshdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajajjjdnobWLAhdAuthenticationChoicehdtypedefdtrueadAuthenticationChoicehdtypejhdCHOICElhd ComponentTypeadsimplehdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadsaslhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dSaslCredentialsjjdnod mandatorylhdCONTEXTajd undefinedjjjdnobWLAhd AttributehdtypedefdtrueaEd Attributehdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeaFdtypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeaGdvalshdtypelhdtagd UNIVERSALadIMPLICITa jhdSET OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnod mandatorylhd UNIVERSALajajjjdnoIbWLAhdPartialAttributeListhdtypedefdtrueadPartialAttributeListhdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadtypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadvalshdtypelhdtagd UNIVERSALadIMPLICITa jhdSET OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnod mandatorylhd UNIVERSALajajjjdnojjdnobWLAhd LDAPResulthdtypedefdtrueaKd LDAPResulthdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeaLd resultCodehdtypelhdtagd UNIVERSALa dIMPLICITajhd ENUMERATEDl'hdsuccessahdoperationsErrorahd protocolErrorahdtimeLimitExceededahdsizeLimitExceededahd compareFalseahd compareTrueahdauthMethodNotSupportedahdstrongAuthRequiredahdreferrala hdadminLimitExceededa hdunavailableCriticalExtensiona hdconfidentialityRequireda hdsaslBindInProgressahdnoSuchAttributeahdundefinedAttributeTypeahdinappropriateMatchingahdconstraintViolationahdattributeOrValueExistsahdinvalidAttributeSyntaxahd noSuchObjecta hd aliasProblema!hdinvalidDNSyntaxa"hdaliasDereferencingProblema$hdinappropriateAuthenticationa0hdinvalidCredentialsa1hdinsufficientAccessRightsa2hdbusya3hd unavailablea4hdunwillingToPerforma5hd loopDetecta6hdnamingViolationa@hdobjectClassViolationaAhdnotAllowedOnNonLeafaBhdnotAllowedOnRDNaChdentryAlreadyExistsaDhdobjectClassModsProhibitedaEhdaffectsMultipleDSAsaGhdotheraPjjjdnod mandatorylhd UNIVERSALa jahd ComponentTypead matchedDNhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead errorMessagehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadreferralhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenceadELDAPv3dReferraljjdnodOPTIONALlhdCONTEXTajajjjdnoUbWLAhdmaxInthdvaluedefdtruea$dmaxInthdtypejdINTEGERjjdnobdELDAPv3bWLAhd ModifyRequesthdtypedefdtruead ModifyRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadobjecthdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead modificationhdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypead operationhdtypelhdtagd UNIVERSALa dIMPLICITajhd ENUMERATEDlhdaddahddeleteahdreplaceajjjdnod mandatorylhd UNIVERSALa jahd ComponentTypead modificationhdtypelhdtagd UNIVERSALadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dAttributeTypeAndValuesjjdnod mandatorylhd UNIVERSALajajjjdnojjdnod mandatorylhd UNIVERSALajajjjdnobWLAhd MessageIDhdtypedefdtruea"d MessageIDhdtypelhdtagd UNIVERSALadIMPLICITajdINTEGERlhd ValueRangehabjjdnobWLAhdAttributeTypeAndValueshdtypedefdtrueadAttributeTypeAndValueshdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypeadtypehdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypeadvalshdtypelhdtagd UNIVERSALadIMPLICITa jhdSET OFhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnojjdnod mandatorylhd UNIVERSALajajjjdnobWLAhdControlshdtypedefdtrueadControlshdtypelhdtagd UNIVERSALadIMPLICITa jhd SEQUENCE OFhdtypelhdtagd UNIVERSALadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dControljjdnojjdnobWLAhdSaslCredentialshdtypedefdtrueadSaslCredentialshdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypead mechanismhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypead credentialshdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhd UNIVERSALajajjjdnobWLAhdRelativeLDAPDNhdtypedefdtruea,dRelativeLDAPDNhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnorbWLAhdLDAPDNhdtypedefdtruea*dLDAPDNhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnobWLAhdABSTRACT-SYNTAXhdclassdefdtrued undefineddABSTRACT-SYNTAXhd objectclasslhdfixedtypevaluefielddidhdtypehdtagd UNIVERSALadIMPLICITadOBJECT IDENTIFIERjjdnodUNIQUEd MANDATORYhd typefielddTyped MANDATORYhdfixedtypevaluefielddpropertyhdtypehdtagd UNIVERSALadIMPLICITahd BIT STRINGjjjdnod undefinedhdDEFAULTkjhd WITH SYNTAXlhdtypefieldreferencedTyped IDENTIFIEDdBYhdvaluefieldreferencedidldHASdPROPERTYhdvaluefieldreferencedpropertyjjRbWLAhdCHARACTER STRINGhdtypedefdfalsed undefineddCHARACTER STRINGhdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefineddidentificationhdtypejhdCHOICElhd ComponentTyped undefineddsyntaxeshdtypejhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefineddabstracthdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddtransferhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedjjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddsyntaxhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddpresentation-context-idhdtypejdINTEGERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddcontext-negotiationhdtypejhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTyped undefineddpresentation-context-idhdtypejdINTEGERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddtransfer-syntaxhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedjjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddtransfer-syntaxhdtypejdOBJECT IDENTIFIERjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefineddfixedhdtypejdNULLjjdnod mandatoryd undefinedd undefinedjjjdnod mandatoryd undefinedd undefinedhd ComponentTyped undefinedd string-valuehdtypejd OCTET STRINGjjdnod mandatoryd undefinedd undefinedjjjdnoabWLAhd LDAPMessagehdtypedefdtruea d LDAPMessagehdtypelhdtagd UNIVERSALadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypea d messageIDhdtypelhdtagd UNIVERSALadIMPLICITajdINTEGERlhd ValueRangehabjjdnod mandatorylhd UNIVERSALajahd ComponentTypea d protocolOphdtypejhdCHOICElhd ComponentTypea d bindRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d BindRequestjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypea d bindResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d BindResponsejjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead unbindRequesthdtypelhdtagd APPLICATIONadIMPLICITajdNULLjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead searchRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d SearchRequestjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypeadsearchResEntryhdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dSearchResultEntryjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead searchResDonehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dSearchResultDonejjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead searchResRefhdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dSearchResultReferencejjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead modifyRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d ModifyRequestjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypeadmodifyResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dModifyResponsejjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead addRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d AddRequestjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead addResponsehdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d AddResponsejjdnod mandatorylhd APPLICATIONa jd undefinedhd ComponentTypead delRequesthdtypelhdtagd APPLICATIONa dIMPLICITajd OCTET STRINGjjdnod mandatorylhd APPLICATIONa jd undefinedhd ComponentTypead delResponsehdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferenced undefineddELDAPv3d DelResponsejjdnod mandatorylhd APPLICATIONa jd undefinedhd ComponentTypead modDNRequesthdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dModifyDNRequestjjdnod mandatorylhd APPLICATIONa jd undefinedhd ComponentTypead modDNResponsehdtypelhdtagd APPLICATIONa dIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dModifyDNResponsejjdnod mandatorylhd APPLICATIONa jd undefinedhd ComponentTypeadcompareRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dCompareRequestjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypeadcompareResponsehdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dCompareResponsejjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypeadabandonRequesthdtypelhdtagd APPLICATIONadIMPLICITajdINTEGERlhd ValueRangehabjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead extendedReqhdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dExtendedRequestjjdnod mandatorylhd APPLICATIONajd undefinedhd ComponentTypead extendedResphdtypelhdtagd APPLICATIONadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dExtendedResponsejjdnod mandatorylhd APPLICATIONajd undefinedjjjdnod mandatorylhd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONa hd APPLICATIONa hd APPLICATIONa hd APPLICATIONa hd APPLICATIONa hd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONahd APPLICATIONajahd ComponentTypea dcontrolshdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dControlsjjdnodOPTIONALlhdCONTEXTajajjjdnobWLAhdMODULEh dmoduleadELDAPv3jdIMPLICIThdexportsdallhdimportsjd undefinedhl1d LDAPMessaged MessageIDd LDAPStringdLDAPOIDdLDAPDNdRelativeLDAPDNd AttributeTypedAttributeDescriptiondAttributeDescriptionListdAttributeValuedAttributeValueAssertiondAssertionValued AttributedMatchingRuleIdd LDAPResultdReferraldLDAPURLdControlsdControld BindRequestdAuthenticationChoicedSaslCredentialsd BindResponsed UnbindRequestd SearchRequestdFilterdSubstringFilterdMatchingRuleAssertiondSearchResultEntrydPartialAttributeListdSearchResultReferencedSearchResultDoned ModifyRequestdAttributeTypeAndValuesdModifyResponsed AddRequestd AttributeListd AddResponsed DelRequestd DelResponsedModifyDNRequestdModifyDNResponsedCompareRequestdCompareResponsedAbandonRequestdExtendedRequestdExtendedResponsedPasswdModifyRequestValuedPasswdModifyResponseValuejldmaxIntdpasswdModifyOIDjjjjjbWLAhdExtendedRequesthdtypedefdtruebdExtendedRequesthdtypelhdtagd APPLICATIONadIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypebd requestNamehdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajahd ComponentTypebd requestValuehdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajajjjdnobWLAhdModifyDNRequesthdtypedefdtruebdModifyDNRequesthdtypelhdtagd APPLICATIONa dIMPLICITa jhdSEQUENCEdfalsedfalsed undefinedlhd ComponentTypebdentryhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypebdnewrdnhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnod mandatorylhd UNIVERSALajahd ComponentTypebd deleteoldrdnhdtypelhdtagd UNIVERSALadIMPLICITajdBOOLEANjjdnod mandatorylhd UNIVERSALajahd ComponentTypebd newSuperiorhdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnodOPTIONALlhdCONTEXTajajjjdnobWLAhdMatchingRuleIdhdtypedefdtrueaIdMatchingRuleIdhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnotbWLAhdLDAPOIDhdtypedefdtruea(dLDAPOIDhdtypelhdtagd UNIVERSALadIMPLICITajd OCTET STRINGjjdnobWLAhdpasswdModifyOIDhdvaluedefdtrueb!dpasswdModifyOIDhdtypejhdExternaltypereferenceb!dELDAPv3dLDAPOIDjjdnok1.3.6.1.4.1.4203.1.11.1dELDAPv3bWLAhdFilterhdtypedefdtrueadFilterhdtypejhdCHOICEl hd ComponentTypeadandhdtypelhdtagdCONTEXTadIMPLICITa jhdSET OFhdtypejhdExternaltypereferenced undefineddELDAPv3dFilterjjdnojjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadorhdtypelhdtagdCONTEXTadIMPLICITa jhdSET OFhdtypejhdExternaltypereferenceadELDAPv3dFilterjjdnojjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadnothdtypelhdtagdCONTEXTadEXPLICITa jhdExternaltypereferenceadELDAPv3dFilterjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypead equalityMatchhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dAttributeValueAssertionjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypead substringshdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dSubstringFilterjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadgreaterOrEqualhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenceadELDAPv3dAttributeValueAssertionjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypead lessOrEqualhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenceadELDAPv3dAttributeValueAssertionjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadpresenthdtypelhdtagdCONTEXTadIMPLICITajd OCTET STRINGjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypead approxMatchhdtypelhdtagdCONTEXTadIMPLICITa jhdExternaltypereferenceadELDAPv3dAttributeValueAssertionjjdnod mandatorylhdCONTEXTajd undefinedhd ComponentTypeadextensibleMatchhdtypelhdtagdCONTEXTa dIMPLICITa jhdExternaltypereferenced undefineddELDAPv3dMatchingRuleAssertionjjdnod mandatorylhdCONTEXTa jd undefinedjjjdnoejabberd-18.01/src/ejabberd_logger.erl0000644000232200023220000002130413225664356020233 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_logger.erl %%% Author : Evgeniy Khramtsov %%% Purpose : ejabberd logger wrapper %%% Created : 12 May 2013 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2013-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%%------------------------------------------------------------------- -module(ejabberd_logger). -behaviour(ejabberd_config). %% API -export([start/0, restart/0, reopen_log/0, rotate_log/0, get/0, set/1, get_log_path/0, opt_type/1]). -include("ejabberd.hrl"). -type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5. -spec start() -> ok. -spec get_log_path() -> string(). -spec reopen_log() -> ok. -spec rotate_log() -> ok. -spec get() -> {loglevel(), atom(), string()}. -spec set(loglevel() | {loglevel(), list()}) -> {module, module()}. %%%=================================================================== %%% API %%%=================================================================== %% @doc Returns the full path to the ejabberd log file. %% It first checks for application configuration parameter 'log_path'. %% If not defined it checks the environment variable EJABBERD_LOG_PATH. %% And if that one is neither defined, returns the default value: %% "ejabberd.log" in current directory. %% Note: If the directory where to place the ejabberd log file to not exist, %% it is not created and no log file will be generated. %% @spec () -> string() get_log_path() -> case ejabberd_config:env_binary_to_list(ejabberd, log_path) of {ok, Path} -> Path; undefined -> case os:getenv("EJABBERD_LOG_PATH") of false -> ?LOG_PATH; Path -> Path end end. opt_type(log_rotate_date) -> fun(S) -> binary_to_list(iolist_to_binary(S)) end; opt_type(log_rotate_size) -> fun(I) when is_integer(I), I >= 0 -> I end; opt_type(log_rotate_count) -> fun(I) when is_integer(I), I >= 0 -> I end; opt_type(log_rate_limit) -> fun(I) when is_integer(I), I >= 0 -> I end; opt_type(_) -> [log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit]. get_integer_env(Name, Default) -> case application:get_env(ejabberd, Name) of {ok, I} when is_integer(I), I>=0 -> I; undefined -> Default; {ok, Junk} -> error_logger:error_msg("wrong value for ~s: ~p; " "using ~p as a fallback~n", [Name, Junk, Default]), Default end. get_string_env(Name, Default) -> case application:get_env(ejabberd, Name) of {ok, L} when is_list(L) -> L; undefined -> Default; {ok, Junk} -> error_logger:error_msg("wrong value for ~s: ~p; " "using ~p as a fallback~n", [Name, Junk, Default]), Default end. %% @spec () -> ok start() -> start(4). -spec start(loglevel()) -> ok. start(Level) -> LLevel = get_lager_loglevel(Level), StartedApps = application:which_applications(5000), case lists:keyfind(logger, 1, StartedApps) of %% Elixir logger is started. We assume everything is in place %% to use lager to Elixir logger bridge. {logger, _, _} -> error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []), %% Do not start lager, we rely on Elixir Logger do_start_for_logger(LLevel); _ -> do_start(LLevel) end. do_start_for_logger(Level) -> application:load(sasl), application:set_env(sasl, sasl_error_logger, false), application:load(lager), application:set_env(lager, error_logger_redirect, false), application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']), application:set_env(lager, crash_log, false), application:set_env(lager, handlers, [{elixir_logger_backend, [{level, Level}]}]), ejabberd:start_app(lager), ok. %% Start lager do_start(Level) -> application:load(sasl), application:set_env(sasl, sasl_error_logger, false), application:load(lager), ConsoleLog = get_log_path(), Dir = filename:dirname(ConsoleLog), ErrorLog = filename:join([Dir, "error.log"]), CrashLog = filename:join([Dir, "crash.log"]), LogRotateDate = get_string_env(log_rotate_date, ""), LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), LogRotateCount = get_integer_env(log_rotate_count, 1), LogRateLimit = get_integer_env(log_rate_limit, 100), application:set_env(lager, error_logger_hwm, LogRateLimit), application:set_env( lager, handlers, [{lager_console_backend, Level}, {lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate}, {count, LogRotateCount}, {size, LogRotateSize}]}, {lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate}, {count, LogRotateCount}, {size, LogRotateSize}]}]), application:set_env(lager, crash_log, CrashLog), application:set_env(lager, crash_log_date, LogRotateDate), application:set_env(lager, crash_log_size, LogRotateSize), application:set_env(lager, crash_log_count, LogRotateCount), ejabberd:start_app(lager), lists:foreach(fun(Handler) -> lager:set_loghwm(Handler, LogRateLimit) end, gen_event:which_handlers(lager_event)), ok. restart() -> Level = ejabberd_config:get_option(loglevel, 4), application:stop(lager), start(Level). %% @spec () -> ok reopen_log() -> %% Lager detects external log rotation automatically. ok. %% @spec () -> ok rotate_log() -> catch lager_crash_log ! rotate, lists:foreach( fun({lager_file_backend, File}) -> whereis(lager_event) ! {rotate, File}; (_) -> ok end, gen_event:which_handlers(lager_event)). %% @spec () -> {loglevel(), atom(), string()} get() -> case get_lager_loglevel() of none -> {0, no_log, "No log"}; emergency -> {1, critical, "Critical"}; alert -> {1, critical, "Critical"}; critical -> {1, critical, "Critical"}; error -> {2, error, "Error"}; warning -> {3, warning, "Warning"}; notice -> {3, warning, "Warning"}; info -> {4, info, "Info"}; debug -> {5, debug, "Debug"} end. %% @spec (loglevel() | {loglevel(), list()}) -> {module, module()} set(LogLevel) when is_integer(LogLevel) -> LagerLogLevel = get_lager_loglevel(LogLevel), case get_lager_loglevel() of LagerLogLevel -> ok; _ -> ConsoleLog = get_log_path(), lists:foreach( fun({lager_file_backend, File} = H) when File == ConsoleLog -> lager:set_loglevel(H, LagerLogLevel); (lager_console_backend = H) -> lager:set_loglevel(H, LagerLogLevel); (elixir_logger_backend = H) -> lager:set_loglevel(H, LagerLogLevel); (_) -> ok end, gen_event:which_handlers(lager_event)) end, {module, lager}; set({_LogLevel, _}) -> error_logger:error_msg("custom loglevels are not supported for 'lager'"), {module, lager}. get_lager_loglevel() -> Handlers = get_lager_handlers(), lists:foldl(fun(lager_console_backend, _Acc) -> lager:get_loglevel(lager_console_backend); (elixir_logger_backend, _Acc) -> lager:get_loglevel(elixir_logger_backend); (_, Acc) -> Acc end, none, Handlers). get_lager_loglevel(LogLevel) -> case LogLevel of 0 -> none; 1 -> critical; 2 -> error; 3 -> warning; 4 -> info; 5 -> debug; E -> erlang:error({wrong_loglevel, E}) end. get_lager_handlers() -> case catch gen_event:which_handlers(lager_event) of {'EXIT',noproc} -> []; Result -> Result end. ejabberd-18.01/src/misc.erl0000644000232200023220000002104013225664356016066 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% @doc %%% This is the place for some unsorted auxiliary functions %%% Some functions from jlib.erl are moved here %%% Mild rubbish heap is accepted ;) %%% @end %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(misc). %% API -export([tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1, hex_to_bin/1, hex_to_base64/1, expand_keyword/3, atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1, l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1, now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2, compile_exprs/2, join_atoms/2, try_read_file/1, have_eimp/0, css_dir/0, img_dir/0, js_dir/0, read_css/1, read_img/1, read_js/1]). %% Deprecated functions -export([decode_base64/1, encode_base64/1]). -deprecated([{decode_base64, 1}, {encode_base64, 1}]). -include("logger.hrl"). -include_lib("kernel/include/file.hrl"). %%%=================================================================== %%% API %%%=================================================================== -spec tolower(binary()) -> binary(). tolower(B) -> iolist_to_binary(tolower_s(binary_to_list(B))). tolower_s([C | Cs]) -> if C >= $A, C =< $Z -> [C + 32 | tolower_s(Cs)]; true -> [C | tolower_s(Cs)] end; tolower_s([]) -> []. -spec term_to_base64(term()) -> binary(). term_to_base64(Term) -> encode_base64(term_to_binary(Term)). -spec base64_to_term(binary()) -> {term, term()} | error. base64_to_term(Base64) -> try binary_to_term(base64:decode(Base64), [safe]) of Term -> {term, Term} catch _:badarg -> error end. -spec decode_base64(binary()) -> binary(). decode_base64(S) -> try base64:mime_decode(S) catch _:badarg -> <<>> end. -spec encode_base64(binary()) -> binary(). encode_base64(Data) -> base64:encode(Data). -spec ip_to_list(inet:ip_address() | undefined | {inet:ip_address(), inet:port_number()}) -> binary(). ip_to_list({IP, _Port}) -> ip_to_list(IP); %% This function clause could use inet_parse too: ip_to_list(undefined) -> <<"unknown">>; ip_to_list(IP) -> list_to_binary(inet_parse:ntoa(IP)). -spec hex_to_bin(binary()) -> binary(). hex_to_bin(Hex) -> hex_to_bin(binary_to_list(Hex), []). -spec hex_to_bin(list(), list()) -> binary(). hex_to_bin([], Acc) -> list_to_binary(lists:reverse(Acc)); hex_to_bin([H1, H2 | T], Acc) -> {ok, [V], []} = io_lib:fread("~16u", [H1, H2]), hex_to_bin(T, [V | Acc]). -spec hex_to_base64(binary()) -> binary(). hex_to_base64(Hex) -> base64:encode(hex_to_bin(Hex)). -spec expand_keyword(binary(), binary(), binary()) -> binary(). expand_keyword(Keyword, Input, Replacement) -> Parts = binary:split(Input, Keyword, [global]), str:join(Parts, Replacement). binary_to_atom(Bin) -> erlang:binary_to_atom(Bin, utf8). tuple_to_binary(T) -> iolist_to_binary(tuple_to_list(T)). atom_to_binary(A) -> erlang:atom_to_binary(A, utf8). expr_to_term(Expr) -> Str = binary_to_list(<>), {ok, Tokens, _} = erl_scan:string(Str), {ok, Term} = erl_parse:parse_term(Tokens), Term. term_to_expr(Term) -> list_to_binary(io_lib:print(Term)). -spec now_to_usec(erlang:timestamp()) -> non_neg_integer(). now_to_usec({MSec, Sec, USec}) -> (MSec*1000000 + Sec)*1000000 + USec. -spec usec_to_now(non_neg_integer()) -> erlang:timestamp(). usec_to_now(Int) -> Secs = Int div 1000000, USec = Int rem 1000000, MSec = Secs div 1000000, Sec = Secs rem 1000000, {MSec, Sec, USec}. l2i(I) when is_integer(I) -> I; l2i(L) when is_binary(L) -> binary_to_integer(L). i2l(I) when is_integer(I) -> integer_to_binary(I); i2l(L) when is_binary(L) -> L. i2l(I, N) when is_integer(I) -> i2l(i2l(I), N); i2l(L, N) when is_binary(L) -> case str:len(L) of N -> L; C when C > N -> L; _ -> i2l(<<$0, L/binary>>, N) end. -spec encode_pid(pid()) -> binary(). encode_pid(Pid) -> list_to_binary(erlang:pid_to_list(Pid)). -spec decode_pid(binary(), binary()) -> pid(). decode_pid(PidBin, NodeBin) -> PidStr = binary_to_list(PidBin), Pid = erlang:list_to_pid(PidStr), case erlang:binary_to_atom(NodeBin, latin1) of Node when Node == node() -> Pid; Node -> try set_node_id(PidStr, NodeBin) catch _:badarg -> erlang:error({bad_node, Node}) end end. -spec compile_exprs(module(), [string()]) -> ok | {error, any()}. compile_exprs(Mod, Exprs) -> try Forms = lists:map( fun(Expr) -> {ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)), {ok, Form} = erl_parse:parse_form(Tokens), Form end, Exprs), {ok, Code} = case compile:forms(Forms, []) of {ok, Mod, Bin} -> {ok, Bin}; {ok, Mod, Bin, _Warnings} -> {ok, Bin}; Error -> Error end, {module, Mod} = code:load_binary(Mod, "nofile", Code), ok catch _:{badmatch, {error, ErrInfo, _ErrLocation}} -> {error, ErrInfo}; _:{badmatch, {error, _} = Err} -> Err; _:{badmatch, error} -> {error, compile_failed} end. -spec join_atoms([atom()], binary()) -> binary(). join_atoms(Atoms, Sep) -> str:join([io_lib:format("~p", [A]) || A <- Atoms], Sep). %% @doc Checks if the file is readable and converts its name to binary. %% Fails with `badarg` otherwise. The function is intended for usage %% in configuration validators only. -spec try_read_file(file:filename_all()) -> binary(). try_read_file(Path) -> case file:open(Path, [read]) of {ok, Fd} -> file:close(Fd), iolist_to_binary(Path); {error, Why} -> ?ERROR_MSG("Failed to read ~s: ~s", [Path, file:format_error(Why)]), erlang:error(badarg) end. -ifdef(GRAPHICS). have_eimp() -> true. -else. have_eimp() -> false. -endif. -spec css_dir() -> file:filename(). css_dir() -> case os:getenv("EJABBERD_CSS_PATH") of false -> case code:priv_dir(ejabberd) of {error, _} -> filename:join(["priv", "css"]); Path -> filename:join([Path, "css"]) end; Path -> Path end. -spec img_dir() -> file:filename(). img_dir() -> case os:getenv("EJABBERD_IMG_PATH") of false -> case code:priv_dir(ejabberd) of {error, _} -> filename:join(["priv", "img"]); Path -> filename:join([Path, "img"]) end; Path -> Path end. -spec js_dir() -> file:filename(). js_dir() -> case os:getenv("EJABBERD_JS_PATH") of false -> case code:priv_dir(ejabberd) of {error, _} -> filename:join(["priv", "js"]); Path -> filename:join([Path, "js"]) end; Path -> Path end. -spec read_css(file:filename()) -> {ok, binary()} | {error, file:posix()}. read_css(File) -> read_file(filename:join(css_dir(), File)). -spec read_img(file:filename()) -> {ok, binary()} | {error, file:posix()}. read_img(File) -> read_file(filename:join(img_dir(), File)). -spec read_js(file:filename()) -> {ok, binary()} | {error, file:posix()}. read_js(File) -> read_file(filename:join(js_dir(), File)). %%%=================================================================== %%% Internal functions %%%=================================================================== -spec set_node_id(string(), binary()) -> pid(). set_node_id(PidStr, NodeBin) -> ExtPidStr = erlang:pid_to_list( binary_to_term( <<131,103,100,(size(NodeBin)):16,NodeBin/binary,0:72>>)), [H|_] = string:tokens(ExtPidStr, "."), [_|T] = string:tokens(PidStr, "."), erlang:list_to_pid(string:join([H|T], ".")). -spec read_file(file:filename()) -> {ok, binary()} | {error, file:posix()}. read_file(Path) -> case file:read_file(Path) of {ok, Data} -> {ok, Data}; {error, Why} = Err -> ?ERROR_MSG("Failed to read file ~s: ~s", [Path, file:format_error(Why)]), Err end. ejabberd-18.01/src/mod_privilege.erl0000644000232200023220000002742713225664356017777 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_privilege.erl %%% Author : Anna Mukharram %%% Purpose : XEP-0356: Privileged Entity %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_privilege). -author('amuhar3@gmail.com'). -protocol({xep, 0356, '0.2.1'}). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([component_connected/1, component_disconnected/2, roster_access/2, process_message/1, process_presence_out/1, process_presence_in/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -record(state, {server_host = <<"">> :: binary(), permissions = dict:new() :: ?TDICT}). %%%=================================================================== %%% API %%%=================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(_Host, _NewOpts, _OldOpts) -> ok. mod_opt_type({roster, _}) -> fun acl:access_rules_validator/1; mod_opt_type({message, _}) -> fun acl:access_rules_validator/1; mod_opt_type({presence, _}) -> fun acl:access_rules_validator/1; mod_opt_type(_) -> [{roster, both}, {roster, get}, {roster, set}, {message, outgoing}, {presence, managed_entity}, {presence, roster}]. depends(_, _) -> []. -spec component_connected(binary()) -> ok. component_connected(Host) -> lists:foreach( fun(ServerHost) -> Proc = gen_mod:get_module_proc(ServerHost, ?MODULE), gen_server:cast(Proc, {component_connected, Host}) end, ?MYHOSTS). -spec component_disconnected(binary(), binary()) -> ok. component_disconnected(Host, _Reason) -> lists:foreach( fun(ServerHost) -> Proc = gen_mod:get_module_proc(ServerHost, ?MODULE), gen_server:cast(Proc, {component_disconnected, Host}) end, ?MYHOSTS). -spec process_message(stanza()) -> stop | ok. process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From, to = #jid{lresource = <<"">>} = To, lang = Lang, type = T} = Msg) when T /= error -> Host = From#jid.lserver, ServerHost = To#jid.lserver, Permissions = get_permissions(ServerHost), case dict:find(Host, Permissions) of {ok, Access} -> case proplists:get_value(message, Access, none) of outgoing -> forward_message(Msg); none -> Txt = <<"Insufficient privilege">>, Err = xmpp:err_forbidden(Txt, Lang), ejabberd_router:route_error(Msg, Err) end, stop; error -> %% Component is disconnected ok end; process_message(_Stanza) -> ok. -spec roster_access(boolean(), iq()) -> boolean(). roster_access(true, _) -> true; roster_access(false, #iq{from = From, to = To, type = Type}) -> Host = From#jid.lserver, ServerHost = To#jid.lserver, Permissions = get_permissions(ServerHost), case dict:find(Host, Permissions) of {ok, Access} -> Permission = proplists:get_value(roster, Access, none), (Permission == both) orelse (Permission == get andalso Type == get) orelse (Permission == set andalso Type == set); error -> %% Component is disconnected false end. -spec process_presence_out({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. process_presence_out({#presence{ from = #jid{luser = LUser, lserver = LServer} = From, to = #jid{luser = LUser, lserver = LServer, lresource = <<"">>}, type = Type} = Pres, C2SState}) when Type == available; Type == unavailable -> %% Self-presence processing Permissions = get_permissions(LServer), lists:foreach( fun({Host, Access}) -> Permission = proplists:get_value(presence, Access, none), if Permission == roster; Permission == managed_entity -> To = jid:make(Host), ejabberd_router:route( xmpp:set_from_to(Pres, From, To)); true -> ok end end, dict:to_list(Permissions)), {Pres, C2SState}; process_presence_out(Acc) -> Acc. -spec process_presence_in({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. process_presence_in({#presence{ from = #jid{luser = U, lserver = S} = From, to = #jid{luser = LUser, lserver = LServer}, type = Type} = Pres, C2SState}) when {U, S} /= {LUser, LServer} andalso (Type == available orelse Type == unavailable) -> Permissions = get_permissions(LServer), lists:foreach( fun({Host, Access}) -> case proplists:get_value(presence, Access, none) of roster -> Permission = proplists:get_value(roster, Access, none), if Permission == both; Permission == get -> To = jid:make(Host), ejabberd_router:route( xmpp:set_from_to(Pres, From, To)); true -> ok end; true -> ok end end, dict:to_list(Permissions)), {Pres, C2SState}; process_presence_in(Acc) -> Acc. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([Host, _Opts]) -> process_flag(trap_exit, true), ejabberd_hooks:add(component_connected, ?MODULE, component_connected, 50), ejabberd_hooks:add(component_disconnected, ?MODULE, component_disconnected, 50), ejabberd_hooks:add(local_send_to_resource_hook, Host, ?MODULE, process_message, 50), ejabberd_hooks:add(roster_remote_access, Host, ?MODULE, roster_access, 50), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, process_presence_out, 50), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, process_presence_in, 50), {ok, #state{server_host = Host}}. handle_call(get_permissions, _From, State) -> {reply, {ok, State#state.permissions}, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast({component_connected, Host}, State) -> ServerHost = State#state.server_host, From = jid:make(ServerHost), To = jid:make(Host), RosterPerm = get_roster_permission(ServerHost, Host), PresencePerm = get_presence_permission(ServerHost, Host), MessagePerm = get_message_permission(ServerHost, Host), if RosterPerm /= none; PresencePerm /= none; MessagePerm /= none -> Priv = #privilege{perms = [#privilege_perm{access = message, type = MessagePerm}, #privilege_perm{access = roster, type = RosterPerm}, #privilege_perm{access = presence, type = PresencePerm}]}, ?INFO_MSG("Granting permissions to external " "component '~s': roster = ~s, presence = ~s, " "message = ~s", [Host, RosterPerm, PresencePerm, MessagePerm]), Msg = #message{from = From, to = To, sub_els = [Priv]}, ejabberd_router:route(Msg), Permissions = dict:store(Host, [{roster, RosterPerm}, {presence, PresencePerm}, {message, MessagePerm}], State#state.permissions), {noreply, State#state{permissions = Permissions}}; true -> ?INFO_MSG("Granting no permissions to external component '~s'", [Host]), {noreply, State} end; handle_cast({component_disconnected, Host}, State) -> Permissions = dict:erase(Host, State#state.permissions), {noreply, State#state{permissions = Permissions}}; handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, State) -> %% Note: we don't remove component_* hooks because they are global %% and might be registered within a module on another virtual host Host = State#state.server_host, ejabberd_hooks:delete(local_send_to_resource_hook, Host, ?MODULE, process_message, 50), ejabberd_hooks:delete(roster_remote_access, Host, ?MODULE, roster_access, 50), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, process_presence_out, 50), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, process_presence_in, 50). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== get_permissions(ServerHost) -> Proc = gen_mod:get_module_proc(ServerHost, ?MODULE), try gen_server:call(Proc, get_permissions) of {ok, Permissions} -> Permissions catch exit:{noproc, _} -> %% No module is loaded for this virtual host dict:new() end. forward_message(#message{to = To} = Msg) -> ServerHost = To#jid.lserver, Lang = xmpp:get_lang(Msg), try xmpp:try_subtag(Msg, #privilege{}) of #privilege{forwarded = #forwarded{xml_els = [SubEl]}} -> try xmpp:decode(SubEl, ?NS_CLIENT, [ignore_els]) of #message{} = NewMsg -> case NewMsg#message.from of #jid{lresource = <<"">>, lserver = ServerHost} -> ejabberd_router:route(NewMsg); _ -> Lang = xmpp:get_lang(Msg), Txt = <<"Invalid 'from' attribute in forwarded message">>, Err = xmpp:err_forbidden(Txt, Lang), ejabberd_router:route_error(Msg, Err) end; _ -> Txt = <<"Message not found in forwarded payload">>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Msg, Err) catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Msg, Err) end; _ -> Txt = <<"No element found">>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Msg, Err) catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Msg, Err) end. get_roster_permission(ServerHost, Host) -> Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, roster, []), case match_rule(ServerHost, Host, Perms, both) of allow -> both; deny -> Get = match_rule(ServerHost, Host, Perms, get), Set = match_rule(ServerHost, Host, Perms, set), if Get == allow, Set == allow -> both; Get == allow -> get; Set == allow -> set; true -> none end end. get_message_permission(ServerHost, Host) -> Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, message, []), case match_rule(ServerHost, Host, Perms, outgoing) of allow -> outgoing; deny -> none end. get_presence_permission(ServerHost, Host) -> Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, presence, []), case match_rule(ServerHost, Host, Perms, roster) of allow -> roster; deny -> case match_rule(ServerHost, Host, Perms, managed_entity) of allow -> managed_entity; deny -> none end end. match_rule(ServerHost, Host, Perms, Type) -> Access = proplists:get_value(Type, Perms, none), acl:match_rule(ServerHost, Access, jid:make(Host)). ejabberd-18.01/src/mod_adhoc.erl0000644000232200023220000002341013225664356017053 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_adhoc.erl %%% Author : Magnus Henoch %%% Purpose : Handle incoming ad-doc requests (XEP-0050) %%% Created : 15 Nov 2005 by Magnus Henoch %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_adhoc). -author('henoch@dtek.chalmers.se'). -protocol({xep, 50, '1.2'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_local_iq/1, process_sm_iq/1, get_local_commands/5, get_local_identity/5, get_local_features/5, get_sm_commands/5, get_sm_identity/5, get_sm_features/5, ping_item/4, ping_command/4, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS, ?MODULE, process_sm_iq, IQDisc), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 99), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 99), ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_commands, 99), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 99), ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_commands, 99), ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, ping_item, 100), ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, ping_command, 100). stop(Host) -> ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, ping_command, 100), ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, ping_item, 100), ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_commands, 99), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 99), ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_commands, 99), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 99), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 99), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS). reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS, ?MODULE, process_sm_iq, IQDisc); true -> ok end. %------------------------------------------------------------------------- get_local_commands(Acc, _From, #jid{server = Server, lserver = LServer} = _To, <<"">>, Lang) -> Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false), case Display of false -> Acc; _ -> Items = case Acc of {result, I} -> I; _ -> [] end, Nodes = [#disco_item{jid = jid:make(Server), node = ?NS_COMMANDS, name = translate:translate(Lang, <<"Commands">>)}], {result, Items ++ Nodes} end; get_local_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) -> ejabberd_hooks:run_fold(adhoc_local_items, LServer, {result, []}, [From, To, Lang]); get_local_commands(_Acc, _From, _To, <<"ping">>, _Lang) -> {result, []}; get_local_commands(Acc, _From, _To, _Node, _Lang) -> Acc. %------------------------------------------------------------------------- get_sm_commands(Acc, _From, #jid{lserver = LServer} = To, <<"">>, Lang) -> Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false), case Display of false -> Acc; _ -> Items = case Acc of {result, I} -> I; _ -> [] end, Nodes = [#disco_item{jid = To, node = ?NS_COMMANDS, name = translate:translate(Lang, <<"Commands">>)}], {result, Items ++ Nodes} end; get_sm_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) -> ejabberd_hooks:run_fold(adhoc_sm_items, LServer, {result, []}, [From, To, Lang]); get_sm_commands(Acc, _From, _To, _Node, _Lang) -> Acc. %------------------------------------------------------------------------- %% On disco info request to the ad-hoc node, return automation/command-list. get_local_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) -> [#identity{category = <<"automation">>, type = <<"command-list">>, name = translate:translate(Lang, <<"Commands">>)} | Acc]; get_local_identity(Acc, _From, _To, <<"ping">>, Lang) -> [#identity{category = <<"automation">>, type = <<"command-node">>, name = translate:translate(Lang, <<"Ping">>)} | Acc]; get_local_identity(Acc, _From, _To, _Node, _Lang) -> Acc. %------------------------------------------------------------------------- %% On disco info request to the ad-hoc node, return automation/command-list. get_sm_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) -> [#identity{category = <<"automation">>, type = <<"command-list">>, name = translate:translate(Lang, <<"Commands">>)} | Acc]; get_sm_identity(Acc, _From, _To, _Node, _Lang) -> Acc. %------------------------------------------------------------------------- -spec get_local_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]} | empty. get_local_features(Acc, _From, _To, <<"">>, _Lang) -> Feats = case Acc of {result, I} -> I; _ -> [] end, {result, Feats ++ [?NS_COMMANDS]}; get_local_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) -> {result, []}; get_local_features(_Acc, _From, _To, <<"ping">>, _Lang) -> {result, [?NS_COMMANDS]}; get_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. %------------------------------------------------------------------------- get_sm_features(Acc, _From, _To, <<"">>, _Lang) -> Feats = case Acc of {result, I} -> I; _ -> [] end, {result, Feats ++ [?NS_COMMANDS]}; get_sm_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) -> {result, []}; get_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc. %------------------------------------------------------------------------- process_local_iq(IQ) -> process_adhoc_request(IQ, local). process_sm_iq(IQ) -> process_adhoc_request(IQ, sm). process_adhoc_request(#iq{from = From, to = To, type = set, lang = Lang, sub_els = [#adhoc_command{} = SubEl]} = IQ, Type) -> Host = To#jid.lserver, Res = case Type of local -> ejabberd_hooks:run_fold(adhoc_local_commands, Host, empty, [From, To, SubEl]); sm -> ejabberd_hooks:run_fold(adhoc_sm_commands, Host, empty, [From, To, SubEl]) end, case Res of ignore -> ignore; empty -> Txt = <<"No hook has processed this command">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)); {error, Error} -> xmpp:make_error(IQ, Error); Command -> xmpp:make_iq_result(IQ, Command) end; process_adhoc_request(#iq{} = IQ, _Hooks) -> xmpp:make_error(IQ, xmpp:err_bad_request()). -spec ping_item(empty | {error, stanza_error()} | {result, [disco_item()]}, jid(), jid(), binary()) -> {result, [disco_item()]}. ping_item(Acc, _From, #jid{server = Server} = _To, Lang) -> Items = case Acc of {result, I} -> I; _ -> [] end, Nodes = [#disco_item{jid = jid:make(Server), node = <<"ping">>, name = translate:translate(Lang, <<"Ping">>)}], {result, Items ++ Nodes}. -spec ping_command(adhoc_command(), jid(), jid(), adhoc_command()) -> adhoc_command() | {error, stanza_error()}. ping_command(_Acc, _From, _To, #adhoc_command{lang = Lang, node = <<"ping">>, action = Action} = Request) -> if Action == execute -> xmpp_util:make_adhoc_response( Request, #adhoc_command{ status = completed, notes = [#adhoc_note{ type = info, data = translate:translate(Lang, <<"Pong">>)}]}); true -> Txt = <<"Incorrect value of 'action' attribute">>, {error, xmpp:err_bad_request(Txt, Lang)} end; ping_command(Acc, _From, _To, _Request) -> Acc. depends(_Host, _Opts) -> []. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(report_commands_node) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [iqdisc, report_commands_node]. ejabberd-18.01/src/ejabberd_sql_sup.erl0000644000232200023220000001621413225664356020446 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_sql_sup.erl %%% Author : Alexey Shchepin %%% Purpose : SQL connections supervisor %%% Created : 22 Dec 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sql_sup). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([start_link/1, init/1, add_pid/2, remove_pid/2, get_pids/1, get_random_pid/1, transform_options/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -define(PGSQL_PORT, 5432). -define(MYSQL_PORT, 3306). -define(DEFAULT_POOL_SIZE, 10). -define(DEFAULT_SQL_START_INTERVAL, 30). -define(CONNECT_TIMEOUT, 500). -record(sql_pool, {host, pid}). start_link(Host) -> ejabberd_mnesia:create(?MODULE, sql_pool, [{ram_copies, [node()]}, {type, bag}, {local_content, true}, {attributes, record_info(fields, sql_pool)}]), F = fun () -> mnesia:delete({sql_pool, Host}) end, mnesia:ets(F), supervisor:start_link({local, gen_mod:get_module_proc(Host, ?MODULE)}, ?MODULE, [Host]). init([Host]) -> StartInterval = ejabberd_config:get_option( {sql_start_interval, Host}, ?DEFAULT_SQL_START_INTERVAL), Type = ejabberd_config:get_option({sql_type, Host}, odbc), PoolSize = get_pool_size(Type, Host), case Type of sqlite -> check_sqlite_db(Host); mssql -> ejabberd_sql:init_mssql(Host); _ -> ok end, {ok, {{one_for_one, PoolSize * 10, 1}, lists:map(fun (I) -> {I, {ejabberd_sql, start_link, [Host, StartInterval * 1000]}, transient, 2000, worker, [?MODULE]} end, lists:seq(1, PoolSize))}}. get_pids(Host) -> Rs = mnesia:dirty_read(sql_pool, Host), [R#sql_pool.pid || R <- Rs, is_process_alive(R#sql_pool.pid)]. get_random_pid(Host) -> case get_pids(Host) of [] -> none; Pids -> I = randoms:round_robin(length(Pids)) + 1, lists:nth(I, Pids) end. add_pid(Host, Pid) -> F = fun () -> mnesia:write(#sql_pool{host = Host, pid = Pid}) end, mnesia:ets(F). remove_pid(Host, Pid) -> F = fun () -> mnesia:delete_object(#sql_pool{host = Host, pid = Pid}) end, mnesia:ets(F). -spec get_pool_size(atom(), binary()) -> pos_integer(). get_pool_size(SQLType, Host) -> PoolSize = ejabberd_config:get_option( {sql_pool_size, Host}, case SQLType of sqlite -> 1; _ -> ?DEFAULT_POOL_SIZE end), if PoolSize > 1 andalso SQLType == sqlite -> ?WARNING_MSG("it's not recommended to set sql_pool_size > 1 for " "sqlite, because it may cause race conditions", []); true -> ok end, PoolSize. transform_options(Opts) -> lists:foldl(fun transform_options/2, [], Opts). transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) -> [{sql_type, Type}, {sql_server, Server}, {sql_port, Port}, {sql_database, DB}, {sql_username, User}, {sql_password, Pass}|Opts]; transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) -> transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts); transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) -> transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts); transform_options({odbc_server, {sqlite, DB}}, Opts) -> transform_options({odbc_server, {sqlite, DB}}, Opts); transform_options(Opt, Opts) -> [Opt|Opts]. check_sqlite_db(Host) -> DB = ejabberd_sql:sqlite_db(Host), File = ejabberd_sql:sqlite_file(Host), Ret = case filelib:ensure_dir(File) of ok -> case sqlite3:open(DB, [{file, File}]) of {ok, _Ref} -> ok; {error, {already_started, _Ref}} -> ok; {error, R} -> {error, R} end; Err -> Err end, case Ret of ok -> sqlite3:sql_exec(DB, "pragma foreign_keys = on"), case sqlite3:list_tables(DB) of [] -> create_sqlite_tables(DB), sqlite3:close(DB), ok; [_H | _] -> ok end; {error, Reason} -> ?INFO_MSG("Failed open sqlite database, reason ~p", [Reason]) end. create_sqlite_tables(DB) -> SqlDir = case code:priv_dir(ejabberd) of {error, _} -> ?SQL_DIR; PrivDir -> filename:join(PrivDir, "sql") end, File = filename:join(SqlDir, "lite.sql"), case file:open(File, [read, binary]) of {ok, Fd} -> Qs = read_lines(Fd, File, []), ok = sqlite3:sql_exec(DB, "begin"), [ok = sqlite3:sql_exec(DB, Q) || Q <- Qs], ok = sqlite3:sql_exec(DB, "commit"); {error, Reason} -> ?INFO_MSG("Failed to read SQLite schema file: ~s", [file:format_error(Reason)]) end. read_lines(Fd, File, Acc) -> case file:read_line(Fd) of {ok, Line} -> NewAcc = case str:strip(str:strip(Line, both, $\r), both, $\n) of <<"--", _/binary>> -> Acc; <<>> -> Acc; _ -> [Line|Acc] end, read_lines(Fd, File, NewAcc); eof -> QueryList = str:tokens(list_to_binary(lists:reverse(Acc)), <<";">>), lists:flatmap( fun(Query) -> case str:strip(str:strip(Query, both, $\r), both, $\n) of <<>> -> []; Q -> [<>] end end, QueryList); {error, _} = Err -> ?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]), [] end. -spec opt_type(sql_pool_size) -> fun((pos_integer()) -> pos_integer()); (sql_start_interval) -> fun((pos_integer()) -> pos_integer()); (atom()) -> [atom()]. opt_type(sql_pool_size) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(sql_start_interval) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(_) -> [sql_pool_size, sql_start_interval]. ejabberd-18.01/src/ejabberd_mnesia.erl0000644000232200023220000003401113225664356020227 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mnesia_mnesia.erl %%% Author : Christophe Romain %%% Purpose : Handle configurable mnesia schema %%% Created : 17 Nov 2016 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% This module should be used everywhere ejabberd creates a mnesia table %%% to make the schema customizable without code change %%% Just apply this change in ejabberd modules %%% s/ejabberd_mnesia:create(?MODULE, /ejabberd_mnesia:create(?MODULE, / -module(ejabberd_mnesia). -author('christophe.romain@process-one.net'). -behaviour(gen_server). -export([start/0, create/3, update/2, transform/2, transform/3, dump_schema/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -define(STORAGE_TYPES, [disc_copies, disc_only_copies, ram_copies]). -define(NEED_RESET, [local_content, type]). -include("logger.hrl"). -record(state, {tables = #{} :: map(), schema = [] :: [{atom(), [{atom(), any()}]}]}). start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec create(module(), atom(), list()) -> any(). create(Module, Name, TabDef) -> gen_server:call(?MODULE, {create, Module, Name, TabDef}, %% Huge timeout is need to have enough %% time to transform huge tables timer:minutes(30)). init([]) -> ejabberd_config:env_binary_to_list(mnesia, dir), MyNode = node(), DbNodes = mnesia:system_info(db_nodes), case lists:member(MyNode, DbNodes) of true -> case mnesia:system_info(extra_db_nodes) of [] -> mnesia:create_schema([node()]); _ -> ok end, ejabberd:start_app(mnesia, permanent), ?DEBUG("Waiting for Mnesia tables synchronization...", []), mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity), Schema = read_schema_file(), {ok, #state{schema = Schema}}; false -> ?CRITICAL_MSG("Node name mismatch: I'm [~s], " "the database is owned by ~p", [MyNode, DbNodes]), ?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg " "or change node name in Mnesia", []), {stop, node_name_mismatch} end. handle_call({create, Module, Name, TabDef}, _From, State) -> case maps:get(Name, State#state.tables, undefined) of {TabDef, Result} -> {reply, Result, State}; _ -> Result = do_create(Module, Name, TabDef, State#state.schema), Tables = maps:put(Name, {TabDef, Result}, State#state.tables), {reply, Result, State#state{tables = Tables}} end; handle_call(_Request, _From, State) -> {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. do_create(Module, Name, TabDef, TabDefs) -> code:ensure_loaded(Module), Schema = schema(Name, TabDef, TabDefs), {attributes, Attrs} = lists:keyfind(attributes, 1, Schema), case catch mnesia:table_info(Name, attributes) of {'EXIT', _} -> create(Name, TabDef); Attrs -> case need_reset(Name, Schema) of true -> reset(Name, Schema); false -> case update(Name, Attrs, Schema) of {atomic, ok} -> transform(Module, Name, Attrs, Attrs); Err -> Err end end; OldAttrs -> transform(Module, Name, OldAttrs, Attrs) end. reset(Name, TabDef) -> ?INFO_MSG("Deleting Mnesia table '~s'", [Name]), mnesia_op(delete_table, [Name]), create(Name, TabDef). update(Name, TabDef) -> {attributes, Attrs} = lists:keyfind(attributes, 1, TabDef), update(Name, Attrs, TabDef). update(Name, Attrs, TabDef) -> case change_table_copy_type(Name, TabDef) of {atomic, ok} -> CurrIndexes = [lists:nth(N-1, Attrs) || N <- mnesia:table_info(Name, index)], NewIndexes = proplists:get_value(index, TabDef, []), case delete_indexes(Name, CurrIndexes -- NewIndexes) of {atomic, ok} -> add_indexes(Name, NewIndexes -- CurrIndexes); Err -> Err end; Err -> Err end. change_table_copy_type(Name, TabDef) -> CurrType = mnesia:table_info(Name, storage_type), NewType = case lists:filter(fun is_storage_type_option/1, TabDef) of [{Type, _}|_] -> Type; [] -> CurrType end, if NewType /= CurrType -> ?INFO_MSG("Changing Mnesia table '~s' from ~s to ~s", [Name, CurrType, NewType]), mnesia_op(change_table_copy_type, [Name, node(), NewType]); true -> {atomic, ok} end. delete_indexes(Name, [Index|Indexes]) -> ?INFO_MSG("Deleting index '~s' from Mnesia table '~s'", [Index, Name]), case mnesia_op(del_table_index, [Name, Index]) of {atomic, ok} -> delete_indexes(Name, Indexes); Err -> Err end; delete_indexes(_Name, []) -> {atomic, ok}. add_indexes(Name, [Index|Indexes]) -> ?INFO_MSG("Adding index '~s' to Mnesia table '~s'", [Index, Name]), case mnesia_op(add_table_index, [Name, Index]) of {atomic, ok} -> add_indexes(Name, Indexes); Err -> Err end; add_indexes(_Name, []) -> {atomic, ok}. % % utilities % schema(Name, Default, Schema) -> case lists:keyfind(Name, 1, Schema) of {_, Custom} -> TabDefs = merge(Custom, Default), ?DEBUG("Using custom schema for table '~s': ~p", [Name, TabDefs]), TabDefs; false -> ?DEBUG("No custom Mnesia schema for table '~s' found", [Name]), Default end. read_schema_file() -> File = schema_path(), case fast_yaml:decode_from_file(File, [plain_as_atom]) of {ok, [Defs|_]} -> ?INFO_MSG("Using custom Mnesia schema from ~s", [File]), lists:flatmap( fun({Tab, Opts}) -> case validate_schema_opts(File, Opts) of {ok, NewOpts} -> [{Tab, lists:ukeysort(1, NewOpts)}]; error -> [] end end, Defs); {ok, []} -> ?WARNING_MSG("Mnesia schema file ~s is empty", [File]), []; {error, enoent} -> ?DEBUG("No custom Mnesia schema file found", []), []; {error, Reason} -> ?ERROR_MSG("Failed to read Mnesia schema file ~s: ~s", [File, fast_yaml:format_error(Reason)]), [] end. validate_schema_opts(File, Opts) -> try {ok, lists:map( fun({storage_type, Type}) when Type == ram_copies; Type == disc_copies; Type == disc_only_copies -> {Type, [node()]}; ({storage_type, _} = Opt) -> erlang:error({invalid_value, Opt}); ({local_content, Bool}) when is_boolean(Bool) -> {local_content, Bool}; ({local_content, _} = Opt) -> erlang:error({invalid_value, Opt}); ({type, Type}) when Type == set; Type == ordered_set; Type == bag -> {type, Type}; ({type, _} = Opt) -> erlang:error({invalid_value, Opt}); ({attributes, Attrs} = Opt) -> try lists:all(fun is_atom/1, Attrs) of true -> {attributes, Attrs}; false -> erlang:error({invalid_value, Opt}) catch _:_ -> erlang:error({invalid_value, Opt}) end; ({index, Indexes} = Opt) -> try lists:all(fun is_atom/1, Indexes) of true -> {index, Indexes}; false -> erlang:error({invalid_value, Opt}) catch _:_ -> erlang:error({invalid_value, Opt}) end; (Opt) -> erlang:error({unknown_option, Opt}) end, Opts)} catch _:{invalid_value, {Opt, Val}} -> ?ERROR_MSG("Mnesia schema ~s is incorrect: invalid value ~p of " "option '~s'", [File, Val, Opt]), error; _:{unknown_option, Opt} -> ?ERROR_MSG("Mnesia schema ~s is incorrect: unknown option ~p", [File, Opt]), error end. create(Name, TabDef) -> ?INFO_MSG("Creating Mnesia table '~s'", [Name]), case mnesia_op(create_table, [Name, TabDef]) of {atomic, ok} -> add_table_copy(Name); Err -> Err end. %% The table MUST exist, otherwise the function would fail add_table_copy(Name) -> Type = mnesia:table_info(Name, storage_type), Nodes = mnesia:table_info(Name, Type), case lists:member(node(), Nodes) of true -> {atomic, ok}; false -> mnesia_op(add_table_copy, [Name, node(), Type]) end. merge(Custom, Default) -> NewDefault = case lists:any(fun is_storage_type_option/1, Custom) of true -> lists:filter( fun(O) -> not is_storage_type_option(O) end, Default); false -> Default end, lists:ukeymerge(1, Custom, lists:ukeysort(1, NewDefault)). need_reset(Table, TabDef) -> ValuesF = [mnesia:table_info(Table, Key) || Key <- ?NEED_RESET], ValuesT = [proplists:get_value(Key, TabDef) || Key <- ?NEED_RESET], lists:foldl( fun({Val, Val}, Acc) -> Acc; ({_, undefined}, Acc) -> Acc; ({_, _}, _) -> true end, false, lists:zip(ValuesF, ValuesT)). transform(Module, Name) -> try mnesia:table_info(Name, attributes) of Attrs -> transform(Module, Name, Attrs, Attrs) catch _:{aborted, _} = Err -> Err end. transform(Module, Name, NewAttrs) -> try mnesia:table_info(Name, attributes) of OldAttrs -> transform(Module, Name, OldAttrs, NewAttrs) catch _:{aborted, _} = Err -> Err end. transform(Module, Name, Attrs, Attrs) -> case need_transform(Module, Name) of true -> ?INFO_MSG("Transforming table '~s', this may take a while", [Name]), transform_table(Module, Name); false -> {atomic, ok} end; transform(Module, Name, OldAttrs, NewAttrs) -> Fun = case erlang:function_exported(Module, transform, 1) of true -> transform_fun(Module, Name); false -> fun(Old) -> do_transform(OldAttrs, NewAttrs, Old) end end, mnesia_op(transform_table, [Name, Fun, NewAttrs]). -spec need_transform(module(), atom()) -> boolean(). need_transform(Module, Name) -> case erlang:function_exported(Module, need_transform, 1) of true -> do_need_transform(Module, Name, mnesia:dirty_first(Name)); false -> false end. do_need_transform(_Module, _Name, '$end_of_table') -> false; do_need_transform(Module, Name, Key) -> Objs = mnesia:dirty_read(Name, Key), case lists:foldl( fun(_, true) -> true; (Obj, _) -> Module:need_transform(Obj) end, undefined, Objs) of true -> true; false -> false; _ -> do_need_transform(Module, Name, mnesia:dirty_next(Name, Key)) end. do_transform(OldAttrs, Attrs, Old) -> [Name|OldValues] = tuple_to_list(Old), Before = lists:zip(OldAttrs, OldValues), After = lists:foldl( fun(Attr, Acc) -> case lists:keyfind(Attr, 1, Before) of false -> [{Attr, undefined}|Acc]; Value -> [Value|Acc] end end, [], lists:reverse(Attrs)), {Attrs, NewRecord} = lists:unzip(After), list_to_tuple([Name|NewRecord]). transform_fun(Module, Name) -> fun(Obj) -> try Module:transform(Obj) catch E:R -> StackTrace = erlang:get_stacktrace(), ?ERROR_MSG("Failed to transform Mnesia table ~s:~n" "** Record: ~p~n" "** Reason: ~p~n" "** StackTrace: ~p", [Name, Obj, R, StackTrace]), erlang:raise(E, R, StackTrace) end end. transform_table(Module, Name) -> Type = mnesia:table_info(Name, type), Attrs = mnesia:table_info(Name, attributes), TmpTab = list_to_atom(atom_to_list(Name) ++ "_backup"), StorageType = if Type == ordered_set -> disc_copies; true -> disc_only_copies end, mnesia:create_table(TmpTab, [{StorageType, [node()]}, {type, Type}, {local_content, true}, {record_name, Name}, {attributes, Attrs}]), mnesia:clear_table(TmpTab), Fun = transform_fun(Module, Name), Res = mnesia_op( transaction, [fun() -> do_transform_table(Name, Fun, TmpTab, mnesia:first(Name)) end]), mnesia:delete_table(TmpTab), Res. do_transform_table(Name, _Fun, TmpTab, '$end_of_table') -> mnesia:foldl( fun(Obj, _) -> mnesia:write(Name, Obj, write) end, ok, TmpTab); do_transform_table(Name, Fun, TmpTab, Key) -> Next = mnesia:next(Name, Key), Objs = mnesia:read(Name, Key), lists:foreach( fun(Obj) -> mnesia:write(TmpTab, Fun(Obj), write), mnesia:delete_object(Obj) end, Objs), do_transform_table(Name, Fun, TmpTab, Next). mnesia_op(Fun, Args) -> case apply(mnesia, Fun, Args) of {atomic, ok} -> {atomic, ok}; Other -> ?ERROR_MSG("failure on mnesia ~s ~p: ~p", [Fun, Args, Other]), Other end. schema_path() -> Dir = case os:getenv("EJABBERD_MNESIA_SCHEMA") of false -> mnesia:system_info(directory); Path -> Path end, filename:join(Dir, "ejabberd.schema"). is_storage_type_option({O, _}) -> O == ram_copies orelse O == disc_copies orelse O == disc_only_copies. dump_schema() -> File = schema_path(), Schema = lists:flatmap( fun(schema) -> []; (Tab) -> [{Tab, [{storage_type, mnesia:table_info(Tab, storage_type)}, {local_content, mnesia:table_info(Tab, local_content)}]}] end, mnesia:system_info(tables)), case file:write_file(File, [fast_yaml:encode(Schema), io_lib:nl()]) of ok -> io:format("Mnesia schema is written to ~s~n", [File]); {error, Reason} -> io:format("Failed to write Mnesia schema to ~s: ~s", [File, file:format_error(Reason)]) end. ejabberd-18.01/src/mod_irc.erl0000644000232200023220000010701113225664356016552 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_irc.erl %%% Author : Alexey Shchepin %%% Purpose : IRC transport %%% Created : 15 Feb 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_irc). -author('alexey@process-one.net'). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, export/1, import/1, import/3, closed_connection/3, get_connection_params/3, data_to_binary/2, process_disco_info/1, process_disco_items/1, process_register/1, process_vcard/1, process_command/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_irc.hrl"). -include("translate.hrl"). -define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>). -define(DEFAULT_IRC_PORT, 6667). -define(DEFAULT_REALNAME, <<"WebIRC-User">>). -define(DEFAULT_WEBIRC_PASSWORD, <<"">>). -define(POSSIBLE_ENCODINGS, [<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>, <<"utf-8">>, <<"utf-8+latin-1">>]). -record(state, {hosts = [] :: [binary()], server_host = <<"">> :: binary(), access = all :: atom()}). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), #irc_custom{}) -> ok | pass. -callback get_data(binary(), binary(), jid()) -> error | empty | irc_data(). -callback set_data(binary(), binary(), jid(), irc_data()) -> {atomic, any()}. %%==================================================================== %% gen_mod API %%==================================================================== start(Host, Opts) -> start_supervisor(Host), gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> stop_supervisor(Host), gen_mod:stop_child(?MODULE, Host). reload(Host, NewOpts, OldOpts) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). depends(_Host, _Opts) -> []. %%==================================================================== %% gen_server callbacks %%==================================================================== %%-------------------------------------------------------------------- %% Function: init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- init([Host, Opts]) -> process_flag(trap_exit, true), ejabberd:start_app(iconv), MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"irc.@HOST@">>), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), Access = gen_mod:get_opt(access, Opts, all), catch ets:new(irc_connection, [named_table, public, {keypos, #irc_connection.jid_server_host}]), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), lists:foreach( fun(MyHost) -> register_hooks(MyHost, IQDisc), ejabberd_router:register_route(MyHost, Host) end, MyHosts), {ok, #state{hosts = MyHosts, server_host = Host, access = Access}}. %%-------------------------------------------------------------------- %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(stop, _From, State) -> {stop, normal, ok, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) -> NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts, <<"irc.@HOST@">>), OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts, <<"irc.@HOST@">>), NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)), OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)), NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE), Access = gen_mod:get_opt(access, NewOpts, all), if NewMod /= OldMod -> NewMod:init(ServerHost, NewOpts); true -> ok end, if (NewIQDisc /= OldIQDisc) -> lists:foreach( fun(NewHost) -> register_hooks(NewHost, NewIQDisc) end, NewHosts -- (NewHosts -- OldHosts)); true -> ok end, lists:foreach( fun(NewHost) -> ejabberd_router:register_route(NewHost, ServerHost), register_hooks(NewHost, NewIQDisc) end, NewHosts -- OldHosts), lists:foreach( fun(OldHost) -> ejabberd_router:unregister_route(OldHost), unregister_hooks(OldHost) end, OldHosts -- NewHosts), Access = gen_mod:get_opt(access, NewOpts, all), {noreply, State#state{hosts = NewHosts, access = Access}}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({route, Packet}, #state{server_host = ServerHost, access = Access} = State) -> To = xmpp:get_to(Packet), Host = To#jid.lserver, case catch do_route(Host, ServerHost, Access, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); _ -> ok end, {noreply, State}; handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- terminate(_Reason, #state{hosts = MyHosts}) -> lists:foreach( fun(MyHost) -> ejabberd_router:unregister_route(MyHost), unregister_hooks(MyHost) end, MyHosts). %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- register_hooks(Host, IQDisc) -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, ?MODULE, process_disco_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_disco_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, ?MODULE, process_register, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS, ?MODULE, process_command, IQDisc). unregister_hooks(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS). start_supervisor(Host) -> Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), ChildSpec = {Proc, {ejabberd_tmp_sup, start_link, [Proc, mod_irc_connection]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, supervisor:start_child(ejabberd_gen_mod_sup, ChildSpec). stop_supervisor(Host) -> Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), supervisor:terminate_child(ejabberd_gen_mod_sup, Proc), supervisor:delete_child(ejabberd_gen_mod_sup, Proc). do_route(Host, ServerHost, Access, Packet) -> #jid{luser = LUser, lresource = LResource} = xmpp:get_to(Packet), From = xmpp:get_from(Packet), case acl:match_rule(ServerHost, Access, From) of allow -> case Packet of #iq{} when LUser == <<"">>, LResource == <<"">> -> ejabberd_router:process_iq(Packet); #iq{} when LUser == <<"">>, LResource /= <<"">> -> Err = xmpp:err_service_unavailable(), ejabberd_router:route_error(Packet, Err); _ -> sm_route(Host, ServerHost, Packet) end; deny -> Lang = xmpp:get_lang(Packet), Err = xmpp:err_forbidden(<<"Access denied by service policy">>, Lang), ejabberd_router:route_error(Packet, Err) end. process_disco_info(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_info(#iq{type = get, lang = Lang, to = To, sub_els = [#disco_info{node = Node}]} = IQ) -> ServerHost = ejabberd_router:host_of_route(To#jid.lserver), Info = ejabberd_hooks:run_fold(disco_info, ServerHost, [], [ServerHost, ?MODULE, <<"">>, <<"">>]), case iq_disco(ServerHost, Node, Lang) of undefined -> xmpp:make_iq_result(IQ, #disco_info{}); DiscoInfo -> xmpp:make_iq_result(IQ, DiscoInfo#disco_info{xdata = Info}) end. process_disco_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_items(#iq{type = get, lang = Lang, to = To, sub_els = [#disco_items{node = Node}]} = IQ) -> case Node of <<"">> -> xmpp:make_iq_result(IQ, #disco_items{}); <<"join">> -> xmpp:make_iq_result(IQ, #disco_items{}); <<"register">> -> xmpp:make_iq_result(IQ, #disco_items{}); ?NS_COMMANDS -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), xmpp:make_iq_result( IQ, #disco_items{node = Node, items = command_items(ServerHost, Host, Lang)}); _ -> Txt = <<"Node not found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) end. process_register(#iq{type = get, to = To, from = From, lang = Lang} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), case get_form(ServerHost, Host, From, Lang) of {result, Res} -> xmpp:make_iq_result(IQ, Res); {error, Error} -> xmpp:make_error(IQ, Error) end; process_register(#iq{type = set, lang = Lang, to = To, from = From, sub_els = [#register{xdata = #xdata{} = X}]} = IQ) -> case X#xdata.type of cancel -> xmpp:make_iq_result(IQ, #register{}); submit -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), case set_form(ServerHost, Host, From, Lang, X) of {result, Res} -> xmpp:make_iq_result(IQ, Res); {error, Error} -> xmpp:make_error(IQ, Error) end; _ -> Txt = <<"Incorrect value of 'type' attribute">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end; process_register(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"No data form found">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)). process_vcard(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_vcard(#iq{type = get, lang = Lang} = IQ) -> xmpp:make_iq_result(IQ, iq_get_vcard(Lang)). process_command(#iq{type = get, lang = Lang} = IQ) -> Txt = <<"Value 'get' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_command(#iq{type = set, lang = Lang, to = To, from = From, sub_els = [#adhoc_command{node = Node} = Request]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), case lists:keyfind(Node, 1, commands(ServerHost)) of {_, _, Function} -> try Function(From, To, Request) of ignore -> ignore; {error, Error} -> xmpp:make_error(IQ, Error); Command -> xmpp:make_iq_result(IQ, Command) catch E:R -> ?ERROR_MSG("ad-hoc handler failed: ~p", [{E, {R, erlang:get_stacktrace()}}]), xmpp:make_error(IQ, xmpp:err_internal_server_error()) end; _ -> Txt = <<"Node not found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) end. sm_route(Host, ServerHost, Packet) -> From = xmpp:get_from(Packet), #jid{user = ChanServ, resource = Resource} = xmpp:get_to(Packet), case str:tokens(ChanServ, <<"%">>) of [<<_, _/binary>> = Channel, <<_, _/binary>> = Server] -> case ets:lookup(irc_connection, {From, Server, Host}) of [] -> ?DEBUG("open new connection~n", []), {Username, Encoding, Port, Password} = get_connection_params(Host, ServerHost, From, Server), ConnectionUsername = case Packet of %% If the user tries to join a %% chatroom, the packet for sure %% contains the desired username. #presence{} -> Resource; %% Otherwise, there is no firm %% conclusion from the packet. %% Better to use the configured %% username (which defaults to the %% username part of the JID). _ -> Username end, Ident = extract_ident(Packet), RemoteAddr = extract_ip_address(Packet), RealName = get_realname(ServerHost), WebircPassword = get_webirc_password(ServerHost), {ok, Pid} = mod_irc_connection:start( From, Host, ServerHost, Server, ConnectionUsername, Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, ?MODULE), ets:insert(irc_connection, #irc_connection{ jid_server_host = {From, Server, Host}, pid = Pid}), mod_irc_connection:route_chan(Pid, Channel, Resource, Packet); [R] -> Pid = R#irc_connection.pid, ?DEBUG("send to process ~p~n", [Pid]), mod_irc_connection:route_chan(Pid, Channel, Resource, Packet) end; _ -> Lang = xmpp:get_lang(Packet), case str:tokens(ChanServ, <<"!">>) of [<<_, _/binary>> = Nick, <<_, _/binary>> = Server] -> case ets:lookup(irc_connection, {From, Server, Host}) of [] -> Txt = <<"IRC connection not found">>, Err = xmpp:err_service_unavailable(Txt, Lang), ejabberd_router:route_error(Packet, Err); [R] -> Pid = R#irc_connection.pid, ?DEBUG("send to process ~p~n", [Pid]), mod_irc_connection:route_nick(Pid, Nick, Packet) end; _ -> Txt = <<"Failed to parse chanserv">>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err) end end. closed_connection(Host, From, Server) -> ets:delete(irc_connection, {From, Server, Host}). iq_disco(ServerHost, <<"">>, Lang) -> Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("IRC Transport")), #disco_info{ identities = [#identity{category = <<"conference">>, type = <<"irc">>, name = translate:translate(Lang, Name)}], features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MUC, ?NS_REGISTER, ?NS_VCARD, ?NS_COMMANDS]}; iq_disco(ServerHost, Node, Lang) -> case lists:keyfind(Node, 1, commands(ServerHost)) of {_, Name, _} -> #disco_info{ identities = [#identity{category = <<"automation">>, type = <<"command-node">>, name = translate:translate(Lang, Name)}], features = [?NS_COMMANDS, ?NS_XDATA]}; _ -> undefined end. iq_get_vcard(Lang) -> Desc = translate:translate(Lang, <<"ejabberd IRC module">>), #vcard_temp{fn = <<"ejabberd/mod_irc">>, url = ?EJABBERD_URI, desc = <>}. command_items(ServerHost, Host, Lang) -> lists:map(fun({Node, Name, _Function}) -> #disco_item{jid = jid:make(Host), node = Node, name = translate:translate(Lang, Name)} end, commands(ServerHost)). commands(ServerHost) -> [{<<"join">>, <<"Join channel">>, fun adhoc_join/3}, {<<"register">>, <<"Configure username, encoding, port and " "password">>, fun (From, To, Request) -> adhoc_register(ServerHost, From, To, Request) end}]. get_data(ServerHost, Host, From) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:get_data(LServer, Host, From). get_form(ServerHost, Host, From, Lang) -> #jid{user = User, server = Server} = From, DefaultEncoding = get_default_encoding(Host), Customs = case get_data(ServerHost, Host, From) of error -> Txt1 = <<"Database failure">>, {error, xmpp:err_internal_server_error(Txt1, Lang)}; empty -> {User, []}; Data -> get_username_and_connection_params(Data) end, case Customs of {error, _Error} -> Customs; {Username, ConnectionsParams} -> Fs = [#xdata_field{type = 'text-single', label = translate:translate(Lang, <<"IRC Username">>), var = <<"username">>, values = [Username]}, #xdata_field{type = fixed, values = [str:format( translate:translate( Lang, <<"If you want to specify" " different ports, " "passwords, encodings " "for IRC servers, " "fill this list with " "values in format " "'{\"irc server\", " "\"encoding\", port, " "\"password\"}'. " "By default this " "service use \"~s\" " "encoding, port ~p, " "empty password.">>), [DefaultEncoding, ?DEFAULT_IRC_PORT])]}, #xdata_field{type = fixed, values = [translate:translate( Lang, <<"Example: [{\"irc.lucky.net\", \"koi8-r\", " "6667, \"secret\"}, {\"vendetta.fef.net\", " "\"iso8859-1\", 7000}, {\"irc.sometestserver.n" "et\", \"utf-8\"}].">>)]}, #xdata_field{type = 'text-multi', label = translate:translate( Lang, <<"Connections parameters">>), var = <<"connections_params">>, values = str:tokens(str:format( "~p.", [conn_params_to_list( ConnectionsParams)]), <<"\n">>)}], X = #xdata{type = form, title = <<(translate:translate( Lang, <<"Registration in mod_irc for ">>))/binary, User/binary, "@", Server/binary>>, instructions = [translate:translate( Lang, <<"Enter username, encodings, ports and " "passwords you wish to use for connecting " "to IRC servers">>)], fields = Fs}, {result, #register{instructions = translate:translate(Lang, <<"You need an x:data capable client to " "configure mod_irc settings">>), xdata = X}} end. set_data(ServerHost, Host, From, Data) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:set_data(LServer, Host, From, data_to_binary(From, Data)). set_form(ServerHost, Host, From, Lang, XData) -> case {xmpp_util:get_xdata_values(<<"username">>, XData), xmpp_util:get_xdata_values(<<"connections_params">>, XData)} of {[Username], [_|_] = Strings} -> EncString = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(EncString)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, ConnectionsParams} -> case set_data(ServerHost, Host, From, [{username, Username}, {connections_params, ConnectionsParams}]) of {atomic, _} -> {result, undefined}; _ -> Txt = <<"Database failure">>, {error, xmpp:err_internal_server_error(Txt, Lang)} end; _ -> Txt = <<"Parse error">>, {error, xmpp:err_not_acceptable(Txt, Lang)} end; _ -> {error, xmpp:err_not_acceptable(<<"Scan error">>, Lang)} end; _ -> Txt = <<"Incorrect value in data form">>, {error, xmpp:err_not_acceptable(Txt, Lang)} end. get_connection_params(Host, From, IRCServer) -> [_ | HostTail] = str:tokens(Host, <<".">>), ServerHost = str:join(HostTail, <<".">>), get_connection_params(Host, ServerHost, From, IRCServer). get_default_encoding(ServerHost) -> Result = gen_mod:get_module_opt(ServerHost, ?MODULE, default_encoding, ?DEFAULT_IRC_ENCODING), ?INFO_MSG("The default_encoding configured for " "host ~p is: ~p~n", [ServerHost, Result]), Result. get_realname(ServerHost) -> gen_mod:get_module_opt(ServerHost, ?MODULE, realname, ?DEFAULT_REALNAME). get_webirc_password(ServerHost) -> gen_mod:get_module_opt(ServerHost, ?MODULE, webirc_password, ?DEFAULT_WEBIRC_PASSWORD). get_connection_params(Host, ServerHost, From, IRCServer) -> #jid{user = User, server = _Server} = From, DefaultEncoding = get_default_encoding(ServerHost), case get_data(ServerHost, Host, From) of error -> {User, DefaultEncoding, ?DEFAULT_IRC_PORT, <<"">>}; empty -> {User, DefaultEncoding, ?DEFAULT_IRC_PORT, <<"">>}; Data -> {Username, ConnParams} = get_username_and_connection_params(Data), {NewUsername, NewEncoding, NewPort, NewPassword} = case lists:keysearch(IRCServer, 1, ConnParams) of {value, {_, Encoding, Port, Password}} -> {Username, Encoding, Port, Password}; {value, {_, Encoding, Port}} -> {Username, Encoding, Port, <<"">>}; {value, {_, Encoding}} -> {Username, Encoding, ?DEFAULT_IRC_PORT, <<"">>}; _ -> {Username, DefaultEncoding, ?DEFAULT_IRC_PORT, <<"">>} end, {iolist_to_binary(NewUsername), iolist_to_binary(NewEncoding), if NewPort >= 0 andalso NewPort =< 65535 -> NewPort; true -> ?DEFAULT_IRC_PORT end, iolist_to_binary(NewPassword)} end. adhoc_join(_From, _To, #adhoc_command{action = cancel} = Request) -> xmpp_util:make_adhoc_response(Request, #adhoc_command{status = canceled}); adhoc_join(_From, _To, #adhoc_command{lang = Lang, xdata = undefined} = Request) -> X = #xdata{type = form, title = translate:translate(Lang, <<"Join IRC channel">>), fields = [#xdata_field{var = <<"channel">>, type = 'text-single', label = translate:translate( Lang, <<"IRC channel (don't put the first #)">>), required = true}, #xdata_field{var = <<"server">>, type = 'text-single', label = translate:translate(Lang, <<"IRC server">>), required = true}]}, xmpp_util:make_adhoc_response( Request, #adhoc_command{status = executing, xdata = X}); adhoc_join(From, To, #adhoc_command{lang = Lang, xdata = X} = Request) -> Channel = case xmpp_util:get_xdata_values(<<"channel">>, X) of [C] -> C; _ -> false end, Server = case xmpp_util:get_xdata_values(<<"server">>, X) of [S] -> S; _ -> false end, if Channel /= false, Server /= false -> RoomJID = jid:make(<>, To#jid.server), Reason = translate:translate(Lang, <<"Join the IRC channel here.">>), BodyTxt = {<<"Join the IRC channel in this Jabber ID: ~s">>, [jid:encode(RoomJID)]}, Invite = #message{ from = RoomJID, to = From, body = xmpp:mk_text(BodyTxt, Lang), sub_els = [#muc_user{ invites = [#muc_invite{from = From, reason = Reason}]}, #x_conference{reason = Reason, jid = RoomJID}]}, ejabberd_router:route(Invite), xmpp_util:make_adhoc_response( Request, #adhoc_command{status = completed}); true -> Txt = <<"Missing 'channel' or 'server' in the data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end. -spec adhoc_register(binary(), jid(), jid(), adhoc_command()) -> adhoc_command() | {error, stanza_error()}. adhoc_register(_ServerHost, _From, _To, #adhoc_command{action = cancel} = Request) -> xmpp_util:make_adhoc_response(Request, #adhoc_command{status = canceled}); adhoc_register(ServerHost, From, To, #adhoc_command{lang = Lang, xdata = X, action = Action} = Request) -> #jid{user = User} = From, #jid{lserver = Host} = To, {Username, ConnectionsParams} = if X == undefined -> case get_data(ServerHost, Host, From) of error -> {User, []}; empty -> {User, []}; Data -> get_username_and_connection_params(Data) end; true -> {case xmpp_util:get_xdata_values(<<"username">>, X) of [U] -> U; _ -> User end, parse_connections_params(X)} end, if Action == complete -> case set_data(ServerHost, Host, From, [{username, Username}, {connections_params, ConnectionsParams}]) of {atomic, _} -> xmpp_util:make_adhoc_response( Request, #adhoc_command{status = completed}); _ -> Txt = <<"Database failure">>, {error, xmpp:err_internal_server_error(Txt, Lang)} end; true -> Form = generate_adhoc_register_form(Lang, Username, ConnectionsParams), xmpp_util:make_adhoc_response( Request, #adhoc_command{ status = executing, xdata = Form, actions = #adhoc_actions{next = true, complete = true}}) end. generate_adhoc_register_form(Lang, Username, ConnectionsParams) -> #xdata{type = form, title = translate:translate(Lang, <<"IRC settings">>), instructions = [translate:translate( Lang, <<"Enter username and encodings you wish " "to use for connecting to IRC servers. " " Press 'Next' to get more fields to " "fill in. Press 'Complete' to save settings.">>)], fields = [#xdata_field{ var = <<"username">>, type = 'text-single', label = translate:translate(Lang, <<"IRC username">>), required = true, values = [Username]} | generate_connection_params_fields( Lang, ConnectionsParams, 1, [])]}. generate_connection_params_fields(Lang, [], Number, Acc) -> Field = generate_connection_params_field(Lang, <<"">>, <<"">>, -1, <<"">>, Number), lists:reverse(Field ++ Acc); generate_connection_params_fields(Lang, [ConnectionParams | ConnectionsParams], Number, Acc) -> case ConnectionParams of {Server, Encoding, Port, Password} -> Field = generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number), generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc); {Server, Encoding, Port} -> Field = generate_connection_params_field(Lang, Server, Encoding, Port, <<"">>, Number), generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc); {Server, Encoding} -> Field = generate_connection_params_field(Lang, Server, Encoding, -1, <<"">>, Number), generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc); _ -> [] end. generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number) -> EncodingUsed = case Encoding of <<>> -> get_default_encoding(Server); _ -> Encoding end, PortUsedInt = if Port >= 0 andalso Port =< 65535 -> Port; true -> ?DEFAULT_IRC_PORT end, PortUsed = integer_to_binary(PortUsedInt), PasswordUsed = case Password of <<>> -> <<>>; _ -> Password end, NumberString = integer_to_binary(Number), [#xdata_field{var = <<"password", NumberString/binary>>, type = 'text-single', label = str:format( translate:translate(Lang, <<"Password ~b">>), [Number]), values = [PasswordUsed]}, #xdata_field{var = <<"port", NumberString/binary>>, type = 'text-single', label = str:format( translate:translate(Lang, <<"Port ~b">>), [Number]), values = [PortUsed]}, #xdata_field{var = <<"encoding", NumberString/binary>>, type = 'list-single', label = str:format( translate:translate(Lang, <<"Encoding for server ~b">>), [Number]), values = [EncodingUsed], options = [#xdata_option{label = E, value = E} || E <- ?POSSIBLE_ENCODINGS]}, #xdata_field{var = <<"server", NumberString/binary>>, type = 'text-single', label = str:format( translate:translate(Lang, <<"Server ~b">>), [Number]), values = [Server]}]. parse_connections_params(#xdata{fields = Fields}) -> Servers = lists:flatmap( fun(#xdata_field{var = <<"server", Var/binary>>, values = Values}) -> [{Var, Values}]; (_) -> [] end, Fields), Encodings = lists:flatmap( fun(#xdata_field{var = <<"encoding", Var/binary>>, values = Values}) -> [{Var, Values}]; (_) -> [] end, Fields), Ports = lists:flatmap( fun(#xdata_field{var = <<"port", Var/binary>>, values = Values}) -> [{Var, Values}]; (_) -> [] end, Fields), Passwords = lists:flatmap( fun(#xdata_field{var = <<"password", Var/binary>>, values = Values}) -> [{Var, Values}]; (_) -> [] end, Fields), parse_connections_params(Servers, Encodings, Ports, Passwords). retrieve_connections_params(ConnectionParams, ServerN) -> case ConnectionParams of [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail] -> if ServerN == ConnectionParamN -> {ConnectionParam, ConnectionParamsTail}; ServerN < ConnectionParamN -> {[], [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail]}; ServerN > ConnectionParamN -> {[], ConnectionParamsTail} end; _ -> {[], []} end. parse_connections_params([], _, _, _) -> []; parse_connections_params(_, [], [], []) -> []; parse_connections_params([{ServerN, Server} | Servers], Encodings, Ports, Passwords) -> {NewEncoding, NewEncodings} = retrieve_connections_params(Encodings, ServerN), {NewPort, NewPorts} = retrieve_connections_params(Ports, ServerN), {NewPassword, NewPasswords} = retrieve_connections_params(Passwords, ServerN), [{Server, NewEncoding, NewPort, NewPassword} | parse_connections_params(Servers, NewEncodings, NewPorts, NewPasswords)]. get_username_and_connection_params(Data) -> Username = case lists:keysearch(username, 1, Data) of {value, {_, U}} when is_binary(U) -> U; _ -> <<"">> end, ConnParams = case lists:keysearch(connections_params, 1, Data) of {value, {_, L}} when is_list(L) -> L; _ -> [] end, {Username, ConnParams}. data_to_binary(JID, Data) -> lists:map( fun({username, U}) -> {username, iolist_to_binary(U)}; ({connections_params, Params}) -> {connections_params, lists:flatmap( fun(Param) -> try [conn_param_to_binary(Param)] catch _:_ -> if JID /= error -> ?ERROR_MSG("failed to convert " "parameter ~p for user ~s", [Param, jid:encode(JID)]); true -> ?ERROR_MSG("failed to convert " "parameter ~p", [Param]) end, [] end end, Params)}; (Opt) -> Opt end, Data). conn_param_to_binary({S}) -> {iolist_to_binary(S)}; conn_param_to_binary({S, E}) -> {iolist_to_binary(S), iolist_to_binary(E)}; conn_param_to_binary({S, E, Port}) when is_integer(Port) -> {iolist_to_binary(S), iolist_to_binary(E), Port}; conn_param_to_binary({S, E, Port, P}) when is_integer(Port) -> {iolist_to_binary(S), iolist_to_binary(E), Port, iolist_to_binary(P)}. conn_params_to_list(Params) -> lists:map( fun({S}) -> {binary_to_list(S)}; ({S, E}) -> {binary_to_list(S), binary_to_list(E)}; ({S, E, Port}) -> {binary_to_list(S), binary_to_list(E), Port}; ({S, E, Port, P}) -> {binary_to_list(S), binary_to_list(E), Port, binary_to_list(P)} end, Params). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:import(LServer). import(LServer, DBType, Data) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, Data). mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(default_encoding) -> fun iolist_to_binary/1; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun (L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(_) -> [access, db_type, default_encoding, host, hosts, name]. -spec extract_ident(stanza()) -> binary(). extract_ident(Packet) -> Hdrs = extract_headers(Packet), proplists:get_value(<<"X-Irc-Ident">>, Hdrs, <<"chatmovil">>). -spec extract_ip_address(stanza()) -> binary(). extract_ip_address(Packet) -> Hdrs = extract_headers(Packet), proplists:get_value(<<"X-Forwarded-For">>, Hdrs, <<"127.0.0.1">>). -spec extract_headers(stanza()) -> [{binary(), binary()}]. extract_headers(Packet) -> case xmpp:get_subtag(Packet, #shim{}) of #shim{headers = Hs} -> Hs; false -> [] end. ejabberd-18.01/src/ejabberd_listener.erl0000644000232200023220000005251513225664356020611 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_listener.erl %%% Author : Alexey Shchepin %%% Purpose : Manage socket listener %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_listener). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([start_link/0, init/1, start/3, init/3, start_listeners/0, start_listener/3, stop_listeners/0, stop_listener/2, parse_listener_portip/2, add_listener/3, delete_listener/2, transform_options/1, validate_cfg/1, opt_type/1, config_reloaded/0]). -include("ejabberd.hrl"). -include("logger.hrl"). %% We do not block on send anymore. -define(TCP_SEND_TIMEOUT, 15000). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init(_) -> ets:new(?MODULE, [named_table, public]), ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), {ok, {{one_for_one, 10, 1}, listeners_childspec()}}. listeners_childspec() -> Ls = ejabberd_config:get_option(listen, []), Specs = lists:map( fun({Port, Module, Opts}) -> ets:insert(?MODULE, {Port, Module, Opts}), {Port, {?MODULE, start, [Port, Module, Opts]}, transient, brutal_kill, worker, [?MODULE]} end, Ls), report_duplicated_portips(Ls), Specs. start_listeners() -> lists:foreach( fun(Spec) -> supervisor:start_child(?MODULE, Spec) end, listeners_childspec()). report_duplicated_portips(L) -> LKeys = [Port || {Port, _, _} <- L], LNoDupsKeys = proplists:get_keys(L), case LKeys -- LNoDupsKeys of [] -> ok; Dups -> ?CRITICAL_MSG("In the ejabberd configuration there are duplicated " "Port number + IP address:~n ~p", [Dups]) end. start(Port, Module, Opts) -> NewOpts = validate_module_options(Module, Opts), %% Check if the module is an ejabberd listener or an independent listener case Module:socket_type() of independent -> Module:start_listener(Port, NewOpts); _ -> start_dependent(Port, Module, NewOpts) end. %% @spec(Port, Module, Opts) -> {ok, Pid} | {error, ErrorMessage} start_dependent(Port, Module, Opts) -> proc_lib:start_link(?MODULE, init, [Port, Module, Opts]). init(PortIP, Module, RawOpts) -> {Port, IPT, IPS, IPV, Proto, OptsClean} = parse_listener_portip(PortIP, RawOpts), {Opts, SockOpts} = prepare_opts(IPT, IPV, OptsClean), if Proto == udp -> init_udp(PortIP, Module, Opts, SockOpts, Port, IPS); true -> init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) end. init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) -> case gen_udp:open(Port, [binary, {active, false}, {reuseaddr, true} | SockOpts]) of {ok, Socket} -> %% Inform my parent that this port was opened succesfully proc_lib:init_ack({ok, self()}), application:ensure_started(ejabberd), start_module_sup(Port, Module), ?INFO_MSG("Start accepting UDP connections at ~s for ~p", [format_portip(PortIP), Module]), case erlang:function_exported(Module, udp_init, 2) of false -> udp_recv(Socket, Module, Opts); true -> case catch Module:udp_init(Socket, Opts) of {'EXIT', _} = Err -> ?ERROR_MSG("failed to process callback function " "~p:~s(~p, ~p): ~p", [Module, udp_init, Socket, Opts, Err]), udp_recv(Socket, Module, Opts); NewOpts -> udp_recv(Socket, Module, NewOpts) end end; {error, Reason} -> socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) end. init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) -> ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS), %% Inform my parent that this port was opened succesfully proc_lib:init_ack({ok, self()}), application:ensure_started(ejabberd), start_module_sup(Port, Module), ?INFO_MSG("Start accepting TCP connections at ~s for ~p", [format_portip(PortIP), Module]), case erlang:function_exported(Module, tcp_init, 2) of false -> accept(ListenSocket, Module, Opts); true -> case catch Module:tcp_init(ListenSocket, Opts) of {'EXIT', _} = Err -> ?ERROR_MSG("failed to process callback function " "~p:~s(~p, ~p): ~p", [Module, tcp_init, ListenSocket, Opts, Err]), accept(ListenSocket, Module, Opts); NewOpts -> accept(ListenSocket, Module, NewOpts) end end. listen_tcp(PortIP, Module, SockOpts, Port, IPS) -> Res = gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}, {reuseaddr, true}, {nodelay, true}, {send_timeout, ?TCP_SEND_TIMEOUT}, {send_timeout_close, true}, {keepalive, true} | SockOpts]), case Res of {ok, ListenSocket} -> ListenSocket; {error, Reason} -> socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) end. %% @spec (PortIP, Opts) -> {Port, IPT, IPS, IPV, OptsClean} %% where %% PortIP = Port | {Port, IPT | IPS} %% Port = integer() %% IPT = tuple() %% IPS = string() %% IPV = inet | inet6 %% Opts = [IPV | {ip, IPT} | atom() | tuple()] %% OptsClean = [atom() | tuple()] %% @doc Parse any kind of ejabberd listener specification. %% The parsed options are returned in several formats. %% OptsClean does not include inet/inet6 or ip options. %% Opts can include the options inet6 and {ip, Tuple}, %% but they are only used when no IP address was specified in the PortIP. %% The IP version (either IPv4 or IPv6) is inferred from the IP address type, %% so the option inet/inet6 is only used when no IP is specified at all. parse_listener_portip(PortIP, Opts) -> {IPOpt, Opts2} = strip_ip_option(Opts), {IPVOpt, OptsClean} = case proplists:get_bool(inet6, Opts2) of true -> {inet6, proplists:delete(inet6, Opts2)}; false -> {inet, Opts2} end, {Port, IPT, IPS, Proto} = case add_proto(PortIP, Opts) of {P, Prot} -> T = get_ip_tuple(IPOpt, IPVOpt), S = misc:ip_to_list(T), {P, T, S, Prot}; {P, T, Prot} when is_integer(P) and is_tuple(T) -> S = misc:ip_to_list(T), {P, T, S, Prot}; {P, S, Prot} when is_integer(P) and is_binary(S) -> [S | _] = str:tokens(S, <<"/">>), {ok, T} = inet_parse:address(binary_to_list(S)), {P, T, S, Prot} end, IPV = case tuple_size(IPT) of 4 -> inet; 8 -> inet6 end, {Port, IPT, IPS, IPV, Proto, OptsClean}. prepare_opts(IPT, IPV, OptsClean) -> %% The first inet|inet6 and the last {ip, _} work, %% so overriding those in Opts Opts = [IPV | OptsClean] ++ [{ip, IPT}], SockOpts = lists:filter(fun({ip, _}) -> true; (inet6) -> true; (inet) -> true; ({backlog, _}) -> true; (_) -> false end, Opts), {Opts, SockOpts}. add_proto(Port, Opts) when is_integer(Port) -> {Port, get_proto(Opts)}; add_proto({Port, Proto}, _Opts) when is_atom(Proto) -> {Port, normalize_proto(Proto)}; add_proto({Port, Addr}, Opts) -> {Port, Addr, get_proto(Opts)}; add_proto({Port, Addr, Proto}, _Opts) -> {Port, Addr, normalize_proto(Proto)}. strip_ip_option(Opts) -> {IPL, OptsNoIP} = lists:partition( fun({ip, _}) -> true; (_) -> false end, Opts), case IPL of %% Only the first ip option is considered [{ip, T1} | _] -> {T1, OptsNoIP}; [] -> {no_ip_option, OptsNoIP} end. get_ip_tuple(no_ip_option, inet) -> {0, 0, 0, 0}; get_ip_tuple(no_ip_option, inet6) -> {0, 0, 0, 0, 0, 0, 0, 0}; get_ip_tuple(IPOpt, _IPVOpt) -> IPOpt. accept(ListenSocket, Module, Opts) -> IntervalOpt = case proplists:get_value(accept_interval, Opts) of [{linear, [I1_, T1_, T2_, I2_]}] -> {linear, I1_, T1_, T2_, I2_}; I_ -> I_ end, Interval = case IntervalOpt of undefined -> 0; I when is_integer(I), I >= 0 -> I; {linear, I1, T1, T2, I2} when is_integer(I1), is_integer(T1), is_integer(T2), is_integer(I2), I1 >= 0, I2 >= 0, T2 > 0 -> {MSec, Sec, _USec} = os:timestamp(), TS = MSec * 1000000 + Sec, {linear, I1, TS + T1, T2, I2}; I -> ?WARNING_MSG("There is a problem in the configuration: " "~p is a wrong accept_interval value. " "Using 0 as fallback", [I]), 0 end, accept(ListenSocket, Module, Opts, Interval). accept(ListenSocket, Module, Opts, Interval) -> NewInterval = check_rate_limit(Interval), case gen_tcp:accept(ListenSocket) of {ok, Socket} -> case {inet:sockname(Socket), inet:peername(Socket)} of {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} -> Receiver = case xmpp_socket:start(Module, gen_tcp, Socket, Opts) of {ok, RecvPid} -> RecvPid; _ -> none end, ?INFO_MSG("(~p) Accepted connection ~s:~p -> ~s:~p", [Receiver, ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)), PPort, inet_parse:ntoa(Addr), Port]); _ -> gen_tcp:close(Socket) end, accept(ListenSocket, Module, Opts, NewInterval); {error, Reason} -> ?ERROR_MSG("(~w) Failed TCP accept: ~s", [ListenSocket, inet:format_error(Reason)]), accept(ListenSocket, Module, Opts, NewInterval) end. udp_recv(Socket, Module, Opts) -> case gen_udp:recv(Socket, 0) of {ok, {Addr, Port, Packet}} -> case catch Module:udp_recv(Socket, Addr, Port, Packet, Opts) of {'EXIT', Reason} -> ?ERROR_MSG("failed to process UDP packet:~n" "** Source: {~p, ~p}~n" "** Reason: ~p~n** Packet: ~p", [Addr, Port, Reason, Packet]), udp_recv(Socket, Module, Opts); NewOpts -> udp_recv(Socket, Module, NewOpts) end; {error, Reason} -> ?ERROR_MSG("unexpected UDP error: ~s", [format_error(Reason)]), throw({error, Reason}) end. %% @spec (Port, Module, Opts) -> {ok, Pid} | {error, Error} start_listener(Port, Module, Opts) -> case start_listener2(Port, Module, Opts) of {ok, _Pid} = R -> R; {error, {{'EXIT', {undef, [{M, _F, _A}|_]}}, _} = Error} -> ?ERROR_MSG("Error starting the ejabberd listener: ~p.~n" "It could not be loaded or is not an ejabberd listener.~n" "Error: ~p~n", [Module, Error]), {error, {module_not_available, M}}; {error, {already_started, Pid}} -> {ok, Pid}; {error, Error} -> {error, Error} end. %% @spec (Port, Module, Opts) -> {ok, Pid} | {error, Error} start_listener2(Port, Module, Opts) -> %% It is only required to start the supervisor in some cases. %% But it doesn't hurt to attempt to start it for any listener. %% So, it's normal (and harmless) that in most cases this call returns: {error, {already_started, pid()}} start_listener_sup(Port, Module, Opts). start_module_sup(_Port, Module) -> Proc1 = gen_mod:get_module_proc(<<"sup">>, Module), ChildSpec1 = {Proc1, {ejabberd_tmp_sup, start_link, [Proc1, Module]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, supervisor:start_child(ejabberd_sup, ChildSpec1). start_listener_sup(Port, Module, Opts) -> ChildSpec = {Port, {?MODULE, start, [Port, Module, Opts]}, transient, brutal_kill, worker, [?MODULE]}, supervisor:start_child(?MODULE, ChildSpec). stop_listeners() -> Ports = ejabberd_config:get_option(listen, []), lists:foreach( fun({PortIpNetp, Module, _Opts}) -> delete_listener(PortIpNetp, Module) end, Ports). stop_listener({_, _, Transport} = PortIP, Module) -> case supervisor:terminate_child(?MODULE, PortIP) of ok -> ?INFO_MSG("Stop accepting ~s connections at ~s for ~p", [case Transport of udp -> "UDP"; tcp -> "TCP" end, format_portip(PortIP), Module]), ets:delete(?MODULE, PortIP), supervisor:delete_child(?MODULE, PortIP); Err -> Err end. add_listener(PortIP, Module, Opts) -> {Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts), PortIP1 = {Port, IPT, Proto}, case start_listener(PortIP1, Module, Opts) of {ok, _Pid} -> ok; {error, {already_started, _Pid}} -> {error, {already_started, PortIP}}; {error, Error} -> {error, Error} end. delete_listener(PortIP, Module) -> delete_listener(PortIP, Module, []). %% @spec (PortIP, Module, Opts) -> ok %% where %% PortIP = {Port, IPT | IPS} %% Port = integer() %% IPT = tuple() %% IPS = string() %% Module = atom() %% Opts = [term()] delete_listener(PortIP, Module, Opts) -> {Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts), PortIP1 = {Port, IPT, Proto}, stop_listener(PortIP1, Module). config_reloaded() -> New = ejabberd_config:get_option(listen, []), Old = ets:tab2list(?MODULE), lists:foreach( fun({PortIP, Module, _Opts}) -> case lists:keyfind(PortIP, 1, New) of false -> stop_listener(PortIP, Module); _ -> ok end end, Old), lists:foreach( fun({PortIP, Module, Opts}) -> case lists:keyfind(PortIP, 1, Old) of {_, Module, Opts} -> ok; {_, OldModule, _} -> stop_listener(PortIP, OldModule), ets:insert(?MODULE, {PortIP, Module, Opts}), start_listener(PortIP, Module, Opts); false -> ets:insert(?MODULE, {PortIP, Module, Opts}), start_listener(PortIP, Module, Opts) end end, New). %%% %%% Check options %%% get_proto(Opts) -> case proplists:get_value(proto, Opts) of undefined -> tcp; Proto -> normalize_proto(Proto) end. normalize_proto(tcp) -> tcp; normalize_proto(udp) -> udp; normalize_proto(UnknownProto) -> ?WARNING_MSG("There is a problem in the configuration: " "~p is an unknown IP protocol. Using tcp as fallback", [UnknownProto]), tcp. socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) -> ReasonT = case Reason of eaddrnotavail -> "IP address not available: " ++ binary_to_list(IPS); eaddrinuse -> "IP address and port number already used: " ++binary_to_list(IPS)++" "++integer_to_list(Port); _ -> format_error(Reason) end, ?ERROR_MSG("Failed to open socket:~n ~p~nReason: ~s", [{Port, Module, SockOpts}, ReasonT]), throw({Reason, PortIP}). format_error(Reason) -> case inet:format_error(Reason) of "unknown POSIX error" -> atom_to_list(Reason); ReasonStr -> ReasonStr end. format_portip({Port, IP, _Transport}) -> IPStr = case tuple_size(IP) of 4 -> inet:ntoa(IP); 8 -> "[" ++ inet:ntoa(IP) ++ "]" end, IPStr ++ ":" ++ integer_to_list(Port). check_rate_limit(Interval) -> NewInterval = receive {rate_limit, AcceptInterval} -> AcceptInterval after 0 -> Interval end, case NewInterval of 0 -> ok; Ms when is_integer(Ms) -> timer:sleep(Ms); {linear, I1, T1, T2, I2} -> {MSec, Sec, _USec} = os:timestamp(), TS = MSec * 1000000 + Sec, I = if TS =< T1 -> I1; TS >= T1 + T2 -> I2; true -> round((I2 - I1) * (TS - T1) / T2 + I1) end, timer:sleep(I) end, NewInterval. -define(IS_CHAR(C), (is_integer(C) and (C >= 0) and (C =< 255))). -define(IS_UINT(U), (is_integer(U) and (U >= 0) and (U =< 65535))). -define(IS_PORT(P), (is_integer(P) and (P > 0) and (P =< 65535))). -define(IS_TRANSPORT(T), ((T == tcp) or (T == udp))). transform_option({{Port, IP, Transport}, Mod, Opts}) -> IPStr = if is_tuple(IP) -> list_to_binary(inet_parse:ntoa(IP)); true -> IP end, Opts1 = lists:map( fun({ip, IPT}) when is_tuple(IPT) -> {ip, list_to_binary(inet_parse:ntoa(IP))}; (ssl) -> {tls, true}; (A) when is_atom(A) -> {A, true}; (Opt) -> Opt end, Opts), Opts2 = lists:foldl( fun(Opt, Acc) -> try Mod:transform_listen_option(Opt, Acc) catch error:undef -> [Opt|Acc] end end, [], Opts1), TransportOpt = if Transport == tcp -> []; true -> [{transport, Transport}] end, IPOpt = if IPStr == <<"0.0.0.0">> -> []; true -> [{ip, IPStr}] end, IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2]; transform_option({{Port, Transport}, Mod, Opts}) when ?IS_TRANSPORT(Transport) -> transform_option({{Port, all_zero_ip(Opts), Transport}, Mod, Opts}); transform_option({{Port, IP}, Mod, Opts}) -> transform_option({{Port, IP, tcp}, Mod, Opts}); transform_option({Port, Mod, Opts}) -> transform_option({{Port, all_zero_ip(Opts), tcp}, Mod, Opts}); transform_option(Opt) -> Opt. transform_options(Opts) -> lists:foldl(fun transform_options/2, [], Opts). transform_options({listen, LOpts}, Opts) -> [{listen, lists:map(fun transform_option/1, LOpts)} | Opts]; transform_options(Opt, Opts) -> [Opt|Opts]. -spec validate_module_options(module(), [{atom(), any()}]) -> [{atom(), any()}]. validate_module_options(Module, Opts) -> try Module:listen_opt_type('') of _ -> lists:filtermap( fun({Opt, Val}) -> case validate_module_option(Module, Opt, Val) of {ok, NewVal} -> {true, {Opt, NewVal}}; error -> false end end, Opts) catch _:undef -> ?WARNING_MSG("module '~s' doesn't export listen_opt_type/1", [Module]), Opts end. -spec validate_module_option(module(), atom(), any()) -> {ok, any()} | error. validate_module_option(Module, Opt, Val) -> case Module:listen_opt_type(Opt) of VFun when is_function(VFun) -> try VFun(Val) of NewVal -> {ok, NewVal} catch {invalid_syntax, Error} -> ?ERROR_MSG("ignoring listen option '~s' with " "invalid value: ~p: ~s", [Opt, Val, Error]), error; _:_ -> ?ERROR_MSG("ignoring listen option '~s' with " "invalid value: ~p", [Opt, Val]), error end; [] -> ?ERROR_MSG("unknown listen option '~s' for '~s' will be likely " "ignored because the listening module doesn't have " "any options", [Opt, Module]), {ok, Val}; KnownOpts when is_list(KnownOpts) -> ?ERROR_MSG("unknown listen option '~s' for '~s' will be likely " "ignored, available options are: ~s", [Opt, Module, misc:join_atoms(KnownOpts, <<", ">>)]), {ok, Val} end. -type transport() :: udp | tcp. -type port_ip_transport() :: inet:port_number() | {inet:port_number(), transport()} | {inet:port_number(), inet:ip_address()} | {inet:port_number(), inet:ip_address(), transport()}. -spec validate_cfg(list()) -> [{port_ip_transport(), module(), list()}]. validate_cfg(L) -> lists:map( fun(LOpts) -> lists:foldl( fun({port, Port}, {{_, IP, T}, Mod, Opts}) -> true = ?IS_PORT(Port), {{Port, IP, T}, Mod, Opts}; ({ip, IP}, {{Port, _, T}, Mod, Opts}) -> {{Port, prepare_ip(IP), T}, Mod, Opts}; ({transport, T}, {{Port, IP, _}, Mod, Opts}) -> true = ?IS_TRANSPORT(T), {{Port, IP, T}, Mod, Opts}; ({module, Mod}, {Port, _, Opts}) -> {Port, Mod, Opts}; (Opt, {Port, Mod, Opts}) -> {Port, Mod, [Opt|Opts]} end, {{5222, all_zero_ip(LOpts), tcp}, ejabberd_c2s, []}, LOpts) end, L). prepare_ip({A, B, C, D} = IP) when ?IS_CHAR(A) and ?IS_CHAR(B) and ?IS_CHAR(C) and ?IS_CHAR(D) -> IP; prepare_ip({A, B, C, D, E, F, G, H} = IP) when ?IS_UINT(A) and ?IS_UINT(B) and ?IS_UINT(C) and ?IS_UINT(D) and ?IS_UINT(E) and ?IS_UINT(F) and ?IS_UINT(G) and ?IS_UINT(H) -> IP; prepare_ip(IP) when is_list(IP) -> {ok, Addr} = inet_parse:address(IP), Addr; prepare_ip(IP) when is_binary(IP) -> prepare_ip(binary_to_list(IP)). all_zero_ip(Opts) -> case proplists:get_bool(inet6, Opts) of true -> {0,0,0,0,0,0,0,0}; false -> {0,0,0,0} end. opt_type(listen) -> fun validate_cfg/1; opt_type(_) -> [listen]. ejabberd-18.01/src/eldap_filter.erl0000644000232200023220000001612513225664356017575 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File: eldap_filter.erl %%% Purpose: Converts String Representation of %%% LDAP Search Filter (RFC 2254) %%% to eldap's representation of filter %%% Author: Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(eldap_filter). %% TODO: remove this when new regexp module will be used -export([parse/1, parse/2, do_sub/2]). %%==================================================================== %% API %%==================================================================== %%%------------------------------------------------------------------- %%% Arity: parse/1 %%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} | %%% {error, bad_filter} %%% %%% RFC2254_Filter = string(). %%% %%% Description: Converts String Representation of LDAP Search Filter (RFC 2254) %%% to eldap's representation of filter. %%% %%% Example: %%% > eldap_filter:parse("(&(!(uid<=100))(mail=*))"). %%% %%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}}, %%% {present,"mail"}]}} %%%------------------------------------------------------------------- -spec parse(binary()) -> {error, any()} | {ok, eldap:filter()}. parse(L) -> parse(L, []). %%%------------------------------------------------------------------- %%% Arity: parse/2 %%% Function: parse(RFC2254_Filter, [SubstValue |...]) -> %%% {ok, EldapFilter} | %%% {error, bad_filter} | %%% {error, bad_regexp} | %%% {error, max_substitute_recursion} %%% %%% SubstValue = {RegExp, Value} | {RegExp, Value, N}, %%% RFC2254_Filter = RegExp = Value = string(), %%% N = integer(). %%% %%% Description: The same as parse/1, but substitutes N or all occurences %%% of RegExp with Value *after* parsing. %%% %%% Example: %%% > eldap_filter:parse( %%% "(|(mail=%u@%d)(jid=%u@%d))", %%% [{"%u", "xramtsov"},{"%d","gmail.com"}]). %%% %%% {ok,{'or',[{equalityMatch,{'AttributeValueAssertion', %%% "mail", %%% "xramtsov@gmail.com"}}, %%% {equalityMatch,{'AttributeValueAssertion', %%% "jid", %%% "xramtsov@gmail.com"}}]}} %%%------------------------------------------------------------------- -spec parse(binary(), [{binary(), binary()} | {binary(), binary(), pos_integer()}]) -> {error, any()} | {ok, eldap:filter()}. parse(L, SList) -> case catch eldap_filter_yecc:parse(scan(binary_to_list(L), SList)) of {'EXIT', _} = Err -> {error, Err}; {error, {_, _, Msg}} -> {error, Msg}; {ok, Result} -> {ok, Result}; {regexp, Err} -> {error, Err} end. %%==================================================================== %% Internal functions %%==================================================================== -define(do_scan(L), scan(Rest, <<>>, [{L, 1} | check(Buf, S) ++ Result], L, S)). scan(L, SList) -> scan(L, <<"">>, [], undefined, SList). scan("=*)" ++ Rest, Buf, Result, '(', S) -> scan(Rest, <<>>, [{')', 1}, {'=*', 1} | check(Buf, S) ++ Result], ')', S); scan(":dn" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':dn'); scan(":=" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':='); scan(":=" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':='); scan(":=" ++ Rest, Buf, Result, ':', S) -> ?do_scan(':='); scan("~=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('~='); scan(">=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('>='); scan("<=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('<='); scan("=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('='); scan(":" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':'); scan(":" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':'); scan("&" ++ Rest, Buf, Result, '(', S) when Buf==<<"">> -> ?do_scan('&'); scan("|" ++ Rest, Buf, Result, '(', S) when Buf==<<"">> -> ?do_scan('|'); scan("!" ++ Rest, Buf, Result, '(', S) when Buf==<<"">> -> ?do_scan('!'); scan("*" ++ Rest, Buf, Result, '*', S) -> ?do_scan('*'); scan("*" ++ Rest, Buf, Result, '=', S) -> ?do_scan('*'); scan("(" ++ Rest, Buf, Result, _, S) -> ?do_scan('('); scan(")" ++ Rest, Buf, Result, _, S) -> ?do_scan(')'); scan([Letter | Rest], Buf, Result, PreviosAtom, S) -> scan(Rest, <>, Result, PreviosAtom, S); scan([], Buf, Result, _, S) -> lists:reverse(check(Buf, S) ++ Result). check(<<>>, _) -> []; check(Buf, S) -> [{str, 1, binary_to_list(do_sub(Buf, S))}]. -define(MAX_RECURSION, 100). -spec do_sub(binary(), [{binary(), binary()} | {binary(), binary(), pos_integer()}]) -> binary(). do_sub(S, []) -> S; do_sub(<<>>, _) -> <<>>; do_sub(S, [{RegExp, New} | T]) -> Result = do_sub(S, {RegExp, replace_amps(New)}, 1), do_sub(Result, T); do_sub(S, [{RegExp, New, Times} | T]) -> Result = do_sub(S, {RegExp, replace_amps(New), Times}, 1), do_sub(Result, T). do_sub(S, {RegExp, New}, Iter) -> case ejabberd_regexp:run(S, RegExp) of match -> case ejabberd_regexp:replace(S, RegExp, New) of NewS when Iter =< ?MAX_RECURSION -> do_sub(NewS, {RegExp, New}, Iter+1); _NewS when Iter > ?MAX_RECURSION -> erlang:error(max_substitute_recursion) end; nomatch -> S; _ -> erlang:error(bad_regexp) end; do_sub(S, {_, _, N}, _) when N<1 -> S; do_sub(S, {RegExp, New, Times}, Iter) -> case ejabberd_regexp:run(S, RegExp) of match -> case ejabberd_regexp:replace(S, RegExp, New) of NewS when Iter < Times -> do_sub(NewS, {RegExp, New, Times}, Iter+1); NewS -> NewS end; nomatch -> S; _ -> erlang:error(bad_regexp) end. replace_amps(Bin) -> list_to_binary( lists:flatmap( fun($&) -> "\\&"; ($\\) -> "\\\\"; (Chr) -> [Chr] end, binary_to_list(Bin))). ejabberd-18.01/src/mod_announce_mnesia.erl0000644000232200023220000001017713225664356021145 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_announce_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_announce_mnesia). -behaviour(mod_announce). %% API -export([init/2, set_motd_users/2, set_motd/2, delete_motd/1, get_motd/1, is_motd_user/2, set_motd_user/2, import/3]). -export([need_transform/1, transform/1]). -include("xmpp.hrl"). -include("mod_announce.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, motd, [{disc_only_copies, [node()]}, {attributes, record_info(fields, motd)}]), ejabberd_mnesia:create(?MODULE, motd_users, [{disc_only_copies, [node()]}, {attributes, record_info(fields, motd_users)}]). set_motd_users(_LServer, USRs) -> F = fun() -> lists:foreach( fun({U, S, _R}) -> mnesia:write(#motd_users{us = {U, S}}) end, USRs) end, transaction(F). set_motd(LServer, Packet) -> F = fun() -> mnesia:write(#motd{server = LServer, packet = Packet}) end, transaction(F). delete_motd(LServer) -> F = fun() -> mnesia:delete({motd, LServer}), mnesia:write_lock_table(motd_users), Users = mnesia:select( motd_users, [{#motd_users{us = '$1', _ = '_'}, [{'==', {element, 2, '$1'}, LServer}], ['$1']}]), lists:foreach(fun(US) -> mnesia:delete({motd_users, US}) end, Users) end, transaction(F). get_motd(LServer) -> case mnesia:dirty_read({motd, LServer}) of [#motd{packet = Packet}] -> {ok, Packet}; [] -> error end. is_motd_user(LUser, LServer) -> case mnesia:dirty_read({motd_users, {LUser, LServer}}) of [#motd_users{}] -> {ok, true}; _ -> {ok, false} end. set_motd_user(LUser, LServer) -> F = fun() -> mnesia:write(#motd_users{us = {LUser, LServer}}) end, transaction(F). need_transform(#motd{server = S}) when is_list(S) -> ?INFO_MSG("Mnesia table 'motd' will be converted to binary", []), true; need_transform(#motd_users{us = {U, S}}) when is_list(U) orelse is_list(S) -> ?INFO_MSG("Mnesia table 'motd_users' will be converted to binary", []), true; need_transform(_) -> false. transform(#motd{server = S, packet = P} = R) -> NewS = iolist_to_binary(S), NewP = fxml:to_xmlel(P), R#motd{server = NewS, packet = NewP}; transform(#motd_users{us = {U, S}} = R) -> NewUS = {iolist_to_binary(U), iolist_to_binary(S)}, R#motd_users{us = NewUS}. import(LServer, <<"motd">>, [<<>>, XML, _TimeStamp]) -> El = fxml_stream:parse_element(XML), mnesia:dirty_write(#motd{server = LServer, packet = El}); import(LServer, <<"motd">>, [LUser, <<>>, _TimeStamp]) -> mnesia:dirty_write(#motd_users{us = {LUser, LServer}}). %%%=================================================================== %%% Internal functions %%%=================================================================== transaction(F) -> case mnesia:transaction(F) of {atomic, Res} -> Res; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. ejabberd-18.01/src/mod_disco.erl0000644000232200023220000004115113225664356017100 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_disco.erl %%% Author : Alexey Shchepin %%% Purpose : Service Discovery (XEP-0030) support %%% Created : 1 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_disco). -author('alexey@process-one.net'). -protocol({xep, 30, '2.4'}). -protocol({xep, 157, '1.0'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_local_iq_items/1, process_local_iq_info/1, get_local_identity/5, get_local_features/5, get_local_services/5, process_sm_iq_items/1, process_sm_iq_info/1, get_sm_identity/5, get_sm_features/5, get_sm_items/5, get_info/5, transform_module_options/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("translate.hrl"). -include("xmpp.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include("mod_roster.hrl"). -type features_acc() :: {error, stanza_error()} | {result, [binary()]} | empty. -type items_acc() :: {error, stanza_error()} | {result, [disco_item()]} | empty. start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_local_iq_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, ?MODULE, process_local_iq_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO, ?MODULE, process_sm_iq_info, IQDisc), catch ets:new(disco_extra_domains, [named_table, ordered_set, public, {heir, erlang:group_leader(), none}]), ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []), lists:foreach(fun (Domain) -> register_extra_domain(Host, Domain) end, ExtraDomains), ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_services, 100), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 100), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 100), ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 100), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 100), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100), ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 100), ok. stop(Host) -> ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 100), ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 100), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 100), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 100), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_services, 100), ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 100), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO), catch ets:match_delete(disco_extra_domains, {{'_', Host}}), ok. reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts, []) of {false, NewDomains, OldDomains} -> lists:foreach( fun(Domain) -> register_extra_domain(Host, Domain) end, NewDomains -- OldDomains), lists:foreach( fun(Domain) -> unregister_extra_domain(Host, Domain) end, OldDomains -- NewDomains); true -> ok end, case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_local_iq_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, ?MODULE, process_local_iq_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO, ?MODULE, process_sm_iq_info, IQDisc); true -> ok end. -spec register_extra_domain(binary(), binary()) -> true. register_extra_domain(Host, Domain) -> ets:insert(disco_extra_domains, {{Domain, Host}}). -spec unregister_extra_domain(binary(), binary()) -> true. unregister_extra_domain(Host, Domain) -> ets:delete_object(disco_extra_domains, {{Domain, Host}}). -spec process_local_iq_items(iq()) -> iq(). process_local_iq_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_local_iq_items(#iq{type = get, lang = Lang, from = From, to = To, sub_els = [#disco_items{node = Node}]} = IQ) -> Host = To#jid.lserver, case ejabberd_hooks:run_fold(disco_local_items, Host, empty, [From, To, Node, Lang]) of {result, Items} -> xmpp:make_iq_result(IQ, #disco_items{node = Node, items = Items}); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec process_local_iq_info(iq()) -> iq(). process_local_iq_info(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_local_iq_info(#iq{type = get, lang = Lang, from = From, to = To, sub_els = [#disco_info{node = Node}]} = IQ) -> Host = To#jid.lserver, Identity = ejabberd_hooks:run_fold(disco_local_identity, Host, [], [From, To, Node, Lang]), Info = ejabberd_hooks:run_fold(disco_info, Host, [], [Host, ?MODULE, Node, Lang]), case ejabberd_hooks:run_fold(disco_local_features, Host, empty, [From, To, Node, Lang]) of {result, Features} -> xmpp:make_iq_result(IQ, #disco_info{node = Node, identities = Identity, xdata = Info, features = Features}); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec get_local_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. get_local_identity(Acc, _From, To, <<"">>, _Lang) -> Host = To#jid.lserver, Name = gen_mod:get_module_opt(Host, ?MODULE, name, ?T("ejabberd")), Acc ++ [#identity{category = <<"server">>, type = <<"im">>, name = Name}]; get_local_identity(Acc, _From, _To, _Node, _Lang) -> Acc. -spec get_local_features(features_acc(), jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. get_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> Acc; get_local_features(Acc, _From, To, <<"">>, _Lang) -> Feats = case Acc of {result, Features} -> Features; empty -> [] end, {result, lists:usort( lists:flatten( [<<"iq">>, <<"presence">>, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, Feats, ejabberd_local:get_features(To#jid.lserver)]))}; get_local_features(Acc, _From, _To, _Node, Lang) -> case Acc of {result, _Features} -> Acc; empty -> Txt = <<"No features available">>, {error, xmpp:err_item_not_found(Txt, Lang)} end. -spec get_local_services(items_acc(), jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [disco_item()]}. get_local_services({error, _Error} = Acc, _From, _To, _Node, _Lang) -> Acc; get_local_services(Acc, _From, To, <<"">>, _Lang) -> Items = case Acc of {result, Its} -> Its; empty -> [] end, Host = To#jid.lserver, {result, lists:usort( lists:map( fun(Domain) -> #disco_item{jid = jid:make(Domain)} end, get_vh_services(Host) ++ ets:select(disco_extra_domains, ets:fun2ms( fun({{D, H}}) when H == Host -> D end)))) ++ Items}; get_local_services({result, _} = Acc, _From, _To, _Node, _Lang) -> Acc; get_local_services(empty, _From, _To, _Node, Lang) -> {error, xmpp:err_item_not_found(<<"No services available">>, Lang)}. -spec get_vh_services(binary()) -> [binary()]. get_vh_services(Host) -> Hosts = lists:sort(fun (H1, H2) -> byte_size(H1) >= byte_size(H2) end, ?MYHOSTS), lists:filter(fun (H) -> case lists:dropwhile(fun (VH) -> not str:suffix( <<".", VH/binary>>, H) end, Hosts) of [] -> false; [VH | _] -> VH == Host end end, ejabberd_router:get_all_routes()). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec process_sm_iq_items(iq()) -> iq(). process_sm_iq_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_sm_iq_items(#iq{type = get, lang = Lang, from = From, to = To, sub_els = [#disco_items{node = Node}]} = IQ) -> case is_presence_subscribed(From, To) of true -> Host = To#jid.lserver, case ejabberd_hooks:run_fold(disco_sm_items, Host, empty, [From, To, Node, Lang]) of {result, Items} -> xmpp:make_iq_result( IQ, #disco_items{node = Node, items = Items}); {error, Error} -> xmpp:make_error(IQ, Error) end; false -> Txt = <<"Not subscribed">>, xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang)) end. -spec get_sm_items(items_acc(), jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [disco_item()]}. get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) -> Acc; get_sm_items(Acc, From, #jid{user = User, server = Server} = To, <<"">>, _Lang) -> Items = case Acc of {result, Its} -> Its; empty -> [] end, Items1 = case is_presence_subscribed(From, To) of true -> get_user_resources(User, Server); _ -> [] end, {result, Items ++ Items1}; get_sm_items({result, _} = Acc, _From, _To, _Node, _Lang) -> Acc; get_sm_items(empty, From, To, _Node, Lang) -> #jid{luser = LFrom, lserver = LSFrom} = From, #jid{luser = LTo, lserver = LSTo} = To, case {LFrom, LSFrom} of {LTo, LSTo} -> {error, xmpp:err_item_not_found()}; _ -> Txt = <<"Query to another users is forbidden">>, {error, xmpp:err_not_allowed(Txt, Lang)} end. -spec is_presence_subscribed(jid(), jid()) -> boolean(). is_presence_subscribed(#jid{luser = User, lserver = Server}, #jid{luser = User, lserver = Server}) -> true; is_presence_subscribed(#jid{luser = FromUser, lserver = FromServer}, #jid{luser = ToUser, lserver = ToServer}) -> lists:any(fun (#roster{jid = {SubUser, SubServer, _}, subscription = Sub}) when FromUser == SubUser, FromServer == SubServer, Sub /= none -> true; (_RosterEntry) -> false end, ejabberd_hooks:run_fold(roster_get, ToServer, [], [{ToUser, ToServer}])). -spec process_sm_iq_info(iq()) -> iq(). process_sm_iq_info(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_sm_iq_info(#iq{type = get, lang = Lang, from = From, to = To, sub_els = [#disco_info{node = Node}]} = IQ) -> case is_presence_subscribed(From, To) of true -> Host = To#jid.lserver, Identity = ejabberd_hooks:run_fold(disco_sm_identity, Host, [], [From, To, Node, Lang]), Info = ejabberd_hooks:run_fold(disco_info, Host, [], [From, To, Node, Lang]), case ejabberd_hooks:run_fold(disco_sm_features, Host, empty, [From, To, Node, Lang]) of {result, Features} -> xmpp:make_iq_result(IQ, #disco_info{node = Node, identities = Identity, xdata = Info, features = Features}); {error, Error} -> xmpp:make_error(IQ, Error) end; false -> Txt = <<"Not subscribed">>, xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang)) end. -spec get_sm_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. get_sm_identity(Acc, _From, #jid{luser = LUser, lserver = LServer}, _Node, _Lang) -> Acc ++ case ejabberd_auth:user_exists(LUser, LServer) of true -> [#identity{category = <<"account">>, type = <<"registered">>}]; _ -> [] end. -spec get_sm_features(features_acc(), jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. get_sm_features(empty, From, To, _Node, Lang) -> #jid{luser = LFrom, lserver = LSFrom} = From, #jid{luser = LTo, lserver = LSTo} = To, case {LFrom, LSFrom} of {LTo, LSTo} -> {error, xmpp:err_item_not_found()}; _ -> Txt = <<"Query to another users is forbidden">>, {error, xmpp:err_not_allowed(Txt, Lang)} end; get_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec get_user_resources(binary(), binary()) -> [disco_item()]. get_user_resources(User, Server) -> Rs = ejabberd_sm:get_user_resources(User, Server), [#disco_item{jid = jid:make(User, Server, Resource), name = User} || Resource <- lists:sort(Rs)]. -spec transform_module_options(gen_mod:opts()) -> gen_mod:opts(). transform_module_options(Opts) -> lists:map( fun({server_info, Infos}) -> NewInfos = lists:map( fun({Modules, Name, URLs}) -> [[{modules, Modules}, {name, Name}, {urls, URLs}]]; (Opt) -> Opt end, Infos), {server_info, NewInfos}; (Opt) -> Opt end, Opts). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Support for: XEP-0157 Contact Addresses for XMPP Services -spec get_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()]; ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()]. get_info(_A, Host, Mod, Node, _Lang) when is_atom(Mod), Node == <<"">> -> Module = case Mod of undefined -> ?MODULE; _ -> Mod end, [#xdata{type = result, fields = [#xdata_field{type = hidden, var = <<"FORM_TYPE">>, values = [?NS_SERVERINFO]} | get_fields(Host, Module)]}]; get_info(Acc, _, _, _Node, _) -> Acc. -spec get_fields(binary(), module()) -> [xdata_field()]. get_fields(Host, Module) -> Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []), Fields1 = lists:filter(fun ({Modules, _, _}) -> case Modules of all -> true; Modules -> lists:member(Module, Modules) end end, Fields), [#xdata_field{var = Var, values = Values} || {_, Var, Values} <- Fields1]. -spec depends(binary(), gen_mod:opts()) -> []. depends(_Host, _Opts) -> []. mod_opt_type(extra_domains) -> fun (Hs) -> [iolist_to_binary(H) || H <- Hs] end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(server_info) -> fun (L) -> lists:map(fun (Opts) -> Mods = proplists:get_value(modules, Opts, all), Name = proplists:get_value(name, Opts, <<>>), URLs = proplists:get_value(urls, Opts, []), {Mods, Name, URLs} end, L) end; mod_opt_type(_) -> [extra_domains, iqdisc, server_info, name]. ejabberd-18.01/src/mod_roster_sql.erl0000644000232200023220000003046113225664356020176 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_roster_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_roster_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_roster). %% API -export([init/2, read_roster_version/2, write_roster_version/4, get_roster/2, get_roster_item/3, roster_subscribe/4, read_subscription_and_groups/3, remove_user/2, update_roster/4, del_roster/3, transaction/2, import/3, export/1, raw_to_record/2]). -include("mod_roster.hrl"). -include("ejabberd_sql_pt.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. read_roster_version(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(version)s from roster_version" " where username = %(LUser)s and %(LServer)H")) of {selected, [{Version}]} -> {ok, Version}; {selected, []} -> error; _ -> {error, db_failure} end. write_roster_version(LUser, LServer, InTransaction, Ver) -> if InTransaction -> set_roster_version(LUser, LServer, Ver); true -> transaction( LServer, fun () -> set_roster_version(LUser, LServer, Ver) end) end. get_roster(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s, " "@(ask)s, @(askmessage)s, @(server)s, @(subscribe)s, " "@(type)s from rosterusers " "where username=%(LUser)s and %(LServer)H")) of {selected, Items} when is_list(Items) -> JIDGroups = case get_roster_jid_groups(LServer, LUser) of {selected, JGrps} when is_list(JGrps) -> JGrps; _ -> [] end, GroupsDict = lists:foldl(fun({J, G}, Acc) -> dict:append(J, G, Acc) end, dict:new(), JIDGroups), {ok, lists:flatmap( fun(I) -> case raw_to_record(LServer, I) of %% Bad JID in database: error -> []; R -> SJID = jid:encode(R#roster.jid), Groups = case dict:find(SJID, GroupsDict) of {ok, Gs} -> Gs; error -> [] end, [R#roster{groups = Groups}] end end, Items)}; _ -> error end. roster_subscribe(_LUser, _LServer, _LJID, Item) -> ItemVals = record_to_row(Item), roster_subscribe(ItemVals). transaction(LServer, F) -> ejabberd_sql:sql_transaction(LServer, F). get_roster_item(LUser, LServer, LJID) -> SJID = jid:encode(LJID), case get_roster_by_jid(LServer, LUser, SJID) of {selected, [I]} -> case raw_to_record(LServer, I) of error -> error; R -> Groups = case get_roster_groups(LServer, LUser, SJID) of {selected, JGrps} when is_list(JGrps) -> [JGrp || {JGrp} <- JGrps]; _ -> [] end, {ok, R#roster{groups = Groups}} end; {selected, []} -> error end. remove_user(LUser, LServer) -> transaction( LServer, fun () -> ejabberd_sql:sql_query_t( ?SQL("delete from rosterusers" " where username=%(LUser)s and %(LServer)H")), ejabberd_sql:sql_query_t( ?SQL("delete from rostergroups" " where username=%(LUser)s and %(LServer)H")) end), ok. update_roster(LUser, LServer, LJID, Item) -> SJID = jid:encode(LJID), ItemVals = record_to_row(Item), ItemGroups = Item#roster.groups, roster_subscribe(ItemVals), ejabberd_sql:sql_query_t( ?SQL("delete from rostergroups" " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")), lists:foreach( fun(ItemGroup) -> ejabberd_sql:sql_query_t( ?SQL_INSERT( "rostergroups", ["username=%(LUser)s", "server_host=%(LServer)s", "jid=%(SJID)s", "grp=%(ItemGroup)s"])) end, ItemGroups). del_roster(LUser, LServer, LJID) -> SJID = jid:encode(LJID), ejabberd_sql:sql_query_t( ?SQL("delete from rosterusers" " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")), ejabberd_sql:sql_query_t( ?SQL("delete from rostergroups" " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). read_subscription_and_groups(LUser, LServer, LJID) -> SJID = jid:encode(LJID), case get_subscription(LServer, LUser, SJID) of {selected, [{SSubscription}]} -> Subscription = case SSubscription of <<"B">> -> both; <<"T">> -> to; <<"F">> -> from; <<"N">> -> none; <<"">> -> none; _ -> ?ERROR_MSG("~s", [format_row_error( LUser, LServer, {subscription, SSubscription})]), none end, Groups = case get_rostergroup_by_jid(LServer, LUser, SJID) of {selected, JGrps} when is_list(JGrps) -> [JGrp || {JGrp} <- JGrps]; _ -> [] end, {ok, {Subscription, Groups}}; _ -> error end. export(_Server) -> [{roster, fun(Host, #roster{usj = {_LUser, LServer, _LJID}} = R) when LServer == Host -> ItemVals = record_to_row(R), ItemGroups = R#roster.groups, update_roster_sql(ItemVals, ItemGroups); (_Host, _R) -> [] end}, {roster_version, fun(Host, #roster_version{us = {LUser, LServer}, version = Ver}) when LServer == Host -> [?SQL("delete from roster_version" " where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT( "roster_version", ["username=%(LUser)s", "server_host=%(LServer)s", "version=%(Ver)s"])]; (_Host, _R) -> [] end}]. import(_, _, _) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== set_roster_version(LUser, LServer, Version) -> ?SQL_UPSERT_T( "roster_version", ["!username=%(LUser)s", "!server_host=%(LServer)s", "version=%(Version)s"]). get_roster_jid_groups(LServer, LUser) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(jid)s, @(grp)s from rostergroups where " "username=%(LUser)s and %(LServer)H")). get_roster_groups(LServer, LUser, SJID) -> ejabberd_sql:sql_query_t( ?SQL("select @(grp)s from rostergroups" " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). roster_subscribe({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}) -> ?SQL_UPSERT_T( "rosterusers", ["!username=%(LUser)s", "!server_host=%(LServer)s", "!jid=%(SJID)s", "nick=%(Name)s", "subscription=%(SSubscription)s", "ask=%(SAsk)s", "askmessage=%(AskMessage)s", "server='N'", "subscribe=''", "type='item'"]). get_roster_by_jid(LServer, LUser, SJID) -> ejabberd_sql:sql_query_t( ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s," " @(ask)s, @(askmessage)s, @(server)s, @(subscribe)s," " @(type)s from rosterusers" " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). get_rostergroup_by_jid(LServer, LUser, SJID) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(grp)s from rostergroups" " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). get_subscription(LServer, LUser, SJID) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(subscription)s from rosterusers " "where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). update_roster_sql({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}, ItemGroups) -> [?SQL("delete from rosterusers where" " username=%(LUser)s and %(LServer)H and jid=%(SJID)s;"), ?SQL_INSERT( "rosterusers", ["username=%(LUser)s", "server_host=%(LServer)s", "jid=%(SJID)s", "nick=%(Name)s", "subscription=%(SSubscription)s", "ask=%(SAsk)s", "askmessage=%(AskMessage)s", "server='N'", "subscribe=''", "type='item'"]), ?SQL("delete from rostergroups where" " username=%(LUser)s and %(LServer)H and jid=%(SJID)s;")] ++ [?SQL_INSERT( "rostergroups", ["username=%(LUser)s", "server_host=%(LServer)s", "jid=%(SJID)s", "grp=%(ItemGroup)s"]) || ItemGroup <- ItemGroups]. raw_to_record(LServer, [User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType]) -> raw_to_record(LServer, {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}); raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}) -> raw_to_record(LServer, {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}); raw_to_record(LServer, {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}) -> try jid:decode(SJID) of JID -> LJID = jid:tolower(JID), Subscription = case SSubscription of <<"B">> -> both; <<"T">> -> to; <<"F">> -> from; <<"N">> -> none; <<"">> -> none; _ -> ?ERROR_MSG("~s", [format_row_error( User, LServer, {subscription, SSubscription})]), none end, Ask = case SAsk of <<"S">> -> subscribe; <<"U">> -> unsubscribe; <<"B">> -> both; <<"O">> -> out; <<"I">> -> in; <<"N">> -> none; <<"">> -> none; _ -> ?ERROR_MSG("~s", [format_row_error(User, LServer, {ask, SAsk})]), none end, #roster{usj = {User, LServer, LJID}, us = {User, LServer}, jid = LJID, name = Nick, subscription = Subscription, ask = Ask, askmessage = SAskMessage} catch _:{bad_jid, _} -> ?ERROR_MSG("~s", [format_row_error(User, LServer, {jid, SJID})]), error end. record_to_row( #roster{us = {LUser, LServer}, jid = JID, name = Name, subscription = Subscription, ask = Ask, askmessage = AskMessage}) -> SJID = jid:encode(jid:tolower(JID)), SSubscription = case Subscription of both -> <<"B">>; to -> <<"T">>; from -> <<"F">>; none -> <<"N">> end, SAsk = case Ask of subscribe -> <<"S">>; unsubscribe -> <<"U">>; both -> <<"B">>; out -> <<"O">>; in -> <<"I">>; none -> <<"N">> end, {LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}. format_row_error(User, Server, Why) -> [case Why of {jid, JID} -> ["Malformed 'jid' field with value '", JID, "'"]; {subscription, Sub} -> ["Malformed 'subscription' field with value '", Sub, "'"]; {ask, Ask} -> ["Malformed 'ask' field with value '", Ask, "'"] end, " detected for ", User, "@", Server, " in table 'rosterusers'"]. ejabberd-18.01/src/mod_muc_mnesia.erl0000644000232200023220000003065613225664356020127 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_muc_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_muc_mnesia). -behaviour(mod_muc). -behaviour(mod_muc_room). %% API -export([init/2, import/3, store_room/5, restore_room/3, forget_room/3, can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]). -export([register_online_room/4, unregister_online_room/4, find_online_room/3, get_online_rooms/3, count_online_rooms/2, rsm_supported/0, register_online_user/4, unregister_online_user/4, count_online_rooms_by_user/3, get_online_rooms_by_user/3, get_subscribed_rooms/3]). -export([set_affiliation/6, set_affiliations/4, get_affiliation/5, get_affiliations/3, search_affiliation/4]). %% gen_server callbacks -export([start_link/2, init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3]). -export([need_transform/1, transform/1]). -include("mod_muc.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== init(Host, Opts) -> Spec = {?MODULE, {?MODULE, start_link, [Host, Opts]}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. start_link(Host, Opts) -> Name = gen_mod:get_module_proc(Host, ?MODULE), gen_server:start_link({local, Name}, ?MODULE, [Host, Opts], []). store_room(_LServer, Host, Name, Opts, _) -> F = fun () -> mnesia:write(#muc_room{name_host = {Name, Host}, opts = Opts}) end, mnesia:transaction(F). restore_room(_LServer, Host, Name) -> case catch mnesia:dirty_read(muc_room, {Name, Host}) of [#muc_room{opts = Opts}] -> Opts; _ -> error end. forget_room(_LServer, Host, Name) -> F = fun () -> mnesia:delete({muc_room, {Name, Host}}) end, mnesia:transaction(F). can_use_nick(_LServer, Host, JID, Nick) -> {LUser, LServer, _} = jid:tolower(JID), LUS = {LUser, LServer}, case catch mnesia:dirty_select(muc_registered, [{#muc_registered{us_host = '$1', nick = Nick, _ = '_'}, [{'==', {element, 2, '$1'}, Host}], ['$_']}]) of {'EXIT', _Reason} -> true; [] -> true; [#muc_registered{us_host = {U, _Host}}] -> U == LUS end. get_rooms(_LServer, Host) -> mnesia:dirty_select(muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'}, [], ['$_']}]). get_nick(_LServer, Host, From) -> {LUser, LServer, _} = jid:tolower(From), LUS = {LUser, LServer}, case mnesia:dirty_read(muc_registered, {LUS, Host}) of [] -> error; [#muc_registered{nick = Nick}] -> Nick end. set_nick(_LServer, Host, From, Nick) -> {LUser, LServer, _} = jid:tolower(From), LUS = {LUser, LServer}, F = fun () -> case Nick of <<"">> -> mnesia:delete({muc_registered, {LUS, Host}}), ok; _ -> Allow = case mnesia:select( muc_registered, [{#muc_registered{us_host = '$1', nick = Nick, _ = '_'}, [{'==', {element, 2, '$1'}, Host}], ['$_']}]) of [] -> true; [#muc_registered{us_host = {U, _Host}}] -> U == LUS end, if Allow -> mnesia:write(#muc_registered{ us_host = {LUS, Host}, nick = Nick}), ok; true -> false end end end, mnesia:transaction(F). set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) -> {error, not_implemented}. set_affiliations(_ServerHost, _Room, _Host, _Affiliations) -> {error, not_implemented}. get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) -> {error, not_implemented}. get_affiliations(_ServerHost, _Room, _Host) -> {error, not_implemented}. search_affiliation(_ServerHost, _Room, _Host, _Affiliation) -> {error, not_implemented}. register_online_room(_ServerHost, Room, Host, Pid) -> F = fun() -> mnesia:write( #muc_online_room{name_host = {Room, Host}, pid = Pid}) end, mnesia:transaction(F). unregister_online_room(_ServerHost, Room, Host, Pid) -> F = fun () -> mnesia:delete_object( #muc_online_room{name_host = {Room, Host}, pid = Pid}) end, mnesia:transaction(F). find_online_room(_ServerHost, Room, Host) -> find_online_room(Room, Host). find_online_room(Room, Host) -> case mnesia:dirty_read(muc_online_room, {Room, Host}) of [] -> error; [#muc_online_room{pid = Pid}] -> {ok, Pid} end. count_online_rooms(_ServerHost, Host) -> ets:select_count( muc_online_room, ets:fun2ms( fun(#muc_online_room{name_host = {_, H}}) -> H == Host end)). get_online_rooms(_ServerHost, Host, #rsm_set{max = Max, 'after' = After, before = undefined}) when is_binary(After), After /= <<"">> -> lists:reverse(get_online_rooms(next, {After, Host}, Host, 0, Max, [])); get_online_rooms(_ServerHost, Host, #rsm_set{max = Max, 'after' = undefined, before = Before}) when is_binary(Before), Before /= <<"">> -> get_online_rooms(prev, {Before, Host}, Host, 0, Max, []); get_online_rooms(_ServerHost, Host, #rsm_set{max = Max, 'after' = undefined, before = <<"">>}) -> get_online_rooms(last, {<<"">>, Host}, Host, 0, Max, []); get_online_rooms(_ServerHost, Host, #rsm_set{max = Max}) -> lists:reverse(get_online_rooms(first, {<<"">>, Host}, Host, 0, Max, [])); get_online_rooms(_ServerHost, Host, undefined) -> mnesia:dirty_select( muc_online_room, ets:fun2ms( fun(#muc_online_room{name_host = {Name, H}, pid = Pid}) when H == Host -> {Name, Host, Pid} end)). -spec get_online_rooms(prev | next | last | first, {binary(), binary()}, binary(), non_neg_integer(), non_neg_integer() | undefined, [{binary(), binary(), pid()}]) -> [{binary(), binary(), pid()}]. get_online_rooms(_Action, _Key, _Host, Count, Max, Items) when Count >= Max -> Items; get_online_rooms(Action, Key, Host, Count, Max, Items) -> Call = fun() -> case Action of prev -> mnesia:dirty_prev(muc_online_room, Key); next -> mnesia:dirty_next(muc_online_room, Key); last -> mnesia:dirty_last(muc_online_room); first -> mnesia:dirty_first(muc_online_room) end end, NewAction = case Action of last -> prev; first -> next; _ -> Action end, try Call() of '$end_of_table' -> Items; {Room, Host} = NewKey -> case find_online_room(Room, Host) of {ok, Pid} -> get_online_rooms(NewAction, NewKey, Host, Count + 1, Max, [{Room, Host, Pid}|Items]); error -> get_online_rooms(NewAction, NewKey, Host, Count, Max, Items) end; NewKey -> get_online_rooms(NewAction, NewKey, Host, Count, Max, Items) catch _:{aborted, {badarg, _}} -> Items end. rsm_supported() -> true. register_online_user(_ServerHost, {U, S, R}, Room, Host) -> ets:insert(muc_online_users, #muc_online_users{us = {U, S}, resource = R, room = Room, host = Host}). unregister_online_user(_ServerHost, {U, S, R}, Room, Host) -> ets:delete_object(muc_online_users, #muc_online_users{us = {U, S}, resource = R, room = Room, host = Host}). count_online_rooms_by_user(_ServerHost, U, S) -> ets:select_count( muc_online_users, ets:fun2ms( fun(#muc_online_users{us = {U1, S1}}) -> U == U1 andalso S == S1 end)). get_online_rooms_by_user(ServerHost, U, S) -> MucHost = gen_mod:get_module_opt_host(ServerHost, mod_muc, <<"conference.@HOST@">>), ets:select( muc_online_users, ets:fun2ms( fun(#muc_online_users{us = {U1, S1}, room = Room, host = Host}) when U == U1 andalso S == S1 andalso MucHost == Host -> {Room, Host} end)). import(_LServer, <<"muc_room">>, [Name, RoomHost, SOpts, _TimeStamp]) -> Opts = mod_muc:opts_to_binary(ejabberd_sql:decode_term(SOpts)), mnesia:dirty_write( #muc_room{name_host = {Name, RoomHost}, opts = Opts}); import(_LServer, <<"muc_registered">>, [J, RoomHost, Nick, _TimeStamp]) -> #jid{user = U, server = S} = jid:decode(J), mnesia:dirty_write( #muc_registered{us_host = {{U, S}, RoomHost}, nick = Nick}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([Host, Opts]) -> MyHosts = proplists:get_value(hosts, Opts), case gen_mod:db_mod(Host, Opts, mod_muc) of ?MODULE -> ejabberd_mnesia:create(?MODULE, muc_room, [{disc_copies, [node()]}, {attributes, record_info(fields, muc_room)}]), ejabberd_mnesia:create(?MODULE, muc_registered, [{disc_copies, [node()]}, {attributes, record_info(fields, muc_registered)}, {index, [nick]}]); _ -> ok end, case gen_mod:ram_db_mod(Host, Opts, mod_muc) of ?MODULE -> ejabberd_mnesia:create(?MODULE, muc_online_room, [{ram_copies, [node()]}, {type, ordered_set}, {attributes, record_info(fields, muc_online_room)}]), catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]), lists:foreach( fun(MyHost) -> clean_table_from_bad_node(node(), MyHost) end, MyHosts), mnesia:subscribe(system); _ -> ok end, {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> clean_table_from_bad_node(Node), {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== clean_table_from_bad_node(Node) -> F = fun() -> Es = mnesia:select( muc_online_room, [{#muc_online_room{pid = '$1', _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}]), lists:foreach(fun(E) -> mnesia:delete_object(E) end, Es) end, mnesia:async_dirty(F). clean_table_from_bad_node(Node, Host) -> F = fun() -> Es = mnesia:select( muc_online_room, [{#muc_online_room{pid = '$1', name_host = {'_', Host}, _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}]), lists:foreach(fun(E) -> mnesia:delete_object(E) end, Es) end, mnesia:async_dirty(F). need_transform(#muc_room{name_host = {N, H}}) when is_list(N) orelse is_list(H) -> ?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []), true; need_transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick}) when is_list(U) orelse is_list(S) orelse is_list(H) orelse is_list(Nick) -> ?INFO_MSG("Mnesia table 'muc_registered' will be converted to binary", []), true; need_transform(_) -> false. transform(#muc_room{name_host = {N, H}, opts = Opts} = R) -> R#muc_room{name_host = {iolist_to_binary(N), iolist_to_binary(H)}, opts = mod_muc:opts_to_binary(Opts)}; transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick} = R) -> R#muc_registered{us_host = {{iolist_to_binary(U), iolist_to_binary(S)}, iolist_to_binary(H)}, nick = iolist_to_binary(Nick)}. get_subscribed_rooms(_, _, _) -> not_implemented. ejabberd-18.01/src/ejabberd.app.src.in0000644000232200023220000000045413225664356020070 0ustar debalancedebalance%% $Id$ {application, ejabberd, [{description, "@PACKAGE_NAME@"}, {vsn, "@PACKAGE_VERSION@"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}, {env, []}, {mod, {ejabberd_app, []}}]}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: ejabberd-18.01/src/cyrsasl_plain.erl0000644000232200023220000000656613225664356020016 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : cyrsasl_plain.erl %%% Author : Alexey Shchepin %%% Purpose : PLAIN SASL mechanism %%% Created : 8 Mar 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(cyrsasl_plain). -author('alexey@process-one.net'). -export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]). -behaviour(cyrsasl). -record(state, {check_password}). -type error_reason() :: parser_failed | not_authorized. -export_type([error_reason/0]). start(_Opts) -> cyrsasl:register_mechanism(<<"PLAIN">>, ?MODULE, plain). stop() -> ok. -spec format_error(error_reason()) -> {atom(), binary()}. format_error(parser_failed) -> {'bad-protocol', <<"Response decoding failed">>}; format_error(not_authorized) -> {'not-authorized', <<"Invalid username or password">>}. mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) -> {ok, #state{check_password = CheckPassword}}. mech_step(State, ClientIn) -> case prepare(ClientIn) of [AuthzId, User, Password] -> case (State#state.check_password)(User, AuthzId, Password) of {true, AuthModule} -> {ok, [{username, User}, {authzid, AuthzId}, {auth_module, AuthModule}]}; _ -> {error, not_authorized, User} end; _ -> {error, parser_failed} end. prepare(ClientIn) -> case parse(ClientIn) of [<<"">>, UserMaybeDomain, Password] -> case parse_domain(UserMaybeDomain) of %% login@domainpwd [User, _Domain] -> [User, User, Password]; %% loginpwd [User] -> [User, User, Password] end; [AuthzId, User, Password] -> case parse_domain(AuthzId) of %% login@domainloginpwd [AuthzUser, _Domain] -> [AuthzUser, User, Password]; %% loginloginpwd [AuthzUser] -> [AuthzUser, User, Password] end; _ -> error end. parse(S) -> parse1(binary_to_list(S), "", []). parse1([0 | Cs], S, T) -> parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T); %parse1([], [], T) -> % lists:reverse(T); parse1([], S, T) -> lists:reverse([list_to_binary(lists:reverse(S)) | T]). parse_domain(S) -> parse_domain1(binary_to_list(S), "", []). parse_domain1([$@ | Cs], S, T) -> parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); parse_domain1([C | Cs], S, T) -> parse_domain1(Cs, [C | S], T); parse_domain1([], S, T) -> lists:reverse([list_to_binary(lists:reverse(S)) | T]). ejabberd-18.01/src/mod_last_sql.erl0000644000232200023220000000564713225664356017633 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_last_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_last_sql). -behaviour(mod_last). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/2, get_last/2, store_last_info/4, remove_user/2, import/2, export/1]). -include("mod_last.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. get_last(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(seconds)d, @(state)s from last" " where username=%(LUser)s and %(LServer)H")) of {selected, []} -> error; {selected, [{TimeStamp, Status}]} -> {ok, {TimeStamp, Status}}; _Reason -> {error, db_failure} end. store_last_info(LUser, LServer, TimeStamp, Status) -> case ?SQL_UPSERT(LServer, "last", ["!username=%(LUser)s", "!server_host=%(LServer)s", "seconds=%(TimeStamp)d", "state=%(Status)s"]) of ok -> ok; _Err -> {error, db_failure} end. remove_user(LUser, LServer) -> ejabberd_sql:sql_query( LServer, ?SQL("delete from last where username=%(LUser)s and %(LServer)H")). export(_Server) -> [{last_activity, fun(Host, #last_activity{us = {LUser, LServer}, timestamp = TimeStamp, status = Status}) when LServer == Host -> [?SQL("delete from last where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT("last", ["username=%(LUser)s", "server_host=%(LServer)s", "seconds=%(TimeStamp)d", "state=%(Status)s"])]; (_Host, _R) -> [] end}]. import(_LServer, _LA) -> pass. ejabberd-18.01/src/ejabberd_sm_redis.erl0000644000232200023220000001424313225664356020565 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_sm_redis.erl %%% Author : Evgeny Khramtsov %%% Created : 11 Mar 2015 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sm_redis). -ifndef(GEN_SERVER). -define(GEN_SERVER, p1_server). -endif. -behaviour(?GEN_SERVER). -behaviour(ejabberd_sm). -export([init/0, set_session/1, delete_session/1, get_sessions/0, get_sessions/1, get_sessions/2, cache_nodes/1]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("ejabberd.hrl"). -include("ejabberd_sm.hrl"). -include("logger.hrl"). -define(SM_KEY, <<"ejabberd:sm">>). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== -spec init() -> ok | {error, any()}. init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> ?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []). -spec cache_nodes(binary()) -> [node()]. cache_nodes(_LServer) -> [node()]. -spec set_session(#session{}) -> ok | {error, ejabberd_redis:error_reason()}. set_session(Session) -> T = term_to_binary(Session), USKey = us_to_key(Session#session.us), SIDKey = sid_to_key(Session#session.sid), ServKey = server_to_key(element(2, Session#session.us)), USSIDKey = us_sid_to_key(Session#session.us, Session#session.sid), case ejabberd_redis:multi( fun() -> ejabberd_redis:hset(USKey, SIDKey, T), ejabberd_redis:hset(ServKey, USSIDKey, T), ejabberd_redis:publish( ?SM_KEY, term_to_binary({delete, Session#session.us})) end) of {ok, _} -> ok; Err -> Err end. -spec delete_session(#session{}) -> ok | {error, ejabberd_redis:error_reason()}. delete_session(#session{sid = SID} = Session) -> USKey = us_to_key(Session#session.us), SIDKey = sid_to_key(SID), ServKey = server_to_key(element(2, Session#session.us)), USSIDKey = us_sid_to_key(Session#session.us, SID), case ejabberd_redis:multi( fun() -> ejabberd_redis:hdel(USKey, [SIDKey]), ejabberd_redis:hdel(ServKey, [USSIDKey]), ejabberd_redis:publish( ?SM_KEY, term_to_binary({delete, Session#session.us})) end) of {ok, _} -> ok; Err -> Err end. -spec get_sessions() -> [#session{}]. get_sessions() -> lists:flatmap( fun(LServer) -> get_sessions(LServer) end, ejabberd_sm:get_vh_by_backend(?MODULE)). -spec get_sessions(binary()) -> [#session{}]. get_sessions(LServer) -> ServKey = server_to_key(LServer), case ejabberd_redis:hgetall(ServKey) of {ok, Vals} -> decode_session_list(Vals); {error, _} -> [] end. -spec get_sessions(binary(), binary()) -> {ok, [#session{}]} | {error, ejabberd_redis:error_reason()}. get_sessions(LUser, LServer) -> USKey = us_to_key({LUser, LServer}), case ejabberd_redis:hgetall(USKey) of {ok, Vals} -> {ok, decode_session_list(Vals)}; Err -> Err end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> ejabberd_redis:subscribe([?SM_KEY]), clean_table(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({redis_message, ?SM_KEY, Data}, State) -> case binary_to_term(Data) of {delete, Key} -> ets_cache:delete(?SM_CACHE, Key); Msg -> ?WARNING_MSG("unexpected redis message: ~p", [Msg]) end, {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== us_to_key({LUser, LServer}) -> <<"ejabberd:sm:", LUser/binary, "@", LServer/binary>>. server_to_key(LServer) -> <<"ejabberd:sm:", LServer/binary>>. us_sid_to_key(US, SID) -> term_to_binary({US, SID}). sid_to_key(SID) -> term_to_binary(SID). decode_session_list(Vals) -> [binary_to_term(Val) || {_, Val} <- Vals]. clean_table() -> ?DEBUG("Cleaning Redis SM table...", []), try lists:foreach( fun(LServer) -> ServKey = server_to_key(LServer), {ok, Vals} = ejabberd_redis:hkeys(ServKey), {ok, _} = ejabberd_redis:multi( fun() -> lists:foreach( fun(USSIDKey) -> {US, SID} = binary_to_term(USSIDKey), if node(element(2, SID)) == node() -> USKey = us_to_key(US), SIDKey = sid_to_key(SID), ejabberd_redis:hdel(ServKey, [USSIDKey]), ejabberd_redis:hdel(USKey, [SIDKey]); true -> ok end end, Vals) end) end, ejabberd_sm:get_vh_by_backend(?MODULE)) catch _:{badmatch, {error, _}} -> ?ERROR_MSG("failed to clean redis c2s sessions", []) end. ejabberd-18.01/src/mod_register_web.erl0000644000232200023220000005035613225664356020467 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_register_web.erl %%% Author : Badlop %%% Purpose : Web page to register account and related tasks %%% Created : 4 May 2008 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% IDEAS: %%% %%% * Implement those options, already present in mod_register: %%% + access %%% + captcha_protected %%% + password_strength %%% + welcome_message %%% + registration_timeout %%% %%% * Improve this module to allow each virtual host to have different %%% options. See http://support.process-one.net/browse/EJAB-561 %%% %%% * Check that all the text is translatable. %%% %%% * Add option to use a custom CSS file, or custom CSS lines. %%% %%% * Don't hardcode the "register" path in URL. %%% %%% * Allow private email during register, and store in custom table. %%% * Optionally require private email to register. %%% * Optionally require email confirmation to register. %%% * Allow to set a private email address anytime. %%% * Allow to recover password using private email to confirm (mod_passrecover) %%% * Optionally require invitation %%% * Optionally register request is forwarded to admin, no account created. -module(mod_register_web). -author('badlop@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). %%%---------------------------------------------------------------------- %%% gen_mod callbacks %%%---------------------------------------------------------------------- start(_Host, _Opts) -> %% case gen_mod:get_opt(docroot, Opts, fun(A) -> A end, undefined) of ok. stop(_Host) -> ok. reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> [{mod_register, hard}]. %%%---------------------------------------------------------------------- %%% HTTP handlers %%%---------------------------------------------------------------------- process([], #request{method = 'GET', lang = Lang}) -> index_page(Lang); process([<<"register.css">>], #request{method = 'GET'}) -> serve_css(); process([<<"new">>], #request{method = 'GET', lang = Lang, host = Host, ip = IP}) -> {Addr, _Port} = IP, form_new_get(Host, Lang, Addr); process([<<"delete">>], #request{method = 'GET', lang = Lang, host = Host}) -> form_del_get(Host, Lang); process([<<"change_password">>], #request{method = 'GET', lang = Lang, host = Host}) -> form_changepass_get(Host, Lang); process([<<"new">>], #request{method = 'POST', q = Q, ip = {Ip, _Port}, lang = Lang, host = _HTTPHost}) -> case form_new_post(Q) of {success, ok, {Username, Host, _Password}} -> Jid = jid:make(Username, Host), mod_register:send_registration_notifications(?MODULE, Jid, Ip), Text = (?T(<<"Your Jabber account was successfully " "created.">>)), {200, [], Text}; Error -> ErrorText = list_to_binary([?T(<<"There was an error creating the account: ">>), ?T(get_error_text(Error))]), {404, [], ErrorText} end; process([<<"delete">>], #request{method = 'POST', q = Q, lang = Lang, host = _HTTPHost}) -> case form_del_post(Q) of {atomic, ok} -> Text = (?T(<<"Your Jabber account was successfully " "deleted.">>)), {200, [], Text}; Error -> ErrorText = list_to_binary([?T(<<"There was an error deleting the account: ">>), ?T(get_error_text(Error))]), {404, [], ErrorText} end; %% TODO: Currently only the first vhost is usable. The web request record %% should include the host where the POST was sent. process([<<"change_password">>], #request{method = 'POST', q = Q, lang = Lang, host = _HTTPHost}) -> case form_changepass_post(Q) of {atomic, ok} -> Text = (?T(<<"The password of your Jabber account " "was successfully changed.">>)), {200, [], Text}; Error -> ErrorText = list_to_binary([?T(<<"There was an error changing the password: ">>), ?T(get_error_text(Error))]), {404, [], ErrorText} end; process(_Path, _Request) -> {404, [], "Not Found"}. %%%---------------------------------------------------------------------- %%% CSS %%%---------------------------------------------------------------------- serve_css() -> case css() of {ok, CSS} -> {200, [{<<"Content-Type">>, <<"text/css">>}, last_modified(), cache_control_public()], CSS}; error -> {404, [], "CSS not found"} end. last_modified() -> {<<"Last-Modified">>, <<"Mon, 25 Feb 2008 13:23:30 GMT">>}. cache_control_public() -> {<<"Cache-Control">>, <<"public">>}. -spec css() -> {ok, binary()} | error. css() -> Dir = misc:css_dir(), File = filename:join(Dir, "register.css"), case file:read_file(File) of {ok, Data} -> {ok, Data}; {error, Why} -> ?ERROR_MSG("failed to read ~s: ~s", [File, file:format_error(Why)]), error end. meta() -> ?XA(<<"meta">>, [{<<"name">>, <<"viewport">>}, {<<"content">>, <<"width=device-width, initial-scale=1">>}]). %%%---------------------------------------------------------------------- %%% Index page %%%---------------------------------------------------------------------- index_page(Lang) -> HeadEls = [meta(), ?XCT(<<"title">>, <<"Jabber Account Registration">>), ?XA(<<"link">>, [{<<"href">>, <<"/register/register.css">>}, {<<"type">>, <<"text/css">>}, {<<"rel">>, <<"stylesheet">>}])], Els = [?XACT(<<"h1">>, [{<<"class">>, <<"title">>}, {<<"style">>, <<"text-align:center;">>}], <<"Jabber Account Registration">>), ?XE(<<"ul">>, [?XE(<<"li">>, [?ACT(<<"new">>, <<"Register a Jabber account">>)]), ?XE(<<"li">>, [?ACT(<<"change_password">>, <<"Change Password">>)]), ?XE(<<"li">>, [?ACT(<<"delete">>, <<"Unregister a Jabber account">>)])])], {200, [{<<"Server">>, <<"ejabberd">>}, {<<"Content-Type">>, <<"text/html">>}], ejabberd_web:make_xhtml(HeadEls, Els)}. %%%---------------------------------------------------------------------- %%% Formulary new account GET %%%---------------------------------------------------------------------- form_new_get(Host, Lang, IP) -> CaptchaEls = build_captcha_li_list(Lang, IP), HeadEls = [meta(), ?XCT(<<"title">>, <<"Register a Jabber account">>), ?XA(<<"link">>, [{<<"href">>, <<"/register/register.css">>}, {<<"type">>, <<"text/css">>}, {<<"rel">>, <<"stylesheet">>}])], Els = [?XACT(<<"h1">>, [{<<"class">>, <<"title">>}, {<<"style">>, <<"text-align:center;">>}], <<"Register a Jabber account">>), ?XCT(<<"p">>, <<"This page allows to create a Jabber " "account in this Jabber server. Your " "JID (Jabber IDentifier) will be of the " "form: username@server. Please read carefully " "the instructions to fill correctly the " "fields.">>), ?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XE(<<"ol">>, ([?XE(<<"li">>, [?CT(<<"Username:">>), ?C(<<" ">>), ?INPUTS(<<"text">>, <<"username">>, <<"">>, <<"20">>), ?BR, ?XE(<<"ul">>, [?XCT(<<"li">>, <<"This is case insensitive: macbeth is " "the same that MacBeth and Macbeth.">>), ?XC(<<"li">>, <<(?T(<<"Characters not allowed:">>))/binary, " \" & ' / : < > @ ">>)])]), ?XE(<<"li">>, [?CT(<<"Server:">>), ?C(<<" ">>), ?INPUTS(<<"text">>, <<"host">>, Host, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"Password:">>), ?C(<<" ">>), ?INPUTS(<<"password">>, <<"password">>, <<"">>, <<"20">>), ?BR, ?XE(<<"ul">>, [?XCT(<<"li">>, <<"Don't tell your password to anybody, " "not even the administrators of the Jabber " "server.">>), ?XCT(<<"li">>, <<"You can later change your password using " "a Jabber client.">>), ?XCT(<<"li">>, <<"Some Jabber clients can store your password " "in the computer, but you should do this only " "in your personal computer for safety reasons.">>), ?XCT(<<"li">>, <<"Memorize your password, or write it " "in a paper placed in a safe place. In " "Jabber there isn't an automated way " "to recover your password if you forget " "it.">>)])]), ?XE(<<"li">>, [?CT(<<"Password Verification:">>), ?C(<<" ">>), ?INPUTS(<<"password">>, <<"password2">>, <<"">>, <<"20">>)])] ++ CaptchaEls ++ [?XE(<<"li">>, [?INPUTT(<<"submit">>, <<"register">>, <<"Register">>)])]))])], {200, [{<<"Server">>, <<"ejabberd">>}, {<<"Content-Type">>, <<"text/html">>}], ejabberd_web:make_xhtml(HeadEls, Els)}. %% Copied from mod_register.erl %% Function copied from ejabberd_logger_h.erl and customized %%%---------------------------------------------------------------------- %%% Formulary new POST %%%---------------------------------------------------------------------- form_new_post(Q) -> case catch get_register_parameters(Q) of [Username, Host, Password, Password, Id, Key] -> form_new_post(Username, Host, Password, {Id, Key}); [_Username, _Host, _Password, _Password2, false, false] -> {error, passwords_not_identical}; [_Username, _Host, _Password, _Password2, Id, Key] -> ejabberd_captcha:check_captcha(Id, Key), {error, passwords_not_identical}; _ -> {error, wrong_parameters} end. get_register_parameters(Q) -> lists:map(fun (Key) -> case lists:keysearch(Key, 1, Q) of {value, {_Key, Value}} -> Value; false -> false end end, [<<"username">>, <<"host">>, <<"password">>, <<"password2">>, <<"id">>, <<"key">>]). form_new_post(Username, Host, Password, {false, false}) -> register_account(Username, Host, Password); form_new_post(Username, Host, Password, {Id, Key}) -> case ejabberd_captcha:check_captcha(Id, Key) of captcha_valid -> register_account(Username, Host, Password); captcha_non_valid -> {error, captcha_non_valid}; captcha_not_found -> {error, captcha_non_valid} end. %%%---------------------------------------------------------------------- %%% Formulary Captcha support for new GET/POST %%%---------------------------------------------------------------------- build_captcha_li_list(Lang, IP) -> case ejabberd_captcha:is_feature_available() of true -> build_captcha_li_list2(Lang, IP); false -> [] end. build_captcha_li_list2(Lang, IP) -> SID = <<"">>, From = #jid{user = <<"">>, server = <<"test">>, resource = <<"">>}, To = #jid{user = <<"">>, server = <<"test">>, resource = <<"">>}, Args = [], case ejabberd_captcha:create_captcha(SID, From, To, Lang, IP, Args) of {ok, Id, _, _} -> {_, {CImg, CText, CId, CKey}} = ejabberd_captcha:build_captcha_html(Id, Lang), [?XE(<<"li">>, [CText, ?C(<<" ">>), CId, CKey, ?BR, CImg])]; _ -> [] end. %%%---------------------------------------------------------------------- %%% Formulary change password GET %%%---------------------------------------------------------------------- form_changepass_get(Host, Lang) -> HeadEls = [meta(), ?XCT(<<"title">>, <<"Change Password">>), ?XA(<<"link">>, [{<<"href">>, <<"/register/register.css">>}, {<<"type">>, <<"text/css">>}, {<<"rel">>, <<"stylesheet">>}])], Els = [?XACT(<<"h1">>, [{<<"class">>, <<"title">>}, {<<"style">>, <<"text-align:center;">>}], <<"Change Password">>), ?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XE(<<"ol">>, [?XE(<<"li">>, [?CT(<<"Username:">>), ?C(<<" ">>), ?INPUTS(<<"text">>, <<"username">>, <<"">>, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"Server:">>), ?C(<<" ">>), ?INPUTS(<<"text">>, <<"host">>, Host, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"Old Password:">>), ?C(<<" ">>), ?INPUTS(<<"password">>, <<"passwordold">>, <<"">>, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"New Password:">>), ?C(<<" ">>), ?INPUTS(<<"password">>, <<"password">>, <<"">>, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"Password Verification:">>), ?C(<<" ">>), ?INPUTS(<<"password">>, <<"password2">>, <<"">>, <<"20">>)]), ?XE(<<"li">>, [?INPUTT(<<"submit">>, <<"changepass">>, <<"Change Password">>)])])])], {200, [{<<"Server">>, <<"ejabberd">>}, {<<"Content-Type">>, <<"text/html">>}], ejabberd_web:make_xhtml(HeadEls, Els)}. %%%---------------------------------------------------------------------- %%% Formulary change password POST %%%---------------------------------------------------------------------- form_changepass_post(Q) -> case catch get_changepass_parameters(Q) of [Username, Host, PasswordOld, Password, Password] -> try_change_password(Username, Host, PasswordOld, Password); [_Username, _Host, _PasswordOld, _Password, _Password2] -> {error, passwords_not_identical}; _ -> {error, wrong_parameters} end. get_changepass_parameters(Q) -> %% @spec(Username,Host,PasswordOld,Password) -> {atomic, ok} | %% {error, account_doesnt_exist} | %% {error, password_not_changed} | %% {error, password_incorrect} lists:map(fun (Key) -> {value, {_Key, Value}} = lists:keysearch(Key, 1, Q), Value end, [<<"username">>, <<"host">>, <<"passwordold">>, <<"password">>, <<"password2">>]). try_change_password(Username, Host, PasswordOld, Password) -> try change_password(Username, Host, PasswordOld, Password) of {atomic, ok} -> {atomic, ok} catch error:{badmatch, Error} -> {error, Error} end. change_password(Username, Host, PasswordOld, Password) -> account_exists = check_account_exists(Username, Host), password_correct = check_password(Username, Host, PasswordOld), ok = ejabberd_auth:set_password(Username, Host, Password), case check_password(Username, Host, Password) of password_correct -> {atomic, ok}; password_incorrect -> {error, password_not_changed} end. check_account_exists(Username, Host) -> case ejabberd_auth:user_exists(Username, Host) of true -> account_exists; false -> account_doesnt_exist end. check_password(Username, Host, Password) -> case ejabberd_auth:check_password(Username, <<"">>, Host, Password) of true -> password_correct; false -> password_incorrect end. %%%---------------------------------------------------------------------- %%% Formulary delete account GET %%%---------------------------------------------------------------------- form_del_get(Host, Lang) -> HeadEls = [meta(), ?XCT(<<"title">>, <<"Unregister a Jabber account">>), ?XA(<<"link">>, [{<<"href">>, <<"/register/register.css">>}, {<<"type">>, <<"text/css">>}, {<<"rel">>, <<"stylesheet">>}])], Els = [?XACT(<<"h1">>, [{<<"class">>, <<"title">>}, {<<"style">>, <<"text-align:center;">>}], <<"Unregister a Jabber account">>), ?XCT(<<"p">>, <<"This page allows to unregister a Jabber " "account in this Jabber server.">>), ?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XE(<<"ol">>, [?XE(<<"li">>, [?CT(<<"Username:">>), ?C(<<" ">>), ?INPUTS(<<"text">>, <<"username">>, <<"">>, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"Server:">>), ?C(<<" ">>), ?INPUTS(<<"text">>, <<"host">>, Host, <<"20">>)]), ?XE(<<"li">>, [?CT(<<"Password:">>), ?C(<<" ">>), ?INPUTS(<<"password">>, <<"password">>, <<"">>, <<"20">>)]), ?XE(<<"li">>, [?INPUTT(<<"submit">>, <<"unregister">>, <<"Unregister">>)])])])], {200, [{<<"Server">>, <<"ejabberd">>}, {<<"Content-Type">>, <<"text/html">>}], ejabberd_web:make_xhtml(HeadEls, Els)}. %% @spec(Username, Host, Password) -> {success, ok, {Username, Host, Password} | %% {success, exists, {Username, Host, Password}} | %% {error, not_allowed} | %% {error, invalid_jid} register_account(Username, Host, Password) -> Access = gen_mod:get_module_opt(Host, mod_register, access, all), case jid:make(Username, Host) of error -> {error, invalid_jid}; JID -> case acl:match_rule(Host, Access, JID) of deny -> {error, not_allowed}; allow -> register_account2(Username, Host, Password) end end. register_account2(Username, Host, Password) -> case ejabberd_auth:try_register(Username, Host, Password) of ok -> {success, ok, {Username, Host, Password}}; Other -> Other end. %%%---------------------------------------------------------------------- %%% Formulary delete POST %%%---------------------------------------------------------------------- form_del_post(Q) -> case catch get_unregister_parameters(Q) of [Username, Host, Password] -> try_unregister_account(Username, Host, Password); _ -> {error, wrong_parameters} end. get_unregister_parameters(Q) -> %% @spec(Username, Host, Password) -> {atomic, ok} | %% {error, account_doesnt_exist} | %% {error, account_exists} | %% {error, password_incorrect} lists:map(fun (Key) -> {value, {_Key, Value}} = lists:keysearch(Key, 1, Q), Value end, [<<"username">>, <<"host">>, <<"password">>]). try_unregister_account(Username, Host, Password) -> try unregister_account(Username, Host, Password) of {atomic, ok} -> {atomic, ok} catch error:{badmatch, Error} -> {error, Error} end. unregister_account(Username, Host, Password) -> account_exists = check_account_exists(Username, Host), password_correct = check_password(Username, Host, Password), ok = ejabberd_auth:remove_user(Username, Host, Password), account_doesnt_exist = check_account_exists(Username, Host), {atomic, ok}. %%%---------------------------------------------------------------------- %%% Error texts %%%---------------------------------------------------------------------- get_error_text({error, captcha_non_valid}) -> <<"The captcha you entered is wrong">>; get_error_text({success, exists, _}) -> get_error_text({atomic, exists}); get_error_text({atomic, exists}) -> <<"The account already exists">>; get_error_text({error, password_incorrect}) -> <<"Incorrect password">>; get_error_text({error, invalid_jid}) -> <<"The username is not valid">>; get_error_text({error, not_allowed}) -> <<"Not allowed">>; get_error_text({error, account_doesnt_exist}) -> <<"Account doesn't exist">>; get_error_text({error, account_exists}) -> <<"The account was not deleted">>; get_error_text({error, password_not_changed}) -> <<"The password was not changed">>; get_error_text({error, passwords_not_identical}) -> <<"The passwords are different">>; get_error_text({error, wrong_parameters}) -> <<"Wrong parameters in the web formulary">>. mod_opt_type(_) -> []. ejabberd-18.01/src/mod_sic.erl0000644000232200023220000001036113225664356016554 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_sic.erl %%% Author : Karim Gemayel %%% Purpose : XEP-0279 Server IP Check %%% Created : 6 Mar 2010 by Karim Gemayel %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_sic). -protocol({xep, 279, '0.2'}). -author('karim.gemayel@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_local_iq/1, process_sm_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_0, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_0, ?MODULE, process_sm_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_1, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_1, ?MODULE, process_sm_iq, IQDisc). stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_0), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_0), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_1), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_1). reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_0, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_0, ?MODULE, process_sm_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_1, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_1, ?MODULE, process_sm_iq, IQDisc); true -> ok end. depends(_Host, _Opts) -> []. process_local_iq(#iq{from = #jid{user = User, server = Server, resource = Resource}, type = get} = IQ) -> get_ip({User, Server, Resource}, IQ); process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). process_sm_iq(#iq{from = #jid{user = User, server = Server, resource = Resource}, to = #jid{user = User, server = Server}, type = get} = IQ) -> get_ip({User, Server, Resource}, IQ); process_sm_iq(#iq{type = get, lang = Lang} = IQ) -> Txt = <<"Query to another users is forbidden">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); process_sm_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). get_ip({User, Server, Resource}, #iq{lang = Lang, sub_els = [#sic{xmlns = NS}]} = IQ) -> case ejabberd_sm:get_user_ip(User, Server, Resource) of {IP, Port} when is_tuple(IP) -> Result = case NS of ?NS_SIC_0 -> #sic{ip = IP, xmlns = NS}; ?NS_SIC_1 -> #sic{ip = IP, port = Port, xmlns = NS} end, xmpp:make_iq_result(IQ, Result); _ -> Txt = <<"User session not found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) end. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(_) -> [iqdisc]. ejabberd-18.01/src/mod_proxy65_redis.erl0000644000232200023220000001241713225664356020524 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 31 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_proxy65_redis). -behaviour(mod_proxy65). %% API -export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -record(proxy65, {pid_t :: pid(), pid_i :: pid() | undefined, jid_i :: binary() | undefined}). %%%=================================================================== %%% API %%%=================================================================== init() -> ?DEBUG("Cleaning Redis 'proxy65' table...", []), NodeKey = node_key(), case ejabberd_redis:smembers(NodeKey) of {ok, SIDs} -> SIDKeys = [sid_key(S) || S <- SIDs], JIDs = lists:flatmap( fun(SIDKey) -> case ejabberd_redis:get(SIDKey) of {ok, Val} -> try binary_to_term(Val) of #proxy65{jid_i = J} when is_binary(J) -> [jid_key(J)]; _ -> [] catch _:badarg -> [] end; _ -> [] end end, SIDKeys), ejabberd_redis:multi( fun() -> if SIDs /= [] -> ejabberd_redis:del(SIDKeys), if JIDs /= [] -> ejabberd_redis:del(JIDs); true -> ok end; true -> ok end, ejabberd_redis:del([NodeKey]) end), ok; {error, _} -> {error, db_failure} end. register_stream(SID, Pid) -> SIDKey = sid_key(SID), try {ok, Val} = ejabberd_redis:get(SIDKey), try binary_to_term(Val) of #proxy65{pid_i = undefined} = R -> NewVal = term_to_binary(R#proxy65{pid_i = Pid}), ok = ejabberd_redis:set(SIDKey, NewVal); _ -> {error, conflict} catch _:badarg when Val == undefined -> NewVal = term_to_binary(#proxy65{pid_t = Pid}), {ok, _} = ejabberd_redis:multi( fun() -> ejabberd_redis:set(SIDKey, NewVal), ejabberd_redis:sadd(node_key(), [SID]) end), ok; _:badarg -> ?ERROR_MSG("malformed data in redis (key = '~s'): ~p", [SIDKey, Val]), {error, db_failure} end catch _:{badmatch, {error, _}} -> {error, db_failure} end. unregister_stream(SID) -> SIDKey = sid_key(SID), NodeKey = node_key(), try {ok, Val} = ejabberd_redis:get(SIDKey), try binary_to_term(Val) of #proxy65{jid_i = JID} when is_binary(JID) -> JIDKey = jid_key(JID), {ok, _} = ejabberd_redis:multi( fun() -> ejabberd_redis:del([SIDKey]), ejabberd_redis:srem(JIDKey, [SID]), ejabberd_redis:srem(NodeKey, [SID]) end), ok; _ -> {ok, _} = ejabberd_redis:multi( fun() -> ejabberd_redis:del([SIDKey]), ejabberd_redis:srem(NodeKey, [SID]) end), ok catch _:badarg when Val == undefined -> ok; _:badarg -> ?ERROR_MSG("malformed data in redis (key = '~s'): ~p", [SIDKey, Val]), {error, db_failure} end catch _:{badmatch, {error, _}} -> {error, db_failure} end. activate_stream(SID, IJID, MaxConnections, _Node) -> SIDKey = sid_key(SID), JIDKey = jid_key(IJID), try {ok, Val} = ejabberd_redis:get(SIDKey), try binary_to_term(Val) of #proxy65{pid_t = TPid, pid_i = IPid, jid_i = undefined} = R when is_pid(IPid) -> {ok, Num} = ejabberd_redis:scard(JIDKey), if Num >= MaxConnections -> {error, {limit, IPid, TPid}}; true -> NewVal = term_to_binary(R#proxy65{jid_i = IJID}), {ok, _} = ejabberd_redis:multi( fun() -> ejabberd_redis:sadd(JIDKey, [SID]), ejabberd_redis:set(SIDKey, NewVal) end), {ok, IPid, TPid} end; #proxy65{jid_i = JID} when is_binary(JID) -> {error, conflict}; _ -> {error, notfound} catch _:badarg when Val == undefined -> {error, notfound}; _:badarg -> ?ERROR_MSG("malformed data in redis (key = '~s'): ~p", [SIDKey, Val]), {error, db_failure} end catch _:{badmatch, {error, _}} -> {error, db_failure} end. %%%=================================================================== %%% Internal functions %%%=================================================================== sid_key(SID) -> <<"ejabberd:proxy65:sid:", SID/binary>>. jid_key(JID) -> <<"ejabberd:proxy65:initiator:", JID/binary>>. node_key() -> Node = erlang:atom_to_binary(node(), latin1), <<"ejabberd:proxy65:node:", Node/binary>>. ejabberd-18.01/src/node_public.erl0000644000232200023220000001333013225664356017421 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_public.erl %%% Author : Christophe Romain %%% Purpose : %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_public). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost). options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, {purge_offline, false}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, open}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, never}, {deliver_notifications, true}, {presence_based_delivery, false}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"delete-nodes">>, <<"delete-items">>, <<"instant-nodes">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"multi-items">>, <<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>, <<"subscription-notifications">>]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Removed) -> node_flat:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). ejabberd-18.01/src/mod_announce_riak.erl0000644000232200023220000000655613225664356020625 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_announce_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_announce_riak). -behaviour(mod_announce). %% API -export([init/2, set_motd_users/2, set_motd/2, delete_motd/1, get_motd/1, is_motd_user/2, set_motd_user/2, import/3]). -include("xmpp.hrl"). -include("mod_announce.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. set_motd_users(_LServer, USRs) -> try lists:foreach( fun({U, S, _R}) -> ok = ejabberd_riak:put(#motd_users{us = {U, S}}, motd_users_schema(), [{'2i', [{<<"server">>, S}]}]) end, USRs) catch _:{badmatch, {error, _} = Err} -> Err end. set_motd(LServer, Packet) -> ejabberd_riak:put(#motd{server = LServer, packet = Packet}, motd_schema()). delete_motd(LServer) -> try ok = ejabberd_riak:delete(motd, LServer), ok = ejabberd_riak:delete_by_index(motd_users, <<"server">>, LServer) catch _:{badmatch, {error, _} = Err} -> Err end. get_motd(LServer) -> case ejabberd_riak:get(motd, motd_schema(), LServer) of {ok, #motd{packet = Packet}} -> {ok, Packet}; {error, notfound} -> error; {error, _} = Err -> Err end. is_motd_user(LUser, LServer) -> case ejabberd_riak:get(motd_users, motd_users_schema(), {LUser, LServer}) of {ok, #motd_users{}} -> {ok, true}; {error, notfound} -> {ok, false}; {error, _} = Err -> Err end. set_motd_user(LUser, LServer) -> ejabberd_riak:put( #motd_users{us = {LUser, LServer}}, motd_users_schema(), [{'2i', [{<<"server">>, LServer}]}]). import(LServer, <<"motd">>, [<<>>, XML, _TimeStamp]) -> El = fxml_stream:parse_element(XML), ejabberd_riak:put(#motd{server = LServer, packet = El}, motd_schema()); import(LServer, <<"motd">>, [LUser, <<>>, _TimeStamp]) -> Users = #motd_users{us = {LUser, LServer}}, ejabberd_riak:put(Users, motd_users_schema(), [{'2i', [{<<"server">>, LServer}]}]). %%%=================================================================== %%% Internal functions %%%=================================================================== motd_schema() -> {record_info(fields, motd), #motd{}}. motd_users_schema() -> {record_info(fields, motd_users), #motd_users{}}. ejabberd-18.01/src/ejabberd_backend_sup.erl0000644000232200023220000000334613225664356021240 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 24 Feb 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_backend_sup). -behaviour(supervisor). %% API -export([start_link/0]). %% Supervisor callbacks -export([init/1]). %%%=================================================================== %%% API functions %%%=================================================================== start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). %%%=================================================================== %%% Supervisor callbacks %%%=================================================================== init([]) -> {ok, {{one_for_one, 10, 1}, []}}. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/mod_sip.erl0000644000232200023220000002630713225664356016600 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_sip.erl %%% Author : Evgeny Khramtsov %%% Purpose : SIP RFC-3261 %%% Created : 21 Apr 2014 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2014-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_sip). -protocol({rfc, 3261}). -include("logger.hrl"). -ifndef(SIP). -export([start/2, stop/1, depends/2, mod_opt_type/1]). start(_, _) -> ?CRITICAL_MSG("ejabberd is not compiled with SIP support", []), {error, sip_not_compiled}. stop(_) -> ok. depends(_, _) -> []. mod_opt_type(_) -> []. -else. -behaviour(gen_mod). -behaviour(esip). %% API -export([start/2, stop/1, reload/3, make_response/2, is_my_host/1, at_my_host/1]). -export([data_in/2, data_out/2, message_in/2, message_out/2, request/2, request/3, response/2, locate/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include_lib("esip/include/esip.hrl"). %%%=================================================================== %%% API %%%=================================================================== start(_Host, _Opts) -> ejabberd:start_app(esip), esip:set_config_value(max_server_transactions, 10000), esip:set_config_value(max_client_transactions, 10000), esip:set_config_value(software, <<"ejabberd ", (?VERSION)/binary>>), esip:set_config_value(module, ?MODULE), Spec = {mod_sip_registrar, {mod_sip_registrar, start_link, []}, transient, 2000, worker, [mod_sip_registrar]}, TmpSupSpec = {mod_sip_proxy_sup, {ejabberd_tmp_sup, start_link, [mod_sip_proxy_sup, mod_sip_proxy]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, supervisor:start_child(ejabberd_gen_mod_sup, Spec), supervisor:start_child(ejabberd_gen_mod_sup, TmpSupSpec), ok. stop(_Host) -> ok. reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> []. data_in(Data, #sip_socket{type = Transport, addr = {MyIP, MyPort}, peer = {PeerIP, PeerPort}}) -> ?DEBUG( "SIP [~p/in] ~s:~p -> ~s:~p:~n~s", [Transport, inet_parse:ntoa(PeerIP), PeerPort, inet_parse:ntoa(MyIP), MyPort, Data]). data_out(Data, #sip_socket{type = Transport, addr = {MyIP, MyPort}, peer = {PeerIP, PeerPort}}) -> ?DEBUG( "SIP [~p/out] ~s:~p -> ~s:~p:~n~s", [Transport, inet_parse:ntoa(MyIP), MyPort, inet_parse:ntoa(PeerIP), PeerPort, Data]). message_in(#sip{type = request, method = M} = Req, SIPSock) when M /= <<"ACK">>, M /= <<"CANCEL">> -> case action(Req, SIPSock) of {relay, _LServer} -> ok; Action -> request(Req, SIPSock, undefined, Action) end; message_in(ping, SIPSock) -> mod_sip_registrar:ping(SIPSock); message_in(_, _) -> ok. message_out(_, _) -> ok. response(_Resp, _SIPSock) -> ok. request(#sip{method = <<"ACK">>} = Req, SIPSock) -> case action(Req, SIPSock) of {relay, LServer} -> mod_sip_proxy:route(Req, LServer, [{authenticated, true}]); {proxy_auth, LServer} -> mod_sip_proxy:route(Req, LServer, [{authenticated, false}]); _ -> ok end; request(_Req, _SIPSock) -> ok. request(Req, SIPSock, TrID) -> request(Req, SIPSock, TrID, action(Req, SIPSock)). request(Req, SIPSock, TrID, Action) -> case Action of to_me -> process(Req, SIPSock); register -> mod_sip_registrar:request(Req, SIPSock); loop -> make_response(Req, #sip{status = 483, type = response}); {unsupported, Require} -> make_response(Req, #sip{status = 420, type = response, hdrs = [{'unsupported', Require}]}); {relay, LServer} -> case mod_sip_proxy:start(LServer, []) of {ok, Pid} -> mod_sip_proxy:route(Req, SIPSock, TrID, Pid), {mod_sip_proxy, route, [Pid]}; Err -> ?INFO_MSG("failed to proxy request ~p: ~p", [Req, Err]), Err end; {proxy_auth, LServer} -> make_response( Req, #sip{status = 407, type = response, hdrs = [{'proxy-authenticate', make_auth_hdr(LServer)}]}); {auth, LServer} -> make_response( Req, #sip{status = 401, type = response, hdrs = [{'www-authenticate', make_auth_hdr(LServer)}]}); deny -> make_response(Req, #sip{status = 403, type = response}); not_found -> make_response(Req, #sip{status = 480, type = response}) end. locate(_SIPMsg) -> ok. find(#uri{user = User, host = Host}) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Host), if LUser == <<"">> -> to_me; true -> case mod_sip_registrar:find_sockets(LUser, LServer) of [] -> not_found; [_|_] -> {relay, LServer} end end. %%%=================================================================== %%% Internal functions %%%=================================================================== action(#sip{method = <<"REGISTER">>, type = request, hdrs = Hdrs, uri = #uri{user = <<"">>} = URI} = Req, SIPSock) -> case at_my_host(URI) of true -> Require = esip:get_hdrs('require', Hdrs) -- supported(), case Require of [_|_] -> {unsupported, Require}; _ -> {_, ToURI, _} = esip:get_hdr('to', Hdrs), case at_my_host(ToURI) of true -> case check_auth(Req, 'authorization', SIPSock) of true -> register; false -> {auth, jid:nameprep(ToURI#uri.host)} end; false -> deny end end; false -> deny end; action(#sip{method = Method, hdrs = Hdrs, type = request} = Req, SIPSock) -> case esip:get_hdr('max-forwards', Hdrs) of 0 when Method == <<"OPTIONS">> -> to_me; 0 -> loop; _ -> Require = esip:get_hdrs('proxy-require', Hdrs) -- supported(), case Require of [_|_] -> {unsupported, Require}; _ -> {_, ToURI, _} = esip:get_hdr('to', Hdrs), {_, FromURI, _} = esip:get_hdr('from', Hdrs), case at_my_host(FromURI) of true -> case check_auth(Req, 'proxy-authorization', SIPSock) of true -> case at_my_host(ToURI) of true -> find(ToURI); false -> LServer = jid:nameprep(FromURI#uri.host), {relay, LServer} end; false -> {proxy_auth, FromURI#uri.host} end; false -> case at_my_host(ToURI) of true -> find(ToURI); false -> deny end end end end. check_auth(#sip{method = <<"CANCEL">>}, _, _SIPSock) -> true; check_auth(#sip{method = Method, hdrs = Hdrs, body = Body}, AuthHdr, _SIPSock) -> Issuer = case AuthHdr of 'authorization' -> to; 'proxy-authorization' -> from end, {_, #uri{user = User, host = Host}, _} = esip:get_hdr(Issuer, Hdrs), LUser = jid:nodeprep(User), LServer = jid:nameprep(Host), case lists:filter( fun({_, Params}) -> Username = esip:get_param(<<"username">>, Params), Realm = esip:get_param(<<"realm">>, Params), (LUser == esip:unquote(Username)) and (LServer == esip:unquote(Realm)) end, esip:get_hdrs(AuthHdr, Hdrs)) of [Auth|_] -> case ejabberd_auth:get_password_s(LUser, LServer) of <<"">> -> false; Password when is_binary(Password) -> esip:check_auth(Auth, Method, Body, Password); _ScramedPassword -> ?ERROR_MSG("unable to authenticate ~s@~s against SCRAM'ed " "password", [LUser, LServer]), false end; [] -> false end. allow() -> [<<"OPTIONS">>, <<"REGISTER">>]. supported() -> [<<"path">>, <<"outbound">>]. process(#sip{method = <<"OPTIONS">>} = Req, _) -> make_response(Req, #sip{type = response, status = 200, hdrs = [{'allow', allow()}, {'supported', supported()}]}); process(#sip{method = <<"REGISTER">>} = Req, _) -> make_response(Req, #sip{type = response, status = 400}); process(Req, _) -> make_response(Req, #sip{type = response, status = 405, hdrs = [{'allow', allow()}]}). make_auth_hdr(LServer) -> {<<"Digest">>, [{<<"realm">>, esip:quote(LServer)}, {<<"qop">>, esip:quote(<<"auth">>)}, {<<"nonce">>, esip:quote(esip:make_hexstr(20))}]}. make_response(Req, Resp) -> esip:make_response(Req, Resp, esip:make_tag()). at_my_host(#uri{host = Host}) -> is_my_host(jid:nameprep(Host)). is_my_host(LServer) -> gen_mod:is_loaded(LServer, ?MODULE). mod_opt_type(always_record_route) -> fun (true) -> true; (false) -> false end; mod_opt_type(flow_timeout_tcp) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(flow_timeout_udp) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(record_route) -> fun (IOList) -> S = iolist_to_binary(IOList), #uri{} = esip:decode_uri(S) end; mod_opt_type(routes) -> fun (L) -> lists:map(fun (IOList) -> S = iolist_to_binary(IOList), #uri{} = esip:decode_uri(S) end, L) end; mod_opt_type(via) -> fun (L) -> lists:map(fun (Opts) -> Type = proplists:get_value(type, Opts), Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), true = (Type == tcp) or (Type == tls) or (Type == udp), true = is_binary(Host) and (Host /= <<"">>), true = is_integer(Port) and (Port > 0) and (Port < 65536) or (Port == undefined), {Type, {Host, Port}} end, L) end; mod_opt_type(_) -> [always_record_route, flow_timeout_tcp, flow_timeout_udp, record_route, routes, via]. -endif. ejabberd-18.01/src/mod_mam_sql.erl0000644000232200023220000003414313225664356017433 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_mam_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_mam_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_mam). %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1]). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). -include("mod_mam.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -ifdef(NEW_SQL_SCHEMA). -define(USE_NEW_SCHEMA, true). -else. -define(USE_NEW_SCHEMA, false). -endif. %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. remove_user(LUser, LServer) -> ejabberd_sql:sql_query( LServer, ?SQL("delete from archive where username=%(LUser)s and %(LServer)H")), ejabberd_sql:sql_query( LServer, ?SQL("delete from archive_prefs where username=%(LUser)s and %(LServer)H")). remove_room(LServer, LName, LHost) -> LUser = jid:encode({LName, LHost, <<>>}), remove_user(LUser, LServer). delete_old_messages(ServerHost, TimeStamp, Type) -> TS = now_to_usec(TimeStamp), case Type of all -> ejabberd_sql:sql_query( ServerHost, ?SQL("delete from archive" " where timestamp < %(TS)d and %(ServerHost)H")); _ -> SType = misc:atom_to_binary(Type), ejabberd_sql:sql_query( ServerHost, ?SQL("delete from archive" " where timestamp < %(TS)d" " and kind=%(SType)s" " and %(ServerHost)H")) end, ok. extended_fields() -> [{withtext, <<"">>}]. store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) -> SUser = case Type of chat -> LUser; groupchat -> jid:encode({LUser, LHost, <<>>}) end, BarePeer = jid:encode( jid:tolower( jid:remove_resource(Peer))), LPeer = jid:encode( jid:tolower(Peer)), XML = fxml:element_to_binary(Pkt), Body = fxml:get_subtag_cdata(Pkt, <<"body">>), SType = misc:atom_to_binary(Type), case ejabberd_sql:sql_query( LServer, ?SQL_INSERT( "archive", ["username=%(SUser)s", "server_host=%(LServer)s", "timestamp=%(TS)d", "peer=%(LPeer)s", "bare_peer=%(BarePeer)s", "xml=%(XML)s", "txt=%(Body)s", "kind=%(SType)s", "nick=%(Nick)s"])) of {updated, _} -> ok; Err -> Err end. write_prefs(LUser, _LServer, #archive_prefs{default = Default, never = Never, always = Always}, ServerHost) -> SDefault = erlang:atom_to_binary(Default, utf8), SAlways = misc:term_to_expr(Always), SNever = misc:term_to_expr(Never), case ?SQL_UPSERT( ServerHost, "archive_prefs", ["!username=%(LUser)s", "!server_host=%(ServerHost)s", "def=%(SDefault)s", "always=%(SAlways)s", "never=%(SNever)s"]) of ok -> ok; Err -> Err end. get_prefs(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(def)s, @(always)s, @(never)s from archive_prefs" " where username=%(LUser)s and %(LServer)H")) of {selected, [{SDefault, SAlways, SNever}]} -> Default = erlang:binary_to_existing_atom(SDefault, utf8), Always = ejabberd_sql:decode_term(SAlways), Never = ejabberd_sql:decode_term(SNever), {ok, #archive_prefs{us = {LUser, LServer}, default = Default, always = Always, never = Never}}; _ -> error end. select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive, MAMQuery, RSM, MsgType) -> User = case MsgType of chat -> LUser; {groupchat, _Role, _MUCState} -> jid:encode(JidArchive) end, {Query, CountQuery} = make_sql_query(User, LServer, MAMQuery, RSM), % TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a % reasonable limit on how many stanzas may be pushed to a client in one % request. If a query returns a number of stanzas greater than this limit % and the client did not specify a limit using RSM then the server should % return a policy-violation error to the client." We currently don't do this % for v0.2 requests, but we do limit #rsm_in.max for v0.3 and newer. case {ejabberd_sql:sql_query(LServer, Query), ejabberd_sql:sql_query(LServer, CountQuery)} of {{selected, _, Res}, {selected, _, [[Count]]}} -> {Max, Direction, _} = get_max_direction_id(RSM), {Res1, IsComplete} = if Max >= 0 andalso Max /= undefined andalso length(Res) > Max -> if Direction == before -> {lists:nthtail(1, Res), false}; true -> {lists:sublist(Res, Max), false} end; true -> {Res, true} end, {lists:flatmap( fun([TS, XML, PeerBin, Kind, Nick]) -> case make_archive_el( TS, XML, PeerBin, Kind, Nick, MsgType, JidRequestor, JidArchive) of {ok, El} -> [{TS, binary_to_integer(TS), El}]; {error, _} -> [] end end, Res1), IsComplete, binary_to_integer(Count)}; _ -> {[], false, 0} end. export(_Server) -> [{archive_prefs, fun(Host, #archive_prefs{us = {LUser, LServer}, default = Default, always = Always, never = Never}) when LServer == Host -> SDefault = erlang:atom_to_binary(Default, utf8), SAlways = misc:term_to_expr(Always), SNever = misc:term_to_expr(Never), [?SQL_INSERT( "archive_prefs", ["username=%(LUser)s", "server_host=%(LServer)s", "def=%(SDefault)s", "always=%(SAlways)s", "never=%(SNever)s"])]; (_Host, _R) -> [] end}, {archive_msg, fun(Host, #archive_msg{us ={LUser, LServer}, id = _ID, timestamp = TS, peer = Peer, type = Type, nick = Nick, packet = Pkt}) when LServer == Host -> TStmp = now_to_usec(TS), SUser = case Type of chat -> LUser; groupchat -> jid:encode({LUser, LServer, <<>>}) end, BarePeer = jid:encode(jid:tolower(jid:remove_resource(Peer))), LPeer = jid:encode(jid:tolower(Peer)), XML = fxml:element_to_binary(Pkt), Body = fxml:get_subtag_cdata(Pkt, <<"body">>), SType = misc:atom_to_binary(Type), [?SQL_INSERT( "archive", ["username=%(SUser)s", "server_host=%(LServer)s", "timestamp=%(TStmp)d", "peer=%(LPeer)s", "bare_peer=%(BarePeer)s", "xml=%(XML)s", "txt=%(Body)s", "kind=%(SType)s", "nick=%(Nick)s"])]; (_Host, _R) -> [] end}]. %%%=================================================================== %%% Internal functions %%%=================================================================== now_to_usec({MSec, Sec, USec}) -> (MSec*1000000 + Sec)*1000000 + USec. usec_to_now(Int) -> Secs = Int div 1000000, USec = Int rem 1000000, MSec = Secs div 1000000, Sec = Secs rem 1000000, {MSec, Sec, USec}. make_sql_query(User, LServer, MAMQuery, RSM) -> Start = proplists:get_value(start, MAMQuery), End = proplists:get_value('end', MAMQuery), With = proplists:get_value(with, MAMQuery), WithText = proplists:get_value(withtext, MAMQuery), {Max, Direction, ID} = get_max_direction_id(RSM), ODBCType = ejabberd_config:get_option({sql_type, LServer}), Escape = case ODBCType of mssql -> fun ejabberd_sql:standard_escape/1; sqlite -> fun ejabberd_sql:standard_escape/1; _ -> fun ejabberd_sql:escape/1 end, LimitClause = if is_integer(Max), Max >= 0, ODBCType /= mssql -> [<<" limit ">>, integer_to_binary(Max+1)]; true -> [] end, TopClause = if is_integer(Max), Max >= 0, ODBCType == mssql -> [<<" TOP ">>, integer_to_binary(Max+1)]; true -> [] end, WithTextClause = if is_binary(WithText), WithText /= <<>> -> [<<" and match (txt) against ('">>, Escape(WithText), <<"')">>]; true -> [] end, WithClause = case catch jid:tolower(With) of {_, _, <<>>} -> [<<" and bare_peer='">>, Escape(jid:encode(With)), <<"'">>]; {_, _, _} -> [<<" and peer='">>, Escape(jid:encode(With)), <<"'">>]; _ -> [] end, PageClause = case catch binary_to_integer(ID) of I when is_integer(I), I >= 0 -> case Direction of before -> [<<" AND timestamp < ">>, ID]; 'after' -> [<<" AND timestamp > ">>, ID]; _ -> [] end; _ -> [] end, StartClause = case Start of {_, _, _} -> [<<" and timestamp >= ">>, integer_to_binary(now_to_usec(Start))]; _ -> [] end, EndClause = case End of {_, _, _} -> [<<" and timestamp <= ">>, integer_to_binary(now_to_usec(End))]; _ -> [] end, SUser = Escape(User), SServer = Escape(LServer), Query = case ?USE_NEW_SCHEMA of true -> [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick" " FROM archive WHERE username='">>, SUser, <<"' and server_host='">>, SServer, <<"'">>, WithClause, WithTextClause, StartClause, EndClause, PageClause]; false -> [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick" " FROM archive WHERE username='">>, SUser, <<"'">>, WithClause, WithTextClause, StartClause, EndClause, PageClause] end, QueryPage = case Direction of before -> % ID can be empty because of % XEP-0059: Result Set Management % 2.5 Requesting the Last Page in a Result Set [<<"SELECT timestamp, xml, peer, kind, nick FROM (">>, Query, <<" ORDER BY timestamp DESC ">>, LimitClause, <<") AS t ORDER BY timestamp ASC;">>]; _ -> [Query, <<" ORDER BY timestamp ASC ">>, LimitClause, <<";">>] end, case ?USE_NEW_SCHEMA of true -> {QueryPage, [<<"SELECT COUNT(*) FROM archive WHERE username='">>, SUser, <<"' and server_host='">>, SServer, <<"'">>, WithClause, WithTextClause, StartClause, EndClause, <<";">>]}; false -> {QueryPage, [<<"SELECT COUNT(*) FROM archive WHERE username='">>, SUser, <<"'">>, WithClause, WithTextClause, StartClause, EndClause, <<";">>]} end. -spec get_max_direction_id(rsm_set() | undefined) -> {integer() | undefined, before | 'after' | undefined, binary()}. get_max_direction_id(RSM) -> case RSM of #rsm_set{max = Max, before = Before} when is_binary(Before) -> {Max, before, Before}; #rsm_set{max = Max, 'after' = After} when is_binary(After) -> {Max, 'after', After}; #rsm_set{max = Max} -> {Max, undefined, <<>>}; _ -> {undefined, undefined, <<>>} end. -spec make_archive_el(binary(), binary(), binary(), binary(), binary(), _, jid(), jid()) -> {ok, xmpp_element()} | {error, invalid_jid | invalid_timestamp | invalid_xml}. make_archive_el(TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchive) -> case fxml_stream:parse_element(XML) of #xmlel{} = El -> try binary_to_integer(TS) of TSInt -> try jid:decode(Peer) of PeerJID -> Now = usec_to_now(TSInt), PeerLJID = jid:tolower(PeerJID), T = case Kind of <<"">> -> chat; null -> chat; _ -> misc:binary_to_atom(Kind) end, mod_mam:msg_to_el( #archive_msg{timestamp = Now, id = TS, packet = El, type = T, nick = Nick, peer = PeerLJID}, MsgType, JidRequestor, JidArchive) catch _:{bad_jid, _} -> ?ERROR_MSG("Malformed 'peer' field with value " "'~s' detected for user ~s in table " "'archive': invalid JID", [Peer, jid:encode(JidArchive)]), {error, invalid_jid} end catch _:_ -> ?ERROR_MSG("Malformed 'timestamp' field with value '~s' " "detected for user ~s in table 'archive': " "not an integer", [TS, jid:encode(JidArchive)]), {error, invalid_timestamp} end; {error, {_, Reason}} -> ?ERROR_MSG("Malformed 'xml' field with value '~s' detected " "for user ~s in table 'archive': ~s", [XML, jid:encode(JidArchive), Reason]), {error, invalid_xml} end. ejabberd-18.01/src/ejabberd_local.erl0000644000232200023220000001725213225664356020055 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_local.erl %%% Author : Alexey Shchepin %%% Purpose : Route local packets %%% Created : 30 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_local). -author('alexey@process-one.net'). -behaviour(gen_server). %% API -export([start/0, start_link/0]). -export([route/1, process_iq/1, get_features/1, register_iq_handler/5, unregister_iq_handler/2, bounce_resource_packet/1, host_up/1, host_down/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% deprecated functions: use ejabberd_router:route_iq/3,4 -export([route_iq/2, route_iq/3]). -deprecated([{route_iq, 2}, {route_iq, 3}]). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). -record(state, {}). -define(IQTABLE, local_iqtable). %%==================================================================== %% API %%==================================================================== %%-------------------------------------------------------------------- %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- start() -> ChildSpec = {?MODULE, {?MODULE, start_link, []}, transient, 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_sup, ChildSpec). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec process_iq(iq()) -> any(). process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet) when T == get; T == set -> XMLNS = xmpp:get_ns(El), Host = To#jid.lserver, case ets:lookup(?IQTABLE, {Host, XMLNS}) of [{_, Module, Function, Opts}] -> gen_iq_handler:handle(Host, Module, Function, Opts, Packet); [] -> Txt = <<"No module is handling this query">>, Err = xmpp:err_service_unavailable(Txt, Lang), ejabberd_router:route_error(Packet, Err) end; process_iq(#iq{type = T, lang = Lang, sub_els = SubEls} = Packet) when T == get; T == set -> Txt = case SubEls of [] -> <<"No child elements found">>; _ -> <<"Too many child elements">> end, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err); process_iq(#iq{type = T}) when T == result; T == error -> ok. -spec route(stanza()) -> any(). route(Packet) -> try do_route(Packet) catch E:R -> ?ERROR_MSG("failed to route packet:~n~s~nReason = ~p", [xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}]) end. -spec route_iq(iq(), function()) -> ok. route_iq(IQ, Fun) -> route_iq(IQ, Fun, undefined). -spec route_iq(iq(), function(), undefined | non_neg_integer()) -> ok. route_iq(IQ, Fun, Timeout) -> ejabberd_router:route_iq(IQ, Fun, undefined, Timeout). -spec register_iq_handler(binary(), binary(), module(), function(), gen_iq_handler:opts()) -> ok. register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> gen_server:cast(?MODULE, {register_iq_handler, Host, XMLNS, Module, Fun, Opts}). -spec unregister_iq_handler(binary(), binary()) -> ok. unregister_iq_handler(Host, XMLNS) -> gen_server:cast(?MODULE, {unregister_iq_handler, Host, XMLNS}). -spec bounce_resource_packet(stanza()) -> ok | stop. bounce_resource_packet(#presence{to = #jid{lresource = <<"">>}}) -> ok; bounce_resource_packet(#message{to = #jid{lresource = <<"">>}, type = headline}) -> ok; bounce_resource_packet(Packet) -> Lang = xmpp:get_lang(Packet), Txt = <<"No available resource found">>, Err = xmpp:err_item_not_found(Txt, Lang), ejabberd_router:route_error(Packet, Err), stop. -spec get_features(binary()) -> [binary()]. get_features(Host) -> get_features(ets:next(?IQTABLE, {Host, <<"">>}), Host, []). get_features({Host, XMLNS}, Host, XMLNSs) -> get_features(ets:next(?IQTABLE, {Host, XMLNS}), Host, [XMLNS|XMLNSs]); get_features(_, _, XMLNSs) -> XMLNSs. %%==================================================================== %% gen_server callbacks %%==================================================================== init([]) -> process_flag(trap_exit, true), lists:foreach(fun host_up/1, ?MYHOSTS), ejabberd_hooks:add(host_up, ?MODULE, host_up, 10), ejabberd_hooks:add(host_down, ?MODULE, host_down, 100), catch ets:new(?IQTABLE, [named_table, public, ordered_set, {read_concurrency, true}]), update_table(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) -> ets:insert(?IQTABLE, {{Host, XMLNS}, Module, Function, Opts}), {noreply, State}; handle_cast({unregister_iq_handler, Host, XMLNS}, State) -> case ets:lookup(?IQTABLE, {Host, XMLNS}) of [{_, Module, Function, Opts}] -> gen_iq_handler:stop_iq_handler(Module, Function, Opts); _ -> ok end, ets:delete(?IQTABLE, {Host, XMLNS}), {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. handle_info({route, Packet}, State) -> route(Packet), {noreply, State}; handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> lists:foreach(fun host_down/1, ?MYHOSTS), ejabberd_hooks:delete(host_up, ?MODULE, host_up, 10), ejabberd_hooks:delete(host_down, ?MODULE, host_down, 100), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -spec do_route(stanza()) -> any(). do_route(Packet) -> ?DEBUG("local route:~n~s", [xmpp:pp(Packet)]), Type = xmpp:get_type(Packet), To = xmpp:get_to(Packet), if To#jid.luser /= <<"">> -> ejabberd_sm:route(Packet); is_record(Packet, iq), To#jid.lresource == <<"">> -> process_iq(Packet); Type == result; Type == error -> ok; true -> ejabberd_hooks:run(local_send_to_resource_hook, To#jid.lserver, [Packet]) end. -spec update_table() -> ok. update_table() -> catch mnesia:delete_table(iq_response), ok. host_up(Host) -> Owner = case whereis(?MODULE) of undefined -> self(); Pid -> Pid end, ejabberd_router:register_route(Host, Host, {apply, ?MODULE, route}, Owner), ejabberd_hooks:add(local_send_to_resource_hook, Host, ?MODULE, bounce_resource_packet, 100). host_down(Host) -> Owner = case whereis(?MODULE) of undefined -> self(); Pid -> Pid end, ejabberd_router:unregister_route(Host, Owner), ejabberd_hooks:delete(local_send_to_resource_hook, Host, ?MODULE, bounce_resource_packet, 100). ejabberd-18.01/src/node_mix_sql.erl0000644000232200023220000001301613225664356017620 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : node_mix_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 8 Mar 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_mix_sql). -behaviour(gen_pubsub_node). %% API -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, get_entity_subscriptions_for_send_last/2]). -include("pubsub.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(Host, ServerHost, Opts) -> node_flat_sql:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_flat_sql:terminate(Host, ServerHost). options() -> [{sql, true}, {rsm, true} | node_mix:options()]. features() -> [<<"rsm">> | node_mix:features()]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat_sql:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat_sql:create_node(Nidx, Owner). delete_node(Removed) -> node_flat_sql:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat_sql:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat_sql:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat_sql:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat_sql:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat_sql:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat_sql:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat_sql:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat_sql:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat_sql:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat_sql:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat_sql:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat_sql:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat_sql:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat_sql:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat_sql:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat_sql:get_states(Nidx). get_state(Nidx, JID) -> node_flat_sql:get_state(Nidx, JID). set_state(State) -> node_flat_sql:set_state(State). get_items(Nidx, From, RSM) -> node_flat_sql:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat_sql:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat_sql:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat_sql:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat_sql:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat_sql:set_item(Item). get_item_name(Host, Node, Id) -> node_flat_sql:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat_sql:node_to_path(Node). path_to_node(Path) -> node_flat_sql:path_to_node(Path). get_entity_subscriptions_for_send_last(Host, Owner) -> node_flat_sql:get_entity_subscriptions_for_send_last(Host, Owner). %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/nodetree_tree.erl0000644000232200023220000001426613225664356017773 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : nodetree_tree.erl %%% Author : Christophe Romain %%% Purpose : Standard node tree plugin %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the default PubSub node tree plugin. %%%

It is used as a default for all unknown PubSub node type. It can serve %%% as a developer basis and reference to build its own custom pubsub node tree %%% types.

%%%

PubSub node tree plugins are using the {@link gen_nodetree} behaviour.

%%%

The API isn't stabilized yet. The pubsub plugin %%% development is still a work in progress. However, the system is already %%% useable and useful as is. Please, send us comments, feedback and %%% improvements.

-module(nodetree_tree). -behaviour(gen_pubsub_nodetree). -author('christophe.romain@process-one.net'). -include_lib("stdlib/include/qlc.hrl"). -include("pubsub.hrl"). -include("xmpp.hrl"). -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). init(_Host, _ServerHost, _Options) -> ejabberd_mnesia:create(?MODULE, pubsub_node, [{disc_copies, [node()]}, {attributes, record_info(fields, pubsub_node)}, {index, [id]}]), %% mnesia:transform_table(pubsub_state, ignore, StatesFields) ok. terminate(_Host, _ServerHost) -> ok. options() -> [{virtual_tree, false}]. set_node(Node) when is_record(Node, pubsub_node) -> mnesia:write(Node). get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node) -> case mnesia:read({pubsub_node, {Host, Node}}) of [Record] when is_record(Record, pubsub_node) -> Record; _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_node(Nidx) -> case mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of [Record] when is_record(Record, pubsub_node) -> Record; _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_nodes(Host, _From) -> get_nodes(Host). get_nodes(Host) -> mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}). get_parentnodes(Host, Node, _From) -> case catch mnesia:read({pubsub_node, {Host, Node}}) of [Record] when is_record(Record, pubsub_node) -> Record#pubsub_node.parents; _ -> [] end. get_parentnodes_tree(Host, Node, _From) -> get_parentnodes_tree(Host, Node, 0, []). get_parentnodes_tree(Host, Node, Level, Acc) -> case catch mnesia:read({pubsub_node, {Host, Node}}) of [Record] when is_record(Record, pubsub_node) -> Tree = [{Level, [Record]}|Acc], case Record#pubsub_node.parents of [Parent] -> get_parentnodes_tree(Host, Parent, Level+1, Tree); _ -> Tree end; _ -> Acc end. get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node). get_subnodes(Host, <<>>) -> Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _}, parents = Parents} = N <- mnesia:table(pubsub_node), Host == NHost, Parents == []]), qlc:e(Q); get_subnodes(Host, Node) -> Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _}, parents = Parents} = N <- mnesia:table(pubsub_node), Host == NHost, lists:member(Node, Parents)]), qlc:e(Q). get_subnodes_tree(Host, Node, _From) -> get_subnodes_tree(Host, Node). get_subnodes_tree(Host, Node) -> case get_node(Host, Node) of {error, _} -> []; Rec -> BasePlugin = misc:binary_to_atom(<<"node_", (Rec#pubsub_node.type)/binary>>), BasePath = BasePlugin:node_to_path(Node), mnesia:foldl(fun (#pubsub_node{nodeid = {H, N}} = R, Acc) -> Plugin = misc:binary_to_atom(<<"node_", (R#pubsub_node.type)/binary>>), Path = Plugin:node_to_path(N), case lists:prefix(BasePath, Path) and (H == Host) of true -> [R | Acc]; false -> Acc end end, [], pubsub_node) end. create_node(Host, Node, Type, Owner, Options, Parents) -> BJID = jid:tolower(jid:remove_resource(Owner)), case mnesia:read({pubsub_node, {Host, Node}}) of [] -> ParentExists = case Host of {_U, _S, _R} -> %% This is special case for PEP handling %% PEP does not uses hierarchy true; _ -> case Parents of [] -> true; [Parent | _] -> case catch mnesia:read({pubsub_node, {Host, Parent}}) of [#pubsub_node{owners = [{<<>>, Host, <<>>}]}] -> true; [#pubsub_node{owners = Owners}] -> lists:member(BJID, Owners); _ -> false end; _ -> false end end, case ParentExists of true -> Nidx = pubsub_index:new(node), mnesia:write(#pubsub_node{nodeid = {Host, Node}, id = Nidx, parents = Parents, type = Type, owners = [BJID], options = Options}), {ok, Nidx}; false -> {error, xmpp:err_forbidden()} end; _ -> {error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)} end. delete_node(Host, Node) -> Removed = get_subnodes_tree(Host, Node), lists:foreach(fun (#pubsub_node{nodeid = {_, SubNode}, id = SubNidx}) -> pubsub_index:free(node, SubNidx), mnesia:delete({pubsub_node, {Host, SubNode}}) end, Removed), Removed. ejabberd-18.01/src/ejabberd_sm_riak.erl0000644000232200023220000000475713225664356020416 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 15 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_sm_riak). -behaviour(ejabberd_sm). %% API -export([init/0, set_session/1, delete_session/1, get_sessions/0, get_sessions/1, get_sessions/2]). -include("ejabberd_sm.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init() -> clean_table(). set_session(Session) -> ejabberd_riak:put(Session, session_schema(), [{'2i', [{<<"us">>, Session#session.us}]}]). delete_session(Session) -> ejabberd_riak:delete(session, Session#session.sid). get_sessions() -> case ejabberd_riak:get(session, session_schema()) of {ok, Ss} -> Ss; {error, _} -> [] end. get_sessions(LServer) -> [S || S <- get_sessions(), element(2, S#session.us) == LServer]. get_sessions(U, S) -> ejabberd_riak:get_by_index(session, session_schema(), <<"us">>, {U, S}). %%%=================================================================== %%% Internal functions %%%=================================================================== session_schema() -> {record_info(fields, session), #session{}}. clean_table() -> %% TODO: not very efficient, rewrite using map-reduce or something ?DEBUG("Cleaning Riak 'sm' table...", []), lists:foreach( fun(#session{sid = {_, Pid} = SID}) when node(Pid) == node() -> ejabberd_riak:delete(session, SID); (_) -> ok end, get_sessions()). ejabberd-18.01/src/mod_pubsub.erl0000644000232200023220000042135013225664356017302 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_pubsub.erl %%% Author : Christophe Romain %%% Purpose : Publish Subscribe service (XEP-0060) %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% Support for subscription-options and multi-subscribe features was %%% added by Brian Cully (bjc AT kublai.com). Subscriptions and options are %%% stored in the pubsub_subscription table, with a link to them provided %%% by the subscriptions field of pubsub_state. For information on %%% subscription-options and mulit-subscribe see XEP-0060 sections 6.1.6, %%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see %%% XEP-0060 section 12.18. -module(mod_pubsub). -behaviour(gen_mod). -behaviour(gen_server). -author('christophe.romain@process-one.net'). -protocol({xep, 60, '1.14'}). -protocol({xep, 163, '1.2'}). -protocol({xep, 248, '0.2'}). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("pubsub.hrl"). -include("mod_roster.hrl"). -include("translate.hrl"). -define(STDTREE, <<"tree">>). -define(STDNODE, <<"flat">>). -define(PEPNODE, <<"pep">>). %% exports for hooks -export([presence_probe/3, caps_add/3, caps_update/3, in_subscription/6, out_subscription/4, on_self_presence/1, on_user_offline/2, remove_user/2, disco_local_identity/5, disco_local_features/5, disco_local_items/5, disco_sm_identity/5, disco_sm_features/5, disco_sm_items/5, c2s_handle_info/2]). %% exported iq handlers -export([iq_sm/1, process_disco_info/1, process_disco_items/1, process_pubsub/1, process_pubsub_owner/1, process_vcard/1, process_commands/1]). %% exports for console debug manual use -export([create_node/5, create_node/7, delete_node/3, subscribe_node/5, unsubscribe_node/5, publish_item/6, delete_item/4, delete_item/5, send_items/7, get_items/2, get_item/3, get_cached_item/2, get_configure/5, set_configure/5, tree_action/3, node_action/4, node_call/4]). %% general helpers for plugins -export([extended_error/2, service_jid/1, tree/1, tree/2, plugin/2, plugins/1, config/3, host/1, serverhost/1]). %% pubsub#errors -export([err_closed_node/0, err_configuration_required/0, err_invalid_jid/0, err_invalid_options/0, err_invalid_payload/0, err_invalid_subid/0, err_item_forbidden/0, err_item_required/0, err_jid_required/0, err_max_items_exceeded/0, err_max_nodes_exceeded/0, err_nodeid_required/0, err_not_in_roster_group/0, err_not_subscribed/0, err_payload_too_big/0, err_payload_required/0, err_pending_subscription/0, err_precondition_not_met/0, err_presence_subscription_required/0, err_subid_required/0, err_too_many_subscriptions/0, err_unsupported/1, err_unsupported_access_model/0]). %% API and gen_server callbacks -export([start/2, stop/1, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, depends/2, mod_opt_type/1]). %%==================================================================== %% API %%==================================================================== %%-------------------------------------------------------------------- %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- -export_type([ host/0, hostPubsub/0, hostPEP/0, %% nodeIdx/0, nodeId/0, itemId/0, subId/0, payload/0, %% nodeOption/0, nodeOptions/0, subOption/0, subOptions/0, pubOption/0, pubOptions/0, %% affiliation/0, subscription/0, accessModel/0, publishModel/0 ]). %% -type payload() defined here because the -type xmlel() is not accessible %% from pubsub.hrl -type(payload() :: [] | [xmlel(),...]). -export_type([ pubsubNode/0, pubsubState/0, pubsubItem/0, pubsubSubscription/0, pubsubLastItem/0 ]). -type(pubsubNode() :: #pubsub_node{ nodeid :: {Host::mod_pubsub:host(), Node::mod_pubsub:nodeId()}, id :: Nidx::mod_pubsub:nodeIdx(), parents :: [Node::mod_pubsub:nodeId()], type :: Type::binary(), owners :: [Owner::ljid(),...], options :: Opts::mod_pubsub:nodeOptions() } ). -type(pubsubState() :: #pubsub_state{ stateid :: {Entity::ljid(), Nidx::mod_pubsub:nodeIdx()}, nodeidx :: Nidx::mod_pubsub:nodeIdx(), items :: [ItemId::mod_pubsub:itemId()], affiliation :: Affs::mod_pubsub:affiliation(), subscriptions :: [{Sub::mod_pubsub:subscription(), SubId::mod_pubsub:subId()}] } ). -type(pubsubItem() :: #pubsub_item{ itemid :: {ItemId::mod_pubsub:itemId(), Nidx::mod_pubsub:nodeIdx()}, nodeidx :: Nidx::mod_pubsub:nodeIdx(), creation :: {erlang:timestamp(), ljid()}, modification :: {erlang:timestamp(), ljid()}, payload :: mod_pubsub:payload() } ). -type(pubsubSubscription() :: #pubsub_subscription{ subid :: SubId::mod_pubsub:subId(), options :: [] | mod_pubsub:subOptions() } ). -type(pubsubLastItem() :: #pubsub_last_item{ nodeid :: {binary(), mod_pubsub:nodeIdx()}, itemid :: mod_pubsub:itemId(), creation :: {erlang:timestamp(), ljid()}, payload :: mod_pubsub:payload() } ). -record(state, { server_host, hosts, access, pep_mapping = [], ignore_pep_from_offline = true, last_item_cache = false, max_items_node = ?MAXITEMS, max_subscriptions_node = undefined, default_node_config = [], nodetree = <<"nodetree_", (?STDTREE)/binary>>, plugins = [?STDNODE], db_type }). -type(state() :: #state{ server_host :: binary(), hosts :: [mod_pubsub:hostPubsub()], access :: atom(), pep_mapping :: [{binary(), binary()}], ignore_pep_from_offline :: boolean(), last_item_cache :: boolean(), max_items_node :: non_neg_integer(), max_subscriptions_node :: non_neg_integer()|undefined, default_node_config :: [{atom(), binary()|boolean()|integer()|atom()}], nodetree :: binary(), plugins :: [binary(),...], db_type :: atom() } ). start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). %%==================================================================== %% gen_server callbacks %%==================================================================== %%-------------------------------------------------------------------- %% Function: init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- -spec init([binary() | [{_,_}],...]) -> {'ok',state()}. init([ServerHost, Opts]) -> process_flag(trap_exit, true), ?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]), Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"pubsub.@HOST@">>), Access = gen_mod:get_opt(access_createnode, Opts, all), PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts, true), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(ServerHost)), LastItemCache = gen_mod:get_opt(last_item_cache, Opts, false), MaxItemsNode = gen_mod:get_opt(max_items_node, Opts, ?MAXITEMS), MaxSubsNode = gen_mod:get_opt(max_subscriptions_node, Opts), ejabberd_mnesia:create(?MODULE, pubsub_last_item, [{ram_copies, [node()]}, {attributes, record_info(fields, pubsub_last_item)}]), AllPlugins = lists:flatmap( fun(Host) -> ejabberd_router:register_route(Host, ServerHost), case gen_mod:db_type(ServerHost, ?MODULE) of mnesia -> pubsub_index:init(Host, ServerHost, Opts); _ -> ok end, {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts), DefaultModule = plugin(Host, hd(Plugins)), DefaultNodeCfg = merge_config( gen_mod:get_opt(default_node_config, Opts, []), DefaultModule:options()), lists:foreach( fun(H) -> T = gen_mod:get_module_proc(H, config), try ets:new(T, [set, named_table]), ets:insert(T, {nodetree, NodeTree}), ets:insert(T, {plugins, Plugins}), ets:insert(T, {last_item_cache, LastItemCache}), ets:insert(T, {max_items_node, MaxItemsNode}), ets:insert(T, {max_subscriptions_node, MaxSubsNode}), ets:insert(T, {default_node_config, DefaultNodeCfg}), ets:insert(T, {pep_mapping, PepMapping}), ets:insert(T, {ignore_pep_from_offline, PepOffline}), ets:insert(T, {host, Host}), ets:insert(T, {access, Access}) catch error:badarg when H == ServerHost -> ok end end, [Host, ServerHost]), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, ?MODULE, process_disco_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_disco_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUBSUB, ?MODULE, process_pubsub, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUBSUB_OWNER, ?MODULE, process_pubsub_owner, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS, ?MODULE, process_commands, IQDisc), Plugins end, Hosts), ejabberd_hooks:add(c2s_self_presence, ServerHost, ?MODULE, on_self_presence, 75), ejabberd_hooks:add(c2s_terminated, ServerHost, ?MODULE, on_user_offline, 75), ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80), ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50), ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:add(c2s_handle_info, ServerHost, ?MODULE, c2s_handle_info, 50), case lists:member(?PEPNODE, AllPlugins) of true -> ejabberd_hooks:add(caps_add, ServerHost, ?MODULE, caps_add, 80), ejabberd_hooks:add(caps_update, ServerHost, ?MODULE, caps_update, 80), ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75), ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc); false -> ok end, NodeTree = config(ServerHost, nodetree), Plugins = config(ServerHost, plugins), PepMapping = config(ServerHost, pep_mapping), DBType = gen_mod:db_type(ServerHost, ?MODULE), {ok, #state{hosts = Hosts, server_host = ServerHost, access = Access, pep_mapping = PepMapping, ignore_pep_from_offline = PepOffline, last_item_cache = LastItemCache, max_items_node = MaxItemsNode, nodetree = NodeTree, plugins = Plugins, db_type = DBType}}. depends(ServerHost, Opts) -> Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>), Plugins = gen_mod:get_opt(plugins, Opts, [?STDNODE]), lists:flatmap( fun(Name) -> Plugin = plugin(ServerHost, Name), try apply(Plugin, depends, [Host, ServerHost, Opts]) catch _:undef -> [] end end, Plugins). %% @doc Call the init/1 function for each plugin declared in the config file. %% The default plugin module is implicit. %%

The Erlang code for the plugin is located in a module called %% node_plugin. The 'node_' prefix is mandatory.

%%

See {@link node_hometree:init/1} for an example implementation.

init_plugins(Host, ServerHost, Opts) -> TreePlugin = tree(Host, gen_mod:get_opt(nodetree, Opts, ?STDTREE)), ?DEBUG("** tree plugin is ~p", [TreePlugin]), TreePlugin:init(Host, ServerHost, Opts), Plugins = gen_mod:get_opt(plugins, Opts, [?STDNODE]), PepMapping = gen_mod:get_opt(pep_mapping, Opts, []), ?DEBUG("** PEP Mapping : ~p~n", [PepMapping]), PluginsOK = lists:foldl( fun (Name, Acc) -> Plugin = plugin(Host, Name), case catch apply(Plugin, init, [Host, ServerHost, Opts]) of {'EXIT', _Error} -> Acc; _ -> ?DEBUG("** init ~s plugin", [Name]), [Name | Acc] end end, [], Plugins), {lists:reverse(PluginsOK), TreePlugin, PepMapping}. terminate_plugins(Host, ServerHost, Plugins, TreePlugin) -> lists:foreach( fun (Name) -> ?DEBUG("** terminate ~s plugin", [Name]), Plugin = plugin(Host, Name), Plugin:terminate(Host, ServerHost) end, Plugins), TreePlugin:terminate(Host, ServerHost), ok. %% ------- %% disco hooks handling functions %% -spec disco_local_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. disco_local_identity(Acc, _From, To, <<>>, _Lang) -> case lists:member(?PEPNODE, plugins(host(To#jid.lserver))) of true -> [#identity{category = <<"pubsub">>, type = <<"pep">>} | Acc]; false -> Acc end; disco_local_identity(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_local_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]} | empty. disco_local_features(Acc, _From, To, <<>>, _Lang) -> Host = host(To#jid.lserver), Feats = case Acc of {result, I} -> I; _ -> [] end, {result, Feats ++ [?NS_PUBSUB|[feature(F) || F <- features(Host, <<>>)]]}; disco_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_local_items({error, stanza_error()} | {result, [disco_item()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [disco_item()]} | empty. disco_local_items(Acc, _From, _To, <<>>, _Lang) -> Acc; disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_sm_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. disco_sm_identity(Acc, From, To, Node, _Lang) -> disco_identity(jid:tolower(jid:remove_resource(To)), Node, From) ++ Acc. -spec disco_identity(binary(), binary(), jid()) -> [identity()]. disco_identity(_Host, <<>>, _From) -> [#identity{category = <<"pubsub">>, type = <<"pep">>}]; disco_identity(Host, Node, From) -> Action = fun(#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) -> Owners = node_owners_call(Host, Type, Nidx, O), case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of {result, _} -> {result, [#identity{category = <<"pubsub">>, type = <<"pep">>}, #identity{category = <<"pubsub">>, type = <<"leaf">>, name = get_option(Options, title, <<>>)}]}; _ -> {result, []} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> Result; _ -> [] end. -spec disco_sm_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. disco_sm_features(empty, From, To, Node, Lang) -> disco_sm_features({result, []}, From, To, Node, Lang); disco_sm_features({result, OtherFeatures} = _Acc, From, To, Node, _Lang) -> {result, OtherFeatures ++ disco_features(jid:tolower(jid:remove_resource(To)), Node, From)}; disco_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_features(ljid(), binary(), jid()) -> [binary()]. disco_features(Host, <<>>, _From) -> [?NS_PUBSUB | [feature(F) || F <- plugin_features(Host, <<"pep">>)]]; disco_features(Host, Node, From) -> Action = fun(#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) -> Owners = node_owners_call(Host, Type, Nidx, O), case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of {result, _} -> {result, [?NS_PUBSUB | [feature(F) || F <- plugin_features(Host, <<"pep">>)]]}; _ -> {result, []} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> Result; _ -> [] end. -spec disco_sm_items({error, stanza_error()} | {result, [disco_item()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [disco_item()]}. disco_sm_items(empty, From, To, Node, Lang) -> disco_sm_items({result, []}, From, To, Node, Lang); disco_sm_items({result, OtherItems}, From, To, Node, _Lang) -> {result, lists:usort(OtherItems ++ disco_items(jid:tolower(jid:remove_resource(To)), Node, From))}; disco_sm_items(Acc, _From, _To, _Node, _Lang) -> Acc. -spec disco_items(ljid(), binary(), jid()) -> [disco_item()]. disco_items(Host, <<>>, From) -> Action = fun(#pubsub_node{nodeid = {_, Node}, options = Options, type = Type, id = Nidx, owners = O}, Acc) -> Owners = node_owners_call(Host, Type, Nidx, O), case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of {result, _} -> [#disco_item{node = Node, jid = jid:make(Host), name = get_option(Options, title, <<>>)} | Acc]; _ -> Acc end end, NodeBloc = fun() -> {result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))} end, case transaction(Host, NodeBloc, sync_dirty) of {result, Items} -> Items; _ -> [] end; disco_items(Host, Node, From) -> Action = fun(#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) -> Owners = node_owners_call(Host, Type, Nidx, O), case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of {result, Items} -> {result, [#disco_item{jid = jid:make(Host), name = ItemId} || #pubsub_item{itemid = {ItemId, _}} <- Items]}; _ -> {result, []} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> Result; _ -> [] end. %% ------- %% presence and session hooks handling functions %% -spec caps_add(jid(), jid(), [binary()]) -> ok. caps_add(JID, JID, _Features) -> %% Send the owner his last PEP items. send_last_pep(JID, JID); caps_add(#jid{lserver = S1} = From, #jid{lserver = S2} = To, _Features) when S1 =/= S2 -> %% When a remote contact goes online while the local user is offline, the %% remote contact won't receive last items from the local user even if %% ignore_pep_from_offline is set to false. To work around this issue a bit, %% we'll also send the last items to remote contacts when the local user %% connects. That's the reason to use the caps_add hook instead of the %% presence_probe_hook for remote contacts: The latter is only called when a %% contact becomes available; the former is also executed when the local %% user goes online (because that triggers the contact to send a presence %% packet with CAPS). send_last_pep(To, From); caps_add(_From, _To, _Feature) -> ok. -spec caps_update(jid(), jid(), [binary()]) -> ok. caps_update(From, To, _Features) -> send_last_pep(To, From). -spec presence_probe(jid(), jid(), pid()) -> ok. presence_probe(#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, _Pid) -> %% ignore presence_probe from my other ressources ok; presence_probe(#jid{lserver = S} = From, #jid{lserver = S} = To, _Pid) -> send_last_pep(To, From); presence_probe(_From, _To, _Pid) -> %% ignore presence_probe from remote contacts, those are handled via caps_add ok. -spec on_self_presence({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. on_self_presence({_, #{pres_last := _}} = Acc) -> % Just a presence update. Acc; on_self_presence({#presence{type = available}, #{jid := JID}} = Acc) -> send_last_items(JID), Acc; on_self_presence(Acc) -> Acc. -spec on_user_offline(ejabberd_c2s:state(), atom()) -> ejabberd_c2s:state(). on_user_offline(#{jid := JID} = C2SState, _Reason) -> purge_offline(jid:tolower(JID)), C2SState; on_user_offline(C2SState, _Reason) -> C2SState. %% ------- %% subscription hooks handling functions %% -spec out_subscription( binary(), binary(), jid(), subscribed | unsubscribed | subscribe | unsubscribe) -> boolean(). out_subscription(User, Server, To, subscribed) -> send_last_pep(jid:make(User, Server), To), true; out_subscription(_, _, _, _) -> true. -spec in_subscription(boolean(), binary(), binary(), jid(), subscribe | subscribed | unsubscribe | unsubscribed, binary()) -> true. in_subscription(_, User, Server, Owner, unsubscribed, _) -> unsubscribe_user(jid:make(User, Server), Owner), true; in_subscription(_, _, _, _, _, _) -> true. unsubscribe_user(Entity, Owner) -> spawn(fun () -> [unsubscribe_user(ServerHost, Entity, Owner) || ServerHost <- lists:usort(lists:foldl( fun(UserHost, Acc) -> case gen_mod:is_loaded(UserHost, mod_pubsub) of true -> [UserHost|Acc]; false -> Acc end end, [], [Entity#jid.lserver, Owner#jid.lserver]))] end). unsubscribe_user(Host, Entity, Owner) -> BJID = jid:tolower(jid:remove_resource(Owner)), lists:foreach(fun (PType) -> {result, Subs} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]), lists:foreach(fun ({#pubsub_node{options = Options, owners = O, id = Nidx}, subscribed, _, JID}) -> Unsubscribe = match_option(Options, access_model, presence) andalso lists:member(BJID, node_owners_action(Host, PType, Nidx, O)), case Unsubscribe of true -> node_action(Host, PType, unsubscribe_node, [Nidx, Entity, JID, all]); false -> ok end; (_) -> ok end, Subs) end, plugins(Host)). %% ------- %% user remove hook handling function %% -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Entity = jid:make(LUser, LServer), Host = host(LServer), HomeTreeBase = <<"/home/", LServer/binary, "/", LUser/binary>>, spawn(fun () -> lists:foreach(fun (PType) -> {result, Subs} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]), lists:foreach(fun ({#pubsub_node{id = Nidx}, _, _, JID}) -> node_action(Host, PType, unsubscribe_node, [Nidx, Entity, JID, all]); (_) -> ok end, Subs), {result, Affs} = node_action(Host, PType, get_entity_affiliations, [Host, Entity]), lists:foreach(fun ({#pubsub_node{nodeid = {H, N}, parents = []}, owner}) -> delete_node(H, N, Entity); ({#pubsub_node{nodeid = {H, N}, type = Type}, owner}) when N == HomeTreeBase, Type == <<"hometree">> -> delete_node(H, N, Entity); ({#pubsub_node{id = Nidx}, publisher}) -> node_action(Host, PType, set_affiliation, [Nidx, Entity, none]); (_) -> ok end, Affs) end, plugins(Host)) end), ok. handle_call(server_host, _From, State) -> {reply, State#state.server_host, State}; handle_call(plugins, _From, State) -> {reply, State#state.plugins, State}; handle_call(pep_mapping, _From, State) -> {reply, State#state.pep_mapping, State}; handle_call(nodetree, _From, State) -> {reply, State#state.nodetree, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- %% @private handle_cast(_Msg, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- %% @private handle_info({route, #iq{to = To} = IQ}, State) when To#jid.lresource == <<"">> -> ejabberd_router:process_iq(IQ), {noreply, State}; handle_info({route, Packet}, State) -> To = xmpp:get_to(Packet), case catch do_route(To#jid.lserver, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); _ -> ok end, {noreply, State}; handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- %% @private terminate(_Reason, #state{hosts = Hosts, server_host = ServerHost, nodetree = TreePlugin, plugins = Plugins}) -> case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:delete(caps_add, ServerHost, ?MODULE, caps_add, 80), ejabberd_hooks:delete(caps_update, ServerHost, ?MODULE, caps_update, 80), ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75), ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB), gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER); false -> ok end, ejabberd_hooks:delete(c2s_self_presence, ServerHost, ?MODULE, on_self_presence, 75), ejabberd_hooks:delete(c2s_terminated, ServerHost, ?MODULE, on_user_offline, 75), ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80), ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50), ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:delete(c2s_handle_info, ServerHost, ?MODULE, c2s_handle_info, 50), lists:foreach( fun(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUBSUB), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUBSUB_OWNER), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS), terminate_plugins(Host, ServerHost, Plugins, TreePlugin), ejabberd_router:unregister_route(Host) end, Hosts). %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- %% @private code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -spec process_disco_info(iq()) -> iq(). process_disco_info(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_info(#iq{from = From, to = To, lang = Lang, type = get, sub_els = [#disco_info{node = Node}]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), Info = ejabberd_hooks:run_fold(disco_info, ServerHost, [], [ServerHost, ?MODULE, <<>>, <<>>]), case iq_disco_info(ServerHost, Host, Node, From, Lang) of {result, IQRes} -> XData = IQRes#disco_info.xdata ++ Info, xmpp:make_iq_result(IQ, IQRes#disco_info{node = Node, xdata = XData}); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec process_disco_items(iq()) -> iq(). process_disco_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_items(#iq{type = get, from = From, to = To, sub_els = [#disco_items{node = Node} = SubEl]} = IQ) -> Host = To#jid.lserver, case iq_disco_items(Host, Node, From, SubEl#disco_items.rsm) of {result, IQRes} -> xmpp:make_iq_result(IQ, IQRes#disco_items{node = Node}); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec process_pubsub(iq()) -> iq(). process_pubsub(#iq{to = To} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), Access = config(ServerHost, access), case iq_pubsub(Host, Access, IQ) of {result, IQRes} -> xmpp:make_iq_result(IQ, IQRes); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec process_pubsub_owner(iq()) -> iq(). process_pubsub_owner(#iq{to = To} = IQ) -> Host = To#jid.lserver, case iq_pubsub_owner(Host, IQ) of {result, IQRes} -> xmpp:make_iq_result(IQ, IQRes); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec process_vcard(iq()) -> iq(). process_vcard(#iq{type = get, lang = Lang} = IQ) -> xmpp:make_iq_result(IQ, iq_get_vcard(Lang)); process_vcard(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). -spec process_commands(iq()) -> iq(). process_commands(#iq{type = set, to = To, from = From, sub_els = [#adhoc_command{} = Request]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), Plugins = config(ServerHost, plugins), Access = config(ServerHost, access), case adhoc_request(Host, ServerHost, From, Request, Access, Plugins) of {error, Error} -> xmpp:make_error(IQ, Error); Response -> xmpp:make_iq_result( IQ, xmpp_util:make_adhoc_response(Request, Response)) end; process_commands(#iq{type = get, lang = Lang} = IQ) -> Txt = <<"Value 'get' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). -spec do_route(binary(), stanza()) -> ok. do_route(Host, Packet) -> To = xmpp:get_to(Packet), case To of #jid{luser = <<>>, lresource = <<>>} -> case Packet of #message{type = T} when T /= error -> case find_authorization_response(Packet) of undefined -> ok; {error, Err} -> ejabberd_router:route_error(Packet, Err); AuthResponse -> handle_authorization_response( Host, Packet, AuthResponse) end; _ -> Err = xmpp:err_service_unavailable(), ejabberd_router:route_error(Packet, Err) end; _ -> Err = xmpp:err_item_not_found(), ejabberd_router:route_error(Packet, Err) end. -spec command_disco_info(binary(), binary(), jid()) -> {result, disco_info()}. command_disco_info(_Host, ?NS_COMMANDS, _From) -> {result, #disco_info{identities = [#identity{category = <<"automation">>, type = <<"command-list">>}]}}; command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING, _From) -> {result, #disco_info{identities = [#identity{category = <<"automation">>, type = <<"command-node">>}], features = [?NS_COMMANDS]}}. -spec node_disco_info(binary(), binary(), jid()) -> {result, disco_info()} | {error, stanza_error()}. node_disco_info(Host, Node, From) -> node_disco_info(Host, Node, From, true, true). -spec node_disco_info(binary(), binary(), jid(), boolean(), boolean()) -> {result, disco_info()} | {error, stanza_error()}. node_disco_info(Host, Node, _From, _Identity, _Features) -> Action = fun(#pubsub_node{id = Nidx, type = Type, options = Options}) -> NodeType = case get_option(Options, node_type) of collection -> <<"collection">>; _ -> <<"leaf">> end, Affs = case node_call(Host, Type, get_node_affiliations, [Nidx]) of {result, As} -> As; _ -> [] end, Subs = case node_call(Host, Type, get_node_subscriptions, [Nidx]) of {result, Ss} -> Ss; _ -> [] end, Meta = [{title, get_option(Options, title, <<>>)}, {description, get_option(Options, description, <<>>)}, {owner, [jid:make(LJID) || {LJID, Aff} <- Affs, Aff =:= owner]}, {publisher, [jid:make(LJID) || {LJID, Aff} <- Affs, Aff =:= publisher]}, {num_subscribers, length(Subs)}], XData = #xdata{type = result, fields = pubsub_meta_data:encode(Meta)}, Is = [#identity{category = <<"pubsub">>, type = NodeType}], Fs = [?NS_PUBSUB | [feature(F) || F <- plugin_features(Host, Type)]], {result, #disco_info{identities = Is, features = Fs, xdata = [XData]}} end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> {result, Result}; Other -> Other end. -spec iq_disco_info(binary(), binary(), binary(), jid(), binary()) -> {result, disco_info()} | {error, stanza_error()}. iq_disco_info(ServerHost, Host, SNode, From, Lang) -> [Node | _] = case SNode of <<>> -> [<<>>]; _ -> str:tokens(SNode, <<"!">>) end, case Node of <<>> -> Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("Publish-Subscribe")), {result, #disco_info{ identities = [#identity{ category = <<"pubsub">>, type = <<"service">>, name = translate:translate(Lang, Name)}], features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_PUBSUB, ?NS_COMMANDS, ?NS_VCARD | [feature(F) || F <- features(Host, Node)]]}}; ?NS_COMMANDS -> command_disco_info(Host, Node, From); ?NS_PUBSUB_GET_PENDING -> command_disco_info(Host, Node, From); _ -> node_disco_info(Host, Node, From) end. -spec iq_disco_items(host(), binary(), jid(), undefined | rsm_set()) -> {result, disco_items()} | {error, stanza_error()}. iq_disco_items(Host, <<>>, From, _RSM) -> Items = lists:map( fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> case get_option(Options, title) of false -> #disco_item{jid = jid:make(Host), node = SubNode}; Title -> #disco_item{jid = jid:make(Host), name = Title, node = SubNode} end end, tree_action(Host, get_subnodes, [Host, <<>>, From])), {result, #disco_items{items = Items}}; iq_disco_items(Host, ?NS_COMMANDS, _From, _RSM) -> {result, #disco_items{items = [#disco_item{jid = jid:make(Host), node = ?NS_PUBSUB_GET_PENDING, name = <<"Get Pending">>}]}}; iq_disco_items(_Host, ?NS_PUBSUB_GET_PENDING, _From, _RSM) -> {result, #disco_items{}}; iq_disco_items(Host, Item, From, RSM) -> case str:tokens(Item, <<"!">>) of [_Node, _ItemId] -> {result, #disco_items{}}; [Node] -> Action = fun (#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) -> Owners = node_owners_call(Host, Type, Nidx, O), {NodeItems, RsmOut} = case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) of {result, R} -> R; _ -> {[], undefined} end, Nodes = lists:map( fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) -> case get_option(SubOptions, title) of false -> #disco_item{jid = jid:make(Host), node = SubNode}; Title -> #disco_item{jid = jid:make(Host), name = Title, node = SubNode} end end, tree_call(Host, get_subnodes, [Host, Node, From])), Items = lists:map( fun(#pubsub_item{itemid = {RN, _}}) -> {result, Name} = node_call(Host, Type, get_item_name, [Host, Node, RN]), #disco_item{jid = jid:make(Host), name = Name} end, NodeItems), {result, #disco_items{items = Nodes ++ Items, rsm = RsmOut}} end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> {result, Result}; Other -> Other end end. -spec iq_sm(iq()) -> iq(). iq_sm(#iq{to = To, sub_els = [SubEl]} = IQ) -> LOwner = jid:tolower(jid:remove_resource(To)), Res = case xmpp:get_ns(SubEl) of ?NS_PUBSUB -> iq_pubsub(LOwner, all, IQ); ?NS_PUBSUB_OWNER -> iq_pubsub_owner(LOwner, IQ) end, case Res of {result, IQRes} -> xmpp:make_iq_result(IQ, IQRes); {error, Error} -> xmpp:make_error(IQ, Error) end. -spec iq_get_vcard(binary()) -> vcard_temp(). iq_get_vcard(Lang) -> Desc = translate:translate(Lang, <<"ejabberd Publish-Subscribe module">>), #vcard_temp{fn = <<"ejabberd/mod_pubsub">>, url = ?EJABBERD_URI, desc = <>}. -spec iq_pubsub(binary() | ljid(), atom(), iq()) -> {result, pubsub()} | {error, stanza_error()}. iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang, sub_els = [SubEl]}) -> case {IQType, SubEl} of {set, #pubsub{create = Node, configure = Configure, _ = undefined}} when is_binary(Node) -> ServerHost = serverhost(Host), Plugins = config(ServerHost, plugins), Config = case Configure of {_, XData} -> decode_node_config(XData, Host, Lang); undefined -> [] end, Type = hd(Plugins), case Config of {error, _} = Err -> Err; _ -> create_node(Host, ServerHost, Node, From, Type, Access, Config) end; {set, #pubsub{publish = #ps_publish{node = Node, items = Items}, publish_options = XData, configure = _, _ = undefined}} -> ServerHost = serverhost(Host), case Items of [#ps_item{id = ItemId, xml_els = Payload}] -> case decode_publish_options(XData, Lang) of {error, _} = Err -> Err; PubOpts -> publish_item(Host, ServerHost, Node, From, ItemId, Payload, PubOpts, Access) end; [] -> {error, extended_error(xmpp:err_bad_request(), err_item_required())}; _ -> {error, extended_error(xmpp:err_bad_request(), err_invalid_payload())} end; {set, #pubsub{retract = #ps_retract{node = Node, notify = Notify, items = Items}, _ = undefined}} -> case Items of [#ps_item{id = ItemId}] -> if ItemId /= <<>> -> delete_item(Host, Node, From, ItemId, Notify); true -> {error, extended_error(xmpp:err_bad_request(), err_item_required())} end; [] -> {error, extended_error(xmpp:err_bad_request(), err_item_required())}; _ -> {error, extended_error(xmpp:err_bad_request(), err_invalid_payload())} end; {set, #pubsub{subscribe = #ps_subscribe{node = Node, jid = JID}, options = Options, _ = undefined}} -> Config = case Options of #ps_options{xdata = XData} -> decode_subscribe_options(XData, Lang); _ -> [] end, case Config of {error, _} = Err -> Err; _ -> subscribe_node(Host, Node, From, JID, Config) end; {set, #pubsub{unsubscribe = #ps_unsubscribe{node = Node, jid = JID, subid = SubId}, _ = undefined}} -> unsubscribe_node(Host, Node, From, JID, SubId); {get, #pubsub{items = #ps_items{node = Node, max_items = MaxItems, subid = SubId, items = Items}, rsm = RSM, _ = undefined}} -> ItemIds = [ItemId || #ps_item{id = ItemId} <- Items, ItemId /= <<>>], get_items(Host, Node, From, SubId, MaxItems, ItemIds, RSM); {get, #pubsub{subscriptions = {Node, _}, _ = undefined}} -> Plugins = config(serverhost(Host), plugins), get_subscriptions(Host, Node, From, Plugins); {get, #pubsub{affiliations = {Node, _}, _ = undefined}} -> Plugins = config(serverhost(Host), plugins), get_affiliations(Host, Node, From, Plugins); {get, #pubsub{options = #ps_options{node = Node, subid = SubId, jid = JID}, _ = undefined}} -> get_options(Host, Node, JID, SubId, Lang); {set, #pubsub{options = #ps_options{node = Node, subid = SubId, jid = JID, xdata = XData}, _ = undefined}} -> case decode_subscribe_options(XData, Lang) of {error, _} = Err -> Err; Config -> set_options(Host, Node, JID, SubId, Config) end; {set, #pubsub{}} -> {error, xmpp:err_bad_request()}; _ -> {error, xmpp:err_feature_not_implemented()} end. -spec iq_pubsub_owner(binary() | ljid(), iq()) -> {result, pubsub_owner() | undefined} | {error, stanza_error()}. iq_pubsub_owner(Host, #iq{type = IQType, from = From, lang = Lang, sub_els = [SubEl]}) -> case {IQType, SubEl} of {get, #pubsub_owner{configure = {Node, undefined}, _ = undefined}} -> ServerHost = serverhost(Host), get_configure(Host, ServerHost, Node, From, Lang); {set, #pubsub_owner{configure = {Node, XData}, _ = undefined}} -> case XData of undefined -> {error, xmpp:err_bad_request(<<"No data form found">>, Lang)}; #xdata{type = cancel} -> {result, #pubsub_owner{}}; #xdata{type = submit} -> case decode_node_config(XData, Host, Lang) of {error, _} = Err -> Err; Config -> set_configure(Host, Node, From, Config, Lang) end; #xdata{} -> {error, xmpp:err_bad_request(<<"Incorrect data form">>, Lang)} end; {get, #pubsub_owner{default = {Node, undefined}, _ = undefined}} -> get_default(Host, Node, From, Lang); {set, #pubsub_owner{delete = {Node, _}, _ = undefined}} -> delete_node(Host, Node, From); {set, #pubsub_owner{purge = Node, _ = undefined}} when Node /= undefined -> purge_node(Host, Node, From); {get, #pubsub_owner{subscriptions = {Node, []}, _ = undefined}} -> get_subscriptions(Host, Node, From); {set, #pubsub_owner{subscriptions = {Node, Subs}, _ = undefined}} -> set_subscriptions(Host, Node, From, Subs); {get, #pubsub_owner{affiliations = {Node, []}, _ = undefined}} -> get_affiliations(Host, Node, From); {set, #pubsub_owner{affiliations = {Node, Affs}, _ = undefined}} -> set_affiliations(Host, Node, From, Affs); {_, #pubsub_owner{}} -> {error, xmpp:err_bad_request()}; _ -> {error, xmpp:err_feature_not_implemented()} end. -spec adhoc_request(binary(), binary(), jid(), adhoc_command(), atom(), [binary()]) -> adhoc_command() | {error, stanza_error()}. adhoc_request(Host, _ServerHost, Owner, #adhoc_command{node = ?NS_PUBSUB_GET_PENDING, lang = Lang, action = execute, xdata = undefined}, _Access, Plugins) -> send_pending_node_form(Host, Owner, Lang, Plugins); adhoc_request(Host, _ServerHost, Owner, #adhoc_command{node = ?NS_PUBSUB_GET_PENDING, lang = Lang, action = execute, xdata = #xdata{} = XData} = Request, _Access, _Plugins) -> case decode_get_pending(XData, Lang) of {error, _} = Err -> Err; Config -> Node = proplists:get_value(node, Config), case send_pending_auth_events(Host, Node, Owner, Lang) of ok -> xmpp_util:make_adhoc_response( Request, #adhoc_command{action = completed}); Err -> Err end end; adhoc_request(_Host, _ServerHost, _Owner, #adhoc_command{action = cancel}, _Access, _Plugins) -> #adhoc_command{status = canceled}; adhoc_request(_Host, _ServerHost, _Owner, Other, _Access, _Plugins) -> ?DEBUG("Couldn't process ad hoc command:~n~p", [Other]), {error, xmpp:err_item_not_found()}. -spec send_pending_node_form(binary(), jid(), binary(), [binary()]) -> adhoc_command() | {error, stanza_error()}. send_pending_node_form(Host, Owner, Lang, Plugins) -> Filter = fun (Type) -> lists:member(<<"get-pending">>, plugin_features(Host, Type)) end, case lists:filter(Filter, Plugins) of [] -> Err = extended_error(xmpp:err_feature_not_implemented(), err_unsupported('get-pending')), {error, Err}; Ps -> case get_pending_nodes(Host, Owner, Ps) of {ok, Nodes} -> XForm = #xdata{type = form, fields = pubsub_get_pending:encode( [{node, Nodes}], Lang)}, #adhoc_command{status = executing, action = execute, xdata = XForm}; Err -> Err end end. -spec get_pending_nodes(binary(), jid(), [binary()]) -> {ok, [binary()]} | {error, stanza_error()}. get_pending_nodes(Host, Owner, Plugins) -> Tr = fun (Type) -> case node_call(Host, Type, get_pending_nodes, [Host, Owner]) of {result, Nodes} -> Nodes; _ -> [] end end, Action = fun() -> {result, lists:flatmap(Tr, Plugins)} end, case transaction(Host, Action, sync_dirty) of {result, Res} -> {ok, Res}; Err -> Err end. %% @doc

Send a subscription approval form to Owner for all pending %% subscriptions on Host and Node.

-spec send_pending_auth_events(binary(), binary(), jid(), binary()) -> adhoc_command() | {error, stanza_error()}. send_pending_auth_events(Host, Node, Owner, Lang) -> ?DEBUG("Sending pending auth events for ~s on ~s:~s", [jid:encode(Owner), Host, Node]), Action = fun(#pubsub_node{id = Nidx, type = Type}) -> case lists:member(<<"get-pending">>, plugin_features(Host, Type)) of true -> case node_call(Host, Type, get_affiliation, [Nidx, Owner]) of {result, owner} -> node_call(Host, Type, get_node_subscriptions, [Nidx]); _ -> {error, xmpp:err_forbidden( <<"Owner privileges required">>, Lang)} end; false -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('get-pending'))} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {N, Subs}} -> lists:foreach( fun({J, pending, _SubId}) -> send_authorization_request(N, jid:make(J)); ({J, pending}) -> send_authorization_request(N, jid:make(J)); (_) -> ok end, Subs), #adhoc_command{}; Err -> Err end. %%% authorization handling -spec send_authorization_request(#pubsub_node{}, jid()) -> ok. send_authorization_request(#pubsub_node{nodeid = {Host, Node}, type = Type, id = Nidx, owners = O}, Subscriber) -> %% TODO: pass lang to this function Lang = <<"en">>, Fs = pubsub_subscribe_authorization:encode( [{node, Node}, {subscriber_jid, Subscriber}, {allow, false}], Lang), X = #xdata{type = form, title = translate:translate( Lang, <<"PubSub subscriber request">>), instructions = [translate:translate( Lang, <<"Choose whether to approve this entity's " "subscription.">>)], fields = Fs}, Stanza = #message{from = service_jid(Host), sub_els = [X]}, lists:foreach( fun (Owner) -> ejabberd_router:route(xmpp:set_to(Stanza, jid:make(Owner))) end, node_owners_action(Host, Type, Nidx, O)). -spec find_authorization_response(message()) -> undefined | pubsub_subscribe_authorization:result() | {error, stanza_error()}. find_authorization_response(Packet) -> case xmpp:get_subtag(Packet, #xdata{type = form}) of #xdata{type = cancel} -> undefined; #xdata{type = submit, fields = Fs} -> try pubsub_subscribe_authorization:decode(Fs) of Result -> Result catch _:{pubsub_subscribe_authorization, Why} -> Lang = xmpp:get_lang(Packet), Txt = pubsub_subscribe_authorization:format_error(Why), {error, xmpp:err_bad_request(Txt, Lang)} end; #xdata{} -> {error, xmpp:err_bad_request()}; false -> undefined end. %% @doc Send a message to JID with the supplied Subscription -spec send_authorization_approval(binary(), jid(), binary(), subscribed | none) -> ok. send_authorization_approval(Host, JID, SNode, Subscription) -> Event = #ps_event{subscription = #ps_subscription{jid = JID, node = SNode, type = Subscription}}, Stanza = #message{from = service_jid(Host), to = JID, sub_els = [Event]}, ejabberd_router:route(Stanza). -spec handle_authorization_response(binary(), message(), pubsub_subscribe_authorization:result()) -> ok. handle_authorization_response(Host, #message{from = From} = Packet, Response) -> Node = proplists:get_value(node, Response), Subscriber = proplists:get_value(subscriber_jid, Response), Allow = proplists:get_value(allow, Response), Lang = xmpp:get_lang(Packet), FromLJID = jid:tolower(jid:remove_resource(From)), Action = fun(#pubsub_node{type = Type, id = Nidx, owners = O}) -> Owners = node_owners_call(Host, Type, Nidx, O), case lists:member(FromLJID, Owners) of true -> {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]), update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs); false -> {error, xmpp:err_forbidden(<<"Owner privileges required">>, Lang)} end end, case transaction(Host, Node, Action, sync_dirty) of {error, Error} -> ejabberd_router:route_error(Packet, Error); {result, {_, _NewSubscription}} -> %% XXX: notify about subscription state change, section 12.11 ok; _ -> Err = xmpp:err_internal_server_error(), ejabberd_router:route_error(Packet, Err) end. -spec update_auth(binary(), binary(), _, _, jid() | error, boolean(), _) -> {result, ok} | {error, stanza_error()}. update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) -> Sub= lists:filter(fun ({pending, _}) -> true; (_) -> false end, Subs), case Sub of [{pending, SubId}|_] -> NewSub = case Allow of true -> subscribed; false -> none end, node_call(Host, Type, set_subscriptions, [Nidx, Subscriber, NewSub, SubId]), send_authorization_approval(Host, Subscriber, Node, NewSub), {result, ok}; _ -> Txt = <<"No pending subscriptions found">>, {error, xmpp:err_unexpected_request(Txt, ?MYLANG)} end. %% @doc

Create new pubsub nodes

%%

In addition to method-specific error conditions, there are several general reasons why the node creation request might fail:

%%
    %%
  • The service does not support node creation.
  • %%
  • Only entities that are registered with the service are allowed to create nodes but the requesting entity is not registered.
  • %%
  • The requesting entity does not have sufficient privileges to create nodes.
  • %%
  • The requested Node already exists.
  • %%
  • The request did not include a Node and "instant nodes" are not supported.
  • %%
%%

ote: node creation is a particular case, error return code is evaluated at many places:

%%
    %%
  • iq_pubsub checks if service supports node creation (type exists)
  • %%
  • create_node checks if instant nodes are supported
  • %%
  • create_node asks node plugin if entity have sufficient privilege
  • %%
  • nodetree create_node checks if nodeid already exists
  • %%
  • node plugin create_node just sets default affiliation/subscription
  • %%
-spec create_node(host(), binary(), binary(), jid(), binary()) -> {result, pubsub()} | {error, stanza_error()}. create_node(Host, ServerHost, Node, Owner, Type) -> create_node(Host, ServerHost, Node, Owner, Type, all, []). -spec create_node(host(), binary(), binary(), jid(), binary(), atom(), [{binary(), [binary()]}]) -> {result, pubsub()} | {error, stanza_error()}. create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) -> case lists:member(<<"instant-nodes">>, plugin_features(Host, Type)) of true -> Node = randoms:get_string(), case create_node(Host, ServerHost, Node, Owner, Type, Access, Configuration) of {result, _} -> {result, #pubsub{create = Node}}; Error -> Error end; false -> {error, extended_error(xmpp:err_not_acceptable(), err_nodeid_required())} end; create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> Type = select_type(ServerHost, Host, Node, GivenType), NodeOptions = merge_config(Configuration, node_options(Host, Type)), CreateNode = fun() -> Parent = case node_call(Host, Type, node_to_path, [Node]) of {result, [Node]} -> <<>>; {result, Path} -> element(2, node_call(Host, Type, path_to_node, [lists:sublist(Path, length(Path)-1)])) end, Parents = case Parent of <<>> -> []; _ -> [Parent] end, case node_call(Host, Type, create_node_permission, [Host, ServerHost, Node, Parent, Owner, Access]) of {result, true} -> case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of {ok, Nidx} -> SubsByDepth = get_node_subs_by_depth(Host, Node, Owner), case node_call(Host, Type, create_node, [Nidx, Owner]) of {result, Result} -> {result, {Nidx, SubsByDepth, Result}}; Error -> Error end; {error, {virtual, Nidx}} -> case node_call(Host, Type, create_node, [Nidx, Owner]) of {result, Result} -> {result, {Nidx, [], Result}}; Error -> Error end; Error -> Error end; _ -> Txt = <<"You're not allowed to create nodes">>, {error, xmpp:err_forbidden(Txt, ?MYLANG)} end end, Reply = #pubsub{create = Node}, case transaction(Host, CreateNode, transaction) of {result, {Nidx, SubsByDepth, {Result, broadcast}}} -> broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth), ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, Nidx, NodeOptions]), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {Nidx, _SubsByDepth, Result}} -> ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, Nidx, NodeOptions]), case Result of default -> {result, Reply}; _ -> {result, Result} end; Error -> %% in case we change transaction to sync_dirty... %% node_call(Host, Type, delete_node, [Host, Node]), %% tree_call(Host, delete_node, [Host, Node]), Error end. %% @doc

Delete specified node and all childs.

%%

There are several reasons why the node deletion request might fail:

%%
    %%
  • The requesting entity does not have sufficient privileges to delete the node.
  • %%
  • The node is the root collection node, which cannot be deleted.
  • %%
  • The specified node does not exist.
  • %%
-spec delete_node(host(), binary(), jid()) -> {result, pubsub_owner()} | {error, stanza_error()}. delete_node(_Host, <<>>, _Owner) -> {error, xmpp:err_not_allowed(<<"No node specified">>, ?MYLANG)}; delete_node(Host, Node, Owner) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> case node_call(Host, Type, get_affiliation, [Nidx, Owner]) of {result, owner} -> SubsByDepth = get_node_subs_by_depth(Host, Node, service_jid(Host)), Removed = tree_call(Host, delete_node, [Host, Node]), case node_call(Host, Type, delete_node, [Removed]) of {result, Res} -> {result, {SubsByDepth, Res}}; Error -> Error end; _ -> {error, xmpp:err_forbidden(<<"Owner privileges required">>, ?MYLANG)} end end, Reply = undefined, ServerHost = serverhost(Host), case transaction(Host, Node, Action, transaction) of {result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} -> lists:foreach(fun ({RNode, _RSubs}) -> {RH, RN} = RNode#pubsub_node.nodeid, RNidx = RNode#pubsub_node.id, RType = RNode#pubsub_node.type, ROptions = RNode#pubsub_node.options, unset_cached_item(RH, RNidx), broadcast_removed_node(RH, RN, RNidx, RType, ROptions, SubsByDepth), ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, RNidx]) end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {_, {_, {Result, Removed}}}} -> lists:foreach(fun ({RNode, _RSubs}) -> {RH, RN} = RNode#pubsub_node.nodeid, RNidx = RNode#pubsub_node.id, unset_cached_item(RH, RNidx), ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, RNidx]) end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {TNode, {_, Result}}} -> Nidx = TNode#pubsub_node.id, unset_cached_item(Host, Nidx), ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, Nidx]), case Result of default -> {result, Reply}; _ -> {result, Result} end; Error -> Error end. %% @see node_hometree:subscribe_node/5 %% @doc

Accepts or rejects subcription requests on a PubSub node.

%%

There are several reasons why the subscription request might fail:

%%
    %%
  • The bare JID portions of the JIDs do not match.
  • %%
  • The node has an access model of "presence" and the requesting entity is not subscribed to the owner's presence.
  • %%
  • The node has an access model of "roster" and the requesting entity is not in one of the authorized roster groups.
  • %%
  • The node has an access model of "whitelist" and the requesting entity is not on the whitelist.
  • %%
  • The service requires payment for subscriptions to the node.
  • %%
  • The requesting entity is anonymous and the service does not allow anonymous entities to subscribe.
  • %%
  • The requesting entity has a pending subscription.
  • %%
  • The requesting entity is blocked from subscribing (e.g., because having an affiliation of outcast).
  • %%
  • The node does not support subscriptions.
  • %%
  • The node does not exist.
  • %%
-spec subscribe_node(host(), binary(), jid(), jid(), [{binary(), [binary()]}]) -> {result, pubsub()} | {error, stanza_error()}. subscribe_node(Host, Node, From, JID, Configuration) -> SubModule = subscription_plugin(Host), SubOpts = case SubModule:parse_options_xform(Configuration) of {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, Subscriber = jid:tolower(JID), Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx, owners = O}) -> Features = plugin_features(Host, Type), SubscribeFeature = lists:member(<<"subscribe">>, Features), OptionsFeature = lists:member(<<"subscription-options">>, Features), HasOptions = not (SubOpts == []), SubscribeConfig = get_option(Options, subscribe), AccessModel = get_option(Options, access_model), SendLast = get_option(Options, send_last_published_item), AllowedGroups = get_option(Options, roster_groups_allowed, []), CanSubscribe = case get_max_subscriptions_node(Host) of Max when is_integer(Max) -> case node_call(Host, Type, get_node_subscriptions, [Nidx]) of {result, NodeSubs} -> SubsNum = lists:foldl( fun ({_, subscribed, _}, Acc) -> Acc+1; (_, Acc) -> Acc end, 0, NodeSubs), SubsNum < Max; _ -> true end; _ -> true end, if not SubscribeFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('subscribe'))}; not SubscribeConfig -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('subscribe'))}; HasOptions andalso not OptionsFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('subscription-options'))}; SubOpts == invalid -> {error, extended_error(xmpp:err_bad_request(), err_invalid_options())}; not CanSubscribe -> %% fallback to closest XEP compatible result, assume we are not allowed to subscribe {error, extended_error(xmpp:err_not_allowed(), err_closed_node())}; true -> Owners = node_owners_call(Host, Type, Nidx, O), {PS, RG} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups), node_call(Host, Type, subscribe_node, [Nidx, From, Subscriber, AccessModel, SendLast, PS, RG, SubOpts]) end end, Reply = fun (Subscription) -> Sub = case Subscription of {subscribed, SubId} -> #ps_subscription{jid = JID, type = subscribed, subid = SubId}; Other -> #ps_subscription{jid = JID, type = Other} end, #pubsub{subscription = Sub#ps_subscription{node = Node}} end, case transaction(Host, Node, Action, sync_dirty) of {result, {TNode, {Result, subscribed, SubId, send_last}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, send_items(Host, Node, Nidx, Type, Options, Subscriber, 1), ServerHost = serverhost(Host), ejabberd_hooks:run(pubsub_subscribe_node, ServerHost, [ServerHost, Host, Node, Subscriber, SubId]), case Result of default -> {result, Reply({subscribed, SubId})}; _ -> {result, Result} end; {result, {_TNode, {default, subscribed, SubId}}} -> {result, Reply({subscribed, SubId})}; {result, {_TNode, {Result, subscribed, _SubId}}} -> {result, Result}; {result, {TNode, {default, pending, _SubId}}} -> send_authorization_request(TNode, Subscriber), {result, Reply(pending)}; {result, {TNode, {Result, pending}}} -> send_authorization_request(TNode, Subscriber), {result, Result}; {result, {_, Result}} -> {result, Result}; Error -> Error end. %% @doc

Unsubscribe JID from the Node.

%%

There are several reasons why the unsubscribe request might fail:

%%
    %%
  • The requesting entity has multiple subscriptions to the node but does not specify a subscription ID.
  • %%
  • The request does not specify an existing subscriber.
  • %%
  • The requesting entity does not have sufficient privileges to unsubscribe the specified JID.
  • %%
  • The node does not exist.
  • %%
  • The request specifies a subscription ID that is not valid or current.
  • %%
-spec unsubscribe_node(host(), binary(), jid(), jid(), binary()) -> {result, undefined} | {error, stanza_error()}. unsubscribe_node(Host, Node, From, JID, SubId) -> Subscriber = jid:tolower(JID), Action = fun (#pubsub_node{type = Type, id = Nidx}) -> node_call(Host, Type, unsubscribe_node, [Nidx, From, Subscriber, SubId]) end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, default}} -> ServerHost = serverhost(Host), ejabberd_hooks:run(pubsub_unsubscribe_node, ServerHost, [ServerHost, Host, Node, Subscriber, SubId]), {result, undefined}; Error -> Error end. %% @doc

Publish item to a PubSub node.

%%

The permission to publish an item must be verified by the plugin implementation.

%%

There are several reasons why the publish request might fail:

%%
    %%
  • The requesting entity does not have sufficient privileges to publish.
  • %%
  • The node does not support item publication.
  • %%
  • The node does not exist.
  • %%
  • The payload size exceeds a service-defined limit.
  • %%
  • The item contains more than one payload element or the namespace of the root payload element does not match the configured namespace for the node.
  • %%
  • The request does not match the node configuration.
  • %%
-spec publish_item(host(), binary(), binary(), jid(), binary(), [xmlel()]) -> {result, pubsub()} | {error, stanza_error()}. publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, [], all). publish_item(Host, ServerHost, Node, Publisher, <<>>, Payload, PubOpts, Access) -> publish_item(Host, ServerHost, Node, Publisher, uniqid(), Payload, PubOpts, Access); publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, PubOpts, Access) -> Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) -> Features = plugin_features(Host, Type), PublishFeature = lists:member(<<"publish">>, Features), PublishModel = get_option(Options, publish_model), DeliverPayloads = get_option(Options, deliver_payloads), PersistItems = get_option(Options, persist_items), MaxItems = max_items(Host, Options), PayloadCount = payload_xmlelements(Payload), PayloadSize = byte_size(term_to_binary(Payload)) - 2, PayloadMaxSize = get_option(Options, max_payload_size), PreconditionsMet = preconditions_met(PubOpts, Options), if not PublishFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported(publish))}; not PreconditionsMet -> {error, extended_error(xmpp:err_conflict(), err_precondition_not_met())}; PayloadSize > PayloadMaxSize -> {error, extended_error(xmpp:err_not_acceptable(), err_payload_too_big())}; (PayloadCount == 0) and (Payload == []) -> {error, extended_error(xmpp:err_bad_request(), err_payload_required())}; (PayloadCount > 1) or (PayloadCount == 0) -> {error, extended_error(xmpp:err_bad_request(), err_invalid_payload())}; (DeliverPayloads == false) and (PersistItems == false) and (PayloadSize > 0) -> {error, extended_error(xmpp:err_bad_request(), err_item_forbidden())}; ((DeliverPayloads == true) or (PersistItems == true)) and (PayloadSize == 0) -> {error, extended_error(xmpp:err_bad_request(), err_item_required())}; true -> node_call(Host, Type, publish_item, [Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, PubOpts]) end end, Reply = #pubsub{publish = #ps_publish{node = Node, items = [#ps_item{id = ItemId}]}}, case transaction(Host, Node, Action, sync_dirty) of {result, {TNode, {Result, Broadcast, Removed}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, BrPayload = case Broadcast of broadcast -> Payload; PluginPayload -> PluginPayload end, set_cached_item(Host, Nidx, ItemId, Publisher, BrPayload), case get_option(Options, deliver_notifications) of true -> broadcast_publish_item(Host, Node, Nidx, Type, Options, ItemId, Publisher, BrPayload, Removed); false -> ok end, ejabberd_hooks:run(pubsub_publish_item, ServerHost, [ServerHost, Node, Publisher, service_jid(Host), ItemId, BrPayload]), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {TNode, {default, Removed}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, broadcast_retract_items(Host, Node, Nidx, Type, Options, Removed), set_cached_item(Host, Nidx, ItemId, Publisher, Payload), {result, Reply}; {result, {TNode, {Result, Removed}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, broadcast_retract_items(Host, Node, Nidx, Type, Options, Removed), set_cached_item(Host, Nidx, ItemId, Publisher, Payload), {result, Result}; {result, {_, default}} -> {result, Reply}; {result, {_, Result}} -> {result, Result}; {error, #stanza_error{reason = 'item-not-found'}} -> Type = select_type(ServerHost, Host, Node), case lists:member(<<"auto-create">>, plugin_features(Host, Type)) of true -> case create_node(Host, ServerHost, Node, Publisher, Type, Access, PubOpts) of {result, #pubsub{create = NewNode}} -> publish_item(Host, ServerHost, NewNode, Publisher, ItemId, Payload, PubOpts, Access); _ -> {error, xmpp:err_item_not_found()} end; false -> Txt = <<"Automatic node creation is not enabled">>, {error, xmpp:err_item_not_found(Txt, ?MYLANG)} end; Error -> Error end. %% @doc

Delete item from a PubSub node.

%%

The permission to delete an item must be verified by the plugin implementation.

%%

There are several reasons why the item retraction request might fail:

%%
    %%
  • The publisher does not have sufficient privileges to delete the requested item.
  • %%
  • The node or item does not exist.
  • %%
  • The request does not specify a node.
  • %%
  • The request does not include an element or the element does not specify an ItemId.
  • %%
  • The node does not support persistent items.
  • %%
  • The service does not support the deletion of items.
  • %%
-spec delete_item(host(), binary(), jid(), binary()) -> {result, undefined} | {error, stanza_error()}. delete_item(Host, Node, Publisher, ItemId) -> delete_item(Host, Node, Publisher, ItemId, false). delete_item(_, <<>>, _, _, _) -> {error, extended_error(xmpp:err_bad_request(), err_nodeid_required())}; delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) -> Features = plugin_features(Host, Type), PersistentFeature = lists:member(<<"persistent-items">>, Features), DeleteFeature = lists:member(<<"delete-items">>, Features), PublishModel = get_option(Options, publish_model), if %%-> iq_pubsub just does that matchs %% %% Request does not specify an item %% {error, extended_error(?ERR_BAD_REQUEST, "item-required")}; not PersistentFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('persistent-items'))}; not DeleteFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('delete-items'))}; true -> node_call(Host, Type, delete_item, [Nidx, Publisher, PublishModel, ItemId]) end end, Reply = undefined, case transaction(Host, Node, Action, sync_dirty) of {result, {TNode, {Result, broadcast}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, broadcast_retract_items(Host, Node, Nidx, Type, Options, [ItemId], ForceNotify), case get_cached_item(Host, Nidx) of #pubsub_item{itemid = {ItemId, Nidx}} -> unset_cached_item(Host, Nidx); _ -> ok end, case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {_, default}} -> {result, Reply}; {result, {_, Result}} -> {result, Result}; Error -> Error end. %% @doc

Delete all items of specified node owned by JID.

%%

There are several reasons why the node purge request might fail:

%%
    %%
  • The node or service does not support node purging.
  • %%
  • The requesting entity does not have sufficient privileges to purge the node.
  • %%
  • The node is not configured to persist items.
  • %%
  • The specified node does not exist.
  • %%
-spec purge_node(mod_pubsub:host(), binary(), jid()) -> {result, undefined} | {error, stanza_error()}. purge_node(Host, Node, Owner) -> Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) -> Features = plugin_features(Host, Type), PurgeFeature = lists:member(<<"purge-nodes">>, Features), PersistentFeature = lists:member(<<"persistent-items">>, Features), PersistentConfig = get_option(Options, persist_items), if not PurgeFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('purge-nodes'))}; not PersistentFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('persistent-items'))}; not PersistentConfig -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('persistent-items'))}; true -> node_call(Host, Type, purge_node, [Nidx, Owner]) end end, Reply = undefined, case transaction(Host, Node, Action, sync_dirty) of {result, {TNode, {Result, broadcast}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, broadcast_purge_node(Host, Node, Nidx, Type, Options), unset_cached_item(Host, Nidx), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {_, default}} -> {result, Reply}; {result, {_, Result}} -> {result, Result}; Error -> Error end. %% @doc

Return the items of a given node.

%%

The number of items to return is limited by MaxItems.

%%

The permission are not checked in this function.

-spec get_items(host(), binary(), jid(), binary(), binary(), [binary()], undefined | rsm_set()) -> {result, pubsub()} | {error, stanza_error()}. get_items(Host, Node, From, SubId, MaxItems, ItemIds, undefined) when MaxItems =/= undefined -> get_items(Host, Node, From, SubId, MaxItems, ItemIds, #rsm_set{max = MaxItems, before = <<>>}); get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) -> Action = fun(#pubsub_node{options = Options, type = Type, id = Nidx, owners = O}) -> Features = plugin_features(Host, Type), RetreiveFeature = lists:member(<<"retrieve-items">>, Features), PersistentFeature = lists:member(<<"persistent-items">>, Features), AccessModel = get_option(Options, access_model), AllowedGroups = get_option(Options, roster_groups_allowed, []), if not RetreiveFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('retrieve-items'))}; not PersistentFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('persistent-items'))}; true -> Owners = node_owners_call(Host, Type, Nidx, O), {PS, RG} = get_presence_and_roster_permissions( Host, From, Owners, AccessModel, AllowedGroups), case ItemIds of [ItemId] -> node_call(Host, Type, get_item, [Nidx, ItemId, From, AccessModel, PS, RG, undefined]); _ -> node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, SubId, RSM]) end end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, {Items, RsmOut}}} -> SendItems = case ItemIds of [] -> Items; _ -> lists:filter( fun(#pubsub_item{itemid = {ItemId, _}}) -> lists:member(ItemId, ItemIds) end, Items) end, {result, #pubsub{items = #ps_items{node = Node, items = itemsEls(SendItems)}, rsm = RsmOut}}; {result, {_, Item}} -> {result, #pubsub{items = #ps_items{node = Node, items = itemsEls([Item])}}}; Error -> Error end. get_items(Host, Node) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> node_call(Host, Type, get_items, [Nidx, service_jid(Host), undefined]) end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, {Items, _}}} -> Items; Error -> Error end. get_item(Host, Node, ItemId) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> node_call(Host, Type, get_item, [Nidx, ItemId]) end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Items}} -> Items; Error -> Error end. get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) -> case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, undefined) of {result, {Items, _RSM}} -> {result, Items}; Error -> Error end. get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) -> AccessModel = get_option(Options, access_model), AllowedGroups = get_option(Options, roster_groups_allowed, []), {PS, RG} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, undefined, RSM]). get_last_items(Host, Type, Nidx, LJID, 1) -> case get_cached_item(Host, Nidx) of undefined -> case node_action(Host, Type, get_last_items, [Nidx, LJID, 1]) of {result, Items} -> Items; _ -> [] end; LastItem -> [LastItem] end; get_last_items(Host, Type, Nidx, LJID, Count) when Count > 1 -> case node_action(Host, Type, get_last_items, [Nidx, LJID, Count]) of {result, Items} -> Items; _ -> [] end; get_last_items(_Host, _Type, _Nidx, _LJID, _Count) -> []. %% @doc

Return the list of affiliations as an XMPP response.

-spec get_affiliations(host(), binary(), jid(), [binary()]) -> {result, pubsub()} | {error, stanza_error()}. get_affiliations(Host, Node, JID, Plugins) when is_list(Plugins) -> Result = lists:foldl( fun(Type, {Status, Acc}) -> Features = plugin_features(Host, Type), RetrieveFeature = lists:member(<<"retrieve-affiliations">>, Features), if not RetrieveFeature -> {{error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('retrieve-affiliations'))}, Acc}; true -> {result, Affs} = node_action(Host, Type, get_entity_affiliations, [Host, JID]), {Status, [Affs | Acc]} end end, {ok, []}, Plugins), case Result of {ok, Affs} -> Entities = lists:flatmap( fun({_, none}) -> []; ({#pubsub_node{nodeid = {_, NodeId}}, Aff}) -> if (Node == <<>>) or (Node == NodeId) -> [#ps_affiliation{node = NodeId, type = Aff}]; true -> [] end; (_) -> [] end, lists:usort(lists:flatten(Affs))), {result, #pubsub{affiliations = {<<>>, Entities}}}; {Error, _} -> Error end. -spec get_affiliations(host(), binary(), jid()) -> {result, pubsub_owner()} | {error, stanza_error()}. get_affiliations(Host, Node, JID) -> Action = fun(#pubsub_node{type = Type, id = Nidx}) -> Features = plugin_features(Host, Type), RetrieveFeature = lists:member(<<"modify-affiliations">>, Features), {result, Affiliation} = node_call(Host, Type, get_affiliation, [Nidx, JID]), if not RetrieveFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('modify-affiliations'))}; Affiliation /= owner -> {error, xmpp:err_forbidden(<<"Owner privileges required">>, ?MYLANG)}; true -> node_call(Host, Type, get_node_affiliations, [Nidx]) end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, []}} -> {error, xmpp:err_item_not_found()}; {result, {_, Affs}} -> Entities = lists:flatmap( fun({_, none}) -> []; ({AJID, Aff}) -> [#ps_affiliation{jid = AJID, type = Aff}] end, Affs), {result, #pubsub_owner{affiliations = {Node, Entities}}}; Error -> Error end. -spec set_affiliations(host(), binary(), jid(), [ps_affiliation()]) -> {result, undefined} | {error, stanza_error()}. set_affiliations(Host, Node, From, Affs) -> Owner = jid:tolower(jid:remove_resource(From)), Action = fun(#pubsub_node{type = Type, id = Nidx, owners = O} = N) -> Owners = node_owners_call(Host, Type, Nidx, O), case lists:member(Owner, Owners) of true -> OwnerJID = jid:make(Owner), FilteredAffs = case Owners of [Owner] -> [Aff || Aff <- Affs, Aff#ps_affiliation.jid /= OwnerJID]; _ -> Affs end, lists:foreach( fun(#ps_affiliation{jid = JID, type = Affiliation}) -> node_call(Host, Type, set_affiliation, [Nidx, JID, Affiliation]), case Affiliation of owner -> NewOwner = jid:tolower(jid:remove_resource(JID)), NewOwners = [NewOwner | Owners], tree_call(Host, set_node, [N#pubsub_node{owners = NewOwners}]); none -> OldOwner = jid:tolower(jid:remove_resource(JID)), case lists:member(OldOwner, Owners) of true -> NewOwners = Owners -- [OldOwner], tree_call(Host, set_node, [N#pubsub_node{owners = NewOwners}]); _ -> ok end; _ -> ok end end, FilteredAffs), {result, undefined}; _ -> {error, xmpp:err_forbidden( <<"Owner privileges required">>, ?MYLANG)} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> {result, Result}; Other -> Other end. -spec get_options(binary(), binary(), jid(), binary(), binary()) -> {result, xdata()} | {error, stanza_error()}. get_options(Host, Node, JID, SubId, Lang) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> case lists:member(<<"subscription-options">>, plugin_features(Host, Type)) of true -> get_options_helper(Host, JID, Lang, Node, Nidx, SubId, Type); false -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('subscription-options'))} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_Node, XForm}} -> {result, XForm}; Error -> Error end. -spec get_options_helper(binary(), jid(), binary(), binary(), _, binary(), binary()) -> {result, pubsub()} | {error, stanza_error()}. get_options_helper(Host, JID, Lang, Node, Nidx, SubId, Type) -> Subscriber = jid:tolower(JID), {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]), SubIds = [Id || {Sub, Id} <- Subs, Sub == subscribed], case {SubId, SubIds} of {_, []} -> {error, extended_error(xmpp:err_not_acceptable(), err_not_subscribed())}; {<<>>, [SID]} -> read_sub(Host, Node, Nidx, Subscriber, SID, Lang); {<<>>, _} -> {error, extended_error(xmpp:err_not_acceptable(), err_subid_required())}; {_, _} -> ValidSubId = lists:member(SubId, SubIds), if ValidSubId -> read_sub(Host, Node, Nidx, Subscriber, SubId, Lang); true -> {error, extended_error(xmpp:err_not_acceptable(), err_invalid_subid())} end end. -spec read_sub(binary(), binary(), nodeIdx(), ljid(), binary(), binary()) -> {result, pubsub()}. read_sub(Host, Node, Nidx, Subscriber, SubId, Lang) -> SubModule = subscription_plugin(Host), XData = case SubModule:get_subscription(Subscriber, Nidx, SubId) of {error, notfound} -> undefined; {result, #pubsub_subscription{options = Options}} -> {result, X} = SubModule:get_options_xform(Lang, Options), X end, {result, #pubsub{options = #ps_options{jid = jid:make(Subscriber), subid = SubId, node = Node, xdata = XData}}}. -spec set_options(binary(), binary(), jid(), binary(), [{binary(), [binary()]}]) -> {result, undefined} | {error, stanza_error()}. set_options(Host, Node, JID, SubId, Configuration) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> case lists:member(<<"subscription-options">>, plugin_features(Host, Type)) of true -> set_options_helper(Host, Configuration, JID, Nidx, SubId, Type); false -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('subscription-options'))} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_Node, Result}} -> {result, Result}; Error -> Error end. -spec set_options_helper(binary(), [{binary(), [binary()]}], jid(), nodeIdx(), binary(), binary()) -> {result, undefined} | {error, stanza_error()}. set_options_helper(Host, Configuration, JID, Nidx, SubId, Type) -> SubModule = subscription_plugin(Host), SubOpts = case SubModule:parse_options_xform(Configuration) of {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, Subscriber = jid:tolower(JID), {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]), SubIds = [Id || {Sub, Id} <- Subs, Sub == subscribed], case {SubId, SubIds} of {_, []} -> {error, extended_error(xmpp:err_not_acceptable(), err_not_subscribed())}; {<<>>, [SID]} -> write_sub(Host, Nidx, Subscriber, SID, SubOpts); {<<>>, _} -> {error, extended_error(xmpp:err_not_acceptable(), err_subid_required())}; {_, _} -> write_sub(Host, Nidx, Subscriber, SubId, SubOpts) end. -spec write_sub(binary(), nodeIdx(), ljid(), binary(), _) -> {result, undefined} | {error, stanza_error()}. write_sub(_Host, _Nidx, _Subscriber, _SubId, invalid) -> {error, extended_error(xmpp:err_bad_request(), err_invalid_options())}; write_sub(_Host, _Nidx, _Subscriber, _SubId, []) -> {result, undefined}; write_sub(Host, Nidx, Subscriber, SubId, Options) -> SubModule = subscription_plugin(Host), case SubModule:set_subscription(Subscriber, Nidx, SubId, Options) of {result, _} -> {result, undefined}; {error, _} -> {error, extended_error(xmpp:err_not_acceptable(), err_invalid_subid())} end. %% @doc

Return the list of subscriptions as an XMPP response.

-spec get_subscriptions(host(), binary(), jid(), [binary()]) -> {result, pubsub()} | {error, stanza_error()}. get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) -> Result = lists:foldl(fun (Type, {Status, Acc}) -> Features = plugin_features(Host, Type), RetrieveFeature = lists:member(<<"retrieve-subscriptions">>, Features), if not RetrieveFeature -> {{error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('retrieve-subscriptions'))}, Acc}; true -> Subscriber = jid:remove_resource(JID), {result, Subs} = node_action(Host, Type, get_entity_subscriptions, [Host, Subscriber]), {Status, [Subs | Acc]} end end, {ok, []}, Plugins), case Result of {ok, Subs} -> Entities = lists:flatmap(fun ({#pubsub_node{nodeid = {_, SubsNode}}, Sub}) -> case Node of <<>> -> [#ps_subscription{node = SubsNode, type = Sub}]; SubsNode -> [#ps_subscription{type = Sub}]; _ -> [] end; ({#pubsub_node{nodeid = {_, SubsNode}}, Sub, SubId, SubJID}) -> case Node of <<>> -> [#ps_subscription{jid = SubJID, subid = SubId, type = Sub, node = SubsNode}]; SubsNode -> [#ps_subscription{jid = SubJID, subid = SubId, type = Sub}]; _ -> [] end; ({#pubsub_node{nodeid = {_, SubsNode}}, Sub, SubJID}) -> case Node of <<>> -> [#ps_subscription{jid = SubJID, type = Sub, node = SubsNode}]; SubsNode -> [#ps_subscription{jid = SubJID, type = Sub}]; _ -> [] end end, lists:usort(lists:flatten(Subs))), {result, #pubsub{subscriptions = {<<>>, Entities}}}; {Error, _} -> Error end. -spec get_subscriptions(host(), binary(), jid()) -> {result, pubsub_owner()} | {error, stanza_error()}. get_subscriptions(Host, Node, JID) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> Features = plugin_features(Host, Type), RetrieveFeature = lists:member(<<"manage-subscriptions">>, Features), {result, Affiliation} = node_call(Host, Type, get_affiliation, [Nidx, JID]), if not RetrieveFeature -> {error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('manage-subscriptions'))}; Affiliation /= owner -> {error, xmpp:err_forbidden(<<"Owner privileges required">>, ?MYLANG)}; true -> node_call(Host, Type, get_node_subscriptions, [Nidx]) end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Subs}} -> Entities = lists:flatmap( fun({_, none}) -> []; ({_, pending, _}) -> []; ({AJID, Sub}) -> [#ps_subscription{jid = AJID, type = Sub}]; ({AJID, Sub, SubId}) -> [#ps_subscription{jid = AJID, type = Sub, subid = SubId}] end, Subs), {result, #pubsub_owner{subscriptions = {Node, Entities}}}; Error -> Error end. get_subscriptions_for_send_last(Host, PType, sql, JID, LJID, BJID) -> {result, Subs} = node_action(Host, PType, get_entity_subscriptions_for_send_last, [Host, JID]), [{Node, SubId, SubJID} || {Node, Sub, SubId, SubJID} <- Subs, Sub =:= subscribed, (SubJID == LJID) or (SubJID == BJID)]; % sql version already filter result by on_sub_and_presence get_subscriptions_for_send_last(Host, PType, _, JID, LJID, BJID) -> {result, Subs} = node_action(Host, PType, get_entity_subscriptions, [Host, JID]), [{Node, SubId, SubJID} || {Node, Sub, SubId, SubJID} <- Subs, Sub =:= subscribed, (SubJID == LJID) or (SubJID == BJID), match_option(Node, send_last_published_item, on_sub_and_presence)]. -spec set_subscriptions(host(), binary(), jid(), [ps_subscription()]) -> {result, undefined} | {error, stanza_error()}. set_subscriptions(Host, Node, From, Entities) -> Owner = jid:tolower(jid:remove_resource(From)), Notify = fun(#ps_subscription{jid = JID, type = Sub}) -> Stanza = #message{ from = service_jid(Host), to = JID, sub_els = [#ps_event{ subscription = #ps_subscription{ jid = JID, type = Sub, node = Node}}]}, ejabberd_router:route(Stanza) end, Action = fun(#pubsub_node{type = Type, id = Nidx, owners = O}) -> Owners = node_owners_call(Host, Type, Nidx, O), case lists:member(Owner, Owners) of true -> Result = lists:foldl( fun(_, {error, _} = Err) -> Err; (#ps_subscription{jid = JID, type = Sub, subid = SubId} = Entity, _) -> case node_call(Host, Type, set_subscriptions, [Nidx, JID, Sub, SubId]) of {error, _} = Err -> Err; _ -> Notify(Entity) end end, ok, Entities), case Result of ok -> {result, undefined}; {error, _} = Err -> Err end; _ -> {error, xmpp:err_forbidden( <<"Owner privileges required">>, ?MYLANG)} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> {result, Result}; Other -> Other end. -spec get_presence_and_roster_permissions( host(), ljid(), [ljid()], accessModel(), [binary()]) -> {boolean(), boolean()}. get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) -> if (AccessModel == presence) or (AccessModel == roster) -> case Host of {User, Server, _} -> get_roster_info(User, Server, From, AllowedGroups); _ -> [{OUser, OServer, _} | _] = Owners, get_roster_info(OUser, OServer, From, AllowedGroups) end; true -> {true, true} end. get_roster_info(_, _, {<<>>, <<>>, _}, _) -> {false, false}; get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) -> LJID = {SubscriberUser, SubscriberServer, <<>>}, {Subscription, Groups} = ejabberd_hooks:run_fold(roster_get_jid_info, OwnerServer, {none, []}, [OwnerUser, OwnerServer, LJID]), PresenceSubscription = Subscription == both orelse Subscription == from orelse {OwnerUser, OwnerServer} == {SubscriberUser, SubscriberServer}, RosterGroup = lists:any(fun (Group) -> lists:member(Group, AllowedGroups) end, Groups), {PresenceSubscription, RosterGroup}; get_roster_info(OwnerUser, OwnerServer, JID, AllowedGroups) -> get_roster_info(OwnerUser, OwnerServer, jid:tolower(JID), AllowedGroups). -spec preconditions_met(pubsub_publish_options:result(), pubsub_node_config:result()) -> boolean(). preconditions_met(PubOpts, NodeOpts) -> lists:all(fun(Opt) -> lists:member(Opt, NodeOpts) end, PubOpts). -spec service_jid(jid() | ljid() | binary()) -> jid(). service_jid(#jid{} = Jid) -> Jid; service_jid({U, S, R}) -> jid:make(U, S, R); service_jid(Host) -> jid:make(Host). %% @spec (LJID, NotifyType, Depth, NodeOptions, SubOptions) -> boolean() %% LJID = jid() %% NotifyType = items | nodes %% Depth = integer() %% NodeOptions = [{atom(), term()}] %% SubOptions = [{atom(), term()}] %% @doc

Check if a notification must be delivered or not based on %% node and subscription options.

is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) -> sub_to_deliver(LJID, NotifyType, Depth, SubOptions) andalso node_to_deliver(LJID, NodeOptions). sub_to_deliver(_LJID, NotifyType, Depth, SubOptions) -> lists:all(fun (Option) -> sub_option_can_deliver(NotifyType, Depth, Option) end, SubOptions). node_to_deliver(LJID, NodeOptions) -> presence_can_deliver(LJID, get_option(NodeOptions, presence_based_delivery)). sub_option_can_deliver(items, _, {subscription_type, nodes}) -> false; sub_option_can_deliver(nodes, _, {subscription_type, items}) -> false; sub_option_can_deliver(_, _, {subscription_depth, all}) -> true; sub_option_can_deliver(_, Depth, {subscription_depth, D}) -> Depth =< D; sub_option_can_deliver(_, _, {deliver, false}) -> false; sub_option_can_deliver(_, _, {expire, When}) -> p1_time_compat:timestamp() < When; sub_option_can_deliver(_, _, _) -> true. -spec presence_can_deliver(ljid(), boolean()) -> boolean(). presence_can_deliver(_, false) -> true; presence_can_deliver({User, Server, Resource}, true) -> case ejabberd_sm:get_user_present_resources(User, Server) of [] -> false; Ss -> lists:foldl(fun (_, true) -> true; ({_, R}, _Acc) -> case Resource of <<>> -> true; R -> true; _ -> false end end, false, Ss) end. -spec state_can_deliver(ljid(), subOptions() | []) -> [ljid()]. state_can_deliver({U, S, R}, []) -> [{U, S, R}]; state_can_deliver({U, S, R}, SubOptions) -> case lists:keysearch(show_values, 1, SubOptions) of %% If not in suboptions, item can be delivered, case doesn't apply false -> [{U, S, R}]; %% If in a suboptions ... {_, {_, ShowValues}} -> Resources = case R of %% If the subscriber JID is a bare one, get all its resources <<>> -> user_resources(U, S); %% If the subscriber JID is a full one, use its resource R -> [R] end, lists:foldl(fun (Resource, Acc) -> get_resource_state({U, S, Resource}, ShowValues, Acc) end, [], Resources) end. -spec get_resource_state(ljid(), [binary()], [ljid()]) -> [ljid()]. get_resource_state({U, S, R}, ShowValues, JIDs) -> case ejabberd_sm:get_session_pid(U, S, R) of none -> %% If no PID, item can be delivered lists:append([{U, S, R}], JIDs); Pid -> Show = case ejabberd_c2s:get_presence(Pid) of #presence{type = unavailable} -> <<"unavailable">>; #presence{show = undefined} -> <<"online">>; #presence{show = S} -> atom_to_binary(S, latin1) end, case lists:member(Show, ShowValues) of %% If yes, item can be delivered true -> lists:append([{U, S, R}], JIDs); %% If no, item can't be delivered false -> JIDs end end. -spec payload_xmlelements([xmlel()]) -> non_neg_integer(). payload_xmlelements(Payload) -> payload_xmlelements(Payload, 0). payload_xmlelements([], Count) -> Count; payload_xmlelements([#xmlel{} | Tail], Count) -> payload_xmlelements(Tail, Count + 1); payload_xmlelements([_ | Tail], Count) -> payload_xmlelements(Tail, Count). items_event_stanza(Node, Options, Items) -> MoreEls = case Items of [LastItem] -> {ModifNow, ModifUSR} = LastItem#pubsub_item.modification, [#delay{stamp = ModifNow, from = jid:make(ModifUSR)}]; _ -> [] end, BaseStanza = #message{ sub_els = [#ps_event{items = #ps_items{ node = Node, items = itemsEls(Items)}} | MoreEls]}, NotificationType = get_option(Options, notification_type, headline), add_message_type(BaseStanza, NotificationType). %%%%%% broadcast functions broadcast_publish_item(Host, Node, Nidx, Type, NodeOptions, ItemId, From, Payload, Removed) -> case get_collection_subscriptions(Host, Node) of SubsByDepth when is_list(SubsByDepth) -> EventItem0 = case get_option(NodeOptions, deliver_payloads) of true -> #ps_item{xml_els = Payload, id = ItemId}; false -> #ps_item{id = ItemId} end, EventItem = case get_option(NodeOptions, itemreply, none) of owner -> %% owner not supported EventItem0; publisher -> EventItem0#ps_item{ publisher = jid:encode(From)}; none -> EventItem0 end, Stanza = #message{ sub_els = [#ps_event{items = #ps_items{node = Node, items = [EventItem]}}]}, broadcast_stanza(Host, From, Node, Nidx, Type, NodeOptions, SubsByDepth, items, Stanza, true), case Removed of [] -> ok; _ -> case get_option(NodeOptions, notify_retract) of true -> RetractStanza = #message{ sub_els = [#ps_event{ items = #ps_items{ node = Node, retract = Removed}}]}, broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, items, RetractStanza, true); _ -> ok end end, {result, true}; _ -> {result, false} end. broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds) -> broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, false). broadcast_retract_items(_Host, _Node, _Nidx, _Type, _NodeOptions, [], _ForceNotify) -> {result, false}; broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, ForceNotify) -> case (get_option(NodeOptions, notify_retract) or ForceNotify) of true -> case get_collection_subscriptions(Host, Node) of SubsByDepth when is_list(SubsByDepth) -> Stanza = #message{ sub_els = [#ps_event{ items = #ps_items{ node = Node, retract = ItemIds}}]}, broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, items, Stanza, true), {result, true}; _ -> {result, false} end; _ -> {result, false} end. broadcast_purge_node(Host, Node, Nidx, Type, NodeOptions) -> case get_option(NodeOptions, notify_retract) of true -> case get_collection_subscriptions(Host, Node) of SubsByDepth when is_list(SubsByDepth) -> Stanza = #message{sub_els = [#ps_event{purge = Node}]}, broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, nodes, Stanza, false), {result, true}; _ -> {result, false} end; _ -> {result, false} end. broadcast_removed_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth) -> case get_option(NodeOptions, notify_delete) of true -> case SubsByDepth of [] -> {result, false}; _ -> Stanza = #message{sub_els = [#ps_event{delete = {Node, <<>>}}]}, broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, nodes, Stanza, false), {result, true} end; _ -> {result, false} end. broadcast_created_node(_, _, _, _, _, []) -> {result, false}; broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth) -> Stanza = #message{sub_els = [#ps_event{create = Node}]}, broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, nodes, Stanza, true), {result, true}. broadcast_config_notification(Host, Node, Nidx, Type, NodeOptions, Lang) -> case get_option(NodeOptions, notify_config) of true -> case get_collection_subscriptions(Host, Node) of SubsByDepth when is_list(SubsByDepth) -> Content = case get_option(NodeOptions, deliver_payloads) of true -> #xdata{type = result, fields = get_configure_xfields( Type, NodeOptions, Lang, [])}; false -> undefined end, Stanza = #message{ sub_els = [#ps_event{ configuration = {Node, Content}}]}, broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, nodes, Stanza, false), {result, true}; _ -> {result, false} end; _ -> {result, false} end. get_collection_subscriptions(Host, Node) -> Action = fun() -> {result, get_node_subs_by_depth(Host, Node, service_jid(Host))} end, case transaction(Host, Action, sync_dirty) of {result, CollSubs} -> CollSubs; _ -> [] end. get_node_subs_by_depth(Host, Node, From) -> ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, From]), [{Depth, [{N, get_node_subs(Host, N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree]. get_node_subs(Host, #pubsub_node{type = Type, id = Nidx}) -> WithOptions = lists:member(<<"subscription-options">>, plugin_features(Host, Type)), case node_call(Host, Type, get_node_subscriptions, [Nidx]) of {result, Subs} -> get_options_for_subs(Host, Nidx, Subs, WithOptions); Other -> Other end. get_options_for_subs(_Host, _Nidx, Subs, false) -> lists:foldl(fun({JID, subscribed, SubID}, Acc) -> [{JID, SubID, []} | Acc]; (_, Acc) -> Acc end, [], Subs); get_options_for_subs(Host, Nidx, Subs, true) -> SubModule = subscription_plugin(Host), lists:foldl(fun({JID, subscribed, SubID}, Acc) -> case SubModule:get_subscription(JID, Nidx, SubID) of #pubsub_subscription{options = Options} -> [{JID, SubID, Options} | Acc]; {error, notfound} -> [{JID, SubID, []} | Acc] end; (_, Acc) -> Acc end, [], Subs). broadcast_stanza(Host, _Node, _Nidx, _Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> NotificationType = get_option(NodeOptions, notification_type, headline), BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull Stanza = add_message_type( xmpp:set_from(BaseStanza, service_jid(Host)), NotificationType), %% Handles explicit subscriptions SubIDsByJID = subscribed_nodes_by_jid(NotifyType, SubsByDepth), lists:foreach(fun ({LJID, _NodeName, SubIDs}) -> LJIDs = case BroadcastAll of true -> {U, S, _} = LJID, [{U, S, R} || R <- user_resources(U, S)]; false -> [LJID] end, %% Determine if the stanza should have SHIM ('SubID' and 'name') headers StanzaToSend = case {SHIM, SubIDs} of {false, _} -> Stanza; %% If there's only one SubID, don't add it {true, [_]} -> Stanza; {true, SubIDs} -> add_shim_headers(Stanza, subid_shim(SubIDs)) end, lists:foreach(fun(To) -> ejabberd_router:route( xmpp:set_to(StanzaToSend, jid:make(To))) end, LJIDs) end, SubIDsByJID). broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> broadcast_stanza({LUser, LServer, <<>>}, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM), %% Handles implicit presence subscriptions SenderResource = user_resource(LUser, LServer, LResource), NotificationType = get_option(NodeOptions, notification_type, headline), %% set the from address on the notification to the bare JID of the account owner %% Also, add "replyto" if entity has presence subscription to the account owner %% See XEP-0163 1.1 section 4.3.1 FromBareJid = xmpp:set_from(BaseStanza, jid:make(LUser, LServer)), Stanza = add_extended_headers( add_message_type(FromBareJid, NotificationType), extended_headers([Publisher])), ejabberd_sm:route(jid:make(LUser, LServer, SenderResource), {pep_message, <<((Node))/binary, "+notify">>, Stanza}), ejabberd_router:route(xmpp:set_to(Stanza, jid:make(LUser, LServer))); broadcast_stanza(Host, _Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM). -spec c2s_handle_info(ejabberd_c2s:state(), term()) -> ejabberd_c2s:state(). c2s_handle_info(#{lserver := LServer} = C2SState, {pep_message, Feature, Packet}) -> [maybe_send_pep_stanza(LServer, USR, Caps, Feature, Packet) || {USR, Caps} <- mod_caps:list_features(C2SState)], {stop, C2SState}; c2s_handle_info(#{lserver := LServer} = C2SState, {pep_message, Feature, Packet, USR}) -> case mod_caps:get_user_caps(USR, C2SState) of {ok, Caps} -> maybe_send_pep_stanza(LServer, USR, Caps, Feature, Packet); error -> ok end, {stop, C2SState}; c2s_handle_info(C2SState, _) -> C2SState. send_items(Host, Node, Nidx, Type, Options, LJID, Number) -> send_items(Host, Node, Nidx, Type, Options, Host, LJID, LJID, Number). send_items(Host, Node, Nidx, Type, Options, Publisher, SubLJID, ToLJID, Number) -> case get_last_items(Host, Type, Nidx, SubLJID, Number) of [] -> ok; Items -> Stanza = items_event_stanza(Node, Options, Items), send_stanza(Publisher, ToLJID, Node, Stanza) end. send_stanza({LUser, LServer, _} = Publisher, USR, Node, BaseStanza) -> Stanza = xmpp:set_from(BaseStanza, jid:make(LUser, LServer)), USRs = case USR of {PUser, PServer, <<>>} -> [{PUser, PServer, PRessource} || PRessource <- user_resources(PUser, PServer)]; _ -> [USR] end, [ejabberd_sm:route(jid:make(Publisher), {pep_message, <<((Node))/binary, "+notify">>, add_extended_headers( Stanza, extended_headers([Publisher])), To}) || To <- USRs]; send_stanza(Host, USR, _Node, Stanza) -> ejabberd_router:route( xmpp:set_from_to(Stanza, service_jid(Host), jid:make(USR))). maybe_send_pep_stanza(LServer, USR, Caps, Feature, Packet) -> Features = mod_caps:get_features(LServer, Caps), case lists:member(Feature, Features) of true -> ejabberd_router:route(xmpp:set_to(Packet, jid:make(USR))); false -> ok end. send_last_items(JID) -> ServerHost = JID#jid.lserver, Host = host(ServerHost), DBType = config(ServerHost, db_type), LJID = jid:tolower(JID), BJID = jid:remove_resource(LJID), lists:foreach( fun(PType) -> Subs = get_subscriptions_for_send_last(Host, PType, DBType, JID, LJID, BJID), lists:foreach( fun({#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}, _, SubJID}) when Type == PType-> send_items(Host, Node, Nidx, PType, Options, Host, SubJID, LJID, 1); (_) -> ok end, lists:usort(Subs)) end, config(ServerHost, plugins)). % pep_from_offline hack can not work anymore, as sender c2s does not % exists when sender is offline, so we can't get match receiver caps % does it make sens to send PEP from an offline contact anyway ? % case config(ServerHost, ignore_pep_from_offline) of % false -> % Roster = ejabberd_hooks:run_fold(roster_get, ServerHost, [], % [{JID#jid.luser, ServerHost}]), % lists:foreach( % fun(#roster{jid = {U, S, R}, subscription = Sub}) % when Sub == both orelse Sub == from, % S == ServerHost -> % case user_resources(U, S) of % [] -> send_last_pep(jid:make(U, S, R), JID); % _ -> ok %% this is already handled by presence probe % end; % (_) -> % ok %% we can not do anything in any cases % end, Roster); % true -> % ok % end. send_last_pep(From, To) -> ServerHost = From#jid.lserver, Host = host(ServerHost), Publisher = jid:tolower(From), Owner = jid:remove_resource(Publisher), lists:foreach( fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}) -> case match_option(Options, send_last_published_item, on_sub_and_presence) of true -> LJID = jid:tolower(To), Subscribed = case get_option(Options, access_model) of open -> true; presence -> true; whitelist -> false; % subscribers are added manually authorize -> false; % likewise roster -> Grps = get_option(Options, roster_groups_allowed, []), {OU, OS, _} = Owner, element(2, get_roster_info(OU, OS, LJID, Grps)) end, if Subscribed -> send_items(Owner, Node, Nidx, Type, Options, Publisher, LJID, LJID, 1); true -> ok end; _ -> ok end end, tree_action(Host, get_nodes, [Owner, From])). subscribed_nodes_by_jid(NotifyType, SubsByDepth) -> NodesToDeliver = fun (Depth, Node, Subs, Acc) -> NodeName = case Node#pubsub_node.nodeid of {_, N} -> N; Other -> Other end, NodeOptions = Node#pubsub_node.options, lists:foldl(fun({LJID, SubID, SubOptions}, {JIDs, Recipients}) -> case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of true -> case state_can_deliver(LJID, SubOptions) of [] -> {JIDs, Recipients}; [LJID] -> {JIDs, [{LJID, NodeName, [SubID]} | Recipients]}; JIDsToDeliver -> lists:foldl( fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) -> case lists:member(JIDToDeliver, JIDs) of %% check if the JIDs co-accumulator contains the Subscription Jid, false -> %% - if not, %% - add the Jid to JIDs list co-accumulator ; %% - create a tuple of the Jid, Nidx, and SubID (as list), %% and add the tuple to the Recipients list co-accumulator {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]}; true -> %% - if the JIDs co-accumulator contains the Jid %% get the tuple containing the Jid from the Recipient list co-accumulator {_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc), %% delete the tuple from the Recipients list % v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients), % v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, Nidx1, [SubID | SubIDs]}), %% add the SubID to the SubIDs list in the tuple, %% and add the tuple back to the Recipients list co-accumulator % v1.1 : {JIDs, lists:append(Recipients1, [{LJID, Nidx1, lists:append(SubIDs, [SubID])}])} % v1.2 : {JIDs, [{LJID, Nidx1, [SubID | SubIDs]} | Recipients1]} % v2: {JIDs, Recipients1} {JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})} end end, {JIDs, Recipients}, JIDsToDeliver) end; false -> {JIDs, Recipients} end end, Acc, Subs) end, DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) -> lists:foldl(fun({Node, Subs}, Acc2) -> NodesToDeliver(Depth, Node, Subs, Acc2) end, Acc1, SubsByNode) end, {_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth), JIDSubs. -spec user_resources(binary(), binary()) -> [binary()]. user_resources(User, Server) -> ejabberd_sm:get_user_resources(User, Server). -spec user_resource(binary(), binary(), binary()) -> binary(). user_resource(User, Server, <<>>) -> case user_resources(User, Server) of [R | _] -> R; _ -> <<>> end; user_resource(_, _, Resource) -> Resource. %%%%%%% Configuration handling -spec get_configure(host(), binary(), binary(), jid(), binary()) -> {error, stanza_error()} | {result, pubsub_owner()}. get_configure(Host, ServerHost, Node, From, Lang) -> Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) -> case node_call(Host, Type, get_affiliation, [Nidx, From]) of {result, owner} -> Groups = ejabberd_hooks:run_fold(roster_groups, ServerHost, [], [ServerHost]), Fs = get_configure_xfields(Type, Options, Lang, Groups), {result, #pubsub_owner{ configure = {Node, #xdata{type = form, fields = Fs}}}}; _ -> {error, xmpp:err_forbidden(<<"Owner privileges required">>, Lang)} end end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> {result, Result}; Other -> Other end. -spec get_default(host(), binary(), jid(), binary()) -> {result, pubsub_owner()}. get_default(Host, Node, _From, Lang) -> Type = select_type(serverhost(Host), Host, Node), Options = node_options(Host, Type), Fs = get_configure_xfields(Type, Options, Lang, []), {result, #pubsub_owner{default = {<<>>, #xdata{type = form, fields = Fs}}}}. -spec match_option(#pubsub_node{} | [{atom(), any()}], atom(), any()) -> boolean(). match_option(Node, Var, Val) when is_record(Node, pubsub_node) -> match_option(Node#pubsub_node.options, Var, Val); match_option(Options, Var, Val) when is_list(Options) -> get_option(Options, Var) == Val; match_option(_, _, _) -> false. -spec get_option([{atom(), any()}], atom()) -> any(). get_option([], _) -> false; get_option(Options, Var) -> get_option(Options, Var, false). -spec get_option([{atom(), any()}], atom(), any()) -> any(). get_option(Options, Var, Def) -> case lists:keysearch(Var, 1, Options) of {value, {_Val, Ret}} -> Ret; _ -> Def end. -spec node_options(host(), binary()) -> [{atom(), any()}]. node_options(Host, Type) -> DefaultOpts = node_plugin_options(Host, Type), case config(Host, plugins) of [Type|_] -> config(Host, default_node_config, DefaultOpts); _ -> DefaultOpts end. -spec node_plugin_options(host(), binary()) -> [{atom(), any()}]. node_plugin_options(Host, Type) -> Module = plugin(Host, Type), case catch Module:options() of {'EXIT', {undef, _}} -> DefaultModule = plugin(Host, ?STDNODE), DefaultModule:options(); Result -> Result end. -spec node_owners_action(host(), binary(), nodeIdx(), [ljid()]) -> [ljid()]. node_owners_action(Host, Type, Nidx, []) -> case node_action(Host, Type, get_node_affiliations, [Nidx]) of {result, Affs} -> [LJID || {LJID, Aff} <- Affs, Aff =:= owner]; _ -> [] end; node_owners_action(_Host, _Type, _Nidx, Owners) -> Owners. -spec node_owners_call(host(), binary(), nodeIdx(), [ljid()]) -> [ljid()]. node_owners_call(Host, Type, Nidx, []) -> case node_call(Host, Type, get_node_affiliations, [Nidx]) of {result, Affs} -> [LJID || {LJID, Aff} <- Affs, Aff =:= owner]; _ -> [] end; node_owners_call(_Host, _Type, _Nidx, Owners) -> Owners. %% @spec (Host, Options) -> MaxItems %% Host = host() %% Options = [Option] %% Option = {Key::atom(), Value::term()} %% MaxItems = integer() | unlimited %% @doc

Return the maximum number of items for a given node.

%%

Unlimited means that there is no limit in the number of items that can %% be stored.

-spec max_items(host(), [{atom(), any()}]) -> non_neg_integer(). max_items(Host, Options) -> case get_option(Options, persist_items) of true -> case get_option(Options, max_items) of I when is_integer(I), I < 0 -> 0; I when is_integer(I) -> I; _ -> ?MAXITEMS end; false -> case get_option(Options, send_last_published_item) of never -> 0; _ -> case is_last_item_cache_enabled(Host) of true -> 0; false -> 1 end end end. -spec get_configure_xfields(_, pubsub_node_config:result(), binary(), [binary()]) -> [xdata_field()]. get_configure_xfields(_Type, Options, Lang, Groups) -> pubsub_node_config:encode( lists:map( fun({roster_groups_allowed, Value}) -> {roster_groups_allowed, Value, Groups}; (Opt) -> Opt end, Options), Lang). %%

There are several reasons why the node configuration request might fail:

%%
    %%
  • The service does not support node configuration.
  • %%
  • The requesting entity does not have sufficient privileges to configure the node.
  • %%
  • The request did not specify a node.
  • %%
  • The node has no configuration options.
  • %%
  • The specified node does not exist.
  • %%
-spec set_configure(host(), binary(), jid(), [{binary(), [binary()]}], binary()) -> {result, undefined} | {error, stanza_error()}. set_configure(_Host, <<>>, _From, _Config, _Lang) -> {error, extended_error(xmpp:err_bad_request(), err_nodeid_required())}; set_configure(Host, Node, From, Config, Lang) -> Action = fun(#pubsub_node{options = Options, type = Type, id = Nidx} = N) -> case node_call(Host, Type, get_affiliation, [Nidx, From]) of {result, owner} -> OldOpts = case Options of [] -> node_options(Host, Type); _ -> Options end, NewOpts = merge_config(Config, OldOpts), case tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]) of {result, Nidx} -> {result, NewOpts}; ok -> {result, NewOpts}; Err -> Err end; _ -> {error, xmpp:err_forbidden( <<"Owner privileges required">>, Lang)} end end, case transaction(Host, Node, Action, transaction) of {result, {TNode, Options}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, broadcast_config_notification(Host, Node, Nidx, Type, Options, Lang), {result, undefined}; Other -> Other end. -spec merge_config([proplists:property()], [proplists:property()]) -> [proplists:property()]. merge_config(CustomConfig, DefaultConfig) -> lists:foldl( fun({Opt, Val}, Acc) -> lists:keystore(Opt, 1, Acc, {Opt, Val}) end, DefaultConfig, CustomConfig). -spec decode_node_config(undefined | xdata(), binary(), binary()) -> pubsub_node_config:result() | {error, stanza_error()}. decode_node_config(undefined, _, _) -> []; decode_node_config(#xdata{fields = Fs}, Host, Lang) -> try Config = pubsub_node_config:decode(Fs), Max = get_max_items_node(Host), case {check_opt_range(max_items, Config, Max), check_opt_range(max_payload_size, Config, ?MAX_PAYLOAD_SIZE)} of {true, true} -> Config; {true, false} -> erlang:error( {pubsub_node_config, {bad_var_value, <<"pubsub#max_payload_size">>, ?NS_PUBSUB_NODE_CONFIG}}); {false, _} -> erlang:error( {pubsub_node_config, {bad_var_value, <<"pubsub#max_items">>, ?NS_PUBSUB_NODE_CONFIG}}) end catch _:{pubsub_node_config, Why} -> Txt = pubsub_node_config:format_error(Why), {error, xmpp:err_resource_constraint(Txt, Lang)} end. -spec decode_subscribe_options(undefined | xdata(), binary()) -> pubsub_subscribe_options:result() | {error, stanza_error()}. decode_subscribe_options(undefined, _) -> []; decode_subscribe_options(#xdata{fields = Fs}, Lang) -> try pubsub_subscribe_options:decode(Fs) catch _:{pubsub_subscribe_options, Why} -> Txt = pubsub_subscribe_options:format_error(Why), {error, xmpp:err_resource_constraint(Txt, Lang)} end. -spec decode_publish_options(undefined | xdata(), binary()) -> pubsub_publish_options:result() | {error, stanza_error()}. decode_publish_options(undefined, _) -> []; decode_publish_options(#xdata{fields = Fs}, Lang) -> try pubsub_publish_options:decode(Fs) catch _:{pubsub_publish_options, Why} -> Txt = pubsub_publish_options:format_error(Why), {error, xmpp:err_resource_constraint(Txt, Lang)} end. -spec decode_get_pending(xdata(), binary()) -> pubsub_get_pending:result() | {error, stanza_error()}. decode_get_pending(#xdata{fields = Fs}, Lang) -> try pubsub_get_pending:decode(Fs) catch _:{pubsub_get_pending, Why} -> Txt = pubsub_get_pending:format_error(Why), {error, xmpp:err_resource_constraint(Txt, Lang)} end; decode_get_pending(undefined, Lang) -> {error, xmpp:err_bad_request(<<"No data form found">>, Lang)}. -spec check_opt_range(atom(), [proplists:property()], non_neg_integer()) -> boolean(). check_opt_range(_Opt, _Opts, undefined) -> true; check_opt_range(Opt, Opts, Max) -> Val = proplists:get_value(Opt, Opts, Max), Val =< Max. -spec get_max_items_node(host()) -> undefined | non_neg_integer(). get_max_items_node(Host) -> config(Host, max_items_node, undefined). -spec get_max_subscriptions_node(host()) -> undefined | non_neg_integer(). get_max_subscriptions_node(Host) -> config(Host, max_subscriptions_node, undefined). %%%% last item cache handling -spec is_last_item_cache_enabled(host()) -> boolean(). is_last_item_cache_enabled(Host) -> config(Host, last_item_cache, false). -spec set_cached_item(host(), nodeIdx(), binary(), binary(), [xmlel()]) -> ok. set_cached_item({_, ServerHost, _}, Nidx, ItemId, Publisher, Payload) -> set_cached_item(ServerHost, Nidx, ItemId, Publisher, Payload); set_cached_item(Host, Nidx, ItemId, Publisher, Payload) -> case is_last_item_cache_enabled(Host) of true -> Stamp = {p1_time_compat:timestamp(), jid:tolower(jid:remove_resource(Publisher))}, Item = #pubsub_last_item{nodeid = {Host, Nidx}, itemid = ItemId, creation = Stamp, payload = Payload}, mnesia:dirty_write(Item); _ -> ok end. -spec unset_cached_item(host(), nodeIdx()) -> ok. unset_cached_item({_, ServerHost, _}, Nidx) -> unset_cached_item(ServerHost, Nidx); unset_cached_item(Host, Nidx) -> case is_last_item_cache_enabled(Host) of true -> mnesia:dirty_delete({pubsub_last_item, {Host, Nidx}}); _ -> ok end. -spec get_cached_item(host(), nodeIdx()) -> undefined | pubsubItem(). get_cached_item({_, ServerHost, _}, Nidx) -> get_cached_item(ServerHost, Nidx); get_cached_item(Host, Nidx) -> case is_last_item_cache_enabled(Host) of true -> case mnesia:dirty_read({pubsub_last_item, {Host, Nidx}}) of [#pubsub_last_item{itemid = ItemId, creation = Creation, payload = Payload}] -> #pubsub_item{itemid = {ItemId, Nidx}, payload = Payload, creation = Creation, modification = Creation}; _ -> undefined end; _ -> undefined end. %%%% plugin handling -spec host(binary()) -> binary(). host(ServerHost) -> config(ServerHost, host, <<"pubsub.", ServerHost/binary>>). -spec serverhost(host()) -> binary(). serverhost({_U, ServerHost, _R})-> serverhost(ServerHost); serverhost(Host) -> ejabberd_router:host_of_route(Host). -spec tree(host()) -> atom(). tree(Host) -> case config(Host, nodetree) of undefined -> tree(Host, ?STDTREE); Tree -> Tree end. -spec tree(host(), binary()) -> atom(). tree(_Host, <<"virtual">>) -> nodetree_virtual; % special case, virtual does not use any backend tree(Host, Name) -> submodule(Host, <<"nodetree">>, Name). -spec plugin(host(), binary()) -> atom(). plugin(Host, Name) -> submodule(Host, <<"node">>, Name). -spec plugins(host()) -> [binary()]. plugins(Host) -> case config(Host, plugins) of undefined -> [?STDNODE]; [] -> [?STDNODE]; Plugins -> Plugins end. -spec subscription_plugin(host()) -> atom(). subscription_plugin(Host) -> submodule(Host, <<"pubsub">>, <<"subscription">>). -spec submodule(host(), binary(), binary()) -> atom(). submodule(Host, Type, Name) -> case gen_mod:db_type(serverhost(Host), ?MODULE) of mnesia -> ejabberd:module_name([<<"pubsub">>, Type, Name]); Db -> ejabberd:module_name([<<"pubsub">>, Type, Name, misc:atom_to_binary(Db)]) end. -spec config(binary(), any()) -> any(). config(ServerHost, Key) -> config(ServerHost, Key, undefined). -spec config(host(), any(), any()) -> any(). config({_User, Host, _Resource}, Key, Default) -> config(Host, Key, Default); config(ServerHost, Key, Default) -> case catch ets:lookup(gen_mod:get_module_proc(ServerHost, config), Key) of [{Key, Value}] -> Value; _ -> Default end. -spec select_type(binary(), host(), binary(), binary()) -> binary(). select_type(ServerHost, {_User, _Server, _Resource}, Node, _Type) -> case config(ServerHost, pep_mapping) of undefined -> ?PEPNODE; Mapping -> proplists:get_value(Node, Mapping, ?PEPNODE) end; select_type(ServerHost, _Host, _Node, Type) -> case config(ServerHost, plugins) of undefined -> Type; Plugins -> case lists:member(Type, Plugins) of true -> Type; false -> hd(Plugins) end end. -spec select_type(binary(), host(), binary()) -> binary(). select_type(ServerHost, Host, Node) -> select_type(ServerHost, Host, Node, hd(plugins(Host))). -spec feature(binary()) -> binary(). feature(<<"rsm">>) -> ?NS_RSM; feature(Feature) -> <<(?NS_PUBSUB)/binary, "#", Feature/binary>>. -spec features() -> [binary()]. features() -> [% see plugin "access-authorize", % OPTIONAL <<"access-open">>, % OPTIONAL this relates to access_model option in node_hometree <<"access-presence">>, % OPTIONAL this relates to access_model option in node_pep <<"access-whitelist">>, % OPTIONAL <<"collections">>, % RECOMMENDED <<"config-node">>, % RECOMMENDED <<"create-and-configure">>, % RECOMMENDED <<"item-ids">>, % RECOMMENDED <<"last-published">>, % RECOMMENDED <<"member-affiliation">>, % RECOMMENDED <<"presence-notifications">>, % OPTIONAL <<"presence-subscribe">>, % RECOMMENDED <<"publisher-affiliation">>, % RECOMMENDED <<"publish-only-affiliation">>, % OPTIONAL <<"publish-options">>, % OPTIONAL <<"retrieve-default">>, <<"shim">>]. % RECOMMENDED % see plugin "retrieve-items", % RECOMMENDED % see plugin "retrieve-subscriptions", % RECOMMENDED % see plugin "subscribe", % REQUIRED % see plugin "subscription-options", % OPTIONAL % see plugin "subscription-notifications" % OPTIONAL -spec plugin_features(binary(), binary()) -> [binary()]. plugin_features(Host, Type) -> Module = plugin(Host, Type), case catch Module:features() of {'EXIT', {undef, _}} -> []; Result -> Result end. -spec features(binary(), binary()) -> [binary()]. features(Host, <<>>) -> lists:usort(lists:foldl(fun (Plugin, Acc) -> Acc ++ plugin_features(Host, Plugin) end, features(), plugins(Host))); features(Host, Node) when is_binary(Node) -> Action = fun (#pubsub_node{type = Type}) -> {result, plugin_features(Host, Type)} end, case transaction(Host, Node, Action, sync_dirty) of {result, Features} -> lists:usort(features() ++ Features); _ -> features() end. %% @doc

node tree plugin call.

tree_call({_User, Server, _Resource}, Function, Args) -> tree_call(Server, Function, Args); tree_call(Host, Function, Args) -> Tree = tree(Host), ?DEBUG("tree_call apply(~s, ~s, ~p) @ ~s", [Tree, Function, Args, Host]), catch apply(Tree, Function, Args). tree_action(Host, Function, Args) -> ?DEBUG("tree_action ~p ~p ~p", [Host, Function, Args]), ServerHost = serverhost(Host), Fun = fun () -> tree_call(Host, Function, Args) end, case gen_mod:db_type(ServerHost, ?MODULE) of mnesia -> catch mnesia:sync_dirty(Fun); sql -> case catch ejabberd_sql:sql_bloc(ServerHost, Fun) of {atomic, Result} -> Result; {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), ErrTxt = <<"Database failure">>, {error, xmpp:err_internal_server_error(ErrTxt, ?MYLANG)} end; Other -> case catch Fun() of {'EXIT', _} -> ?ERROR_MSG("unsupported backend: ~p~n", [Other]), ErrTxt = <<"Database failure">>, {error, xmpp:err_internal_server_error(ErrTxt, ?MYLANG)}; Result -> Result end end. %% @doc

node plugin call.

node_call(Host, Type, Function, Args) -> ?DEBUG("node_call ~p ~p ~p", [Type, Function, Args]), Module = plugin(Host, Type), case apply(Module, Function, Args) of {result, Result} -> {result, Result}; {error, Error} -> {error, Error}; {'EXIT', {undef, Undefined}} -> case Type of ?STDNODE -> {error, {undef, Undefined}}; _ -> node_call(Host, ?STDNODE, Function, Args) end; {'EXIT', Reason} -> {error, Reason}; Result -> {result, Result} %% any other return value is forced as result end. node_action(Host, Type, Function, Args) -> ?DEBUG("node_action ~p ~p ~p ~p", [Host, Type, Function, Args]), transaction(Host, fun () -> node_call(Host, Type, Function, Args) end, sync_dirty). %% @doc

plugin transaction handling.

transaction(Host, Node, Action, Trans) -> transaction(Host, fun () -> case tree_call(Host, get_node, [Host, Node]) of N when is_record(N, pubsub_node) -> case Action(N) of {result, Result} -> {result, {N, Result}}; {atomic, {result, Result}} -> {result, {N, Result}}; Other -> Other end; Error -> Error end end, Trans). transaction(Host, Fun, Trans) -> ServerHost = serverhost(Host), DBType = gen_mod:db_type(ServerHost, ?MODULE), Retry = case DBType of sql -> 2; _ -> 1 end, transaction_retry(Host, ServerHost, Fun, Trans, DBType, Retry). transaction_retry(_Host, _ServerHost, _Fun, _Trans, _DBType, 0) -> {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count) -> Res = case DBType of mnesia -> catch mnesia:Trans(Fun); sql -> SqlFun = case Trans of transaction -> sql_transaction; _ -> sql_bloc end, catch ejabberd_sql:SqlFun(ServerHost, Fun); _ -> catch Fun() end, case Res of {result, Result} -> {result, Result}; {error, Error} -> {error, Error}; {atomic, {result, Result}} -> {result, Result}; {atomic, {error, Error}} -> {error, Error}; {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; {'EXIT', {timeout, _} = Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [Reason]), transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count - 1); {'EXIT', Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; Other -> ?ERROR_MSG("transaction return internal error: ~p~n", [Other]), {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)} end. %%%% helpers %% Add pubsub-specific error element -spec extended_error(stanza_error(), ps_error()) -> stanza_error(). extended_error(StanzaErr, PubSubErr) -> StanzaErr#stanza_error{sub_els = [PubSubErr]}. -spec err_closed_node() -> ps_error(). err_closed_node() -> #ps_error{type = 'closed-node'}. -spec err_configuration_required() -> ps_error(). err_configuration_required() -> #ps_error{type = 'configuration-required'}. -spec err_invalid_jid() -> ps_error(). err_invalid_jid() -> #ps_error{type = 'invalid-jid'}. -spec err_invalid_options() -> ps_error(). err_invalid_options() -> #ps_error{type = 'invalid-options'}. -spec err_invalid_payload() -> ps_error(). err_invalid_payload() -> #ps_error{type = 'invalid-payload'}. -spec err_invalid_subid() -> ps_error(). err_invalid_subid() -> #ps_error{type = 'invalid-subid'}. -spec err_item_forbidden() -> ps_error(). err_item_forbidden() -> #ps_error{type = 'item-forbidden'}. -spec err_item_required() -> ps_error(). err_item_required() -> #ps_error{type = 'item-required'}. -spec err_jid_required() -> ps_error(). err_jid_required() -> #ps_error{type = 'jid-required'}. -spec err_max_items_exceeded() -> ps_error(). err_max_items_exceeded() -> #ps_error{type = 'max-items-exceeded'}. -spec err_max_nodes_exceeded() -> ps_error(). err_max_nodes_exceeded() -> #ps_error{type = 'max-nodes-exceeded'}. -spec err_nodeid_required() -> ps_error(). err_nodeid_required() -> #ps_error{type = 'nodeid-required'}. -spec err_not_in_roster_group() -> ps_error(). err_not_in_roster_group() -> #ps_error{type = 'not-in-roster-group'}. -spec err_not_subscribed() -> ps_error(). err_not_subscribed() -> #ps_error{type = 'not-subscribed'}. -spec err_payload_too_big() -> ps_error(). err_payload_too_big() -> #ps_error{type = 'payload-too-big'}. -spec err_payload_required() -> ps_error(). err_payload_required() -> #ps_error{type = 'payload-required'}. -spec err_pending_subscription() -> ps_error(). err_pending_subscription() -> #ps_error{type = 'pending-subscription'}. -spec err_precondition_not_met() -> ps_error(). err_precondition_not_met() -> #ps_error{type = 'precondition-not-met'}. -spec err_presence_subscription_required() -> ps_error(). err_presence_subscription_required() -> #ps_error{type = 'presence-subscription-required'}. -spec err_subid_required() -> ps_error(). err_subid_required() -> #ps_error{type = 'subid-required'}. -spec err_too_many_subscriptions() -> ps_error(). err_too_many_subscriptions() -> #ps_error{type = 'too-many-subscriptions'}. -spec err_unsupported(ps_feature()) -> ps_error(). err_unsupported(Feature) -> #ps_error{type = 'unsupported', feature = Feature}. -spec err_unsupported_access_model() -> ps_error(). err_unsupported_access_model() -> #ps_error{type = 'unsupported-access-model'}. -spec uniqid() -> mod_pubsub:itemId(). uniqid() -> {T1, T2, T3} = p1_time_compat:timestamp(), (str:format("~.16B~.16B~.16B", [T1, T2, T3])). -spec itemsEls([#pubsub_item{}]) -> [ps_item()]. itemsEls(Items) -> [#ps_item{id = ItemId, xml_els = Payload} || #pubsub_item{itemid = {ItemId, _}, payload = Payload} <- Items]. -spec add_message_type(message(), message_type()) -> message(). add_message_type(#message{} = Message, Type) -> Message#message{type = Type}. %% Place of changed at the bottom of the stanza %% cf. http://xmpp.org/extensions/xep-0060.html#publisher-publish-success-subid %% %% "[SHIM Headers] SHOULD be included after the event notification information %% (i.e., as the last child of the stanza)". -spec add_shim_headers(stanza(), [{binary(), binary()}]) -> stanza(). add_shim_headers(Stanza, Headers) -> xmpp:set_subtag(Stanza, #shim{headers = Headers}). -spec add_extended_headers(stanza(), [address()]) -> stanza(). add_extended_headers(Stanza, Addrs) -> xmpp:set_subtag(Stanza, #addresses{list = Addrs}). -spec subid_shim([binary()]) -> [{binary(), binary()}]. subid_shim(SubIds) -> [{<<"SubId">>, SubId} || SubId <- SubIds]. %% The argument is a list of Jids because this function could be used %% with the 'pubsub#replyto' (type=jid-multi) node configuration. -spec extended_headers([jid()]) -> [address()]. extended_headers(Jids) -> [#address{type = replyto, jid = Jid} || Jid <- Jids]. -spec purge_offline(ljid()) -> ok. purge_offline(LJID) -> Host = host(element(2, LJID)), Plugins = plugins(Host), Result = lists:foldl(fun (Type, {Status, Acc}) -> Features = plugin_features(Host, Type), case lists:member(<<"retrieve-affiliations">>, plugin_features(Host, Type)) of false -> {{error, extended_error(xmpp:err_feature_not_implemented(), err_unsupported('retrieve-affiliations'))}, Acc}; true -> Items = lists:member(<<"retract-items">>, Features) andalso lists:member(<<"persistent-items">>, Features), if Items -> {result, Affs} = node_action(Host, Type, get_entity_affiliations, [Host, LJID]), {Status, [Affs | Acc]}; true -> {Status, Acc} end end end, {ok, []}, Plugins), case Result of {ok, Affs} -> lists:foreach( fun ({Node, Affiliation}) -> Options = Node#pubsub_node.options, Publisher = lists:member(Affiliation, [owner,publisher,publish_only]), Open = (get_option(Options, publish_model) == open), Purge = (get_option(Options, purge_offline) andalso get_option(Options, persist_items)), if (Publisher or Open) and Purge -> purge_offline(Host, LJID, Node); true -> ok end end, lists:usort(lists:flatten(Affs))); {Error, _} -> ?ERROR_MSG("can not purge offline: ~p", [Error]) end. -spec purge_offline(host(), ljid(), binary()) -> ok | {error, stanza_error()}. purge_offline(Host, LJID, Node) -> Nidx = Node#pubsub_node.id, Type = Node#pubsub_node.type, Options = Node#pubsub_node.options, case node_action(Host, Type, get_items, [Nidx, service_jid(Host), undefined]) of {result, {[], _}} -> ok; {result, {Items, _}} -> {User, Server, Resource} = LJID, PublishModel = get_option(Options, publish_model), ForceNotify = get_option(Options, notify_retract), {_, NodeId} = Node#pubsub_node.nodeid, lists:foreach(fun (#pubsub_item{itemid = {ItemId, _}, modification = {_, {U, S, R}}}) when (U == User) and (S == Server) and (R == Resource) -> case node_action(Host, Type, delete_item, [Nidx, {U, S, <<>>}, PublishModel, ItemId]) of {result, {_, broadcast}} -> broadcast_retract_items(Host, NodeId, Nidx, Type, Options, [ItemId], ForceNotify), case get_cached_item(Host, Nidx) of #pubsub_item{itemid = {ItemId, Nidx}} -> unset_cached_item(Host, Nidx); _ -> ok end; {result, _} -> ok; Error -> Error end; (_) -> true end, Items); Error -> Error end. mod_opt_type(access_createnode) -> fun acl:access_rules_validator/1; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun (L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(ignore_pep_from_offline) -> fun (A) when is_boolean(A) -> A end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(last_item_cache) -> fun (A) when is_boolean(A) -> A end; mod_opt_type(max_items_node) -> fun (A) when is_integer(A) andalso A >= 0 -> A end; mod_opt_type(max_subscriptions_node) -> fun (A) when is_integer(A) andalso A >= 0 -> A end; mod_opt_type(default_node_config) -> fun (A) when is_list(A) -> A end; mod_opt_type(nodetree) -> fun (A) when is_binary(A) -> A end; mod_opt_type(pep_mapping) -> fun (A) when is_list(A) -> A end; mod_opt_type(plugins) -> fun (A) when is_list(A) -> A end; mod_opt_type(_) -> [access_createnode, db_type, host, hosts, name, ignore_pep_from_offline, iqdisc, last_item_cache, max_items_node, nodetree, pep_mapping, plugins, max_subscriptions_node, default_node_config]. ejabberd-18.01/src/ejabberd_http.erl0000644000232200023220000010132013225664356017730 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_http.erl %%% Author : Alexey Shchepin %%% Purpose : %%% Created : 27 Feb 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_http). -behaviour(ejabberd_config). -author('alexey@process-one.net'). %% External exports -export([start/2, start_link/2, become_controller/1, socket_type/0, receive_headers/1, url_encode/1, transform_listen_option/2, listen_opt_type/1]). -export([init/2, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -record(state, {sockmod, socket, request_method, request_version, request_path, request_auth, request_keepalive, request_content_length, request_lang = <<"en">>, %% XXX bard: request handlers are configured in %% ejabberd.cfg under the HTTP service. For example, %% to have the module test_web handle requests with %% paths starting with "/test/module": %% %% {5280, ejabberd_http, [http_bind, web_admin, %% {request_handlers, [{["test", "module"], mod_test_web}]}]} %% request_handlers = [], request_host, request_port, request_tp, request_headers = [], end_of_request = false, options = [], default_host, custom_headers, trail = <<>>, addr_re }). -define(XHTML_DOCTYPE, <<"\n\n">>). -define(HTML_DOCTYPE, <<"\n" "">>). start(SockData, Opts) -> {ok, proc_lib:spawn(ejabberd_http, init, [SockData, Opts])}. start_link(SockData, Opts) -> {ok, proc_lib:spawn_link(ejabberd_http, init, [SockData, Opts])}. init({SockMod, Socket}, Opts) -> TLSEnabled = proplists:get_bool(tls, Opts), TLSOpts1 = lists:filter(fun ({ciphers, _}) -> true; ({dhfile, _}) -> true; ({protocol_options, _}) -> true; (_) -> false end, Opts), TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of false -> [compression_none | TLSOpts1]; true -> TLSOpts1 end, TLSOpts3 = case get_certfile(Opts) of undefined -> TLSOpts2; CertFile -> [{certfile, CertFile}|TLSOpts2] end, TLSOpts = [verify_none | TLSOpts3], {SockMod1, Socket1} = if TLSEnabled -> inet:setopts(Socket, [{recbuf, 8192}]), {ok, TLSSocket} = fast_tls:tcp_to_tls(Socket, TLSOpts), {fast_tls, TLSSocket}; true -> {SockMod, Socket} end, Captcha = case proplists:get_bool(captcha, Opts) of true -> [{[<<"captcha">>], ejabberd_captcha}]; false -> [] end, Register = case proplists:get_bool(register, Opts) of true -> [{[<<"register">>], mod_register_web}]; false -> [] end, Admin = case proplists:get_bool(web_admin, Opts) of true -> [{[<<"admin">>], ejabberd_web_admin}]; false -> [] end, Bind = case proplists:get_bool(http_bind, Opts) of true -> [{[<<"http-bind">>], mod_bosh}]; false -> [] end, XMLRPC = case proplists:get_bool(xmlrpc, Opts) of true -> [{[], ejabberd_xmlrpc}]; false -> [] end, DefinedHandlers = proplists:get_value(request_handlers, Opts, []), RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++ Admin ++ Bind ++ XMLRPC, ?DEBUG("S: ~p~n", [RequestHandlers]), DefaultHost = proplists:get_value(default_host, Opts), {ok, RE} = re:compile(<<"^(?:\\[(.*?)\\]|(.*?))(?::(\\d+))?$">>), CustomHeaders = proplists:get_value(custom_headers, Opts, []), State = #state{sockmod = SockMod1, socket = Socket1, default_host = DefaultHost, custom_headers = CustomHeaders, options = Opts, request_handlers = RequestHandlers, addr_re = RE}, try receive_headers(State) of V -> V catch {error, _} -> State end. become_controller(_Pid) -> ok. socket_type() -> raw. send_text(State, Text) -> case catch (State#state.sockmod):send(State#state.socket, Text) of ok -> ok; {error, timeout} -> ?INFO_MSG("Timeout on ~p:send", [State#state.sockmod]), exit(normal); Error -> ?DEBUG("Error in ~p:send: ~p", [State#state.sockmod, Error]), exit(normal) end. receive_headers(#state{trail = Trail} = State) -> SockMod = State#state.sockmod, Socket = State#state.socket, Data = SockMod:recv(Socket, 0, 300000), case Data of {error, _} -> ok; {ok, D} -> parse_headers(State#state{trail = <>}) end. parse_headers(#state{trail = <<>>} = State) -> receive_headers(State); parse_headers(#state{request_method = Method, trail = Data} = State) -> PktType = case Method of undefined -> http_bin; _ -> httph_bin end, case erlang:decode_packet(PktType, Data, []) of {ok, Pkt, Rest} -> NewState = process_header(State#state{trail = Rest}, {ok, Pkt}), case NewState#state.end_of_request of true -> ok; _ -> parse_headers(NewState) end; {more, _} -> receive_headers(State#state{trail = Data}); _ -> ok end. process_header(State, Data) -> SockMod = State#state.sockmod, Socket = State#state.socket, case Data of {ok, {http_request, Method, Uri, Version}} -> KeepAlive = case Version of {1, 1} -> true; _ -> false end, Path = case Uri of {absoluteURI, _Scheme, _Host, _Port, P} -> {abs_path, P}; {abs_path, P} -> {abs_path, P}; _ -> Uri end, State#state{request_method = Method, request_version = Version, request_path = Path, request_keepalive = KeepAlive}; {ok, {http_header, _, 'Connection' = Name, _, Conn}} -> KeepAlive1 = case misc:tolower(Conn) of <<"keep-alive">> -> true; <<"close">> -> false; _ -> State#state.request_keepalive end, State#state{request_keepalive = KeepAlive1, request_headers = add_header(Name, Conn, State)}; {ok, {http_header, _, 'Authorization' = Name, _, Auth}} -> State#state{request_auth = parse_auth(Auth), request_headers = add_header(Name, Auth, State)}; {ok, {http_header, _, 'Content-Length' = Name, _, SLen}} -> case catch binary_to_integer(SLen) of Len when is_integer(Len) -> State#state{request_content_length = Len, request_headers = add_header(Name, SLen, State)}; _ -> State end; {ok, {http_header, _, 'Accept-Language' = Name, _, Langs}} -> State#state{request_lang = parse_lang(Langs), request_headers = add_header(Name, Langs, State)}; {ok, {http_header, _, 'Host' = Name, _, Host}} -> State#state{request_host = Host, request_headers = add_header(Name, Host, State)}; {ok, {http_header, _, Name, _, Value}} when is_binary(Name) -> State#state{request_headers = add_header(normalize_header_name(Name), Value, State)}; {ok, {http_header, _, Name, _, Value}} -> State#state{request_headers = add_header(Name, Value, State)}; {ok, http_eoh} when State#state.request_host == undefined -> ?DEBUG("An HTTP request without 'Host' HTTP " "header was received.", []), {State1, Out} = process_request(State), send_text(State1, Out), process_header(State, {ok, {http_error, <<>>}}); {ok, http_eoh} -> ?DEBUG("(~w) http query: ~w ~p~n", [State#state.socket, State#state.request_method, element(2, State#state.request_path)]), {HostProvided, Port, TP} = get_transfer_protocol(State#state.addr_re, SockMod, State#state.request_host), Host = get_host_really_served(State#state.default_host, HostProvided), State2 = State#state{request_host = Host, request_port = Port, request_tp = TP}, {State3, Out} = process_request(State2), send_text(State3, Out), case State3#state.request_keepalive of true -> #state{sockmod = SockMod, socket = Socket, trail = State3#state.trail, options = State#state.options, default_host = State#state.default_host, custom_headers = State#state.custom_headers, request_handlers = State#state.request_handlers, addr_re = State#state.addr_re}; _ -> #state{end_of_request = true, trail = State3#state.trail, options = State#state.options, default_host = State#state.default_host, custom_headers = State#state.custom_headers, request_handlers = State#state.request_handlers, addr_re = State#state.addr_re} end; _ -> #state{end_of_request = true, options = State#state.options, default_host = State#state.default_host, custom_headers = State#state.custom_headers, request_handlers = State#state.request_handlers, addr_re = State#state.addr_re} end. add_header(Name, Value, State)-> [{Name, Value} | State#state.request_headers]. get_host_really_served(undefined, Provided) -> Provided; get_host_really_served(Default, Provided) -> case ejabberd_router:is_my_host(Provided) of true -> Provided; false -> Default end. get_transfer_protocol(RE, SockMod, HostPort) -> {Proto, DefPort} = case SockMod of gen_tcp -> {http, 80}; fast_tls -> {https, 443} end, {Host, Port} = case re:run(HostPort, RE, [{capture,[1,2,3],binary}]) of nomatch -> {<<"0.0.0.0">>, DefPort}; {match, [<<>>, H, <<>>]} -> {H, DefPort}; {match, [H, <<>>, <<>>]} -> {H, DefPort}; {match, [<<>>, H, PortStr]} -> {H, binary_to_integer(PortStr)}; {match, [H, <<>>, PortStr]} -> {H, binary_to_integer(PortStr)} end, {Host, Port, Proto}. %% XXX bard: search through request handlers looking for one that %% matches the requested URL path, and pass control to it. If none is %% found, answer with HTTP 404. process([], _, _, _, _) -> ejabberd_web:error(not_found); process(Handlers, Request, Socket, SockMod, Trail) -> {HandlerPathPrefix, HandlerModule, HandlerOpts, HandlersLeft} = case Handlers of [{Pfx, Mod} | Tail] -> {Pfx, Mod, [], Tail}; [{Pfx, Mod, Opts} | Tail] -> {Pfx, Mod, Opts, Tail} end, case (lists:prefix(HandlerPathPrefix, Request#request.path) or (HandlerPathPrefix==Request#request.path)) of true -> ?DEBUG("~p matches ~p", [Request#request.path, HandlerPathPrefix]), %% LocalPath is the path "local to the handler", i.e. if %% the handler was registered to handle "/test/" and the %% requested path is "/test/foo/bar", the local path is %% ["foo", "bar"] LocalPath = lists:nthtail(length(HandlerPathPrefix), Request#request.path), R = try HandlerModule:socket_handoff( LocalPath, Request, Socket, SockMod, Trail, HandlerOpts) catch error:undef -> HandlerModule:process(LocalPath, Request) end, ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]), R; false -> process(HandlersLeft, Request, Socket, SockMod, Trail) end. extract_path_query(#state{request_method = Method, request_path = {abs_path, Path}} = State) when Method =:= 'GET' orelse Method =:= 'HEAD' orelse Method =:= 'DELETE' orelse Method =:= 'OPTIONS' -> case catch url_decode_q_split(Path) of {'EXIT', _} -> {State, false}; {NPath, Query} -> LPath = normalize_path([NPE || NPE <- str:tokens(path_decode(NPath), <<"/">>)]), LQuery = case catch parse_urlencoded(Query) of {'EXIT', _Reason} -> []; LQ -> LQ end, {State, {LPath, LQuery, <<"">>}} end; extract_path_query(#state{request_method = Method, request_path = {abs_path, Path}, request_content_length = Len, sockmod = _SockMod, socket = _Socket} = State) when (Method =:= 'POST' orelse Method =:= 'PUT') andalso is_integer(Len) -> case recv_data(State, Len) of error -> {State, false}; {NewState, Data} -> ?DEBUG("client data: ~p~n", [Data]), case catch url_decode_q_split(Path) of {'EXIT', _} -> {NewState, false}; {NPath, _Query} -> LPath = normalize_path([NPE || NPE <- str:tokens(path_decode(NPath), <<"/">>)]), LQuery = case catch parse_urlencoded(Data) of {'EXIT', _Reason} -> []; LQ -> LQ end, {NewState, {LPath, LQuery, Data}} end end; extract_path_query(State) -> {State, false}. process_request(#state{request_host = undefined, custom_headers = CustomHeaders} = State) -> {State, make_text_output(State, 400, CustomHeaders, <<"Missing Host header">>)}; process_request(#state{request_method = Method, request_auth = Auth, request_lang = Lang, sockmod = SockMod, socket = Socket, options = Options, request_host = Host, request_port = Port, request_tp = TP, request_headers = RequestHeaders, request_handlers = RequestHandlers, custom_headers = CustomHeaders, trail = Trail} = State) -> case extract_path_query(State) of {State2, false} -> {State2, make_bad_request(State)}; {State2, {LPath, LQuery, Data}} -> PeerName = case SockMod of gen_tcp -> inet:peername(Socket); _ -> SockMod:peername(Socket) end, IPHere = case PeerName of {ok, V} -> V; {error, _} = E -> throw(E) end, XFF = proplists:get_value('X-Forwarded-For', RequestHeaders, []), IP = analyze_ip_xff(IPHere, XFF, Host), Request = #request{method = Method, path = LPath, q = LQuery, auth = Auth, data = Data, lang = Lang, host = Host, port = Port, tp = TP, opts = Options, headers = RequestHeaders, ip = IP}, RequestHandlers1 = ejabberd_hooks:run_fold( http_request_handlers, RequestHandlers, [Host, Request]), Res = case process(RequestHandlers1, Request, Socket, SockMod, Trail) of El when is_record(El, xmlel) -> make_xhtml_output(State, 200, CustomHeaders, El); {Status, Headers, El} when is_record(El, xmlel) -> make_xhtml_output(State, Status, Headers ++ CustomHeaders, El); Output when is_binary(Output) or is_list(Output) -> make_text_output(State, 200, CustomHeaders, Output); {Status, Headers, Output} when is_binary(Output) or is_list(Output) -> make_text_output(State, Status, Headers ++ CustomHeaders, Output); {Status, Reason, Headers, Output} when is_binary(Output) or is_list(Output) -> make_text_output(State, Status, Reason, Headers ++ CustomHeaders, Output); _ -> none end, {State2, Res} end. make_bad_request(State) -> make_xhtml_output(State, 400, State#state.custom_headers, ejabberd_web:make_xhtml([#xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"400 Bad Request">>}]}])). analyze_ip_xff(IP, [], _Host) -> IP; analyze_ip_xff({IPLast, Port}, XFF, Host) -> [ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++ [misc:ip_to_list(IPLast)], TrustedProxies = ejabberd_config:get_option({trusted_proxies, Host}, []), IPClient = case is_ipchain_trusted(ProxiesIPs, TrustedProxies) of true -> {ok, IPFirst} = inet_parse:address( binary_to_list(ClientIP)), ?DEBUG("The IP ~w was replaced with ~w due to " "header X-Forwarded-For: ~s", [IPLast, IPFirst, XFF]), IPFirst; false -> IPLast end, {IPClient, Port}. is_ipchain_trusted(_UserIPs, all) -> true; is_ipchain_trusted(UserIPs, TrustedIPs) -> [] == UserIPs -- [<<"127.0.0.1">> | TrustedIPs]. recv_data(State, Len) -> recv_data(State, Len, <<>>). recv_data(State, 0, Acc) -> {State, Acc}; recv_data(#state{trail = Trail} = State, Len, <<>>) when byte_size(Trail) > Len -> <> = Trail, {State#state{trail = Rest}, Data}; recv_data(State, Len, Acc) -> case State#state.trail of <<>> -> case (State#state.sockmod):recv(State#state.socket, min(Len, 16#4000000), 300000) of {ok, Data} -> recv_data(State, Len - byte_size(Data), <>); Err -> ?DEBUG("Cannot receive HTTP data: ~p", [Err]), error end; _ -> Trail = (State#state.trail), recv_data(State#state{trail = <<>>}, Len - byte_size(Trail), <>) end. make_xhtml_output(State, Status, Headers, XHTML) -> Data = case lists:member(html, Headers) of true -> iolist_to_binary([?HTML_DOCTYPE, fxml:element_to_binary(XHTML)]); _ -> iolist_to_binary([?XHTML_DOCTYPE, fxml:element_to_binary(XHTML)]) end, Headers1 = case lists:keysearch(<<"Content-Type">>, 1, Headers) of {value, _} -> [{<<"Content-Length">>, integer_to_binary(byte_size(Data))} | Headers]; _ -> [{<<"Content-Type">>, <<"text/html; charset=utf-8">>}, {<<"Content-Length">>, integer_to_binary(byte_size(Data))} | Headers] end, HeadersOut = case {State#state.request_version, State#state.request_keepalive} of {{1, 1}, true} -> Headers1; {_, true} -> [{<<"Connection">>, <<"keep-alive">>} | Headers1]; {_, false} -> [{<<"Connection">>, <<"close">>} | Headers1] end, Version = case State#state.request_version of {1, 1} -> <<"HTTP/1.1 ">>; _ -> <<"HTTP/1.0 ">> end, H = lists:map(fun ({Attr, Val}) -> [Attr, <<": ">>, Val, <<"\r\n">>]; (_) -> [] end, HeadersOut), SL = [Version, integer_to_binary(Status), <<" ">>, code_to_phrase(Status), <<"\r\n">>], Data2 = case State#state.request_method of 'HEAD' -> <<"">>; _ -> Data end, [SL, H, <<"\r\n">>, Data2]. make_text_output(State, Status, Headers, Text) -> make_text_output(State, Status, <<"">>, Headers, Text). make_text_output(State, Status, Reason, Headers, Text) -> Data = iolist_to_binary(Text), Headers1 = case lists:keysearch(<<"Content-Type">>, 1, Headers) of {value, _} -> [{<<"Content-Length">>, integer_to_binary(byte_size(Data))} | Headers]; _ -> [{<<"Content-Type">>, <<"text/html; charset=utf-8">>}, {<<"Content-Length">>, integer_to_binary(byte_size(Data))} | Headers] end, HeadersOut = case {State#state.request_version, State#state.request_keepalive} of {{1, 1}, true} -> Headers1; {_, true} -> [{<<"Connection">>, <<"keep-alive">>} | Headers1]; {_, false} -> [{<<"Connection">>, <<"close">>} | Headers1] end, Version = case State#state.request_version of {1, 1} -> <<"HTTP/1.1 ">>; _ -> <<"HTTP/1.0 ">> end, H = lists:map(fun ({Attr, Val}) -> [Attr, <<": ">>, Val, <<"\r\n">>] end, HeadersOut), NewReason = case Reason of <<"">> -> code_to_phrase(Status); _ -> Reason end, SL = [Version, integer_to_binary(Status), <<" ">>, NewReason, <<"\r\n">>], Data2 = case State#state.request_method of 'HEAD' -> <<"">>; _ -> Data end, [SL, H, <<"\r\n">>, Data2]. parse_lang(Langs) -> case str:tokens(Langs, <<",; ">>) of [First | _] -> First; [] -> <<"en">> end. % Code below is taken (with some modifications) from the yaws webserver, which % is distributed under the folowing license: % % This software (the yaws webserver) is free software. % Parts of this software is Copyright (c) Claes Wikstrom % Any use or misuse of the source code is hereby freely allowed. % % 1. Redistributions of source code must retain the above copyright % notice as well as this list of conditions. % % 2. Redistributions in binary form must reproduce the above copyright % notice as well as this list of conditions. %% @doc Split the URL and return {Path, QueryPart} url_decode_q_split(Path) -> url_decode_q_split(Path, <<>>). url_decode_q_split(<<$?, T/binary>>, Acc) -> %% Don't decode the query string here, that is parsed separately. {path_norm_reverse(Acc), T}; url_decode_q_split(<>, Acc) when H /= 0 -> url_decode_q_split(T, <>); url_decode_q_split(<<>>, Ack) -> {path_norm_reverse(Ack), <<>>}. %% @doc Decode a part of the URL and return string() path_decode(Path) -> path_decode(Path, <<>>). path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) -> Hex = hex_to_integer([Hi, Lo]), if Hex == 0 -> exit(badurl); true -> ok end, path_decode(Tail, <>); path_decode(<>, Acc) when H /= 0 -> path_decode(T, <>); path_decode(<<>>, Acc) -> Acc. path_norm_reverse(<<"/", T/binary>>) -> start_dir(0, <<"/">>, T); path_norm_reverse(T) -> start_dir(0, <<"">>, T). start_dir(N, Path, <<"..">>) -> rest_dir(N, Path, <<"">>); start_dir(N, Path, <<"/", T/binary>>) -> start_dir(N, Path, T); start_dir(N, Path, <<"./", T/binary>>) -> start_dir(N, Path, T); start_dir(N, Path, <<"../", T/binary>>) -> start_dir(N + 1, Path, T); start_dir(N, Path, T) -> rest_dir(N, Path, T). rest_dir(_N, Path, <<>>) -> case Path of <<>> -> <<"/">>; _ -> Path end; rest_dir(0, Path, <<$/, T/binary>>) -> start_dir(0, <<$/, Path/binary>>, T); rest_dir(N, Path, <<$/, T/binary>>) -> start_dir(N - 1, Path, T); rest_dir(0, Path, <>) -> rest_dir(0, <>, T); rest_dir(N, Path, <<_H, T/binary>>) -> rest_dir(N, Path, T). expand_custom_headers(Headers) -> lists:map(fun({K, V}) -> {K, misc:expand_keyword(<<"@VERSION@">>, V, ?VERSION)} end, Headers). %% hex_to_integer hex_to_integer(Hex) -> case catch list_to_integer(Hex, 16) of {'EXIT', _} -> old_hex_to_integer(Hex); X -> X end. old_hex_to_integer(Hex) -> DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10; (H) when H >= $A, H =< $F -> H - $A + 10; (H) when H >= $0, H =< $9 -> H - $0 end, lists:foldl(fun (E, Acc) -> Acc * 16 + DEHEX(E) end, 0, Hex). code_to_phrase(100) -> <<"Continue">>; code_to_phrase(101) -> <<"Switching Protocols ">>; code_to_phrase(200) -> <<"OK">>; code_to_phrase(201) -> <<"Created">>; code_to_phrase(202) -> <<"Accepted">>; code_to_phrase(203) -> <<"Non-Authoritative Information">>; code_to_phrase(204) -> <<"No Content">>; code_to_phrase(205) -> <<"Reset Content">>; code_to_phrase(206) -> <<"Partial Content">>; code_to_phrase(300) -> <<"Multiple Choices">>; code_to_phrase(301) -> <<"Moved Permanently">>; code_to_phrase(302) -> <<"Found">>; code_to_phrase(303) -> <<"See Other">>; code_to_phrase(304) -> <<"Not Modified">>; code_to_phrase(305) -> <<"Use Proxy">>; code_to_phrase(306) -> <<"(Unused)">>; code_to_phrase(307) -> <<"Temporary Redirect">>; code_to_phrase(400) -> <<"Bad Request">>; code_to_phrase(401) -> <<"Unauthorized">>; code_to_phrase(402) -> <<"Payment Required">>; code_to_phrase(403) -> <<"Forbidden">>; code_to_phrase(404) -> <<"Not Found">>; code_to_phrase(405) -> <<"Method Not Allowed">>; code_to_phrase(406) -> <<"Not Acceptable">>; code_to_phrase(407) -> <<"Proxy Authentication Required">>; code_to_phrase(408) -> <<"Request Timeout">>; code_to_phrase(409) -> <<"Conflict">>; code_to_phrase(410) -> <<"Gone">>; code_to_phrase(411) -> <<"Length Required">>; code_to_phrase(412) -> <<"Precondition Failed">>; code_to_phrase(413) -> <<"Request Entity Too Large">>; code_to_phrase(414) -> <<"Request-URI Too Long">>; code_to_phrase(415) -> <<"Unsupported Media Type">>; code_to_phrase(416) -> <<"Requested Range Not Satisfiable">>; code_to_phrase(417) -> <<"Expectation Failed">>; code_to_phrase(500) -> <<"Internal Server Error">>; code_to_phrase(501) -> <<"Not Implemented">>; code_to_phrase(502) -> <<"Bad Gateway">>; code_to_phrase(503) -> <<"Service Unavailable">>; code_to_phrase(504) -> <<"Gateway Timeout">>; code_to_phrase(505) -> <<"HTTP Version Not Supported">>. -spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | undefined. parse_auth(<<"Basic ", Auth64/binary>>) -> Auth = try base64:decode(Auth64) catch _:badarg -> <<>> end, %% Auth should be a string with the format: user@server:password %% Note that password can contain additional characters '@' and ':' case str:chr(Auth, $:) of 0 -> undefined; Pos -> {User, <<$:, Pass/binary>>} = erlang:split_binary(Auth, Pos-1), PassUtf8 = unicode:characters_to_binary(binary_to_list(Pass), utf8), {User, PassUtf8} end; parse_auth(<<"Bearer ", SToken/binary>>) -> Token = str:strip(SToken), {oauth, Token, []}; parse_auth(<<_/binary>>) -> undefined. parse_urlencoded(S) -> parse_urlencoded(S, nokey, <<>>, key). parse_urlencoded(<<$%, Hi, Lo, Tail/binary>>, Last, Cur, State) -> Hex = hex_to_integer([Hi, Lo]), parse_urlencoded(Tail, Last, <>, State); parse_urlencoded(<<$&, Tail/binary>>, _Last, Cur, key) -> [{Cur, <<"">>} | parse_urlencoded(Tail, nokey, <<>>, key)]; %% cont keymode parse_urlencoded(<<$&, Tail/binary>>, Last, Cur, value) -> V = {Last, Cur}, [V | parse_urlencoded(Tail, nokey, <<>>, key)]; parse_urlencoded(<<$+, Tail/binary>>, Last, Cur, State) -> parse_urlencoded(Tail, Last, <>, State); parse_urlencoded(<<$=, Tail/binary>>, _Last, Cur, key) -> parse_urlencoded(Tail, Cur, <<>>, value); %% change mode parse_urlencoded(<>, Last, Cur, State) -> parse_urlencoded(Tail, Last, <>, State); parse_urlencoded(<<>>, Last, Cur, _State) -> [{Last, Cur}]; parse_urlencoded(undefined, _, _, _) -> []. url_encode(A) -> url_encode(A, <<>>). url_encode(<>, Acc) when (H >= $a andalso H =< $z) orelse (H >= $A andalso H =< $Z) orelse (H >= $0 andalso H =< $9) orelse H == $_ orelse H == $. orelse H == $- orelse H == $/ orelse H == $: -> url_encode(T, <>); url_encode(<>, Acc) -> case integer_to_hex(H) of [X, Y] -> url_encode(T, <>); [X] -> url_encode(T, <>) end; url_encode(<<>>, Acc) -> Acc. integer_to_hex(I) -> case catch erlang:integer_to_list(I, 16) of {'EXIT', _} -> old_integer_to_hex(I); Int -> Int end. old_integer_to_hex(I) when I < 10 -> integer_to_list(I); old_integer_to_hex(I) when I < 16 -> [I - 10 + $A]; old_integer_to_hex(I) when I >= 16 -> N = trunc(I / 16), old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16). % The following code is mostly taken from yaws_ssl.erl toupper(C) when C >= $a andalso C =< $z -> C - 32; toupper(C) -> C. tolower(C) when C >= $A andalso C =< $Z -> C + 32; tolower(C) -> C. normalize_header_name(Name) -> normalize_header_name(Name, [], true). normalize_header_name(<<"">>, Acc, _) -> iolist_to_binary(Acc); normalize_header_name(<<"-", Rest/binary>>, Acc, _) -> normalize_header_name(Rest, [Acc, "-"], true); normalize_header_name(<>, Acc, true) -> normalize_header_name(Rest, [Acc, toupper(C)], false); normalize_header_name(<>, Acc, false) -> normalize_header_name(Rest, [Acc, tolower(C)], false). normalize_path(Path) -> normalize_path(Path, []). normalize_path([], Norm) -> lists:reverse(Norm); normalize_path([<<"..">>|Path], Norm) -> normalize_path(Path, Norm); normalize_path([_Parent, <<"..">>|Path], Norm) -> normalize_path(Path, Norm); normalize_path([Part | Path], Norm) -> normalize_path(Path, [Part|Norm]). -spec get_certfile([proplists:property()]) -> binary() | undefined. get_certfile(Opts) -> case lists:keyfind(certfile, 1, Opts) of {_, CertFile} -> CertFile; false -> case ejabberd_pkix:get_certfile(?MYNAME) of {ok, CertFile} -> CertFile; error -> ejabberd_config:get_option({domain_certfile, ?MYNAME}) end end. transform_listen_option(captcha, Opts) -> [{captcha, true}|Opts]; transform_listen_option(register, Opts) -> [{register, true}|Opts]; transform_listen_option(web_admin, Opts) -> [{web_admin, true}|Opts]; transform_listen_option(http_bind, Opts) -> [{http_bind, true}|Opts]; transform_listen_option(http_poll, Opts) -> Opts; transform_listen_option({request_handlers, Hs}, Opts) -> Hs1 = lists:map( fun({PList, Mod}) when is_list(PList) -> Path = iolist_to_binary([[$/, P] || P <- PList]), {Path, Mod}; (Opt) -> Opt end, Hs), [{request_handlers, Hs1} | Opts]; transform_listen_option(Opt, Opts) -> [Opt|Opts]. -spec opt_type(trusted_proxies) -> fun((all | [binary()]) -> all | [binary()]); (atom()) -> [atom()]. opt_type(trusted_proxies) -> fun (all) -> all; (TPs) -> [iolist_to_binary(TP) || TP <- TPs] end; opt_type(_) -> [trusted_proxies]. -spec listen_opt_type(tls) -> fun((boolean()) -> boolean()); (certfile) -> fun((binary()) -> binary()); (ciphers) -> fun((binary()) -> binary()); (dhfile) -> fun((binary()) -> binary()); (protocol_options) -> fun(([binary()]) -> binary()); (tls_compression) -> fun((boolean()) -> boolean()); (captcha) -> fun((boolean()) -> boolean()); (register) -> fun((boolean()) -> boolean()); (web_admin) -> fun((boolean()) -> boolean()); (http_bind) -> fun((boolean()) -> boolean()); (xmlrpc) -> fun((boolean()) -> boolean()); (request_handlers) -> fun(([{binary(), atom()}]) -> [{binary(), atom()}]); (default_host) -> fun((binary()) -> binary()); (custom_headers) -> fun(([{binary(), binary()}]) -> [{binary(), binary()}]); (atom()) -> [atom()]. listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(certfile = Opt) -> fun(S) -> ?WARNING_MSG("Listening option '~s' for ~s is deprecated, use " "'certfiles' global option instead", [Opt, ?MODULE]), ejabberd_pkix:add_certfile(S), iolist_to_binary(S) end; listen_opt_type(ciphers) -> fun iolist_to_binary/1; listen_opt_type(dhfile) -> fun misc:try_read_file/1; listen_opt_type(protocol_options) -> fun(Options) -> str:join(Options, <<"|">>) end; listen_opt_type(tls_compression) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(captcha) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(register) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(web_admin) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(http_bind) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(xmlrpc) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(request_handlers) -> fun(Hs) -> Hs1 = lists:map(fun ({Mod, Path}) when is_atom(Mod) -> {Path, Mod}; ({Path, Mod}) -> {Path, Mod} end, Hs), Hs2 = [{str:tokens( iolist_to_binary(Path), <<"/">>), Mod} || {Path, Mod} <- Hs1], [{Path, case Mod of mod_http_bind -> mod_bosh; _ -> Mod end} || {Path, Mod} <- Hs2] end; listen_opt_type(default_host) -> fun(A) -> A end; listen_opt_type(custom_headers) -> fun expand_custom_headers/1; listen_opt_type(_) -> %% TODO fun(A) -> A end. ejabberd-18.01/src/mod_block_strangers.erl0000644000232200023220000001257513225664356021171 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_block_strangers.erl %%% Author : Alexey Shchepin %%% Purpose : Block packets from non-subscribers %%% Created : 25 Dec 2016 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_block_strangers). -author('alexey@process-one.net'). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). -export([filter_packet/1, filter_offline_msg/1]). -include("xmpp.hrl"). -include("ejabberd.hrl"). -include("logger.hrl"). -define(SETS, gb_sets). start(Host, _Opts) -> ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, filter_packet, 25), ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, filter_offline_msg, 25). stop(Host) -> ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, filter_packet, 25), ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, filter_offline_msg, 25). reload(_Host, _NewOpts, _OldOpts) -> ok. filter_packet({#message{from = From} = Msg, State} = Acc) -> LFrom = jid:tolower(From), LBFrom = jid:remove_resource(LFrom), #{pres_a := PresA} = State, case (?SETS):is_element(LFrom, PresA) orelse (?SETS):is_element(LBFrom, PresA) orelse sets_bare_member(LBFrom, PresA) of false -> case check_message(Msg) of allow -> Acc; deny -> {stop, {drop, State}} end; true -> Acc end; filter_packet(Acc) -> Acc. filter_offline_msg({_Action, #message{} = Msg} = Acc) -> case check_message(Msg) of allow -> Acc; deny -> {stop, {drop, Msg}} end. check_message(#message{from = From, to = To, lang = Lang} = Msg) -> LServer = To#jid.lserver, AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users, true), case (Msg#message.body == [] andalso Msg#message.subject == []) orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>) andalso ejabberd_router:is_my_host(From#jid.lserver)) of false -> case check_subscription(From, To) of none -> Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop, true), Log = gen_mod:get_module_opt(LServer, ?MODULE, log, false), if Log -> ?INFO_MSG("~s message from stranger ~s to ~s", [if Drop -> "Rejecting"; true -> "Allow" end, jid:encode(From), jid:encode(To)]); true -> ok end, if Drop -> Txt = <<"Messages from strangers are rejected">>, Err = xmpp:err_policy_violation(Txt, Lang), ejabberd_router:route_error(Msg, Err), deny; true -> allow end; some -> allow end; true -> allow end. -spec check_subscription(jid(), jid()) -> none | some. check_subscription(From, To) -> {LocalUser, LocalServer, _} = jid:tolower(To), {RemoteUser, RemoteServer, _} = jid:tolower(From), case ejabberd_hooks:run_fold( roster_get_jid_info, LocalServer, {none, []}, [LocalUser, LocalServer, From]) of {none, _} when RemoteUser == <<"">> -> none; {none, _} -> case gen_mod:get_module_opt(LocalServer, ?MODULE, allow_transports, true) of true -> %% Check if the contact's server is in the roster case ejabberd_hooks:run_fold( roster_get_jid_info, LocalServer, {none, []}, [LocalUser, LocalServer, jid:make(RemoteServer)]) of {none, _} -> none; _ -> some end; false -> none end; _ -> some end. sets_bare_member({U, S, <<"">>} = LBJID, Set) -> case ?SETS:next(sets_iterator_from(LBJID, Set)) of {{U, S, _}, _} -> true; _ -> false end. -ifdef(GB_SETS_ITERATOR_FROM). sets_iterator_from(Element, Set) -> ?SETS:iterator_from(Element, Set). -else. %% Copied from gb_sets.erl %% TODO: Remove after dropping R17 support sets_iterator_from(S, {_, T}) -> iterator_from(S, T, []). iterator_from(S, {K, _, T}, As) when K < S -> iterator_from(S, T, As); iterator_from(_, {_, nil, _} = T, As) -> [T | As]; iterator_from(S, {_, L, _} = T, As) -> iterator_from(S, L, [T | As]); iterator_from(_, nil, As) -> As. -endif. depends(_Host, _Opts) -> []. mod_opt_type(drop) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(log) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(allow_local_users) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(allow_transports) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [drop, log, allow_local_users, allow_transports]. ejabberd-18.01/src/ext_mod.erl0000644000232200023220000006073513225664356016610 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ext_mod.erl %%% Author : Christophe Romain %%% Purpose : external modules management %%% Created : 19 Feb 2015 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2006-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ext_mod). -behaviour(ejabberd_config). -behaviour(gen_server). -author("Christophe Romain "). -export([start_link/0, update/0, check/1, available_command/0, available/0, available/1, installed_command/0, installed/0, installed/1, install/1, uninstall/1, upgrade/0, upgrade/1, add_sources/2, del_sources/1, modules_dir/0, config_dir/0, opt_type/1, get_commands_spec/0]). -export([compile_erlang_file/2, compile_elixir_file/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd_commands.hrl"). -include("logger.hrl"). -define(REPOS, "https://github.com/processone/ejabberd-contrib"). -record(state, {}). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> process_flag(trap_exit, true), [code:add_patha(module_ebin_dir(Module)) || {Module, _} <- installed()], p1_http:start(), ejabberd_commands:register_commands(get_commands_spec()), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ejabberd_commands:unregister_commands(get_commands_spec()). code_change(_OldVsn, State, _Extra) -> {ok, State}. %% -- ejabberd commands get_commands_spec() -> [#ejabberd_commands{name = modules_update_specs, tags = [admin,modules], desc = "Update the module source code from Git", longdesc = "A connection to Internet is required", module = ?MODULE, function = update, args = [], result = {res, rescode}}, #ejabberd_commands{name = modules_available, tags = [admin,modules], desc = "List the contributed modules available to install", module = ?MODULE, function = available_command, result_desc = "List of tuples with module name and description", result_example = [{mod_cron, "Execute scheduled commands"}, {mod_rest, "ReST frontend"}], args = [], result = {modules, {list, {module, {tuple, [{name, atom}, {summary, string}]}}}}}, #ejabberd_commands{name = modules_installed, tags = [admin,modules], desc = "List the contributed modules already installed", module = ?MODULE, function = installed_command, result_desc = "List of tuples with module name and description", result_example = [{mod_cron, "Execute scheduled commands"}, {mod_rest, "ReST frontend"}], args = [], result = {modules, {list, {module, {tuple, [{name, atom}, {summary, string}]}}}}}, #ejabberd_commands{name = module_install, tags = [admin,modules], desc = "Compile, install and start an available contributed module", module = ?MODULE, function = install, args_desc = ["Module name"], args_example = [<<"mod_rest">>], args = [{module, binary}], result = {res, rescode}}, #ejabberd_commands{name = module_uninstall, tags = [admin,modules], desc = "Uninstall a contributed module", module = ?MODULE, function = uninstall, args_desc = ["Module name"], args_example = [<<"mod_rest">>], args = [{module, binary}], result = {res, rescode}}, #ejabberd_commands{name = module_upgrade, tags = [admin,modules], desc = "Upgrade the running code of an installed module", longdesc = "In practice, this uninstalls and installs the module", module = ?MODULE, function = upgrade, args_desc = ["Module name"], args_example = [<<"mod_rest">>], args = [{module, binary}], result = {res, rescode}}, #ejabberd_commands{name = module_check, tags = [admin,modules], desc = "Check the contributed module repository compliance", module = ?MODULE, function = check, args_desc = ["Module name"], args_example = [<<"mod_rest">>], args = [{module, binary}], result = {res, rescode}} ]. %% -- public modules functions update() -> Contrib = maps:put(?REPOS, [], maps:new()), Jungles = lists:foldl(fun({Package, Spec}, Acc) -> Repo = proplists:get_value(url, Spec, ""), Mods = maps:get(Repo, Acc, []), maps:put(Repo, [Package|Mods], Acc) end, Contrib, modules_spec(sources_dir(), "*/*")), Repos = maps:fold(fun(Repo, _Mods, Acc) -> Update = add_sources(Repo), ?INFO_MSG("Update packages from repo ~s: ~p", [Repo, Update]), case Update of ok -> Acc; Error -> [{repository, Repo, Error}|Acc] end end, [], Jungles), Res = lists:foldl(fun({Package, Spec}, Acc) -> Repo = proplists:get_value(url, Spec, ""), Update = add_sources(Package, Repo), ?INFO_MSG("Update package ~s: ~p", [Package, Update]), case Update of ok -> Acc; Error -> [{Package, Repo, Error}|Acc] end end, Repos, modules_spec(sources_dir(), "*")), case Res of [] -> ok; [Error|_] -> Error end. available() -> Jungle = modules_spec(sources_dir(), "*/*"), Standalone = modules_spec(sources_dir(), "*"), lists:keysort(1, lists:foldl(fun({Key, Val}, Acc) -> lists:keystore(Key, 1, Acc, {Key, Val}) end, Jungle, Standalone)). available(Module) when is_atom(Module) -> available(misc:atom_to_binary(Module)); available(Package) when is_binary(Package) -> Available = [misc:atom_to_binary(K) || K<-proplists:get_keys(available())], lists:member(Package, Available). available_command() -> [short_spec(Item) || Item <- available()]. installed() -> modules_spec(modules_dir(), "*"). installed(Module) when is_atom(Module) -> installed(misc:atom_to_binary(Module)); installed(Package) when is_binary(Package) -> Installed = [misc:atom_to_binary(K) || K<-proplists:get_keys(installed())], lists:member(Package, Installed). installed_command() -> [short_spec(Item) || Item <- installed()]. install(Module) when is_atom(Module) -> install(misc:atom_to_binary(Module)); install(Package) when is_binary(Package) -> Spec = [S || {Mod, S} <- available(), misc:atom_to_binary(Mod)==Package], case {Spec, installed(Package), is_contrib_allowed()} of {_, _, false} -> {error, not_allowed}; {[], _, _} -> {error, not_available}; {_, true, _} -> {error, conflict}; {[Attrs], _, _} -> Module = misc:binary_to_atom(Package), case compile_and_install(Module, Attrs) of ok -> code:add_patha(module_ebin_dir(Module)), ejabberd_config:reload_file(), case erlang:function_exported(Module, post_install, 0) of true -> Module:post_install(); _ -> ok end; Error -> delete_path(module_lib_dir(Module)), Error end end. uninstall(Module) when is_atom(Module) -> uninstall(misc:atom_to_binary(Module)); uninstall(Package) when is_binary(Package) -> case installed(Package) of true -> Module = misc:binary_to_atom(Package), case erlang:function_exported(Module, pre_uninstall, 0) of true -> Module:pre_uninstall(); _ -> ok end, [catch gen_mod:stop_module(Host, Module) || Host <- ejabberd_config:get_myhosts()], code:purge(Module), code:delete(Module), code:del_path(module_ebin_dir(Module)), delete_path(module_lib_dir(Module)), ejabberd_config:reload_file(); false -> {error, not_installed} end. upgrade() -> [{Package, upgrade(Package)} || {Package, _Spec} <- installed()]. upgrade(Module) when is_atom(Module) -> upgrade(misc:atom_to_binary(Module)); upgrade(Package) when is_binary(Package) -> uninstall(Package), install(Package). add_sources(Path) when is_list(Path) -> add_sources(iolist_to_binary(module_name(Path)), Path). add_sources(_, "") -> {error, no_url}; add_sources(Module, Path) when is_atom(Module), is_list(Path) -> add_sources(misc:atom_to_binary(Module), Path); add_sources(Package, Path) when is_binary(Package), is_list(Path) -> DestDir = sources_dir(), RepDir = filename:join(DestDir, module_name(Path)), delete_path(RepDir), case filelib:ensure_dir(RepDir) of ok -> case {string:left(Path, 4), string:right(Path, 2)} of {"http", "ip"} -> extract(zip, geturl(Path), DestDir); {"http", "gz"} -> extract(tar, geturl(Path), DestDir); {"http", _} -> extract_url(Path, DestDir); {"git@", _} -> extract_github_master(Path, DestDir); {_, "ip"} -> extract(zip, Path, DestDir); {_, "gz"} -> extract(tar, Path, DestDir); _ -> {error, unsupported_source} end; Error -> Error end. del_sources(Module) when is_atom(Module) -> del_sources(misc:atom_to_binary(Module)); del_sources(Package) when is_binary(Package) -> case uninstall(Package) of ok -> SrcDir = module_src_dir(misc:binary_to_atom(Package)), delete_path(SrcDir); Error -> Error end. check(Module) when is_atom(Module) -> check(misc:atom_to_binary(Module)); check(Package) when is_binary(Package) -> case {available(Package), installed(Package)} of {false, _} -> {error, not_available}; {_, false} -> Status = install(Package), uninstall(Package), case Status of ok -> check_sources(misc:binary_to_atom(Package)); Error -> Error end; _ -> check_sources(misc:binary_to_atom(Package)) end. %% -- archives and variables functions geturl(Url) -> geturl(Url, []). geturl(Url, UsrOpts) -> geturl(Url, [], UsrOpts). geturl(Url, Hdrs, UsrOpts) -> Host = case getenv("PROXY_SERVER", "", ":") of [H, Port] -> [{proxy_host, H}, {proxy_port, list_to_integer(Port)}]; [H] -> [{proxy_host, H}, {proxy_port, 8080}]; _ -> [] end, User = case getenv("PROXY_USER", "", [4]) of [U, Pass] -> [{proxy_user, U}, {proxy_password, Pass}]; _ -> [] end, case p1_http:request(get, Url, Hdrs, [], Host++User++UsrOpts++[{version, "HTTP/1.0"}]) of {ok, 200, Headers, Response} -> {ok, Headers, Response}; {ok, Code, _Headers, Response} -> {error, {Code, Response}}; {error, Reason} -> {error, Reason} end. getenv(Env) -> getenv(Env, ""). getenv(Env, Default) -> case os:getenv(Env) of false -> Default; "" -> Default; Value -> Value end. getenv(Env, Default, Separator) -> string:tokens(getenv(Env, Default), Separator). extract(zip, {ok, _, Body}, DestDir) -> extract(zip, iolist_to_binary(Body), DestDir); extract(tar, {ok, _, Body}, DestDir) -> extract(tar, {binary, iolist_to_binary(Body)}, DestDir); extract(_, {error, Reason}, _) -> {error, Reason}; extract(zip, Zip, DestDir) -> case zip:extract(Zip, [{cwd, DestDir}]) of {ok, _} -> ok; Error -> Error end; extract(tar, Tar, DestDir) -> erl_tar:extract(Tar, [compressed, {cwd, DestDir}]). extract_url(Path, DestDir) -> hd([extract_github_master(Path, DestDir) || string:str(Path, "github") > 0] ++[{error, unsupported_source}]). extract_github_master(Repos, DestDir) -> Base = case string:tokens(Repos, ":") of ["git@github.com", T1] -> "https://github.com/"++T1; _ -> Repos end, Url = case lists:reverse(Base) of [$t,$i,$g,$.|T2] -> lists:reverse(T2); _ -> Base end, case extract(zip, geturl(Url++"/archive/master.zip"), DestDir) of ok -> RepDir = filename:join(DestDir, module_name(Repos)), file:rename(RepDir++"-master", RepDir); Error -> Error end. copy(From, To) -> case filelib:is_dir(From) of true -> Copy = fun(F) -> SubFrom = filename:join(From, F), SubTo = filename:join(To, F), copy(SubFrom, SubTo) end, lists:foldl(fun(ok, ok) -> ok; (ok, Error) -> Error; (Error, _) -> Error end, ok, [Copy(filename:basename(X)) || X<-filelib:wildcard(From++"/*")]); false -> filelib:ensure_dir(To), case file:copy(From, To) of {ok, _} -> ok; Error -> Error end end. delete_path(Path) -> case filelib:is_dir(Path) of true -> [delete_path(SubPath) || SubPath <- filelib:wildcard(Path++"/*")], file:del_dir(Path); false -> file:delete(Path) end. modules_dir() -> DefaultDir = filename:join(getenv("HOME"), ".ejabberd-modules"), getenv("CONTRIB_MODULES_PATH", DefaultDir). sources_dir() -> filename:join(modules_dir(), "sources"). config_dir() -> DefaultDir = filename:join(modules_dir(), "conf"), getenv("CONTRIB_MODULES_CONF_DIR", DefaultDir). module_lib_dir(Package) -> filename:join(modules_dir(), Package). module_ebin_dir(Package) -> filename:join(module_lib_dir(Package), "ebin"). module_src_dir(Package) -> Rep = module_name(Package), SrcDir = sources_dir(), Standalone = filelib:wildcard(Rep, SrcDir), Jungle = filelib:wildcard("*/"++Rep, SrcDir), case Standalone++Jungle of [RepDir|_] -> filename:join(SrcDir, RepDir); _ -> filename:join(SrcDir, Rep) end. module_name(Id) -> filename:basename(filename:rootname(Id)). module(Id) -> misc:binary_to_atom(iolist_to_binary(module_name(Id))). module_spec(Spec) -> [{path, filename:dirname(Spec)} | case consult(Spec) of {ok, Meta} -> Meta; _ -> [] end]. modules_spec(Dir, Path) -> Wildcard = filename:join(Path, "*.spec"), lists:sort( [{module(Match), module_spec(filename:join(Dir, Match))} || Match <- filelib:wildcard(Wildcard, Dir)]). short_spec({Module, Attrs}) when is_atom(Module), is_list(Attrs) -> {Module, proplists:get_value(summary, Attrs, "")}. is_contrib_allowed() -> ejabberd_config:get_option(allow_contrib_modules, true). %% -- build functions check_sources(Module) -> SrcDir = module_src_dir(Module), SpecFile = filename:flatten([Module, ".spec"]), {ok, Dir} = file:get_cwd(), file:set_cwd(SrcDir), HaveSrc = case filelib:is_dir("src") or filelib:is_dir("lib") of true -> []; false -> [{missing, "src (Erlang) or lib (Elixir) sources directory"}] end, DirCheck = lists:foldl( fun({Type, Name}, Acc) -> case filelib:Type(Name) of true -> Acc; false -> [{missing, Name}|Acc] end end, HaveSrc, [{is_file, "README.txt"}, {is_file, "COPYING"}, {is_file, SpecFile}]), SpecCheck = case consult(SpecFile) of {ok, Spec} -> lists:foldl( fun(Key, Acc) -> case lists:keysearch(Key, 1, Spec) of false -> [{missing_meta, Key}|Acc]; {value, {Key, [_NoEmpty|_]}} -> Acc; {value, {Key, Val}} -> [{invalid_meta, {Key, Val}}|Acc] end end, [], [author, summary, home, url]); {error, Error} -> [{invalid_spec, Error}] end, file:set_cwd(Dir), Result = DirCheck ++ SpecCheck, case Result of [] -> ok; _ -> {error, Result} end. compile_and_install(Module, Spec) -> SrcDir = module_src_dir(Module), LibDir = module_lib_dir(Module), case filelib:is_dir(SrcDir) of true -> case compile_deps(SrcDir) of ok -> case compile(SrcDir) of ok -> install(Module, Spec, SrcDir, LibDir); Error -> Error end; Error -> Error end; false -> Path = proplists:get_value(url, Spec, ""), case add_sources(Module, Path) of ok -> compile_and_install(Module, Spec); Error -> Error end end. compile_deps(LibDir) -> Deps = filename:join(LibDir, "deps"), case filelib:is_dir(Deps) of true -> ok; % assume deps are included false -> fetch_rebar_deps(LibDir) end, Rs = [compile(Dep) || Dep <- filelib:wildcard(filename:join(Deps, "*"))], compile_result(Rs). compile(LibDir) -> Bin = filename:join(LibDir, "ebin"), Inc = filename:join(LibDir, "include"), Lib = filename:join(LibDir, "lib"), Src = filename:join(LibDir, "src"), Options = [{outdir, Bin}, {i, Inc} | compile_options()], filelib:ensure_dir(filename:join(Bin, ".")), [copy(App, Bin) || App <- filelib:wildcard(Src++"/*.app")], Er = [compile_erlang_file(Bin, File, Options) || File <- filelib:wildcard(Src++"/*.erl")], Ex = [compile_elixir_file(Bin, File) || File <- filelib:wildcard(Lib ++ "/*.ex")], compile_result(Er++Ex). compile_result(Results) -> case lists:dropwhile( fun({ok, _}) -> true; (_) -> false end, Results) of [] -> ok; [Error|_] -> Error end. compile_options() -> [verbose, report_errors, report_warnings] ++ [{i, filename:join(app_dir(App), "include")} || App <- [fast_xml, xmpp, p1_utils, ejabberd]] ++ [{i, filename:join(mod_dir(Mod), "include")} || Mod <- installed()]. app_dir(App) -> case code:lib_dir(App) of {error, bad_name} -> case code:which(App) of Beam when is_list(Beam) -> filename:dirname(filename:dirname(Beam)); _ -> "." end; Dir -> Dir end. mod_dir({Package, Spec}) -> Default = filename:join(modules_dir(), Package), proplists:get_value(path, Spec, Default). compile_erlang_file(Dest, File) -> compile_erlang_file(Dest, File, compile_options()). compile_erlang_file(Dest, File, ErlOptions) -> Options = [{outdir, Dest} | ErlOptions], case compile:file(File, Options) of {ok, Module} -> {ok, Module}; {ok, Module, _} -> {ok, Module}; {ok, Module, _, _} -> {ok, Module}; error -> {error, {compilation_failed, File}}; {error, E, W} -> {error, {compilation_failed, File, E, W}} end. compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) -> compile_elixir_file(list_to_binary(Dest), list_to_binary(File)); compile_elixir_file(Dest, File) -> try 'Elixir.Kernel.ParallelCompiler':files_to_path([File], Dest, []) of [Module] -> {ok, Module} catch _ -> {error, {compilation_failed, File}} end. install(Module, Spec, SrcDir, LibDir) -> {ok, CurDir} = file:get_cwd(), file:set_cwd(SrcDir), Files1 = [{File, copy(File, filename:join(LibDir, File))} || File <- filelib:wildcard("{ebin,priv,conf,include}/**")], Files2 = [{File, copy(File, filename:join(LibDir, filename:join(lists:nthtail(2,filename:split(File)))))} || File <- filelib:wildcard("deps/*/{ebin,priv}/**")], Errors = lists:dropwhile(fun({_, ok}) -> true; (_) -> false end, Files1++Files2), Result = case Errors of [{F, {error, E}}|_] -> {error, {F, E}}; [] -> SpecPath = proplists:get_value(path, Spec), SpecFile = filename:flatten([Module, ".spec"]), copy(filename:join(SpecPath, SpecFile), filename:join(LibDir, SpecFile)) end, file:set_cwd(CurDir), Result. %% -- minimalist rebar spec parser, only support git fetch_rebar_deps(SrcDir) -> case rebar_deps(filename:join(SrcDir, "rebar.config")) ++ rebar_deps(filename:join(SrcDir, "rebar.config.script")) of [] -> ok; Deps -> {ok, CurDir} = file:get_cwd(), file:set_cwd(SrcDir), filelib:ensure_dir(filename:join("deps", ".")), lists:foreach(fun({_App, Cmd}) -> os:cmd("cd deps; "++Cmd++"; cd ..") end, Deps), file:set_cwd(CurDir) end. rebar_deps(Script) -> case file:script(Script) of {ok, Config} when is_list(Config) -> [rebar_dep(Dep) || Dep <- proplists:get_value(deps, Config, [])]; {ok, {deps, Deps}} -> [rebar_dep(Dep) || Dep <- Deps]; _ -> [] end. rebar_dep({App, _, {git, Url}}) -> {App, "git clone "++Url++" "++filename:basename(App)}; rebar_dep({App, _, {git, Url, {branch, Ref}}}) -> {App, "git clone -n "++Url++" "++filename:basename(App)++ "; (cd "++filename:basename(App)++ "; git checkout -q origin/"++Ref++")"}; rebar_dep({App, _, {git, Url, {tag, Ref}}}) -> {App, "git clone -n "++Url++" "++filename:basename(App)++ "; (cd "++filename:basename(App)++ "; git checkout -q "++Ref++")"}; rebar_dep({App, _, {git, Url, Ref}}) -> {App, "git clone -n "++Url++" "++filename:basename(App)++ "; (cd "++filename:basename(App)++ "; git checkout -q "++Ref++")"}. %% -- YAML spec parser consult(File) -> case fast_yaml:decode_from_file(File, [plain_as_atom]) of {ok, []} -> {ok, []}; {ok, [Doc|_]} -> {ok, [format(Spec) || Spec <- Doc]}; {error, Err} -> {error, fast_yaml:format_error(Err)} end. format({Key, Val}) when is_binary(Val) -> {Key, binary_to_list(Val)}; format({Key, Val}) -> % TODO: improve Yaml parsing {Key, Val}. -spec opt_type(allow_contrib_modules) -> fun((boolean()) -> boolean()); (atom()) -> [atom()]. opt_type(allow_contrib_modules) -> fun (false) -> false; (no) -> false; (_) -> true end; opt_type(_) -> [allow_contrib_modules]. ejabberd-18.01/src/mod_admin_update_sql.erl0000644000232200023220000003335013225664356021312 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_admin_update_sql.erl %%% Author : Alexey Shchepin %%% Purpose : Convert SQL DB to the new format %%% Created : 9 Aug 2017 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_admin_update_sql). -author('alexey@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, mod_opt_type/1, get_commands_spec/0, depends/2]). % Commands API -export([update_sql/0]). -include("logger.hrl"). -include("ejabberd.hrl"). -include("ejabberd_commands.hrl"). -include("xmpp.hrl"). -include("ejabberd_sql_pt.hrl"). %%% %%% gen_mod %%% start(_Host, _Opts) -> ejabberd_commands:register_commands(get_commands_spec()). stop(_Host) -> ejabberd_commands:unregister_commands(get_commands_spec()). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> []. %%% %%% Register commands %%% get_commands_spec() -> [#ejabberd_commands{name = update_sql, tags = [sql], desc = "Convert SQL DB to the new format", module = ?MODULE, function = update_sql, args = [], args_example = [], args_desc = [], result = {res, rescode}, result_example = ok, result_desc = "Status code: 0 on success, 1 otherwise"} ]. update_sql() -> lists:foreach( fun(Host) -> case ejabberd_sql_sup:get_pids(Host) of [] -> ok; _ -> update_sql(Host) end end, ?MYHOSTS), ok. -record(state, {host :: binary(), dbtype :: mysql | pgsql | sqlite | mssql | odbc, escape}). update_sql(Host) -> LHost = jid:nameprep(Host), DBType = ejabberd_config:get_option({sql_type, LHost}, undefined), IsSupported = case DBType of pgsql -> true; _ -> false end, if not IsSupported -> io:format("Converting ~p DB is not supported~n", [DBType]), error; true -> Escape = case DBType of mssql -> fun ejabberd_sql:standard_escape/1; sqlite -> fun ejabberd_sql:standard_escape/1; _ -> fun ejabberd_sql:escape/1 end, State = #state{host = LHost, dbtype = DBType, escape = Escape}, update_tables(State) end. update_tables(State) -> add_sh_column(State, "users"), drop_pkey(State, "users"), add_pkey(State, "users", ["server_host", "username"]), drop_sh_default(State, "users"), add_sh_column(State, "last"), drop_pkey(State, "last"), add_pkey(State, "last", ["server_host", "username"]), drop_sh_default(State, "last"), add_sh_column(State, "rosterusers"), drop_index(State, "i_rosteru_user_jid"), drop_index(State, "i_rosteru_username"), drop_index(State, "i_rosteru_jid"), create_unique_index(State, "rosterusers", "i_rosteru_sh_user_jid", ["server_host", "username", "jid"]), create_index(State, "rosterusers", "i_rosteru_sh_username", ["server_host", "username"]), create_index(State, "rosterusers", "i_rosteru_sh_jid", ["server_host", "jid"]), drop_sh_default(State, "rosterusers"), add_sh_column(State, "rostergroups"), drop_index(State, "pk_rosterg_user_jid"), create_index(State, "rostergroups", "i_rosterg_sh_user_jid", ["server_host", "username", "jid"]), drop_sh_default(State, "rostergroups"), add_sh_column(State, "sr_group"), add_pkey(State, "sr_group", ["server_host", "name"]), drop_sh_default(State, "sr_group"), add_sh_column(State, "sr_user"), drop_index(State, "i_sr_user_jid_grp"), drop_index(State, "i_sr_user_jid"), drop_index(State, "i_sr_user_grp"), add_pkey(State, "sr_user", ["server_host", "jid", "grp"]), create_index(State, "sr_user", "i_sr_user_sh_jid", ["server_host", "jid"]), create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), drop_sh_default(State, "sr_user"), add_sh_column(State, "spool"), drop_index(State, "i_despool"), create_index(State, "spool", "i_spool_sh_username", ["server_host", "username"]), drop_sh_default(State, "spool"), add_sh_column(State, "archive"), drop_index(State, "i_username"), drop_index(State, "i_username_timestamp"), drop_index(State, "i_timestamp"), drop_index(State, "i_peer"), drop_index(State, "i_bare_peer"), create_index(State, "archive", "i_archive_sh_username_timestamp", ["server_host", "username", "timestamp"]), create_index(State, "archive", "i_archive_sh_timestamp", ["server_host", "timestamp"]), create_index(State, "archive", "i_archive_sh_peer", ["server_host", "peer"]), create_index(State, "archive", "i_archive_sh_bare_peer", ["server_host", "bare_peer"]), drop_sh_default(State, "archive"), add_sh_column(State, "archive_prefs"), drop_pkey(State, "archive_prefs"), add_pkey(State, "archive_prefs", ["server_host", "username"]), drop_sh_default(State, "archive_prefs"), add_sh_column(State, "vcard"), drop_pkey(State, "vcard"), add_pkey(State, "vcard", ["server_host", "username"]), drop_sh_default(State, "vcard"), add_sh_column(State, "vcard_search"), drop_pkey(State, "vcard_search"), drop_index(State, "i_vcard_search_lfn"), drop_index(State, "i_vcard_search_lfamily"), drop_index(State, "i_vcard_search_lgiven"), drop_index(State, "i_vcard_search_lmiddle"), drop_index(State, "i_vcard_search_lnickname"), drop_index(State, "i_vcard_search_lbday"), drop_index(State, "i_vcard_search_lctry"), drop_index(State, "i_vcard_search_llocality"), drop_index(State, "i_vcard_search_lemail"), drop_index(State, "i_vcard_search_lorgname"), drop_index(State, "i_vcard_search_lorgunit"), add_pkey(State, "vcard_search", ["server_host", "username"]), create_index(State, "vcard_search", "i_vcard_search_sh_lfn", ["server_host", "lfn"]), create_index(State, "vcard_search", "i_vcard_search_sh_lfamily", ["server_host", "lfamily"]), create_index(State, "vcard_search", "i_vcard_search_sh_lgiven", ["server_host", "lgiven"]), create_index(State, "vcard_search", "i_vcard_search_sh_lmiddle", ["server_host", "lmiddle"]), create_index(State, "vcard_search", "i_vcard_search_sh_lnickname", ["server_host", "lnickname"]), create_index(State, "vcard_search", "i_vcard_search_sh_lbday", ["server_host", "lbday"]), create_index(State, "vcard_search", "i_vcard_search_sh_lctry", ["server_host", "lctry"]), create_index(State, "vcard_search", "i_vcard_search_sh_llocality", ["server_host", "llocality"]), create_index(State, "vcard_search", "i_vcard_search_sh_lemail", ["server_host", "lemail"]), create_index(State, "vcard_search", "i_vcard_search_sh_lorgname", ["server_host", "lorgname"]), create_index(State, "vcard_search", "i_vcard_search_sh_lorgunit", ["server_host", "lorgunit"]), drop_sh_default(State, "vcard_search"), add_sh_column(State, "privacy_default_list"), drop_pkey(State, "privacy_default_list"), add_pkey(State, "privacy_default_list", ["server_host", "username"]), drop_sh_default(State, "privacy_default_list"), add_sh_column(State, "privacy_list"), drop_index(State, "i_privacy_list_username"), drop_index(State, "i_privacy_list_username_name"), create_index(State, "privacy_list", "i_privacy_list_sh_username", ["server_host", "username"]), create_unique_index(State, "privacy_list", "i_privacy_list_sh_username_name", ["server_host", "username", "name"]), drop_sh_default(State, "privacy_list"), add_sh_column(State, "private_storage"), drop_index(State, "i_private_storage_username"), drop_index(State, "i_private_storage_username_namespace"), add_pkey(State, "private_storage", ["server_host", "username", "namespace"]), create_index(State, "private_storage", "i_private_storage_sh_username", ["server_host", "username"]), drop_sh_default(State, "private_storage"), add_sh_column(State, "roster_version"), drop_pkey(State, "roster_version"), add_pkey(State, "roster_version", ["server_host", "username"]), drop_sh_default(State, "roster_version"), add_sh_column(State, "muc_room"), drop_sh_default(State, "muc_room"), add_sh_column(State, "muc_registered"), drop_sh_default(State, "muc_registered"), add_sh_column(State, "muc_online_room"), drop_sh_default(State, "muc_online_room"), add_sh_column(State, "muc_online_users"), drop_sh_default(State, "muc_online_users"), add_sh_column(State, "irc_custom"), drop_sh_default(State, "irc_custom"), add_sh_column(State, "motd"), drop_pkey(State, "motd"), add_pkey(State, "motd", ["server_host", "username"]), drop_sh_default(State, "motd"), add_sh_column(State, "sm"), drop_index(State, "i_sm_sid"), drop_index(State, "i_sm_username"), add_pkey(State, "sm", ["usec", "pid"]), create_index(State, "sm", "i_sm_sh_username", ["server_host", "username"]), drop_sh_default(State, "sm"), add_sh_column(State, "carboncopy"), drop_index(State, "i_carboncopy_ur"), drop_index(State, "i_carboncopy_user"), add_pkey(State, "carboncopy", ["server_host", "username", "resource"]), create_index(State, "carboncopy", "i_carboncopy_sh_user", ["server_host", "username"]), drop_sh_default(State, "carboncopy"), add_sh_column(State, "push_session"), drop_index(State, "i_push_usn"), drop_index(State, "i_push_ut"), add_pkey(State, "push_session", ["server_host", "username", "timestamp"]), create_index(State, "push_session", "i_push_session_susn", ["server_host", "username", "service", "node"]), drop_sh_default(State, "push_session"), ok. add_sh_column(#state{dbtype = pgsql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD COLUMN server_host text NOT NULL DEFAULT '", (State#state.escape)(State#state.host), "';"]); add_sh_column(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD COLUMN server_host text NOT NULL DEFAULT '", (State#state.escape)(State#state.host), "';"]). drop_pkey(#state{dbtype = pgsql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " DROP CONSTRAINT ", Table, "_pkey;"]); drop_pkey(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " DROP PRIMARY KEY;"]). add_pkey(#state{dbtype = pgsql} = State, Table, Cols) -> SCols = string:join(Cols, ", "), sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD PRIMARY KEY (", SCols, ");"]); add_pkey(#state{dbtype = mysql} = State, Table, Cols) -> SCols = string:join(Cols, ", "), sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD PRIMARY KEY (", SCols, ");"]). drop_sh_default(#state{dbtype = pgsql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ALTER COLUMN server_host DROP DEFAULT;"]); drop_sh_default(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ALTER COLUMN server_host DROP DEFAULT;"]). drop_index(#state{dbtype = pgsql} = State, Index) -> sql_query( State#state.host, ["DROP INDEX ", Index, ";"]); drop_index(#state{dbtype = mysql} = State, Index) -> sql_query( State#state.host, ["DROP INDEX ", Index, ";"]). create_unique_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> SCols = string:join(Cols, ", "), sql_query( State#state.host, ["CREATE UNIQUE INDEX ", Index, " ON ", Table, " USING btree (", SCols, ");"]); create_unique_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> Cols2 = [C ++ "(75)" || C <- Cols], SCols = string:join(Cols2, ", "), sql_query( State#state.host, ["CREATE UNIQUE INDEX ", Index, " ON ", Table, "(", SCols, ");"]). create_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> SCols = string:join(Cols, ", "), sql_query( State#state.host, ["CREATE INDEX ", Index, " ON ", Table, " USING btree (", SCols, ");"]); create_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> Cols2 = [C ++ "(75)" || C <- Cols], SCols = string:join(Cols2, ", "), sql_query( State#state.host, ["CREATE INDEX ", Index, " ON ", Table, "(", SCols, ");"]). sql_query(Host, Query) -> io:format("executing \"~s\" on ~s~n", [Query, Host]), case ejabberd_sql:sql_query(Host, Query) of {error, Error} -> io:format("error: ~p~n", [Error]), ok; _ -> ok end. mod_opt_type(_) -> []. ejabberd-18.01/src/mod_announce.erl0000644000232200023220000007471513225664356017621 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_announce.erl %%% Author : Alexey Shchepin %%% Purpose : Manage announce messages %%% Created : 11 Aug 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% Implements a small subset of XEP-0133: Service Administration %%% Version 1.1 (2005-08-19) -module(mod_announce). -author('alexey@process-one.net'). -behaviour(gen_server). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, export/1, import_info/0, import_start/2, import/5, announce/1, send_motd/1, disco_identity/5, disco_features/5, disco_items/5, depends/2, send_announcement_to_all/3, announce_commands/4, announce_items/4, mod_opt_type/1, clean_cache/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([announce_all/1, announce_all_hosts_all/1, announce_online/1, announce_all_hosts_online/1, announce_motd/1, announce_all_hosts_motd/1, announce_motd_update/1, announce_all_hosts_motd_update/1, announce_motd_delete/1, announce_all_hosts_motd_delete/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_announce.hrl"). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), binary(), [binary()]) -> ok. -callback set_motd_users(binary(), [{binary(), binary(), binary()}]) -> ok | {error, any()}. -callback set_motd(binary(), xmlel()) -> ok | {error, any()}. -callback delete_motd(binary()) -> ok | {error, any()}. -callback get_motd(binary()) -> {ok, xmlel()} | error | {error, any()}. -callback is_motd_user(binary(), binary()) -> {ok, boolean()} | {error, any()}. -callback set_motd_user(binary(), binary()) -> ok | {error, any()}. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). -record(state, {host :: binary()}). -define(NS_ADMINL(Sub), [<<"http:">>, <<"jabber.org">>, <<"protocol">>, <<"admin">>, <>]). -define(MOTD_CACHE, motd_cache). tokenize(Node) -> str:tokens(Node, <<"/#">>). %%==================================================================== %% gen_mod callbacks %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, init_cache(NewMod, Host, NewOpts). depends(_Host, _Opts) -> [{mod_adhoc, hard}]. %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), ejabberd_hooks:add(local_send_to_resource_hook, Host, ?MODULE, announce, 50), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 50), ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, announce_items, 50), ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, announce_commands, 50), ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, send_motd, 50), {ok, #state{host = Host}}. handle_call(_Call, _From, State) -> {noreply, State}. handle_cast({F, #message{from = From, to = To} = Pkt}, State) when is_atom(F) -> LServer = To#jid.lserver, Host = case F of announce_all -> LServer; announce_all_hosts_all -> global; announce_online -> LServer; announce_all_hosts_online -> global; announce_motd -> LServer; announce_all_hosts_motd -> global; announce_motd_update -> LServer; announce_all_hosts_motd_update -> global; announce_motd_delete -> LServer; announce_all_hosts_motd_delete -> global end, Access = get_access(Host), case acl:match_rule(Host, Access, From) of deny -> route_forbidden_error(Pkt); allow -> ?MODULE:F(Pkt) end, {noreply, State}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, #state{host = Host}) -> ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, announce_commands, 50), ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, announce_items, 50), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 50), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 50), ejabberd_hooks:delete(local_send_to_resource_hook, Host, ?MODULE, announce, 50), ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, send_motd, 50). code_change(_OldVsn, State, _Extra) -> {ok, State}. %% Announcing via messages to a custom resource -spec announce(stanza()) -> ok | stop. announce(#message{to = #jid{luser = <<>>} = To} = Packet) -> Proc = gen_mod:get_module_proc(To#jid.lserver, ?MODULE), Res = case To#jid.lresource of <<"announce/all">> -> gen_server:cast(Proc, {announce_all, Packet}); <<"announce/all-hosts/all">> -> gen_server:cast(Proc, {announce_all_hosts_all, Packet}); <<"announce/online">> -> gen_server:cast(Proc, {announce_online, Packet}); <<"announce/all-hosts/online">> -> gen_server:cast(Proc, {announce_all_hosts_online, Packet}); <<"announce/motd">> -> gen_server:cast(Proc, {announce_motd, Packet}); <<"announce/all-hosts/motd">> -> gen_server:cast(Proc, {announce_all_hosts_motd, Packet}); <<"announce/motd/update">> -> gen_server:cast(Proc, {announce_motd_update, Packet}); <<"announce/all-hosts/motd/update">> -> gen_server:cast(Proc, {announce_all_hosts_motd_update, Packet}); <<"announce/motd/delete">> -> gen_server:cast(Proc, {announce_motd_delete, Packet}); <<"announce/all-hosts/motd/delete">> -> gen_server:cast(Proc, {announce_all_hosts_motd_delete, Packet}); _ -> undefined end, case Res of ok -> stop; _ -> ok end; announce(_Packet) -> ok. %%------------------------------------------------------------------------- %% Announcing via ad-hoc commands -define(INFO_COMMAND(Lang, Node), [#identity{category = <<"automation">>, type = <<"command-node">>, name = get_title(Lang, Node)}]). disco_identity(Acc, _From, _To, Node, Lang) -> LNode = tokenize(Node), case LNode of ?NS_ADMINL("announce") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("announce-allhosts") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("announce-all") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("announce-all-allhosts") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("set-motd") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("set-motd-allhosts") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("edit-motd") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("edit-motd-allhosts") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("delete-motd") -> ?INFO_COMMAND(Lang, Node); ?NS_ADMINL("delete-motd-allhosts") -> ?INFO_COMMAND(Lang, Node); _ -> Acc end. %%------------------------------------------------------------------------- -define(INFO_RESULT(Allow, Feats, Lang), case Allow of deny -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; allow -> {result, Feats} end). disco_features(Acc, From, #jid{lserver = LServer} = _To, <<"announce">>, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Access1 = get_access(LServer), Access2 = get_access(global), case {acl:match_rule(LServer, Access1, From), acl:match_rule(global, Access2, From)} of {deny, deny} -> Txt = <<"Access denied by service policy">>, {error, xmpp:err_forbidden(Txt, Lang)}; _ -> {result, []} end end; disco_features(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Access = get_access(LServer), Allow = acl:match_rule(LServer, Access, From), AccessGlobal = get_access(global), AllowGlobal = acl:match_rule(global, AccessGlobal, From), case Node of ?NS_ADMIN_ANNOUNCE -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMIN_ANNOUNCE_ALL -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMIN_SET_MOTD -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMIN_EDIT_MOTD -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMIN_DELETE_MOTD -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMIN_ANNOUNCE_ALLHOSTS -> ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang); ?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS -> ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang); ?NS_ADMIN_SET_MOTD_ALLHOSTS -> ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang); ?NS_ADMIN_EDIT_MOTD_ALLHOSTS -> ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang); ?NS_ADMIN_DELETE_MOTD_ALLHOSTS -> ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang); _ -> Acc end end. %%------------------------------------------------------------------------- -define(NODE_TO_ITEM(Lang, Server, Node), #disco_item{jid = jid:make(Server), node = Node, name = get_title(Lang, Node)}). -define(ITEMS_RESULT(Allow, Items, Lang), case Allow of deny -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; allow -> {result, Items} end). disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, <<"">>, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Access1 = get_access(LServer), Access2 = get_access(global), case {acl:match_rule(LServer, Access1, From), acl:match_rule(global, Access2, From)} of {deny, deny} -> Acc; _ -> Items = case Acc of {result, I} -> I; _ -> [] end, Nodes = [?NODE_TO_ITEM(Lang, Server, <<"announce">>)], {result, Items ++ Nodes} end end; disco_items(Acc, From, #jid{lserver = LServer} = To, <<"announce">>, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> announce_items(Acc, From, To, Lang) end; disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Access = get_access(LServer), Allow = acl:match_rule(LServer, Access, From), AccessGlobal = get_access(global), AllowGlobal = acl:match_rule(global, AccessGlobal, From), case Node of ?NS_ADMIN_ANNOUNCE -> ?ITEMS_RESULT(Allow, [], Lang); ?NS_ADMIN_ANNOUNCE_ALL -> ?ITEMS_RESULT(Allow, [], Lang); ?NS_ADMIN_SET_MOTD -> ?ITEMS_RESULT(Allow, [], Lang); ?NS_ADMIN_EDIT_MOTD -> ?ITEMS_RESULT(Allow, [], Lang); ?NS_ADMIN_DELETE_MOTD -> ?ITEMS_RESULT(Allow, [], Lang); ?NS_ADMIN_ANNOUNCE_ALLHOSTS -> ?ITEMS_RESULT(AllowGlobal, [], Lang); ?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS -> ?ITEMS_RESULT(AllowGlobal, [], Lang); ?NS_ADMIN_SET_MOTD_ALLHOSTS -> ?ITEMS_RESULT(AllowGlobal, [], Lang); ?NS_ADMIN_EDIT_MOTD_ALLHOSTS -> ?ITEMS_RESULT(AllowGlobal, [], Lang); ?NS_ADMIN_DELETE_MOTD_ALLHOSTS -> ?ITEMS_RESULT(AllowGlobal, [], Lang); _ -> Acc end end. %%------------------------------------------------------------------------- -spec announce_items(empty | {error, stanza_error()} | {result, [disco_item()]}, jid(), jid(), binary()) -> {error, stanza_error()} | {result, [disco_item()]} | empty. announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang) -> Access1 = get_access(LServer), Nodes1 = case acl:match_rule(LServer, Access1, From) of allow -> [?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_ANNOUNCE), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_ANNOUNCE_ALL), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_SET_MOTD), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_EDIT_MOTD), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_DELETE_MOTD)]; deny -> [] end, Access2 = get_access(global), Nodes2 = case acl:match_rule(global, Access2, From) of allow -> [?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_ANNOUNCE_ALLHOSTS), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_SET_MOTD_ALLHOSTS), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_EDIT_MOTD_ALLHOSTS), ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN_DELETE_MOTD_ALLHOSTS)]; deny -> [] end, case {Nodes1, Nodes2} of {[], []} -> Acc; _ -> Items = case Acc of {result, I} -> I; _ -> [] end, {result, Items ++ Nodes1 ++ Nodes2} end. %%------------------------------------------------------------------------- commands_result(Allow, From, To, Request) -> case Allow of deny -> Lang = Request#adhoc_command.lang, {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; allow -> announce_commands(From, To, Request) end. -spec announce_commands(empty | adhoc_command(), jid(), jid(), adhoc_command()) -> adhoc_command() | {error, stanza_error()}. announce_commands(Acc, From, #jid{lserver = LServer} = To, #adhoc_command{node = Node} = Request) -> LNode = tokenize(Node), F = fun() -> Access = get_access(global), Allow = acl:match_rule(global, Access, From), commands_result(Allow, From, To, Request) end, R = case LNode of ?NS_ADMINL("announce-allhosts") -> F(); ?NS_ADMINL("announce-all-allhosts") -> F(); ?NS_ADMINL("set-motd-allhosts") -> F(); ?NS_ADMINL("edit-motd-allhosts") -> F(); ?NS_ADMINL("delete-motd-allhosts") -> F(); _ -> Access = get_access(LServer), Allow = acl:match_rule(LServer, Access, From), case LNode of ?NS_ADMINL("announce") -> commands_result(Allow, From, To, Request); ?NS_ADMINL("announce-all") -> commands_result(Allow, From, To, Request); ?NS_ADMINL("set-motd") -> commands_result(Allow, From, To, Request); ?NS_ADMINL("edit-motd") -> commands_result(Allow, From, To, Request); ?NS_ADMINL("delete-motd") -> commands_result(Allow, From, To, Request); _ -> unknown end end, case R of unknown -> Acc; _ -> {stop, R} end. %%------------------------------------------------------------------------- announce_commands(From, To, #adhoc_command{lang = Lang, node = Node, sid = SID, xdata = XData, action = Action} = Request) -> ActionIsExecute = Action == execute orelse Action == complete, if Action == cancel -> %% User cancels request #adhoc_command{status = canceled, lang = Lang, node = Node, sid = SID}; XData == undefined, ActionIsExecute -> %% User requests form Form = generate_adhoc_form(Lang, Node, To#jid.lserver), #adhoc_command{status = executing, lang = Lang, node = Node, sid = SID, xdata = Form}; XData /= undefined, ActionIsExecute -> handle_adhoc_form(From, To, Request); true -> Txt = <<"Unexpected action">>, {error, xmpp:err_bad_request(Txt, Lang)} end. -define(TVFIELD(Type, Var, Val), #xdata_field{type = Type, var = Var, values = vvaluel(Val)}). vvaluel(Val) -> case Val of <<>> -> []; _ -> [Val] end. generate_adhoc_form(Lang, Node, ServerHost) -> LNode = tokenize(Node), {OldSubject, OldBody} = if (LNode == ?NS_ADMINL("edit-motd")) or (LNode == ?NS_ADMINL("edit-motd-allhosts")) -> get_stored_motd(ServerHost); true -> {<<>>, <<>>} end, Fs = if (LNode == ?NS_ADMINL("delete-motd")) or (LNode == ?NS_ADMINL("delete-motd-allhosts")) -> [#xdata_field{type = boolean, var = <<"confirm">>, label = translate:translate( Lang, <<"Really delete message of the day?">>), values = [<<"true">>]}]; true -> [#xdata_field{type = 'text-single', var = <<"subject">>, label = translate:translate(Lang, <<"Subject">>), values = vvaluel(OldSubject)}, #xdata_field{type = 'text-multi', var = <<"body">>, label = translate:translate(Lang, <<"Message body">>), values = vvaluel(OldBody)}] end, #xdata{type = form, title = get_title(Lang, Node), fields = [#xdata_field{type = hidden, var = <<"FORM_TYPE">>, values = [?NS_ADMIN]}|Fs]}. join_lines([]) -> <<>>; join_lines(Lines) -> join_lines(Lines, []). join_lines([Line|Lines], Acc) -> join_lines(Lines, [<<"\n">>,Line|Acc]); join_lines([], Acc) -> %% Remove last newline iolist_to_binary(lists:reverse(tl(Acc))). handle_adhoc_form(From, #jid{lserver = LServer} = To, #adhoc_command{lang = Lang, node = Node, sid = SessionID, xdata = XData}) -> Confirm = case xmpp_util:get_xdata_values(<<"confirm">>, XData) of [<<"true">>] -> true; [<<"1">>] -> true; _ -> false end, Subject = join_lines(xmpp_util:get_xdata_values(<<"subject">>, XData)), Body = join_lines(xmpp_util:get_xdata_values(<<"body">>, XData)), Response = #adhoc_command{lang = Lang, node = Node, sid = SessionID, status = completed}, Packet = #message{from = From, to = To, type = headline, body = xmpp:mk_text(Body), subject = xmpp:mk_text(Subject)}, Proc = gen_mod:get_module_proc(LServer, ?MODULE), case {Node, Body} of {?NS_ADMIN_DELETE_MOTD, _} -> if Confirm -> gen_server:cast(Proc, {announce_motd_delete, Packet}), Response; true -> Response end; {?NS_ADMIN_DELETE_MOTD_ALLHOSTS, _} -> if Confirm -> gen_server:cast(Proc, {announce_all_hosts_motd_delete, Packet}), Response; true -> Response end; {_, <<>>} -> %% An announce message with no body is definitely an operator error. %% Throw an error and give him/her a chance to send message again. {error, xmpp:err_not_acceptable( <<"No body provided for announce message">>, Lang)}; %% Now send the packet to ?MODULE. %% We don't use direct announce_* functions because it %% leads to large delay in response and queries processing {?NS_ADMIN_ANNOUNCE, _} -> gen_server:cast(Proc, {announce_online, Packet}), Response; {?NS_ADMIN_ANNOUNCE_ALLHOSTS, _} -> gen_server:cast(Proc, {announce_all_hosts_online, Packet}), Response; {?NS_ADMIN_ANNOUNCE_ALL, _} -> gen_server:cast(Proc, {announce_all, Packet}), Response; {?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS, _} -> gen_server:cast(Proc, {announce_all_hosts_all, Packet}), Response; {?NS_ADMIN_SET_MOTD, _} -> gen_server:cast(Proc, {announce_motd, Packet}), Response; {?NS_ADMIN_SET_MOTD_ALLHOSTS, _} -> gen_server:cast(Proc, {announce_all_hosts_motd, Packet}), Response; {?NS_ADMIN_EDIT_MOTD, _} -> gen_server:cast(Proc, {announce_motd_update, Packet}), Response; {?NS_ADMIN_EDIT_MOTD_ALLHOSTS, _} -> gen_server:cast(Proc, {announce_all_hosts_motd_update, Packet}), Response; Junk -> %% This can't happen, as we haven't registered any other %% command nodes. ?ERROR_MSG("got unexpected node/body = ~p", [Junk]), {error, xmpp:err_internal_server_error()} end. get_title(Lang, <<"announce">>) -> translate:translate(Lang, <<"Announcements">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE_ALL) -> translate:translate(Lang, <<"Send announcement to all users">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS) -> translate:translate(Lang, <<"Send announcement to all users on all hosts">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE) -> translate:translate(Lang, <<"Send announcement to all online users">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE_ALLHOSTS) -> translate:translate(Lang, <<"Send announcement to all online users on all hosts">>); get_title(Lang, ?NS_ADMIN_SET_MOTD) -> translate:translate(Lang, <<"Set message of the day and send to online users">>); get_title(Lang, ?NS_ADMIN_SET_MOTD_ALLHOSTS) -> translate:translate(Lang, <<"Set message of the day on all hosts and send to online users">>); get_title(Lang, ?NS_ADMIN_EDIT_MOTD) -> translate:translate(Lang, <<"Update message of the day (don't send)">>); get_title(Lang, ?NS_ADMIN_EDIT_MOTD_ALLHOSTS) -> translate:translate(Lang, <<"Update message of the day on all hosts (don't send)">>); get_title(Lang, ?NS_ADMIN_DELETE_MOTD) -> translate:translate(Lang, <<"Delete message of the day">>); get_title(Lang, ?NS_ADMIN_DELETE_MOTD_ALLHOSTS) -> translate:translate(Lang, <<"Delete message of the day on all hosts">>). %%------------------------------------------------------------------------- announce_all(#message{to = To} = Packet) -> Local = jid:make(To#jid.server), lists:foreach( fun({User, Server}) -> Dest = jid:make(User, Server), ejabberd_router:route( xmpp:set_from_to(add_store_hint(Packet), Local, Dest)) end, ejabberd_auth:get_users(To#jid.lserver)). announce_all_hosts_all(#message{to = To} = Packet) -> Local = jid:make(To#jid.server), lists:foreach( fun({User, Server}) -> Dest = jid:make(User, Server), ejabberd_router:route( xmpp:set_from_to(add_store_hint(Packet), Local, Dest)) end, ejabberd_auth:get_users()). announce_online(#message{to = To} = Packet) -> announce_online1(ejabberd_sm:get_vh_session_list(To#jid.lserver), To#jid.server, Packet). announce_all_hosts_online(#message{to = To} = Packet) -> announce_online1(ejabberd_sm:dirty_get_sessions_list(), To#jid.server, Packet). announce_online1(Sessions, Server, Packet) -> Local = jid:make(Server), lists:foreach( fun({U, S, R}) -> Dest = jid:make(U, S, R), ejabberd_router:route(xmpp:set_from_to(Packet, Local, Dest)) end, Sessions). announce_motd(#message{to = To} = Packet) -> announce_motd(To#jid.lserver, Packet). announce_all_hosts_motd(Packet) -> Hosts = ?MYHOSTS, [announce_motd(Host, Packet) || Host <- Hosts]. announce_motd(Host, Packet) -> LServer = jid:nameprep(Host), announce_motd_update(LServer, Packet), Sessions = ejabberd_sm:get_vh_session_list(LServer), announce_online1(Sessions, LServer, Packet), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:set_motd_users(LServer, Sessions). announce_motd_update(#message{to = To} = Packet) -> announce_motd_update(To#jid.lserver, Packet). announce_all_hosts_motd_update(Packet) -> Hosts = ?MYHOSTS, [announce_motd_update(Host, Packet) || Host <- Hosts]. announce_motd_update(LServer, Packet) -> Mod = gen_mod:db_mod(LServer, ?MODULE), delete_motd(Mod, LServer), set_motd(Mod, LServer, xmpp:encode(Packet)). announce_motd_delete(#message{to = To}) -> LServer = To#jid.lserver, Mod = gen_mod:db_mod(LServer, ?MODULE), delete_motd(Mod, LServer). announce_all_hosts_motd_delete(_Packet) -> lists:foreach( fun(Host) -> Mod = gen_mod:db_mod(Host, ?MODULE), delete_motd(Mod, Host) end, ?MYHOSTS). -spec send_motd({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. send_motd({_, #{pres_last := _}} = Acc) -> %% This is just a presence update, nothing to do Acc; send_motd({#presence{type = available}, #{jid := #jid{luser = LUser, lserver = LServer} = JID}} = Acc) when LUser /= <<>> -> Mod = gen_mod:db_mod(LServer, ?MODULE), case get_motd(Mod, LServer) of {ok, Packet} -> try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of Msg -> case is_motd_user(Mod, LUser, LServer) of false -> Local = jid:make(LServer), ejabberd_router:route( xmpp:set_from_to(Msg, Local, JID)), set_motd_user(Mod, LUser, LServer); true -> ok end catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode motd packet ~p: ~s", [Packet, xmpp:format_error(Why)]) end; _ -> ok end, Acc; send_motd(Acc) -> Acc. -spec get_motd(module(), binary()) -> {ok, xmlel()} | error | {error, any()}. get_motd(Mod, LServer) -> case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?MOTD_CACHE, {<<"">>, LServer}, fun() -> Mod:get_motd(LServer) end); false -> Mod:get_motd(LServer) end. -spec set_motd(module(), binary(), xmlel()) -> any(). set_motd(Mod, LServer, XML) -> case use_cache(Mod, LServer) of true -> ets_cache:update( ?MOTD_CACHE, {<<"">>, LServer}, {ok, XML}, fun() -> Mod:set_motd(LServer, XML) end, cache_nodes(Mod, LServer)); false -> Mod:set_motd(LServer, XML) end. -spec is_motd_user(module(), binary(), binary()) -> boolean(). is_motd_user(Mod, LUser, LServer) -> Res = case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?MOTD_CACHE, {LUser, LServer}, fun() -> Mod:is_motd_user(LUser, LServer) end); false -> Mod:is_motd_user(LUser, LServer) end, case Res of {ok, Bool} -> Bool; _ -> false end. -spec set_motd_user(module(), binary(), binary()) -> any(). set_motd_user(Mod, LUser, LServer) -> case use_cache(Mod, LServer) of true -> ets_cache:update( ?MOTD_CACHE, {LUser, LServer}, {ok, true}, fun() -> Mod:set_motd_user(LUser, LServer) end, cache_nodes(Mod, LServer)); false -> Mod:set_motd_user(LUser, LServer) end. -spec delete_motd(module(), binary()) -> ok | {error, any()}. delete_motd(Mod, LServer) -> case Mod:delete_motd(LServer) of ok -> case use_cache(Mod, LServer) of true -> ejabberd_cluster:eval_everywhere( ?MODULE, clean_cache, [LServer]); false -> ok end; Err -> Err end. get_stored_motd(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case get_motd(Mod, LServer) of {ok, Packet} -> try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of #message{body = Body, subject = Subject} -> {xmpp:get_text(Subject), xmpp:get_text(Body)} catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode motd packet ~p: ~s", [Packet, xmpp:format_error(Why)]) end; _ -> {<<>>, <<>>} end. %% This function is similar to others, but doesn't perform any ACL verification send_announcement_to_all(Host, SubjectS, BodyS) -> Packet = #message{type = headline, body = xmpp:mk_text(BodyS), subject = xmpp:mk_text(SubjectS)}, Sessions = ejabberd_sm:dirty_get_sessions_list(), Local = jid:make(Host), lists:foreach( fun({U, S, R}) -> Dest = jid:make(U, S, R), ejabberd_router:route( xmpp:set_from_to(add_store_hint(Packet), Local, Dest)) end, Sessions). -spec get_access(global | binary()) -> atom(). get_access(Host) -> gen_mod:get_module_opt(Host, ?MODULE, access, none). -spec add_store_hint(stanza()) -> stanza(). add_store_hint(El) -> xmpp:set_subtag(El, #hint{type = store}). -spec route_forbidden_error(stanza()) -> ok. route_forbidden_error(Packet) -> Lang = xmpp:get_lang(Packet), Err = xmpp:err_forbidden(<<"Access denied by service policy">>, Lang), ejabberd_router:route_error(Packet, Err). -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> case use_cache(Mod, Host) of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?MOTD_CACHE, CacheOpts); false -> ets_cache:delete(?MOTD_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(Host); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. -spec clean_cache(binary()) -> non_neg_integer(). clean_cache(LServer) -> ets_cache:filter( ?MOTD_CACHE, fun({_, S}, _) -> S /= LServer end). %%------------------------------------------------------------------------- export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import_info() -> [{<<"motd">>, 3}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). import(LServer, {sql, _}, DBType, Tab, List) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, Tab, List). mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [access, db_type, cache_life_time, cache_size, use_cache, cache_missed]. ejabberd-18.01/src/mod_private_riak.erl0000644000232200023220000000574513225664356020470 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_private_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_private_riak). -behaviour(mod_private). %% API -export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2, import/3]). -include("xmpp.hrl"). -include("mod_private.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. set_data(LUser, LServer, Data) -> lists:foldl( fun(_, {error, _} = Err) -> Err; ({XMLNS, El}, _) -> ejabberd_riak:put(#private_storage{usns = {LUser, LServer, XMLNS}, xml = El}, private_storage_schema(), [{'2i', [{<<"us">>, {LUser, LServer}}]}]) end, ok, Data). get_data(LUser, LServer, XMLNS) -> case ejabberd_riak:get(private_storage, private_storage_schema(), {LUser, LServer, XMLNS}) of {ok, #private_storage{xml = El}} -> {ok, El}; {error, notfound} -> error; Err -> Err end. get_all_data(LUser, LServer) -> case ejabberd_riak:get_by_index( private_storage, private_storage_schema(), <<"us">>, {LUser, LServer}) of {ok, []} -> error; {ok, Res} -> {ok, [El || #private_storage{xml = El} <- Res]}; Err -> Err end. del_data(LUser, LServer) -> ejabberd_riak:delete_by_index(private_storage, <<"us">>, {LUser, LServer}). import(LServer, <<"private_storage">>, [LUser, XMLNS, XML, _TimeStamp]) -> El = #xmlel{} = fxml_stream:parse_element(XML), PS = #private_storage{usns = {LUser, LServer, XMLNS}, xml = El}, ejabberd_riak:put(PS, private_storage_schema(), [{'2i', [{<<"us">>, {LUser, LServer}}]}]). %%%=================================================================== %%% Internal functions %%%=================================================================== private_storage_schema() -> {record_info(fields, private_storage), #private_storage{}}. ejabberd-18.01/src/xmpp_socket.erl0000644000232200023220000002766613225664356017513 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : xmpp_socket.erl %%% Author : Alexey Shchepin %%% Purpose : Socket with zlib and TLS support library %%% Created : 23 Aug 2006 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(xmpp_socket). -author('alexey@process-one.net'). %% API -export([start/4, connect/3, connect/4, connect/5, starttls/2, compress/1, compress/2, reset_stream/1, send_element/2, send_header/2, send_trailer/1, send/2, send_xml/2, recv/2, activate/1, change_shaper/2, monitor/1, get_sockmod/1, get_transport/1, get_peer_certificate/2, get_verify_result/1, close/1, pp/1, sockname/1, peername/1]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -type sockmod() :: ejabberd_bosh | ejabberd_http_ws | gen_tcp | fast_tls | ezlib. -type receiver() :: atom(). -type socket() :: pid() | inet:socket() | fast_tls:tls_socket() | ezlib:zlib_socket() | ejabberd_bosh:bosh_socket() | ejabberd_http_ws:ws_socket(). -record(socket_state, {sockmod = gen_tcp :: sockmod(), socket :: socket(), max_stanza_size = infinity :: timeout(), xml_stream :: undefined | fxml_stream:xml_stream_state(), shaper = none :: none | shaper:shaper(), receiver :: receiver()}). -type socket_state() :: #socket_state{}. -export_type([socket/0, socket_state/0, sockmod/0]). -callback start({module(), socket_state()}, [proplists:property()]) -> {ok, pid()} | {error, term()} | ignore. -callback start_link({module(), socket_state()}, [proplists:property()]) -> {ok, pid()} | {error, term()} | ignore. -callback socket_type() -> xml_stream | independent | raw. -define(is_http_socket(S), (S#socket_state.sockmod == ejabberd_bosh orelse S#socket_state.sockmod == ejabberd_http_ws)). %%==================================================================== %% API %%==================================================================== -spec start(atom(), sockmod(), socket(), [proplists:property()]) -> {ok, pid() | independent} | {error, inet:posix() | any()} | ignore. start(Module, SockMod, Socket, Opts) -> try case Module:socket_type() of independent -> {ok, independent}; xml_stream -> MaxStanzaSize = proplists:get_value(max_stanza_size, Opts, infinity), Receiver = proplists:get_value(receiver, Opts), SocketData = #socket_state{sockmod = SockMod, socket = Socket, receiver = Receiver, max_stanza_size = MaxStanzaSize}, {ok, Pid} = Module:start({?MODULE, SocketData}, Opts), Receiver1 = if is_pid(Receiver) -> Receiver; true -> Pid end, ok = controlling_process(SocketData, Receiver1), ok = become_controller(SocketData, Pid), {ok, Receiver1}; raw -> {ok, Pid} = Module:start({SockMod, Socket}, Opts), ok = SockMod:controlling_process(Socket, Pid), {ok, Pid} end catch _:{badmatch, {error, _} = Err} -> SockMod:close(Socket), Err end. connect(Addr, Port, Opts) -> connect(Addr, Port, Opts, infinity, self()). connect(Addr, Port, Opts, Timeout) -> connect(Addr, Port, Opts, Timeout, self()). connect(Addr, Port, Opts, Timeout, Owner) -> case gen_tcp:connect(Addr, Port, Opts, Timeout) of {ok, Socket} -> SocketData = #socket_state{sockmod = gen_tcp, socket = Socket}, case controlling_process(SocketData, Owner) of ok -> activate_after(Socket, Owner, 0), {ok, SocketData}; {error, _Reason} = Error -> gen_tcp:close(Socket), Error end; {error, _Reason} = Error -> Error end. starttls(#socket_state{socket = Socket, receiver = undefined} = SocketData, TLSOpts) -> case fast_tls:tcp_to_tls(Socket, TLSOpts) of {ok, TLSSocket} -> SocketData1 = SocketData#socket_state{socket = TLSSocket, sockmod = fast_tls}, SocketData2 = reset_stream(SocketData1), case fast_tls:recv_data(TLSSocket, <<>>) of {ok, TLSData} -> parse(SocketData2, TLSData); {error, _} = Err -> Err end; {error, _} = Err -> Err end. compress(SocketData) -> compress(SocketData, undefined). compress(#socket_state{receiver = undefined, sockmod = SockMod, socket = Socket} = SocketData, Data) -> ejabberd:start_app(ezlib), {ok, ZlibSocket} = ezlib:enable_zlib(SockMod, Socket), case Data of undefined -> ok; _ -> send(SocketData, Data) end, SocketData1 = SocketData#socket_state{socket = ZlibSocket, sockmod = ezlib}, SocketData2 = reset_stream(SocketData1), case ezlib:recv_data(ZlibSocket, <<"">>) of {ok, ZlibData} -> parse(SocketData2, ZlibData); {error, _} = Err -> Err end. reset_stream(#socket_state{xml_stream = XMLStream, receiver = undefined, max_stanza_size = MaxStanzaSize} = SocketData) -> XMLStream1 = try fxml_stream:reset(XMLStream) catch error:_ -> close_stream(XMLStream), fxml_stream:new(self(), MaxStanzaSize) end, SocketData#socket_state{xml_stream = XMLStream1}; reset_stream(#socket_state{sockmod = SockMod, socket = Socket} = SocketData) -> Socket1 = SockMod:reset_stream(Socket), SocketData#socket_state{socket = Socket1}. -spec send_element(socket_state(), fxml:xmlel()) -> ok | {error, inet:posix()}. send_element(SocketData, El) when ?is_http_socket(SocketData) -> send_xml(SocketData, {xmlstreamelement, El}); send_element(SocketData, El) -> send(SocketData, fxml:element_to_binary(El)). -spec send_header(socket_state(), fxml:xmlel()) -> ok | {error, inet:posix()}. send_header(SocketData, El) when ?is_http_socket(SocketData) -> send_xml(SocketData, {xmlstreamstart, El#xmlel.name, El#xmlel.attrs}); send_header(SocketData, El) -> send(SocketData, fxml:element_to_header(El)). -spec send_trailer(socket_state()) -> ok | {error, inet:posix()}. send_trailer(SocketData) when ?is_http_socket(SocketData) -> send_xml(SocketData, {xmlstreamend, <<"stream:stream">>}); send_trailer(SocketData) -> send(SocketData, <<"">>). -spec send(socket_state(), iodata()) -> ok | {error, closed | inet:posix()}. send(#socket_state{sockmod = SockMod, socket = Socket} = SocketData, Data) -> ?DEBUG("(~s) Send XML on stream = ~p", [pp(SocketData), Data]), try SockMod:send(Socket, Data) of {error, einval} -> {error, closed}; Result -> Result catch _:badarg -> %% Some modules throw badarg exceptions on closed sockets %% TODO: their code should be improved {error, closed} end. -spec send_xml(socket_state(), {xmlstreamelement, fxml:xmlel()} | {xmlstreamstart, binary(), [{binary(), binary()}]} | {xmlstreamend, binary()} | {xmlstreamraw, iodata()}) -> term(). send_xml(SocketData, El) -> (SocketData#socket_state.sockmod):send_xml(SocketData#socket_state.socket, El). recv(#socket_state{xml_stream = undefined} = SocketData, Data) -> XMLStream = fxml_stream:new(self(), SocketData#socket_state.max_stanza_size), recv(SocketData#socket_state{xml_stream = XMLStream}, Data); recv(#socket_state{sockmod = SockMod, socket = Socket} = SocketData, Data) -> case SockMod of fast_tls -> case fast_tls:recv_data(Socket, Data) of {ok, TLSData} -> parse(SocketData, TLSData); {error, _} = Err -> Err end; ezlib -> case ezlib:recv_data(Socket, Data) of {ok, ZlibData} -> parse(SocketData, ZlibData); {error, _} = Err -> Err end; _ -> parse(SocketData, Data) end. change_shaper(#socket_state{receiver = undefined} = SocketData, Shaper) -> ShaperState = shaper:new(Shaper), SocketData#socket_state{shaper = ShaperState}; change_shaper(#socket_state{sockmod = SockMod, socket = Socket} = SocketData, Shaper) -> SockMod:change_shaper(Socket, Shaper), SocketData. monitor(#socket_state{receiver = undefined}) -> make_ref(); monitor(#socket_state{sockmod = SockMod, socket = Socket}) -> SockMod:monitor(Socket). controlling_process(#socket_state{sockmod = SockMod, socket = Socket}, Pid) -> SockMod:controlling_process(Socket, Pid). become_controller(#socket_state{receiver = Receiver, sockmod = SockMod, socket = Socket}, Pid) -> if is_pid(Receiver) -> SockMod:become_controller(Receiver, Pid); true -> activate_after(Socket, Pid, 0) end. get_sockmod(SocketData) -> SocketData#socket_state.sockmod. get_transport(#socket_state{sockmod = SockMod, socket = Socket}) -> case SockMod of gen_tcp -> tcp; fast_tls -> tls; ezlib -> case ezlib:get_sockmod(Socket) of gen_tcp -> tcp_zlib; fast_tls -> tls_zlib end; ejabberd_bosh -> http_bind; ejabberd_http_ws -> websocket end. get_peer_certificate(SocketData, Type) -> fast_tls:get_peer_certificate(SocketData#socket_state.socket, Type). get_verify_result(SocketData) -> fast_tls:get_verify_result(SocketData#socket_state.socket). close(#socket_state{sockmod = SockMod, socket = Socket}) -> SockMod:close(Socket). sockname(#socket_state{sockmod = SockMod, socket = Socket}) -> case SockMod of gen_tcp -> inet:sockname(Socket); _ -> SockMod:sockname(Socket) end. peername(#socket_state{sockmod = SockMod, socket = Socket}) -> case SockMod of gen_tcp -> inet:peername(Socket); _ -> SockMod:peername(Socket) end. activate(#socket_state{sockmod = SockMod, socket = Socket}) -> case SockMod of gen_tcp -> inet:setopts(Socket, [{active, once}]); _ -> SockMod:setopts(Socket, [{active, once}]) end. activate_after(Socket, Pid, Pause) -> if Pause > 0 -> erlang:send_after(Pause, Pid, {tcp, Socket, <<>>}); true -> Pid ! {tcp, Socket, <<>>} end, ok. pp(#socket_state{receiver = Receiver} = State) -> Transport = get_transport(State), Receiver1 = case Receiver of undefined -> self(); _ -> Receiver end, io_lib:format("~s|~w", [Transport, Receiver1]). parse(SocketData, Data) when Data == <<>>; Data == [] -> case activate(SocketData) of ok -> {ok, SocketData}; {error, _} = Err -> Err end; parse(SocketData, [El | Els]) when is_record(El, xmlel) -> self() ! {'$gen_event', {xmlstreamelement, El}}, parse(SocketData, Els); parse(SocketData, [El | Els]) when element(1, El) == xmlstreamstart; element(1, El) == xmlstreamelement; element(1, El) == xmlstreamend; element(1, El) == xmlstreamerror -> self() ! {'$gen_event', El}, parse(SocketData, Els); parse(#socket_state{xml_stream = XMLStream, socket = Socket, shaper = ShaperState} = SocketData, Data) when is_binary(Data) -> XMLStream1 = fxml_stream:parse(XMLStream, Data), {ShaperState1, Pause} = shaper:update(ShaperState, byte_size(Data)), Ret = if Pause > 0 -> activate_after(Socket, self(), Pause); true -> activate(SocketData) end, case Ret of ok -> {ok, SocketData#socket_state{xml_stream = XMLStream1, shaper = ShaperState1}}; {error, _} = Err -> Err end. close_stream(undefined) -> ok; close_stream(XMLStream) -> fxml_stream:close(XMLStream). ejabberd-18.01/src/ejabberd_xmlrpc.erl0000644000232200023220000005412013225664356020263 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_xmlrpc.erl %%% Author : Badlop %%% Purpose : XML-RPC server that frontends ejabberd commands %%% Created : 21 Aug 2007 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% TODO: Implement a command in ejabberdctl 'help COMMAND LANGUAGE' that shows %%% a coding example to call that command in a specific language (python, php). %%% TODO: Remove support for plaintext password %%% TODO: commands strings should be strings without ~n -module(ejabberd_xmlrpc). -author('badlop@process-one.net'). -export([start/2, handler/2, process/2, socket_type/0, transform_listen_option/2, listen_opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). -include("mod_roster.hrl"). -include("xmpp.hrl"). -record(state, {access_commands = [] :: list(), auth = noauth :: noauth | map(), get_auth = true :: boolean(), ip :: inet:ip_address()}). %% Test: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_integer, [{struct, [{thisinteger, 5}]}]}). %% {ok,{response,[{struct,[{zero,0}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_string, [{struct, [{thisstring, "abcd"}]}]}). %% {ok,{response,[{struct,[{thatstring,"abcd"}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, tell_tuple_3integer, [{struct, [{thisstring, "abcd"}]}]}). %% {ok,{response, %% [{struct, %% [{thattuple, %% {array, %% [{struct,[{first,123}]}, %% {struct,[{second,456}]}, %% {struct,[{third,789}]}]}}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, pow, [{struct, [{base, 5}, {exponent, 7}]}]}). %% {ok,{response,[{struct,[{pow,78125}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, seq, [{struct, [{from, 3}, {to, 7}]}]}). %% {ok,{response,[{array,[{struct,[{intermediate,3}]}, %% {struct,[{intermediate,4}]}, %% {struct,[{intermediate,5}]}, %% {struct,[{intermediate,6}]}, %% {struct,[{intermediate,7}]}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, substrs, [{struct, [{word, "abcd"}]}]}). %% NO: %% {ok,{response,[{array,[{struct,[{miniword,"a"}]}, %% {struct,[{miniword,"ab"}]}, %% {struct,[{miniword,"abc"}]}, %% {struct,[{miniword,"abcd"}]}]}]}} %% {ok,{response, %% [{struct, %% [{substrings, %% {array, %% [{struct,[{miniword,"a"}]}, %% {struct,[{miniword,"ab"}]}, %% {struct,[{miniword,"abc"}]}, %% {struct,[{miniword,"abcd"}]}]}}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, splitjid, [{struct, [{jid, "abcd@localhost/work"}]}]}). %% {ok,{response, %% [{struct, %% [{jidparts, %% {array, %% [{struct,[{user,"abcd"}]}, %% {struct,[{server,"localhost"}]}, %% {struct,[{resource,"work"}]}]}}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 55}]}]}). %% {ok,{response, %% [{struct, %% [{thistuple, %% {array, %% [{struct,[{thisinteger,55}]}, %% {struct,[{thisstring,"abc"}]}]}}]}]}} %% %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_list_integer, [{struct, [{thislist, {array, [{struct, [{thisinteger, 55}, {thisinteger, 4567}]}]}}]}]}). %% {ok,{response, %% [{struct, %% [{thatlist, %% {array, %% [{struct,[{thatinteger,55}]}, %% {struct,[{thatinteger,4567}]}]}}]}]}} %% %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_list_string, [{struct, [{thisinteger, 123456}, {thislist, {array, [{struct, [{thisstring, "abc"}, {thisstring, "bobo baba"}]}]}}]}]}). %% {ok, %% {response, %% [{struct, %% [{thistuple, %% {array, %% [{struct,[{thatinteger,123456}]}, %% {struct, %% [{thatlist, %% {array, %% [{struct,[{thatstring,"abc"}]}, %% {struct,[{thatstring,"bobo baba"}]}]}}]}]}}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger2, 4567}]}]}}]}]}). %% {ok,{response,[{struct,[{zero,0}]}]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_isatils, [{struct, %% [{thisinteger, 123456990}, %% {thisstring, "This is ISATILS"}, %% {thisatom, "test_isatils"}, %% {thistuple, {array, [{struct, [ %% {listlen, 2}, %% {thislist, {array, [{struct, [ %% {contentstring, "word1"}, %% {contentstring, "word 2"} %% ]}]}} %% ]}]}} %% ]}]}). %% {ok,{response, %% [{struct, %% [{results, %% {array, %% [{struct,[{thatinteger,123456990}]}, %% {struct,[{thatstring,"This is ISATILS"}]}, %% {struct,[{thatatom,"test_isatils"}]}, %% {struct, %% [{thattuple, %% {array, %% [{struct,[{listlen,123456990}]}, %% {struct,[{thatlist,...}]}]}}]}]}}]}]}} %% ecommand doesn't exist: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string2, [{struct, [{thisstring, "abc"}]}]}). %% {ok,{response,{fault,-1, "Unknown call: {call,echo_integer_string2,[{struct,[{thisstring,\"abc\"}]}]}"}}} %% %% Duplicated argument: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 44}, {thisinteger, 55}]}]}). %% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger' duplicated:\n[{thisstring,\"abc\"},{thisinteger,44},{thisinteger,55}]"}}} %% %% Missing argument: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}]}]}). %% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger' not found:\n[{thisstring,\"abc\"}]"}}} %% %% Duplicated tuple element: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger1, 66}, {thisinteger2, 4567}]}]}}]}]}). %% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger1' defined multiple times:\n[{thisinteger1,55},{thisinteger1,66},{thisinteger2,4567}]"}}} %% %% Missing element in tuple: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisintegerc, 66}, {thisinteger, 4567}]}]}}]}]}). %% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger2' not found:\n[{thisintegerc,66},{thisinteger,4567}]"}}} %% %% The ecommand crashed: %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, this_crashes, [{struct, []}]}). %% {ok,{response,{fault,-100, "Error -100\nA problem 'error' occurred executing the command this_crashes with arguments []: badarith"}}} %% ----------------------------- %% Listener interface %% ----------------------------- start({gen_tcp = _SockMod, Socket}, Opts) -> ejabberd_http:start({gen_tcp, Socket}, [{xmlrpc, true}|Opts]). socket_type() -> raw. %% ----------------------------- %% HTTP interface %% ----------------------------- process(_, #request{method = 'POST', data = Data, opts = Opts, ip = {IP, _}}) -> AccessCommands = proplists:get_value(access_commands, Opts), GetAuth = true, State = #state{access_commands = AccessCommands, get_auth = GetAuth, ip = IP}, case fxml_stream:parse_element(Data) of {error, _} -> {400, [], #xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"Malformed XML">>}]}}; El -> case fxmlrpc:decode(El) of {error, _} = Err -> ?ERROR_MSG("XML-RPC request ~s failed with reason: ~p", [Data, Err]), {400, [], #xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"Malformed Request">>}]}}; {ok, RPC} -> ?DEBUG("got XML-RPC request: ~p", [RPC]), {false, Result} = handler(State, RPC), XML = fxml:element_to_binary(fxmlrpc:encode(Result)), {200, [{<<"Content-Type">>, <<"text/xml">>}], <<"", XML/binary>>} end end; process(_, _) -> {400, [], #xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"400 Bad Request">>}]}}. %% ----------------------------- %% Access verification %% ----------------------------- extract_auth(AuthList) -> ?DEBUG("AUTHLIST ~p", [AuthList]), try get_attrs([user, server, token], AuthList) of [U0, S0, T] -> U = jid:nodeprep(U0), S = jid:nameprep(S0), case ejabberd_oauth:check_token(T) of {ok, {U, S}, Scope} -> #{usr => {U, S, <<"">>}, oauth_scope => Scope, caller_server => S}; {false, Reason} -> {error, Reason}; _ -> {error, not_found} end catch exit:{attribute_not_found, _, _} -> try get_attrs([user, server, password], AuthList) of [U0, S0, P] -> U = jid:nodeprep(U0), S = jid:nameprep(S0), case ejabberd_auth:check_password(U, <<"">>, S, P) of true -> #{usr => {U, S, <<"">>}, caller_server => S}; false -> {error, invalid_auth} end catch exit:{attribute_not_found, Attr, _} -> throw({error, missing_auth_arguments, Attr}) end end. %% ----------------------------- %% Handlers %% ----------------------------- %% Call: Arguments: Returns: %% ............................. %% Access verification %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [152]}). %% {ok,{response,{fault,-103, "Error -103\nRequired authentication: {call,echothis,[152]}"}}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada"}]}, 152]}). %% {ok,{response,{fault,-103, %% "Error -103\nAuthentication non valid: [{user,\"badlop\"},\n %% {server,\"localhost\"},\n %% {password,\"ada\"}]"}}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada90ada"}]}, 152]}). %% {ok,{response,[152]}} %% %% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "79C1574A43BC995F2B145A299EF97277"}]}, 152]}). %% {ok,{response,[152]}} handler(#state{get_auth = true, auth = noauth, ip = IP} = State, {call, Method, [{struct, AuthList} | Arguments] = AllArgs}) -> try extract_auth(AuthList) of {error, invalid_auth} -> build_fault_response(-118, "Invalid authentication data", []); {error, not_found} -> build_fault_response(-118, "Invalid oauth token", []); {error, expired} -> build_fault_response(-118, "Invalid oauth token", []); {error, Value} -> build_fault_response(-118, "Invalid authentication data: ~p", [Value]); Auth -> handler(State#state{get_auth = false, auth = Auth#{ip => IP, caller_module => ?MODULE}}, {call, Method, Arguments}) catch {error, missing_auth_arguments, _Attr} -> handler(State#state{get_auth = false, auth = #{ip => IP, caller_module => ?MODULE}}, {call, Method, AllArgs}) end; %% ............................. %% Debug %% echothis String String handler(_State, {call, echothis, [A]}) -> {false, {response, [A]}}; %% echothisnew struct[{sentence, String}] struct[{repeated, String}] handler(_State, {call, echothisnew, [{struct, [{sentence, A}]}]}) -> {false, {response, [{struct, [{repeated, A}]}]}}; %% multhis struct[{a, Integer}, {b, Integer}] Integer handler(_State, {call, multhis, [{struct, [{a, A}, {b, B}]}]}) -> {false, {response, [A * B]}}; %% multhisnew struct[{a, Integer}, {b, Integer}] struct[{mu, Integer}] handler(_State, {call, multhisnew, [{struct, [{a, A}, {b, B}]}]}) -> {false, {response, [{struct, [{mu, A * B}]}]}}; %% ............................. %% ejabberd commands handler(State, {call, Command, []}) -> handler(State, {call, Command, [{struct, []}]}); handler(State, {call, Command, [{struct, AttrL}]} = Payload) -> case ejabberd_commands:get_command_format(Command, State#state.auth) of {error, command_unknown} -> build_fault_response(-112, "Unknown call: ~p", [Payload]); {ArgsF, ResultF} -> try_do_command(State#state.access_commands, State#state.auth, Command, AttrL, ArgsF, ResultF) end; %% If no other guard matches handler(_State, Payload) -> build_fault_response(-112, "Unknown call: ~p", [Payload]). %% ----------------------------- %% Command %% ----------------------------- try_do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) -> try do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) of {command_result, ResultFormatted} -> {false, {response, [ResultFormatted]}} catch exit:{duplicated_attribute, ExitAt, ExitAtL} -> build_fault_response(-114, "Attribute '~p' duplicated:~n~p", [ExitAt, ExitAtL]); exit:{attribute_not_found, ExitAt, ExitAtL} -> build_fault_response(-116, "Required attribute '~p' not found:~n~p", [ExitAt, ExitAtL]); exit:{additional_unused_args, ExitAtL} -> build_fault_response(-120, "The call provided additional unused " "arguments:~n~p", [ExitAtL]); exit:{invalid_arg_type, Arg, Type} -> build_fault_response(-122, "Parameter '~p' can't be coerced to type '~p'", [Arg, Type]); Why -> build_fault_response(-118, "A problem '~p' occurred executing the " "command ~p with arguments~n~p", [Why, Command, AttrL]) end. build_fault_response(Code, ParseString, ParseArgs) -> FaultString = "Error " ++ integer_to_list(Code) ++ "\n" ++ lists:flatten(io_lib:format(ParseString, ParseArgs)), ?WARNING_MSG(FaultString, []), {false, {response, {fault, Code, list_to_binary(FaultString)}}}. do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) -> ArgsFormatted = format_args(AttrL, ArgsF), Auth2 = case AccessCommands of V when is_list(V) -> Auth#{extra_permissions => AccessCommands}; _ -> Auth end, Result = ejabberd_commands:execute_command2(Command, ArgsFormatted, Auth2), ResultFormatted = format_result(Result, ResultF), {command_result, ResultFormatted}. %%----------------------------- %% Format arguments %%----------------------------- get_attrs(Attribute_names, L) -> [get_attr(A, L) || A <- Attribute_names]. get_attr(A, L) -> case lists:keysearch(A, 1, L) of {value, {A, Value}} -> Value; false -> %% Report the error and then force a crash exit({attribute_not_found, A, L}) end. get_elem_delete(A, L) -> case proplists:get_all_values(A, L) of [Value] -> {Value, proplists:delete(A, L)}; [_, _ | _] -> %% Crash reporting the error exit({duplicated_attribute, A, L}); [] -> %% Report the error and then force a crash exit({attribute_not_found, A, L}) end. format_args(Args, ArgsFormat) -> {ArgsRemaining, R} = lists:foldl(fun ({ArgName, ArgFormat}, {Args1, Res}) -> {ArgValue, Args2} = get_elem_delete(ArgName, Args1), Formatted = format_arg(ArgValue, ArgFormat), {Args2, Res ++ [Formatted]} end, {Args, []}, ArgsFormat), case ArgsRemaining of [] -> R; L when is_list(L) -> exit({additional_unused_args, L}) end. format_arg({array, Elements}, {list, {ElementDefName, ElementDefFormat}}) when is_list(Elements) -> lists:map(fun ({struct, [{ElementName, ElementValue}]}) when ElementDefName == ElementName -> format_arg(ElementValue, ElementDefFormat) end, Elements); format_arg({array, [{struct, Elements}]}, {list, {ElementDefName, ElementDefFormat}}) when is_list(Elements) -> lists:map(fun ({ElementName, ElementValue}) -> true = ElementDefName == ElementName, format_arg(ElementValue, ElementDefFormat) end, Elements); format_arg({array, [{struct, Elements}]}, {tuple, ElementsDef}) when is_list(Elements) -> FormattedList = format_args(Elements, ElementsDef), list_to_tuple(FormattedList); format_arg({array, Elements}, {list, ElementsDef}) when is_list(Elements) and is_atom(ElementsDef) -> [format_arg(Element, ElementsDef) || Element <- Elements]; format_arg(Arg, integer) when is_integer(Arg) -> Arg; format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg); format_arg(Arg, binary) when is_binary(Arg) -> Arg; format_arg(Arg, string) when is_list(Arg) -> Arg; format_arg(Arg, string) when is_binary(Arg) -> binary_to_list(Arg); format_arg(undefined, binary) -> <<>>; format_arg(undefined, string) -> ""; format_arg(Arg, Format) -> ?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]), exit({invalid_arg_type, Arg, Format}). process_unicode_codepoints(Str) -> iolist_to_binary(lists:map(fun(X) when X > 255 -> unicode:characters_to_binary([X]); (Y) -> Y end, Str)). %% ----------------------------- %% Result %% ----------------------------- format_result({error, Error}, _) -> throw({error, Error}); format_result({error, _Type, _Code, Error}, _) -> throw({error, Error}); format_result(String, string) -> lists:flatten(String); format_result(Atom, {Name, atom}) -> {struct, [{Name, iolist_to_binary(atom_to_list(Atom))}]}; format_result(Int, {Name, integer}) -> {struct, [{Name, Int}]}; format_result([A|_]=String, {Name, string}) when is_list(String) and is_integer(A) -> {struct, [{Name, lists:flatten(String)}]}; format_result(Binary, {Name, string}) when is_binary(Binary) -> {struct, [{Name, binary_to_list(Binary)}]}; format_result(Atom, {Name, string}) when is_atom(Atom) -> {struct, [{Name, atom_to_list(Atom)}]}; format_result(Integer, {Name, string}) when is_integer(Integer) -> {struct, [{Name, integer_to_list(Integer)}]}; format_result(Other, {Name, string}) -> {struct, [{Name, io_lib:format("~p", [Other])}]}; format_result(String, {Name, binary}) when is_list(String) -> {struct, [{Name, lists:flatten(String)}]}; format_result(Binary, {Name, binary}) when is_binary(Binary) -> {struct, [{Name, binary_to_list(Binary)}]}; format_result(Code, {Name, rescode}) -> {struct, [{Name, make_status(Code)}]}; format_result({Code, Text}, {Name, restuple}) -> {struct, [{Name, make_status(Code)}, {text, lists:flatten(Text)}]}; %% Result is a list of something: [something()] format_result(Elements, {Name, {list, ElementsDef}}) -> FormattedList = lists:map(fun (Element) -> format_result(Element, ElementsDef) end, Elements), {struct, [{Name, {array, FormattedList}}]}; %% Result is a tuple with several elements: {something1(), something2(), ...} format_result(ElementsTuple, {Name, {tuple, ElementsDef}}) -> ElementsList = tuple_to_list(ElementsTuple), ElementsAndDef = lists:zip(ElementsList, ElementsDef), FormattedList = lists:map(fun ({Element, ElementDef}) -> format_result(Element, ElementDef) end, ElementsAndDef), {struct, [{Name, {array, FormattedList}}]}; format_result(404, {Name, _}) -> {struct, [{Name, make_status(not_found)}]}. make_status(ok) -> 0; make_status(true) -> 0; make_status(false) -> 1; make_status(error) -> 1; make_status(_) -> 1. transform_listen_option({access_commands, ACOpts}, Opts) -> NewACOpts = lists:map( fun({AName, ACmds, AOpts}) -> {AName, [{commands, ACmds}, {options, AOpts}]}; (Opt) -> Opt end, ACOpts), [{access_commands, NewACOpts}|Opts]; transform_listen_option(Opt, Opts) -> [Opt|Opts]. listen_opt_type(access_commands) -> fun(Opts) -> lists:map( fun({Ac, AcOpts}) -> Commands = case proplists:get_value( commands, lists:flatten(AcOpts), all) of Cmd when is_atom(Cmd) -> Cmd; Cmds when is_list(Cmds) -> true = lists:all(fun is_atom/1, Cmds), Cmds end, {<<"ejabberd_xmlrpc compatibility shim">>, {[?MODULE], [{access, Ac}], Commands}} end, lists:flatten(Opts)) end; listen_opt_type(maxsessions) -> fun(I) when is_integer(I), I>0 -> I end; listen_opt_type(timeout) -> fun(I) when is_integer(I), I>0 -> I end; listen_opt_type(_) -> [access_commands, maxsessions, timeout]. ejabberd-18.01/src/ejabberd_sm_mnesia.erl0000644000232200023220000001154013225664356020730 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_sm_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 9 Mar 2015 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sm_mnesia). -behaviour(gen_server). -behaviour(ejabberd_sm). %% API -export([init/0, use_cache/1, set_session/1, delete_session/1, get_sessions/0, get_sessions/1, get_sessions/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("ejabberd.hrl"). -include("ejabberd_sm.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== -spec init() -> ok | {error, any()}. init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec use_cache(binary()) -> boolean(). use_cache(_LServer) -> false. -spec set_session(#session{}) -> ok. set_session(Session) -> mnesia:dirty_write(Session). -spec delete_session(#session{}) -> ok. delete_session(#session{sid = SID}) -> mnesia:dirty_delete(session, SID). -spec get_sessions() -> [#session{}]. get_sessions() -> ets:tab2list(session). -spec get_sessions(binary()) -> [#session{}]. get_sessions(LServer) -> mnesia:dirty_select(session, [{#session{usr = '$1', _ = '_'}, [{'==', {element, 2, '$1'}, LServer}], ['$_']}]). -spec get_sessions(binary(), binary()) -> {ok, [#session{}]}. get_sessions(LUser, LServer) -> {ok, mnesia:dirty_index_read(session, {LUser, LServer}, #session.us)}. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> update_tables(), ejabberd_mnesia:create(?MODULE, session, [{ram_copies, [node()]}, {attributes, record_info(fields, session)}, {index, [usr,us]}]), ejabberd_mnesia:create(?MODULE, session_counter, [{ram_copies, [node()]}, {attributes, record_info(fields, session_counter)}]), mnesia:subscribe(system), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> Sessions = ets:select( session, ets:fun2ms( fun(#session{sid = {_, Pid}} = S) when node(Pid) == Node -> S end)), lists:foreach( fun(S) -> mnesia:dirty_delete_object(S) end, Sessions), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== update_tables() -> case catch mnesia:table_info(session, attributes) of [ur, user, node] -> mnesia:delete_table(session); [ur, user, pid] -> mnesia:delete_table(session); [usr, us, pid] -> mnesia:delete_table(session); [usr, us, sid, priority, info] -> mnesia:delete_table(session); [sid, usr, us, priority] -> mnesia:delete_table(session); [sid, usr, us, priority, info] -> ok; {'EXIT', _} -> ok end, case lists:member(presence, mnesia:system_info(tables)) of true -> mnesia:delete_table(presence); false -> ok end, case lists:member(local_session, mnesia:system_info(tables)) of true -> mnesia:delete_table(local_session); false -> ok end. ejabberd-18.01/src/mod_carboncopy_mnesia.erl0000644000232200023220000000512113225664356021467 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_carboncopy_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_carboncopy_mnesia). -behaviour(mod_carboncopy). %% API -export([init/2, enable/4, disable/3, list/2, use_cache/1]). -include("mod_carboncopy.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> Fields = record_info(fields, carboncopy), try mnesia:table_info(carboncopy, attributes) of Fields -> ok; _ -> %% recreate.. mnesia:delete_table(carboncopy) catch _:_Error -> %% probably table don't exist ok end, ejabberd_mnesia:create(?MODULE, carboncopy, [{ram_copies, [node()]}, {attributes, record_info(fields, carboncopy)}, {type, bag}]). enable(LUser, LServer, LResource, NS) -> mnesia:dirty_write( #carboncopy{us = {LUser, LServer}, resource = LResource, version = NS}). disable(LUser, LServer, LResource) -> ToDelete = mnesia:dirty_match_object( #carboncopy{us = {LUser, LServer}, resource = LResource, _ = '_'}), lists:foreach(fun mnesia:dirty_delete_object/1, ToDelete). list(LUser, LServer) -> {ok, mnesia:dirty_select( carboncopy, [{#carboncopy{us = {LUser, LServer}, resource = '$2', version = '$3', node = '$4'}, [], [{{'$2','$3','$4'}}]}])}. use_cache(_LServer) -> false. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/mod_carboncopy_riak.erl0000644000232200023220000000541613225664356021150 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 15 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_carboncopy_riak). -behaviour(mod_carboncopy). %% API -export([init/2, enable/4, disable/3, list/2]). -include("logger.hrl"). -include("mod_carboncopy.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> clean_table(). enable(LUser, LServer, LResource, NS) -> ejabberd_riak:put(#carboncopy{us = {LUser, LServer}, resource = LResource, version = NS}, carboncopy_schema(), [{i, {LUser, LServer, LResource}}, {'2i', [{<<"us">>, {LUser, LServer}}]}]). disable(LUser, LServer, LResource) -> ejabberd_riak:delete(carboncopy, {LUser, LServer, LResource}). list(LUser, LServer) -> case ejabberd_riak:get_by_index( carboncopy, carboncopy_schema(), <<"us">>, {LUser, LServer}) of {ok, Rs} -> {ok, [{Resource, NS, Node} || #carboncopy{resource = Resource, version = NS, node = Node} <- Rs]}; {error, _} = Err -> Err end. %%%=================================================================== %%% Internal functions %%%=================================================================== carboncopy_schema() -> {record_info(fields, carboncopy), #carboncopy{}}. clean_table() -> ?DEBUG("Cleaning Riak 'carboncopy' table...", []), case ejabberd_riak:get(carboncopy, carboncopy_schema()) of {ok, Rs} -> lists:foreach( fun(#carboncopy{us = {U, S}, resource = R, node = Node}) when Node == node() -> ejabberd_riak:delete(carboncopy, {U, S, R}); (_) -> ok end, Rs); {error, Reason} = Err -> ?ERROR_MSG("Failed to clean Riak 'carboncopy' table: ~p", [Reason]), Err end. ejabberd-18.01/src/mod_offline.erl0000644000232200023220000006674613225664356017442 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_offline.erl %%% Author : Alexey Shchepin %%% Purpose : Store and manage offline messages. %%% Created : 5 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_offline). -author('alexey@process-one.net'). -protocol({xep, 13, '1.2'}). -protocol({xep, 22, '1.4'}). -protocol({xep, 23, '1.3'}). -protocol({xep, 160, '1.0'}). -protocol({xep, 334, '0.2'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, store_packet/1, store_offline_msg/1, c2s_self_presence/1, get_sm_features/5, get_sm_identity/5, get_sm_items/5, get_info/5, handle_offline_query/1, remove_expired_messages/1, remove_old_messages/2, remove_user/2, import_info/0, import_start/2, import/5, export/1, get_queue_length/2, count_offline_messages/2, get_offline_els/2, find_x_expire/2, c2s_handle_info/2, c2s_copy_session/2, webadmin_page/3, webadmin_user/4, webadmin_user_parse_query/5]). -export([mod_opt_type/1, depends/2]). -deprecated({get_queue_length,2}). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -include("mod_offline.hrl"). -define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000). %% default value for the maximum number of user messages -define(MAX_USER_MESSAGES, infinity). -type c2s_state() :: ejabberd_c2s:state(). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(#offline_msg{}) -> ok. -callback store_message(#offline_msg{}) -> ok | {error, any()}. -callback pop_messages(binary(), binary()) -> {ok, [#offline_msg{}]} | {error, any()}. -callback remove_expired_messages(binary()) -> {atomic, any()}. -callback remove_old_messages(non_neg_integer(), binary()) -> {atomic, any()}. -callback remove_user(binary(), binary()) -> any(). -callback read_message_headers(binary(), binary()) -> [{non_neg_integer(), jid(), jid(), undefined | erlang:timestamp(), xmlel()}]. -callback read_message(binary(), binary(), non_neg_integer()) -> {ok, #offline_msg{}} | error. -callback remove_message(binary(), binary(), non_neg_integer()) -> ok | {error, any()}. -callback read_all_messages(binary(), binary()) -> [#offline_msg{}]. -callback remove_all_messages(binary(), binary()) -> {atomic, any()}. -callback count_messages(binary(), binary()) -> non_neg_integer(). depends(_Host, _Opts) -> []. start(Host, Opts) -> Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, store_packet, 50), ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 50), ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50), ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), ejabberd_hooks:add(webadmin_user, Host, ?MODULE, webadmin_user, 50), ejabberd_hooks:add(webadmin_user_parse_query, Host, ?MODULE, webadmin_user_parse_query, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE, ?MODULE, handle_offline_query, IQDisc). stop(Host) -> ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, store_packet, 50), ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 50), ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 50), ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, webadmin_user, 50), ejabberd_hooks:delete(webadmin_user_parse_query, Host, ?MODULE, webadmin_user_parse_query, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE, ?MODULE, handle_offline_query, IQDisc); true -> ok end. -spec store_offline_msg(#offline_msg{}) -> ok | {error, full | any()}. store_offline_msg(#offline_msg{us = {User, Server}} = Msg) -> Mod = gen_mod:db_mod(Server, ?MODULE), case get_max_user_messages(User, Server) of infinity -> Mod:store_message(Msg); Limit -> Num = count_offline_messages(User, Server), if Num < Limit -> Mod:store_message(Msg); true -> {error, full} end end. get_max_user_messages(User, Server) -> Access = gen_mod:get_module_opt(Server, ?MODULE, access_max_user_messages, max_user_offline_messages), case acl:match_rule(Server, Access, jid:make(User, Server)) of Max when is_integer(Max) -> Max; infinity -> infinity; _ -> ?MAX_USER_MESSAGES end. get_sm_features(Acc, _From, _To, <<"">>, _Lang) -> Feats = case Acc of {result, I} -> I; _ -> [] end, {result, Feats ++ [?NS_FEATURE_MSGOFFLINE, ?NS_FLEX_OFFLINE]}; get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) -> %% override all lesser features... {result, []}; get_sm_features(_Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) -> {result, [?NS_FLEX_OFFLINE]}; get_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc. get_sm_identity(Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) -> [#identity{category = <<"automation">>, type = <<"message-list">>}|Acc]; get_sm_identity(Acc, _From, _To, _Node, _Lang) -> Acc. get_sm_items(_Acc, #jid{luser = U, lserver = S} = JID, #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) -> ejabberd_sm:route(JID, {resend_offline, false}), Mod = gen_mod:db_mod(S, ?MODULE), Hdrs = Mod:read_message_headers(U, S), BareJID = jid:remove_resource(JID), {result, lists:map( fun({Seq, From, _To, _TS, _El}) -> Node = integer_to_binary(Seq), #disco_item{jid = BareJID, node = Node, name = jid:encode(From)} end, Hdrs)}; get_sm_items(Acc, _From, _To, _Node, _Lang) -> Acc. -spec get_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()]; ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()]. get_info(_Acc, #jid{luser = U, lserver = S} = JID, #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, Lang) -> ejabberd_sm:route(JID, {resend_offline, false}), [#xdata{type = result, fields = flex_offline:encode( [{number_of_messages, count_offline_messages(U, S)}], Lang)}]; get_info(Acc, _From, _To, _Node, _Lang) -> Acc. -spec c2s_handle_info(c2s_state(), term()) -> c2s_state(). c2s_handle_info(State, {resend_offline, Flag}) -> {stop, State#{resend_offline => Flag}}; c2s_handle_info(State, _) -> State. -spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state(). c2s_copy_session(State, #{resend_offline := Flag}) -> State#{resend_offline => Flag}; c2s_copy_session(State, _) -> State. -spec handle_offline_query(iq()) -> iq(). handle_offline_query(#iq{from = #jid{luser = U1, lserver = S1}, to = #jid{luser = U2, lserver = S2}, lang = Lang, sub_els = [#offline{}]} = IQ) when {U1, S1} /= {U2, S2} -> Txt = <<"Query to another users is forbidden">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); handle_offline_query(#iq{from = #jid{luser = U, lserver = S} = From, to = #jid{luser = U, lserver = S} = _To, type = Type, lang = Lang, sub_els = [#offline{} = Offline]} = IQ) -> case {Type, Offline} of {get, #offline{fetch = true, items = [], purge = false}} -> %% TODO: report database errors handle_offline_fetch(From), xmpp:make_iq_result(IQ); {get, #offline{fetch = false, items = [_|_] = Items, purge = false}} -> case handle_offline_items_view(From, Items) of true -> xmpp:make_iq_result(IQ); false -> xmpp:make_error(IQ, xmpp:err_item_not_found()) end; {set, #offline{fetch = false, items = [], purge = true}} -> case delete_all_msgs(U, S) of {atomic, ok} -> xmpp:make_iq_result(IQ); _Err -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end; {set, #offline{fetch = false, items = [_|_] = Items, purge = false}} -> case handle_offline_items_remove(From, Items) of true -> xmpp:make_iq_result(IQ); false -> xmpp:make_error(IQ, xmpp:err_item_not_found()) end; _ -> xmpp:make_error(IQ, xmpp:err_bad_request()) end; handle_offline_query(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec handle_offline_items_view(jid(), [offline_item()]) -> boolean(). handle_offline_items_view(JID, Items) -> {U, S, R} = jid:tolower(JID), lists:foldl( fun(#offline_item{node = Node, action = view}, Acc) -> case fetch_msg_by_node(JID, Node) of {ok, OfflineMsg} -> case offline_msg_to_route(S, OfflineMsg) of {route, El} -> NewEl = set_offline_tag(El, Node), case ejabberd_sm:get_session_pid(U, S, R) of Pid when is_pid(Pid) -> Pid ! {route, NewEl}; none -> ok end, Acc or true; error -> Acc or false end; error -> Acc or false end end, false, Items). -spec handle_offline_items_remove(jid(), [offline_item()]) -> boolean(). handle_offline_items_remove(JID, Items) -> lists:foldl( fun(#offline_item{node = Node, action = remove}, Acc) -> Acc or remove_msg_by_node(JID, Node) end, false, Items). -spec set_offline_tag(message(), binary()) -> message(). set_offline_tag(Msg, Node) -> xmpp:set_subtag(Msg, #offline{items = [#offline_item{node = Node}]}). -spec handle_offline_fetch(jid()) -> ok. handle_offline_fetch(#jid{luser = U, lserver = S} = JID) -> ejabberd_sm:route(JID, {resend_offline, false}), lists:foreach( fun({Node, El}) -> El1 = set_offline_tag(El, Node), ejabberd_router:route(El1) end, read_messages(U, S)). -spec fetch_msg_by_node(jid(), binary()) -> error | {ok, #offline_msg{}}. fetch_msg_by_node(To, Seq) -> case catch binary_to_integer(Seq) of I when is_integer(I), I >= 0 -> LUser = To#jid.luser, LServer = To#jid.lserver, Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:read_message(LUser, LServer, I); _ -> error end. -spec remove_msg_by_node(jid(), binary()) -> boolean(). remove_msg_by_node(To, Seq) -> case catch binary_to_integer(Seq) of I when is_integer(I), I>= 0 -> LUser = To#jid.luser, LServer = To#jid.lserver, Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_message(LUser, LServer, I), true; _ -> false end. -spec need_to_store(binary(), message()) -> boolean(). need_to_store(_LServer, #message{type = error}) -> false; need_to_store(_LServer, #message{type = groupchat}) -> false; need_to_store(LServer, #message{type = Type} = Packet) -> case xmpp:has_subtag(Packet, #offline{}) of false -> case check_store_hint(Packet) of store -> true; no_store -> false; none when Type == headline -> false; none -> case gen_mod:get_module_opt( LServer, ?MODULE, store_empty_body, unless_chat_state) of true -> true; false -> Packet#message.body /= []; unless_chat_state -> not xmpp_util:is_standalone_chat_state(Packet) end end; true -> false end. -spec store_packet({any(), message()}) -> {any(), message()}. store_packet({_Action, #message{from = From, to = To} = Packet} = Acc) -> case need_to_store(To#jid.lserver, Packet) of true -> case check_event(Packet) of true -> #jid{luser = LUser, lserver = LServer} = To, case ejabberd_hooks:run_fold(store_offline_message, LServer, Packet, []) of drop -> Acc; NewPacket -> TimeStamp = p1_time_compat:timestamp(), Expire = find_x_expire(TimeStamp, NewPacket), OffMsg = #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, expire = Expire, from = From, to = To, packet = NewPacket}, case store_offline_msg(OffMsg) of ok -> {offlined, NewPacket}; {error, Reason} -> discard_warn_sender(Packet, Reason), stop end end; _ -> Acc end; false -> Acc end. -spec check_store_hint(message()) -> store | no_store | none. check_store_hint(Packet) -> case has_store_hint(Packet) of true -> store; false -> case has_no_store_hint(Packet) of true -> no_store; false -> none end end. -spec has_store_hint(message()) -> boolean(). has_store_hint(Packet) -> xmpp:has_subtag(Packet, #hint{type = 'store'}). -spec has_no_store_hint(message()) -> boolean(). has_no_store_hint(Packet) -> xmpp:has_subtag(Packet, #hint{type = 'no-store'}) orelse xmpp:has_subtag(Packet, #hint{type = 'no-storage'}). %% Check if the packet has any content about XEP-0022 -spec check_event(message()) -> boolean(). check_event(#message{from = From, to = To, id = ID, type = Type} = Msg) -> case xmpp:get_subtag(Msg, #xevent{}) of false -> true; #xevent{id = undefined, offline = false} -> true; #xevent{id = undefined, offline = true} -> NewMsg = #message{from = To, to = From, id = ID, type = Type, sub_els = [#xevent{id = ID, offline = true}]}, ejabberd_router:route(NewMsg), true; _ -> false end. -spec find_x_expire(erlang:timestamp(), message()) -> erlang:timestamp() | never. find_x_expire(TimeStamp, Msg) -> case xmpp:get_subtag(Msg, #expire{seconds = 0}) of #expire{seconds = Int} -> {MegaSecs, Secs, MicroSecs} = TimeStamp, S = MegaSecs * 1000000 + Secs + Int, MegaSecs1 = S div 1000000, Secs1 = S rem 1000000, {MegaSecs1, Secs1, MicroSecs}; false -> never end. c2s_self_presence({_Pres, #{resend_offline := false}} = Acc) -> Acc; c2s_self_presence({#presence{type = available} = NewPres, State} = Acc) -> NewPrio = get_priority_from_presence(NewPres), LastPrio = case maps:get(pres_last, State, error) of error -> -1; LastPres -> get_priority_from_presence(LastPres) end, if LastPrio < 0 andalso NewPrio >= 0 -> route_offline_messages(State); true -> ok end, Acc; c2s_self_presence(Acc) -> Acc. -spec route_offline_messages(c2s_state()) -> ok. route_offline_messages(#{jid := #jid{luser = LUser, lserver = LServer}} = State) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:pop_messages(LUser, LServer) of {ok, OffMsgs} -> lists:foreach( fun(OffMsg) -> route_offline_message(State, OffMsg) end, OffMsgs); _ -> ok end. -spec route_offline_message(c2s_state(), #offline_msg{}) -> ok. route_offline_message(#{lserver := LServer} = State, #offline_msg{expire = Expire} = OffMsg) -> case offline_msg_to_route(LServer, OffMsg) of error -> ok; {route, Msg} -> case is_message_expired(Expire, Msg) of true -> ok; false -> case privacy_check_packet(State, Msg, in) of allow -> ejabberd_router:route(Msg); deny -> ok end end end. -spec is_message_expired(erlang:timestamp() | never, message()) -> boolean(). is_message_expired(Expire, Msg) -> TS = p1_time_compat:timestamp(), Expire1 = case Expire of undefined -> find_x_expire(TS, Msg); _ -> Expire end, Expire1 /= never andalso Expire1 =< TS. -spec privacy_check_packet(c2s_state(), stanza(), in | out) -> allow | deny. privacy_check_packet(#{lserver := LServer} = State, Pkt, Dir) -> ejabberd_hooks:run_fold(privacy_check_packet, LServer, allow, [State, Pkt, Dir]). remove_expired_messages(Server) -> LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_expired_messages(LServer). remove_old_messages(Days, Server) -> LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_old_messages(Days, LServer). -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_user(LUser, LServer), ok. %% Helper functions: %% Warn senders that their messages have been discarded: -spec discard_warn_sender(message(), full | any()) -> ok. discard_warn_sender(Packet, full) -> ErrText = <<"Your contact offline message queue is " "full. The message has been discarded.">>, Lang = xmpp:get_lang(Packet), Err = xmpp:err_resource_constraint(ErrText, Lang), ejabberd_router:route_error(Packet, Err); discard_warn_sender(Packet, _) -> ErrText = <<"Database failure">>, Lang = xmpp:get_lang(Packet), Err = xmpp:err_internal_server_error(ErrText, Lang), ejabberd_router:route_error(Packet, Err). webadmin_page(_, Host, #request{us = _US, path = [<<"user">>, U, <<"queue">>], q = Query, lang = Lang} = _Request) -> Res = user_queue(U, Host, Query, Lang), {stop, Res}; webadmin_page(Acc, _, _) -> Acc. get_offline_els(LUser, LServer) -> [Packet || {_Seq, Packet} <- read_messages(LUser, LServer)]. -spec offline_msg_to_route(binary(), #offline_msg{}) -> {route, message()} | error. offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) -> try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, [ignore_els]) of Pkt -> Pkt1 = xmpp:set_from_to(Pkt, From, To), Pkt2 = add_delay_info(Pkt1, LServer, R#offline_msg.timestamp), {route, Pkt2} catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode packet ~p of user ~s: ~s", [R#offline_msg.packet, jid:encode(To), xmpp:format_error(Why)]), error end. -spec read_messages(binary(), binary()) -> [{binary(), message()}]. read_messages(LUser, LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), lists:flatmap( fun({Seq, From, To, TS, El}) -> Node = integer_to_binary(Seq), try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of Pkt -> Node = integer_to_binary(Seq), Pkt1 = add_delay_info(Pkt, LServer, TS), Pkt2 = xmpp:set_from_to(Pkt1, From, To), [{Node, Pkt2}] catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode packet ~p " "of user ~s: ~s", [El, jid:encode(To), xmpp:format_error(Why)]), [] end end, Mod:read_message_headers(LUser, LServer)). format_user_queue(Hdrs) -> lists:map( fun({Seq, From, To, TS, El}) -> ID = integer_to_binary(Seq), FPacket = ejabberd_web_admin:pretty_print_xml(El), SFrom = jid:encode(From), STo = jid:encode(To), Time = case TS of undefined -> Stamp = fxml:get_path_s(El, [{elem, <<"delay">>}, {attr, <<"stamp">>}]), try xmpp_util:decode_timestamp(Stamp) of {_, _, _} = Now -> format_time(Now) catch _:_ -> <<"">> end; {_, _, _} = Now -> format_time(Now) end, ?XE(<<"tr">>, [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], Time), ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], SFrom), ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], STo), ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], [?XC(<<"pre">>, FPacket)])]) end, Hdrs). format_time(Now) -> {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(Now), str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second]). user_queue(User, Server, Query, Lang) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), US = {LUser, LServer}, Mod = gen_mod:db_mod(LServer, ?MODULE), Res = user_queue_parse_query(LUser, LServer, Query), HdrsAll = Mod:read_message_headers(LUser, LServer), Hdrs = get_messages_subset(User, Server, HdrsAll), FMsgs = format_user_queue(Hdrs), [?XC(<<"h1">>, (str:format(?T(<<"~s's Offline Messages Queue">>), [us_to_list(US)])))] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XE(<<"table">>, [?XE(<<"thead">>, [?XE(<<"tr">>, [?X(<<"td">>), ?XCT(<<"td">>, <<"Time">>), ?XCT(<<"td">>, <<"From">>), ?XCT(<<"td">>, <<"To">>), ?XCT(<<"td">>, <<"Packet">>)])]), ?XE(<<"tbody">>, if FMsgs == [] -> [?XE(<<"tr">>, [?XAC(<<"td">>, [{<<"colspan">>, <<"4">>}], <<" ">>)])]; true -> FMsgs end)]), ?BR, ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>)])]. user_queue_parse_query(LUser, LServer, Query) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case lists:keysearch(<<"delete">>, 1, Query) of {value, _} -> user_queue_parse_query(LUser, LServer, Query, Mod); _ -> nothing end. user_queue_parse_query(LUser, LServer, Query, Mod) -> case lists:keytake(<<"selected">>, 1, Query) of {value, {_, Seq}, Query2} -> case catch binary_to_integer(Seq) of I when is_integer(I), I>=0 -> Mod:remove_message(LUser, LServer, I); _ -> nothing end, user_queue_parse_query(LUser, LServer, Query2, Mod); false -> nothing end. us_to_list({User, Server}) -> jid:encode({User, Server, <<"">>}). get_queue_length(LUser, LServer) -> count_offline_messages(LUser, LServer). get_messages_subset(User, Host, MsgsAll) -> MaxOfflineMsgs = case get_max_user_messages(User, Host) of Number when is_integer(Number) -> Number; _ -> 100 end, Length = length(MsgsAll), get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll). get_messages_subset2(Max, Length, MsgsAll) when Length =< Max * 2 -> MsgsAll; get_messages_subset2(Max, Length, MsgsAll) -> FirstN = Max, {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll), MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2), NoJID = jid:make(<<"...">>, <<"...">>), Seq = <<"0">>, IntermediateMsg = #xmlel{name = <<"...">>, attrs = [], children = []}, MsgsFirstN ++ [{Seq, NoJID, NoJID, IntermediateMsg}] ++ MsgsLastN. webadmin_user(Acc, User, Server, Lang) -> QueueLen = count_offline_messages(jid:nodeprep(User), jid:nameprep(Server)), FQueueLen = [?AC(<<"queue/">>, (integer_to_binary(QueueLen)))], Acc ++ [?XCT(<<"h3">>, <<"Offline Messages:">>)] ++ FQueueLen ++ [?C(<<" ">>), ?INPUTT(<<"submit">>, <<"removealloffline">>, <<"Remove All Offline Messages">>)]. -spec delete_all_msgs(binary(), binary()) -> {atomic, any()}. delete_all_msgs(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_all_messages(LUser, LServer). webadmin_user_parse_query(_, <<"removealloffline">>, User, Server, _Query) -> case delete_all_msgs(User, Server) of {atomic, ok} -> ?INFO_MSG("Removed all offline messages for ~s@~s", [User, Server]), {stop, ok}; Err -> ?ERROR_MSG("Failed to remove offline messages: ~p", [Err]), {stop, error} end; webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) -> Acc. %% Returns as integer the number of offline messages for a given user -spec count_offline_messages(binary(), binary()) -> non_neg_integer(). count_offline_messages(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:count_messages(LUser, LServer). -spec add_delay_info(message(), binary(), undefined | erlang:timestamp()) -> message(). add_delay_info(Packet, LServer, TS) -> NewTS = case TS of undefined -> p1_time_compat:timestamp(); _ -> TS end, Packet1 = xmpp:put_meta(Packet, from_offline, true), xmpp_util:add_delay_info(Packet1, jid:make(LServer), NewTS, <<"Offline storage">>). -spec get_priority_from_presence(presence()) -> integer(). get_priority_from_presence(#presence{priority = Prio}) -> case Prio of undefined -> 0; _ -> Prio end. export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import_info() -> [{<<"spool">>, 4}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, []). import(LServer, {sql, _}, DBType, <<"spool">>, [LUser, XML, _Seq, _TimeStamp]) -> El = fxml_stream:parse_element(XML), #message{from = From, to = To} = Msg = xmpp:decode(El, ?NS_CLIENT, [ignore_els]), TS = case xmpp:get_subtag(Msg, #delay{stamp = {0,0,0}}) of #delay{stamp = {MegaSecs, Secs, _}} -> {MegaSecs, Secs, 0}; false -> p1_time_compat:timestamp() end, US = {LUser, LServer}, Expire = find_x_expire(TS, Msg), OffMsg = #offline_msg{us = US, packet = El, from = From, to = To, timestamp = TS, expire = Expire}, Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(OffMsg). mod_opt_type(access_max_user_messages) -> fun acl:shaper_rules_validator/1; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(store_empty_body) -> fun (V) when is_boolean(V) -> V; (unless_chat_state) -> unless_chat_state end; mod_opt_type(_) -> [access_max_user_messages, db_type, store_empty_body]. ejabberd-18.01/src/node_flat_sql.erl0000644000232200023220000010642613225664356017761 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_flat_sql.erl %%% Author : Christophe Romain %%% Purpose : Standard PubSub node plugin with ODBC backend %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the default PubSub plugin. %%%

It is used as a default for all unknown PubSub node type. It can serve %%% as a developer basis and reference to build its own custom pubsub node %%% types.

%%%

PubSub plugin nodes are using the {@link gen_node} behaviour.

-module(node_flat_sql). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -compile([{parse_transform, ejabberd_sql_pt}]). -include("pubsub.hrl"). -include("xmpp.hrl"). -include("ejabberd_sql_pt.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, get_entity_subscriptions_for_send_last/2, get_last_items/3]). -export([decode_jid/1, encode_jid/1, encode_jid_like/1, decode_affiliation/1, decode_subscriptions/1, encode_affiliation/1, encode_subscriptions/1, encode_host/1, encode_host_like/1]). init(_Host, _ServerHost, _Opts) -> %%pubsub_subscription_sql:init(Host, ServerHost, Opts), ok. terminate(_Host, _ServerHost) -> ok. options() -> [{sql, true}, {rsm, true} | node_flat:options()]. features() -> [<<"rsm">> | node_flat:features()]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> {_U, _S, _R} = OwnerKey = jid:tolower(jid:remove_resource(Owner)), J = encode_jid(OwnerKey), A = encode_affiliation(owner), S = encode_subscriptions([]), ejabberd_sql:sql_query_t( ?SQL("insert into pubsub_state(" "nodeid, jid, affiliation, subscriptions) " "values (%(Nidx)d, %(J)s, %(A)s, %(S)s)")), {result, {default, broadcast}}. delete_node(Nodes) -> Reply = lists:map( fun(#pubsub_node{id = Nidx} = PubsubNode) -> Subscriptions = case ejabberd_sql:sql_query_t( ?SQL("select @(jid)s, @(subscriptions)s " "from pubsub_state where nodeid=%(Nidx)d")) of {selected, RItems} -> [{decode_jid(SJID), decode_subscriptions(Subs)} || {SJID, Subs} <- RItems]; _ -> [] end, {PubsubNode, Subscriptions} end, Nodes), {result, {default, broadcast, Reply}}. subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, _Options) -> SubKey = jid:tolower(Subscriber), GenKey = jid:remove_resource(SubKey), Authorized = jid:tolower(jid:remove_resource(Sender)) == GenKey, {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey), Whitelisted = lists:member(Affiliation, [member, publisher, owner]), PendingSubscription = lists:any(fun ({pending, _}) -> true; (_) -> false end, Subscriptions), Owner = Affiliation == owner, if not Authorized -> {error, mod_pubsub:extended_error( xmpp:err_bad_request(), mod_pubsub:err_invalid_jid())}; (Affiliation == outcast) or (Affiliation == publish_only) -> {error, xmpp:err_forbidden()}; PendingSubscription -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_pending_subscription())}; (AccessModel == presence) and (not PresenceSubscription) and (not Owner) -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and (not RosterGroup) and (not Owner) -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and (not Whitelisted) and (not Owner) -> {error, mod_pubsub:extended_error( xmpp:err_not_allowed(), mod_pubsub:err_closed_node())}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; %%ForbiddenAnonymous -> %% % Requesting entity is anonymous %% {error, ?ERR_FORBIDDEN}; true -> %%{result, SubId} = pubsub_subscription_sql:subscribe_node(Subscriber, Nidx, Options), {NewSub, SubId} = case Subscriptions of [{subscribed, Id}|_] -> {subscribed, Id}; [] -> Id = pubsub_subscription_sql:make_subid(), Sub = case AccessModel of authorize -> pending; _ -> subscribed end, update_subscription(Nidx, SubKey, [{Sub, Id} | Subscriptions]), {Sub, Id} end, case {NewSub, SendLast} of {subscribed, never} -> {result, {default, subscribed, SubId}}; {subscribed, _} -> {result, {default, subscribed, SubId, send_last}}; {_, _} -> {result, {default, pending, SubId}} end end. unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> SubKey = jid:tolower(Subscriber), GenKey = jid:remove_resource(SubKey), Authorized = jid:tolower(jid:remove_resource(Sender)) == GenKey, {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, SubKey), SubIdExists = case SubId of <<>> -> false; Binary when is_binary(Binary) -> true; _ -> false end, if %% Requesting entity is prohibited from unsubscribing entity not Authorized -> {error, xmpp:err_forbidden()}; %% Entity did not specify SubId %%SubId == "", ?? -> %% {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; %% Invalid subscription identifier %%InvalidSubId -> %% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; %% Requesting entity is not a subscriber Subscriptions == [] -> {error, mod_pubsub:extended_error( xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())}; %% Subid supplied, so use that. SubIdExists -> Sub = first_in_list(fun ({_, S}) when S == SubId -> true; (_) -> false end, Subscriptions), case Sub of {value, S} -> delete_subscription(SubKey, Nidx, S, Affiliation, Subscriptions), {result, default}; false -> {error, mod_pubsub:extended_error( xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())} end; %% Asking to remove all subscriptions to the given node SubId == all -> [delete_subscription(SubKey, Nidx, S, Affiliation, Subscriptions) || S <- Subscriptions], {result, default}; %% No subid supplied, but there's only one matching subscription length(Subscriptions) == 1 -> delete_subscription(SubKey, Nidx, hd(Subscriptions), Affiliation, Subscriptions), {result, default}; %% No subid and more than one possible subscription match. true -> {error, mod_pubsub:extended_error( xmpp:err_bad_request(), mod_pubsub:err_subid_required())} end. delete_subscription(SubKey, Nidx, {Subscription, SubId}, Affiliation, Subscriptions) -> NewSubs = Subscriptions -- [{Subscription, SubId}], %%pubsub_subscription_sql:unsubscribe_node(SubKey, Nidx, SubId), case {Affiliation, NewSubs} of {none, []} -> del_state(Nidx, SubKey); _ -> update_subscription(Nidx, SubKey, NewSubs) end. publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, _PubOpts) -> SubKey = jid:tolower(Publisher), GenKey = jid:remove_resource(SubKey), {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey), Subscribed = case PublishModel of subscribers -> node_flat:is_subscribed(Subscriptions); _ -> undefined end, if not ((PublishModel == open) or (PublishModel == publishers) and ((Affiliation == owner) or (Affiliation == publisher) or (Affiliation == publish_only)) or (Subscribed == true)) -> {error, xmpp:err_forbidden()}; true -> if MaxItems > 0 -> Now = p1_time_compat:timestamp(), case get_item(Nidx, ItemId) of {result, #pubsub_item{creation = {_, GenKey}} = OldItem} -> set_item(OldItem#pubsub_item{ modification = {Now, SubKey}, payload = Payload}), {result, {default, broadcast, []}}; {result, _} -> {error, xmpp:err_forbidden()}; _ -> Items = [ItemId | itemids(Nidx, GenKey)], {result, {_NI, OI}} = remove_extra_items(Nidx, MaxItems, Items), set_item(#pubsub_item{ itemid = {ItemId, Nidx}, creation = {Now, GenKey}, modification = {Now, SubKey}, payload = Payload}), {result, {default, broadcast, OI}} end; true -> {result, {default, broadcast, []}} end end. remove_extra_items(_Nidx, unlimited, ItemIds) -> {result, {ItemIds, []}}; remove_extra_items(Nidx, MaxItems, ItemIds) -> NewItems = lists:sublist(ItemIds, MaxItems), OldItems = lists:nthtail(length(NewItems), ItemIds), del_items(Nidx, OldItems), {result, {NewItems, OldItems}}. delete_item(Nidx, Publisher, PublishModel, ItemId) -> SubKey = jid:tolower(Publisher), GenKey = jid:remove_resource(SubKey), {result, Affiliation} = get_affiliation(Nidx, GenKey), Allowed = Affiliation == publisher orelse Affiliation == owner orelse (PublishModel == open andalso case get_item(Nidx, ItemId) of {result, #pubsub_item{creation = {_, GenKey}}} -> true; _ -> false end), if not Allowed -> {error, xmpp:err_forbidden()}; true -> Items = itemids(Nidx, GenKey), case lists:member(ItemId, Items) of true -> case del_item(Nidx, ItemId) of {updated, 1} -> {result, {default, broadcast}}; _ -> {error, xmpp:err_item_not_found()} end; false -> case Affiliation of owner -> case del_item(Nidx, ItemId) of {updated, 1} -> {result, {default, broadcast}}; _ -> {error, xmpp:err_item_not_found()} end; _ -> {error, xmpp:err_forbidden()} end end end. purge_node(Nidx, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), GenState = get_state(Nidx, GenKey), case GenState of #pubsub_state{affiliation = owner} -> {result, States} = get_states(Nidx), lists:foreach(fun (#pubsub_state{items = []}) -> ok; (#pubsub_state{items = Items}) -> del_items(Nidx, Items) end, States), {result, {default, broadcast}}; _ -> {error, xmpp:err_forbidden()} end. get_entity_affiliations(Host, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), H = encode_host(Host), J = encode_jid(GenKey), {result, case ejabberd_sql:sql_query_t( ?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(affiliation)s " "from pubsub_state i, pubsub_node n where " "i.nodeid = n.nodeid and jid=%(J)s and host=%(H)s")) of {selected, RItems} -> [{nodetree_tree_sql:raw_to_node(Host, {N, <<"">>, T, I}), decode_affiliation(A)} || {N, T, I, A} <- RItems]; _ -> [] end}. get_node_affiliations(Nidx) -> {result, case ejabberd_sql:sql_query_t( ?SQL("select @(jid)s, @(affiliation)s from pubsub_state " "where nodeid=%(Nidx)d")) of {selected, RItems} -> [{decode_jid(J), decode_affiliation(A)} || {J, A} <- RItems]; _ -> [] end}. get_affiliation(Nidx, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), J = encode_jid(GenKey), {result, case ejabberd_sql:sql_query_t( ?SQL("select @(affiliation)s from pubsub_state " "where nodeid=%(Nidx)d and jid=%(J)s")) of {selected, [{A}]} -> decode_affiliation(A); _ -> none end}. set_affiliation(Nidx, Owner, Affiliation) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), {_, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey), case {Affiliation, Subscriptions} of {none, []} -> del_state(Nidx, GenKey); _ -> update_affiliation(Nidx, GenKey, Affiliation) end. get_entity_subscriptions(Host, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), H = encode_host(Host), GJ = encode_jid(GenKey), Query = case SubKey of GenKey -> GJLike = <<(encode_jid_like(GenKey))/binary, "/%">>, ?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n " "where i.nodeid = n.nodeid and " "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host=%(H)s"); _ -> SJ = encode_jid(SubKey), ?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n " "where i.nodeid = n.nodeid and " "jid in (%(SJ)s, %(GJ)s) and host=%(H)s") end, {result, case ejabberd_sql:sql_query_t(Query) of {selected, RItems} -> lists:foldl( fun({N, T, I, J, S}, Acc) -> Node = nodetree_tree_sql:raw_to_node(Host, {N, <<"">>, T, I}), Jid = decode_jid(J), lists:foldl( fun({Sub, SubId}, Acc2) -> [{Node, Sub, SubId, Jid} | Acc2] end, Acc, decode_subscriptions(S)) end, [], RItems); _ -> [] end}. -spec get_entity_subscriptions_for_send_last(Host :: mod_pubsub:hostPubsub(), Owner :: jid()) -> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:subscription(), mod_pubsub:subId(), ljid()}]}. get_entity_subscriptions_for_send_last(Host, Owner) -> SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), H = encode_host(Host), GJ = encode_jid(GenKey), Query = case SubKey of GenKey -> GJLike = <<(encode_jid_like(GenKey))/binary, "/%">>, ?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n, pubsub_node_option o " "where i.nodeid = n.nodeid and n.nodeid = o.nodeid and " "name='send_last_published_item' and val='on_sub_and_presence' and " "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host=%(H)s"); _ -> SJ = encode_jid(SubKey), ?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s " "from pubsub_state i, pubsub_node n, pubsub_node_option o " "where i.nodeid = n.nodeid and n.nodeid = o.nodeid and " "name='send_last_published_item' and val='on_sub_and_presence' and " "jid in (%(SJ)s, %(GJ)s) and host=%(H)s") end, {result, case ejabberd_sql:sql_query_t(Query) of {selected, RItems} -> lists:foldl( fun ({N, T, I, J, S}, Acc) -> Node = nodetree_tree_sql:raw_to_node(Host, {N, <<"">>, T, I}), Jid = decode_jid(J), lists:foldl( fun ({Sub, SubId}, Acc2) -> [{Node, Sub, SubId, Jid}| Acc2] end, Acc, decode_subscriptions(S)) end, [], RItems); _ -> [] end}. get_node_subscriptions(Nidx) -> {result, case ejabberd_sql:sql_query_t( ?SQL("select @(jid)s, @(subscriptions)s from pubsub_state " "where nodeid=%(Nidx)d")) of {selected, RItems} -> lists:foldl( fun ({J, S}, Acc) -> Jid = decode_jid(J), lists:foldl( fun ({Sub, SubId}, Acc2) -> [{Jid, Sub, SubId} | Acc2] end, Acc, decode_subscriptions(S)) end, [], RItems); _ -> [] end}. get_subscriptions(Nidx, Owner) -> SubKey = jid:tolower(Owner), J = encode_jid(SubKey), {result, case ejabberd_sql:sql_query_t( ?SQL("select @(subscriptions)s from pubsub_state" " where nodeid=%(Nidx)d and jid=%(J)s")) of {selected, [{S}]} -> decode_subscriptions(S); _ -> [] end}. set_subscriptions(Nidx, Owner, Subscription, SubId) -> SubKey = jid:tolower(Owner), SubState = get_state_without_itemids(Nidx, SubKey), case {SubId, SubState#pubsub_state.subscriptions} of {_, []} -> case Subscription of none -> {error, mod_pubsub:extended_error( xmpp:err_bad_request(), mod_pubsub:err_not_subscribed())}; _ -> new_subscription(Nidx, Owner, Subscription, SubState) end; {<<>>, [{_, SID}]} -> case Subscription of none -> unsub_with_subid(Nidx, SID, SubState); _ -> replace_subscription({Subscription, SID}, SubState) end; {<<>>, [_ | _]} -> {error, mod_pubsub:extended_error( xmpp:err_bad_request(), mod_pubsub:err_subid_required())}; _ -> case Subscription of none -> unsub_with_subid(Nidx, SubId, SubState); _ -> replace_subscription({Subscription, SubId}, SubState) end end. replace_subscription(NewSub, SubState) -> NewSubs = replace_subscription(NewSub, SubState#pubsub_state.subscriptions, []), set_state(SubState#pubsub_state{subscriptions = NewSubs}). replace_subscription(_, [], Acc) -> Acc; replace_subscription({Sub, SubId}, [{_, SubId} | T], Acc) -> replace_subscription({Sub, SubId}, T, [{Sub, SubId} | Acc]). new_subscription(_Nidx, _Owner, Subscription, SubState) -> %%{result, SubId} = pubsub_subscription_sql:subscribe_node(Owner, Nidx, []), SubId = pubsub_subscription_sql:make_subid(), Subscriptions = [{Subscription, SubId} | SubState#pubsub_state.subscriptions], set_state(SubState#pubsub_state{subscriptions = Subscriptions}), {Subscription, SubId}. unsub_with_subid(Nidx, SubId, SubState) -> %%pubsub_subscription_sql:unsubscribe_node(SubState#pubsub_state.stateid, Nidx, SubId), NewSubs = [{S, Sid} || {S, Sid} <- SubState#pubsub_state.subscriptions, SubId =/= Sid], case {NewSubs, SubState#pubsub_state.affiliation} of {[], none} -> del_state(Nidx, element(1, SubState#pubsub_state.stateid)); _ -> set_state(SubState#pubsub_state{subscriptions = NewSubs}) end. get_pending_nodes(Host, Owner) -> GenKey = jid:remove_resource(jid:tolower(Owner)), States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, affiliation = owner, _ = '_'}), Nidxxs = [Nidx || #pubsub_state{stateid = {_, Nidx}} <- States], NodeTree = mod_pubsub:tree(Host), Reply = mnesia:foldl(fun (#pubsub_state{stateid = {_, Nidx}} = S, Acc) -> case lists:member(Nidx, Nidxxs) of true -> case get_nodes_helper(NodeTree, S) of {value, Node} -> [Node | Acc]; false -> Acc end; false -> Acc end end, [], pubsub_state), {result, Reply}. get_nodes_helper(NodeTree, #pubsub_state{stateid = {_, N}, subscriptions = Subs}) -> HasPending = fun ({pending, _}) -> true; (pending) -> true; (_) -> false end, case lists:any(HasPending, Subs) of true -> case NodeTree:get_node(N) of #pubsub_node{nodeid = {_, Node}} -> {value, Node}; _ -> false end; false -> false end. get_states(Nidx) -> case ejabberd_sql:sql_query_t( ?SQL("select @(jid)s, @(affiliation)s, @(subscriptions)s " "from pubsub_state where nodeid=%(Nidx)d")) of {selected, RItems} -> {result, lists:map( fun({SJID, Aff, Subs}) -> JID = decode_jid(SJID), #pubsub_state{stateid = {JID, Nidx}, nodeidx = Nidx, items = itemids(Nidx, JID), affiliation = decode_affiliation(Aff), subscriptions = decode_subscriptions(Subs)} end, RItems)}; _ -> {result, []} end. get_state(Nidx, JID) -> State = get_state_without_itemids(Nidx, JID), {SJID, _} = State#pubsub_state.stateid, State#pubsub_state{items = itemids(Nidx, SJID)}. -spec get_state_without_itemids(Nidx :: mod_pubsub:nodeIdx(), Key :: ljid()) -> mod_pubsub:pubsubState(). get_state_without_itemids(Nidx, JID) -> J = encode_jid(JID), case ejabberd_sql:sql_query_t( ?SQL("select @(jid)s, @(affiliation)s, @(subscriptions)s " "from pubsub_state " "where nodeid=%(Nidx)d and jid=%(J)s")) of {selected, [{SJID, Aff, Subs}]} -> #pubsub_state{stateid = {decode_jid(SJID), Nidx}, nodeidx = Nidx, affiliation = decode_affiliation(Aff), subscriptions = decode_subscriptions(Subs)}; _ -> #pubsub_state{stateid = {JID, Nidx}, nodeidx = Nidx} end. set_state(State) -> {_, Nidx} = State#pubsub_state.stateid, set_state(Nidx, State). set_state(Nidx, State) -> {JID, _} = State#pubsub_state.stateid, J = encode_jid(JID), S = encode_subscriptions(State#pubsub_state.subscriptions), A = encode_affiliation(State#pubsub_state.affiliation), ?SQL_UPSERT_T( "pubsub_state", ["!nodeid=%(Nidx)d", "!jid=%(J)s", "affiliation=%(A)s", "subscriptions=%(S)s" ]), ok. del_state(Nidx, JID) -> J = encode_jid(JID), catch ejabberd_sql:sql_query_t( ?SQL("delete from pubsub_state" " where jid=%(J)s and nodeid=%(Nidx)d")), ok. get_items(Nidx, _From, undefined) -> SNidx = misc:i2l(Nidx), case ejabberd_sql:sql_query_t( [<<"select itemid, publisher, creation, modification, payload", " from pubsub_item where nodeid='", SNidx/binary, "'", " order by creation asc">>]) of {selected, _, AllItems} -> {result, {[raw_to_item(Nidx, RItem) || RItem <- AllItems], undefined}}; _ -> {result, {[], undefined}} end; get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex, 'after' = After, before = Before}) -> Count = case catch ejabberd_sql:sql_query_t( ?SQL("select @(count(itemid))d from pubsub_item" " where nodeid=%(Nidx)d")) of {selected, [{C}]} -> C; _ -> 0 end, Offset = case {IncIndex, Before, After} of {I, undefined, undefined} when is_integer(I) -> I; _ -> 0 end, Limit = case Max of undefined -> ?MAXITEMS; _ -> Max end, Filters = rsm_filters(misc:i2l(Nidx), Before, After), Query = fun(mssql, _) -> ejabberd_sql:sql_query_t( [<<"select top ", (integer_to_binary(Limit))/binary, " itemid, publisher, creation, modification, payload", " from pubsub_item", Filters/binary>>]); %OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; (_, _) -> ejabberd_sql:sql_query_t( [<<"select itemid, publisher, creation, modification, payload", " from pubsub_item", Filters/binary, " limit ", (integer_to_binary(Limit))/binary, " offset ", (integer_to_binary(Offset))/binary>>]) end, case ejabberd_sql:sql_query_t(Query) of {selected, _, []} -> {result, {[], #rsm_set{count = Count}}}; {selected, [<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} -> Rsm = rsm_page(Count, IncIndex, Offset, RItems), {result, {[raw_to_item(Nidx, RItem) || RItem <- RItems], Rsm}}; _ -> {result, {[], undefined}} end. get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM) -> SubKey = jid:tolower(JID), GenKey = jid:remove_resource(SubKey), {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey), Whitelisted = node_flat:can_fetch_item(Affiliation, Subscriptions), if %%SubId == "", ?? -> %% Entity has multiple subscriptions to the node but does not specify a subscription ID %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; %%InvalidSubId -> %% Entity is subscribed but specifies an invalid subscription ID %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; (Affiliation == outcast) or (Affiliation == publish_only) -> {error, xmpp:err_forbidden()}; (AccessModel == presence) and not PresenceSubscription -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and not RosterGroup -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and not Whitelisted -> {error, mod_pubsub:extended_error( xmpp:err_not_allowed(), mod_pubsub:err_closed_node())}; (AccessModel == authorize) and not Whitelisted -> {error, xmpp:err_forbidden()}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; true -> get_items(Nidx, JID, RSM) end. get_last_items(Nidx, _From, Limit) -> SNidx = misc:i2l(Nidx), Query = fun(mssql, _) -> ejabberd_sql:sql_query_t( [<<"select top ", (integer_to_binary(Limit))/binary, " itemid, publisher, creation, modification, payload", " from pubsub_item where nodeid='", SNidx/binary, "' order by modification desc">>]); (_, _) -> ejabberd_sql:sql_query_t( [<<"select itemid, publisher, creation, modification, payload", " from pubsub_item where nodeid='", SNidx/binary, "' order by modification desc ", " limit ", (integer_to_binary(Limit))/binary>>]) end, case catch ejabberd_sql:sql_query_t(Query) of {selected, [<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} -> {result, [raw_to_item(Nidx, RItem) || RItem <- RItems]}; _ -> {result, []} end. get_item(Nidx, ItemId) -> case catch ejabberd_sql:sql_query_t( ?SQL("select @(itemid)s, @(publisher)s, @(creation)s," " @(modification)s, @(payload)s from pubsub_item" " where nodeid=%(Nidx)d and itemid=%(ItemId)s")) of {selected, [RItem]} -> {result, raw_to_item(Nidx, RItem)}; {selected, []} -> {error, xmpp:err_item_not_found()}; {'EXIT', _} -> {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)} end. get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> SubKey = jid:tolower(JID), GenKey = jid:remove_resource(SubKey), {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey), Whitelisted = node_flat:can_fetch_item(Affiliation, Subscriptions), if %%SubId == "", ?? -> %% Entity has multiple subscriptions to the node but does not specify a subscription ID %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; %%InvalidSubId -> %% Entity is subscribed but specifies an invalid subscription ID %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; (Affiliation == outcast) or (Affiliation == publish_only) -> {error, xmpp:err_forbidden()}; (AccessModel == presence) and not PresenceSubscription -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and not RosterGroup -> {error, mod_pubsub:extended_error( xmpp:err_not_authorized(), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and not Whitelisted -> {error, mod_pubsub:extended_error( xmpp:err_not_allowed(), mod_pubsub:err_closed_node())}; (AccessModel == authorize) and not Whitelisted -> {error, xmpp:err_forbidden()}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; true -> get_item(Nidx, ItemId) end. set_item(Item) -> {ItemId, Nidx} = Item#pubsub_item.itemid, {C, _} = Item#pubsub_item.creation, {M, JID} = Item#pubsub_item.modification, P = encode_jid(JID), Payload = Item#pubsub_item.payload, XML = str:join([fxml:element_to_binary(X) || X<-Payload], <<>>), SM = encode_now(M), SC = encode_now(C), ?SQL_UPSERT_T( "pubsub_item", ["!nodeid=%(Nidx)d", "!itemid=%(ItemId)s", "publisher=%(P)s", "modification=%(SM)s", "payload=%(XML)s", "-creation=%(SC)s" ]), ok. del_item(Nidx, ItemId) -> catch ejabberd_sql:sql_query_t( ?SQL("delete from pubsub_item where itemid=%(ItemId)s" " and nodeid=%(Nidx)d")). del_items(_, []) -> ok; del_items(Nidx, [ItemId]) -> del_item(Nidx, ItemId); del_items(Nidx, ItemIds) -> I = str:join([[<<"'">>, ejabberd_sql:escape(X), <<"'">>] || X <- ItemIds], <<",">>), SNidx = misc:i2l(Nidx), catch ejabberd_sql:sql_query_t([<<"delete from pubsub_item where itemid in (">>, I, <<") and nodeid='">>, SNidx, <<"';">>]). get_item_name(_Host, _Node, Id) -> Id. node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). first_in_list(_Pred, []) -> false; first_in_list(Pred, [H | T]) -> case Pred(H) of true -> {value, H}; _ -> first_in_list(Pred, T) end. itemids(Nidx, {_U, _S, _R} = JID) -> SJID = encode_jid(JID), SJIDLike = <<(encode_jid_like(JID))/binary, "/%">>, case catch ejabberd_sql:sql_query_t( ?SQL("select @(itemid)s from pubsub_item where " "nodeid=%(Nidx)d and (publisher=%(SJID)s" " or publisher like %(SJIDLike)s escape '^') " "order by modification desc")) of {selected, RItems} -> [ItemId || {ItemId} <- RItems]; _ -> [] end. select_affiliation_subscriptions(Nidx, JID) -> J = encode_jid(JID), case catch ejabberd_sql:sql_query_t( ?SQL("select @(affiliation)s, @(subscriptions)s from " " pubsub_state where nodeid=%(Nidx)d and jid=%(J)s")) of {selected, [{A, S}]} -> {decode_affiliation(A), decode_subscriptions(S)}; _ -> {none, []} end. select_affiliation_subscriptions(Nidx, JID, JID) -> select_affiliation_subscriptions(Nidx, JID); select_affiliation_subscriptions(Nidx, GenKey, SubKey) -> {result, Affiliation} = get_affiliation(Nidx, GenKey), {result, BareJidSubs} = get_subscriptions(Nidx, GenKey), {result, FullJidSubs} = get_subscriptions(Nidx, SubKey), {Affiliation, BareJidSubs++FullJidSubs}. update_affiliation(Nidx, JID, Affiliation) -> J = encode_jid(JID), A = encode_affiliation(Affiliation), ?SQL_UPSERT_T( "pubsub_state", ["!nodeid=%(Nidx)d", "!jid=%(J)s", "affiliation=%(A)s", "-subscriptions=''" ]). update_subscription(Nidx, JID, Subscription) -> J = encode_jid(JID), S = encode_subscriptions(Subscription), ?SQL_UPSERT_T( "pubsub_state", ["!nodeid=%(Nidx)d", "!jid=%(J)s", "subscriptions=%(S)s", "-affiliation='n'" ]). -spec decode_jid(SJID :: binary()) -> ljid(). decode_jid(SJID) -> jid:tolower(jid:decode(SJID)). -spec decode_affiliation(Arg :: binary()) -> atom(). decode_affiliation(<<"o">>) -> owner; decode_affiliation(<<"p">>) -> publisher; decode_affiliation(<<"u">>) -> publish_only; decode_affiliation(<<"m">>) -> member; decode_affiliation(<<"c">>) -> outcast; decode_affiliation(_) -> none. -spec decode_subscription(Arg :: binary()) -> atom(). decode_subscription(<<"s">>) -> subscribed; decode_subscription(<<"p">>) -> pending; decode_subscription(<<"u">>) -> unconfigured; decode_subscription(_) -> none. -spec decode_subscriptions(Subscriptions :: binary()) -> [] | [{atom(), binary()},...]. decode_subscriptions(Subscriptions) -> lists:foldl(fun (Subscription, Acc) -> case str:tokens(Subscription, <<":">>) of [S, SubId] -> [{decode_subscription(S), SubId} | Acc]; _ -> Acc end end, [], str:tokens(Subscriptions, <<",">>)). -spec encode_jid(JID :: ljid()) -> binary(). encode_jid(JID) -> jid:encode(JID). -spec encode_jid_like(JID :: ljid()) -> binary(). encode_jid_like(JID) -> ejabberd_sql:escape_like_arg_circumflex(jid:encode(JID)). -spec encode_host(Host :: host()) -> binary(). encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID); encode_host(Host) -> Host. -spec encode_host_like(Host :: host()) -> binary(). encode_host_like({_U, _S, _R} = LJID) -> ejabberd_sql:escape(encode_jid_like(LJID)); encode_host_like(Host) -> ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(Host)). -spec encode_affiliation(Arg :: atom()) -> binary(). encode_affiliation(owner) -> <<"o">>; encode_affiliation(publisher) -> <<"p">>; encode_affiliation(publish_only) -> <<"u">>; encode_affiliation(member) -> <<"m">>; encode_affiliation(outcast) -> <<"c">>; encode_affiliation(_) -> <<"n">>. -spec encode_subscription(Arg :: atom()) -> binary(). encode_subscription(subscribed) -> <<"s">>; encode_subscription(pending) -> <<"p">>; encode_subscription(unconfigured) -> <<"u">>; encode_subscription(_) -> <<"n">>. -spec encode_subscriptions(Subscriptions :: [] | [{atom(), binary()},...]) -> binary(). encode_subscriptions(Subscriptions) -> str:join([<<(encode_subscription(S))/binary, ":", SubId/binary>> || {S, SubId} <- Subscriptions], <<",">>). %%% record getter/setter raw_to_item(Nidx, [ItemId, SJID, Creation, Modification, XML]) -> raw_to_item(Nidx, {ItemId, SJID, Creation, Modification, XML}); raw_to_item(Nidx, {ItemId, SJID, Creation, Modification, XML}) -> JID = decode_jid(SJID), Payload = case fxml_stream:parse_element(XML) of {error, _Reason} -> []; El -> [El] end, #pubsub_item{itemid = {ItemId, Nidx}, nodeidx = Nidx, creation = {decode_now(Creation), jid:remove_resource(JID)}, modification = {decode_now(Modification), JID}, payload = Payload}. rsm_filters(SNidx, undefined, undefined) -> <<" where nodeid='", SNidx/binary, "'", " order by creation asc">>; rsm_filters(SNidx, undefined, After) -> <<" where nodeid='", SNidx/binary, "'", " and creation>'", (encode_stamp(After))/binary, "'", " order by creation asc">>; rsm_filters(SNidx, <<>>, undefined) -> %% 2.5 Requesting the Last Page in a Result Set <<" where nodeid='", SNidx/binary, "'", " order by creation desc">>; rsm_filters(SNidx, Before, undefined) -> <<" where nodeid='", SNidx/binary, "'", " and creation<'", (encode_stamp(Before))/binary, "'", " order by creation desc">>. rsm_page(Count, Index, Offset, Items) -> First = decode_stamp(lists:nth(3, hd(Items))), Last = decode_stamp(lists:nth(3, lists:last(Items))), #rsm_set{count = Count, index = Index, first = #rsm_first{index = Offset, data = First}, last = Last}. encode_stamp(Stamp) -> case catch xmpp_util:decode_timestamp(Stamp) of {MS,S,US} -> encode_now({MS,S,US}); _ -> Stamp end. decode_stamp(Stamp) -> case catch xmpp_util:encode_timestamp(decode_now(Stamp)) of TimeStamp when is_binary(TimeStamp) -> TimeStamp; _ -> Stamp end. encode_now({T1, T2, T3}) -> <<(misc:i2l(T1, 6))/binary, ":", (misc:i2l(T2, 6))/binary, ":", (misc:i2l(T3, 6))/binary>>. decode_now(NowStr) -> [MS, S, US] = binary:split(NowStr, <<":">>, [global]), {binary_to_integer(MS), binary_to_integer(S), binary_to_integer(US)}. ejabberd-18.01/src/mod_last_riak.erl0000644000232200023220000000471513225664356017755 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_last_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_last_riak). -behaviour(mod_last). %% API -export([init/2, import/2, get_last/2, store_last_info/4, remove_user/2]). -include("mod_last.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. get_last(LUser, LServer) -> case ejabberd_riak:get(last_activity, last_activity_schema(), {LUser, LServer}) of {ok, #last_activity{timestamp = TimeStamp, status = Status}} -> {ok, {TimeStamp, Status}}; {error, notfound} -> error; _Err -> %% TODO: log error {error, db_failure} end. store_last_info(LUser, LServer, TimeStamp, Status) -> US = {LUser, LServer}, ejabberd_riak:put(#last_activity{us = US, timestamp = TimeStamp, status = Status}, last_activity_schema()). remove_user(LUser, LServer) -> {atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}. import(_LServer, #last_activity{} = LA) -> ejabberd_riak:put(LA, last_activity_schema()). %%%=================================================================== %%% Internal functions %%%=================================================================== last_activity_schema() -> {record_info(fields, last_activity), #last_activity{}}. ejabberd-18.01/src/ejabberd_access_permissions.erl0000644000232200023220000004430713225664356022660 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_access_permissions.erl %%% Author : Paweł Chmielowski %%% Purpose : Administrative functions and commands %%% Created : 7 Sep 2016 by Paweł Chmielowski %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_access_permissions). -author("pawel@process-one.net"). -include("ejabberd_commands.hrl"). -include("logger.hrl"). -behaviour(gen_server). -behavior(ejabberd_config). %% API -export([start_link/0, parse_api_permissions/1, can_access/2, invalidate/0, opt_type/1, show_current_definitions/0, register_permission_addon/2, unregister_permission_addon/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -define(SERVER, ?MODULE). -record(state, { definitions = none, fragments_generators = [] }). %%%=================================================================== %%% API %%%=================================================================== -spec can_access(atom(), map()) -> allow | deny. can_access(Cmd, CallerInfo) -> gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}). -spec invalidate() -> ok. invalidate() -> gen_server:cast(?MODULE, invalidate). -spec register_permission_addon(atom(), fun()) -> ok. register_permission_addon(Name, Fun) -> gen_server:call(?MODULE, {register_config_fragment_generator, Name, Fun}). -spec unregister_permission_addon(atom()) -> ok. unregister_permission_addon(Name) -> gen_server:call(?MODULE, {unregister_config_fragment_generator, Name}). -spec show_current_definitions() -> any(). show_current_definitions() -> gen_server:call(?MODULE, show_current_definitions). %%-------------------------------------------------------------------- %% @doc %% Starts the server %% %% @end %%-------------------------------------------------------------------- -spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}. start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %%-------------------------------------------------------------------- %% @private %% @doc %% Initializes the server %% %% @spec init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% @end %%-------------------------------------------------------------------- -spec init(Args :: term()) -> {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | {stop, Reason :: term()} | ignore. init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90), {ok, #state{}}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling call messages %% %% @end %%-------------------------------------------------------------------- -spec handle_call(Request :: term(), From :: {pid(), Tag :: term()}, State :: #state{}) -> {reply, Reply :: term(), NewState :: #state{}} | {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | {stop, Reason :: term(), NewState :: #state{}}. handle_call({can_access, Cmd, CallerInfo}, _From, State) -> CallerModule = maps:get(caller_module, CallerInfo, none), Host = maps:get(caller_host, CallerInfo, global), {State2, Defs0} = get_definitions(State), Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0, Res = lists:foldl( fun({Name, _} = Def, none) -> case matches_definition(Def, Cmd, CallerModule, Host, CallerInfo) of true -> ?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]), allow; _ -> none end; (_, Val) -> Val end, none, Defs), Res2 = case Res of allow -> allow; _ -> ?DEBUG("Command '~p' execution denied (CallerInfo=~p)", [Cmd, CallerInfo]), deny end, {reply, Res2, State2}; handle_call(show_current_definitions, _From, State) -> {State2, Defs} = get_definitions(State), {reply, Defs, State2}; handle_call({register_config_fragment_generator, Name, Fun}, _From, #state{fragments_generators = Gens} = State) -> NGens = lists:keystore(Name, 1, Gens, {Name, Fun}), {reply, ok, State#state{fragments_generators = NGens}}; handle_call({unregister_config_fragment_generator, Name}, _From, #state{fragments_generators = Gens} = State) -> NGens = lists:keydelete(Name, 1, Gens), {reply, ok, State#state{fragments_generators = NGens}}; handle_call(_Request, _From, State) -> {reply, ok, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling cast messages %% %% @end %%-------------------------------------------------------------------- -spec handle_cast(Request :: term(), State :: #state{}) -> {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}. handle_cast(invalidate, State) -> {noreply, State#state{definitions = none}}; handle_cast(_Request, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling all non call/cast messages %% %% @spec handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% @end %%-------------------------------------------------------------------- -spec handle_info(Info :: timeout() | term(), State :: #state{}) -> {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}. handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_server terminates %% with Reason. The return value is ignored. %% %% @spec terminate(Reason, State) -> void() %% @end %%-------------------------------------------------------------------- -spec terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), State :: #state{}) -> term(). terminate(_Reason, _State) -> ejabberd_hooks:delete(config_reloaded, ?MODULE, invalidate, 90). %%-------------------------------------------------------------------- %% @private %% @doc %% Convert process state when code is changed %% %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} %% @end %%-------------------------------------------------------------------- -spec code_change(OldVsn :: term() | {down, term()}, State :: #state{}, Extra :: term()) -> {ok, NewState :: #state{}} | {error, Reason :: term()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec get_definitions(#state{}) -> {#state{}, any()}. get_definitions(#state{definitions = Defs} = State) when Defs /= none -> {State, Defs}; get_definitions(#state{definitions = none, fragments_generators = Gens} = State) -> DefaultOptions = [{<<"admin access">>, {[], [{acl,{acl,admin}}, {oauth,[<<"ejabberd:admin">>],[{acl,{acl,admin}}]}], {all, [start, stop]}}}], ApiPerms = ejabberd_config:get_option(api_permissions, DefaultOptions), AllCommands = ejabberd_commands:get_commands_definition(), Frags = lists:foldl( fun({_Name, Generator}, Acc) -> Acc ++ Generator() end, [], Gens), NDefs0 = lists:map( fun({Name, {From, Who, {Add, Del}}}) -> Cmds = filter_commands_with_permissions(AllCommands, Add, Del), {Name, {From, Who, Cmds}} end, ApiPerms ++ Frags), NDefs = case lists:keyfind(<<"console commands">>, 1, NDefs0) of false -> [{<<"console commands">>, {[ejabberd_ctl], [{acl, all}], filter_commands_with_permissions(AllCommands, all, none)}} | NDefs0]; _ -> NDefs0 end, {State#state{definitions = NDefs}, NDefs}. matches_definition({_Name, {From, Who, What}}, Cmd, Module, Host, CallerInfo) -> case What == all orelse lists:member(Cmd, What) of true -> case From == [] orelse lists:member(Module, From) of true -> Scope = maps:get(oauth_scope, CallerInfo, none), lists:any( fun({access, Access}) when Scope == none -> acl:access_matches(Access, CallerInfo, Host) == allow; ({acl, Acl}) when Scope == none -> acl:acl_rule_matches(Acl, CallerInfo, Host); ({oauth, Scopes, List}) when Scope /= none -> case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of true -> lists:any( fun({access, Access}) -> acl:access_matches(Access, CallerInfo, Host) == allow; ({acl, Acl}) -> acl:acl_rule_matches(Acl, CallerInfo, Host) end, List); _ -> false end; (_) -> false end, Who); _ -> false end; _ -> false end. filter_commands_with_permissions(AllCommands, Add, Del) -> CommandsAdd = filter_commands_with_patterns(AllCommands, Add, []), CommandsDel = filter_commands_with_patterns(CommandsAdd, Del, []), lists:map(fun(#ejabberd_commands{name = N}) -> N end, CommandsAdd -- CommandsDel). filter_commands_with_patterns([], _Patterns, Acc) -> Acc; filter_commands_with_patterns([C | CRest], Patterns, Acc) -> case command_matches_patterns(C, Patterns) of true -> filter_commands_with_patterns(CRest, Patterns, [C | Acc]); _ -> filter_commands_with_patterns(CRest, Patterns, Acc) end. command_matches_patterns(_, all) -> true; command_matches_patterns(_, none) -> false; command_matches_patterns(_, []) -> false; command_matches_patterns(#ejabberd_commands{tags = Tags} = C, [{tag, Tag} | Tail]) -> case lists:member(Tag, Tags) of true -> true; _ -> command_matches_patterns(C, Tail) end; command_matches_patterns(#ejabberd_commands{name = Name}, [Name | _Tail]) -> true; command_matches_patterns(C, [_ | Tail]) -> command_matches_patterns(C, Tail). %%%=================================================================== %%% Options parsing code %%%=================================================================== parse_api_permissions(Data) when is_list(Data) -> [parse_api_permission(Name, Args) || {Name, Args} <- Data]. parse_api_permission(Name, Args0) -> Args = lists:flatten(Args0), {From, Who, What} = case key_split(Args, [{from, []}, {who, none}, {what, []}]) of {error, Msg} -> report_error(<<"~s inside api_permission '~s' section">>, [Msg, Name]); Val -> Val end, {Name, {parse_from(Name, From), parse_who(Name, Who, oauth), parse_what(Name, What)}}. parse_from(_Name, Module) when is_atom(Module) -> [Module]; parse_from(Name, Modules) when is_list(Modules) -> lists:foreach(fun(Module) when is_atom(Module) -> ok; (Val) -> report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>, [Val, Name]) end, Modules), Modules; parse_from(Name, Val) -> report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>, [Val, Name]). parse_who(Name, Atom, ParseOauth) when is_atom(Atom) -> parse_who(Name, [Atom], ParseOauth); parse_who(Name, Defs, ParseOauth) when is_list(Defs) -> lists:map( fun([Val]) -> [NVal] = parse_who(Name, [Val], ParseOauth), NVal; ({access, Val}) -> try acl:access_rules_validator(Val) of Rule -> {access, Rule} catch throw:{invalid_syntax, Msg} -> report_error(<<"Invalid access rule: '~s' used inside 'who' section for api_permission '~s'">>, [Msg, Name]); error:_ -> report_error(<<"Invalid access rule '~p' used inside 'who' section for api_permission '~s'">>, [Val, Name]) end; ({oauth, OauthList}) when is_list(OauthList) -> case ParseOauth of oauth -> Nested = parse_who(Name, lists:flatten(OauthList), scope), {Scopes, Rest} = lists:partition( fun({scope, _}) -> true; (_) -> false end, Nested), case Scopes of [] -> report_error(<<"Oauth rule must contain at least one scope rule in 'who' section for api_permission '~s'">>, [Name]); _ -> {oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest} end; scope -> report_error(<<"Oauth rule can't be embeded inside other oauth rule in 'who' section for api_permission '~s'">>, [Name]) end; ({scope, ScopeList}) -> case ParseOauth of oauth -> report_error(<<"Scope can be included only inside oauth rule in 'who' section for api_permission '~s'">>, [Name]); scope -> ScopeList2 = case ScopeList of V when is_binary(V) -> [V]; V2 when is_list(V2) -> V2; V3 -> report_error(<<"Invalid value for scope '~p' in 'who' section for api_permission '~s'">>, [V3, Name]) end, {scope, ScopeList2} end; (Atom) when is_atom(Atom) -> {acl, {acl, Atom}}; (Other) -> try acl:normalize_spec(Other) of Rule2 -> {acl, Rule2} catch _:_ -> report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>, [Other, Name]) end end, Defs); parse_who(Name, Val, _ParseOauth) -> report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>, [Val, Name]). parse_what(Name, Binary) when is_binary(Binary) -> parse_what(Name, [Binary]); parse_what(Name, Defs) when is_list(Defs) -> {A, D} = lists:foldl( fun(Def, {Add, Del}) -> case parse_single_what(Def) of {error, Err} -> report_error(<<"~s used in value '~p' in 'what' section for api_permission '~s'">>, [Err, Def, Name]); all -> {case Add of none -> none; _ -> all end, Del}; {neg, all} -> {none, all}; {neg, Value} -> {Add, case Del of L when is_list(L) -> [Value | L]; L2 -> L2 end}; Value -> {case Add of L when is_list(L) -> [Value | L]; L2 -> L2 end, Del} end end, {[], []}, Defs), case {A, D} of {[], _} -> {none, all}; {A2, []} -> {A2, none}; V -> V end; parse_what(Name, Val) -> report_error(<<"Invalid value '~p' used inside 'what' section for api_permission '~s'">>, [Val, Name]). parse_single_what(<<"*">>) -> all; parse_single_what(<<"!*">>) -> {neg, all}; parse_single_what(<<"!", Rest/binary>>) -> case parse_single_what(Rest) of {neg, _} -> {error, <<"Double negation">>}; {error, _} = Err -> Err; V -> {neg, V} end; parse_single_what(<<"[tag:", Rest/binary>>) -> case binary:split(Rest, <<"]">>) of [TagName, <<"">>] -> case parse_single_what(TagName) of {error, _} = Err -> Err; V when is_atom(V) -> {tag, V}; _ -> {error, <<"Invalid tag">>} end; _ -> {error, <<"Invalid tag">>} end; parse_single_what(Binary) when is_binary(Binary) -> case is_valid_command_name(Binary) of true -> binary_to_atom(Binary, latin1); _ -> {error, <<"Invalid value">>} end; parse_single_what(_) -> {error, <<"Invalid value">>}. is_valid_command_name(<<>>) -> false; is_valid_command_name(Val) -> is_valid_command_name2(Val). is_valid_command_name2(<<>>) -> true; is_valid_command_name2(<>) when K >= $a andalso K =< $z orelse K == $_ -> is_valid_command_name2(Rest); is_valid_command_name2(_) -> false. key_split(Args, Fields) -> {_, Order1, Results1, Required1} = lists:foldl( fun({Field, Default}, {Idx, Order, Results, Required}) -> {Idx + 1, maps:put(Field, Idx, Order), [Default | Results], Required}; (Field, {Idx, Order, Results, Required}) -> {Idx + 1, maps:put(Field, Idx, Order), [none | Results], maps:put(Field, 1, Required)} end, {1, #{}, [], #{}}, Fields), key_split(Args, list_to_tuple(Results1), Order1, Required1, #{}). key_split([], _Results, _Order, Required, _Duplicates) when map_size(Required) > 0 -> parse_error(<<"Missing fields '~s">>, [str:join(maps:keys(Required), <<", ">>)]); key_split([], Results, _Order, _Required, _Duplicates) -> Results; key_split([{Arg, Value} | Rest], Results, Order, Required, Duplicates) -> case maps:find(Arg, Order) of {ok, Idx} -> case maps:is_key(Arg, Duplicates) of false -> Results2 = setelement(Idx, Results, Value), key_split(Rest, Results2, Order, maps:remove(Arg, Required), maps:put(Arg, 1, Duplicates)); true -> parse_error(<<"Duplicate field '~s'">>, [Arg]) end; _ -> parse_error(<<"Unknown field '~s'">>, [Arg]) end. report_error(Format, Args) -> throw({invalid_syntax, (str:format(Format, Args))}). parse_error(Format, Args) -> {error, (str:format(Format, Args))}. opt_type(api_permissions) -> fun parse_api_permissions/1; opt_type(_) -> [api_permissions]. ejabberd-18.01/src/ejabberd_websocket.erl0000644000232200023220000004104413225664356020745 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_websocket.erl %%% Author : Eric Cestari %%% Purpose : XMPP Websocket support %%% Created : 09-10-2010 by Eric Cestari %%% %%% Some code lifted from MISULTIN - WebSocket misultin_websocket.erl - >-|-|-(°> %%% (http://github.com/ostinelli/misultin/blob/master/src/misultin_websocket.erl) %%% Copyright (C) 2010, Roberto Ostinelli , Joe Armstrong. %%% All rights reserved. %%% %%% Code portions from Joe Armstrong have been originally taken under MIT license at the address: %%% %%% %%% BSD License %%% %%% Redistribution and use in source and binary forms, with or without modification, are permitted provided %%% that the following conditions are met: %%% %%% * Redistributions of source code must retain the above copyright notice, this list of conditions and the %%% following disclaimer. %%% * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and %%% the following disclaimer in the documentation and/or other materials provided with the distribution. %%% * Neither the name of the authors nor the names of its contributors may be used to endorse or promote %%% products derived from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED %%% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A %%% PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR %%% ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED %%% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) %%% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING %%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %%% ========================================================================================================== %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%%---------------------------------------------------------------------- -module(ejabberd_websocket). -protocol({rfc, 6455}). -author('ecestari@process-one.net'). -export([check/2, socket_handoff/8]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -define(CT_XML, {<<"Content-Type">>, <<"text/xml; charset=utf-8">>}). -define(CT_PLAIN, {<<"Content-Type">>, <<"text/plain">>}). -define(AC_ALLOW_ORIGIN, {<<"Access-Control-Allow-Origin">>, <<"*">>}). -define(AC_ALLOW_METHODS, {<<"Access-Control-Allow-Methods">>, <<"GET, OPTIONS">>}). -define(AC_ALLOW_HEADERS, {<<"Access-Control-Allow-Headers">>, <<"Content-Type">>}). -define(AC_MAX_AGE, {<<"Access-Control-Max-Age">>, <<"86400">>}). -define(OPTIONS_HEADER, [?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS, ?AC_ALLOW_HEADERS, ?AC_MAX_AGE]). -define(HEADER, [?CT_XML, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]). check(_Path, Headers) -> RequiredHeaders = [{'Upgrade', <<"websocket">>}, {'Connection', ignore}, {'Host', ignore}, {<<"Sec-Websocket-Key">>, ignore}, {<<"Sec-Websocket-Version">>, <<"13">>}], F = fun ({Tag, Val}) -> case lists:keyfind(Tag, 1, Headers) of false -> true; % header not found, keep in list {_, HVal} -> case Val of ignore -> false; % ignore value -> ok, remove from list _ -> % expected value -> ok, remove from list (false) % value is different, keep in list (true) str:to_lower(HVal) /= Val end end end, case lists:filter(F, RequiredHeaders) of [] -> true; _MissingHeaders -> false end. socket_handoff(LocalPath, #request{method = 'GET', ip = IP, q = Q, path = Path, headers = Headers, host = Host, port = Port, opts = HOpts}, Socket, SockMod, Buf, _Opts, HandlerModule, InfoMsgFun) -> case check(LocalPath, Headers) of true -> WS = #ws{socket = Socket, sockmod = SockMod, ip = IP, q = Q, host = Host, port = Port, path = Path, headers = Headers, local_path = LocalPath, buf = Buf, http_opts = HOpts}, connect(WS, HandlerModule); _ -> {200, ?HEADER, InfoMsgFun()} end; socket_handoff(_, #request{method = 'OPTIONS'}, _, _, _, _, _, _) -> {200, ?OPTIONS_HEADER, []}; socket_handoff(_, #request{method = 'HEAD'}, _, _, _, _, _, _) -> {200, ?HEADER, []}; socket_handoff(_, _, _, _, _, _, _, _) -> {400, ?HEADER, #xmlel{name = <<"h1">>, children = [{xmlcdata, <<"400 Bad Request">>}]}}. connect(#ws{socket = Socket, sockmod = SockMod} = Ws, WsLoop) -> {NewWs, HandshakeResponse} = handshake(Ws), SockMod:send(Socket, HandshakeResponse), ?DEBUG("Sent handshake response : ~p", [HandshakeResponse]), Ws0 = {Ws, self()}, {ok, WsHandleLoopPid} = WsLoop:start_link(Ws0), erlang:monitor(process, WsHandleLoopPid), case NewWs#ws.buf of <<>> -> ok; Data -> self() ! {raw, Socket, Data} end, % set opts case SockMod of gen_tcp -> inet:setopts(Socket, [{packet, 0}, {active, true}]); _ -> SockMod:setopts(Socket, [{packet, 0}, {active, true}]) end, ws_loop(none, Socket, WsHandleLoopPid, SockMod). handshake(#ws{headers = Headers} = State) -> {_, Key} = lists:keyfind(<<"Sec-Websocket-Key">>, 1, Headers), SubProtocolHeader = case find_subprotocol(Headers) of false -> []; V -> [<<"Sec-Websocket-Protocol:">>, V, <<"\r\n">>] end, Hash = base64:encode( crypto:hash(sha, <>)), {State, [<<"HTTP/1.1 101 Switching Protocols\r\n">>, <<"Upgrade: websocket\r\n">>, <<"Connection: Upgrade\r\n">>, SubProtocolHeader, <<"Sec-WebSocket-Accept: ">>, Hash, <<"\r\n\r\n">>]}. find_subprotocol(Headers) -> case lists:keysearch(<<"Sec-Websocket-Protocol">>, 1, Headers) of false -> case lists:keysearch(<<"Websocket-Protocol">>, 1, Headers) of false -> false; {value, {_, Protocol2}} -> Protocol2 end; {value, {_, Protocol}} -> Protocol end. ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode) -> receive {DataType, _Socket, Data} when DataType =:= tcp orelse DataType =:= raw -> case handle_data(DataType, FrameInfo, Data, Socket, WsHandleLoopPid, SocketMode) of {error, Error} -> ?DEBUG("tls decode error ~p", [Error]), websocket_close(Socket, WsHandleLoopPid, SocketMode, 1002); % protocol error {NewFrameInfo, ToSend} -> lists:foreach(fun(Pkt) -> SocketMode:send(Socket, Pkt) end, ToSend), ws_loop(NewFrameInfo, Socket, WsHandleLoopPid, SocketMode) end; {tcp_closed, _Socket} -> ?DEBUG("tcp connection was closed, exit", []), websocket_close(Socket, WsHandleLoopPid, SocketMode, 0); {'DOWN', Ref, process, WsHandleLoopPid, Reason} -> Code = case Reason of normal -> 1000; % normal close _ -> ?ERROR_MSG("linked websocket controlling loop crashed " "with reason: ~p", [Reason]), 1011 % internal error end, erlang:demonitor(Ref), websocket_close(Socket, WsHandleLoopPid, SocketMode, Code); {send, Data} -> SocketMode:send(Socket, encode_frame(Data, 1)), ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode); {ping, Data} -> SocketMode:send(Socket, encode_frame(Data, 9)), ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode); shutdown -> ?DEBUG("shutdown request received, closing websocket " "with pid ~p", [self()]), websocket_close(Socket, WsHandleLoopPid, SocketMode, 1001); % going away _Ignored -> ?WARNING_MSG("received unexpected message, ignoring: ~p", [_Ignored]), ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode) end. encode_frame(Data, Opcode) -> case byte_size(Data) of S1 when S1 < 126 -> <<1:1, 0:3, Opcode:4, 0:1, S1:7, Data/binary>>; S2 when S2 < 65536 -> <<1:1, 0:3, Opcode:4, 0:1, 126:7, S2:16, Data/binary>>; S3 -> <<1:1, 0:3, Opcode:4, 0:1, 127:7, S3:64, Data/binary>> end. -record(frame_info, {mask = none, offset = 0, left, final_frame = true, opcode, unprocessed = <<>>, unmasked = <<>>, unmasked_msg = <<>>}). decode_header(<>) when Len < 126 -> {Len, Final, Opcode, none, Data}; decode_header(<>) -> {Len, Final, Opcode, none, Data}; decode_header(<>) -> {Len, Final, Opcode, none, Data}; decode_header(<>) when Len < 126 -> {Len, Final, Opcode, Mask, Data}; decode_header(<>) -> {Len, Final, Opcode, Mask, Data}; decode_header(<>) -> {Len, Final, Opcode, Mask, Data}; decode_header(_) -> none. unmask_int(Offset, _, <<>>, Acc) -> {Acc, Offset}; unmask_int(0, <> = Mask, <>, Acc) -> unmask_int(0, Mask, Rest, <>); unmask_int(0, <> = Mask, <>, Acc) -> unmask_int(1, Mask, Rest, <>); unmask_int(1, <<_:8, M:8, _/binary>> = Mask, <>, Acc) -> unmask_int(2, Mask, Rest, <>); unmask_int(2, <<_:16, M:8, _/binary>> = Mask, <>, Acc) -> unmask_int(3, Mask, Rest, <>); unmask_int(3, <<_:24, M:8>> = Mask, <>, Acc) -> unmask_int(0, Mask, Rest, <>). unmask(#frame_info{mask = none} = State, Data) -> {State, Data}; unmask(#frame_info{mask = Mask, offset = Offset} = State, Data) -> {Unmasked, NewOffset} = unmask_int(Offset, Mask, Data, <<>>), {State#frame_info{offset = NewOffset}, Unmasked}. process_frame(none, Data) -> process_frame(#frame_info{}, Data); process_frame(#frame_info{left = Left} = FrameInfo, <<>>) when Left > 0 -> {FrameInfo, [], []}; process_frame(#frame_info{unprocessed = none, unmasked = UnmaskedPre, left = Left} = State, Data) when byte_size(Data) < Left -> {State2, Unmasked} = unmask(State, Data), {State2#frame_info{left = Left - byte_size(Data), unmasked = [UnmaskedPre, Unmasked]}, [], []}; process_frame(#frame_info{unprocessed = none, unmasked = UnmaskedPre, opcode = Opcode, final_frame = Final, left = Left, unmasked_msg = UnmaskedMsg} = FrameInfo, Data) -> <> = Data, {_, Unmasked} = unmask(FrameInfo, ToProcess), case Final of true -> {FrameInfo3, Recv, Send} = process_frame(#frame_info{}, Unprocessed), case Opcode of X when X < 3 -> {FrameInfo3, [iolist_to_binary([UnmaskedMsg, UnmaskedPre, Unmasked]) | Recv], Send}; 9 -> % Ping Frame = encode_frame(Unprocessed, 10), {FrameInfo3#frame_info{unmasked_msg = UnmaskedMsg}, [ping | Recv], [Frame | Send]}; 10 -> % Pong {FrameInfo3, [pong | Recv], Send}; 8 -> % Close CloseCode = case Unmasked of <> -> ?DEBUG("WebSocket close op: ~p ~s", [Code, Message]), Code; <> -> ?DEBUG("WebSocket close op: ~p", [Code]), Code; _ -> ?DEBUG("WebSocket close op unknown: ~p", [Unmasked]), 1000 end, Frame = encode_frame(<>, 8), {FrameInfo3#frame_info{unmasked_msg=UnmaskedMsg}, Recv, [Frame | Send]}; _ -> {FrameInfo3#frame_info{unmasked_msg = UnmaskedMsg}, Recv, Send} end; _ -> process_frame(#frame_info{unmasked_msg = [UnmaskedMsg, UnmaskedPre, Unmasked]}, Unprocessed) end; process_frame(#frame_info{unprocessed = <<>>} = FrameInfo, Data) -> case decode_header(Data) of none -> {FrameInfo#frame_info{unprocessed = Data}, [], []}; {Len, Final, Opcode, Mask, Rest} -> process_frame(FrameInfo#frame_info{mask = Mask, final_frame = Final == 1, left = Len, opcode = Opcode, unprocessed = none}, Rest) end; process_frame(#frame_info{unprocessed = UnprocessedPre} = FrameInfo, Data) -> process_frame(FrameInfo#frame_info{unprocessed = <<>>}, <>). handle_data(tcp, FrameInfo, Data, Socket, WsHandleLoopPid, fast_tls) -> case fast_tls:recv_data(Socket, Data) of {ok, NewData} -> handle_data_int(FrameInfo, NewData, Socket, WsHandleLoopPid, fast_tls); {error, Error} -> {error, Error} end; handle_data(_, FrameInfo, Data, Socket, WsHandleLoopPid, SockMod) -> handle_data_int(FrameInfo, Data, Socket, WsHandleLoopPid, SockMod). handle_data_int(FrameInfo, Data, _Socket, WsHandleLoopPid, _SocketMode) -> {NewFrameInfo, Recv, Send} = process_frame(FrameInfo, Data), lists:foreach(fun (El) -> case El of pong -> WsHandleLoopPid ! pong; ping -> WsHandleLoopPid ! ping; _ -> WsHandleLoopPid ! {received, El} end end, Recv), {NewFrameInfo, Send}. websocket_close(Socket, WsHandleLoopPid, SocketMode, CloseCode) when CloseCode > 0 -> Frame = encode_frame(<>, 8), SocketMode:send(Socket, Frame), websocket_close(Socket, WsHandleLoopPid, SocketMode, 0); websocket_close(Socket, WsHandleLoopPid, SocketMode, _CloseCode) -> WsHandleLoopPid ! closed, SocketMode:close(Socket). ejabberd-18.01/src/mod_muc_admin.erl0000644000232200023220000012446313225664356017743 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_muc_admin.erl %%% Author : Badlop %%% Purpose : Tools for additional MUC administration %%% Created : 8 Sep 2007 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_muc_admin). -author('badlop@ono.com'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, depends/2, muc_online_rooms/1, muc_online_rooms_by_regex/2, muc_register_nick/3, muc_unregister_nick/2, create_room_with_opts/4, create_room/3, destroy_room/2, create_rooms_file/1, destroy_rooms_file/1, rooms_unused_list/2, rooms_unused_destroy/2, get_user_rooms/2, get_room_occupants/2, get_room_occupants_number/2, send_direct_invitation/5, change_room_option/4, get_room_options/2, set_room_affiliation/4, get_room_affiliations/2, web_menu_main/2, web_page_main/2, web_menu_host/3, subscribe_room/4, unsubscribe_room/2, get_subscribers/2, web_page_host/3, mod_opt_type/1, get_commands_spec/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_muc.hrl"). -include("mod_muc_room.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -include("ejabberd_commands.hrl"). %%---------------------------- %% gen_mod %%---------------------------- start(Host, _Opts) -> ejabberd_commands:register_commands(get_commands_spec()), ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50), ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, web_page_host, 50). stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> ejabberd_commands:unregister_commands(get_commands_spec()); true -> ok end, ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50), ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50), ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, web_page_host, 50). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> [{mod_muc, hard}]. %%% %%% Register commands %%% get_commands_spec() -> [ #ejabberd_commands{name = muc_online_rooms, tags = [muc], desc = "List existing rooms ('global' to get all vhosts)", policy = admin, module = ?MODULE, function = muc_online_rooms, args_desc = ["Server domain where the MUC service is, or 'global' for all"], args_example = ["example.com"], result_desc = "List of rooms", result_example = ["room1@muc.example.com", "room2@muc.example.com"], args = [{host, binary}], result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = muc_online_rooms_by_regex, tags = [muc], desc = "List existing rooms ('global' to get all vhosts) by regex", policy = admin, module = ?MODULE, function = muc_online_rooms_by_regex, args_desc = ["Server domain where the MUC service is, or 'global' for all", "Regex pattern for room name"], args_example = ["example.com", "^prefix"], result_desc = "List of rooms with summary", result_example = [{"room1@muc.example.com", "true", 10}, {"room2@muc.example.com", "false", 10}], args = [{host, binary}, {regex, binary}], result = {rooms, {list, {room, {tuple, [{jid, string}, {public, string}, {participants, integer} ]}}}}}, #ejabberd_commands{name = muc_register_nick, tags = [muc], desc = "Register a nick to a User JID in the MUC service of a server", module = ?MODULE, function = muc_register_nick, args_desc = ["Nick", "User JID", "Server Host"], args_example = [<<"Tim">>, <<"tim@example.org">>, <<"example.org">>], args = [{nick, binary}, {jid, binary}, {serverhost, binary}], result = {res, rescode}}, #ejabberd_commands{name = muc_unregister_nick, tags = [muc], desc = "Unregister the nick registered by that account in the MUC service", module = ?MODULE, function = muc_unregister_nick, args_desc = ["User JID", "MUC service"], args_example = [<<"tim@example.org">>, <<"example.org">>], args = [{jid, binary}, {serverhost, binary}], result = {res, rescode}}, #ejabberd_commands{name = create_room, tags = [muc_room], desc = "Create a MUC room name@service in host", module = ?MODULE, function = create_room, args_desc = ["Room name", "MUC service", "Server host"], args_example = ["room1", "muc.example.com", "example.com"], args = [{name, binary}, {service, binary}, {host, binary}], result = {res, rescode}}, #ejabberd_commands{name = destroy_room, tags = [muc_room], desc = "Destroy a MUC room", module = ?MODULE, function = destroy_room, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], args = [{name, binary}, {service, binary}], result = {res, rescode}}, #ejabberd_commands{name = create_rooms_file, tags = [muc], desc = "Create the rooms indicated in file", longdesc = "Provide one room JID per line. Rooms will be created after restart.", module = ?MODULE, function = create_rooms_file, args_desc = ["Path to the text file with one room JID per line"], args_example = ["/home/ejabberd/rooms.txt"], args = [{file, string}], result = {res, rescode}}, #ejabberd_commands{name = create_room_with_opts, tags = [muc_room], desc = "Create a MUC room name@service in host with given options", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], args_example = ["room1", "muc.example.com", "localhost", [{"members_only","true"}]], args = [{name, binary}, {service, binary}, {host, binary}, {options, {list, {option, {tuple, [{name, binary}, {value, binary} ]}} }}], result = {res, rescode}}, #ejabberd_commands{name = destroy_rooms_file, tags = [muc], desc = "Destroy the rooms indicated in file", longdesc = "Provide one room JID per line.", module = ?MODULE, function = destroy_rooms_file, args_desc = ["Path to the text file with one room JID per line"], args_example = ["/home/ejabberd/rooms.txt"], args = [{file, string}], result = {res, rescode}}, #ejabberd_commands{name = rooms_unused_list, tags = [muc], desc = "List the rooms that are unused for many days in host", module = ?MODULE, function = rooms_unused_list, args_desc = ["Server host", "Number of days"], args_example = ["example.com", 31], result_desc = "List of unused rooms", result_example = ["room1@muc.example.com", "room2@muc.example.com"], args = [{host, binary}, {days, integer}], result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = rooms_unused_destroy, tags = [muc], desc = "Destroy the rooms that are unused for many days in host", module = ?MODULE, function = rooms_unused_destroy, args_desc = ["Server host", "Number of days"], args_example = ["example.com", 31], result_desc = "List of unused rooms that has been destroyed", result_example = ["room1@muc.example.com", "room2@muc.example.com"], args = [{host, binary}, {days, integer}], result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = get_user_rooms, tags = [muc], desc = "Get the list of rooms where this user is occupant", module = ?MODULE, function = get_user_rooms, args_desc = ["Username", "Server host"], args_example = ["tom", "example.com"], result_example = ["room1@muc.example.com", "room2@muc.example.com"], args = [{user, binary}, {host, binary}], result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = get_room_occupants, tags = [muc_room], desc = "Get the list of occupants of a MUC room", module = ?MODULE, function = get_room_occupants, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], result_desc = "The list of occupants with JID, nick and affiliation", result_example = [{"user1@example.com/psi", "User 1", "owner"}], args = [{name, binary}, {service, binary}], result = {occupants, {list, {occupant, {tuple, [{jid, string}, {nick, string}, {role, string} ]}} }}}, #ejabberd_commands{name = get_room_occupants_number, tags = [muc_room], desc = "Get the number of occupants of a MUC room", module = ?MODULE, function = get_room_occupants_number, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], result_desc = "Number of room occupants", result_example = 7, args = [{name, binary}, {service, binary}], result = {occupants, integer}}, #ejabberd_commands{name = send_direct_invitation, tags = [muc_room], desc = "Send a direct invitation to several destinations", longdesc = "Password and Message can also be: none. Users JIDs are separated with : ", module = ?MODULE, function = send_direct_invitation, args_desc = ["Room name", "MUC service", "Password, or none", "Reason text, or none", "Users JIDs separated with : characters"], args_example = [<<"room1">>, <<"muc.example.com">>, <<>>, <<"Check this out!">>, "user2@localhost:user3@example.com"], args = [{name, binary}, {service, binary}, {password, binary}, {reason, binary}, {users, binary}], result = {res, rescode}}, #ejabberd_commands{name = change_room_option, tags = [muc_room], desc = "Change an option in a MUC room", module = ?MODULE, function = change_room_option, args_desc = ["Room name", "MUC service", "Option name", "Value to assign"], args_example = ["room1", "muc.example.com", "members_only", "true"], args = [{name, binary}, {service, binary}, {option, binary}, {value, binary}], result = {res, rescode}}, #ejabberd_commands{name = get_room_options, tags = [muc_room], desc = "Get options from a MUC room", module = ?MODULE, function = get_room_options, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], result_desc = "List of room options tuples with name and value", result_example = [{"members_only", "true"}], args = [{name, binary}, {service, binary}], result = {options, {list, {option, {tuple, [{name, string}, {value, string} ]}} }}}, #ejabberd_commands{name = subscribe_room, tags = [muc_room], desc = "Subscribe to a MUC conference", module = ?MODULE, function = subscribe_room, args_desc = ["Full JID, including some resource", "a user's nick", "the room to subscribe", "nodes separated by commas: ,"], args_example = ["tom@localhost/dummy", "Tom", "room1@conference.localhost", "urn:xmpp:mucsub:nodes:messages,urn:xmpp:mucsub:nodes:affiliations"], result_desc = "The list of nodes that has subscribed", result_example = ["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"], args = [{user, binary}, {nick, binary}, {room, binary}, {nodes, binary}], result = {nodes, {list, {node, string}}}}, #ejabberd_commands{name = unsubscribe_room, tags = [muc_room], desc = "Unsubscribe from a MUC conference", module = ?MODULE, function = unsubscribe_room, args_desc = ["User JID", "the room to subscribe"], args_example = ["tom@localhost", "room1@conference.localhost"], args = [{user, binary}, {room, binary}], result = {res, rescode}}, #ejabberd_commands{name = get_subscribers, tags = [muc_room], desc = "List subscribers of a MUC conference", module = ?MODULE, function = get_subscribers, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], result_desc = "The list of users that are subscribed to that room", result_example = ["user2@example.com", "user3@example.com"], args = [{name, binary}, {service, binary}], result = {subscribers, {list, {jid, string}}}}, #ejabberd_commands{name = set_room_affiliation, tags = [muc_room], desc = "Change an affiliation in a MUC room", module = ?MODULE, function = set_room_affiliation, args_desc = ["Room name", "MUC service", "User JID", "Affiliation to set"], args_example = ["room1", "muc.example.com", "user2@example.com", "member"], args = [{name, binary}, {service, binary}, {jid, binary}, {affiliation, binary}], result = {res, rescode}}, #ejabberd_commands{name = get_room_affiliations, tags = [muc_room], desc = "Get the list of affiliations of a MUC room", module = ?MODULE, function = get_room_affiliations, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], result_desc = "The list of affiliations with username, domain, affiliation and reason", result_example = [{"user1", "example.com", member, "member"}], args = [{name, binary}, {service, binary}], result = {affiliations, {list, {affiliation, {tuple, [{username, string}, {domain, string}, {affiliation, atom}, {reason, string} ]}} }}} ]. %%% %%% ejabberd commands %%% muc_online_rooms(ServerHost) -> Hosts = find_hosts(ServerHost), lists:flatmap( fun(Host) -> [<> || {Name, _, _} <- mod_muc:get_online_rooms(Host)] end, Hosts). muc_online_rooms_by_regex(ServerHost, Regex) -> {_, P} = re:compile(Regex), Hosts = find_hosts(ServerHost), lists:flatmap( fun(Host) -> [build_summary_room(Name, RoomHost, Pid) || {Name, RoomHost, Pid} <- mod_muc:get_online_rooms(Host), is_name_match(Name, P)] end, Hosts). is_name_match(Name, P) -> case re:run(Name, P) of {match, _} -> true; nomatch -> false end. build_summary_room(Name, Host, Pid) -> C = get_room_config(Pid), Public = C#config.public, S = get_room_state(Pid), Participants = dict:size(S#state.users), {<>, misc:atom_to_binary(Public), Participants }. muc_register_nick(Nick, FromBinary, ServerHost) -> Host = find_host(ServerHost), From = jid:decode(FromBinary), Lang = <<"en">>, mod_muc:iq_set_register_info(ServerHost, Host, From, Nick, Lang). muc_unregister_nick(FromBinary, ServerHost) -> muc_register_nick(<<"">>, FromBinary, ServerHost). get_user_rooms(User, Server) -> lists:flatmap( fun(ServerHost) -> case gen_mod:is_loaded(ServerHost, mod_muc) of true -> Rooms = mod_muc:get_online_rooms_by_user( ServerHost, jid:nodeprep(User), jid:nodeprep(Server)), [<> || {Name, Host} <- Rooms]; false -> [] end end, ?MYHOSTS). %%---------------------------- %% Ad-hoc commands %%---------------------------- %%---------------------------- %% Web Admin %%---------------------------- %%--------------- %% Web Admin Menu web_menu_main(Acc, Lang) -> Acc ++ [{<<"muc">>, ?T(<<"Multi-User Chat">>)}]. web_menu_host(Acc, _Host, Lang) -> Acc ++ [{<<"muc">>, ?T(<<"Multi-User Chat">>)}]. %%--------------- %% Web Admin Page -define(TDTD(L, N), ?XE(<<"tr">>, [?XCT(<<"td">>, L), ?XC(<<"td">>, integer_to_binary(N)) ])). web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) -> OnlineRoomsNumber = lists:foldl( fun(Host, Acc) -> Acc + mod_muc:count_online_rooms(Host) end, 0, find_hosts(global)), Res = [?XCT(<<"h1">>, <<"Multi-User Chat">>), ?XCT(<<"h3">>, <<"Statistics">>), ?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?TDTD(<<"Total rooms">>, OnlineRoomsNumber), ?TDTD(<<"Permanent rooms">>, mnesia:table_info(muc_room, size)), ?TDTD(<<"Registered nicknames">>, mnesia:table_info(muc_registered, size)) ]) ]), ?XE(<<"ul">>, [?LI([?ACT(<<"rooms">>, <<"List of rooms">>)])]) ], {stop, Res}; web_page_main(_, #request{path=[<<"muc">>, <<"rooms">>], q = Q, lang = Lang} = _Request) -> Sort_query = get_sort_query(Q), Res = make_rooms_page(global, Lang, Sort_query), {stop, Res}; web_page_main(Acc, _) -> Acc. web_page_host(_, Host, #request{path = [<<"muc">>], q = Q, lang = Lang} = _Request) -> Sort_query = get_sort_query(Q), Res = make_rooms_page(Host, Lang, Sort_query), {stop, Res}; web_page_host(Acc, _, _) -> Acc. %% Returns: {normal | reverse, Integer} get_sort_query(Q) -> case catch get_sort_query2(Q) of {ok, Res} -> Res; _ -> {normal, 1} end. get_sort_query2(Q) -> {value, {_, String}} = lists:keysearch(<<"sort">>, 1, Q), Integer = binary_to_integer(String), case Integer >= 0 of true -> {ok, {normal, Integer}}; false -> {ok, {reverse, abs(Integer)}} end. make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) -> Rooms_names = get_rooms(Host), Rooms_infos = build_info_rooms(Rooms_names), Rooms_sorted = sort_rooms(Sort_direction, Sort_column, Rooms_infos), Rooms_prepared = prepare_rooms_infos(Rooms_sorted), TList = lists:map( fun(Room) -> ?XE(<<"tr">>, [?XC(<<"td">>, E) || E <- Room]) end, Rooms_prepared), Titles = [<<"Jabber ID">>, <<"# participants">>, <<"Last message">>, <<"Public">>, <<"Persistent">>, <<"Logging">>, <<"Just created">>, <<"Room title">>], {Titles_TR, _} = lists:mapfoldl( fun(Title, Num_column) -> NCS = integer_to_binary(Num_column), TD = ?XE(<<"td">>, [?CT(Title), ?C(<<" ">>), ?AC(<<"?sort=", NCS/binary>>, <<"<">>), ?C(<<" ">>), ?AC(<<"?sort=-", NCS/binary>>, <<">">>)]), {TD, Num_column+1} end, 1, Titles), [?XCT(<<"h1">>, <<"Multi-User Chat">>), ?XCT(<<"h2">>, <<"Chatrooms">>), ?XE(<<"table">>, [?XE(<<"thead">>, [?XE(<<"tr">>, Titles_TR)] ), ?XE(<<"tbody">>, TList) ] ) ]. sort_rooms(Direction, Column, Rooms) -> Rooms2 = lists:keysort(Column, Rooms), case Direction of normal -> Rooms2; reverse -> lists:reverse(Rooms2) end. build_info_rooms(Rooms) -> [build_info_room(Room) || Room <- Rooms]. build_info_room({Name, Host, Pid}) -> C = get_room_config(Pid), Title = C#config.title, Public = C#config.public, Persistent = C#config.persistent, Logging = C#config.logging, S = get_room_state(Pid), Just_created = S#state.just_created, Num_participants = length(dict:fetch_keys(S#state.users)), History = (S#state.history)#lqueue.queue, Ts_last_message = case p1_queue:is_empty(History) of true -> <<"A long time ago">>; false -> Last_message1 = get_queue_last(History), {_, _, _, Ts_last, _} = Last_message1, xmpp_util:encode_timestamp(Ts_last) end, {<>, Num_participants, Ts_last_message, Public, Persistent, Logging, Just_created, Title}. get_queue_last(Queue) -> List = p1_queue:to_list(Queue), lists:last(List). prepare_rooms_infos(Rooms) -> [prepare_room_info(Room) || Room <- Rooms]. prepare_room_info(Room_info) -> {NameHost, Num_participants, Ts_last_message, Public, Persistent, Logging, Just_created, Title} = Room_info, [NameHost, integer_to_binary(Num_participants), Ts_last_message, misc:atom_to_binary(Public), misc:atom_to_binary(Persistent), misc:atom_to_binary(Logging), misc:atom_to_binary(Just_created), Title]. %%---------------------------- %% Create/Delete Room %%---------------------------- %% @spec (Name::binary(), Host::binary(), ServerHost::binary()) -> %% ok | error %% @doc Create a room immediately with the default options. create_room(Name1, Host1, ServerHost) -> create_room_with_opts(Name1, Host1, ServerHost, []), change_room_option(Name1, Host1, <<"persistent">>, <<"true">>). create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) -> true = (error /= (Name = jid:nodeprep(Name1))), true = (error /= (Host = jid:nodeprep(Host1))), %% Get the default room options from the muc configuration DefRoomOpts = gen_mod:get_module_opt(ServerHost, mod_muc, default_room_options, []), %% Change default room options as required FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts], RoomOpts = lists:ukeymerge(1, lists:keysort(1, FormattedRoomOpts), lists:keysort(1, DefRoomOpts)), %% Store the room on the server, it is not started yet though at this point mod_muc:store_room(ServerHost, Host, Name, RoomOpts), %% Get all remaining mod_muc parameters that might be utilized Access = gen_mod:get_module_opt(ServerHost, mod_muc, access, all), AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create, all), AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin, none), AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent, all), HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, 20), RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper, none), QueueType = gen_mod:get_module_opt(ServerHost, mod_muc, queue_type, ejabberd_config:default_queue_type(ServerHost)), %% If the room does not exist yet in the muc_online_room case mod_muc:find_online_room(Name, Host) of error -> %% Start the room {ok, Pid} = mod_muc_room:start( Host, ServerHost, {Access, AcCreate, AcAdmin, AcPer}, Name, HistorySize, RoomShaper, RoomOpts, QueueType), mod_muc:register_online_room(Name, Host, Pid), ok; {ok, _} -> error end. %% Create the room only in the database. %% It is required to restart the MUC service for the room to appear. muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) -> io:format("Creating room ~s@~s~n", [Name, Host]), mod_muc:store_room(ServerHost, Host, Name, DefRoomOpts). %% @spec (Name::binary(), Host::binary()) -> %% ok | {error, room_not_exists} %% @doc Destroy the room immediately. %% If the room has participants, they are not notified that the room was destroyed; %% they will notice when they try to chat and receive an error that the room doesn't exist. destroy_room(Name, Service) -> case mod_muc:find_online_room(Name, Service) of {ok, Pid} -> p1_fsm:send_all_state_event(Pid, destroy), ok; error -> error end. destroy_room({N, H, SH}) -> io:format("Destroying room: ~s@~s - vhost: ~s~n", [N, H, SH]), destroy_room(N, H). %%---------------------------- %% Destroy Rooms in File %%---------------------------- %% The format of the file is: one chatroom JID per line %% The file encoding must be UTF-8 destroy_rooms_file(Filename) -> {ok, F} = file:open(Filename, [read]), RJID = read_room(F), Rooms = read_rooms(F, RJID, []), file:close(F), [destroy_room(A) || A <- Rooms], ok. read_rooms(_F, eof, L) -> L; read_rooms(F, no_room, L) -> RJID2 = read_room(F), read_rooms(F, RJID2, L); read_rooms(F, RJID, L) -> RJID2 = read_room(F), read_rooms(F, RJID2, [RJID | L]). read_room(F) -> case io:get_line(F, "") of eof -> eof; String -> case io_lib:fread("~s", String) of {ok, [RoomJID], _} -> split_roomjid(list_to_binary(RoomJID)); {error, What} -> io:format("Parse error: what: ~p~non the line: ~p~n~n", [What, String]) end end. %% This function is quite rudimentary %% and may not be accurate split_roomjid(RoomJID) -> split_roomjid2(binary:split(RoomJID, <<"@">>)). split_roomjid2([Name, Host]) -> [_MUC_service_name, ServerHost] = binary:split(Host, <<".">>), {Name, Host, ServerHost}; split_roomjid2(_) -> no_room. %%---------------------------- %% Create Rooms in File %%---------------------------- create_rooms_file(Filename) -> {ok, F} = file:open(Filename, [read]), RJID = read_room(F), Rooms = read_rooms(F, RJID, []), file:close(F), %% Read the default room options defined for the first virtual host DefRoomOpts = gen_mod:get_module_opt(?MYNAME, mod_muc, default_room_options, []), [muc_create_room(?MYNAME, A, DefRoomOpts) || A <- Rooms], ok. %%---------------------------- %% List/Delete Unused Rooms %%---------------------------- %%--------------- %% Control rooms_unused_list(ServerHost, Days) -> rooms_unused_report(list, ServerHost, Days). rooms_unused_destroy(ServerHost, Days) -> rooms_unused_report(destroy, ServerHost, Days). rooms_unused_report(Action, ServerHost, Days) -> {NA, NP, RP} = muc_unused(Action, ServerHost, Days), io:format("Unused rooms: ~p out of ~p~n", [NP, NA]), [<> || {R, H, _P} <- RP]. muc_unused(Action, ServerHost, Last_allowed) -> %% Get all required info about all existing rooms Rooms_all = get_rooms(ServerHost), %% Decide which ones pass the requirements Rooms_pass = decide_rooms(Rooms_all, Last_allowed), Num_rooms_all = length(Rooms_all), Num_rooms_pass = length(Rooms_pass), %% Perform the desired action for matching rooms act_on_rooms(Action, Rooms_pass, ServerHost), {Num_rooms_all, Num_rooms_pass, Rooms_pass}. %%--------------- %% Get info get_rooms(ServerHost) -> Hosts = find_hosts(ServerHost), lists:flatmap( fun(Host) -> mod_muc:get_online_rooms(Host) end, Hosts). get_room_config(Room_pid) -> {ok, R} = p1_fsm:sync_send_all_state_event(Room_pid, get_config), R. get_room_state(Room_pid) -> {ok, R} = p1_fsm:sync_send_all_state_event(Room_pid, get_state), R. %%--------------- %% Decide decide_rooms(Rooms, Last_allowed) -> Decide = fun(R) -> decide_room(R, Last_allowed) end, lists:filter(Decide, Rooms). decide_room({_Room_name, _Host, Room_pid}, Last_allowed) -> C = get_room_config(Room_pid), Persistent = C#config.persistent, S = get_room_state(Room_pid), Just_created = S#state.just_created, Room_users = S#state.users, Num_users = length(?DICT:to_list(Room_users)), History = (S#state.history)#lqueue.queue, Ts_now = calendar:universal_time(), Ts_uptime = uptime_seconds(), {Has_hist, Last} = case p1_queue:is_empty(History) of true -> {false, Ts_uptime}; false -> Last_message = get_queue_last(History), Ts_last = calendar:now_to_universal_time( element(4, Last_message)), Ts_diff = calendar:datetime_to_gregorian_seconds(Ts_now) - calendar:datetime_to_gregorian_seconds(Ts_last), {true, Ts_diff} end, case {Persistent, Just_created, Num_users, Has_hist, seconds_to_days(Last)} of {_true, false, 0, _, Last_days} when Last_days >= Last_allowed -> true; _ -> false end. seconds_to_days(S) -> S div (60*60*24). %%--------------- %% Act act_on_rooms(Action, Rooms, ServerHost) -> ServerHosts = [ {A, find_host(A)} || A <- ?MYHOSTS ], Delete = fun({_N, H, _Pid} = Room) -> SH = case ServerHost of global -> find_serverhost(H, ServerHosts); O -> O end, act_on_room(Action, Room, SH) end, lists:foreach(Delete, Rooms). find_serverhost(Host, ServerHosts) -> {value, {ServerHost, Host}} = lists:keysearch(Host, 2, ServerHosts), ServerHost. act_on_room(destroy, {N, H, Pid}, SH) -> p1_fsm:send_all_state_event( Pid, {destroy, <<"Room destroyed by rooms_unused_destroy.">>}), mod_muc:room_destroyed(H, N, Pid, SH), mod_muc:forget_room(SH, H, N); act_on_room(list, _, _) -> ok. %%---------------------------- %% Change Room Option %%---------------------------- get_room_occupants(Room, Host) -> case get_room_pid(Room, Host) of room_not_found -> throw({error, room_not_found}); Pid -> get_room_occupants(Pid) end. get_room_occupants(Pid) -> S = get_room_state(Pid), lists:map( fun({_LJID, Info}) -> {jid:encode(Info#user.jid), Info#user.nick, atom_to_list(Info#user.role)} end, dict:to_list(S#state.users)). get_room_occupants_number(Room, Host) -> case get_room_pid(Room, Host) of room_not_found -> throw({error, room_not_found}); Pid -> S = get_room_state(Pid), dict:size(S#state.users) end. %%---------------------------- %% Send Direct Invitation %%---------------------------- %% http://xmpp.org/extensions/xep-0249.html send_direct_invitation(RoomName, RoomService, Password, Reason, UsersString) -> RoomJid = jid:make(RoomName, RoomService), XmlEl = build_invitation(Password, Reason, RoomJid), Users = get_users_to_invite(RoomJid, UsersString), [send_direct_invitation(RoomJid, UserJid, XmlEl) || UserJid <- Users], timer:sleep(1000), ok. get_users_to_invite(RoomJid, UsersString) -> UsersStrings = binary:split(UsersString, <<":">>, [global]), OccupantsTuples = get_room_occupants(RoomJid#jid.luser, RoomJid#jid.lserver), OccupantsJids = [jid:decode(JidString) || {JidString, _Nick, _} <- OccupantsTuples], lists:filtermap( fun(UserString) -> UserJid = jid:decode(UserString), Val = lists:all(fun(OccupantJid) -> UserJid#jid.luser /= OccupantJid#jid.luser orelse UserJid#jid.lserver /= OccupantJid#jid.lserver end, OccupantsJids), case {UserJid#jid.luser, Val} of {<<>>, _} -> false; {_, true} -> {true, UserJid}; _ -> false end end, UsersStrings). build_invitation(Password, Reason, RoomJid) -> Invite = #x_conference{jid = RoomJid, password = case Password of <<"none">> -> <<>>; _ -> Password end, reason = case Reason of <<"none">> -> <<>>; _ -> Reason end}, #message{sub_els = [Invite]}. send_direct_invitation(FromJid, UserJid, Msg) -> ejabberd_router:route(xmpp:set_from_to(Msg, FromJid, UserJid)). %%---------------------------- %% Change Room Option %%---------------------------- %% @spec(Name::string(), Service::string(), Option::string(), Value) -> ok %% Value = atom() | integer() | string() %% @doc Change an option in an existing room. %% Requires the name of the room, the MUC service where it exists, %% the option to change (for example title or max_users), %% and the value to assign to the new option. %% For example: %% change_room_option(<<"testroom">>, <<"conference.localhost">>, <<"title">>, <<"Test Room">>) change_room_option(Name, Service, OptionString, ValueString) -> case get_room_pid(Name, Service) of room_not_found -> room_not_found; Pid -> {Option, Value} = format_room_option(OptionString, ValueString), Config = get_room_config(Pid), Config2 = change_option(Option, Value, Config), {ok, _} = p1_fsm:sync_send_all_state_event(Pid, {change_config, Config2}), ok end. format_room_option(OptionString, ValueString) -> Option = misc:binary_to_atom(OptionString), Value = case Option of title -> ValueString; description -> ValueString; password -> ValueString; subject ->ValueString; subject_author ->ValueString; presence_broadcast ->misc:expr_to_term(ValueString); max_users -> binary_to_integer(ValueString); _ -> misc:binary_to_atom(ValueString) end, {Option, Value}. %% @doc Get the Pid of an existing MUC room, or 'room_not_found'. get_room_pid(Name, Service) -> case mod_muc:find_online_room(Name, Service) of error -> room_not_found; {ok, Pid} -> Pid end. %% It is required to put explicitely all the options because %% the record elements are replaced at compile time. %% So, this can't be parametrized. change_option(Option, Value, Config) -> case Option of allow_change_subj -> Config#config{allow_change_subj = Value}; allow_private_messages -> Config#config{allow_private_messages = Value}; allow_private_messages_from_visitors -> Config#config{allow_private_messages_from_visitors = Value}; allow_query_users -> Config#config{allow_query_users = Value}; allow_subscription -> Config#config{allow_subscription = Value}; allow_user_invites -> Config#config{allow_user_invites = Value}; allow_visitor_nickchange -> Config#config{allow_visitor_nickchange = Value}; allow_visitor_status -> Config#config{allow_visitor_status = Value}; allow_voice_requests -> Config#config{allow_voice_requests = Value}; anonymous -> Config#config{anonymous = Value}; captcha_protected -> Config#config{captcha_protected = Value}; description -> Config#config{description = Value}; logging -> Config#config{logging = Value}; mam -> Config#config{mam = Value}; max_users -> Config#config{max_users = Value}; members_by_default -> Config#config{members_by_default = Value}; members_only -> Config#config{members_only = Value}; moderated -> Config#config{moderated = Value}; password -> Config#config{password = Value}; password_protected -> Config#config{password_protected = Value}; persistent -> Config#config{persistent = Value}; presence_broadcast -> Config#config{presence_broadcast = Value}; public -> Config#config{public = Value}; public_list -> Config#config{public_list = Value}; title -> Config#config{title = Value}; vcard -> Config#config{vcard = Value}; voice_request_min_interval -> Config#config{voice_request_min_interval = Value} end. %%---------------------------- %% Get Room Options %%---------------------------- get_room_options(Name, Service) -> case get_room_pid(Name, Service) of room_not_found -> []; Pid -> get_room_options(Pid) end. get_room_options(Pid) -> Config = get_room_config(Pid), get_options(Config). get_options(Config) -> Fields = [misc:atom_to_binary(Field) || Field <- record_info(fields, config)], [config | ValuesRaw] = tuple_to_list(Config), Values = lists:map(fun(V) when is_atom(V) -> misc:atom_to_binary(V); (V) when is_integer(V) -> integer_to_binary(V); (V) when is_tuple(V); is_list(V) -> list_to_binary(hd(io_lib:format("~w", [V]))); (V) -> V end, ValuesRaw), lists:zip(Fields, Values). %%---------------------------- %% Get Room Affiliations %%---------------------------- %% @spec(Name::binary(), Service::binary()) -> %% [{JID::string(), Domain::string(), Role::string(), Reason::string()}] %% @doc Get the affiliations of the room Name@Service. get_room_affiliations(Name, Service) -> case mod_muc:find_online_room(Name, Service) of {ok, Pid} -> %% Get the PID of the online room, then request its state {ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state), Affiliations = ?DICT:to_list(StateData#state.affiliations), lists:map( fun({{Uname, Domain, _Res}, {Aff, Reason}}) when is_atom(Aff)-> {Uname, Domain, Aff, Reason}; ({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)-> {Uname, Domain, Aff, <<>>} end, Affiliations); error -> throw({error, "The room does not exist."}) end. %%---------------------------- %% Change Room Affiliation %%---------------------------- %% @spec(Name, Service, JID, AffiliationString) -> ok | {error, Error} %% Name = binary() %% Service = binary() %% JID = binary() %% AffiliationString = "outcast" | "none" | "member" | "admin" | "owner" %% @doc Set the affiliation of JID in the room Name@Service. %% If the affiliation is 'none', the action is to remove, %% In any other case the action will be to create the affiliation. set_room_affiliation(Name, Service, JID, AffiliationString) -> Affiliation = misc:binary_to_atom(AffiliationString), case mod_muc:find_online_room(Name, Service) of {ok, Pid} -> %% Get the PID for the online room so we can get the state of the room {ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, {process_item_change, {jid:decode(JID), affiliation, Affiliation, <<"">>}, undefined}), mod_muc:store_room(StateData#state.server_host, StateData#state.host, StateData#state.room, make_opts(StateData)), ok; error -> error end. %%% %%% MUC Subscription %%% subscribe_room(_User, Nick, _Room, _Nodes) when Nick == <<"">> -> throw({error, "Nickname must be set"}); subscribe_room(User, Nick, Room, Nodes) -> NodeList = re:split(Nodes, "\\h*,\\h*"), try jid:decode(Room) of #jid{luser = Name, lserver = Host} when Name /= <<"">> -> try jid:decode(User) of #jid{lresource = <<"">>} -> throw({error, "User's JID should have a resource"}); UserJID -> case get_room_pid(Name, Host) of Pid when is_pid(Pid) -> case p1_fsm:sync_send_all_state_event( Pid, {muc_subscribe, UserJID, Nick, NodeList}) of {ok, SubscribedNodes} -> SubscribedNodes; {error, Reason} -> throw({error, binary_to_list(Reason)}) end; _ -> throw({error, "The room does not exist"}) end catch _:{bad_jid, _} -> throw({error, "Malformed user JID"}) end; _ -> throw({error, "Malformed room JID"}) catch _:{bad_jid, _} -> throw({error, "Malformed room JID"}) end. unsubscribe_room(User, Room) -> try jid:decode(Room) of #jid{luser = Name, lserver = Host} when Name /= <<"">> -> try jid:decode(User) of UserJID -> case get_room_pid(Name, Host) of Pid when is_pid(Pid) -> case p1_fsm:sync_send_all_state_event( Pid, {muc_unsubscribe, UserJID}) of ok -> ok; {error, Reason} -> throw({error, binary_to_list(Reason)}) end; _ -> throw({error, "The room does not exist"}) end catch _:{bad_jid, _} -> throw({error, "Malformed user JID"}) end; _ -> throw({error, "Malformed room JID"}) catch _:{bad_jid, _} -> throw({error, "Malformed room JID"}) end. get_subscribers(Name, Host) -> case get_room_pid(Name, Host) of Pid when is_pid(Pid) -> {ok, JIDList} = p1_fsm:sync_send_all_state_event(Pid, get_subscribers), [jid:encode(jid:remove_resource(J)) || J <- JIDList]; _ -> throw({error, "The room does not exist"}) end. %% Copied from mod_muc_room.erl get_config_opt_name(Pos) -> Fs = [config|record_info(fields, config)], lists:nth(Pos, Fs). -define(MAKE_CONFIG_OPT(Opt), {get_config_opt_name(Opt), element(Opt, Config)}). make_opts(StateData) -> Config = StateData#state.config, Subscribers = (?DICT):fold( fun(_LJID, Sub, Acc) -> [{Sub#subscriber.jid, Sub#subscriber.nick, Sub#subscriber.nodes}|Acc] end, [], StateData#state.subscribers), [?MAKE_CONFIG_OPT(#config.title), ?MAKE_CONFIG_OPT(#config.description), ?MAKE_CONFIG_OPT(#config.allow_change_subj), ?MAKE_CONFIG_OPT(#config.allow_query_users), ?MAKE_CONFIG_OPT(#config.allow_private_messages), ?MAKE_CONFIG_OPT(#config.allow_private_messages_from_visitors), ?MAKE_CONFIG_OPT(#config.allow_visitor_status), ?MAKE_CONFIG_OPT(#config.allow_visitor_nickchange), ?MAKE_CONFIG_OPT(#config.public), ?MAKE_CONFIG_OPT(#config.public_list), ?MAKE_CONFIG_OPT(#config.persistent), ?MAKE_CONFIG_OPT(#config.moderated), ?MAKE_CONFIG_OPT(#config.members_by_default), ?MAKE_CONFIG_OPT(#config.members_only), ?MAKE_CONFIG_OPT(#config.allow_user_invites), ?MAKE_CONFIG_OPT(#config.password_protected), ?MAKE_CONFIG_OPT(#config.captcha_protected), ?MAKE_CONFIG_OPT(#config.password), ?MAKE_CONFIG_OPT(#config.anonymous), ?MAKE_CONFIG_OPT(#config.logging), ?MAKE_CONFIG_OPT(#config.max_users), ?MAKE_CONFIG_OPT(#config.allow_voice_requests), ?MAKE_CONFIG_OPT(#config.allow_subscription), ?MAKE_CONFIG_OPT(#config.mam), ?MAKE_CONFIG_OPT(#config.presence_broadcast), ?MAKE_CONFIG_OPT(#config.voice_request_min_interval), ?MAKE_CONFIG_OPT(#config.vcard), {captcha_whitelist, (?SETS):to_list((StateData#state.config)#config.captcha_whitelist)}, {affiliations, (?DICT):to_list(StateData#state.affiliations)}, {subject, StateData#state.subject}, {subject_author, StateData#state.subject_author}, {subscribers, Subscribers}]. %%---------------------------- %% Utils %%---------------------------- uptime_seconds() -> trunc(element(1, erlang:statistics(wall_clock))/1000). find_host(global) -> global; find_host("global") -> global; find_host(<<"global">>) -> global; find_host(ServerHost) when is_list(ServerHost) -> find_host(list_to_binary(ServerHost)); find_host(ServerHost) -> gen_mod:get_module_opt_host(ServerHost, mod_muc, <<"conference.@HOST@">>). find_hosts(Global) when Global == global; Global == "global"; Global == <<"global">> -> lists:flatmap( fun(ServerHost) -> case gen_mod:is_loaded(ServerHost, mod_muc) of true -> [find_host(ServerHost)]; false -> [] end end, ?MYHOSTS); find_hosts(ServerHost) when is_list(ServerHost) -> find_hosts(list_to_binary(ServerHost)); find_hosts(ServerHost) -> case gen_mod:is_loaded(ServerHost, mod_muc) of true -> [find_host(ServerHost)]; false -> [] end. mod_opt_type(_) -> []. ejabberd-18.01/src/ejabberd_s2s_out.erl0000644000232200023220000004116113225664356020355 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 16 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_s2s_out). -behaviour(xmpp_stream_out). -behaviour(ejabberd_config). %% ejabberd_config callbacks -export([opt_type/1, transform_options/1]). %% xmpp_stream_out callbacks -export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1, connect_timeout/1, address_families/1, default_port/1, dns_retries/1, dns_timeout/1, handle_auth_success/2, handle_auth_failure/3, handle_packet/2, handle_stream_end/2, handle_stream_downgraded/2, handle_recv/3, handle_send/3, handle_cdata/2, handle_stream_established/1, handle_timeout/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% Hooks -export([process_auth_result/2, process_closed/2, handle_unexpected_info/2, handle_unexpected_cast/2, process_downgraded/2]). %% API -export([start/3, start_link/3, connect/1, close/1, close/2, stop/1, send/2, route/2, establish/1, update_state/2, host_up/1, host_down/1]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -type state() :: map(). -export_type([state/0]). %%%=================================================================== %%% API %%%=================================================================== start(From, To, Opts) -> case proplists:get_value(supervisor, Opts, true) of true -> case supervisor:start_child(ejabberd_s2s_out_sup, [From, To, Opts]) of {ok, undefined} -> ignore; Res -> Res end; _ -> xmpp_stream_out:start(?MODULE, [xmpp_socket, From, To, Opts], ejabberd_config:fsm_limit_opts([])) end. start_link(From, To, Opts) -> xmpp_stream_out:start_link(?MODULE, [xmpp_socket, From, To, Opts], ejabberd_config:fsm_limit_opts([])). -spec connect(pid()) -> ok. connect(Ref) -> xmpp_stream_out:connect(Ref). -spec close(pid()) -> ok; (state()) -> state(). close(Ref) -> xmpp_stream_out:close(Ref). -spec close(pid(), atom()) -> ok; (state(), atom()) -> state(). close(Ref, Reason) -> xmpp_stream_out:close(Ref, Reason). -spec stop(pid()) -> ok; (state()) -> no_return(). stop(Ref) -> xmpp_stream_out:stop(Ref). -spec send(pid(), xmpp_element()) -> ok; (state(), xmpp_element()) -> state(). send(Stream, Pkt) -> xmpp_stream_out:send(Stream, Pkt). -spec route(pid(), xmpp_element()) -> ok. route(Ref, Pkt) -> Ref ! {route, Pkt}, ok. -spec establish(state()) -> state(). establish(State) -> xmpp_stream_out:establish(State). -spec update_state(pid(), fun((state()) -> state()) | {module(), atom(), list()}) -> ok. update_state(Ref, Callback) -> xmpp_stream_out:cast(Ref, {update_state, Callback}). -spec host_up(binary()) -> ok. host_up(Host) -> ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, process_auth_result, 100), ejabberd_hooks:add(s2s_out_closed, Host, ?MODULE, process_closed, 100), ejabberd_hooks:add(s2s_out_handle_info, Host, ?MODULE, handle_unexpected_info, 100), ejabberd_hooks:add(s2s_out_handle_cast, Host, ?MODULE, handle_unexpected_cast, 100), ejabberd_hooks:add(s2s_out_downgraded, Host, ?MODULE, process_downgraded, 100). -spec host_down(binary()) -> ok. host_down(Host) -> ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE, process_auth_result, 100), ejabberd_hooks:delete(s2s_out_closed, Host, ?MODULE, process_closed, 100), ejabberd_hooks:delete(s2s_out_handle_info, Host, ?MODULE, handle_unexpected_info, 100), ejabberd_hooks:delete(s2s_out_handle_cast, Host, ?MODULE, handle_unexpected_cast, 100), ejabberd_hooks:delete(s2s_out_downgraded, Host, ?MODULE, process_downgraded, 100). %%%=================================================================== %%% Hooks %%%=================================================================== process_auth_result(#{server := LServer, remote_server := RServer} = State, {false, Reason}) -> Delay = get_delay(), ?INFO_MSG("Failed to establish outbound s2s connection ~s -> ~s: " "authentication failed; bouncing for ~p seconds", [LServer, RServer, Delay]), State1 = State#{on_route => bounce, stop_reason => Reason}, State2 = close(State1), State3 = bounce_queue(State2), xmpp_stream_out:set_timeout(State3, timer:seconds(Delay)); process_auth_result(State, true) -> State. process_closed(#{server := LServer, remote_server := RServer, on_route := send} = State, Reason) -> ?INFO_MSG("Closing outbound s2s connection ~s -> ~s: ~s", [LServer, RServer, format_error(Reason)]), stop(State); process_closed(#{server := LServer, remote_server := RServer} = State, Reason) -> Delay = get_delay(), ?INFO_MSG("Failed to establish outbound s2s connection ~s -> ~s: ~s; " "bouncing for ~p seconds", [LServer, RServer, format_error(Reason), Delay]), State1 = State#{on_route => bounce}, State2 = bounce_queue(State1), xmpp_stream_out:set_timeout(State2, timer:seconds(Delay)). handle_unexpected_info(State, Info) -> ?WARNING_MSG("got unexpected info: ~p", [Info]), State. handle_unexpected_cast(State, Msg) -> ?WARNING_MSG("got unexpected cast: ~p", [Msg]), State. process_downgraded(State, _StreamStart) -> send(State, xmpp:serr_unsupported_version()). %%%=================================================================== %%% xmpp_stream_out callbacks %%%=================================================================== tls_options(#{server := LServer}) -> ejabberd_s2s:tls_options(LServer, []). tls_required(#{server := LServer}) -> ejabberd_s2s:tls_required(LServer). tls_verify(#{server := LServer}) -> ejabberd_s2s:tls_verify(LServer). tls_enabled(#{server := LServer}) -> ejabberd_s2s:tls_enabled(LServer). connect_timeout(#{server := LServer}) -> ejabberd_config:get_option( {outgoing_s2s_timeout, LServer}, timer:seconds(10)). default_port(#{server := LServer}) -> ejabberd_config:get_option({outgoing_s2s_port, LServer}, 5269). address_families(#{server := LServer}) -> ejabberd_config:get_option( {outgoing_s2s_families, LServer}, [inet, inet6]). dns_retries(#{server := LServer}) -> ejabberd_config:get_option({s2s_dns_retries, LServer}, 2). dns_timeout(#{server := LServer}) -> ejabberd_config:get_option({s2s_dns_timeout, LServer}, timer:seconds(10)). handle_auth_success(Mech, #{socket := Socket, ip := IP, remote_server := RServer, server_host := ServerHost, server := LServer} = State) -> ?INFO_MSG("(~s) Accepted outbound s2s ~s authentication ~s -> ~s (~s)", [xmpp_socket:pp(Socket), Mech, LServer, RServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), ejabberd_hooks:run_fold(s2s_out_auth_result, ServerHost, State, [true]). handle_auth_failure(Mech, Reason, #{socket := Socket, ip := IP, remote_server := RServer, server_host := ServerHost, server := LServer} = State) -> ?INFO_MSG("(~s) Failed outbound s2s ~s authentication ~s -> ~s (~s): ~s", [xmpp_socket:pp(Socket), Mech, LServer, RServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP)), xmpp_stream_out:format_error(Reason)]), ejabberd_hooks:run_fold(s2s_out_auth_result, ServerHost, State, [{false, Reason}]). handle_packet(Pkt, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_packet, ServerHost, State, [Pkt]). handle_stream_end(Reason, #{server_host := ServerHost} = State) -> State1 = State#{stop_reason => Reason}, ejabberd_hooks:run_fold(s2s_out_closed, ServerHost, State1, [Reason]). handle_stream_downgraded(StreamStart, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_downgraded, ServerHost, State, [StreamStart]). handle_stream_established(State) -> State1 = State#{on_route => send}, State2 = resend_queue(State1), set_idle_timeout(State2). handle_cdata(Data, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_handle_cdata, ServerHost, State, [Data]). handle_recv(El, Pkt, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_handle_recv, ServerHost, State, [El, Pkt]). handle_send(El, Pkt, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_handle_send, ServerHost, State, [El, Pkt]). handle_timeout(#{on_route := Action} = State) -> case Action of bounce -> stop(State); _ -> send(State, xmpp:serr_connection_timeout()) end. init([#{server := LServer, remote_server := RServer} = State, Opts]) -> ServerHost = ejabberd_router:host_of_route(LServer), QueueType = ejabberd_s2s:queue_type(LServer), QueueLimit = case lists:keyfind( max_queue, 1, ejabberd_config:fsm_limit_opts([])) of {_, N} -> N; false -> unlimited end, State1 = State#{on_route => queue, queue => p1_queue:new(QueueType, QueueLimit), xmlns => ?NS_SERVER, lang => ?MYLANG, server_host => ServerHost, shaper => none}, ?INFO_MSG("Outbound s2s connection started: ~s -> ~s", [LServer, RServer]), ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State1}, [Opts]). handle_call(Request, From, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]). handle_cast({update_state, Fun}, State) -> case Fun of {M, F, A} -> erlang:apply(M, F, [State|A]); _ when is_function(Fun) -> Fun(State) end; handle_cast(Msg, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_handle_cast, ServerHost, State, [Msg]). handle_info({route, Pkt}, #{queue := Q, on_route := Action} = State) -> case Action of queue -> try State#{queue => p1_queue:in(Pkt, Q)} catch error:full -> Q1 = p1_queue:set_limit(Q, unlimited), Q2 = p1_queue:in(Pkt, Q1), handle_stream_end(queue_full, State#{queue => Q2}) end; bounce -> bounce_packet(Pkt, State); send -> set_idle_timeout(send(State, Pkt)) end; handle_info(Info, #{server_host := ServerHost} = State) -> ejabberd_hooks:run_fold(s2s_out_handle_info, ServerHost, State, [Info]). terminate(Reason, #{server := LServer, remote_server := RServer} = State) -> ejabberd_s2s:remove_connection({LServer, RServer}, self()), State1 = case Reason of normal -> State; _ -> State#{stop_reason => internal_failure} end, bounce_queue(State1), bounce_message_queue(State1). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec resend_queue(state()) -> state(). resend_queue(State) -> queue_fold( fun(Pkt, AccState) -> send(AccState, Pkt) end, State). -spec bounce_queue(state()) -> state(). bounce_queue(State) -> queue_fold( fun(Pkt, AccState) -> bounce_packet(Pkt, AccState) end, State). -spec bounce_message_queue(state()) -> state(). bounce_message_queue(State) -> receive {route, Pkt} -> State1 = bounce_packet(Pkt, State), bounce_message_queue(State1) after 0 -> State end. -spec bounce_packet(xmpp_element(), state()) -> state(). bounce_packet(Pkt, State) when ?is_stanza(Pkt) -> Lang = xmpp:get_lang(Pkt), Err = mk_bounce_error(Lang, State), ejabberd_router:route_error(Pkt, Err), State; bounce_packet(_, State) -> State. -spec mk_bounce_error(binary(), state()) -> stanza_error(). mk_bounce_error(Lang, #{stop_reason := Why}) -> Reason = format_error(Why), case Why of internal_failure -> xmpp:err_internal_server_error(Reason, Lang); queue_full -> xmpp:err_resource_constraint(Reason, Lang); {dns, _} -> xmpp:err_remote_server_not_found(Reason, Lang); _ -> xmpp:err_remote_server_timeout(Reason, Lang) end; mk_bounce_error(_Lang, _State) -> %% We should not be here. Probably :) xmpp:err_remote_server_not_found(). -spec get_delay() -> non_neg_integer(). get_delay() -> MaxDelay = ejabberd_config:get_option(s2s_max_retry_delay, 300), randoms:uniform(MaxDelay). -spec set_idle_timeout(state()) -> state(). set_idle_timeout(#{on_route := send, server := LServer} = State) -> Timeout = ejabberd_s2s:get_idle_timeout(LServer), xmpp_stream_out:set_timeout(State, Timeout); set_idle_timeout(State) -> State. -spec queue_fold(fun((xmpp_element(), state()) -> state()), state()) -> state(). queue_fold(F, #{queue := Q} = State) -> case p1_queue:out(Q) of {{value, Pkt}, Q1} -> State1 = F(Pkt, State#{queue => Q1}), queue_fold(F, State1); {empty, Q1} -> State#{queue => Q1} end. format_error(internal_failure) -> <<"Internal server error">>; format_error(queue_full) -> <<"Stream queue is overloaded">>; format_error(Reason) -> xmpp_stream_out:format_error(Reason). transform_options(Opts) -> lists:foldl(fun transform_options/2, [], Opts). transform_options({outgoing_s2s_options, Families, Timeout}, Opts) -> ?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. " "The option is still supported " "but it is better to fix your config: " "use 'outgoing_s2s_timeout' and " "'outgoing_s2s_families' instead.", []), maybe_report_huge_timeout(outgoing_s2s_timeout, Timeout), [{outgoing_s2s_families, Families}, {outgoing_s2s_timeout, Timeout} | Opts]; transform_options({s2s_dns_options, S2SDNSOpts}, AllOpts) -> ?WARNING_MSG("Option 's2s_dns_options' is deprecated. " "The option is still supported " "but it is better to fix your config: " "use 's2s_dns_timeout' and " "'s2s_dns_retries' instead", []), lists:foldr( fun({timeout, T}, AccOpts) -> maybe_report_huge_timeout(s2s_dns_timeout, T), [{s2s_dns_timeout, T}|AccOpts]; ({retries, R}, AccOpts) -> [{s2s_dns_retries, R}|AccOpts]; (_, AccOpts) -> AccOpts end, AllOpts, S2SDNSOpts); transform_options({Opt, T}, Opts) when Opt == outgoing_s2s_timeout; Opt == s2s_dns_timeout -> maybe_report_huge_timeout(Opt, T), [{Opt, T}|Opts]; transform_options(Opt, Opts) -> [Opt|Opts]. maybe_report_huge_timeout(Opt, T) when is_integer(T), T >= 1000 -> ?WARNING_MSG("value '~p' of option '~p' is too big, " "are you sure you have set seconds?", [T, Opt]); maybe_report_huge_timeout(_, _) -> ok. -spec opt_type(outgoing_s2s_families) -> fun(([ipv4|ipv6]) -> [inet|inet6]); (outgoing_s2s_port) -> fun((0..65535) -> 0..65535); (outgoing_s2s_timeout) -> fun((timeout()) -> timeout()); (s2s_dns_retries) -> fun((non_neg_integer()) -> non_neg_integer()); (s2s_dns_timeout) -> fun((timeout()) -> timeout()); (s2s_max_retry_delay) -> fun((pos_integer()) -> pos_integer()); (atom()) -> [atom()]. opt_type(outgoing_s2s_families) -> fun(Families) -> lists:map( fun(ipv4) -> inet; (ipv6) -> inet6 end, Families) end; opt_type(outgoing_s2s_port) -> fun (I) when is_integer(I), I > 0, I < 65536 -> I end; opt_type(outgoing_s2s_timeout) -> fun(TimeOut) when is_integer(TimeOut), TimeOut > 0 -> timer:seconds(TimeOut); (unlimited) -> infinity; (infinity) -> infinity end; opt_type(s2s_dns_retries) -> fun (I) when is_integer(I), I >= 0 -> I end; opt_type(s2s_dns_timeout) -> fun(I) when is_integer(I), I>=0 -> timer:seconds(I); (infinity) -> infinity; (unlimited) -> infinity end; opt_type(s2s_max_retry_delay) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(_) -> [outgoing_s2s_families, outgoing_s2s_port, outgoing_s2s_timeout, s2s_dns_retries, s2s_dns_timeout, s2s_max_retry_delay]. ejabberd-18.01/src/gen_pubsub_node.erl0000644000232200023220000001441013225664356020274 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : gen_pubsub_node.erl %%% Author : Christophe Romain %%% Purpose : Define pubsub plugin behaviour %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(gen_pubsub_node). -include("xmpp.hrl"). -type(host() :: mod_pubsub:host()). -type(nodeId() :: mod_pubsub:nodeId()). -type(nodeIdx() :: mod_pubsub:nodeIdx()). -type(itemId() :: mod_pubsub:itemId()). -type(pubsubNode() :: mod_pubsub:pubsubNode()). -type(pubsubState() :: mod_pubsub:pubsubState()). -type(pubsubItem() :: mod_pubsub:pubsubItem()). -type(subOptions() :: mod_pubsub:subOptions()). -type(pubOptions() :: mod_pubsub:pubOptions()). -type(affiliation() :: mod_pubsub:affiliation()). -type(subscription() :: mod_pubsub:subscription()). -type(subId() :: mod_pubsub:subId()). -type(accessModel() :: mod_pubsub:accessModel()). -type(publishModel() :: mod_pubsub:publishModel()). -type(payload() :: mod_pubsub:payload()). -callback init(Host :: binary(), ServerHost :: binary(), Opts :: [any()]) -> atom(). -callback terminate(Host :: host(), ServerHost :: binary()) -> atom(). -callback options() -> [{atom(), any()}]. -callback features() -> [binary()]. -callback create_node_permission(Host :: host(), ServerHost :: binary(), Node :: nodeId(), ParentNode :: nodeId(), Owner :: jid(), Access :: atom()) -> {result, boolean()}. -callback create_node(NodeIdx :: nodeIdx(), Owner :: jid()) -> {result, {default, broadcast}}. -callback delete_node(Nodes :: [pubsubNode(),...]) -> {result, {default, broadcast, [{pubsubNode(), [{ljid(), [{subscription(), subId()}]},...]},...] } } | {result, {[], [{pubsubNode(), [{ljid(), [{subscription(), subId()}]},...]},...] } }. -callback purge_node(NodeIdx :: nodeIdx(), Owner :: jid()) -> {result, {default, broadcast}} | {error, stanza_error()}. -callback subscribe_node(NodeIdx :: nodeIdx(), Sender :: jid(), Subscriber :: jid(), AccessModel :: accessModel(), SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence', PresenceSubscription :: boolean(), RosterGroup :: boolean(), Options :: subOptions()) -> {result, {default, subscribed, subId()}} | {result, {default, subscribed, subId(), send_last}} | {result, {default, pending, subId()}} | {error, stanza_error()}. -callback unsubscribe_node(NodeIdx :: nodeIdx(), Sender :: jid(), Subscriber :: jid(), SubId :: subId()) -> {result, []} | {error, stanza_error()}. -callback publish_item(NodeId :: nodeIdx(), Publisher :: jid(), PublishModel :: publishModel(), Max_Items :: non_neg_integer(), ItemId :: <<>> | itemId(), Payload :: payload(), Options :: pubOptions()) -> {result, {default, broadcast, [itemId()]}} | {error, stanza_error()}. -callback delete_item(NodeIdx :: nodeIdx(), Publisher :: jid(), PublishModel :: publishModel(), ItemId :: <<>> | itemId()) -> {result, {default, broadcast}} | {error, stanza_error()}. -callback remove_extra_items(NodeIdx :: nodeIdx(), Max_Items :: unlimited | non_neg_integer(), ItemIds :: [itemId()]) -> {result, {[itemId()], [itemId()]} }. -callback get_node_affiliations(NodeIdx :: nodeIdx()) -> {result, [{ljid(), affiliation()}]}. -callback get_entity_affiliations(Host :: host(), Owner :: jid()) -> {result, [{pubsubNode(), affiliation()}]}. -callback get_affiliation(NodeIdx :: nodeIdx(), Owner :: jid()) -> {result, affiliation()}. -callback set_affiliation(NodeIdx :: nodeIdx(), Owner :: jid(), Affiliation :: affiliation()) -> ok | {error, stanza_error()}. -callback get_node_subscriptions(NodeIdx :: nodeIdx()) -> {result, [{ljid(), subscription(), subId()}] | [{ljid(), none},...] }. -callback get_entity_subscriptions(Host :: host(), Key :: jid()) -> {result, [{pubsubNode(), subscription(), subId(), ljid()}] }. -callback get_subscriptions(NodeIdx :: nodeIdx(), Owner :: jid()) -> {result, [{subscription(), subId()}]}. -callback get_pending_nodes(Host :: host(), Owner :: jid()) -> {result, [nodeId()]}. -callback get_states(NodeIdx::nodeIdx()) -> {result, [pubsubState()]}. -callback get_state(NodeIdx :: nodeIdx(), Key :: ljid()) -> pubsubState(). -callback set_state(State::pubsubState()) -> ok | {error, stanza_error()}. -callback get_items(nodeIdx(), jid(), accessModel(), boolean(), boolean(), binary(), undefined | rsm_set()) -> {result, {[pubsubItem()], undefined | rsm_set()}} | {error, stanza_error()}. -callback get_items(nodeIdx(), jid(), undefined | rsm_set()) -> {result, {[pubsubItem()], undefined | rsm_set()}}. -callback get_last_items(nodeIdx(), jid(), undefined | rsm_set()) -> {result, {[pubsubItem()], undefined | rsm_set()}}. -callback get_item(NodeIdx :: nodeIdx(), ItemId :: itemId(), JID :: jid(), AccessModel :: accessModel(), PresenceSubscription :: boolean(), RosterGroup :: boolean(), SubId :: subId()) -> {result, pubsubItem()} | {error, stanza_error()}. -callback get_item(NodeIdx :: nodeIdx(), ItemId :: itemId()) -> {result, pubsubItem()} | {error, stanza_error()}. -callback set_item(Item :: pubsubItem()) -> ok. % | {error, _}. -callback get_item_name(Host :: host(), ServerHost :: binary(), Node :: nodeId()) -> itemId(). -callback node_to_path(Node :: nodeId()) -> [nodeId()]. -callback path_to_node(Node :: [nodeId()]) -> nodeId(). ejabberd-18.01/src/mod_carboncopy.erl0000644000232200023220000003417713225664356020150 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_carboncopy.erl %%% Author : Eric Cestari %%% Purpose : Message Carbons XEP-0280 0.8 %%% Created : 5 May 2008 by Mickael Remond %%% Usage : Add the following line in modules section of ejabberd.yml: %%% {mod_carboncopy, []} %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module (mod_carboncopy). -author ('ecestari@process-one.net'). -protocol({xep, 280, '0.8'}). -behavior(gen_mod). %% API: -export([start/2, stop/1, reload/3]). -export([user_send_packet/1, user_receive_packet/1, iq_handler/1, remove_connection/4, disco_features/5, is_carbon_copy/1, mod_opt_type/1, depends/2, clean_cache/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_carboncopy.hrl"). -type direction() :: sent | received. -callback init(binary(), gen_mod:opts()) -> any(). -callback enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}. -callback disable(binary(), binary(), binary()) -> ok | {error, any()}. -callback list(binary(), binary()) -> [{binary(), binary(), node()}]. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). -spec is_carbon_copy(stanza()) -> boolean(). is_carbon_copy(#message{meta = #{carbon_copy := true}}) -> true; is_carbon_copy(_) -> false. start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), Mod = gen_mod:ram_db_mod(Host, ?MODULE), init_cache(Mod, Host, Opts), Mod:init(Host, Opts), clean_cache(), ejabberd_hooks:add(unset_presence_hook,Host, ?MODULE, remove_connection, 10), %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) ejabberd_hooks:add(user_send_packet,Host, ?MODULE, user_send_packet, 89), ejabberd_hooks:add(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler, IQDisc). stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) ejabberd_hooks:delete(user_send_packet,Host, ?MODULE, user_send_packet, 89), ejabberd_hooks:delete(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), ejabberd_hooks:delete(unset_presence_hook,Host, ?MODULE, remove_connection, 10). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:ram_db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:ram_db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, case use_cache(NewMod, Host) of true -> ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Host, NewOpts)); false -> ok end, case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler, IQDisc); true -> ok end. -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. disco_features({error, Err}, _From, _To, _Node, _Lang) -> {error, Err}; disco_features(empty, _From, _To, <<"">>, _Lang) -> {result, [?NS_CARBONS_2]}; disco_features({result, Feats}, _From, _To, <<"">>, _Lang) -> {result, [?NS_CARBONS_2|Feats]}; disco_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec iq_handler(iq()) -> iq(). iq_handler(#iq{type = set, lang = Lang, from = From, sub_els = [El]} = IQ) when is_record(El, carbons_enable); is_record(El, carbons_disable) -> {U, S, R} = jid:tolower(From), Result = case El of #carbons_enable{} -> ?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]), enable(S, U, R, ?NS_CARBONS_2); #carbons_disable{} -> ?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]), disable(S, U, R) end, case Result of ok -> ?DEBUG("carbons IQ result: ok", []), xmpp:make_iq_result(IQ); {error,_Error} -> ?ERROR_MSG("Error enabling / disabling carbons: ~p", [Result]), Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end; iq_handler(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Only or tags are allowed">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); iq_handler(#iq{type = get, lang = Lang} = IQ)-> Txt = <<"Value 'get' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). -spec user_send_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()} | {stop, {stanza(), ejabberd_c2s:state()}}. user_send_packet({Packet, C2SState}) -> From = xmpp:get_from(Packet), To = xmpp:get_to(Packet), case check_and_forward(From, To, Packet, sent) of {stop, Pkt} -> {stop, {Pkt, C2SState}}; Pkt -> {Pkt, C2SState} end. -spec user_receive_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()} | {stop, {stanza(), ejabberd_c2s:state()}}. user_receive_packet({Packet, #{jid := JID} = C2SState}) -> To = xmpp:get_to(Packet), case check_and_forward(JID, To, Packet, received) of {stop, Pkt} -> {stop, {Pkt, C2SState}}; Pkt -> {Pkt, C2SState} end. % Modified from original version: % - registered to the user_send_packet hook, to be called only once even for multicast % - do not support "private" message mode, and do not modify the original packet in any way % - we also replicate "read" notifications -spec check_and_forward(jid(), jid(), stanza(), direction()) -> stanza() | {stop, stanza()}. check_and_forward(JID, To, Packet, Direction)-> case is_chat_message(Packet) andalso not is_muc_pm(To, Packet) andalso xmpp:has_subtag(Packet, #carbons_private{}) == false andalso xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of true -> case is_carbon_copy(Packet) of false -> send_copies(JID, To, Packet, Direction), Packet; true -> %% stop the hook chain, we don't want logging modules to duplicates %% this message {stop, Packet} end; _ -> Packet end. -spec remove_connection(binary(), binary(), binary(), binary()) -> ok. remove_connection(User, Server, Resource, _Status)-> disable(Server, User, Resource), ok. %%% Internal %% Direction = received | sent -spec send_copies(jid(), jid(), message(), direction()) -> ok. send_copies(JID, To, Packet, Direction)-> {U, S, R} = jid:tolower(JID), PrioRes = ejabberd_sm:get_user_present_resources(U, S), {_, AvailRs} = lists:unzip(PrioRes), {MaxPrio, _MaxRes} = case catch lists:max(PrioRes) of {Prio, Res} -> {Prio, Res}; _ -> {0, undefined} end, %% unavailable resources are handled like bare JIDs IsBareTo = case {Direction, To} of {received, #jid{lresource = <<>>}} -> true; {received, #jid{lresource = LRes}} -> not lists:member(LRes, AvailRs); _ -> false end, %% list of JIDs that should receive a carbon copy of this message (excluding the %% receiver(s) of the original message TargetJIDs = case {IsBareTo, Packet} of {true, #message{meta = #{sm_copy := true}}} -> %% The message was sent to our bare JID, and we currently have %% multiple resources with the same highest priority, so the session %% manager routes the message to each of them. We create carbon %% copies only from one of those resources in order to avoid %% duplicates. []; {true, _} -> OrigTo = fun(Res) -> lists:member({MaxPrio, Res}, PrioRes) end, [ {jid:make({U, S, CCRes}), CC_Version} || {CCRes, CC_Version} <- list(U, S), lists:member(CCRes, AvailRs), not OrigTo(CCRes) ]; {false, _} -> [ {jid:make({U, S, CCRes}), CC_Version} || {CCRes, CC_Version} <- list(U, S), lists:member(CCRes, AvailRs), CCRes /= R ] %TargetJIDs = lists:delete(JID, [ jid:make({U, S, CCRes}) || CCRes <- list(U, S) ]), end, lists:map(fun({Dest, _Version}) -> {_, _, Resource} = jid:tolower(Dest), ?DEBUG("Sending: ~p =/= ~p", [R, Resource]), Sender = jid:make({U, S, <<>>}), %{xmlelement, N, A, C} = Packet, New = build_forward_packet(JID, Packet, Sender, Dest, Direction), ejabberd_router:route(xmpp:set_from_to(New, Sender, Dest)) end, TargetJIDs), ok. -spec build_forward_packet(jid(), message(), jid(), jid(), direction()) -> message(). build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) -> Forwarded = #forwarded{xml_els = [xmpp:encode(complete_packet(JID, Msg, Direction))]}, Carbon = case Direction of sent -> #carbons_sent{forwarded = Forwarded}; received -> #carbons_received{forwarded = Forwarded} end, #message{from = Sender, to = Dest, type = T, sub_els = [Carbon], meta = #{carbon_copy => true}}. -spec enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}. enable(Host, U, R, CC)-> ?DEBUG("enabling for ~p", [U]), Mod = gen_mod:ram_db_mod(Host, ?MODULE), case Mod:enable(U, Host, R, CC) of ok -> delete_cache(Mod, U, Host); {error, _} = Err -> Err end. -spec disable(binary(), binary(), binary()) -> ok | {error, any()}. disable(Host, U, R)-> ?DEBUG("disabling for ~p", [U]), Mod = gen_mod:ram_db_mod(Host, ?MODULE), Res = Mod:disable(U, Host, R), delete_cache(Mod, U, Host), Res. -spec complete_packet(jid(), message(), direction()) -> message(). complete_packet(From, #message{from = undefined} = Msg, sent) -> %% if this is a packet sent by user on this host, then Packet doesn't %% include the 'from' attribute. We must add it. Msg#message{from = From}; complete_packet(_From, Msg, _Direction) -> Msg. -spec is_chat_message(stanza()) -> boolean(). is_chat_message(#message{type = chat}) -> true; is_chat_message(#message{type = normal, body = [_|_]}) -> true; is_chat_message(_) -> false. is_muc_pm(#jid{lresource = <<>>}, _Packet) -> false; is_muc_pm(_To, Packet) -> xmpp:has_subtag(Packet, #muc_user{}). -spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}]. list(User, Server) -> Mod = gen_mod:ram_db_mod(Server, ?MODULE), case use_cache(Mod, Server) of true -> case ets_cache:lookup( ?CARBONCOPY_CACHE, {User, Server}, fun() -> case Mod:list(User, Server) of {ok, L} when L /= [] -> {ok, L}; _ -> error end end) of {ok, L} -> [{Resource, NS} || {Resource, NS, _} <- L]; error -> [] end; false -> case Mod:list(User, Server) of {ok, L} -> [{Resource, NS} || {Resource, NS, _} <- L]; error -> [] end end. -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> case use_cache(Mod, Host) of true -> ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Host, Opts)); false -> ets_cache:delete(?CARBONCOPY_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(Host); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. -spec clean_cache(node()) -> ok. clean_cache(Node) -> ets_cache:filter( ?CARBONCOPY_CACHE, fun(_, error) -> false; (_, {ok, L}) -> not lists:any(fun({_, _, N}) -> N == Node end, L) end). -spec clean_cache() -> ok. clean_cache() -> ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]). -spec delete_cache(module(), binary(), binary()) -> ok. delete_cache(Mod, User, Server) -> case use_cache(Mod, Server) of true -> ets_cache:delete(?CARBONCOPY_CACHE, {User, Server}, cache_nodes(Mod, Server)); false -> ok end. depends(_Host, _Opts) -> []. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun(B) when is_boolean(B) -> B end; mod_opt_type(O) when O == cache_size; O == cache_life_time -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; mod_opt_type(_) -> [ram_db_type, iqdisc, use_cache, cache_size, cache_missed, cache_life_time]. ejabberd-18.01/src/mod_configure.erl0000644000232200023220000017050613225664356017767 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_configure.erl %%% Author : Alexey Shchepin %%% Purpose : Support for online configuration of ejabberd %%% Created : 19 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_configure). -author('alexey@process-one.net'). -protocol({xep, 133, '1.1'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, get_local_identity/5, get_local_features/5, get_local_items/5, adhoc_local_items/4, adhoc_local_commands/4, get_sm_identity/5, get_sm_features/5, get_sm_items/5, adhoc_sm_items/4, adhoc_sm_commands/4, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_sm.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -define(T(Lang, Text), translate:translate(Lang, Text)). start(Host, _Opts) -> ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_items, 50), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 50), ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 50), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, adhoc_local_items, 50), ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, adhoc_local_commands, 50), ejabberd_hooks:add(adhoc_sm_items, Host, ?MODULE, adhoc_sm_items, 50), ejabberd_hooks:add(adhoc_sm_commands, Host, ?MODULE, adhoc_sm_commands, 50), ok. stop(Host) -> ejabberd_hooks:delete(adhoc_sm_commands, Host, ?MODULE, adhoc_sm_commands, 50), ejabberd_hooks:delete(adhoc_sm_items, Host, ?MODULE, adhoc_sm_items, 50), ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, adhoc_local_commands, 50), ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, adhoc_local_items, 50), ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 50), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 50), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_items, 50). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> [{mod_adhoc, hard}, {mod_last, soft}]. %%%----------------------------------------------------------------------- -define(INFO_IDENTITY(Category, Type, Name, Lang), [#identity{category = Category, type = Type, name = ?T(Lang, Name)}]). -define(INFO_COMMAND(Name, Lang), ?INFO_IDENTITY(<<"automation">>, <<"command-node">>, Name, Lang)). -define(NODEJID(To, Name, Node), #disco_item{jid = To, name = ?T(Lang, Name), node = Node}). -define(NODE(Name, Node), #disco_item{jid = jid:make(Server), node = Node, name = ?T(Lang, Name)}). -define(NS_ADMINX(Sub), <<(?NS_ADMIN)/binary, "#", Sub/binary>>). -define(NS_ADMINL(Sub), [<<"http:">>, <<"jabber.org">>, <<"protocol">>, <<"admin">>, Sub]). tokenize(Node) -> str:tokens(Node, <<"/#">>). get_sm_identity(Acc, _From, _To, Node, Lang) -> case Node of <<"config">> -> ?INFO_COMMAND(<<"Configuration">>, Lang); _ -> Acc end. get_local_identity(Acc, _From, _To, Node, Lang) -> LNode = tokenize(Node), case LNode of [<<"running nodes">>, ENode] -> ?INFO_IDENTITY(<<"ejabberd">>, <<"node">>, ENode, Lang); [<<"running nodes">>, _ENode, <<"DB">>] -> ?INFO_COMMAND(<<"Database">>, Lang); [<<"running nodes">>, _ENode, <<"modules">>, <<"start">>] -> ?INFO_COMMAND(<<"Start Modules">>, Lang); [<<"running nodes">>, _ENode, <<"modules">>, <<"stop">>] -> ?INFO_COMMAND(<<"Stop Modules">>, Lang); [<<"running nodes">>, _ENode, <<"backup">>, <<"backup">>] -> ?INFO_COMMAND(<<"Backup">>, Lang); [<<"running nodes">>, _ENode, <<"backup">>, <<"restore">>] -> ?INFO_COMMAND(<<"Restore">>, Lang); [<<"running nodes">>, _ENode, <<"backup">>, <<"textfile">>] -> ?INFO_COMMAND(<<"Dump to Text File">>, Lang); [<<"running nodes">>, _ENode, <<"import">>, <<"file">>] -> ?INFO_COMMAND(<<"Import File">>, Lang); [<<"running nodes">>, _ENode, <<"import">>, <<"dir">>] -> ?INFO_COMMAND(<<"Import Directory">>, Lang); [<<"running nodes">>, _ENode, <<"restart">>] -> ?INFO_COMMAND(<<"Restart Service">>, Lang); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?INFO_COMMAND(<<"Shut Down Service">>, Lang); ?NS_ADMINL(<<"add-user">>) -> ?INFO_COMMAND(<<"Add User">>, Lang); ?NS_ADMINL(<<"delete-user">>) -> ?INFO_COMMAND(<<"Delete User">>, Lang); ?NS_ADMINL(<<"end-user-session">>) -> ?INFO_COMMAND(<<"End User Session">>, Lang); ?NS_ADMINL(<<"get-user-password">>) -> ?INFO_COMMAND(<<"Get User Password">>, Lang); ?NS_ADMINL(<<"change-user-password">>) -> ?INFO_COMMAND(<<"Change User Password">>, Lang); ?NS_ADMINL(<<"get-user-lastlogin">>) -> ?INFO_COMMAND(<<"Get User Last Login Time">>, Lang); ?NS_ADMINL(<<"user-stats">>) -> ?INFO_COMMAND(<<"Get User Statistics">>, Lang); ?NS_ADMINL(<<"get-registered-users-num">>) -> ?INFO_COMMAND(<<"Get Number of Registered Users">>, Lang); ?NS_ADMINL(<<"get-online-users-num">>) -> ?INFO_COMMAND(<<"Get Number of Online Users">>, Lang); [<<"config">>, <<"acls">>] -> ?INFO_COMMAND(<<"Access Control Lists">>, Lang); [<<"config">>, <<"access">>] -> ?INFO_COMMAND(<<"Access Rules">>, Lang); _ -> Acc end. %%%----------------------------------------------------------------------- -define(INFO_RESULT(Allow, Feats, Lang), case Allow of deny -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; allow -> {result, Feats} end). get_sm_features(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Allow = acl:match_rule(LServer, configure, From), case Node of <<"config">> -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); _ -> Acc end end. get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> LNode = tokenize(Node), Allow = acl:match_rule(LServer, configure, From), case LNode of [<<"config">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"user">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"online users">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"all users">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"all users">>, <<$@, _/binary>>] -> ?INFO_RESULT(Allow, [], Lang); [<<"outgoing s2s">> | _] -> ?INFO_RESULT(Allow, [], Lang); [<<"running nodes">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"stopped nodes">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"running nodes">>, _ENode] -> ?INFO_RESULT(Allow, [?NS_STATS], Lang); [<<"running nodes">>, _ENode, <<"DB">>] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); [<<"running nodes">>, _ENode, <<"modules">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"running nodes">>, _ENode, <<"modules">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); [<<"running nodes">>, _ENode, <<"backup">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"running nodes">>, _ENode, <<"backup">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); [<<"running nodes">>, _ENode, <<"import">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"running nodes">>, _ENode, <<"import">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); [<<"running nodes">>, _ENode, <<"restart">>] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); [<<"config">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"add-user">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"delete-user">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"end-user-session">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"get-user-password">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"change-user-password">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"get-user-lastlogin">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"user-stats">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"get-registered-users-num">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"get-online-users-num">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); _ -> Acc end end. %%%----------------------------------------------------------------------- -spec adhoc_sm_items(empty | {error, stanza_error()} | {result, [disco_item()]}, jid(), jid(), binary()) -> {error, stanza_error()} | {result, [disco_item()]} | empty. adhoc_sm_items(Acc, From, #jid{lserver = LServer} = To, Lang) -> case acl:match_rule(LServer, configure, From) of allow -> Items = case Acc of {result, Its} -> Its; empty -> [] end, Nodes = [#disco_item{jid = To, node = <<"config">>, name = ?T(Lang, <<"Configuration">>)}], {result, Items ++ Nodes}; _ -> Acc end. %%%----------------------------------------------------------------------- get_sm_items(Acc, From, #jid{user = User, server = Server, lserver = LServer} = To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Items = case Acc of {result, Its} -> Its; empty -> [] end, case {acl:match_rule(LServer, configure, From), Node} of {allow, <<"">>} -> Nodes = [?NODEJID(To, <<"Configuration">>, <<"config">>), ?NODEJID(To, <<"User Management">>, <<"user">>)], {result, Items ++ Nodes ++ get_user_resources(User, Server)}; {allow, <<"config">>} -> {result, []}; {_, <<"config">>} -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; _ -> Acc end end. get_user_resources(User, Server) -> Rs = ejabberd_sm:get_user_resources(User, Server), lists:map(fun (R) -> #disco_item{jid = jid:make(User, Server, R), name = User} end, lists:sort(Rs)). %%%----------------------------------------------------------------------- -spec adhoc_local_items(empty | {error, stanza_error()} | {result, [disco_item()]}, jid(), jid(), binary()) -> {error, stanza_error()} | {result, [disco_item()]} | empty. adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To, Lang) -> case acl:match_rule(LServer, configure, From) of allow -> Items = case Acc of {result, Its} -> Its; empty -> [] end, PermLev = get_permission_level(From), Nodes = recursively_get_local_items(PermLev, LServer, <<"">>, Server, Lang), Nodes1 = lists:filter( fun (#disco_item{node = Nd}) -> F = get_local_features([], From, To, Nd, Lang), case F of {result, [?NS_COMMANDS]} -> true; _ -> false end end, Nodes), {result, Items ++ Nodes1}; _ -> Acc end. recursively_get_local_items(_PermLev, _LServer, <<"online users">>, _Server, _Lang) -> []; recursively_get_local_items(_PermLev, _LServer, <<"all users">>, _Server, _Lang) -> []; recursively_get_local_items(PermLev, LServer, Node, Server, Lang) -> LNode = tokenize(Node), Items = case get_local_items({PermLev, LServer}, LNode, Server, Lang) of {result, Res} -> Res; {error, _Error} -> [] end, lists:flatten( lists:map( fun(#disco_item{jid = #jid{server = S}, node = Nd} = Item) -> if (S /= Server) or (Nd == <<"">>) -> []; true -> [Item, recursively_get_local_items( PermLev, LServer, Nd, Server, Lang)] end end, Items)). get_permission_level(JID) -> case acl:match_rule(global, configure, JID) of allow -> global; deny -> vhost end. %%%----------------------------------------------------------------------- -define(ITEMS_RESULT(Allow, LNode, Fallback), case Allow of deny -> Fallback; allow -> PermLev = get_permission_level(From), case get_local_items({PermLev, LServer}, LNode, jid:encode(To), Lang) of {result, Res} -> {result, Res}; {error, Error} -> {error, Error} end end). get_local_items(Acc, From, #jid{lserver = LServer} = To, <<"">>, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Items = case Acc of {result, Its} -> Its; empty -> [] end, Allow = acl:match_rule(LServer, configure, From), case Allow of deny -> {result, Items}; allow -> PermLev = get_permission_level(From), case get_local_items({PermLev, LServer}, [], jid:encode(To), Lang) of {result, Res} -> {result, Items ++ Res}; {error, _Error} -> {result, Items} end end end; get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> LNode = tokenize(Node), Allow = acl:match_rule(LServer, configure, From), Err = xmpp:err_forbidden(<<"Access denied by service policy">>, Lang), case LNode of [<<"config">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"user">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"online users">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"all users">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"all users">>, <<$@, _/binary>>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"outgoing s2s">> | _] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"stopped nodes">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"DB">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"modules">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"modules">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"backup">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"backup">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"import">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"import">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"restart">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); [<<"config">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"add-user">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"delete-user">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"end-user-session">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"get-user-password">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"change-user-password">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"get-user-lastlogin">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"user-stats">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"get-registered-users-num">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"get-online-users-num">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); _ -> Acc end end. %%%----------------------------------------------------------------------- %% @spec ({PermissionLevel, Host}, [string()], Server::string(), Lang) %% -> {result, [xmlelement()]} %% PermissionLevel = global | vhost get_local_items(_Host, [], Server, Lang) -> {result, [?NODE(<<"Configuration">>, <<"config">>), ?NODE(<<"User Management">>, <<"user">>), ?NODE(<<"Online Users">>, <<"online users">>), ?NODE(<<"All Users">>, <<"all users">>), ?NODE(<<"Outgoing s2s Connections">>, <<"outgoing s2s">>), ?NODE(<<"Running Nodes">>, <<"running nodes">>), ?NODE(<<"Stopped Nodes">>, <<"stopped nodes">>)]}; get_local_items(_Host, [<<"config">>], Server, Lang) -> {result, [?NODE(<<"Access Control Lists">>, <<"config/acls">>), ?NODE(<<"Access Rules">>, <<"config/access">>)]}; get_local_items(_Host, [<<"config">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"user">>], Server, Lang) -> {result, [?NODE(<<"Add User">>, (?NS_ADMINX(<<"add-user">>))), ?NODE(<<"Delete User">>, (?NS_ADMINX(<<"delete-user">>))), ?NODE(<<"End User Session">>, (?NS_ADMINX(<<"end-user-session">>))), ?NODE(<<"Get User Password">>, (?NS_ADMINX(<<"get-user-password">>))), ?NODE(<<"Change User Password">>, (?NS_ADMINX(<<"change-user-password">>))), ?NODE(<<"Get User Last Login Time">>, (?NS_ADMINX(<<"get-user-lastlogin">>))), ?NODE(<<"Get User Statistics">>, (?NS_ADMINX(<<"user-stats">>))), ?NODE(<<"Get Number of Registered Users">>, (?NS_ADMINX(<<"get-registered-users-num">>))), ?NODE(<<"Get Number of Online Users">>, (?NS_ADMINX(<<"get-online-users-num">>)))]}; get_local_items(_Host, [<<"http:">> | _], _Server, _Lang) -> {result, []}; get_local_items({_, Host}, [<<"online users">>], _Server, _Lang) -> {result, get_online_vh_users(Host)}; get_local_items({_, Host}, [<<"all users">>], _Server, _Lang) -> {result, get_all_vh_users(Host)}; get_local_items({_, Host}, [<<"all users">>, <<$@, Diap/binary>>], _Server, _Lang) -> Users = ejabberd_auth:get_users(Host), SUsers = lists:sort([{S, U} || {U, S} <- Users]), try [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), N1 = binary_to_integer(S1), N2 = binary_to_integer(S2), Sub = lists:sublist(SUsers, N1, N2 - N1 + 1), {result, lists:map( fun({S, U}) -> #disco_item{jid = jid:make(U, S), name = <>} end, Sub)} catch _:_ -> xmpp:err_not_acceptable() end; get_local_items({_, Host}, [<<"outgoing s2s">>], _Server, Lang) -> {result, get_outgoing_s2s(Host, Lang)}; get_local_items({_, Host}, [<<"outgoing s2s">>, To], _Server, Lang) -> {result, get_outgoing_s2s(Host, Lang, To)}; get_local_items(_Host, [<<"running nodes">>], Server, Lang) -> {result, get_running_nodes(Server, Lang)}; get_local_items(_Host, [<<"stopped nodes">>], _Server, Lang) -> {result, get_stopped_nodes(Lang)}; get_local_items({global, _Host}, [<<"running nodes">>, ENode], Server, Lang) -> {result, [?NODE(<<"Database">>, <<"running nodes/", ENode/binary, "/DB">>), ?NODE(<<"Modules">>, <<"running nodes/", ENode/binary, "/modules">>), ?NODE(<<"Backup Management">>, <<"running nodes/", ENode/binary, "/backup">>), ?NODE(<<"Import Users From jabberd14 Spool Files">>, <<"running nodes/", ENode/binary, "/import">>), ?NODE(<<"Restart Service">>, <<"running nodes/", ENode/binary, "/restart">>), ?NODE(<<"Shut Down Service">>, <<"running nodes/", ENode/binary, "/shutdown">>)]}; get_local_items({vhost, _Host}, [<<"running nodes">>, ENode], Server, Lang) -> {result, [?NODE(<<"Modules">>, <<"running nodes/", ENode/binary, "/modules">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"DB">>], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, ENode, <<"modules">>], Server, Lang) -> {result, [?NODE(<<"Start Modules">>, <<"running nodes/", ENode/binary, "/modules/start">>), ?NODE(<<"Stop Modules">>, <<"running nodes/", ENode/binary, "/modules/stop">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"modules">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, ENode, <<"backup">>], Server, Lang) -> {result, [?NODE(<<"Backup">>, <<"running nodes/", ENode/binary, "/backup/backup">>), ?NODE(<<"Restore">>, <<"running nodes/", ENode/binary, "/backup/restore">>), ?NODE(<<"Dump to Text File">>, <<"running nodes/", ENode/binary, "/backup/textfile">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"backup">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, ENode, <<"import">>], Server, Lang) -> {result, [?NODE(<<"Import File">>, <<"running nodes/", ENode/binary, "/import/file">>), ?NODE(<<"Import Directory">>, <<"running nodes/", ENode/binary, "/import/dir">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"import">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"restart">>], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"shutdown">>], _Server, _Lang) -> {result, []}; get_local_items(_Host, _, _Server, _Lang) -> {error, xmpp:err_item_not_found()}. get_online_vh_users(Host) -> case catch ejabberd_sm:get_vh_session_list(Host) of {'EXIT', _Reason} -> []; USRs -> SURs = lists:sort([{S, U, R} || {U, S, R} <- USRs]), lists:map( fun({S, U, R}) -> #disco_item{jid = jid:make(U, S, R), name = <>} end, SURs) end. get_all_vh_users(Host) -> case catch ejabberd_auth:get_users(Host) of {'EXIT', _Reason} -> []; Users -> SUsers = lists:sort([{S, U} || {U, S} <- Users]), case length(SUsers) of N when N =< 100 -> lists:map(fun({S, U}) -> #disco_item{jid = jid:make(U, S), name = <>} end, SUsers); N -> NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) + 1, M = trunc(N / NParts) + 1, lists:map(fun (K) -> L = K + M - 1, Node = <<"@", (integer_to_binary(K))/binary, "-", (integer_to_binary(L))/binary>>, {FS, FU} = lists:nth(K, SUsers), {LS, LU} = if L < N -> lists:nth(L, SUsers); true -> lists:last(SUsers) end, Name = <>, #disco_item{jid = jid:make(Host), node = <<"all users/", Node/binary>>, name = Name} end, lists:seq(1, N, M)) end end. get_outgoing_s2s(Host, Lang) -> case catch ejabberd_s2s:dirty_get_connections() of {'EXIT', _Reason} -> []; Connections -> DotHost = <<".", Host/binary>>, TConns = [TH || {FH, TH} <- Connections, Host == FH orelse str:suffix(DotHost, FH)], lists:map( fun (T) -> Name = str:format(?T(Lang, <<"To ~s">>),[T]), #disco_item{jid = jid:make(Host), node = <<"outgoing s2s/", T/binary>>, name = Name} end, lists:usort(TConns)) end. get_outgoing_s2s(Host, Lang, To) -> case catch ejabberd_s2s:dirty_get_connections() of {'EXIT', _Reason} -> []; Connections -> lists:map( fun ({F, _T}) -> Node = <<"outgoing s2s/", To/binary, "/", F/binary>>, Name = str:format(?T(Lang, <<"From ~s">>), [F]), #disco_item{jid = jid:make(Host), node = Node, name = Name} end, lists:keysort(1, lists:filter(fun (E) -> element(2, E) == To end, Connections))) end. get_running_nodes(Server, _Lang) -> case catch mnesia:system_info(running_db_nodes) of {'EXIT', _Reason} -> []; DBNodes -> lists:map( fun (N) -> S = iolist_to_binary(atom_to_list(N)), #disco_item{jid = jid:make(Server), node = <<"running nodes/", S/binary>>, name = S} end, lists:sort(DBNodes)) end. get_stopped_nodes(_Lang) -> case catch lists:usort(mnesia:system_info(db_nodes) ++ mnesia:system_info(extra_db_nodes)) -- mnesia:system_info(running_db_nodes) of {'EXIT', _Reason} -> []; DBNodes -> lists:map( fun (N) -> S = iolist_to_binary(atom_to_list(N)), #disco_item{jid = jid:make(?MYNAME), node = <<"stopped nodes/", S/binary>>, name = S} end, lists:sort(DBNodes)) end. %%------------------------------------------------------------------------- -define(COMMANDS_RESULT(LServerOrGlobal, From, To, Request, Lang), case acl:match_rule(LServerOrGlobal, configure, From) of deny -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; allow -> adhoc_local_commands(From, To, Request) end). -spec adhoc_local_commands(adhoc_command(), jid(), jid(), adhoc_command()) -> adhoc_command() | {error, stanza_error()}. adhoc_local_commands(Acc, From, #jid{lserver = LServer} = To, #adhoc_command{node = Node, lang = Lang} = Request) -> LNode = tokenize(Node), case LNode of [<<"running nodes">>, _ENode, <<"DB">>] -> ?COMMANDS_RESULT(global, From, To, Request, Lang); [<<"running nodes">>, _ENode, <<"modules">>, _] -> ?COMMANDS_RESULT(LServer, From, To, Request, Lang); [<<"running nodes">>, _ENode, <<"backup">>, _] -> ?COMMANDS_RESULT(global, From, To, Request, Lang); [<<"running nodes">>, _ENode, <<"import">>, _] -> ?COMMANDS_RESULT(global, From, To, Request, Lang); [<<"running nodes">>, _ENode, <<"restart">>] -> ?COMMANDS_RESULT(global, From, To, Request, Lang); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?COMMANDS_RESULT(global, From, To, Request, Lang); [<<"config">>, _] -> ?COMMANDS_RESULT(LServer, From, To, Request, Lang); ?NS_ADMINL(_) -> ?COMMANDS_RESULT(LServer, From, To, Request, Lang); _ -> Acc end. adhoc_local_commands(From, #jid{lserver = LServer} = _To, #adhoc_command{lang = Lang, node = Node, sid = SessionID, action = Action, xdata = XData} = Request) -> LNode = tokenize(Node), ActionIsExecute = Action == execute orelse Action == complete, if Action == cancel -> #adhoc_command{status = canceled, lang = Lang, node = Node, sid = SessionID}; XData == undefined, ActionIsExecute -> case get_form(LServer, LNode, Lang) of {result, Form} -> xmpp_util:make_adhoc_response( Request, #adhoc_command{status = executing, xdata = Form}); {result, Status, Form} -> xmpp_util:make_adhoc_response( Request, #adhoc_command{status = Status, xdata = Form}); {error, Error} -> {error, Error} end; XData /= undefined, ActionIsExecute -> case catch set_form(From, LServer, LNode, Lang, XData) of {result, Res} -> xmpp_util:make_adhoc_response( Request, #adhoc_command{xdata = Res, status = completed}); {'EXIT', _} -> {error, xmpp:err_bad_request()}; {error, Error} -> {error, Error} end; true -> {error, xmpp:err_bad_request(<<"Unexpected action">>, Lang)} end. -define(TVFIELD(Type, Var, Val), #xdata_field{type = Type, var = Var, values = [Val]}). -define(HFIELD(), ?TVFIELD(hidden, <<"FORM_TYPE">>, (?NS_ADMIN))). -define(TLFIELD(Type, Label, Var), #xdata_field{type = Type, label = ?T(Lang, Label), var = Var}). -define(XFIELD(Type, Label, Var, Val), #xdata_field{type = Type, label = ?T(Lang, Label), var = Var, values = [Val]}). -define(XMFIELD(Type, Label, Var, Vals), #xdata_field{type = Type, label = ?T(Lang, Label), var = Var, values = Vals}). -define(TABLEFIELD(Table, Val), #xdata_field{ type = 'list-single', label = iolist_to_binary(atom_to_list(Table)), var = iolist_to_binary(atom_to_list(Table)), values = [iolist_to_binary(atom_to_list(Val))], options = [#xdata_option{label = ?T(Lang, <<"RAM copy">>), value = <<"ram_copies">>}, #xdata_option{label = ?T(Lang, <<"RAM and disc copy">>), value = <<"disc_copies">>}, #xdata_option{label = ?T(Lang, <<"Disc only copy">>), value = <<"disc_only_copies">>}, #xdata_option{label = ?T(Lang, <<"Remote copy">>), value = <<"unknown">>}]}). get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>], Lang) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of {badrpc, Reason} -> ?ERROR_MSG("RPC call mnesia:system_info(tables) on node " "~s failed: ~p", [Node, Reason]), {error, xmpp:err_internal_server_error()}; Tables -> STables = lists:sort(Tables), Title = <<(?T(Lang, <<"Database Tables Configuration at ">>))/binary, ENode/binary>>, Instr = ?T(Lang, <<"Choose storage type of tables">>), try Fs = lists:map( fun(Table) -> case ejabberd_cluster:call( Node, mnesia, table_info, [Table, storage_type]) of Type when is_atom(Type) -> ?TABLEFIELD(Table, Type) end end, STables), {result, #xdata{title = Title, type = form, instructions = [Instr], fields = [?HFIELD()|Fs]}} catch _:{case_clause, {badrpc, Reason}} -> ?ERROR_MSG("RPC call mnesia:table_info/2 " "on node ~s failed: ~p", [Node, Reason]), {error, xmpp:err_internal_server_error()} end end end; get_form(Host, [<<"running nodes">>, ENode, <<"modules">>, <<"stop">>], Lang) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case ejabberd_cluster:call(Node, gen_mod, loaded_modules, [Host]) of {badrpc, Reason} -> ?ERROR_MSG("RPC call gen_mod:loaded_modules(~s) on node " "~s failed: ~p", [Host, Node, Reason]), {error, xmpp:err_internal_server_error()}; Modules -> SModules = lists:sort(Modules), Title = <<(?T(Lang, <<"Stop Modules at ">>))/binary, ENode/binary>>, Instr = ?T(Lang, <<"Choose modules to stop">>), Fs = lists:map(fun(M) -> S = misc:atom_to_binary(M), ?XFIELD(boolean, S, S, <<"0">>) end, SModules), {result, #xdata{title = Title, type = form, instructions = [Instr], fields = [?HFIELD()|Fs]}} end end; get_form(_Host, [<<"running nodes">>, ENode, <<"modules">>, <<"start">>], Lang) -> {result, #xdata{title = <<(?T(Lang, <<"Start Modules at ">>))/binary, ENode/binary>>, type = form, instructions = [?T(Lang, <<"Enter list of {Module, [Options]}">>)], fields = [?HFIELD(), ?XFIELD('text-multi', <<"List of modules to start">>, <<"modules">>, <<"[].">>)]}}; get_form(_Host, [<<"running nodes">>, ENode, <<"backup">>, <<"backup">>], Lang) -> {result, #xdata{title = <<(?T(Lang, <<"Backup to File at ">>))/binary, ENode/binary>>, type = form, instructions = [?T(Lang, <<"Enter path to backup file">>)], fields = [?HFIELD(), ?XFIELD('text-single', <<"Path to File">>, <<"path">>, <<"">>)]}}; get_form(_Host, [<<"running nodes">>, ENode, <<"backup">>, <<"restore">>], Lang) -> {result, #xdata{title = <<(?T(Lang, <<"Restore Backup from File at ">>))/binary, ENode/binary>>, type = form, instructions = [?T(Lang, <<"Enter path to backup file">>)], fields = [?HFIELD(), ?XFIELD('text-single', <<"Path to File">>, <<"path">>, <<"">>)]}}; get_form(_Host, [<<"running nodes">>, ENode, <<"backup">>, <<"textfile">>], Lang) -> {result, #xdata{title = <<(?T(Lang, <<"Dump Backup to Text File at ">>))/binary, ENode/binary>>, type = form, instructions = [?T(Lang, <<"Enter path to text file">>)], fields = [?HFIELD(), ?XFIELD('text-single', <<"Path to File">>, <<"path">>, <<"">>)]}}; get_form(_Host, [<<"running nodes">>, ENode, <<"import">>, <<"file">>], Lang) -> {result, #xdata{title = <<(?T(Lang, <<"Import User from File at ">>))/binary, ENode/binary>>, type = form, instructions = [?T(Lang, <<"Enter path to jabberd14 spool file">>)], fields = [?HFIELD(), ?XFIELD('text-single', <<"Path to File">>, <<"path">>, <<"">>)]}}; get_form(_Host, [<<"running nodes">>, ENode, <<"import">>, <<"dir">>], Lang) -> {result, #xdata{title = <<(?T(Lang, <<"Import Users from Dir at ">>))/binary, ENode/binary>>, type = form, instructions = [?T(Lang, <<"Enter path to jabberd14 spool dir">>)], fields = [?HFIELD(), ?XFIELD('text-single', <<"Path to Dir">>, <<"path">>, <<"">>)]}}; get_form(_Host, [<<"running nodes">>, _ENode, <<"restart">>], Lang) -> Make_option = fun (LabelNum, LabelUnit, Value) -> #xdata_option{ label = <>, value = Value} end, {result, #xdata{title = ?T(Lang, <<"Restart Service">>), type = form, fields = [?HFIELD(), #xdata_field{ type = 'list-single', label = ?T(Lang, <<"Time delay">>), var = <<"delay">>, required = true, options = [Make_option(<<"">>, <<"immediately">>, <<"1">>), Make_option(<<"15 ">>, <<"seconds">>, <<"15">>), Make_option(<<"30 ">>, <<"seconds">>, <<"30">>), Make_option(<<"60 ">>, <<"seconds">>, <<"60">>), Make_option(<<"90 ">>, <<"seconds">>, <<"90">>), Make_option(<<"2 ">>, <<"minutes">>, <<"120">>), Make_option(<<"3 ">>, <<"minutes">>, <<"180">>), Make_option(<<"4 ">>, <<"minutes">>, <<"240">>), Make_option(<<"5 ">>, <<"minutes">>, <<"300">>), Make_option(<<"10 ">>, <<"minutes">>, <<"600">>), Make_option(<<"15 ">>, <<"minutes">>, <<"900">>), Make_option(<<"30 ">>, <<"minutes">>, <<"1800">>)]}, #xdata_field{type = fixed, label = ?T(Lang, <<"Send announcement to all online users " "on all hosts">>)}, #xdata_field{var = <<"subject">>, type = 'text-single', label = ?T(Lang, <<"Subject">>)}, #xdata_field{var = <<"announcement">>, type = 'text-multi', label = ?T(Lang, <<"Message body">>)}]}}; get_form(_Host, [<<"running nodes">>, _ENode, <<"shutdown">>], Lang) -> Make_option = fun (LabelNum, LabelUnit, Value) -> #xdata_option{ label = <>, value = Value} end, {result, #xdata{title = ?T(Lang, <<"Shut Down Service">>), type = form, fields = [?HFIELD(), #xdata_field{ type = 'list-single', label = ?T(Lang, <<"Time delay">>), var = <<"delay">>, required = true, options = [Make_option(<<"">>, <<"immediately">>, <<"1">>), Make_option(<<"15 ">>, <<"seconds">>, <<"15">>), Make_option(<<"30 ">>, <<"seconds">>, <<"30">>), Make_option(<<"60 ">>, <<"seconds">>, <<"60">>), Make_option(<<"90 ">>, <<"seconds">>, <<"90">>), Make_option(<<"2 ">>, <<"minutes">>, <<"120">>), Make_option(<<"3 ">>, <<"minutes">>, <<"180">>), Make_option(<<"4 ">>, <<"minutes">>, <<"240">>), Make_option(<<"5 ">>, <<"minutes">>, <<"300">>), Make_option(<<"10 ">>, <<"minutes">>, <<"600">>), Make_option(<<"15 ">>, <<"minutes">>, <<"900">>), Make_option(<<"30 ">>, <<"minutes">>, <<"1800">>)]}, #xdata_field{type = fixed, label = ?T(Lang, <<"Send announcement to all online users " "on all hosts">>)}, #xdata_field{var = <<"subject">>, type = 'text-single', label = ?T(Lang, <<"Subject">>)}, #xdata_field{var = <<"announcement">>, type = 'text-multi', label = ?T(Lang, <<"Message body">>)}]}}; get_form(Host, [<<"config">>, <<"acls">>], Lang) -> ACLs = str:tokens( str:format("~p.", [mnesia:dirty_select( acl, ets:fun2ms( fun({acl, {Name, H}, Spec}) when H == Host -> {acl, Name, Spec} end))]), <<"\n">>), {result, #xdata{title = ?T(Lang, <<"Access Control List Configuration">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'text-multi', label = ?T(Lang, <<"Access control lists">>), var = <<"acls">>, values = ACLs}]}}; get_form(Host, [<<"config">>, <<"access">>], Lang) -> Accs = str:tokens( str:format("~p.", [mnesia:dirty_select( access, ets:fun2ms( fun({access, {Name, H}, Acc}) when H == Host -> {access, Name, Acc} end))]), <<"\n">>), {result, #xdata{title = ?T(Lang, <<"Access Configuration">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'text-multi', label = ?T(Lang, <<"Access rules">>), var = <<"access">>, values = Accs}]}}; get_form(_Host, ?NS_ADMINL(<<"add-user">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"Add User">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-single', label = ?T(Lang, <<"Jabber ID">>), required = true, var = <<"accountjid">>}, #xdata_field{type = 'text-private', label = ?T(Lang, <<"Password">>), required = true, var = <<"password">>}, #xdata_field{type = 'text-private', label = ?T(Lang, <<"Password Verification">>), required = true, var = <<"password-verify">>}]}}; get_form(_Host, ?NS_ADMINL(<<"delete-user">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"Delete User">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-multi', label = ?T(Lang, <<"Jabber ID">>), required = true, var = <<"accountjids">>}]}}; get_form(_Host, ?NS_ADMINL(<<"end-user-session">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"End User Session">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-single', label = ?T(Lang, <<"Jabber ID">>), required = true, var = <<"accountjid">>}]}}; get_form(_Host, ?NS_ADMINL(<<"get-user-password">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"Get User Password">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-single', label = ?T(Lang, <<"Jabber ID">>), var = <<"accountjid">>, required = true}]}}; get_form(_Host, ?NS_ADMINL(<<"change-user-password">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"Get User Password">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-single', label = ?T(Lang, <<"Jabber ID">>), required = true, var = <<"accountjid">>}, #xdata_field{type = 'text-private', label = ?T(Lang, <<"Password">>), required = true, var = <<"password">>}]}}; get_form(_Host, ?NS_ADMINL(<<"get-user-lastlogin">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"Get User Last Login Time">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-single', label = ?T(Lang, <<"Jabber ID">>), var = <<"accountjid">>, required = true}]}}; get_form(_Host, ?NS_ADMINL(<<"user-stats">>), Lang) -> {result, #xdata{title = ?T(Lang, <<"Get User Statistics">>), type = form, fields = [?HFIELD(), #xdata_field{type = 'jid-single', label = ?T(Lang, <<"Jabber ID">>), var = <<"accountjid">>, required = true}]}}; get_form(Host, ?NS_ADMINL(<<"get-registered-users-num">>), Lang) -> Num = integer_to_binary(ejabberd_auth:count_users(Host)), {result, completed, #xdata{type = form, fields = [?HFIELD(), #xdata_field{type = 'text-single', label = ?T(Lang, <<"Number of registered users">>), var = <<"registeredusersnum">>, values = [Num]}]}}; get_form(Host, ?NS_ADMINL(<<"get-online-users-num">>), Lang) -> Num = integer_to_binary(ejabberd_sm:get_vh_session_number(Host)), {result, completed, #xdata{type = form, fields = [?HFIELD(), #xdata_field{type = 'text-single', label = ?T(Lang, <<"Number of online users">>), var = <<"onlineusersnum">>, values = [Num]}]}}; get_form(_Host, _, _Lang) -> {error, xmpp:err_service_unavailable()}. set_form(_From, _Host, [<<"running nodes">>, ENode, <<"DB">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> lists:foreach( fun(#xdata_field{var = SVar, values = SVals}) -> Table = misc:binary_to_atom(SVar), Type = case SVals of [<<"unknown">>] -> unknown; [<<"ram_copies">>] -> ram_copies; [<<"disc_copies">>] -> disc_copies; [<<"disc_only_copies">>] -> disc_only_copies; _ -> false end, if Type == false -> ok; Type == unknown -> mnesia:del_table_copy(Table, Node); true -> case mnesia:add_table_copy(Table, Node, Type) of {aborted, _} -> mnesia:change_table_copy_type( Table, Node, Type); _ -> ok end end end, XData#xdata.fields), {result, undefined} end; set_form(_From, Host, [<<"running nodes">>, ENode, <<"modules">>, <<"stop">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> lists:foreach( fun(#xdata_field{var = Var, values = Vals}) -> case Vals of [<<"1">>] -> Module = misc:binary_to_atom(Var), ejabberd_cluster:call(Node, gen_mod, stop_module, [Host, Module]); _ -> ok end end, XData#xdata.fields), {result, undefined} end; set_form(_From, Host, [<<"running nodes">>, ENode, <<"modules">>, <<"start">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case xmpp_util:get_xdata_values(<<"modules">>, XData) of [] -> Txt = <<"No 'modules' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; Strings -> String = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, Modules} -> lists:foreach( fun ({Module, Args}) -> ejabberd_cluster:call( Node, gen_mod, start_module, [Host, Module, Args]) end, Modules), {result, undefined}; _ -> Txt = <<"Parse failed">>, {error, xmpp:err_bad_request(Txt, Lang)} end; _ -> Txt = <<"Scan failed">>, {error, xmpp:err_bad_request(Txt, Lang)} end end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"backup">>, <<"backup">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case xmpp_util:get_xdata_values(<<"path">>, XData) of [] -> Txt = <<"No 'path' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; [String] -> case ejabberd_cluster:call(Node, mnesia, backup, [String]) of {badrpc, Reason} -> ?ERROR_MSG("RPC call mnesia:backup(~s) to node ~s " "failed: ~p", [String, Node, Reason]), {error, xmpp:err_internal_server_error()}; {error, Reason} -> ?ERROR_MSG("RPC call mnesia:backup(~s) to node ~s " "failed: ~p", [String, Node, Reason]), {error, xmpp:err_internal_server_error()}; _ -> {result, undefined} end; _ -> Txt = <<"Incorrect value of 'path' in data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"backup">>, <<"restore">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case xmpp_util:get_xdata_values(<<"path">>, XData) of [] -> Txt = <<"No 'path' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; [String] -> case ejabberd_cluster:call(Node, ejabberd_admin, restore, [String]) of {badrpc, Reason} -> ?ERROR_MSG("RPC call ejabberd_admin:restore(~s) to node " "~s failed: ~p", [String, Node, Reason]), {error, xmpp:err_internal_server_error()}; {error, Reason} -> ?ERROR_MSG("RPC call ejabberd_admin:restore(~s) to node " "~s failed: ~p", [String, Node, Reason]), {error, xmpp:err_internal_server_error()}; _ -> {result, undefined} end; _ -> Txt = <<"Incorrect value of 'path' in data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"backup">>, <<"textfile">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case xmpp_util:get_xdata_values(<<"path">>, XData) of [] -> Txt = <<"No 'path' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; [String] -> case ejabberd_cluster:call(Node, ejabberd_admin, dump_to_textfile, [String]) of {badrpc, Reason} -> ?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~s) " "to node ~s failed: ~p", [String, Node, Reason]), {error, xmpp:err_internal_server_error()}; {error, Reason} -> ?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~s) " "to node ~s failed: ~p", [String, Node, Reason]), {error, xmpp:err_internal_server_error()}; _ -> {result, undefined} end; _ -> Txt = <<"Incorrect value of 'path' in data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"import">>, <<"file">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case xmpp_util:get_xdata_values(<<"path">>, XData) of [] -> Txt = <<"No 'path' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; [String] -> ejabberd_cluster:call(Node, jd2ejd, import_file, [String]), {result, undefined}; _ -> Txt = <<"Incorrect value of 'path' in data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"import">>, <<"dir">>], Lang, XData) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> case xmpp_util:get_xdata_values(<<"path">>, XData) of [] -> Txt = <<"No 'path' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; [String] -> ejabberd_cluster:call(Node, jd2ejd, import_dir, [String]), {result, undefined}; _ -> Txt = <<"Incorrect value of 'path' in data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end end; set_form(From, Host, [<<"running nodes">>, ENode, <<"restart">>], _Lang, XData) -> stop_node(From, Host, ENode, restart, XData); set_form(From, Host, [<<"running nodes">>, ENode, <<"shutdown">>], _Lang, XData) -> stop_node(From, Host, ENode, stop, XData); set_form(_From, Host, [<<"config">>, <<"acls">>], Lang, XData) -> case xmpp_util:get_xdata_values(<<"acls">>, XData) of [] -> Txt = <<"No 'acls' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; Strings -> String = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, ACLs} -> acl:add_list(Host, ACLs, true), {result, undefined}; _ -> Txt = <<"Parse failed">>, {error, xmpp:err_bad_request(Txt, Lang)} end; _ -> {error, xmpp:err_bad_request(<<"Scan failed">>, Lang)} end end; set_form(_From, Host, [<<"config">>, <<"access">>], Lang, XData) -> SetAccess = fun(Rs) -> mnesia:transaction( fun () -> Os = mnesia:select( access, ets:fun2ms( fun({access, {_, H}, _} = O) when H == Host -> O end)), lists:foreach(fun mnesia:delete_object/1, Os), lists:foreach( fun({access, Name, Rules}) -> mnesia:write({access, {Name, Host}, Rules}) end, Rs) end) end, case xmpp_util:get_xdata_values(<<"access">>, XData) of [] -> Txt = <<"No 'access' found in data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; Strings -> String = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, Rs} -> case SetAccess(Rs) of {atomic, _} -> {result, undefined}; _ -> {error, xmpp:err_bad_request()} end; _ -> Txt = <<"Parse failed">>, {error, xmpp:err_bad_request(Txt, Lang)} end; _ -> {error, xmpp:err_bad_request(<<"Scan failed">>, Lang)} end end; set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), Password = get_value(<<"password">>, XData), Password = get_value(<<"password-verify">>, XData), AccountJID = jid:decode(AccountString), User = AccountJID#jid.luser, Server = AccountJID#jid.lserver, true = lists:member(Server, ?MYHOSTS), true = Server == Host orelse get_permission_level(From) == global, ejabberd_auth:try_register(User, Server, Password), {result, undefined}; set_form(From, Host, ?NS_ADMINL(<<"delete-user">>), _Lang, XData) -> AccountStringList = get_values(<<"accountjids">>, XData), [_ | _] = AccountStringList, ASL2 = lists:map(fun (AccountString) -> JID = jid:decode(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, true = ejabberd_auth:user_exists(User, Server), {User, Server} end, AccountStringList), [ejabberd_auth:remove_user(User, Server) || {User, Server} <- ASL2], {result, undefined}; set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jid:decode(AccountString), LUser = JID#jid.luser, LServer = JID#jid.lserver, true = LServer == Host orelse get_permission_level(From) == global, Xmlelement = xmpp:serr_policy_violation(<<"has been kicked">>, Lang), case JID#jid.lresource of <<>> -> SIs = mnesia:dirty_select(session, [{#session{usr = {LUser, LServer, '_'}, sid = '$1', info = '$2', _ = '_'}, [], [{{'$1', '$2'}}]}]), Pids = [P || {{_, P}, Info} <- SIs, not proplists:get_bool(offline, Info)], lists:foreach(fun(Pid) -> Pid ! {kick, kicked_by_admin, Xmlelement} end, Pids); R -> [{{_, Pid}, Info}] = mnesia:dirty_select( session, [{#session{usr = {LUser, LServer, R}, sid = '$1', info = '$2', _ = '_'}, [], [{{'$1', '$2'}}]}]), case proplists:get_bool(offline, Info) of true -> ok; false -> Pid ! {kick, kicked_by_admin, Xmlelement} end end, {result, undefined}; set_form(From, Host, ?NS_ADMINL(<<"get-user-password">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jid:decode(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, Password = ejabberd_auth:get_password(User, Server), true = is_binary(Password), {result, #xdata{type = form, fields = [?HFIELD(), ?XFIELD('jid-single', <<"Jabber ID">>, <<"accountjid">>, AccountString), ?XFIELD('text-single', <<"Password">>, <<"password">>, Password)]}}; set_form(From, Host, ?NS_ADMINL(<<"change-user-password">>), _Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), Password = get_value(<<"password">>, XData), JID = jid:decode(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, true = ejabberd_auth:user_exists(User, Server), ejabberd_auth:set_password(User, Server, Password), {result, undefined}; set_form(From, Host, ?NS_ADMINL(<<"get-user-lastlogin">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jid:decode(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, FLast = case ejabberd_sm:get_user_resources(User, Server) of [] -> case get_last_info(User, Server) of not_found -> ?T(Lang, <<"Never">>); {ok, Timestamp, _Status} -> Shift = Timestamp, TimeStamp = {Shift div 1000000, Shift rem 1000000, 0}, {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(TimeStamp), (str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second])) end; _ -> ?T(Lang, <<"Online">>) end, {result, #xdata{type = form, fields = [?HFIELD(), ?XFIELD('jid-single', <<"Jabber ID">>, <<"accountjid">>, AccountString), ?XFIELD('text-single', <<"Last login">>, <<"lastlogin">>, FLast)]}}; set_form(From, Host, ?NS_ADMINL(<<"user-stats">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jid:decode(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, Resources = ejabberd_sm:get_user_resources(User, Server), IPs1 = [ejabberd_sm:get_user_ip(User, Server, Resource) || Resource <- Resources], IPs = [<<(misc:ip_to_list(IP))/binary, ":", (integer_to_binary(Port))/binary>> || {IP, Port} <- IPs1], Items = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]), Rostersize = integer_to_binary(erlang:length(Items)), {result, #xdata{type = form, fields = [?HFIELD(), ?XFIELD('jid-single', <<"Jabber ID">>, <<"accountjid">>, AccountString), ?XFIELD('text-single', <<"Roster size">>, <<"rostersize">>, Rostersize), ?XMFIELD('text-multi', <<"IP addresses">>, <<"ipaddresses">>, IPs), ?XMFIELD('text-multi', <<"Resources">>, <<"onlineresources">>, Resources)]}}; set_form(_From, _Host, _, _Lang, _XData) -> {error, xmpp:err_service_unavailable()}. get_value(Field, XData) -> hd(get_values(Field, XData)). get_values(Field, XData) -> xmpp_util:get_xdata_values(Field, XData). search_running_node(SNode) -> search_running_node(SNode, mnesia:system_info(running_db_nodes)). search_running_node(_, []) -> false; search_running_node(SNode, [Node | Nodes]) -> case iolist_to_binary(atom_to_list(Node)) of SNode -> Node; _ -> search_running_node(SNode, Nodes) end. stop_node(From, Host, ENode, Action, XData) -> Delay = binary_to_integer(get_value(<<"delay">>, XData)), Subject = case get_value(<<"subject">>, XData) of <<"">> -> []; S -> [#xdata_field{var = <<"subject">>, values = [S]}] end, Announcement = case get_values(<<"announcement">>, XData) of [] -> []; As -> [#xdata_field{var = <<"body">>, values = As}] end, case Subject ++ Announcement of [] -> ok; Fields -> Request = #adhoc_command{node = ?NS_ADMINX(<<"announce-allhosts">>), action = complete, xdata = #xdata{type = submit, fields = Fields}}, To = jid:make(Host), mod_announce:announce_commands(empty, From, To, Request) end, Time = timer:seconds(Delay), Node = misc:binary_to_atom(ENode), {ok, _} = timer:apply_after(Time, rpc, call, [Node, init, Action, []]), {result, undefined}. get_last_info(User, Server) -> case gen_mod:is_loaded(Server, mod_last) of true -> mod_last:get_last_info(User, Server); false -> not_found end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec adhoc_sm_commands(adhoc_command(), jid(), jid(), adhoc_command()) -> adhoc_command(). adhoc_sm_commands(_Acc, From, #jid{user = User, server = Server, lserver = LServer}, #adhoc_command{lang = Lang, node = <<"config">>, action = Action, xdata = XData} = Request) -> case acl:match_rule(LServer, configure, From) of deny -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; allow -> ActionIsExecute = Action == execute orelse Action == complete, if Action == cancel -> xmpp_util:make_adhoc_response( Request, #adhoc_command{status = canceled}); XData == undefined, ActionIsExecute -> case get_sm_form(User, Server, <<"config">>, Lang) of {result, Form} -> xmpp_util:make_adhoc_response( Request, #adhoc_command{status = executing, xdata = Form}); {error, Error} -> {error, Error} end; XData /= undefined, ActionIsExecute -> set_sm_form(User, Server, <<"config">>, Request); true -> Txt = <<"Unexpected action">>, {error, xmpp:err_bad_request(Txt, Lang)} end end; adhoc_sm_commands(Acc, _From, _To, _Request) -> Acc. get_sm_form(User, Server, <<"config">>, Lang) -> {result, #xdata{type = form, title = <<(?T(Lang, <<"Administration of ">>))/binary, User/binary>>, fields = [?HFIELD(), #xdata_field{ type = 'list-single', label = ?T(Lang, <<"Action on user">>), var = <<"action">>, values = [<<"edit">>], options = [#xdata_option{ label = ?T(Lang, <<"Edit Properties">>), value = <<"edit">>}, #xdata_option{ label = ?T(Lang, <<"Remove User">>), value = <<"remove">>}]}, ?XFIELD('text-private', <<"Password">>, <<"password">>, ejabberd_auth:get_password_s(User, Server))]}}; get_sm_form(_User, _Server, _Node, _Lang) -> {error, xmpp:err_service_unavailable()}. set_sm_form(User, Server, <<"config">>, #adhoc_command{lang = Lang, node = Node, sid = SessionID, xdata = XData}) -> Response = #adhoc_command{lang = Lang, node = Node, sid = SessionID, status = completed}, case xmpp_util:get_xdata_values(<<"action">>, XData) of [<<"edit">>] -> case xmpp_util:get_xdata_values(<<"password">>, XData) of [Password] -> ejabberd_auth:set_password(User, Server, Password), xmpp_util:make_adhoc_response(Response); _ -> Txt = <<"No 'password' found in data form">>, {error, xmpp:err_not_acceptable(Txt, Lang)} end; [<<"remove">>] -> catch ejabberd_auth:remove_user(User, Server), xmpp_util:make_adhoc_response(Response); _ -> Txt = <<"Incorrect value of 'action' in data form">>, {error, xmpp:err_not_acceptable(Txt, Lang)} end; set_sm_form(_User, _Server, _Node, _Request) -> {error, xmpp:err_service_unavailable()}. mod_opt_type(_) -> []. ejabberd-18.01/src/ejabberd_piefxis.erl0000644000232200023220000005037113225664356020431 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_piefxis.erl %%% Author : Pablo Polvorin, Vidal Santiago Martinez, Evgeniy Khramtsov %%% Purpose : XEP-0227: Portable Import/Export Format for XMPP-IM Servers %%% Created : 17 Jul 2008 by Pablo Polvorin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% Not implemented: %%% - write mod_piefxis with ejabberdctl commands %%% - Export from mod_offline_sql.erl %%% - Export from mod_private_sql.erl %%% - XEP-227: 6. Security Considerations %%% - Other schemas of XInclude are not tested, and may not be imported correctly. %%% - If a host has many users, split that host in XML files with 50 users each. %%%% Headers -module(ejabberd_piefxis). -protocol({xep, 227, '1.0'}). -export([import_file/1, export_server/1, export_host/2]). -define(CHUNK_SIZE, 1024*20). %20k -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_privacy.hrl"). -include("mod_roster.hrl"). %%-include_lib("exmpp/include/exmpp.hrl"). %%-include_lib("exmpp/include/exmpp_client.hrl"). %% Copied from exmpp header files: %% Copied from mod_private.erl %%-define(ERROR_MSG(M,Args),io:format(M,Args)). %%-define(INFO_MSG(M,Args),ok). %%%================================== %%%% Import file -define(NS_PIE, <<"urn:xmpp:pie:0">>). -define(NS_PIEFXIS, <<"http://www.xmpp.org/extensions/xep-0227.html#ns">>). -define(NS_XI, <<"http://www.w3.org/2001/XInclude">>). -record(state, {xml_stream_state :: fxml_stream:xml_stream_state() | undefined, user = <<"">> :: binary(), server = <<"">> :: binary(), fd = self() :: file:io_device(), dir = <<"">> :: binary()}). -type state() :: #state{}. %%File could be large.. we read it in chunks %%%=================================================================== %%% API %%%=================================================================== import_file(FileName) -> import_file(FileName, #state{}). -spec import_file(binary(), state()) -> ok | {error, atom()}. import_file(FileName, State) -> case file:open(FileName, [read, binary]) of {ok, Fd} -> Dir = filename:dirname(FileName), XMLStreamState = fxml_stream:new(self(), infinity), Res = process(State#state{xml_stream_state = XMLStreamState, fd = Fd, dir = Dir}), file:close(Fd), Res; {error, Reason} -> ErrTxt = file:format_error(Reason), ?ERROR_MSG("Failed to open file '~s': ~s", [FileName, ErrTxt]), {error, Reason} end. -spec export_server(binary()) -> any(). export_server(Dir) -> export_hosts(?MYHOSTS, Dir). -spec export_host(binary(), binary()) -> any(). export_host(Dir, Host) -> export_hosts([Host], Dir). %%%=================================================================== %%% Internal functions %%%=================================================================== export_hosts(Hosts, Dir) -> FnT = make_filename_template(), DFn = make_main_basefilename(Dir, FnT), case file:open(DFn, [raw, write]) of {ok, Fd} -> print(Fd, make_piefxis_xml_head()), print(Fd, make_piefxis_server_head()), FilesAndHosts = [{make_host_filename(FnT, Host), Host} || Host <- Hosts], lists:foreach( fun({FnH, _}) -> print(Fd, make_xinclude(FnH)) end, FilesAndHosts), print(Fd, make_piefxis_server_tail()), print(Fd, make_piefxis_xml_tail()), file:close(Fd), lists:foldl( fun({FnH, Host}, ok) -> export_host(Dir, FnH, Host); (_, Err) -> Err end, ok, FilesAndHosts); {error, Reason} -> ErrTxt = file:format_error(Reason), ?ERROR_MSG("Failed to open file '~s': ~s", [DFn, ErrTxt]), {error, Reason} end. export_host(Dir, FnH, Host) -> DFn = make_host_basefilename(Dir, FnH), case file:open(DFn, [raw, write]) of {ok, Fd} -> print(Fd, make_piefxis_xml_head()), print(Fd, make_piefxis_host_head(Host)), Users = ejabberd_auth:get_users(Host), case export_users(Users, Host, Fd) of ok -> print(Fd, make_piefxis_host_tail()), print(Fd, make_piefxis_xml_tail()), file:close(Fd), ok; Err -> file:close(Fd), file:delete(DFn), Err end; {error, Reason} -> ErrTxt = file:format_error(Reason), ?ERROR_MSG("Failed to open file '~s': ~s", [DFn, ErrTxt]), {error, Reason} end. export_users([{User, _S}|Users], Server, Fd) -> case export_user(User, Server, Fd) of ok -> export_users(Users, Server, Fd); Err -> Err end; export_users([], _Server, _Fd) -> ok. export_user(User, Server, Fd) -> Password = ejabberd_auth:get_password_s(User, Server), LServer = jid:nameprep(Server), PasswordFormat = ejabberd_auth:password_format(LServer), Pass = case Password of {_,_,_,_} -> case PasswordFormat of scram -> format_scram_password(Password); _ -> <<"">> end; _ -> Password end, Els = get_offline(User, Server) ++ get_vcard(User, Server) ++ get_privacy(User, Server) ++ get_roster(User, Server) ++ get_private(User, Server), print(Fd, fxml:element_to_binary( #xmlel{name = <<"user">>, attrs = [{<<"name">>, User}, {<<"password">>, Pass}], children = Els})). format_scram_password({StoredKey, ServerKey, Salt, IterationCount}) -> StoredKeyB64 = base64:encode(StoredKey), ServerKeyB64 = base64:encode(ServerKey), SaltB64 = base64:encode(Salt), IterationCountBin = (integer_to_binary(IterationCount)), <<"scram:", StoredKeyB64/binary, ",", ServerKeyB64/binary, ",", SaltB64/binary, ",", IterationCountBin/binary>>. parse_scram_password(PassData) -> Split = binary:split(PassData, <<",">>, [global]), [StoredKeyB64, ServerKeyB64, SaltB64, IterationCountBin] = Split, #scram{ storedkey = StoredKeyB64, serverkey = ServerKeyB64, salt = SaltB64, iterationcount = (binary_to_integer(IterationCountBin)) }. -spec get_vcard(binary(), binary()) -> [xmlel()]. get_vcard(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), case mod_vcard:get_vcard(LUser, LServer) of error -> []; Els -> Els end. -spec get_offline(binary(), binary()) -> [xmlel()]. get_offline(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), case mod_offline:get_offline_els(LUser, LServer) of [] -> []; Els -> NewEls = lists:map(fun xmpp:encode/1, Els), [#xmlel{name = <<"offline-messages">>, children = NewEls}] end. -spec get_privacy(binary(), binary()) -> [xmlel()]. get_privacy(User, Server) -> case mod_privacy:get_user_lists(User, Server) of {ok, #privacy{default = Default, lists = [_|_] = Lists}} -> XLists = lists:map( fun({Name, Items}) -> XItems = lists:map( fun mod_privacy:encode_list_item/1, Items), #privacy_list{name = Name, items = XItems} end, Lists), [xmpp:encode(#privacy_query{default = Default, lists = XLists})]; _ -> [] end. -spec get_roster(binary(), binary()) -> [xmlel()]. get_roster(User, Server) -> JID = jid:make(User, Server), case mod_roster:get_roster(User, Server) of [_|_] = Items -> Subs = lists:flatmap( fun(#roster{ask = Ask, askmessage = Msg} = R) when Ask == in; Ask == both -> Status = if is_binary(Msg) -> (Msg); true -> <<"">> end, [xmpp:encode( #presence{from = jid:make(R#roster.jid), to = JID, type = subscribe, status = xmpp:mk_text(Status)})]; (_) -> [] end, Items), Rs = lists:flatmap( fun(#roster{ask = in, subscription = none}) -> []; (R) -> [mod_roster:encode_item(R)] end, Items), [xmpp:encode(#roster_query{items = Rs}) | Subs]; _ -> [] end. -spec get_private(binary(), binary()) -> [xmlel()]. get_private(User, Server) -> case mod_private:get_data(User, Server) of [_|_] = Els -> [xmpp:encode(#private{xml_els = Els})]; _ -> [] end. process(#state{xml_stream_state = XMLStreamState, fd = Fd} = State) -> case file:read(Fd, ?CHUNK_SIZE) of {ok, Data} -> NewXMLStreamState = fxml_stream:parse(XMLStreamState, Data), case process_els(State#state{xml_stream_state = NewXMLStreamState}) of {ok, NewState} -> process(NewState); Err -> fxml_stream:close(NewXMLStreamState), Err end; eof -> fxml_stream:close(XMLStreamState), ok end. process_els(State) -> receive {'$gen_event', El} -> case process_el(El, State) of {ok, NewState} -> process_els(NewState); Err -> Err end after 0 -> {ok, State} end. process_el({xmlstreamstart, <<"server-data">>, Attrs}, State) -> case fxml:get_attr_s(<<"xmlns">>, Attrs) of ?NS_PIEFXIS -> {ok, State}; ?NS_PIE -> {ok, State}; NS -> stop("Unknown 'server-data' namespace = ~s", [NS]) end; process_el({xmlstreamend, _}, State) -> {ok, State}; process_el({xmlstreamcdata, _}, State) -> {ok, State}; process_el({xmlstreamelement, #xmlel{name = <<"xi:include">>, attrs = Attrs}}, #state{dir = Dir, user = <<"">>} = State) -> FileName = fxml:get_attr_s(<<"href">>, Attrs), case import_file(filename:join([Dir, FileName]), State) of ok -> {ok, State}; Err -> Err end; process_el({xmlstreamstart, <<"host">>, Attrs}, State) -> process_el({xmlstreamelement, #xmlel{name = <<"host">>, attrs = Attrs}}, State); process_el({xmlstreamelement, #xmlel{name = <<"host">>, attrs = Attrs, children = Els}}, State) -> JIDS = fxml:get_attr_s(<<"jid">>, Attrs), try jid:decode(JIDS) of #jid{lserver = S} -> case ejabberd_router:is_my_host(S) of true -> process_users(Els, State#state{server = S}); false -> stop("Unknown host: ~s", [S]) end catch _:{bad_jid, _} -> stop("Invalid 'jid': ~s", [JIDS]) end; process_el({xmlstreamstart, <<"user">>, Attrs}, State = #state{server = S}) when S /= <<"">> -> process_el({xmlstreamelement, #xmlel{name = <<"user">>, attrs = Attrs}}, State); process_el({xmlstreamelement, #xmlel{name = <<"user">>} = El}, State = #state{server = S}) when S /= <<"">> -> process_user(El, State); process_el({xmlstreamelement, El}, State = #state{server = S, user = U}) when S /= <<"">>, U /= <<"">> -> process_user_el(El, State); process_el({xmlstreamelement, El}, _State) -> stop("Unexpected tag: ~p", [El]); process_el({xmlstreamstart, El, Attrs}, _State) -> stop("Unexpected payload: ~p", [{El, Attrs}]); process_el({xmlstreamerror, Err}, _State) -> stop("Failed to process element = ~p", [Err]). process_users([#xmlel{} = El|Els], State) -> case process_user(El, State) of {ok, NewState} -> process_users(Els, NewState); Err -> Err end; process_users([_|Els], State) -> process_users(Els, State); process_users([], State) -> {ok, State}. process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els}, #state{server = LServer} = State) -> Name = fxml:get_attr_s(<<"name">>, Attrs), Password = fxml:get_attr_s(<<"password">>, Attrs), PasswordFormat = ejabberd_auth:password_format(LServer), Pass = case PasswordFormat of scram -> case Password of <<"scram:", PassData/binary>> -> parse_scram_password(PassData); P -> P end; _ -> Password end, case jid:nodeprep(Name) of error -> stop("Invalid 'user': ~s", [Name]); LUser -> case ejabberd_auth:try_register(LUser, LServer, Pass) of ok -> process_user_els(Els, State#state{user = LUser}); {error, Err} -> stop("Failed to create user '~s': ~p", [Name, Err]) end end. process_user_els([#xmlel{} = El|Els], State) -> case process_user_el(El, State) of {ok, NewState} -> process_user_els(Els, NewState); Err -> Err end; process_user_els([_|Els], State) -> process_user_els(Els, State); process_user_els([], State) -> {ok, State}. process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El, State) -> try case {Name, fxml:get_attr_s(<<"xmlns">>, Attrs)} of {<<"query">>, ?NS_ROSTER} -> process_roster(xmpp:decode(El), State); {<<"query">>, ?NS_PRIVACY} -> %% Make sure elements go before and process_privacy(xmpp:decode(El), State); {<<"query">>, ?NS_PRIVATE} -> process_private(xmpp:decode(El), State); {<<"vCard">>, ?NS_VCARD} -> process_vcard(El, State); {<<"offline-messages">>, NS} -> Msgs = [xmpp:decode(E, NS, [ignore_els]) || E <- Els], process_offline_msgs(Msgs, State); {<<"presence">>, ?NS_CLIENT} -> process_presence(xmpp:decode(El, ?NS_CLIENT, [ignore_els]), State); _ -> {ok, State} end catch _:{xmpp_codec, Why} -> ErrTxt = xmpp:format_error(Why), stop("failed to decode XML '~s': ~s", [fxml:element_to_binary(El), ErrTxt]) end. -spec process_offline_msgs([stanza()], state()) -> {ok, state()} | {error, _}. process_offline_msgs([#message{} = Msg|Msgs], State) -> case process_offline_msg(Msg, State) of {ok, NewState} -> process_offline_msgs(Msgs, NewState); Err -> Err end; process_offline_msgs([_|Msgs], State) -> process_offline_msgs(Msgs, State); process_offline_msgs([], State) -> {ok, State}. -spec process_roster(roster_query(), state()) -> {ok, state()} | {error, _}. process_roster(RosterQuery, State = #state{user = U, server = S}) -> case mod_roster:set_items(U, S, RosterQuery) of {atomic, _} -> {ok, State}; Err -> stop("Failed to write roster: ~p", [Err]) end. -spec process_privacy(privacy_query(), state()) -> {ok, state()} | {error, _}. process_privacy(#privacy_query{lists = Lists, default = Default, active = Active} = PrivacyQuery, State = #state{user = U, server = S}) -> JID = jid:make(U, S), IQ = #iq{type = set, id = randoms:get_string(), from = JID, to = JID, sub_els = [PrivacyQuery]}, case mod_privacy:process_iq(IQ) of #iq{type = error} = ResIQ -> #stanza_error{reason = Reason} = xmpp:get_error(ResIQ), if Reason == 'item-not-found', Lists == [], Active == undefined, Default /= undefined -> %% Failed to set default list because there is no %% list with such name. We shouldn't stop here. {ok, State}; true -> stop("Failed to write privacy: ~p", [Reason]) end; _ -> {ok, State} end. -spec process_private(private(), state()) -> {ok, state()} | {error, _}. process_private(Private, State = #state{user = U, server = S}) -> JID = jid:make(U, S), IQ = #iq{type = set, id = randoms:get_string(), from = JID, to = JID, sub_els = [Private]}, case mod_private:process_sm_iq(IQ) of #iq{type = result} -> {ok, State}; Err -> stop("Failed to write private: ~p", [Err]) end. -spec process_vcard(xmlel(), state()) -> {ok, state()} | {error, _}. process_vcard(El, State = #state{user = U, server = S}) -> JID = jid:make(U, S), IQ = #iq{type = set, id = randoms:get_string(), from = JID, to = JID, sub_els = [El]}, case mod_vcard:process_sm_iq(IQ) of #iq{type = result} -> {ok, State}; Err -> stop("Failed to write vcard: ~p", [Err]) end. -spec process_offline_msg(message(), state()) -> {ok, state()} | {error, _}. process_offline_msg(#message{from = undefined}, _State) -> stop("No 'from' attribute found", []); process_offline_msg(Msg, State = #state{user = U, server = S}) -> To = jid:make(U, S), ejabberd_hooks:run_fold( offline_message_hook, To#jid.lserver, {pass, xmpp:set_to(Msg, To)}, []), {ok, State}. -spec process_presence(presence(), state()) -> {ok, state()} | {error, _}. process_presence(#presence{from = undefined}, _State) -> stop("No 'from' attribute found", []); process_presence(Pres, #state{user = U, server = S} = State) -> To = jid:make(U, S), NewPres = xmpp:set_to(Pres, To), ejabberd_router:route(NewPres), {ok, State}. stop(Fmt, Args) -> ?ERROR_MSG(Fmt, Args), {error, import_failed}. make_filename_template() -> {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(), str:format("~4..0w~2..0w~2..0w-~2..0w~2..0w~2..0w", [Year, Month, Day, Hour, Minute, Second]). make_main_basefilename(Dir, FnT) -> Filename2 = <>, filename:join([Dir, Filename2]). %% @doc Make the filename for the host. %% Example: ``(<<"20080804-231550">>, <<"jabber.example.org">>) -> %% <<"20080804-231550_jabber_example_org.xml">>'' make_host_filename(FnT, Host) -> Host2 = str:join(str:tokens(Host, <<".">>), <<"_">>), <>. %%%================================== %%%% PIEFXIS formatting make_host_basefilename(Dir, FnT) -> filename:join([Dir, FnT]). %% @spec () -> string() make_piefxis_xml_head() -> "". %% @spec () -> string() make_piefxis_xml_tail() -> "". %% @spec () -> string() make_piefxis_server_head() -> io_lib:format("", [?NS_PIE, ?NS_XI]). %% @spec () -> string() make_piefxis_server_tail() -> "". %% @spec (Host::string()) -> string() make_piefxis_host_head(Host) -> io_lib:format("", [?NS_PIE, ?NS_XI, Host]). %% @spec () -> string() make_piefxis_host_tail() -> "". %% @spec (Fn::string()) -> string() make_xinclude(Fn) -> Base = filename:basename(Fn), io_lib:format("", [Base]). print(Fd, String) -> file:write(Fd, String). ejabberd-18.01/src/jd2ejd.erl0000644000232200023220000001214013225664356016276 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : jd2ejd.erl %%% Author : Alexey Shchepin %%% Purpose : Import of jabberd14 user spool file %%% Created : 2 Feb 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(jd2ejd). -author('alexey@process-one.net'). %% External exports -export([import_file/1, import_dir/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- import_file(File) -> User = filename:rootname(filename:basename(File)), Server = filename:basename(filename:dirname(File)), case jid:nodeprep(User) /= error andalso jid:nameprep(Server) /= error of true -> case file:read_file(File) of {ok, Text} -> case fxml_stream:parse_element(Text) of El when is_record(El, xmlel) -> case catch process_xdb(User, Server, El) of {'EXIT', Reason} -> ?ERROR_MSG("Error while processing file \"~s\": " "~p~n", [File, Reason]), {error, Reason}; _ -> ok end; {error, Reason} -> ?ERROR_MSG("Can't parse file \"~s\": ~p~n", [File, Reason]), {error, Reason} end; {error, Reason} -> ?ERROR_MSG("Can't read file \"~s\": ~p~n", [File, Reason]), {error, Reason} end; false -> ?ERROR_MSG("Illegal user/server name in file \"~s\"~n", [File]), {error, <<"illegal user/server">>} end. import_dir(Dir) -> {ok, Files} = file:list_dir(Dir), MsgFiles = lists:filter(fun (FN) -> case length(FN) > 4 of true -> string:substr(FN, length(FN) - 3) == ".xml"; _ -> false end end, Files), lists:foldl(fun (FN, A) -> Res = import_file(filename:join([Dir, FN])), case {A, Res} of {ok, ok} -> ok; {ok, _} -> {error, <<"see ejabberd log for details">>}; _ -> A end end, ok, MsgFiles). %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- process_xdb(User, Server, #xmlel{name = Name, children = Els}) -> case Name of <<"xdb">> -> lists:foreach(fun (El) -> xdb_data(User, Server, El) end, Els); _ -> ok end. xdb_data(_User, _Server, {xmlcdata, _CData}) -> ok; xdb_data(User, Server, #xmlel{attrs = Attrs} = El) -> From = jid:make(User, Server), LUser = From#jid.luser, LServer = From#jid.lserver, case fxml:get_attr_s(<<"xmlns">>, Attrs) of ?NS_AUTH -> Password = fxml:get_tag_cdata(El), ejabberd_auth:set_password(User, Server, Password), ok; ?NS_ROSTER -> catch mod_roster:set_items(User, Server, xmpp:decode(El)), ok; ?NS_LAST -> TimeStamp = fxml:get_attr_s(<<"last">>, Attrs), Status = fxml:get_tag_cdata(El), catch mod_last:store_last_info(User, Server, binary_to_integer(TimeStamp), Status), ok; ?NS_VCARD -> catch mod_vcard:set_vcard(User, LServer, El), ok; <<"jabber:x:offline">> -> process_offline(Server, From, El), ok; XMLNS -> case fxml:get_attr_s(<<"j_private_flag">>, Attrs) of <<"1">> -> NewAttrs = lists:filter( fun({<<"j_private_flag">>, _}) -> false; ({<<"xdbns">>, _}) -> false; (_) -> true end, Attrs), catch mod_private:set_data( LUser, LServer, [{XMLNS, El#xmlel{attrs = NewAttrs}}]); _ -> ?DEBUG("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS]) end, ok end. process_offline(Server, To, #xmlel{children = Els}) -> LServer = jid:nameprep(Server), lists:foreach( fun(#xmlel{} = El) -> try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of #message{from = JID} = Msg -> From = case JID of undefined -> jid:make(Server); _ -> JID end, ejabberd_hooks:run_fold( offline_message_hook, LServer, {pass, xmpp:set_from_to(Msg, From, To)}, []); _ -> ok catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), ?ERROR_MSG("failed to decode XML '~s': ~s", [fxml:element_to_binary(El), Txt]) end end, Els). ejabberd-18.01/src/mod_private_sql.erl0000644000232200023220000001031413225664356020325 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_private_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_private_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_private). %% API -export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2, import/3, export/1]). -include("xmpp.hrl"). -include("mod_private.hrl"). -include("ejabberd_sql_pt.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. set_data(LUser, LServer, Data) -> F = fun() -> lists:foreach( fun({XMLNS, El}) -> SData = fxml:element_to_binary(El), ?SQL_UPSERT_T( "private_storage", ["!username=%(LUser)s", "!server_host=%(LServer)s", "!namespace=%(XMLNS)s", "data=%(SData)s"]) end, Data) end, case ejabberd_sql:sql_transaction(LServer, F) of {atomic, ok} -> ok; _ -> {error, db_failure} end. get_data(LUser, LServer, XMLNS) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(data)s from private_storage" " where username=%(LUser)s and %(LServer)H" " and namespace=%(XMLNS)s")) of {selected, [{SData}]} -> parse_element(LUser, LServer, SData); {selected, []} -> error; _ -> {error, db_failure} end. get_all_data(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(namespace)s, @(data)s from private_storage" " where username=%(LUser)s and %(LServer)H")) of {selected, []} -> error; {selected, Res} -> {ok, lists:flatmap( fun({_, SData}) -> case parse_element(LUser, LServer, SData) of {ok, El} -> [El]; error -> [] end end, Res)}; _ -> {error, db_failure} end. del_data(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("delete from private_storage" " where username=%(LUser)s and %(LServer)H")) of {updated, _} -> ok; _ -> {error, db_failure} end. export(_Server) -> [{private_storage, fun(Host, #private_storage{usns = {LUser, LServer, XMLNS}, xml = Data}) when LServer == Host -> SData = fxml:element_to_binary(Data), [?SQL("delete from private_storage where" " username=%(LUser)s and %(LServer)H and namespace=%(XMLNS)s;"), ?SQL_INSERT( "private_storage", ["username=%(LUser)s", "server_host=%(LServer)s", "namespace=%(XMLNS)s", "data=%(SData)s"])]; (_Host, _R) -> [] end}]. import(_, _, _) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== parse_element(LUser, LServer, XML) -> case fxml_stream:parse_element(XML) of El when is_record(El, xmlel) -> {ok, El}; _ -> ?ERROR_MSG("malformed XML element in SQL table " "'private_storage' for user ~s@~s: ~s", [LUser, LServer, XML]), error end. ejabberd-18.01/src/mod_register.erl0000644000232200023220000005363613225664356017636 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_register.erl %%% Author : Alexey Shchepin %%% Purpose : Inband registration support %%% Created : 8 Dec 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_register). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -protocol({xep, 77, '2.4'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, stream_feature_register/2, c2s_unauthenticated_packet/2, try_register/5, process_iq/1, send_registration_notifications/3, transform_options/1, transform_module_options/1, mod_opt_type/1, opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, ?MODULE, process_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_REGISTER, ?MODULE, process_iq, IQDisc), ejabberd_hooks:add(c2s_pre_auth_features, Host, ?MODULE, stream_feature_register, 50), ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, c2s_unauthenticated_packet, 50), ejabberd_mnesia:create(?MODULE, mod_register_ip, [{ram_copies, [node()]}, {local_content, true}, {attributes, [key, value]}]), ok. stop(Host) -> ejabberd_hooks:delete(c2s_pre_auth_features, Host, ?MODULE, stream_feature_register, 50), ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, c2s_unauthenticated_packet, 50), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_REGISTER). reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, ?MODULE, process_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_REGISTER, ?MODULE, process_iq, IQDisc); true -> ok end. depends(_Host, _Opts) -> []. -spec stream_feature_register([xmpp_element()], binary()) -> [xmpp_element()]. stream_feature_register(Acc, Host) -> AF = gen_mod:get_module_opt(Host, ?MODULE, access_from, all), case (AF /= none) of true -> [#feature_register{}|Acc]; false -> Acc end. c2s_unauthenticated_packet(#{ip := IP, server := Server} = State, #iq{type = T, sub_els = [_]} = IQ) when T == set; T == get -> try xmpp:try_subtag(IQ, #register{}) of #register{} = Register -> {Address, _} = IP, IQ1 = xmpp:set_els(IQ, [Register]), IQ2 = xmpp:set_from_to(IQ1, jid:make(<<>>), jid:make(Server)), ResIQ = process_iq(IQ2, Address), ResIQ1 = xmpp:set_from_to(ResIQ, jid:make(Server), undefined), {stop, ejabberd_c2s:send(State, ResIQ1)}; false -> State catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Lang = maps:get(lang, State), Err = xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)), {stop, ejabberd_c2s:send(State, Err)} end; c2s_unauthenticated_packet(State, _) -> State. process_iq(#iq{from = From} = IQ) -> process_iq(IQ, jid:tolower(From)). process_iq(#iq{from = From, to = To} = IQ, Source) -> IsCaptchaEnabled = case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, captcha_protected, false) of true -> true; false -> false end, Server = To#jid.lserver, Access = gen_mod:get_module_opt(Server, ?MODULE, access_remove, all), AllowRemove = allow == acl:match_rule(Server, Access, From), process_iq(IQ, Source, IsCaptchaEnabled, AllowRemove). process_iq(#iq{type = set, lang = Lang, sub_els = [#register{remove = true}]} = IQ, _Source, _IsCaptchaEnabled, _AllowRemove = false) -> Txt = <<"Access denied by service policy">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); process_iq(#iq{type = set, lang = Lang, to = To, from = From, sub_els = [#register{remove = true, username = User, password = Password}]} = IQ, _Source, _IsCaptchaEnabled, _AllowRemove = true) -> Server = To#jid.lserver, if is_binary(User) -> case From of #jid{user = User, lserver = Server} -> ejabberd_auth:remove_user(User, Server), xmpp:make_iq_result(IQ); _ -> if is_binary(Password) -> ejabberd_auth:remove_user(User, Server, Password), xmpp:make_iq_result(IQ); true -> Txt = <<"No 'password' found in this query">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end end; true -> case From of #jid{luser = LUser, lserver = Server} -> ResIQ = xmpp:make_iq_result(IQ), ejabberd_router:route(xmpp:set_from_to(ResIQ, From, From)), ejabberd_auth:remove_user(LUser, Server), ignore; _ -> Txt = <<"The query is only allowed from local users">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)) end end; process_iq(#iq{type = set, to = To, sub_els = [#register{username = User, password = Password}]} = IQ, Source, IsCaptchaEnabled, _AllowRemove) when is_binary(User), is_binary(Password) -> Server = To#jid.lserver, try_register_or_set_password( User, Server, Password, IQ, Source, not IsCaptchaEnabled); process_iq(#iq{type = set, to = To, lang = Lang, sub_els = [#register{xdata = #xdata{} = X}]} = IQ, Source, true, _AllowRemove) -> Server = To#jid.lserver, case ejabberd_captcha:process_reply(X) of ok -> case process_xdata_submit(X) of {ok, User, Password} -> try_register_or_set_password( User, Server, Password, IQ, Source, true); _ -> Txt = <<"Incorrect data form">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end; {error, malformed} -> Txt = <<"Incorrect CAPTCHA submit">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); _ -> ErrText = <<"The CAPTCHA verification has failed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(ErrText, Lang)) end; process_iq(#iq{type = set} = IQ, _Source, _IsCaptchaEnabled, _AllowRemove) -> xmpp:make_error(IQ, xmpp:err_bad_request()); process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ, Source, IsCaptchaEnabled, _AllowRemove) -> Server = To#jid.lserver, {IsRegistered, Username} = case From of #jid{user = User, lserver = Server} -> case ejabberd_auth:user_exists(User, Server) of true -> {true, User}; false -> {false, User} end; _ -> {false, <<"">>} end, Instr = translate:translate( Lang, <<"Choose a username and password to register " "with this server">>), URL = gen_mod:get_module_opt(Server, ?MODULE, redirect_url, <<"">>), if (URL /= <<"">>) and not IsRegistered -> Txt = translate:translate(Lang, <<"To register, visit ~s">>), Desc = str:format(Txt, [URL]), xmpp:make_iq_result( IQ, #register{instructions = Desc, sub_els = [#oob_x{url = URL}]}); IsCaptchaEnabled and not IsRegistered -> TopInstr = translate:translate( Lang, <<"You need a client that supports x:data " "and CAPTCHA to register">>), UField = #xdata_field{type = 'text-single', label = translate:translate(Lang, <<"User">>), var = <<"username">>, required = true}, PField = #xdata_field{type = 'text-private', label = translate:translate(Lang, <<"Password">>), var = <<"password">>, required = true}, X = #xdata{type = form, instructions = [Instr], fields = [UField, PField]}, case ejabberd_captcha:create_captcha_x(ID, To, Lang, Source, X) of {ok, CaptchaEls} -> xmpp:make_iq_result( IQ, #register{instructions = TopInstr, sub_els = CaptchaEls}); {error, limit} -> ErrText = <<"Too many CAPTCHA requests">>, xmpp:make_error( IQ, xmpp:err_resource_constraint(ErrText, Lang)); _Err -> ErrText = <<"Unable to generate a CAPTCHA">>, xmpp:make_error( IQ, xmpp:err_internal_server_error(ErrText, Lang)) end; true -> xmpp:make_iq_result( IQ, #register{instructions = Instr, username = Username, password = <<"">>, registered = IsRegistered}) end. try_register_or_set_password(User, Server, Password, #iq{from = From, lang = Lang} = IQ, Source, CaptchaSucceed) -> case From of #jid{user = User, lserver = Server} -> try_set_password(User, Server, Password, IQ); _ when CaptchaSucceed -> case check_from(From, Server) of allow -> case try_register(User, Server, Password, Source, Lang) of ok -> xmpp:make_iq_result(IQ); {error, Error} -> xmpp:make_error(IQ, Error) end; deny -> Txt = <<"Access denied by service policy">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)) end; _ -> xmpp:make_error(IQ, xmpp:err_not_allowed()) end. %% @doc Try to change password and return IQ response try_set_password(User, Server, Password, #iq{lang = Lang, meta = M} = IQ) -> case is_strong_password(Server, Password) of true -> case ejabberd_auth:set_password(User, Server, Password) of ok -> ?INFO_MSG("~s has changed password from ~s", [jid:encode({User, Server, <<"">>}), ejabberd_config:may_hide_data( misc:ip_to_list(maps:get(ip, M, {0,0,0,0})))]), xmpp:make_iq_result(IQ); {error, empty_password} -> Txt = <<"Empty password">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); {error, not_allowed} -> Txt = <<"Changing password is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); {error, invalid_jid} -> xmpp:make_error(IQ, xmpp:err_jid_malformed()); {error, invalid_password} -> Txt = <<"Incorrect password">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); Err -> ?ERROR_MSG("failed to register user ~s@~s: ~p", [User, Server, Err]), xmpp:make_error(IQ, xmpp:err_internal_server_error()) end; error_preparing_password -> ErrText = <<"The password contains unacceptable characters">>, xmpp:make_error(IQ, xmpp:err_not_acceptable(ErrText, Lang)); false -> ErrText = <<"The password is too weak">>, xmpp:make_error(IQ, xmpp:err_not_acceptable(ErrText, Lang)) end. try_register(User, Server, Password, SourceRaw, Lang) -> case jid:is_nodename(User) of false -> {error, xmpp:err_bad_request(<<"Malformed username">>, Lang)}; _ -> JID = jid:make(User, Server), Access = gen_mod:get_module_opt(Server, ?MODULE, access, all), IPAccess = get_ip_access(Server), case {acl:match_rule(Server, Access, JID), check_ip_access(SourceRaw, IPAccess)} of {deny, _} -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; {_, deny} -> {error, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)}; {allow, allow} -> Source = may_remove_resource(SourceRaw), case check_timeout(Source) of true -> case is_strong_password(Server, Password) of true -> case ejabberd_auth:try_register(User, Server, Password) of ok -> ?INFO_MSG("The account ~s was registered " "from IP address ~s", [jid:encode({User, Server, <<"">>}), ip_to_string(Source)]), send_welcome_message(JID), send_registration_notifications( ?MODULE, JID, Source), ok; Error -> remove_timeout(Source), case Error of {error, exists} -> Txt = <<"User already exists">>, {error, xmpp:err_conflict(Txt, Lang)}; {error, invalid_jid} -> {error, xmpp:err_jid_malformed()}; {error, invalid_password} -> Txt = <<"Incorrect password">>, {error, xmpp:err_not_allowed(Txt, Lang)}; {error, not_allowed} -> {error, xmpp:err_not_allowed()}; {error, too_many_users} -> Txt = <<"Too many users registered">>, {error, xmpp:err_resource_constraint(Txt, Lang)}; {error, _} -> ?ERROR_MSG("failed to register user " "~s@~s: ~p", [User, Server, Error]), {error, xmpp:err_internal_server_error()} end end; error_preparing_password -> remove_timeout(Source), ErrText = <<"The password contains unacceptable characters">>, {error, xmpp:err_not_acceptable(ErrText, Lang)}; false -> remove_timeout(Source), ErrText = <<"The password is too weak">>, {error, xmpp:err_not_acceptable(ErrText, Lang)} end; false -> ErrText = <<"Users are not allowed to register accounts " "so quickly">>, {error, xmpp:err_resource_constraint(ErrText, Lang)} end end end. send_welcome_message(JID) -> Host = JID#jid.lserver, case gen_mod:get_module_opt(Host, ?MODULE, welcome_message, {<<"">>, <<"">>}) of {<<"">>, <<"">>} -> ok; {Subj, Body} -> ejabberd_router:route( #message{from = jid:make(Host), to = JID, subject = xmpp:mk_text(Subj), body = xmpp:mk_text(Body)}); _ -> ok end. send_registration_notifications(Mod, UJID, Source) -> Host = UJID#jid.lserver, case gen_mod:get_module_opt(Host, Mod, registration_watchers, []) of [] -> ok; JIDs when is_list(JIDs) -> Body = (str:format("[~s] The account ~s was registered from " "IP address ~s on node ~w using ~p.", [get_time_string(), jid:encode(UJID), ip_to_string(Source), node(), Mod])), lists:foreach( fun(JID) -> ejabberd_router:route( #message{from = jid:make(Host), to = JID, type = chat, body = xmpp:mk_text(Body)}) end, JIDs) end. check_from(#jid{user = <<"">>, server = <<"">>}, _Server) -> allow; check_from(JID, Server) -> Access = gen_mod:get_module_opt(Server, ?MODULE, access_from, none), acl:match_rule(Server, Access, JID). check_timeout(undefined) -> true; check_timeout(Source) -> Timeout = ejabberd_config:get_option(registration_timeout, 600), if is_integer(Timeout) -> Priority = -p1_time_compat:system_time(seconds), CleanPriority = Priority + Timeout, F = fun () -> Treap = case mnesia:read(mod_register_ip, treap, write) of [] -> treap:empty(); [{mod_register_ip, treap, T}] -> T end, Treap1 = clean_treap(Treap, CleanPriority), case treap:lookup(Source, Treap1) of error -> Treap2 = treap:insert(Source, Priority, [], Treap1), mnesia:write({mod_register_ip, treap, Treap2}), true; {ok, _, _} -> mnesia:write({mod_register_ip, treap, Treap1}), false end end, case mnesia:transaction(F) of {atomic, Res} -> Res; {aborted, Reason} -> ?ERROR_MSG("mod_register: timeout check error: ~p~n", [Reason]), true end; true -> true end. clean_treap(Treap, CleanPriority) -> case treap:is_empty(Treap) of true -> Treap; false -> {_Key, Priority, _Value} = treap:get_root(Treap), if Priority > CleanPriority -> clean_treap(treap:delete_root(Treap), CleanPriority); true -> Treap end end. remove_timeout(undefined) -> true; remove_timeout(Source) -> Timeout = ejabberd_config:get_option(registration_timeout, 600), if is_integer(Timeout) -> F = fun () -> Treap = case mnesia:read(mod_register_ip, treap, write) of [] -> treap:empty(); [{mod_register_ip, treap, T}] -> T end, Treap1 = treap:delete(Source, Treap), mnesia:write({mod_register_ip, treap, Treap1}), ok end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, Reason} -> ?ERROR_MSG("mod_register: timeout remove error: " "~p~n", [Reason]), ok end; true -> ok end. ip_to_string({_, _, _} = USR) -> jid:encode(USR); ip_to_string(Source) when is_tuple(Source) -> misc:ip_to_list(Source); ip_to_string(undefined) -> <<"undefined">>; ip_to_string(_) -> <<"unknown">>. get_time_string() -> write_time(erlang:localtime()). %% Function copied from ejabberd_logger_h.erl and customized write_time({{Y, Mo, D}, {H, Mi, S}}) -> io_lib:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Y, Mo, D, H, Mi, S]). process_xdata_submit(X) -> case {xmpp_util:get_xdata_values(<<"username">>, X), xmpp_util:get_xdata_values(<<"password">>, X)} of {[User], [Pass]} -> {ok, User, Pass}; _ -> error end. is_strong_password(Server, Password) -> case jid:resourceprep(Password) of PP when is_binary(PP) -> is_strong_password2(Server, Password); error -> error_preparing_password end. is_strong_password2(Server, Password) -> LServer = jid:nameprep(Server), case gen_mod:get_module_opt(LServer, ?MODULE, password_strength, 0) of 0 -> true; Entropy -> ejabberd_auth:entropy(Password) >= Entropy end. transform_options(Opts) -> Opts1 = transform_ip_access(Opts), transform_module_options(Opts1). transform_ip_access(Opts) -> try {value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts), {value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts), {value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts), true = is_list(L), ?WARNING_MSG("Old 'ip_access' format detected. " "The old format is still supported " "but it is better to fix your config: " "use access rules instead.", []), ACLs = lists:flatmap( fun({Action, S}) -> ACLName = misc:binary_to_atom( iolist_to_binary( ["ip_", S])), [{Action, ACLName}, {acl, ACLName, {ip, S}}] end, L), Access = {access, mod_register_networks, [{Action, ACLName} || {Action, ACLName} <- ACLs]}, [ACL || {acl, _, _} = ACL <- ACLs] ++ [Access, {modules, [{mod_register, [{ip_access, mod_register_networks}|RegOpts1]} | ModOpts1]}|Opts1] catch error:{badmatch, false} -> Opts end. transform_module_options(Opts) -> lists:flatmap( fun({welcome_message, {Subj, Body}}) -> ?WARNING_MSG("Old 'welcome_message' format detected. " "The old format is still supported " "but it is better to fix your config: " "change it to {welcome_message, " "[{subject, Subject}, {body, Body}]}", []), [{welcome_message, [{subject, Subj}, {body, Body}]}]; (Opt) -> [Opt] end, Opts). %%% %%% ip_access management %%% may_remove_resource({_, _, _} = From) -> jid:remove_resource(From); may_remove_resource(From) -> From. get_ip_access(Host) -> gen_mod:get_module_opt(Host, ?MODULE, ip_access, all). check_ip_access({User, Server, Resource}, IPAccess) -> case ejabberd_sm:get_user_ip(User, Server, Resource) of {IPAddress, _PortNumber} -> check_ip_access(IPAddress, IPAccess); _ -> deny end; check_ip_access(undefined, _IPAccess) -> deny; check_ip_access(IPAddress, IPAccess) -> acl:match_rule(global, IPAccess, IPAddress). mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(access_from) -> fun acl:access_rules_validator/1; mod_opt_type(access_remove) -> fun acl:access_rules_validator/1; mod_opt_type(captcha_protected) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(ip_access) -> fun acl:access_rules_validator/1; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(password_strength) -> fun (N) when is_number(N), N >= 0 -> N end; mod_opt_type(registration_watchers) -> fun (Ss) -> [jid:decode(iolist_to_binary(S)) || S <- Ss] end; mod_opt_type(welcome_message) -> fun(L) -> {proplists:get_value(subject, L, <<"">>), proplists:get_value(body, L, <<"">>)} end; mod_opt_type({welcome_message, subject}) -> fun iolist_to_binary/1; mod_opt_type({welcome_message, body}) -> fun iolist_to_binary/1; mod_opt_type(redirect_url) -> fun iolist_to_binary/1; mod_opt_type(_) -> [access, access_from, access_remove, captcha_protected, ip_access, iqdisc, password_strength, registration_watchers, redirect_url, {welcome_message, subject}, {welcome_message, body}]. -spec opt_type(registration_timeout) -> fun((timeout()) -> timeout()); (atom()) -> [atom()]. opt_type(registration_timeout) -> fun (TO) when is_integer(TO), TO > 0 -> TO; (infinity) -> infinity; (unlimited) -> infinity end; opt_type(_) -> [registration_timeout]. ejabberd-18.01/src/ejabberd_cluster.erl0000644000232200023220000001526713225664356020450 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 5 Jul 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_cluster). -behaviour(ejabberd_config). -behaviour(gen_server). %% API -export([start_link/0, call/4, multicall/3, multicall/4, eval_everywhere/3, eval_everywhere/4]). %% Backend dependent API -export([get_nodes/0, get_known_nodes/0, join/1, leave/1, subscribe/0, subscribe/1, node_id/0, get_node_by_id/1, send/2, wait_for_sync/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([opt_type/1]). -include("logger.hrl"). -type dst() :: pid() | atom() | {atom(), node()}. -callback init() -> ok | {error, any()}. -callback get_nodes() -> [node()]. -callback get_known_nodes() -> [node()]. -callback join(node()) -> ok | {error, any()}. -callback leave(node()) -> ok | {error, any()}. -callback node_id() -> binary(). -callback get_node_by_id(binary()) -> node(). -callback send({atom(), node()}, term()) -> boolean(). -callback wait_for_sync(timeout()) -> ok | {error, any()}. -callback subscribe(dst()) -> ok. -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec call(node(), module(), atom(), [any()]) -> any(). call(Node, Module, Function, Args) -> rpc:call(Node, Module, Function, Args, rpc_timeout()). -spec multicall(module(), atom(), [any()]) -> {list(), [node()]}. multicall(Module, Function, Args) -> multicall(get_nodes(), Module, Function, Args). -spec multicall([node()], module(), atom(), list()) -> {list(), [node()]}. multicall(Nodes, Module, Function, Args) -> rpc:multicall(Nodes, Module, Function, Args, rpc_timeout()). -spec eval_everywhere(module(), atom(), [any()]) -> ok. eval_everywhere(Module, Function, Args) -> eval_everywhere(get_nodes(), Module, Function, Args), ok. -spec eval_everywhere([node()], module(), atom(), [any()]) -> ok. eval_everywhere(Nodes, Module, Function, Args) -> rpc:eval_everywhere(Nodes, Module, Function, Args), ok. %%%=================================================================== %%% Backend dependent API %%%=================================================================== -spec get_nodes() -> [node()]. get_nodes() -> Mod = get_mod(), Mod:get_nodes(). -spec get_known_nodes() -> [node()]. get_known_nodes() -> Mod = get_mod(), Mod:get_known_nodes(). -spec join(node()) -> ok | {error, any()}. join(Node) -> Mod = get_mod(), Mod:join(Node). -spec leave(node()) -> ok | {error, any()}. leave(Node) -> Mod = get_mod(), Mod:leave(Node). -spec node_id() -> binary(). node_id() -> Mod = get_mod(), Mod:node_id(). -spec get_node_by_id(binary()) -> node(). get_node_by_id(ID) -> Mod = get_mod(), Mod:get_node_by_id(ID). %% Note that false positive returns are possible, while false negatives are not. %% In other words: positive return value (i.e. 'true') doesn't guarantee %% successful delivery, while negative return value ('false') means %% the delivery has definitely failed. -spec send(dst(), term()) -> boolean(). send({Name, Node}, Msg) when Node == node() -> send(Name, Msg); send(undefined, _Msg) -> false; send(Name, Msg) when is_atom(Name) -> send(whereis(Name), Msg); send(Pid, Msg) when is_pid(Pid) andalso node(Pid) == node() -> case erlang:is_process_alive(Pid) of true -> erlang:send(Pid, Msg), true; false -> false end; send(Dst, Msg) -> Mod = get_mod(), Mod:send(Dst, Msg). -spec wait_for_sync(timeout()) -> ok | {error, any()}. wait_for_sync(Timeout) -> Mod = get_mod(), Mod:wait_for_sync(Timeout). -spec subscribe() -> ok. subscribe() -> subscribe(self()). -spec subscribe(dst()) -> ok. subscribe(Proc) -> Mod = get_mod(), Mod:subscribe(Proc). %%%=================================================================== %%% gen_server API %%%=================================================================== init([]) -> Ticktime = ejabberd_config:get_option(net_ticktime, 60), Nodes = ejabberd_config:get_option(cluster_nodes, []), net_kernel:set_net_ticktime(Ticktime), lists:foreach(fun(Node) -> net_kernel:connect_node(Node) end, Nodes), Mod = get_mod(), case Mod:init() of ok -> Mod:subscribe(?MODULE), {ok, #state{}}; {error, Reason} -> {stop, Reason} end. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({node_up, Node}, State) -> ?INFO_MSG("Node ~s has joined", [Node]), {noreply, State}; handle_info({node_down, Node}, State) -> ?INFO_MSG("Node ~s has left", [Node]), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== get_mod() -> Backend = ejabberd_config:get_option(cluster_backend, mnesia), list_to_atom("ejabberd_cluster_" ++ atom_to_list(Backend)). rpc_timeout() -> timer:seconds(ejabberd_config:get_option(rpc_timeout, 5)). opt_type(net_ticktime) -> fun (P) when is_integer(P), P > 0 -> P end; opt_type(cluster_nodes) -> fun (Ns) -> true = lists:all(fun is_atom/1, Ns), Ns end; opt_type(rpc_timeout) -> fun (T) when is_integer(T), T > 0 -> T end; opt_type(cluster_backend) -> fun (T) -> ejabberd_config:v_db(?MODULE, T) end; opt_type(_) -> [rpc_timeout, cluster_backend, cluster_nodes, net_ticktime]. ejabberd-18.01/src/mod_caps_sql.erl0000644000232200023220000000621213225664356017603 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_caps_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_caps_sql). -behaviour(mod_caps). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/2, caps_read/2, caps_write/3, export/1, import/3]). -include("mod_caps.hrl"). -include("ejabberd_sql_pt.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. caps_read(LServer, {Node, SubNode}) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(feature)s from caps_features where" " node=%(Node)s and subnode=%(SubNode)s")) of {selected, [{H}|_] = Fs} -> case catch binary_to_integer(H) of Int when is_integer(Int), Int>=0 -> {ok, Int}; _ -> {ok, [F || {F} <- Fs]} end; _ -> error end. caps_write(LServer, NodePair, Features) -> case ejabberd_sql:sql_transaction( LServer, sql_write_features_t(NodePair, Features)) of {atomic, _} -> ok; {aborted, _Reason} -> {error, db_failure} end. export(_Server) -> [{caps_features, fun(_Host, #caps_features{node_pair = NodePair, features = Features}) -> sql_write_features_t(NodePair, Features); (_Host, _R) -> [] end}]. import(_, _, _) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== sql_write_features_t({Node, SubNode}, Features) -> NewFeatures = if is_integer(Features) -> [integer_to_binary(Features)]; true -> Features end, [?SQL("delete from caps_features where node=%(Node)s" " and subnode=%(SubNode)s;") | [?SQL("insert into caps_features(node, subnode, feature)" " values (%(Node)s, %(SubNode)s, %(F)s);") || F <- NewFeatures]]. ejabberd-18.01/src/mod_push_mnesia.erl0000644000232200023220000001536713225664356020324 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_push_mnesia.erl %%% Author : Holger Weiss %%% Purpose : Mnesia backend for Push Notifications (XEP-0357) %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% %%% ejabberd, Copyright (C) 2017-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_push_mnesia). -author('holger@zedat.fu-berlin.de'). -behavior(mod_push). %% API -export([init/2, store_session/6, lookup_session/4, lookup_session/3, lookup_sessions/3, lookup_sessions/2, lookup_sessions/1, delete_session/3, delete_old_sessions/2, transform/1]). -include_lib("stdlib/include/ms_transform.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_push.hrl"). %%%------------------------------------------------------------------- %%% API %%%------------------------------------------------------------------- init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, push_session, [{disc_only_copies, [node()]}, {type, bag}, {attributes, record_info(fields, push_session)}]). store_session(LUser, LServer, TS, PushJID, Node, XData) -> US = {LUser, LServer}, PushLJID = jid:tolower(PushJID), MaxSessions = ejabberd_sm:get_max_user_sessions(LUser, LServer), F = fun() -> if is_integer(MaxSessions) -> enforce_max_sessions(US, MaxSessions - 1); MaxSessions == infinity -> ok end, mnesia:write(#push_session{us = US, timestamp = TS, service = PushLJID, node = Node, xml = encode_xdata(XData)}) end, case mnesia:transaction(F) of {atomic, ok} -> {ok, {TS, PushLJID, Node, XData}}; {aborted, E} -> ?ERROR_MSG("Cannot store push session for ~s@~s: ~p", [LUser, LServer, E]), {error, db_failure} end. lookup_session(LUser, LServer, PushJID, Node) -> PushLJID = jid:tolower(PushJID), MatchSpec = ets:fun2ms( fun(#push_session{us = {U, S}, service = P, node = N} = Rec) when U == LUser, S == LServer, P == PushLJID, N == Node -> Rec end), case mnesia:dirty_select(push_session, MatchSpec) of [#push_session{timestamp = TS, xml = El}] -> {ok, {TS, PushLJID, Node, decode_xdata(El)}}; [] -> ?DEBUG("No push session found for ~s@~s (~p, ~s)", [LUser, LServer, PushJID, Node]), {error, notfound} end. lookup_session(LUser, LServer, TS) -> MatchSpec = ets:fun2ms( fun(#push_session{us = {U, S}, timestamp = T} = Rec) when U == LUser, S == LServer, T == TS -> Rec end), case mnesia:dirty_select(push_session, MatchSpec) of [#push_session{service = PushLJID, node = Node, xml = El}] -> {ok, {TS, PushLJID, Node, decode_xdata(El)}}; [] -> ?DEBUG("No push session found for ~s@~s (~p)", [LUser, LServer, TS]), {error, notfound} end. lookup_sessions(LUser, LServer, PushJID) -> PushLJID = jid:tolower(PushJID), MatchSpec = ets:fun2ms( fun(#push_session{us = {U, S}, service = P, node = Node, timestamp = TS, xml = El} = Rec) when U == LUser, S == LServer, P == PushLJID -> Rec end), Records = mnesia:dirty_select(push_session, MatchSpec), {ok, records_to_sessions(Records)}. lookup_sessions(LUser, LServer) -> Records = mnesia:dirty_read(push_session, {LUser, LServer}), {ok, records_to_sessions(Records)}. lookup_sessions(LServer) -> MatchSpec = ets:fun2ms( fun(#push_session{us = {_U, S}, timestamp = TS, service = PushLJID, node = Node, xml = El}) when S == LServer -> {TS, PushLJID, Node, El} end), Records = mnesia:dirty_select(push_session, MatchSpec), {ok, records_to_sessions(Records)}. delete_session(LUser, LServer, TS) -> MatchSpec = ets:fun2ms( fun(#push_session{us = {U, S}, timestamp = T} = Rec) when U == LUser, S == LServer, T == TS -> Rec end), F = fun() -> Recs = mnesia:select(push_session, MatchSpec), lists:foreach(fun mnesia:delete_object/1, Recs) end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, E} -> ?ERROR_MSG("Cannot delete push session of ~s@~s: ~p", [LUser, LServer, E]), {error, db_failure} end. delete_old_sessions(_LServer, Time) -> DelIfOld = fun(#push_session{timestamp = T} = Rec, ok) when T < Time -> mnesia:delete_object(Rec); (_Rec, ok) -> ok end, F = fun() -> mnesia:foldl(DelIfOld, ok, push_session) end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, E} -> ?ERROR_MSG("Cannot delete old push sessions: ~p", [E]), {error, db_failure} end. transform({push_session, US, TS, Service, Node, XData}) -> ?INFO_MSG("Transforming push_session Mnesia table", []), #push_session{us = US, timestamp = TS, service = Service, node = Node, xml = encode_xdata(XData)}. %%-------------------------------------------------------------------- %% Internal functions. %%-------------------------------------------------------------------- -spec enforce_max_sessions({binary(), binary()}, non_neg_integer()) -> ok. enforce_max_sessions({U, S} = US, Max) -> Recs = mnesia:wread({push_session, US}), NumRecs = length(Recs), if NumRecs > Max -> NumOldRecs = NumRecs - Max, Recs1 = lists:keysort(#push_session.timestamp, Recs), Recs2 = lists:reverse(Recs1), OldRecs = lists:sublist(Recs2, Max + 1, NumOldRecs), ?INFO_MSG("Disabling ~B old push session(s) of ~s@~s", [NumOldRecs, U, S]), lists:foreach(fun(Rec) -> mnesia:delete_object(Rec) end, OldRecs); true -> ok end. decode_xdata(undefined) -> undefined; decode_xdata(El) -> xmpp:decode(El). encode_xdata(undefined) -> undefined; encode_xdata(XData) -> xmpp:encode(XData). records_to_sessions(Records) -> [{TS, PushLJID, Node, decode_xdata(El)} || #push_session{timestamp = TS, service = PushLJID, node = Node, xml = El} <- Records]. ejabberd-18.01/src/mod_avatar.erl0000644000232200023220000003416713225664356017266 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 13 Sep 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_avatar). -behaviour(gen_mod). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% Hooks -export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1]). -include("xmpp.hrl"). -include("logger.hrl"). -include("pubsub.hrl"). -type convert_rules() :: {default | eimp:img_type(), eimp:img_type()}. %%%=================================================================== %%% API %%%=================================================================== start(Host, _Opts) -> case misc:have_eimp() of true -> ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30), ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100); false -> ?CRITICAL_MSG("ejabberd is built without " "graphics support: reconfigure it with " "--enable-graphics or disable '~s'", [?MODULE]), {error, graphics_not_compiled} end. stop(Host) -> ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30), ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> [{mod_vcard, hard}, {mod_vcard_xupdate, hard}, {mod_pubsub, hard}]. %%%=================================================================== %%% Hooks %%%=================================================================== pubsub_publish_item(LServer, ?NS_AVATAR_METADATA, #jid{luser = LUser, lserver = LServer} = From, #jid{luser = LUser, lserver = LServer} = Host, ItemId, [Payload|_]) -> try xmpp:decode(Payload) of #avatar_meta{info = []} -> delete_vcard_avatar(From); #avatar_meta{info = Info} -> Rules = get_converting_rules(LServer), case get_meta_info(Info, Rules) of #avatar_info{type = MimeType, id = ID, url = <<"">>} = I -> case get_avatar_data(Host, ID) of {ok, Data} -> Meta = #avatar_meta{info = [I]}, Photo = #vcard_photo{type = MimeType, binval = Data}, set_vcard_avatar(From, Photo, #{avatar_meta => {ID, Meta}}); {error, _} -> ok end; #avatar_info{type = MimeType, url = URL} -> Photo = #vcard_photo{type = MimeType, extval = URL}, set_vcard_avatar(From, Photo, #{}) end; _ -> ?WARNING_MSG("invalid avatar metadata of ~s@~s published " "with item id ~s", [LUser, LServer, ItemId]) catch _:{xmpp_codec, Why} -> ?WARNING_MSG("failed to decode avatar metadata of ~s@~s: ~s", [LUser, LServer, xmpp:format_error(Why)]) end; pubsub_publish_item(_, _, _, _, _, _) -> ok. -spec vcard_iq_convert(iq()) -> iq() | {stop, stanza_error()}. vcard_iq_convert(#iq{from = From, lang = Lang, sub_els = [VCard]} = IQ) -> #jid{luser = LUser, lserver = LServer} = From, case convert_avatar(LUser, LServer, VCard) of {ok, MimeType, Data} -> VCard1 = VCard#vcard_temp{ photo = #vcard_photo{type = MimeType, binval = Data}}, IQ#iq{sub_els = [VCard1]}; pass -> IQ; {error, Reason} -> stop_with_error(Lang, Reason) end; vcard_iq_convert(Acc) -> Acc. -spec vcard_iq_publish(iq()) -> iq() | {stop, stanza_error()}. vcard_iq_publish(#iq{sub_els = [#vcard_temp{photo = undefined}]} = IQ) -> publish_avatar(IQ, #avatar_meta{}, <<>>, <<>>, <<>>); vcard_iq_publish(#iq{sub_els = [#vcard_temp{ photo = #vcard_photo{ type = MimeType, binval = Data}}]} = IQ) when is_binary(Data), Data /= <<>> -> SHA1 = str:sha(Data), M = get_avatar_meta(IQ), case M of {ok, SHA1, _} -> IQ; {ok, _ItemID, #avatar_meta{info = Info} = Meta} -> case lists:keyfind(SHA1, #avatar_info.id, Info) of #avatar_info{} -> IQ; false -> Info1 = lists:filter( fun(#avatar_info{url = URL}) -> URL /= <<"">> end, Info), Meta1 = Meta#avatar_meta{info = Info1}, publish_avatar(IQ, Meta1, MimeType, Data, SHA1) end; {error, _} -> publish_avatar(IQ, #avatar_meta{}, MimeType, Data, SHA1) end; vcard_iq_publish(Acc) -> Acc. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec get_meta_info([avatar_info()], convert_rules()) -> avatar_info(). get_meta_info(Info, Rules) -> case lists:foldl( fun(_, #avatar_info{} = Acc) -> Acc; (#avatar_info{url = URL}, Acc) when URL /= <<"">> -> Acc; (#avatar_info{} = I, _) when Rules == [] -> I; (#avatar_info{type = MimeType} = I, Acc) -> T = decode_mime_type(MimeType), case lists:keymember(T, 2, Rules) of true -> I; false -> case convert_to_type(T, Rules) of undefined -> Acc; _ -> [I|Acc] end end end, [], Info) of #avatar_info{} = I -> I; [] -> hd(Info); Is -> hd(lists:reverse(Is)) end. -spec get_avatar_data(jid(), binary()) -> {ok, binary()} | {error, notfound | invalid_data | internal_error}. get_avatar_data(JID, ItemID) -> {LUser, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), case mod_pubsub:get_item(LBJID, ?NS_AVATAR_DATA, ItemID) of #pubsub_item{payload = [Payload|_]} -> try xmpp:decode(Payload) of #avatar_data{data = Data} -> {ok, Data}; _ -> ?WARNING_MSG("invalid avatar data detected " "for ~s@~s with item id ~s", [LUser, LServer, ItemID]), {error, invalid_data} catch _:{xmpp_codec, Why} -> ?WARNING_MSG("failed to decode avatar data for " "~s@~s with item id ~s: ~s", [LUser, LServer, ItemID, xmpp:format_error(Why)]), {error, invalid_data} end; {error, #stanza_error{reason = 'item-not-found'}} -> {error, notfound}; {error, Reason} -> ?WARNING_MSG("failed to get item for ~s@~s at node ~s " "with item id ~s: ~p", [LUser, LServer, ?NS_AVATAR_METADATA, ItemID, Reason]), {error, internal_error} end. -spec get_avatar_meta(iq()) -> {ok, binary(), avatar_meta()} | {error, notfound | invalid_metadata | internal_error}. get_avatar_meta(#iq{meta = #{avatar_meta := {ItemID, Meta}}}) -> {ok, ItemID, Meta}; get_avatar_meta(#iq{from = JID}) -> {LUser, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), case mod_pubsub:get_items(LBJID, ?NS_AVATAR_METADATA) of [#pubsub_item{itemid = {ItemID, _}, payload = [Payload|_]}|_] -> try xmpp:decode(Payload) of #avatar_meta{} = Meta -> {ok, ItemID, Meta}; _ -> ?WARNING_MSG("invalid metadata payload detected " "for ~s@~s with item id ~s", [LUser, LServer, ItemID]), {error, invalid_metadata} catch _:{xmpp_codec, Why} -> ?WARNING_MSG("failed to decode metadata for " "~s@~s with item id ~s: ~s", [LUser, LServer, ItemID, xmpp:format_error(Why)]), {error, invalid_metadata} end; {error, #stanza_error{reason = 'item-not-found'}} -> {error, notfound}; {error, Reason} -> ?WARNING_MSG("failed to get items for ~s@~s at node ~s: ~p", [LUser, LServer, ?NS_AVATAR_METADATA, Reason]), {error, internal_error} end. -spec publish_avatar(iq(), avatar_meta(), binary(), binary(), binary()) -> iq() | {stop, stanza_error()}. publish_avatar(#iq{from = JID} = IQ, Meta, <<>>, <<>>, <<>>) -> {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), case mod_pubsub:publish_item( LBJID, LServer, ?NS_AVATAR_METADATA, JID, <<>>, [xmpp:encode(Meta)]) of {result, _} -> IQ; {error, StanzaErr} -> {stop, StanzaErr} end; publish_avatar(#iq{from = JID} = IQ, Meta, MimeType, Data, ItemID) -> #avatar_meta{info = Info} = Meta, {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), Payload = xmpp:encode(#avatar_data{data = Data}), case mod_pubsub:publish_item( LBJID, LServer, ?NS_AVATAR_DATA, JID, ItemID, [Payload]) of {result, _} -> {W, H} = case eimp:identify(Data) of {ok, ImgInfo} -> {proplists:get_value(width, ImgInfo), proplists:get_value(height, ImgInfo)}; _ -> {undefined, undefined} end, I = #avatar_info{id = ItemID, width = W, height = H, type = MimeType, bytes = size(Data)}, Meta1 = Meta#avatar_meta{info = [I|Info]}, case mod_pubsub:publish_item( LBJID, LServer, ?NS_AVATAR_METADATA, JID, ItemID, [xmpp:encode(Meta1)]) of {result, _} -> IQ; {error, StanzaErr} -> ?ERROR_MSG("Failed to publish avatar metadata for ~s: ~p", [jid:encode(JID), StanzaErr]), {stop, StanzaErr} end; {error, StanzaErr} -> ?ERROR_MSG("Failed to publish avatar data for ~s: ~p", [jid:encode(JID), StanzaErr]), {stop, StanzaErr} end. -spec convert_avatar(binary(), binary(), vcard_temp()) -> {ok, binary(), binary()} | {error, eimp:error_reason() | base64_error} | pass. convert_avatar(LUser, LServer, VCard) -> case get_converting_rules(LServer) of [] -> pass; Rules -> case VCard#vcard_temp.photo of #vcard_photo{binval = Data} when is_binary(Data) -> convert_avatar(LUser, LServer, Data, Rules); _ -> pass end end. -spec convert_avatar(binary(), binary(), binary(), convert_rules()) -> {ok, eimp:img_type(), binary()} | {error, eimp:error_reason()} | pass. convert_avatar(LUser, LServer, Data, Rules) -> Type = get_type(Data), NewType = convert_to_type(Type, Rules), if NewType == undefined orelse Type == NewType -> pass; true -> ?DEBUG("Converting avatar of ~s@~s: ~s -> ~s", [LUser, LServer, Type, NewType]), case eimp:convert(Data, NewType) of {ok, NewData} -> {ok, encode_mime_type(NewType), NewData}; {error, Reason} = Err -> ?ERROR_MSG("Failed to convert avatar of " "~s@~s (~s -> ~s): ~s", [LUser, LServer, Type, NewType, eimp:format_error(Reason)]), Err end end. -spec set_vcard_avatar(jid(), vcard_photo() | undefined, map()) -> ok. set_vcard_avatar(JID, VCardPhoto, Meta) -> case get_vcard(JID) of {ok, #vcard_temp{photo = VCardPhoto}} -> ok; {ok, VCard} -> VCard1 = VCard#vcard_temp{photo = VCardPhoto}, IQ = #iq{from = JID, to = JID, id = randoms:get_string(), type = set, sub_els = [VCard1], meta = Meta}, LServer = JID#jid.lserver, ejabberd_hooks:run_fold(vcard_iq_set, LServer, IQ, []), ok; {error, _} -> ok end. -spec delete_vcard_avatar(jid()) -> ok. delete_vcard_avatar(JID) -> set_vcard_avatar(JID, undefined, #{}). -spec get_vcard(jid()) -> {ok, vcard_temp()} | {error, invalid_vcard}. get_vcard(#jid{luser = LUser, lserver = LServer}) -> VCardEl = case mod_vcard:get_vcard(LUser, LServer) of [El] -> El; _ -> #vcard_temp{} end, try xmpp:decode(VCardEl, ?NS_VCARD, []) of #vcard_temp{} = VCard -> {ok, VCard}; _ -> ?ERROR_MSG("invalid vCard of ~s@~s in the database", [LUser, LServer]), {error, invalid_vcard} catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode vCard of ~s@~s: ~s", [LUser, LServer, xmpp:format_error(Why)]), {error, invalid_vcard} end. -spec stop_with_error(binary(), eimp:error_reason()) -> {stop, stanza_error()}. stop_with_error(Lang, Reason) -> Txt = eimp:format_error(Reason), {stop, xmpp:err_internal_server_error(Txt, Lang)}. -spec get_converting_rules(binary()) -> convert_rules(). get_converting_rules(LServer) -> gen_mod:get_module_opt(LServer, ?MODULE, convert, []). -spec get_type(binary()) -> eimp:img_type() | unknown. get_type(Data) -> eimp:get_type(Data). -spec convert_to_type(eimp:img_type() | unknown, convert_rules()) -> eimp:img_type() | undefined. convert_to_type(unknown, _Rules) -> undefined; convert_to_type(Type, Rules) -> case proplists:get_value(Type, Rules) of undefined -> proplists:get_value(default, Rules); T -> T end. -spec decode_mime_type(binary()) -> eimp:img_type() | unknown. decode_mime_type(MimeType) -> case str:to_lower(MimeType) of <<"image/jpeg">> -> jpeg; <<"image/png">> -> png; <<"image/webp">> -> webp; <<"image/gif">> -> gif; _ -> unknown end. -spec encode_mime_type(eimp:img_type()) -> binary(). encode_mime_type(Type) -> <<"image/", (atom_to_binary(Type, latin1))/binary>>. mod_opt_type({convert, png}) -> fun(jpeg) -> jpeg; (webp) -> webp; (gif) -> gif end; mod_opt_type({convert, webp}) -> fun(jpeg) -> jpeg; (png) -> png; (gif) -> gif end; mod_opt_type({convert, jpeg}) -> fun(png) -> png; (webp) -> webp; (gif) -> gif end; mod_opt_type({convert, gif}) -> fun(png) -> png; (jpeg) -> jpeg; (webp) -> webp end; mod_opt_type({convert, default}) -> fun(png) -> png; (webp) -> webp; (jpeg) -> jpeg; (gif) -> gif end; mod_opt_type(_) -> [{convert, default}, {convert, webp}, {convert, png}, {convert, gif}, {convert, jpeg}]. ejabberd-18.01/src/ejabberd_commands.erl0000644000232200023220000005566613225664356020577 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_commands.erl %%% Author : Badlop %%% Purpose : Management of ejabberd commands %%% Created : 20 May 2008 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @headerfile "ejabberd_commands.hrl" %%% @doc Management of ejabberd commands. %%% %%% An ejabberd command is an abstract function identified by a name, %%% with a defined number and type of calling arguments and type of %%% result, that can be defined in any Erlang module and executed %%% using any valid frontend. %%% %%% %%% == Define a new ejabberd command == %%% %%% ejabberd commands can be defined and registered in %%% any Erlang module. %%% %%% Some commands are procedures; and their purpose is to perform an %%% action in the server, so the command result is only some result %%% code or result tuple. Other commands are inspectors, and their %%% purpose is to gather some information about the server and return %%% a detailed response: it can be integer, string, atom, tuple, list %%% or a mix of those ones. %%% %%% The arguments and result of an ejabberd command are strictly %%% defined. The number and format of the arguments provided when %%% calling an ejabberd command must match the definition of that %%% command. The format of the result provided by an ejabberd command %%% must be exactly its definition. For example, if a command is said %%% to return an integer, it must always return an integer (except in %%% case of a crash). %%% %%% If you are developing an Erlang module that will run inside %%% ejabberd and you want to provide a new ejabberd command to %%% administer some task related to your module, you only need to: %%% implement a function, define the command, and register it. %%% %%% %%% === Define a new ejabberd command === %%% %%% An ejabberd command is defined using the Erlang record %%% 'ejabberd_commands'. This record has several elements that you %%% must define. Note that 'tags', 'desc' and 'longdesc' are optional. %%% %%% For example let's define an ejabberd command 'pow' that gets the %%% integers 'base' and 'exponent'. Its result will be an integer %%% 'power': %%% %%%
#ejabberd_commands{name = pow, tags = [test],
%%%                 desc = "Return the power of base for exponent",
%%%                 longdesc = "This is an example command. The formula is:\n"
%%%                 "  power = base ^ exponent",
%%%                 module = ?MODULE, function = pow,
%%%                 args = [{base, integer}, {exponent, integer}],
%%%                 result = {power, integer}}
%%% %%% %%% === Implement the function associated to the command === %%% %%% Now implement a function in your module that matches the arguments %%% and result of the ejabberd command. %%% %%% For example the function calc_power gets two integers Base and %%% Exponent. It calculates the power and rounds to an integer: %%% %%%
calc_power(Base, Exponent) ->
%%%    PowFloat = math:pow(Base, Exponent),
%%%    round(PowFloat).
%%% %%% Since this function will be called by ejabberd_commands, it must %%% be exported. %%% Add to your module: %%%
-export([calc_power/2]).
%%% %%% Only some types of result formats are allowed. %%% If the format is defined as 'rescode', then your function must return: %%% ok | true | atom() %%% where the atoms ok and true as considered positive answers, %%% and any other response atom is considered negative. %%% %%% If the format is defined as 'restuple', then the command must return: %%% {rescode(), string()} %%% %%% If the format is defined as '{list, something()}', then the command %%% must return a list of something(). %%% %%% %%% === Register the command === %%% %%% Define this function and put inside the #ejabberd_command you %%% defined in the beginning: %%% %%%
commands() ->
%%%    [
%%%
%%%    ].
%%% %%% You need to include this header file in order to use the record: %%% %%%
-include("ejabberd_commands.hrl").
%%% %%% When your module is initialized or started, register your commands: %%% %%%
ejabberd_commands:register_commands(commands()),
%%% %%% And when your module is stopped, unregister your commands: %%% %%%
ejabberd_commands:unregister_commands(commands()),
%%% %%% That's all! Now when your module is started, the command will be %%% registered and any frontend can access it. For example: %%% %%%
$ ejabberdctl help pow
%%%
%%%   Command Name: pow
%%%
%%%   Arguments: base::integer
%%%              exponent::integer
%%%
%%%   Returns: power::integer
%%%
%%%   Tags: test
%%%
%%%   Description: Return the power of base for exponent
%%%
%%% This is an example command. The formula is:
%%%  power = base ^ exponent
%%%
%%% $ ejabberdctl pow 3 4
%%% 81
%%% 
%%% %%% %%% == Execute an ejabberd command == %%% %%% ejabberd commands are mean to be executed using any valid %%% frontend. An ejabberd commands is implemented in a regular Erlang %%% function, so it is also possible to execute this function in any %%% Erlang module, without dealing with the associated ejabberd %%% commands. %%% %%% %%% == Frontend to ejabberd commands == %%% %%% Currently there are two frontends to ejabberd commands: the shell %%% script {@link ejabberd_ctl. ejabberdctl}, and the XML-RPC server %%% ejabberd_xmlrpc. %%% %%% %%% === ejabberdctl as a frontend to ejabberd commands === %%% %%% It is possible to use ejabberdctl to get documentation of any %%% command. But ejabberdctl does not support all the argument types %%% allowed in ejabberd commands, so there are some ejabberd commands %%% that cannot be executed using ejabberdctl. %%% %%% Also note that the ejabberdctl shell administration script also %%% manages ejabberdctl commands, which are unrelated to ejabberd %%% commands and can only be executed using ejabberdctl. %%% %%% %%% === ejabberd_xmlrpc as a frontend to ejabberd commands === %%% %%% ejabberd_xmlrpc provides an XML-RPC server to execute ejabberd commands. %%% ejabberd_xmlrpc is a contributed module published in ejabberd-modules SVN. %%% %%% Since ejabberd_xmlrpc does not provide any method to get documentation %%% of the ejabberd commands, please use ejabberdctl to know which %%% commands are available, and their usage. %%% %%% The number and format of the arguments provided when calling an %%% ejabberd command must match the definition of that command. Please %%% make sure the XML-RPC call provides the required arguments, with %%% the specified format. The order of the arguments in an XML-RPC %%% call is not important, because all the data is tagged and will be %%% correctly prepared by ejabberd_xmlrpc before executing the ejabberd %%% command. %%% TODO: consider this feature: %%% All commands are catched. If an error happens, return the restuple: %%% {error, flattened error string} %%% This means that ecomm call APIs (ejabberd_ctl, ejabberd_xmlrpc) %%% need to allows this. And ejabberd_xmlrpc must be prepared to %%% handle such an unexpected response. -module(ejabberd_commands). -author('badlop@process-one.net'). -behaviour(gen_server). -behaviour(ejabberd_config). -define(DEFAULT_VERSION, 1000000). -export([start_link/0, list_commands/0, list_commands/1, get_command_format/1, get_command_format/2, get_command_format/3, get_command_definition/1, get_command_definition/2, get_tags_commands/0, get_tags_commands/1, get_exposed_commands/0, register_commands/1, unregister_commands/1, expose_commands/1, opt_type/1, get_commands_spec/0, get_commands_definition/0, get_commands_definition/1, execute_command2/3, execute_command2/4]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd_commands.hrl"). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -define(POLICY_ACCESS, '$policy'). -record(state, {}). get_commands_spec() -> [ #ejabberd_commands{name = gen_html_doc_for_commands, tags = [documentation], desc = "Generates html documentation for ejabberd_commands", module = ejabberd_commands_doc, function = generate_html_output, args = [{file, binary}, {regexp, binary}, {examples, binary}], result = {res, rescode}, args_desc = ["Path to file where generated " "documentation should be stored", "Regexp matching names of commands or modules " "that will be included inside generated document", "Comma separated list of languages (choosen from java, perl, xmlrpc, json)" "that will have example invocation include in markdown document"], result_desc = "0 if command failed, 1 when succedded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], result_example = ok}, #ejabberd_commands{name = gen_markdown_doc_for_commands, tags = [documentation], desc = "Generates markdown documentation for ejabberd_commands", module = ejabberd_commands_doc, function = generate_md_output, args = [{file, binary}, {regexp, binary}, {examples, binary}], result = {res, rescode}, args_desc = ["Path to file where generated " "documentation should be stored", "Regexp matching names of commands or modules " "that will be included inside generated document", "Comma separated list of languages (choosen from java, perl, xmlrpc, json)" "that will have example invocation include in markdown document"], result_desc = "0 if command failed, 1 when succedded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], result_example = ok}]. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> try mnesia:transform_table(ejabberd_commands, ignore, record_info(fields, ejabberd_commands)) catch exit:{aborted, {no_exists, _}} -> ok end, ejabberd_mnesia:create(?MODULE, ejabberd_commands, [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, ejabberd_commands)}, {type, bag}]), register_commands(get_commands_spec()), ejabberd_access_permissions:register_permission_addon(?MODULE, fun permission_addon/0), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. -spec register_commands([ejabberd_commands()]) -> ok. %% @doc Register ejabberd commands. %% If a command is already registered, a warning is printed and the %% old command is preserved. %% A registered command is not directly available to be called through %% ejabberd ReST API. It need to be exposed to be available through API. register_commands(Commands) -> lists:foreach( fun(Command) -> %% XXX check if command exists mnesia:dirty_write(Command) %% ?DEBUG("This command is already defined:~n~p", [Command]) end, Commands), ejabberd_access_permissions:invalidate(), ok. -spec unregister_commands([ejabberd_commands()]) -> ok. %% @doc Unregister ejabberd commands. unregister_commands(Commands) -> lists:foreach( fun(Command) -> mnesia:dirty_delete_object(Command) end, Commands), ejabberd_access_permissions:invalidate(), ok. %% @doc Expose command through ejabberd ReST API. %% Pass a list of command names or policy to expose. -spec expose_commands([ejabberd_commands()|atom()|open|user|admin|restricted]) -> ok | {error, atom()}. expose_commands(Commands) -> Names = lists:map(fun(#ejabberd_commands{name = Name}) -> Name; (Name) when is_atom(Name) -> Name end, Commands), case ejabberd_config:add_option(commands, [{add_commands, Names}]) of ok -> ok; {aborted, Reason} -> {error, Reason}; {atomic, Result} -> Result end. -spec list_commands() -> [{atom(), [aterm()], string()}]. %% @doc Get a list of all the available commands, arguments and description. list_commands() -> list_commands(?DEFAULT_VERSION). -spec list_commands(integer()) -> [{atom(), [aterm()], string()}]. %% @doc Get a list of all the available commands, arguments and %% description in a given API verion. list_commands(Version) -> Commands = get_commands_definition(Version), [{Name, Args, Desc} || #ejabberd_commands{name = Name, args = Args, desc = Desc} <- Commands]. -spec list_commands_policy(integer()) -> [{atom(), [aterm()], string(), atom()}]. %% @doc Get a list of all the available commands, arguments, %% description, and policy in a given API version. list_commands_policy(Version) -> Commands = get_commands_definition(Version), [{Name, Args, Desc, Policy} || #ejabberd_commands{name = Name, args = Args, desc = Desc, policy = Policy} <- Commands]. -spec get_command_format(atom()) -> {[aterm()], rterm()}. %% @doc Get the format of arguments and result of a command. get_command_format(Name) -> get_command_format(Name, noauth, ?DEFAULT_VERSION). get_command_format(Name, Version) when is_integer(Version) -> get_command_format(Name, noauth, Version); get_command_format(Name, Auth) -> get_command_format(Name, Auth, ?DEFAULT_VERSION). -spec get_command_format(atom(), {binary(), binary(), binary(), boolean()} | noauth | admin, integer()) -> {[aterm()], rterm()}. get_command_format(Name, Auth, Version) -> Admin = is_admin(Name, Auth, #{}), #ejabberd_commands{args = Args, result = Result, policy = Policy} = get_command_definition(Name, Version), case Policy of user when Admin; Auth == noauth -> {[{user, binary}, {server, binary} | Args], Result}; _ -> {Args, Result} end. %% The oauth scopes for a command are the command name itself, %% also might include either 'ejabberd:user' or 'ejabberd:admin' cmd_scope(#ejabberd_commands{policy = Policy, name = Name}) -> [erlang:atom_to_binary(Name,utf8)] ++ [<<"ejabberd:user">> || Policy == user] ++ [<<"ejabberd:admin">> || Policy == admin]. -spec get_command_definition(atom()) -> ejabberd_commands(). %% @doc Get the definition record of a command. get_command_definition(Name) -> get_command_definition(Name, ?DEFAULT_VERSION). -spec get_command_definition(atom(), integer()) -> ejabberd_commands(). %% @doc Get the definition record of a command in a given API version. get_command_definition(Name, Version) -> case lists:reverse( lists:sort( mnesia:dirty_select( ejabberd_commands, ets:fun2ms( fun(#ejabberd_commands{name = N, version = V} = C) when N == Name, V =< Version -> {V, C} end)))) of [{_, Command} | _ ] -> Command; _E -> throw({error, unknown_command}) end. get_commands_definition() -> get_commands_definition(?DEFAULT_VERSION). -spec get_commands_definition(integer()) -> [ejabberd_commands()]. % @doc Returns all commands for a given API version get_commands_definition(Version) -> L = lists:reverse( lists:sort( mnesia:dirty_select( ejabberd_commands, ets:fun2ms( fun(#ejabberd_commands{name = Name, version = V} = C) when V =< Version -> {Name, V, C} end)))), F = fun({_Name, _V, Command}, []) -> [Command]; ({Name, _V, _Command}, [#ejabberd_commands{name=Name}|_T] = Acc) -> Acc; ({_Name, _V, Command}, Acc) -> [Command | Acc] end, lists:foldl(F, [], L). execute_command2(Name, Arguments, CallerInfo) -> execute_command2(Name, Arguments, CallerInfo, ?DEFAULT_VERSION). execute_command2(Name, Arguments, CallerInfo, Version) -> Command = get_command_definition(Name, Version), case ejabberd_access_permissions:can_access(Name, CallerInfo) of allow -> do_execute_command(Command, Arguments); _ -> throw({error, access_rules_unauthorized}) end. do_execute_command(Command, Arguments) -> Module = Command#ejabberd_commands.module, Function = Command#ejabberd_commands.function, ?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]), apply(Module, Function, Arguments). -spec get_tags_commands() -> [{string(), [string()]}]. %% @spec () -> [{Tag::string(), [CommandName::string()]}] %% @doc Get all the tags and associated commands. get_tags_commands() -> get_tags_commands(?DEFAULT_VERSION). -spec get_tags_commands(integer()) -> [{string(), [string()]}]. %% @spec (integer) -> [{Tag::string(), [CommandName::string()]}] %% @doc Get all the tags and associated commands in a given API version get_tags_commands(Version) -> CommandTags = [{Name, Tags} || #ejabberd_commands{name = Name, tags = Tags} <- get_commands_definition(Version)], Dict = lists:foldl( fun({CommandNameAtom, CTags}, D) -> CommandName = atom_to_list(CommandNameAtom), case CTags of [] -> orddict:append("untagged", CommandName, D); _ -> lists:foldl( fun(TagAtom, DD) -> Tag = atom_to_list(TagAtom), orddict:append(Tag, CommandName, DD) end, D, CTags) end end, orddict:new(), CommandTags), orddict:to_list(Dict). %% ----------------------------- %% Access verification %% ----------------------------- -spec check_auth(ejabberd_commands(), noauth) -> noauth_provided; (ejabberd_commands(), {binary(), binary(), binary(), boolean()}) -> {ok, binary(), binary()}. check_auth(_Command, noauth) -> no_auth_provided; check_auth(Command, {User, Server, {oauth, Token}, _}) -> ScopeList = cmd_scope(Command), case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of true -> {ok, User, Server}; _ -> throw({error, invalid_account_data}) end; check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) -> %% Check the account exists and password is valid case ejabberd_auth:check_password(User, <<"">>, Server, Password) of true -> {ok, User, Server}; _ -> throw({error, invalid_account_data}) end. get_exposed_commands() -> get_exposed_commands(?DEFAULT_VERSION). get_exposed_commands(Version) -> Opts0 = ejabberd_config:get_option(commands, []), Opts = lists:map(fun(V) when is_tuple(V) -> [V]; (V) -> V end, Opts0), CommandsList = list_commands_policy(Version), OpenCmds = [N || {N, _, _, open} <- CommandsList], RestrictedCmds = [N || {N, _, _, restricted} <- CommandsList], AdminCmds = [N || {N, _, _, admin} <- CommandsList], UserCmds = [N || {N, _, _, user} <- CommandsList], Cmds = lists:foldl( fun([{add_commands, L}], Acc) -> Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds), lists:usort(Cmds ++ Acc); ([{remove_commands, L}], Acc) -> Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds), Acc -- Cmds; (_, Acc) -> Acc end, [], Opts), Cmds. %% This is used to allow mixing command policy (like open, user, admin, restricted), with command entry expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_atom(L) -> expand_commands([L], OpenCmds, UserCmds, AdminCmds, RestrictedCmds); expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_list(L) -> lists:foldl(fun(open, Acc) -> OpenCmds ++ Acc; (user, Acc) -> UserCmds ++ Acc; (admin, Acc) -> AdminCmds ++ Acc; (restricted, Acc) -> RestrictedCmds ++ Acc; (Command, Acc) when is_atom(Command) -> [Command|Acc] end, [], L). is_admin(_Name, admin, _Extra) -> true; is_admin(_Name, {_User, _Server, _, false}, _Extra) -> false; is_admin(_Name, Map, _extra) when is_map(Map) -> true; is_admin(Name, Auth, Extra) -> {ACLInfo, Server} = case Auth of {U, S, _, _} -> {Extra#{usr=>jid:split(jid:make(U, S))}, S}; _ -> {Extra, global} end, AdminAccess = ejabberd_config:get_option(commands_admin_access, none), case acl:access_matches(AdminAccess, ACLInfo, Server) of allow -> case catch check_auth(get_command_definition(Name), Auth) of {ok, _, _} -> true; no_auth_provided -> true; _ -> false end; deny -> false end. permission_addon() -> [{<<"'commands' option compatibility shim">>, {[], [{access, ejabberd_config:get_option(commands_admin_access, none)}], {get_exposed_commands(), []}}}]. -spec opt_type(commands_admin_access) -> fun((any()) -> any()); (commands) -> fun((list()) -> list()); (atom()) -> [atom()]. opt_type(commands_admin_access) -> fun acl:access_rules_validator/1; opt_type(commands) -> fun(V) when is_list(V) -> V end; opt_type(_) -> [commands, commands_admin_access]. ejabberd-18.01/src/pubsub_subscription_sql.erl0000644000232200023220000002662013225664356022127 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pubsub_subscription_sql.erl %%% Author : Pablo Polvorin %%% Purpose : Handle pubsub subscriptions options with ODBC backend %%% based on pubsub_subscription.erl by Brian Cully %%% Created : 7 Aug 2009 by Pablo Polvorin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(pubsub_subscription_sql). -author("pablo.polvorin@process-one.net"). %% API -export([init/3, subscribe_node/3, unsubscribe_node/3, get_subscription/3, set_subscription/4, make_subid/0, get_options_xform/2, parse_options_xform/1]). -include("pubsub.hrl"). -include("xmpp.hrl"). -define(PUBSUB_DELIVER, <<"pubsub#deliver">>). -define(PUBSUB_DIGEST, <<"pubsub#digest">>). -define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>). -define(PUBSUB_EXPIRE, <<"pubsub#expire">>). -define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>). -define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>). -define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>). -define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>). -define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>). -define(DIGEST_LABEL, <<"Whether an entity wants to receive digests " "(aggregations) of notifications or all notifications individually">>). -define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between " "sending any two notification digests">>). -define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>). -define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an " "XMPP message body in addition to the payload format">>). -define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>). -define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>). -define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>). -define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>). -define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>). -define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>). -define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>). -define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>). -define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>). -define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>). -define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>). -define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>). -define(DB_MOD, pubsub_db_sql). %%==================================================================== %% API %%==================================================================== init(_Host, _ServerHost, _Opts) -> ok = create_table(). -spec subscribe_node(_JID :: _, _NodeId :: _, Options :: [] | mod_pubsub:subOptions()) -> {result, mod_pubsub:subId()}. subscribe_node(_JID, _NodeId, Options) -> SubID = make_subid(), (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}), {result, SubID}. -spec unsubscribe_node(_JID :: _, _NodeId :: _, SubID :: mod_pubsub:subId()) -> {result, mod_pubsub:subscription()} | {error, notfound}. unsubscribe_node(_JID, _NodeId, SubID) -> case (?DB_MOD):read_subscription(SubID) of {ok, Sub} -> (?DB_MOD):delete_subscription(SubID), {result, Sub}; notfound -> {error, notfound} end. -spec get_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId()) -> {result, mod_pubsub:subscription()} | {error, notfound}. get_subscription(_JID, _NodeId, SubID) -> case (?DB_MOD):read_subscription(SubID) of {ok, Sub} -> {result, Sub}; notfound -> {error, notfound} end. -spec set_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId(), Options :: mod_pubsub:subOptions()) -> {result, ok}. set_subscription(_JID, _NodeId, SubID, Options) -> case (?DB_MOD):read_subscription(SubID) of {ok, _} -> (?DB_MOD):update_subscription(#pubsub_subscription{subid = SubID, options = Options}), {result, ok}; notfound -> (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}), {result, ok} end. get_options_xform(Lang, Options) -> Keys = [deliver, show_values, subscription_type, subscription_depth], XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys], {result, #xdata{type = form, fields = [#xdata_field{type = hidden, var = <<"FORM_TYPE">>, values = [?NS_PUBSUB_SUB_OPTIONS]}| XFields]}}. parse_options_xform(XFields) -> Opts = set_xoption(XFields, []), {result, Opts}. %%==================================================================== %% Internal functions %%==================================================================== create_table() -> ok. -spec make_subid() -> mod_pubsub:subId(). make_subid() -> {T1, T2, T3} = p1_time_compat:timestamp(), (str:format("~.16B~.16B~.16B", [T1, T2, T3])). %% %% Subscription XForm processing. %% %% Return processed options, with types converted and so forth, using %% Opts as defaults. set_xoption([], Opts) -> Opts; set_xoption([{Var, Value} | T], Opts) -> NewOpts = case var_xfield(Var) of {error, _} -> Opts; Key -> Val = val_xfield(Key, Value), lists:keystore(Key, 1, Opts, {Key, Val}) end, set_xoption(T, NewOpts). %% Return the options list's key for an XForm var. %% Convert Values for option list's Key. var_xfield(?PUBSUB_DELIVER) -> deliver; var_xfield(?PUBSUB_DIGEST) -> digest; var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency; var_xfield(?PUBSUB_EXPIRE) -> expire; var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body; var_xfield(?PUBSUB_SHOW_VALUES) -> show_values; var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type; var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth; var_xfield(_) -> {error, badarg}. val_xfield(deliver = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(digest = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(digest_frequency = Opt, [Val]) -> case catch binary_to_integer(Val) of N when is_integer(N) -> N; _ -> Txt = {<<"Value of '~s' should be integer">>, [Opt]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)} end; val_xfield(expire = Opt, [Val]) -> try xmpp_util:decode_timestamp(Val) catch _:{bad_timestamp, _} -> Txt = {<<"Value of '~s' should be datetime string">>, [Opt]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)} end; val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(show_values, Vals) -> Vals; val_xfield(subscription_type, [<<"items">>]) -> items; val_xfield(subscription_type, [<<"nodes">>]) -> nodes; val_xfield(subscription_depth, [<<"all">>]) -> all; val_xfield(subscription_depth = Opt, [Depth]) -> case catch binary_to_integer(Depth) of N when is_integer(N) -> N; _ -> Txt = {<<"Value of '~s' should be integer">>, [Opt]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)} end. %% Convert XForm booleans to Erlang booleans. xopt_to_bool(_, <<"0">>) -> false; xopt_to_bool(_, <<"1">>) -> true; xopt_to_bool(_, <<"false">>) -> false; xopt_to_bool(_, <<"true">>) -> true; xopt_to_bool(Option, _) -> Txt = {<<"Value of '~s' should be boolean">>, [Option]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)}. %% Return a field for an XForm for Key, with data filled in, if %% applicable, from Options. get_option_xfield(Lang, Key, Options) -> Var = xfield_var(Key), Label = xfield_label(Key), {Type, OptEls} = type_and_options(xfield_type(Key), Lang), Vals = case lists:keysearch(Key, 1, Options) of {value, {_, Val}} -> [xfield_val(Key, Val)]; false -> [] end, #xdata_field{type = Type, var = Var, label = translate:translate(Lang, Label), values = Vals, options = OptEls}. type_and_options({Type, Options}, Lang) -> {Type, [tr_xfield_options(O, Lang) || O <- Options]}; type_and_options(Type, _Lang) -> {Type, []}. tr_xfield_options({Value, Label}, Lang) -> #xdata_option{label = translate:translate(Lang, Label), value = Value}. xfield_var(deliver) -> ?PUBSUB_DELIVER; %xfield_var(digest) -> ?PUBSUB_DIGEST; %xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY; %xfield_var(expire) -> ?PUBSUB_EXPIRE; %xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY; xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES; xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE; xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH. xfield_type(deliver) -> boolean; %xfield_type(digest) -> boolean; %xfield_type(digest_frequency) -> 'text-single'; %xfield_type(expire) -> 'text-single'; %xfield_type(include_body) -> boolean; xfield_type(show_values) -> {'list-multi', [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, {<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; xfield_type(subscription_type) -> {'list-single', [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; xfield_type(subscription_depth) -> {'list-single', [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. %% Return the XForm variable label for a subscription option key. xfield_label(deliver) -> ?DELIVER_LABEL; %xfield_label(digest) -> ?DIGEST_LABEL; %xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL; %xfield_label(expire) -> ?EXPIRE_LABEL; %xfield_label(include_body) -> ?INCLUDE_BODY_LABEL; xfield_label(show_values) -> ?SHOW_VALUES_LABEL; %% Return the XForm value for a subscription option key. %% Convert erlang booleans to XForms. xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL; xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL. xfield_val(deliver, Val) -> [bool_to_xopt(Val)]; %xfield_val(digest, Val) -> [bool_to_xopt(Val)]; %xfield_val(digest_frequency, Val) -> % [integer_to_binary(Val))]; %xfield_val(expire, Val) -> % [jlib:now_to_utc_string(Val)]; %xfield_val(include_body, Val) -> [bool_to_xopt(Val)]; xfield_val(show_values, Val) -> Val; xfield_val(subscription_type, items) -> [<<"items">>]; xfield_val(subscription_type, nodes) -> [<<"nodes">>]; xfield_val(subscription_depth, all) -> [<<"all">>]; xfield_val(subscription_depth, N) -> [integer_to_binary(N)]. bool_to_xopt(false) -> <<"false">>; bool_to_xopt(true) -> <<"true">>. ejabberd-18.01/src/ejabberd_auth_ldap.erl0000644000232200023220000002776513225664356020736 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_auth_ldap.erl %%% Author : Alexey Shchepin %%% Purpose : Authentification via LDAP %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_auth_ldap). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -behaviour(gen_server). -behaviour(ejabberd_auth). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, handle_cast/2, terminate/2, code_change/3]). -export([start/1, stop/1, start_link/1, set_password/3, check_password/4, user_exists/2, get_users/2, count_users/2, store_type/1, plain_password_required/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("eldap.hrl"). -record(state, {host = <<"">> :: binary(), eldap_id = <<"">> :: binary(), bind_eldap_id = <<"">> :: binary(), servers = [] :: [binary()], backups = [] :: [binary()], port = ?LDAP_PORT :: inet:port_number(), tls_options = [] :: list(), dn = <<"">> :: binary(), password = <<"">> :: binary(), base = <<"">> :: binary(), uids = [] :: [{binary()} | {binary(), binary()}], ufilter = <<"">> :: binary(), sfilter = <<"">> :: binary(), lfilter :: {any(), any()} | undefined, deref_aliases = never :: never | searching | finding | always, dn_filter :: binary() | undefined, dn_filter_attrs = [] :: [binary()]}). handle_cast(_Request, State) -> {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. handle_info(_Info, State) -> {noreply, State}. -define(LDAP_SEARCH_TIMEOUT, 5). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), ChildSpec = {Proc, {?MODULE, start_link, [Host]}, transient, 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_backend_sup, ChildSpec). stop(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), supervisor:terminate_child(ejabberd_backend_sup, Proc), supervisor:delete_child(ejabberd_backend_sup, Proc). start_link(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), gen_server:start_link({local, Proc}, ?MODULE, Host, []). terminate(_Reason, _State) -> ok. init(Host) -> process_flag(trap_exit, true), State = parse_options(Host), eldap_pool:start_link(State#state.eldap_id, State#state.servers, State#state.backups, State#state.port, State#state.dn, State#state.password, State#state.tls_options), eldap_pool:start_link(State#state.bind_eldap_id, State#state.servers, State#state.backups, State#state.port, State#state.dn, State#state.password, State#state.tls_options), {ok, State}. plain_password_required(_) -> true. store_type(_) -> external. check_password(User, AuthzId, Server, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> false; true -> if Password == <<"">> -> false; true -> case catch check_password_ldap(User, Server, Password) of {'EXIT', _} -> false; Result -> Result end end end. set_password(User, Server, Password) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), case find_user_dn(User, State) of false -> {error, notfound}; DN -> case eldap_pool:modify_passwd(State#state.eldap_id, DN, Password) of ok -> ok; _Err -> {error, db_failure} end end. get_users(Server, []) -> case catch get_users_ldap(Server) of {'EXIT', _} -> []; Result -> Result end. count_users(Server, Opts) -> length(get_users(Server, Opts)). %% @spec (User, Server) -> true | false | {error, Error} user_exists(User, Server) -> case catch user_exists_ldap(User, Server) of {'EXIT', _Error} -> {error, db_failure}; Result -> Result end. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- check_password_ldap(User, Server, Password) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), case find_user_dn(User, State) of false -> false; DN -> case eldap_pool:bind(State#state.bind_eldap_id, DN, Password) of ok -> true; _ -> false end end. get_users_ldap(Server) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), UIDs = State#state.uids, Eldap_ID = State#state.eldap_id, Server = State#state.host, ResAttrs = result_attrs(State), case eldap_filter:parse(State#state.sfilter) of {ok, EldapFilter} -> case eldap_pool:search(Eldap_ID, [{base, State#state.base}, {filter, EldapFilter}, {timeout, ?LDAP_SEARCH_TIMEOUT}, {deref_aliases, State#state.deref_aliases}, {attributes, ResAttrs}]) of #eldap_search_result{entries = Entries} -> lists:flatmap(fun (#eldap_entry{attributes = Attrs, object_name = DN}) -> case is_valid_dn(DN, Attrs, State) of false -> []; _ -> case eldap_utils:find_ldap_attrs(UIDs, Attrs) of <<"">> -> []; {User, UIDFormat} -> case eldap_utils:get_user_part(User, UIDFormat) of {ok, U} -> case jid:nodeprep(U) of error -> []; LU -> [{LU, jid:nameprep(Server)}] end; _ -> [] end end end end, Entries); _ -> [] end; _ -> [] end. user_exists_ldap(User, Server) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), case find_user_dn(User, State) of false -> false; _DN -> true end. handle_call(get_state, _From, State) -> {reply, {ok, State}, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call(_Request, _From, State) -> {reply, bad_request, State}. find_user_dn(User, State) -> ResAttrs = result_attrs(State), case eldap_filter:parse(State#state.ufilter, [{<<"%u">>, User}]) of {ok, Filter} -> case eldap_pool:search(State#state.eldap_id, [{base, State#state.base}, {filter, Filter}, {deref_aliases, State#state.deref_aliases}, {attributes, ResAttrs}]) of #eldap_search_result{entries = [#eldap_entry{attributes = Attrs, object_name = DN} | _]} -> dn_filter(DN, Attrs, State); _ -> false end; _ -> false end. %% apply the dn filter and the local filter: dn_filter(DN, Attrs, State) -> case check_local_filter(Attrs, State) of false -> false; true -> is_valid_dn(DN, Attrs, State) end. %% Check that the DN is valid, based on the dn filter is_valid_dn(DN, _, #state{dn_filter = undefined}) -> DN; is_valid_dn(DN, Attrs, State) -> DNAttrs = State#state.dn_filter_attrs, UIDs = State#state.uids, Values = [{<<"%s">>, eldap_utils:get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs], SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of <<"">> -> Values; {S, UAF} -> case eldap_utils:get_user_part(S, UAF) of {ok, U} -> [{<<"%u">>, U} | Values]; _ -> Values end end ++ [{<<"%d">>, State#state.host}, {<<"%D">>, DN}], case eldap_filter:parse(State#state.dn_filter, SubstValues) of {ok, EldapFilter} -> case eldap_pool:search(State#state.eldap_id, [{base, State#state.base}, {filter, EldapFilter}, {deref_aliases, State#state.deref_aliases}, {attributes, [<<"dn">>]}]) of #eldap_search_result{entries = [_ | _]} -> DN; _ -> false end; _ -> false end. %% The local filter is used to check an attribute in ejabberd %% and not in LDAP to limit the load on the LDAP directory. %% A local rule can be either: %% {equal, {"accountStatus",["active"]}} %% {notequal, {"accountStatus",["disabled"]}} %% {ldap_local_filter, {notequal, {"accountStatus",["disabled"]}}} check_local_filter(_Attrs, #state{lfilter = undefined}) -> true; check_local_filter(Attrs, #state{lfilter = LocalFilter}) -> {Operation, FilterMatch} = LocalFilter, local_filter(Operation, Attrs, FilterMatch). local_filter(equal, Attrs, FilterMatch) -> {Attr, Value} = FilterMatch, case lists:keysearch(Attr, 1, Attrs) of false -> false; {value, {Attr, Value}} -> true; _ -> false end; local_filter(notequal, Attrs, FilterMatch) -> not local_filter(equal, Attrs, FilterMatch). result_attrs(#state{uids = UIDs, dn_filter_attrs = DNFilterAttrs}) -> lists:foldl(fun ({UID}, Acc) -> [UID | Acc]; ({UID, _}, Acc) -> [UID | Acc] end, DNFilterAttrs, UIDs). %%%---------------------------------------------------------------------- %%% Auxiliary functions %%%---------------------------------------------------------------------- parse_options(Host) -> Cfg = eldap_utils:get_config(Host, []), Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)), Bind_Eldap_ID = misc:atom_to_binary( gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)), UIDsTemp = ejabberd_config:get_option( {ldap_uids, Host}, [{<<"uid">>, <<"%u">>}]), UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp), SubFilter = eldap_utils:generate_subfilter(UIDs), UserFilter = case ejabberd_config:get_option({ldap_filter, Host}, <<"">>) of <<"">> -> SubFilter; F -> <<"(&", SubFilter/binary, F/binary, ")">> end, SearchFilter = eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}]), {DNFilter, DNFilterAttrs} = ejabberd_config:get_option({ldap_dn_filter, Host}, {undefined, []}), LocalFilter = ejabberd_config:get_option({ldap_local_filter, Host}), #state{host = Host, eldap_id = Eldap_ID, bind_eldap_id = Bind_Eldap_ID, servers = Cfg#eldap_config.servers, backups = Cfg#eldap_config.backups, port = Cfg#eldap_config.port, tls_options = Cfg#eldap_config.tls_options, dn = Cfg#eldap_config.dn, password = Cfg#eldap_config.password, base = Cfg#eldap_config.base, deref_aliases = Cfg#eldap_config.deref_aliases, uids = UIDs, ufilter = UserFilter, sfilter = SearchFilter, lfilter = LocalFilter, dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}. -spec opt_type(ldap_dn_filter) -> fun(([{binary(), binary()}]) -> [{binary(), binary()}]); (ldap_local_filter) -> fun((any()) -> any()); (atom()) -> [atom()]. opt_type(ldap_dn_filter) -> fun ([{DNF, DNFA}]) -> NewDNFA = case DNFA of undefined -> []; _ -> [iolist_to_binary(A) || A <- DNFA] end, NewDNF = eldap_utils:check_filter(DNF), {NewDNF, NewDNFA} end; opt_type(ldap_local_filter) -> fun (V) -> V end; opt_type(_) -> [ldap_dn_filter, ldap_local_filter]. ejabberd-18.01/src/cyrsasl_digest.erl0000644000232200023220000002227613225664356020166 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : cyrsasl_digest.erl %%% Author : Alexey Shchepin %%% Purpose : DIGEST-MD5 SASL mechanism %%% Created : 11 Mar 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(cyrsasl_digest). -behaviour(ejabberd_config). -author('alexey@sevcom.net'). -export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -behaviour(cyrsasl). -type get_password_fun() :: fun((binary()) -> {false, any()} | {binary(), atom()}). -type check_password_fun() :: fun((binary(), binary(), binary(), binary(), fun((binary()) -> binary())) -> {boolean(), any()} | false). -type error_reason() :: parser_failed | invalid_digest_uri | not_authorized | unexpected_response. -export_type([error_reason/0]). -record(state, {step = 1 :: 1 | 3 | 5, nonce = <<"">> :: binary(), username = <<"">> :: binary(), authzid = <<"">> :: binary(), get_password :: get_password_fun(), check_password :: check_password_fun(), auth_module :: atom(), host = <<"">> :: binary(), hostfqdn = [] :: [binary()]}). start(_Opts) -> Fqdn = get_local_fqdn(), ?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s", [Fqdn]), cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE, digest). stop() -> ok. -spec format_error(error_reason()) -> {atom(), binary()}. format_error(parser_failed) -> {'bad-protocol', <<"Response decoding failed">>}; format_error(invalid_digest_uri) -> {'bad-protocol', <<"Invalid digest URI">>}; format_error(not_authorized) -> {'not-authorized', <<"Invalid username or password">>}; format_error(unexpected_response) -> {'bad-protocol', <<"Unexpected response">>}. mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) -> {ok, #state{step = 1, nonce = randoms:get_string(), host = Host, hostfqdn = get_local_fqdn(), get_password = GetPassword, check_password = CheckPasswordDigest}}. mech_step(#state{step = 1, nonce = Nonce} = State, _) -> {continue, <<"nonce=\"", Nonce/binary, "\",qop=\"auth\",charset=utf-8,algorithm=md5-sess">>, State#state{step = 3}}; mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) -> case parse(ClientIn) of bad -> {error, parser_failed}; KeyVals -> DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>), UserName = proplists:get_value(<<"username">>, KeyVals, <<>>), case is_digesturi_valid(DigestURI, State#state.host, State#state.hostfqdn) of false -> ?DEBUG("User login not authorized because digest-uri " "seems invalid: ~p (checking for Host " "~p, FQDN ~p)", [DigestURI, State#state.host, State#state.hostfqdn]), {error, invalid_digest_uri, UserName}; true -> AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>), case (State#state.get_password)(UserName) of {false, _} -> {error, not_authorized, UserName}; {Passwd, AuthModule} -> case (State#state.check_password)(UserName, UserName, <<"">>, proplists:get_value(<<"response">>, KeyVals, <<>>), fun (PW) -> response(KeyVals, UserName, PW, Nonce, AuthzId, <<"AUTHENTICATE">>) end) of {true, _} -> RspAuth = response(KeyVals, UserName, Passwd, Nonce, AuthzId, <<"">>), {continue, <<"rspauth=", RspAuth/binary>>, State#state{step = 5, auth_module = AuthModule, username = UserName, authzid = AuthzId}}; false -> {error, not_authorized, UserName}; {false, _} -> {error, not_authorized, UserName} end end end end; mech_step(#state{step = 5, auth_module = AuthModule, username = UserName, authzid = AuthzId}, <<"">>) -> {ok, [{username, UserName}, {authzid, case AuthzId of <<"">> -> UserName; _ -> AuthzId end }, {auth_module, AuthModule}]}; mech_step(A, B) -> ?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]), {error, unexpected_response}. parse(S) -> parse1(binary_to_list(S), "", []). parse1([$= | Cs], S, Ts) -> parse2(Cs, lists:reverse(S), "", Ts); parse1([$, | Cs], [], Ts) -> parse1(Cs, [], Ts); parse1([$\s | Cs], [], Ts) -> parse1(Cs, [], Ts); parse1([C | Cs], S, Ts) -> parse1(Cs, [C | S], Ts); parse1([], [], T) -> lists:reverse(T); parse1([], _S, _T) -> bad. parse2([$" | Cs], Key, Val, Ts) -> parse3(Cs, Key, Val, Ts); parse2([C | Cs], Key, Val, Ts) -> parse4(Cs, Key, [C | Val], Ts); parse2([], _, _, _) -> bad. parse3([$" | Cs], Key, Val, Ts) -> parse4(Cs, Key, Val, Ts); parse3([$\\, C | Cs], Key, Val, Ts) -> parse3(Cs, Key, [C | Val], Ts); parse3([C | Cs], Key, Val, Ts) -> parse3(Cs, Key, [C | Val], Ts); parse3([], _, _, _) -> bad. parse4([$, | Cs], Key, Val, Ts) -> parse1(Cs, "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]); parse4([$\s | Cs], Key, Val, Ts) -> parse4(Cs, Key, Val, Ts); parse4([C | Cs], Key, Val, Ts) -> parse4(Cs, Key, [C | Val], Ts); parse4([], Key, Val, Ts) -> %% @doc Check if the digest-uri is valid. %% RFC-2831 allows to provide the IP address in Host, %% however ejabberd doesn't allow that. %% If the service (for example jabber.example.org) %% is provided by several hosts (being one of them server3.example.org), %% then acceptable digest-uris would be: %% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and %% xmpp/jabber.example.org %% The last version is not actually allowed by the RFC, but implemented by popular clients parse1([], "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]). is_digesturi_valid(DigestURICase, JabberDomain, JabberFQDN) -> DigestURI = stringprep:tolower(DigestURICase), case catch str:tokens(DigestURI, <<"/">>) of [<<"xmpp">>, Host] -> IsHostFqdn = is_host_fqdn(Host, JabberFQDN), (Host == JabberDomain) or IsHostFqdn; [<<"xmpp">>, Host, ServName] -> IsHostFqdn = is_host_fqdn(Host, JabberFQDN), (ServName == JabberDomain) and IsHostFqdn; _ -> false end. is_host_fqdn(_Host, []) -> false; is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn -> true; is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn -> is_host_fqdn(Host, FqdnTail). get_local_fqdn() -> case ejabberd_config:get_option(fqdn) of undefined -> {ok, Hostname} = inet:gethostname(), {ok, {hostent, Fqdn, _, _, _, _}} = inet:gethostbyname(Hostname), [list_to_binary(Fqdn)]; Fqdn -> Fqdn end. hex(S) -> str:to_hexlist(S). proplists_get_bin_value(Key, Pairs, Default) -> case proplists:get_value(Key, Pairs, Default) of L when is_list(L) -> list_to_binary(L); L2 -> L2 end. response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) -> Realm = proplists_get_bin_value(<<"realm">>, KeyVals, <<>>), CNonce = proplists_get_bin_value(<<"cnonce">>, KeyVals, <<>>), DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>), NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>), QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>), MD5Hash = erlang:md5(<>), A1 = case AuthzId of <<"">> -> <>; _ -> <> end, A2 = case QOP of <<"auth">> -> <>; _ -> <> end, T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary, ":", NC/binary, ":", CNonce/binary, ":", QOP/binary, ":", (hex((erlang:md5(A2))))/binary>>, hex((erlang:md5(T))). -spec opt_type(fqdn) -> fun((binary() | [binary()]) -> [binary()]); (atom()) -> [atom()]. opt_type(fqdn) -> fun(FQDN) when is_binary(FQDN) -> [FQDN]; (FQDNs) when is_list(FQDNs) -> [iolist_to_binary(FQDN) || FQDN <- FQDNs] end; opt_type(_) -> [fqdn]. ejabberd-18.01/src/nodetree_tree_sql.erl0000644000232200023220000002306613225664356020650 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : nodetree_tree_sql.erl %%% Author : Christophe Romain %%% Purpose : Standard node tree plugin with ODBC backend %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the default PubSub node tree plugin. %%%

It is used as a default for all unknown PubSub node type. It can serve %%% as a developer basis and reference to build its own custom pubsub node tree %%% types.

%%%

PubSub node tree plugins are using the {@link gen_nodetree} behaviour.

%%%

The API isn't stabilized yet. The pubsub plugin %%% development is still a work in progress. However, the system is already %%% useable and useful as is. Please, send us comments, feedback and %%% improvements.

-module(nodetree_tree_sql). -behaviour(gen_pubsub_nodetree). -author('christophe.romain@process-one.net'). -compile([{parse_transform, ejabberd_sql_pt}]). -include("pubsub.hrl"). -include("xmpp.hrl"). -include("ejabberd_sql_pt.hrl"). -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). -export([raw_to_node/2]). init(_Host, _ServerHost, _Opts) -> ok. terminate(_Host, _ServerHost) -> ok. options() -> [{sql, true} | nodetree_tree:options()]. set_node(Record) when is_record(Record, pubsub_node) -> {Host, Node} = Record#pubsub_node.nodeid, Parent = case Record#pubsub_node.parents of [] -> <<>>; [First | _] -> First end, Type = Record#pubsub_node.type, H = node_flat_sql:encode_host(Host), Nidx = case nodeidx(Host, Node) of {result, OldNidx} -> catch ejabberd_sql:sql_query_t( ?SQL("delete from pubsub_node_option " "where nodeid=%(OldNidx)d")), catch ejabberd_sql:sql_query_t( ?SQL("update pubsub_node set" " host=%(H)s, node=%(Node)s," " parent=%(Parent)s, plugin=%(Type)s " "where nodeid=%(OldNidx)d")), OldNidx; _ -> catch ejabberd_sql:sql_query_t( ?SQL("insert into pubsub_node(host, node, parent, plugin) " "values(%(H)s, %(Node)s, %(Parent)s, %(Type)s)")), case nodeidx(Host, Node) of {result, NewNidx} -> NewNidx; _ -> none % this should not happen end end, case Nidx of none -> Txt = <<"Node index not found">>, {error, xmpp:err_internal_server_error(Txt, ?MYLANG)}; _ -> lists:foreach(fun ({Key, Value}) -> SKey = iolist_to_binary(atom_to_list(Key)), SValue = misc:term_to_expr(Value), catch ejabberd_sql:sql_query_t( ?SQL("insert into pubsub_node_option(nodeid, name, val) " "values (%(Nidx)d, %(SKey)s, %(SValue)s)")) end, Record#pubsub_node.options), {result, Nidx} end. get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node) -> H = node_flat_sql:encode_host(Host), case catch ejabberd_sql:sql_query_t( ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node " "where host=%(H)s and node=%(Node)s")) of {selected, [RItem]} -> raw_to_node(Host, RItem); {'EXIT', _Reason} -> {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_node(Nidx) -> case catch ejabberd_sql:sql_query_t( ?SQL("select @(host)s, @(node)s, @(parent)s, @(plugin)s from pubsub_node " "where nodeid=%(Nidx)d")) of {selected, [{Host, Node, Parent, Type}]} -> raw_to_node(Host, {Node, Parent, Type, Nidx}); {'EXIT', _Reason} -> {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_nodes(Host, _From) -> get_nodes(Host). get_nodes(Host) -> H = node_flat_sql:encode_host(Host), case catch ejabberd_sql:sql_query_t( ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node " "where host=%(H)s")) of {selected, RItems} -> [raw_to_node(Host, Item) || Item <- RItems]; _ -> [] end. get_parentnodes(Host, Node, _From) -> case get_node(Host, Node) of Record when is_record(Record, pubsub_node) -> Record#pubsub_node.parents; _ -> [] end. get_parentnodes_tree(Host, Node, _From) -> get_parentnodes_tree(Host, Node, 0, []). get_parentnodes_tree(Host, Node, Level, Acc) -> case get_node(Host, Node) of Record when is_record(Record, pubsub_node) -> Tree = [{Level, [Record]}|Acc], case Record#pubsub_node.parents of [Parent] -> get_parentnodes_tree(Host, Parent, Level+1, Tree); _ -> Tree end; _ -> Acc end. get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node). get_subnodes(Host, Node) -> H = node_flat_sql:encode_host(Host), case catch ejabberd_sql:sql_query_t( ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node " "where host=%(H)s and parent=%(Node)s")) of {selected, RItems} -> [raw_to_node(Host, Item) || Item <- RItems]; _ -> [] end. get_subnodes_tree(Host, Node, _From) -> get_subnodes_tree(Host, Node). get_subnodes_tree(Host, Node) -> case get_node(Host, Node) of {error, _} -> []; Rec -> Type = Rec#pubsub_node.type, H = node_flat_sql:encode_host(Host), N = <<(ejabberd_sql:escape_like_arg_circumflex(Node))/binary, "/%">>, Sub = case catch ejabberd_sql:sql_query_t( ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node " "where host=%(H)s and plugin=%(Type)s and" " (parent=%(Node)s or parent like %(N)s escape '^')")) of {selected, RItems} -> [raw_to_node(Host, Item) || Item <- RItems]; _ -> [] end, [Rec|Sub] end. create_node(Host, Node, Type, Owner, Options, Parents) -> BJID = jid:tolower(jid:remove_resource(Owner)), case nodeidx(Host, Node) of {error, not_found} -> ParentExists = case Host of {_U, _S, _R} -> %% This is special case for PEP handling %% PEP does not uses hierarchy true; _ -> case Parents of [] -> true; [Parent | _] -> case nodeidx(Host, Parent) of {result, PNode} -> case nodeowners(PNode) of [{<<>>, Host, <<>>}] -> true; Owners -> lists:member(BJID, Owners) end; _ -> false end; _ -> false end end, case ParentExists of true -> case set_node(#pubsub_node{nodeid = {Host, Node}, parents = Parents, type = Type, options = Options}) of {result, Nidx} -> {ok, Nidx}; Other -> Other end; false -> {error, xmpp:err_forbidden()} end; {result, _} -> {error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)}; {error, db_fail} -> {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)} end. delete_node(Host, Node) -> lists:map( fun(Rec) -> Nidx = Rec#pubsub_node.id, catch ejabberd_sql:sql_query_t( ?SQL("delete from pubsub_node where nodeid=%(Nidx)d")), Rec end, get_subnodes_tree(Host, Node)). %% helpers raw_to_node(Host, [Node, Parent, Type, Nidx]) -> raw_to_node(Host, {Node, Parent, Type, binary_to_integer(Nidx)}); raw_to_node(Host, {Node, Parent, Type, Nidx}) -> Options = case catch ejabberd_sql:sql_query_t( ?SQL("select @(name)s, @(val)s from pubsub_node_option " "where nodeid=%(Nidx)d")) of {selected, ROptions} -> DbOpts = lists:map(fun ({Key, Value}) -> RKey = misc:binary_to_atom(Key), Tokens = element(2, erl_scan:string(binary_to_list(<>))), RValue = element(2, erl_parse:parse_term(Tokens)), {RKey, RValue} end, ROptions), Module = misc:binary_to_atom(<<"node_", Type/binary, "_sql">>), StdOpts = Module:options(), lists:foldl(fun ({Key, Value}, Acc) -> lists:keystore(Key, 1, Acc, {Key, Value}) end, StdOpts, DbOpts); _ -> [] end, Parents = case Parent of <<>> -> []; _ -> [Parent] end, #pubsub_node{nodeid = {Host, Node}, id = Nidx, parents = Parents, type = Type, options = Options}. nodeidx(Host, Node) -> H = node_flat_sql:encode_host(Host), case catch ejabberd_sql:sql_query_t( ?SQL("select @(nodeid)d from pubsub_node " "where host=%(H)s and node=%(Node)s")) of {selected, [{Nidx}]} -> {result, Nidx}; {'EXIT', _Reason} -> {error, db_fail}; _ -> {error, not_found} end. nodeowners(Nidx) -> {result, Res} = node_flat_sql:get_node_affiliations(Nidx), [LJID || {LJID, Aff} <- Res, Aff =:= owner]. ejabberd-18.01/src/mod_bosh_riak.erl0000644000232200023220000000453313225664356017743 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 15 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_bosh_riak). -behaviour(mod_bosh). %% API -export([init/0, open_session/2, close_session/1, find_session/1]). -record(bosh, {sid :: binary(), pid :: pid()}). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init() -> clean_table(). open_session(SID, Pid) -> ejabberd_riak:put(#bosh{sid = SID, pid = Pid}, bosh_schema()). close_session(SID) -> ejabberd_riak:delete(bosh, SID). find_session(SID) -> case ejabberd_riak:get(bosh, bosh_schema(), SID) of {ok, #bosh{pid = Pid}} -> {ok, Pid}; {error, _} = Err -> Err end. %%%=================================================================== %%% Internal functions %%%=================================================================== bosh_schema() -> {record_info(fields, bosh), #bosh{}}. clean_table() -> ?DEBUG("Cleaning Riak 'bosh' table...", []), case ejabberd_riak:get(bosh, bosh_schema()) of {ok, Rs} -> lists:foreach( fun(#bosh{sid = SID, pid = Pid}) when node(Pid) == node() -> ejabberd_riak:delete(bosh, SID); (_) -> ok end, Rs); {error, Reason} = Err -> ?ERROR_MSG("failed to clean Riak 'bosh' table: ~p", [Reason]), Err end. ejabberd-18.01/src/gen_pubsub_nodetree.erl0000644000232200023220000000577013225664356021165 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : gen_pubsub_nodetree.erl %%% Author : Christophe Romain %%% Purpose : Define the pubsub node tree plugin behaviour %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(gen_pubsub_nodetree). -type(host() :: mod_pubsub:host()). -type(nodeId() :: mod_pubsub:nodeId()). -type(nodeIdx() :: mod_pubsub:nodeIdx()). -type(pubsubNode() :: mod_pubsub:pubsubNode()). -type(nodeOptions() :: mod_pubsub:nodeOptions()). -callback init(Host :: host(), ServerHost :: binary(), Opts :: [any()]) -> atom(). -include("xmpp.hrl"). -callback terminate(Host :: host(), ServerHost :: binary()) -> atom(). -callback options() -> nodeOptions(). -callback set_node(PubsubNode :: pubsubNode()) -> ok | {result, NodeIdx::nodeIdx()} | {error, stanza_error()}. -callback get_node(Host :: host(), NodeId :: nodeId(), From :: jid:jid()) -> pubsubNode() | {error, stanza_error()}. -callback get_node(Host :: host(), NodeId :: nodeId()) -> pubsubNode() | {error, stanza_error()}. -callback get_node(NodeIdx :: nodeIdx()) -> pubsubNode() | {error, stanza_error()}. -callback get_nodes(Host :: host(), From :: jid:jid())-> [pubsubNode()]. -callback get_nodes(Host :: host())-> [pubsubNode()]. -callback get_parentnodes(Host :: host(), NodeId :: nodeId(), From :: jid:jid()) -> [pubsubNode()] | {error, stanza_error()}. -callback get_parentnodes_tree(Host :: host(), NodeId :: nodeId(), From :: jid:jid()) -> [{0, [pubsubNode(),...]}]. -callback get_subnodes(Host :: host(), NodeId :: nodeId(), From :: jid:jid()) -> [pubsubNode()]. -callback get_subnodes_tree(Host :: host(), NodeId :: nodeId(), From :: jid:jid()) -> [pubsubNode()]. -callback create_node(Host :: host(), NodeId :: nodeId(), Type :: binary(), Owner :: jid:jid(), Options :: nodeOptions(), Parents :: [nodeId()]) -> {ok, NodeIdx::nodeIdx()} | {error, stanza_error()} | {error, {virtual, {host(), nodeId()}}}. -callback delete_node(Host :: host(), NodeId :: nodeId()) -> [pubsubNode()]. ejabberd-18.01/src/mod_proxy65_sql.erl0000644000232200023220000001044713225664356020216 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_proxy65_sql). -behaviour(mod_proxy65). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init() -> NodeS = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'proxy65' table...", []), case ejabberd_sql:sql_query( ?MYNAME, ?SQL("delete from proxy65 where " "node_i=%(NodeS)s or node_t=%(NodeS)s")) of {updated, _} -> ok; Err -> ?ERROR_MSG("failed to clean 'proxy65' table: ~p", [Err]), Err end. register_stream(SID, Pid) -> PidS = misc:encode_pid(Pid), NodeS = erlang:atom_to_binary(node(Pid), latin1), F = fun() -> case ejabberd_sql:sql_query_t( ?SQL("update proxy65 set pid_i=%(PidS)s, " "node_i=%(NodeS)s where sid=%(SID)s")) of {updated, 1} -> ok; _ -> ejabberd_sql:sql_query_t( ?SQL("insert into proxy65" "(sid, pid_t, node_t, pid_i, node_i, jid_i) " "values (%(SID)s, %(PidS)s, %(NodeS)s, '', '', '')")) end end, case ejabberd_sql:sql_transaction(?MYNAME, F) of {atomic, _} -> ok; {aborted, Reason} -> {error, Reason} end. unregister_stream(SID) -> F = fun() -> ejabberd_sql:sql_query_t( ?SQL("delete from proxy65 where sid=%(SID)s")) end, case ejabberd_sql:sql_transaction(?MYNAME, F) of {atomic, _} -> ok; {aborted, Reason} -> {error, Reason} end. activate_stream(SID, IJID, MaxConnections, _Node) -> F = fun() -> case ejabberd_sql:sql_query_t( ?SQL("select @(pid_t)s, @(node_t)s, @(pid_i)s, " "@(node_i)s, @(jid_i)s from proxy65 where " "sid=%(SID)s")) of {selected, [{TPidS, TNodeS, IPidS, INodeS, <<"">>}]} when IPidS /= <<"">> -> try {misc:decode_pid(TPidS, TNodeS), misc:decode_pid(IPidS, INodeS)} of {TPid, IPid} -> case ejabberd_sql:sql_query_t( ?SQL("update proxy65 set jid_i=%(IJID)s " "where sid=%(SID)s")) of {updated, 1} when is_integer(MaxConnections) -> case ejabberd_sql:sql_query_t( ?SQL("select @(count(*))d from proxy65 " "where jid_i=%(IJID)s")) of {selected, [{Num}]} when Num > MaxConnections -> ejabberd_sql:abort({limit, IPid, TPid}); {selected, _} -> {ok, IPid, TPid}; Err -> ejabberd_sql:abort(Err) end; {updated, _} -> {ok, IPid, TPid}; Err -> ejabberd_sql:abort(Err) end catch _:{bad_node, _} -> {error, notfound} end; {selected, [{_, _, _, _, JID}]} when JID /= <<"">> -> {error, conflict}; {selected, _} -> {error, notfound}; Err -> ejabberd_sql:abort(Err) end end, case ejabberd_sql:sql_transaction(?MYNAME, F) of {atomic, Result} -> Result; {aborted, {limit, _, _} = Limit} -> {error, Limit}; {aborted, Reason} -> {error, Reason} end. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/ejabberd_stun.erl0000644000232200023220000001274513225664356017756 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_stun.erl %%% Author : Evgeny Khramtsov %%% Purpose : STUN RFC-5766 %%% Created : 8 May 2014 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2013-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_stun). -protocol({rfc, 5766}). -protocol({xep, 176, '1.0'}). -ifndef(STUN). -include("logger.hrl"). -export([socket_type/0, start/2, listen_opt_type/1]). log_error() -> ?CRITICAL_MSG("ejabberd is not compiled with STUN/TURN support", []). socket_type() -> log_error(), raw. listen_opt_type(_) -> log_error(), []. start(_, _) -> log_error(), {error, sip_not_compiled}. -else. -export([tcp_init/2, udp_init/2, udp_recv/5, start/2, socket_type/0, listen_opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== tcp_init(Socket, Opts) -> ejabberd:start_app(stun), stun:tcp_init(Socket, prepare_turn_opts(Opts)). udp_init(Socket, Opts) -> ejabberd:start_app(stun), stun:udp_init(Socket, prepare_turn_opts(Opts)). udp_recv(Socket, Addr, Port, Packet, Opts) -> stun:udp_recv(Socket, Addr, Port, Packet, Opts). start(Opaque, Opts) -> stun:start(Opaque, Opts). socket_type() -> raw. %%%=================================================================== %%% Internal functions %%%=================================================================== prepare_turn_opts(Opts) -> UseTurn = proplists:get_bool(use_turn, Opts), prepare_turn_opts(Opts, UseTurn). prepare_turn_opts(Opts, _UseTurn = false) -> set_certfile(Opts); prepare_turn_opts(Opts, _UseTurn = true) -> NumberOfMyHosts = length(?MYHOSTS), case proplists:get_value(turn_ip, Opts) of undefined -> ?WARNING_MSG("option 'turn_ip' is undefined, " "more likely the TURN relay won't be working " "properly", []); _ -> ok end, AuthFun = fun ejabberd_auth:get_password_s/2, Shaper = proplists:get_value(shaper, Opts, none), AuthType = proplists:get_value(auth_type, Opts, user), Realm = case proplists:get_value(auth_realm, Opts) of undefined when AuthType == user -> if NumberOfMyHosts > 1 -> ?WARNING_MSG("you have several virtual " "hosts configured, but option " "'auth_realm' is undefined and " "'auth_type' is set to 'user', " "more likely the TURN relay won't " "be working properly. Using ~s as " "a fallback", [?MYNAME]); true -> ok end, [{auth_realm, ?MYNAME}]; _ -> [] end, MaxRate = shaper:get_max_rate(Shaper), Opts1 = Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} | lists:keydelete(shaper, 1, Opts)], set_certfile(Opts1). set_certfile(Opts) -> case lists:keymember(certfile, 1, Opts) of true -> Opts; false -> Realm = proplists:get_value(auth_realm, Opts, ?MYNAME), case ejabberd_pkix:get_certfile(Realm) of {ok, CertFile} -> [{certfile, CertFile}|Opts]; error -> case ejabberd_config:get_option({domain_certfile, Realm}) of undefined -> Opts; CertFile -> [{certfile, CertFile}|Opts] end end end. listen_opt_type(use_turn) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(turn_ip) -> fun(S) -> {ok, Addr} = inet_parse:ipv4_address(binary_to_list(S)), Addr end; listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1; listen_opt_type(auth_type) -> fun(anonymous) -> anonymous; (user) -> user end; listen_opt_type(auth_realm) -> fun iolist_to_binary/1; listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(certfile) -> fun(S) -> %% We cannot deprecate the option for now: %% I think STUN/TURN clients are too stupid to set SNI ejabberd_pkix:add_certfile(S), iolist_to_binary(S) end; listen_opt_type(turn_min_port) -> fun(P) when is_integer(P), P > 0, P =< 65535 -> P end; listen_opt_type(turn_max_port) -> fun(P) when is_integer(P), P > 0, P =< 65535 -> P end; listen_opt_type(turn_max_allocations) -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; listen_opt_type(turn_max_permissions) -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; listen_opt_type(server_name) -> fun iolist_to_binary/1; listen_opt_type(_) -> [shaper, auth_type, auth_realm, tls, certfile, turn_min_port, turn_max_port, turn_max_allocations, turn_max_permissions, server_name]. -endif. ejabberd-18.01/src/nodetree_virtual.erl0000644000232200023220000000757713225664356020531 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : nodetree_virtual.erl %%% Author : Christophe Romain %%% Purpose : Standard node tree plugin using no storage backend %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the PubSub node tree plugin that %%% allow virtual nodes handling. This prevent storage of nodes. %%%

PubSub node tree plugins are using the {@link gen_nodetree} behaviour.

%%%

This plugin development is still a work in progress. Due to optimizations in %%% mod_pubsub, this plugin can not work anymore without altering functioning. %%% Please, send us comments, feedback and improvements.

-module(nodetree_virtual). -behaviour(gen_pubsub_nodetree). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). init(_Host, _ServerHost, _Opts) -> ok. terminate(_Host, _ServerHost) -> ok. options() -> [{virtual_tree, true}]. set_node(_Node) -> ok. get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node) -> Nidx = nodeidx(Host, Node), node_record(Host, Node, Nidx). get_node(Nidx) -> {Host, Node} = nodeid(Nidx), node_record(Host, Node, Nidx). get_nodes(Host, _From) -> get_nodes(Host). get_nodes(_Host) -> []. get_parentnodes(_Host, _Node, _From) -> []. get_parentnodes_tree(Host, Node, From) -> [{0, [get_node(Host, Node, From)]}]. get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node). get_subnodes(_Host, _Node) -> []. get_subnodes_tree(Host, Node, _From) -> get_subnodes_tree(Host, Node). get_subnodes_tree(_Host, _Node) -> []. create_node(Host, Node, _Type, _Owner, _Options, _Parents) -> {error, {virtual, nodeidx(Host, Node)}}. delete_node(Host, Node) -> [get_node(Host, Node)]. %% internal helper node_record({U,S,R}, Node, Nidx) -> Host = mod_pubsub:host(S), Type = <<"pep">>, Module = mod_pubsub:plugin(Host, Type), #pubsub_node{nodeid = {{U,S,R},Node}, id = Nidx, type = Type, owners = [{U,S,R}], options = Module:options()}; node_record(Host, Node, Nidx) -> [Type|_] = mod_pubsub:plugins(Host), Module = mod_pubsub:plugin(Host, Type), #pubsub_node{nodeid = {Host, Node}, id = Nidx, type = Type, owners = [{<<"">>, Host, <<"">>}], options = Module:options()}. nodeidx({U,S,R}, Node) -> JID = jid:encode(jid:make(U,S,R)), <>; nodeidx(Host, Node) -> <>. nodeid(Nidx) -> [Head, Node] = binary:split(Nidx, <<":">>), case jid:decode(Head) of {jid,<<>>,Host,<<>>,_,_,_} -> {Host, Node}; {jid,U,S,R,_,_,_} -> {{U,S,R}, Node} end. ejabberd-18.01/src/ejabberd_auth_external.erl0000644000232200023220000000663713225664356021633 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_auth_external.erl %%% Author : Alexey Shchepin %%% Purpose : Authentification via LDAP external script %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_auth_external). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -behaviour(ejabberd_auth). -export([start/1, stop/1, set_password/3, check_password/4, try_register/3, user_exists/2, remove_user/2, store_type/1, plain_password_required/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host) -> Cmd = ejabberd_config:get_option({extauth_program, Host}, "extauth"), extauth:start(Host, Cmd). stop(Host) -> extauth:stop(Host). plain_password_required(_) -> true. store_type(_) -> external. check_password(User, AuthzId, Server, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> false; true -> check_password_extauth(User, AuthzId, Server, Password) end. set_password(User, Server, Password) -> case extauth:set_password(User, Server, Password) of true -> ok; _ -> {error, db_failure} end. try_register(User, Server, Password) -> extauth:try_register(User, Server, Password). user_exists(User, Server) -> try extauth:user_exists(User, Server) of Res -> Res catch _:Error -> ?ERROR_MSG("external authentication program failure: ~p", [Error]), {error, db_failure} end. remove_user(User, Server) -> case extauth:remove_user(User, Server) of false -> {error, not_allowed}; true -> ok end. check_password_extauth(User, _AuthzId, Server, Password) -> extauth:check_password(User, Server, Password) andalso Password /= <<"">>. -spec opt_type(extauth_cache) -> fun((false | non_neg_integer()) -> false | non_neg_integer()); (extauth_program) -> fun((binary()) -> string()); (atom()) -> [atom()]. opt_type(extauth_cache) -> ?WARNING_MSG("option 'extauth_cache' is deprecated and has no effect, " "use authentication or global cache configuration " "options: auth_use_cache, auth_cache_life_time, " "use_cache, cache_life_time, and so on", []), fun (false) -> false; (I) when is_integer(I), I >= 0 -> I end; opt_type(extauth_program) -> fun (V) -> binary_to_list(iolist_to_binary(V)) end; opt_type(_) -> [extauth_cache, extauth_program]. ejabberd-18.01/src/ejabberd_app.erl0000644000232200023220000001175413225664356017544 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_app.erl %%% Author : Alexey Shchepin %%% Purpose : ejabberd's application callback module %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_app). -author('alexey@process-one.net'). -behaviour(application). -export([start/2, prep_stop/1, stop/1]). -include("ejabberd.hrl"). -include("logger.hrl"). %%% %%% Application API %%% start(normal, _Args) -> {T1, _} = statistics(wall_clock), ejabberd_logger:start(), write_pid_file(), start_apps(), start_elixir_application(), ejabberd:check_app(ejabberd), setup_if_elixir_conf_used(), ejabberd_config:start(), ejabberd_mnesia:start(), file_queue_init(), maybe_add_nameservers(), ejabberd_system_monitor:start(), case ejabberd_sup:start_link() of {ok, SupPid} -> register_elixir_config_hooks(), ejabberd_cluster:wait_for_sync(infinity), {T2, _} = statistics(wall_clock), ?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs", [?VERSION, node(), (T2-T1)/1000]), lists:foreach(fun erlang:garbage_collect/1, processes()), {ok, SupPid}; Err -> ?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]), timer:sleep(1000), halt("Refer to ejabberd log files to diagnose the problem") end; start(_, _) -> {error, badarg}. %% Prepare the application for termination. %% This function is called when an application is about to be stopped, %% before shutting down the processes of the application. prep_stop(State) -> ejabberd_listener:stop_listeners(), ejabberd_sm:stop(), gen_mod:stop_modules(), State. %% All the processes were killed when this function is called stop(_State) -> ?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]), delete_pid_file(), %%ejabberd_debug:stop(), ok. %%% %%% Internal functions %%% %% If ejabberd is running on some Windows machine, get nameservers and add to Erlang maybe_add_nameservers() -> case os:type() of {win32, _} -> add_windows_nameservers(); _ -> ok end. add_windows_nameservers() -> IPTs = win32_dns:get_nameservers(), ?INFO_MSG("Adding machine's DNS IPs to Erlang system:~n~p", [IPTs]), lists:foreach(fun(IPT) -> inet_db:add_ns(IPT) end, IPTs). %%% %%% PID file %%% write_pid_file() -> case ejabberd:get_pid_file() of false -> ok; PidFilename -> write_pid_file(os:getpid(), PidFilename) end. write_pid_file(Pid, PidFilename) -> case file:open(PidFilename, [write]) of {ok, Fd} -> io:format(Fd, "~s~n", [Pid]), file:close(Fd); {error, Reason} -> ?ERROR_MSG("Cannot write PID file ~s~nReason: ~p", [PidFilename, Reason]), throw({cannot_write_pid_file, PidFilename, Reason}) end. delete_pid_file() -> case ejabberd:get_pid_file() of false -> ok; PidFilename -> file:delete(PidFilename) end. file_queue_init() -> QueueDir = case ejabberd_config:queue_dir() of undefined -> MnesiaDir = mnesia:system_info(directory), filename:join(MnesiaDir, "queue"); Path -> Path end, p1_queue:start(QueueDir). start_apps() -> crypto:start(), ejabberd:start_app(sasl), ejabberd:start_app(ssl), ejabberd:start_app(p1_utils), ejabberd:start_app(fast_yaml), ejabberd:start_app(fast_tls), ejabberd:start_app(xmpp), ejabberd:start_app(cache_tab), start_eimp(). setup_if_elixir_conf_used() -> case ejabberd_config:is_using_elixir_config() of true -> 'Elixir.Ejabberd.Config.Store':start_link(); false -> ok end. register_elixir_config_hooks() -> case ejabberd_config:is_using_elixir_config() of true -> 'Elixir.Ejabberd.Config':start_hooks(); false -> ok end. start_elixir_application() -> case ejabberd_config:is_elixir_enabled() of true -> case application:ensure_started(elixir) of ok -> ok; {error, _Msg} -> ?ERROR_MSG("Elixir application not started.", []) end; _ -> ok end. -ifdef(GRAPHICS). start_eimp() -> ejabberd:start_app(eimp). -else. start_eimp() -> ok. -endif. ejabberd-18.01/src/ejabberd_cluster_mnesia.erl0000644000232200023220000001126013225664356021771 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_cluster_mnesia.erl %%% Author : Christophe Romain %%% Purpose : Ejabberd clustering management via Mnesia %%% Created : 7 Oct 2015 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_cluster_mnesia). -behaviour(ejabberd_cluster). %% API -export([init/0, get_nodes/0, join/1, leave/1, get_known_nodes/0, node_id/0, get_node_by_id/1, send/2, wait_for_sync/1, subscribe/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -spec init() -> ok. init() -> ok. -spec get_nodes() -> [node()]. get_nodes() -> mnesia:system_info(running_db_nodes). -spec get_known_nodes() -> [node()]. get_known_nodes() -> lists:usort(mnesia:system_info(db_nodes) ++ mnesia:system_info(extra_db_nodes)). -spec join(node()) -> ok | {error, any()}. join(Node) -> case {node(), net_adm:ping(Node)} of {Node, _} -> {error, {not_master, Node}}; {_, pong} -> application:stop(ejabberd), application:stop(mnesia), mnesia:delete_schema([node()]), application:start(mnesia), case mnesia:change_config(extra_db_nodes, [Node]) of {ok, _} -> replicate_database(Node), wait_for_sync(infinity), application:stop(mnesia), application:start(ejabberd); {error, Reason} -> {error, Reason} end; _ -> {error, {no_ping, Node}} end. -spec leave(node()) -> ok | {error, any()}. leave(Node) -> case {node(), net_adm:ping(Node)} of {Node, _} -> Cluster = get_nodes()--[Node], leave(Cluster, Node); {_, pong} -> rpc:call(Node, ?MODULE, leave, [Node], 10000); {_, pang} -> case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> ok; {aborted, Reason} -> {error, Reason} end end. leave([], Node) -> {error, {no_cluster, Node}}; leave([Master|_], Node) -> application:stop(ejabberd), application:stop(mnesia), spawn(fun() -> rpc:call(Master, mnesia, del_table_copy, [schema, Node]), mnesia:delete_schema([node()]), erlang:halt(0) end), ok. -spec node_id() -> binary(). node_id() -> integer_to_binary(erlang:phash2(node())). -spec get_node_by_id(binary()) -> node(). get_node_by_id(Hash) -> try binary_to_integer(Hash) of I -> match_node_id(I) catch _:_ -> node() end. -spec send({atom(), node()}, term()) -> boolean(). send(Dst, Msg) -> case erlang:send(Dst, Msg, [nosuspend, noconnect]) of ok -> true; _ -> false end. -spec wait_for_sync(timeout()) -> ok. wait_for_sync(Timeout) -> ?INFO_MSG("Waiting for Mnesia synchronization to complete", []), mnesia:wait_for_tables(mnesia:system_info(local_tables), Timeout), ok. -spec subscribe(_) -> ok. subscribe(_) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== replicate_database(Node) -> mnesia:change_table_copy_type(schema, node(), disc_copies), lists:foreach( fun(Table) -> Type = ejabberd_cluster:call(Node, mnesia, table_info, [Table, storage_type]), mnesia:add_table_copy(Table, node(), Type) end, mnesia:system_info(tables)--[schema]). -spec match_node_id(integer()) -> node(). match_node_id(I) -> match_node_id(I, get_nodes()). -spec match_node_id(integer(), [node()]) -> node(). match_node_id(I, [Node|Nodes]) -> case erlang:phash2(Node) of I -> Node; _ -> match_node_id(I, Nodes) end; match_node_id(_I, []) -> node(). ejabberd-18.01/src/mod_mam_mnesia.erl0000644000232200023220000001705613225664356020114 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_mam_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_mam_mnesia). -behaviour(mod_mam). %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6]). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -include("mod_mam.hrl"). -define(BIN_GREATER_THAN(A, B), ((A > B andalso byte_size(A) == byte_size(B)) orelse byte_size(A) > byte_size(B))). -define(BIN_LESS_THAN(A, B), ((A < B andalso byte_size(A) == byte_size(B)) orelse byte_size(A) < byte_size(B))). -define(TABLE_SIZE_LIMIT, 2000000000). % A bit less than 2 GiB. %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, archive_msg, [{disc_only_copies, [node()]}, {type, bag}, {attributes, record_info(fields, archive_msg)}]), ejabberd_mnesia:create(?MODULE, archive_prefs, [{disc_only_copies, [node()]}, {attributes, record_info(fields, archive_prefs)}]). remove_user(LUser, LServer) -> US = {LUser, LServer}, F = fun () -> mnesia:delete({archive_msg, US}), mnesia:delete({archive_prefs, US}) end, mnesia:transaction(F). remove_room(_LServer, LName, LHost) -> remove_user(LName, LHost). delete_old_messages(global, TimeStamp, Type) -> mnesia:change_table_copy_type(archive_msg, node(), disc_copies), Result = delete_old_user_messages(mnesia:dirty_first(archive_msg), TimeStamp, Type), mnesia:change_table_copy_type(archive_msg, node(), disc_only_copies), Result. delete_old_user_messages('$end_of_table', _TimeStamp, _Type) -> ok; delete_old_user_messages(User, TimeStamp, Type) -> F = fun() -> Msgs = mnesia:read(archive_msg, User), Keep = lists:filter( fun(#archive_msg{timestamp = MsgTS, type = MsgType}) -> MsgTS >= TimeStamp orelse (Type /= all andalso Type /= MsgType) end, Msgs), if length(Keep) < length(Msgs) -> mnesia:delete({archive_msg, User}), lists:foreach(fun(Msg) -> mnesia:write(Msg) end, Keep); true -> ok end end, NextRecord = mnesia:dirty_next(archive_msg, User), case mnesia:transaction(F) of {atomic, ok} -> delete_old_user_messages(NextRecord, TimeStamp, Type); {aborted, Err} -> ?ERROR_MSG("Cannot delete old MAM messages: ~s", [Err]), Err end. extended_fields() -> []. store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS) -> case {mnesia:table_info(archive_msg, disc_only_copies), mnesia:table_info(archive_msg, memory)} of {[_|_], TableSize} when TableSize > ?TABLE_SIZE_LIMIT -> ?ERROR_MSG("MAM archives too large, won't store message for ~s@~s", [LUser, LServer]), {error, overflow}; _ -> LPeer = {PUser, PServer, _} = jid:tolower(Peer), F = fun() -> mnesia:write( #archive_msg{us = {LUser, LServer}, id = integer_to_binary(TS), timestamp = misc:usec_to_now(TS), peer = LPeer, bare_peer = {PUser, PServer, <<>>}, type = Type, nick = Nick, packet = Pkt}) end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, Err} -> ?ERROR_MSG("Cannot add message to MAM archive of ~s@~s: ~s", [LUser, LServer, Err]), Err end end. write_prefs(_LUser, _LServer, Prefs, _ServerHost) -> mnesia:dirty_write(Prefs). get_prefs(LUser, LServer) -> case mnesia:dirty_read(archive_prefs, {LUser, LServer}) of [Prefs] -> {ok, Prefs}; _ -> error end. select(_LServer, JidRequestor, #jid{luser = LUser, lserver = LServer} = JidArchive, Query, RSM, MsgType) -> Start = proplists:get_value(start, Query), End = proplists:get_value('end', Query), With = proplists:get_value(with, Query), LWith = if With /= undefined -> jid:tolower(With); true -> undefined end, MS = make_matchspec(LUser, LServer, Start, End, LWith), Msgs = mnesia:dirty_select(archive_msg, MS), SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs), {FilteredMsgs, IsComplete} = filter_by_rsm(SortedMsgs, RSM), Count = length(Msgs), Result = {lists:flatmap( fun(Msg) -> case mod_mam:msg_to_el( Msg, MsgType, JidRequestor, JidArchive) of {ok, El} -> [{Msg#archive_msg.id, binary_to_integer(Msg#archive_msg.id), El}]; {error, _} -> [] end end, FilteredMsgs), IsComplete, Count}, erlang:garbage_collect(), Result. %%%=================================================================== %%% Internal functions %%%=================================================================== make_matchspec(LUser, LServer, Start, undefined, With) -> %% List is always greater than a tuple make_matchspec(LUser, LServer, Start, [], With); make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) -> ets:fun2ms( fun(#archive_msg{timestamp = TS, us = US, bare_peer = BPeer} = Msg) when Start =< TS, End >= TS, US == {LUser, LServer}, BPeer == With -> Msg end); make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) -> ets:fun2ms( fun(#archive_msg{timestamp = TS, us = US, peer = Peer} = Msg) when Start =< TS, End >= TS, US == {LUser, LServer}, Peer == With -> Msg end); make_matchspec(LUser, LServer, Start, End, undefined) -> ets:fun2ms( fun(#archive_msg{timestamp = TS, us = US, peer = Peer} = Msg) when Start =< TS, End >= TS, US == {LUser, LServer} -> Msg end). filter_by_rsm(Msgs, undefined) -> {Msgs, true}; filter_by_rsm(_Msgs, #rsm_set{max = Max}) when Max < 0 -> {[], true}; filter_by_rsm(Msgs, #rsm_set{max = Max, before = Before, 'after' = After}) -> NewMsgs = if is_binary(After), After /= <<"">> -> lists:filter( fun(#archive_msg{id = I}) -> ?BIN_GREATER_THAN(I, After) end, Msgs); is_binary(Before), Before /= <<"">> -> lists:foldl( fun(#archive_msg{id = I} = Msg, Acc) when ?BIN_LESS_THAN(I, Before) -> [Msg|Acc]; (_, Acc) -> Acc end, [], Msgs); is_binary(Before), Before == <<"">> -> lists:reverse(Msgs); true -> Msgs end, filter_by_max(NewMsgs, Max). filter_by_max(Msgs, undefined) -> {Msgs, true}; filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 -> {lists:sublist(Msgs, Len), length(Msgs) =< Len}; filter_by_max(_Msgs, _Junk) -> {[], true}. ejabberd-18.01/src/mod_offline_sql.erl0000644000232200023220000001734013225664356020303 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_offline_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_offline_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_offline). -export([init/2, store_message/1, pop_messages/2, remove_expired_messages/1, remove_old_messages/2, remove_user/2, read_message_headers/2, read_message/3, remove_message/3, read_all_messages/2, remove_all_messages/2, count_messages/2, import/1, export/1]). -include("xmpp.hrl"). -include("mod_offline.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. store_message(#offline_msg{us = {LUser, LServer}} = M) -> From = M#offline_msg.from, To = M#offline_msg.to, Packet = xmpp:set_from_to(M#offline_msg.packet, From, To), NewPacket = xmpp_util:add_delay_info( Packet, jid:make(LServer), M#offline_msg.timestamp, <<"Offline Storage">>), XML = fxml:element_to_binary( xmpp:encode(NewPacket)), case ejabberd_sql:sql_query( LServer, ?SQL_INSERT( "spool", ["username=%(LUser)s", "server_host=%(LServer)s", "xml=%(XML)s"])) of {updated, _} -> ok; _ -> {error, db_failure} end. pop_messages(LUser, LServer) -> case get_and_del_spool_msg_t(LServer, LUser) of {atomic, {selected, Rs}} -> {ok, lists:flatmap( fun({_, XML}) -> case xml_to_offline_msg(XML) of {ok, Msg} -> [Msg]; _Err -> [] end end, Rs)}; Err -> {error, Err} end. remove_expired_messages(_LServer) -> %% TODO {atomic, ok}. remove_old_messages(Days, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("DELETE FROM spool" " WHERE created_at < NOW() - INTERVAL %(Days)d DAY")) of {updated, N} -> ?INFO_MSG("~p message(s) deleted from offline spool", [N]); _Error -> ?ERROR_MSG("Cannot delete message in offline spool: ~p", [_Error]) end, {atomic, ok}. remove_user(LUser, LServer) -> ejabberd_sql:sql_query( LServer, ?SQL("delete from spool where username=%(LUser)s and %(LServer)H")). read_message_headers(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(xml)s, @(seq)d from spool" " where username=%(LUser)s and %(LServer)H order by seq")) of {selected, Rows} -> lists:flatmap( fun({XML, Seq}) -> case xml_to_offline_msg(XML) of {ok, #offline_msg{from = From, to = To, timestamp = TS, packet = El}} -> [{Seq, From, To, TS, El}]; _ -> [] end end, Rows); _Err -> [] end. read_message(LUser, LServer, Seq) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(xml)s from spool where username=%(LUser)s" " and %(LServer)H" " and seq=%(Seq)d")) of {selected, [{RawXML}|_]} -> case xml_to_offline_msg(RawXML) of {ok, Msg} -> {ok, Msg}; _ -> error end; _ -> error end. remove_message(LUser, LServer, Seq) -> ejabberd_sql:sql_query( LServer, ?SQL("delete from spool where username=%(LUser)s and %(LServer)H" " and seq=%(Seq)d")), ok. read_all_messages(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(xml)s from spool where " "username=%(LUser)s and %(LServer)H order by seq")) of {selected, Rs} -> lists:flatmap( fun({XML}) -> case xml_to_offline_msg(XML) of {ok, Msg} -> [Msg]; _ -> [] end end, Rs); _ -> [] end. remove_all_messages(LUser, LServer) -> remove_user(LUser, LServer), {atomic, ok}. count_messages(LUser, LServer) -> case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(count(*))d from spool " "where username=%(LUser)s and %(LServer)H")) of {selected, [{Res}]} -> Res; _ -> 0 end. export(_Server) -> [{offline_msg, fun(Host, #offline_msg{us = {LUser, LServer}}) when LServer == Host -> [?SQL("delete from spool where username=%(LUser)s" " and %(LServer)H;")]; (_Host, _R) -> [] end}, {offline_msg, fun(Host, #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, from = From, to = To, packet = El}) when LServer == Host -> try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of Packet -> Packet1 = xmpp:set_from_to(Packet, From, To), Packet2 = xmpp_util:add_delay_info( Packet1, jid:make(LServer), TimeStamp, <<"Offline Storage">>), XML = fxml:element_to_binary(xmpp:encode(Packet2)), [?SQL_INSERT( "spool", ["username=%(LUser)s", "server_host=%(LServer)s", "xml=%(XML)s"])] catch _:{xmpp_codec, Why} -> ?ERROR_MSG("failed to decode packet ~p of user ~s@~s: ~s", [El, LUser, LServer, xmpp:format_error(Why)]), [] end; (_Host, _R) -> [] end}]. import(_) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== xml_to_offline_msg(XML) -> case fxml_stream:parse_element(XML) of #xmlel{} = El -> el_to_offline_msg(El); Err -> ?ERROR_MSG("got ~p when parsing XML packet ~s", [Err, XML]), Err end. el_to_offline_msg(El) -> To_s = fxml:get_tag_attr_s(<<"to">>, El), From_s = fxml:get_tag_attr_s(<<"from">>, El), try To = jid:decode(To_s), From = jid:decode(From_s), {ok, #offline_msg{us = {To#jid.luser, To#jid.lserver}, from = From, to = To, packet = El}} catch _:{bad_jid, To_s} -> ?ERROR_MSG("failed to get 'to' JID from offline XML ~p", [El]), {error, bad_jid_to}; _:{bad_jid, From_s} -> ?ERROR_MSG("failed to get 'from' JID from offline XML ~p", [El]), {error, bad_jid_from} end. get_and_del_spool_msg_t(LServer, LUser) -> F = fun () -> Result = ejabberd_sql:sql_query_t( ?SQL("select @(username)s, @(xml)s from spool where " "username=%(LUser)s and %(LServer)H order by seq;")), ejabberd_sql:sql_query_t( ?SQL("delete from spool where" " username=%(LUser)s and %(LServer)H;")), Result end, ejabberd_sql:sql_transaction(LServer, F). ejabberd-18.01/src/mod_legacy_auth.erl0000644000232200023220000001332013225664356020261 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 11 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_legacy_auth). -behaviour(gen_mod). -protocol({xep, 78, '2.5'}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% hooks -export([c2s_unauthenticated_packet/2, c2s_stream_features/2]). -include("xmpp.hrl"). -type c2s_state() :: ejabberd_c2s:state(). %%%=================================================================== %%% API %%%=================================================================== start(Host, _Opts) -> ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, c2s_unauthenticated_packet, 50), ejabberd_hooks:add(c2s_pre_auth_features, Host, ?MODULE, c2s_stream_features, 50). stop(Host) -> ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, c2s_unauthenticated_packet, 50), ejabberd_hooks:delete(c2s_pre_auth_features, Host, ?MODULE, c2s_stream_features, 50). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> []. mod_opt_type(_) -> []. -spec c2s_unauthenticated_packet(c2s_state(), iq()) -> c2s_state() | {stop, c2s_state()}. c2s_unauthenticated_packet(State, #iq{type = T, sub_els = [_]} = IQ) when T == get; T == set -> try xmpp:try_subtag(IQ, #legacy_auth{}) of #legacy_auth{} = Auth -> {stop, authenticate(State, xmpp:set_els(IQ, [Auth]))}; false -> State catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Lang = maps:get(lang, State), Err = xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)), {stop, ejabberd_c2s:send(State, Err)} end; c2s_unauthenticated_packet(State, _) -> State. -spec c2s_stream_features([xmpp_element()], binary()) -> [xmpp_element()]. c2s_stream_features(Acc, LServer) -> case gen_mod:is_loaded(LServer, ?MODULE) of true -> [#legacy_auth_feature{}|Acc]; false -> Acc end. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec authenticate(c2s_state(), iq()) -> c2s_state(). authenticate(#{server := Server} = State, #iq{type = get, sub_els = [#legacy_auth{}]} = IQ) -> LServer = jid:nameprep(Server), Auth = #legacy_auth{username = <<>>, password = <<>>, resource = <<>>}, Res = case ejabberd_auth:plain_password_required(LServer) of false -> xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>}); true -> xmpp:make_iq_result(IQ, Auth) end, ejabberd_c2s:send(State, Res); authenticate(State, #iq{type = set, lang = Lang, sub_els = [#legacy_auth{username = U, resource = R}]} = IQ) when U == undefined; R == undefined; U == <<"">>; R == <<"">> -> Txt = <<"Both the username and the resource are required">>, Err = xmpp:make_error(IQ, xmpp:err_not_acceptable(Txt, Lang)), ejabberd_c2s:send(State, Err); authenticate(#{stream_id := StreamID, server := Server, access := Access, ip := IP} = State, #iq{type = set, lang = Lang, sub_els = [#legacy_auth{username = U, password = P0, digest = D0, resource = R}]} = IQ) -> P = if is_binary(P0) -> P0; true -> <<>> end, D = if is_binary(D0) -> D0; true -> <<>> end, DGen = fun (PW) -> str:sha(<>) end, JID = jid:make(U, Server, R), case JID /= error andalso acl:access_matches(Access, #{usr => jid:split(JID), ip => IP}, JID#jid.lserver) == allow of true -> case ejabberd_auth:check_password_with_authmodule( U, U, JID#jid.lserver, P, D, DGen) of {true, AuthModule} -> State1 = ejabberd_c2s:handle_auth_success( U, <<"legacy">>, AuthModule, State), State2 = State1#{user := U}, open_session(State2, IQ, R); _ -> Err = xmpp:make_error(IQ, xmpp:err_not_authorized()), process_auth_failure(State, U, Err, 'not-authorized') end; false when JID == error -> Err = xmpp:make_error(IQ, xmpp:err_jid_malformed()), process_auth_failure(State, U, Err, 'jid-malformed'); false -> Txt = <<"Access denied by service policy">>, Err = xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)), process_auth_failure(State, U, Err, 'forbidden') end. -spec open_session(c2s_state(), iq(), binary()) -> c2s_state(). open_session(State, IQ, R) -> case ejabberd_c2s:bind(R, State) of {ok, State1} -> Res = xmpp:make_iq_result(IQ), ejabberd_c2s:send(State1, Res); {error, Err, State1} -> Res = xmpp:make_error(IQ, Err), ejabberd_c2s:send(State1, Res) end. -spec process_auth_failure(c2s_state(), binary(), iq(), atom()) -> c2s_state(). process_auth_failure(State, User, StanzaErr, Reason) -> State1 = ejabberd_c2s:send(State, StanzaErr), ejabberd_c2s:handle_auth_failure(User, <<"legacy">>, Reason, State1). ejabberd-18.01/src/ejabberd_router_mnesia.erl0000644000232200023220000001437313225664356021640 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 11 Jan 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_router_mnesia). -behaviour(ejabberd_router). -behaviour(gen_server). %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, get_all_routes/0, use_cache/0]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("ejabberd.hrl"). -include("ejabberd_router.hrl"). -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== -spec init() -> ok | {error, any()}. init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). use_cache() -> false. register_route(Domain, ServerHost, LocalHint, undefined, Pid) -> F = fun () -> mnesia:write(#route{domain = Domain, pid = Pid, server_host = ServerHost, local_hint = LocalHint}) end, transaction(F); register_route(Domain, ServerHost, _LocalHint, N, Pid) -> F = fun () -> case mnesia:wread({route, Domain}) of [] -> mnesia:write(#route{domain = Domain, server_host = ServerHost, pid = Pid, local_hint = 1}), lists:foreach( fun (I) -> mnesia:write( #route{domain = Domain, pid = undefined, server_host = ServerHost, local_hint = I}) end, lists:seq(2, N)); Rs -> lists:any( fun (#route{pid = undefined, local_hint = I} = R) -> mnesia:write( #route{domain = Domain, pid = Pid, server_host = ServerHost, local_hint = I}), mnesia:delete_object(R), true; (_) -> false end, Rs) end end, transaction(F). unregister_route(Domain, undefined, Pid) -> F = fun () -> case mnesia:match_object( #route{domain = Domain, pid = Pid, _ = '_'}) of [R] -> mnesia:delete_object(R); _ -> ok end end, transaction(F); unregister_route(Domain, _, Pid) -> F = fun () -> case mnesia:match_object( #route{domain = Domain, pid = Pid, _ = '_'}) of [R] -> I = R#route.local_hint, ServerHost = R#route.server_host, mnesia:write(#route{domain = Domain, server_host = ServerHost, pid = undefined, local_hint = I}), mnesia:delete_object(R); _ -> ok end end, transaction(F). find_routes(Domain) -> {ok, mnesia:dirty_read(route, Domain)}. get_all_routes() -> {ok, mnesia:dirty_select( route, ets:fun2ms( fun(#route{domain = Domain, server_host = ServerHost}) when Domain /= ServerHost -> Domain end))}. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> update_tables(), ejabberd_mnesia:create(?MODULE, route, [{ram_copies, [node()]}, {type, bag}, {attributes, record_info(fields, route)}]), mnesia:subscribe({table, route, simple}), lists:foreach( fun (Pid) -> erlang:monitor(process, Pid) end, mnesia:dirty_select(route, [{#route{pid = '$1', _ = '_'}, [], ['$1']}])), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}}, State) -> erlang:monitor(process, Pid), {noreply, State}; handle_info({mnesia_table_event, _}, State) -> {noreply, State}; handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> F = fun () -> Es = mnesia:select(route, [{#route{pid = Pid, _ = '_'}, [], ['$_']}]), lists:foreach( fun(E) -> if is_integer(E#route.local_hint) -> LDomain = E#route.domain, I = E#route.local_hint, ServerHost = E#route.server_host, mnesia:write(#route{domain = LDomain, server_host = ServerHost, pid = undefined, local_hint = I}), mnesia:delete_object(E); true -> mnesia:delete_object(E) end end, Es) end, transaction(F), {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== transaction(F) -> case mnesia:transaction(F) of {atomic, _} -> ok; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. -spec update_tables() -> ok. update_tables() -> try mnesia:transform_table(route, ignore, record_info(fields, route)) catch exit:{aborted, {no_exists, _}} -> ok end, case lists:member(local_route, mnesia:system_info(tables)) of true -> mnesia:delete_table(local_route); false -> ok end. ejabberd-18.01/src/acme_challenge.erl0000644000232200023220000001077113225664356020053 0ustar debalancedebalance-module(acme_challenge). -export ([key_authorization/2, solve_challenge/3, process/2, register_hooks/1, unregister_hooks/1, acme_handler/3 ]). %% Challenge Types %% ================ %% 1. http-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.2 %% 2. dns-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.3 %% 3. tls-sni-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.4 %% 4. (?) oob-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.5 -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_acme.hrl"). %% This is the default endpoint for the http challenge %% This hooks is called from ejabberd_http acme_handler(Handlers, _Host, Request) -> case Request#request.path of [<<".well-known">>|_] -> [{[<<".well-known">>],acme_challenge}|Handlers]; _ -> Handlers end. %% TODO: Maybe validate request here?? process(LocalPath, _Request) -> Result = ets_get_key_authorization(LocalPath), {200, [{<<"Content-Type">>, <<"text/plain">>}], Result}. register_hooks(_Domain) -> ?INFO_MSG("Registering hook for ACME HTTP headers", []), ejabberd_hooks:add(http_request_handlers, ?MODULE, acme_handler, 50). unregister_hooks(_Domain) -> ?INFO_MSG("Unregistering hook for ACME HTTP headers", []), ejabberd_hooks:delete(http_request_handlers, ?MODULE, acme_handler, 50). -spec key_authorization(bitstring(), jose_jwk:key()) -> bitstring(). key_authorization(Token, Key) -> Thumbprint = jose_jwk:thumbprint(Key), KeyAuthorization = erlang:iolist_to_binary([Token, <<".">>, Thumbprint]), KeyAuthorization. -spec parse_challenge({proplist()}) -> {ok, acme_challenge()} | {error, _}. parse_challenge(Challenge0) -> try {Challenge} = Challenge0, {<<"type">>,Type} = proplists:lookup(<<"type">>, Challenge), {<<"status">>,Status} = proplists:lookup(<<"status">>, Challenge), {<<"uri">>,Uri} = proplists:lookup(<<"uri">>, Challenge), {<<"token">>,Token} = proplists:lookup(<<"token">>, Challenge), Res = #challenge{ type = Type, status = list_to_atom(bitstring_to_list(Status)), uri = bitstring_to_list(Uri), token = Token }, {ok, Res} catch _:Error -> {error, Error} end. -spec solve_challenge(bitstring(), [{proplist()}], _) -> {ok, url(), bitstring()} | {error, _}. solve_challenge(ChallengeType, Challenges, Options) -> ParsedChallenges = [parse_challenge(Chall) || Chall <- Challenges], case lists:any(fun is_error/1, ParsedChallenges) of true -> ?ERROR_MSG("Error parsing challenges: ~p~n", [Challenges]), {error, parse_challenge}; false -> case [C || {ok, C} <- ParsedChallenges, is_challenge_type(ChallengeType, C)] of [Challenge] -> solve_challenge1(Challenge, Options); _ -> ?ERROR_MSG("Challenge ~p not found in challenges: ~p~n", [ChallengeType, Challenges]), {error, not_found} end end. -spec solve_challenge1(acme_challenge(), {jose_jwk:key(), string()}) -> {ok, url(), bitstring()} | {error, _}. solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, Key) -> KeyAuthz = key_authorization(Tkn, Key), %% save_key_authorization(Chal, Tkn, KeyAuthz, HttpDir); ets_put_key_authorization(Tkn, KeyAuthz), {ok, Chal#challenge.uri, KeyAuthz}; solve_challenge1(Challenge, _Key) -> ?ERROR_MSG("Unkown Challenge Type: ~p", [Challenge]), {error, unknown_challenge}. -spec ets_put_key_authorization(bitstring(), bitstring()) -> ok. ets_put_key_authorization(Tkn, KeyAuthz) -> Tab = ets_get_acme_table(), Key = [<<"acme-challenge">>, Tkn], ets:insert(Tab, {Key, KeyAuthz}), ok. -spec ets_get_key_authorization([bitstring()]) -> bitstring(). ets_get_key_authorization(Key) -> Tab = ets_get_acme_table(), case ets:lookup(Tab, Key) of [{Key, KeyAuthz}] -> ets:delete(Tab, Key), KeyAuthz; _ -> ?ERROR_MSG("Unable to serve key authorization in: ~p", [Key]), <<"">> end. -spec ets_get_acme_table() -> atom(). ets_get_acme_table() -> case ets:info(acme) of undefined -> ets:new(acme, [named_table, public]); _ -> acme end. %% Useful functions is_challenge_type(DesiredType, #challenge{type = Type}) when DesiredType =:= Type -> true; is_challenge_type(_DesiredType, #challenge{type = _Type}) -> false. -spec is_error({'error', _}) -> 'true'; ({'ok', _}) -> 'false'. is_error({error, _}) -> true; is_error(_) -> false. ejabberd-18.01/src/node_dag.erl0000644000232200023220000001304213225664356016676 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_dag.erl %%% Author : Brian Cully %%% Purpose : experimental support of XEP-248 %%% Created : 15 Jun 2009 by Brian Cully %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_dag). -behaviour(gen_pubsub_node). -author('bjc@kublai.com'). -include("pubsub.hrl"). -include("xmpp.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). init(Host, ServerHost, Opts) -> node_hometree:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_hometree:terminate(Host, ServerHost). options() -> [{node_type, leaf} | node_hometree:options()]. features() -> [<<"multi-collection">> | node_hometree:features()]. create_node_permission(_Host, _ServerHost, _Node, _ParentNode, _Owner, _Access) -> {result, true}. create_node(Nidx, Owner) -> node_hometree:create_node(Nidx, Owner). delete_node(Removed) -> node_hometree:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> case nodetree_dag:get_node(Nidx) of #pubsub_node{options = Options} -> case find_opt(node_type, Options) of collection -> Txt = <<"Publishing items to collection node is not allowed">>, {error, mod_pubsub:extended_error( xmpp:err_not_allowed(Txt, ?MYLANG), mod_pubsub:err_unsupported('publish'))}; _ -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) end; Err -> Err end. find_opt(_, []) -> false; find_opt(Option, [{Option, Value} | _]) -> Value; find_opt(Option, [_ | T]) -> find_opt(Option, T). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_hometree:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_hometree:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_hometree:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_hometree:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_hometree:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_hometree:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_hometree:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_hometree:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_hometree:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_hometree:get_states(Nidx). get_state(Nidx, JID) -> node_hometree:get_state(Nidx, JID). set_state(State) -> node_hometree:set_state(State). get_items(Nidx, From, RSM) -> node_hometree:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_hometree:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_hometree:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_hometree:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_hometree:set_item(Item). get_item_name(Host, Node, Id) -> node_hometree:get_item_name(Host, Node, Id). node_to_path(Node) -> node_hometree:node_to_path(Node). path_to_node(Path) -> node_hometree:path_to_node(Path). ejabberd-18.01/src/ejabberd_auth_pam.erl0000644000232200023220000000557413225664356020565 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_auth_pam.erl %%% Author : Evgeniy Khramtsov %%% Purpose : PAM authentication %%% Created : 5 Jul 2007 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_auth_pam). -behaviour(ejabberd_config). -author('xram@jabber.ru'). -behaviour(ejabberd_auth). -export([start/1, stop/1, check_password/4, user_exists/2, store_type/1, plain_password_required/1, opt_type/1]). start(_Host) -> ejabberd:start_app(epam). stop(_Host) -> ok. check_password(User, AuthzId, Host, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> false; true -> Service = get_pam_service(Host), UserInfo = case get_pam_userinfotype(Host) of username -> User; jid -> <> end, case catch epam:authenticate(Service, UserInfo, Password) of true -> true; _ -> false end end. user_exists(User, Host) -> Service = get_pam_service(Host), UserInfo = case get_pam_userinfotype(Host) of username -> User; jid -> <> end, case catch epam:acct_mgmt(Service, UserInfo) of true -> true; false -> false; _Err -> {error, db_failure} end. plain_password_required(_) -> true. store_type(_) -> external. %%==================================================================== %% Internal functions %%==================================================================== get_pam_service(Host) -> ejabberd_config:get_option({pam_service, Host}, <<"ejabberd">>). get_pam_userinfotype(Host) -> ejabberd_config:get_option({pam_userinfotype, Host}, username). -spec opt_type(pam_service) -> fun((binary()) -> binary()); (pam_userinfotype) -> fun((username | jid) -> username | jid); (atom()) -> [atom()]. opt_type(pam_service) -> fun iolist_to_binary/1; opt_type(pam_userinfotype) -> fun (username) -> username; (jid) -> jid end; opt_type(_) -> [pam_service, pam_userinfotype]. ejabberd-18.01/src/ejabberd_options.erl0000644000232200023220000000324513225664356020453 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% @doc %%% This is a stub module which will be replaced during %%% configuration load via p1_options:compile/1 %%% The only purpose of this file is to shut up xref/dialyzer %%% @end %%% Created : 16 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_options). %% API -export([is_known/1, get_scope/1]). %%%=================================================================== %%% API %%%=================================================================== is_known(_) -> false. get_scope(_) -> []. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/ejabberd_sm_sql.erl0000644000232200023220000001214613225664356020256 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_sm_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 9 Mar 2015 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sm_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(ejabberd_sm). %% API -export([init/0, set_session/1, delete_session/1, get_sessions/0, get_sessions/1, get_sessions/2]). -include("ejabberd.hrl"). -include("ejabberd_sm.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== -spec init() -> ok | {error, any()}. init() -> Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL SM table...", []), lists:foldl( fun(Host, ok) -> case ejabberd_sql:sql_query( Host, ?SQL("delete from sm where node=%(Node)s")) of {updated, _} -> ok; Err -> ?ERROR_MSG("failed to clean 'sm' table: ~p", [Err]), Err end; (_, Err) -> Err end, ok, ejabberd_sm:get_vh_by_backend(?MODULE)). set_session(#session{sid = {Now, Pid}, usr = {U, LServer, R}, priority = Priority, info = Info}) -> InfoS = misc:term_to_expr(Info), PrioS = enc_priority(Priority), TS = now_to_timestamp(Now), PidS = misc:encode_pid(Pid), Node = erlang:atom_to_binary(node(Pid), latin1), case ?SQL_UPSERT(LServer, "sm", ["!usec=%(TS)d", "!pid=%(PidS)s", "node=%(Node)s", "username=%(U)s", "server_host=%(LServer)s", "resource=%(R)s", "priority=%(PrioS)s", "info=%(InfoS)s"]) of ok -> ok; _Err -> {error, db_failure} end. delete_session(#session{usr = {_, LServer, _}, sid = {Now, Pid}}) -> TS = now_to_timestamp(Now), PidS = list_to_binary(erlang:pid_to_list(Pid)), case ejabberd_sql:sql_query( LServer, ?SQL("delete from sm where usec=%(TS)d and pid=%(PidS)s")) of {updated, _} -> ok; _Err -> {error, db_failure} end. get_sessions() -> lists:flatmap( fun(LServer) -> get_sessions(LServer) end, ejabberd_sm:get_vh_by_backend(?MODULE)). get_sessions(LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(usec)d, @(pid)s, @(node)s, @(username)s," " @(resource)s, @(priority)s, @(info)s from sm" " where %(LServer)H")) of {selected, Rows} -> lists:flatmap( fun(Row) -> try [row_to_session(LServer, Row)] catch _:{bad_node, _} -> [] end end, Rows); _Err -> [] end. get_sessions(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(usec)d, @(pid)s, @(node)s, @(username)s," " @(resource)s, @(priority)s, @(info)s from sm" " where username=%(LUser)s and %(LServer)H")) of {selected, Rows} -> {ok, lists:flatmap( fun(Row) -> try [row_to_session(LServer, Row)] catch _:{bad_node, _} -> [] end end, Rows)}; _Err -> {error, db_failure} end. %%%=================================================================== %%% Internal functions %%%=================================================================== now_to_timestamp({MSec, Sec, USec}) -> (MSec * 1000000 + Sec) * 1000000 + USec. timestamp_to_now(I) -> Head = I div 1000000, USec = I rem 1000000, MSec = Head div 1000000, Sec = Head rem 1000000, {MSec, Sec, USec}. dec_priority(Prio) -> case catch binary_to_integer(Prio) of {'EXIT', _} -> undefined; Int -> Int end. enc_priority(undefined) -> <<"">>; enc_priority(Int) when is_integer(Int) -> integer_to_binary(Int). row_to_session(LServer, {USec, PidS, NodeS, User, Resource, PrioS, InfoS}) -> Now = timestamp_to_now(USec), Pid = misc:decode_pid(PidS, NodeS), Priority = dec_priority(PrioS), Info = ejabberd_sql:decode_term(InfoS), #session{sid = {Now, Pid}, us = {User, LServer}, usr = {User, LServer, Resource}, priority = Priority, info = Info}. ejabberd-18.01/src/ELDAPv3.erl0000644000232200023220000032054113225664356016241 0ustar debalancedebalance%% Generated by the Erlang ASN.1 BER_V2-compiler version, utilizing bit-syntax:2.0.1 %% Purpose: encoder and decoder to the types in mod ELDAPv3 -module('ELDAPv3'). -compile(nowarn_unused_vars). -include("ELDAPv3.hrl"). -asn1_info([{vsn,'2.0.1'}, {module,'ELDAPv3'}, {options,[{i,"src"},{outdir,"src"},noobj,{i,"."},{i,"asn1"}]}]). -export([encoding_rule/0,bit_string_format/0]). -export([ 'enc_LDAPMessage'/2, 'enc_MessageID'/2, 'enc_LDAPString'/2, 'enc_LDAPOID'/2, 'enc_LDAPDN'/2, 'enc_RelativeLDAPDN'/2, 'enc_AttributeType'/2, 'enc_AttributeDescription'/2, 'enc_AttributeDescriptionList'/2, 'enc_AttributeValue'/2, 'enc_AttributeValueAssertion'/2, 'enc_AssertionValue'/2, 'enc_Attribute'/2, 'enc_MatchingRuleId'/2, 'enc_LDAPResult'/2, 'enc_Referral'/2, 'enc_LDAPURL'/2, 'enc_Controls'/2, 'enc_Control'/2, 'enc_BindRequest'/2, 'enc_AuthenticationChoice'/2, 'enc_SaslCredentials'/2, 'enc_BindResponse'/2, 'enc_UnbindRequest'/2, 'enc_SearchRequest'/2, 'enc_Filter'/2, 'enc_SubstringFilter'/2, 'enc_MatchingRuleAssertion'/2, 'enc_SearchResultEntry'/2, 'enc_PartialAttributeList'/2, 'enc_SearchResultReference'/2, 'enc_SearchResultDone'/2, 'enc_ModifyRequest'/2, 'enc_AttributeTypeAndValues'/2, 'enc_ModifyResponse'/2, 'enc_AddRequest'/2, 'enc_AttributeList'/2, 'enc_AddResponse'/2, 'enc_DelRequest'/2, 'enc_DelResponse'/2, 'enc_ModifyDNRequest'/2, 'enc_ModifyDNResponse'/2, 'enc_CompareRequest'/2, 'enc_CompareResponse'/2, 'enc_AbandonRequest'/2, 'enc_ExtendedRequest'/2, 'enc_ExtendedResponse'/2, 'enc_PasswdModifyRequestValue'/2, 'enc_PasswdModifyResponseValue'/2 ]). -export([ 'dec_LDAPMessage'/2, 'dec_MessageID'/2, 'dec_LDAPString'/2, 'dec_LDAPOID'/2, 'dec_LDAPDN'/2, 'dec_RelativeLDAPDN'/2, 'dec_AttributeType'/2, 'dec_AttributeDescription'/2, 'dec_AttributeDescriptionList'/2, 'dec_AttributeValue'/2, 'dec_AttributeValueAssertion'/2, 'dec_AssertionValue'/2, 'dec_Attribute'/2, 'dec_MatchingRuleId'/2, 'dec_LDAPResult'/2, 'dec_Referral'/2, 'dec_LDAPURL'/2, 'dec_Controls'/2, 'dec_Control'/2, 'dec_BindRequest'/2, 'dec_AuthenticationChoice'/2, 'dec_SaslCredentials'/2, 'dec_BindResponse'/2, 'dec_UnbindRequest'/2, 'dec_SearchRequest'/2, 'dec_Filter'/2, 'dec_SubstringFilter'/2, 'dec_MatchingRuleAssertion'/2, 'dec_SearchResultEntry'/2, 'dec_PartialAttributeList'/2, 'dec_SearchResultReference'/2, 'dec_SearchResultDone'/2, 'dec_ModifyRequest'/2, 'dec_AttributeTypeAndValues'/2, 'dec_ModifyResponse'/2, 'dec_AddRequest'/2, 'dec_AttributeList'/2, 'dec_AddResponse'/2, 'dec_DelRequest'/2, 'dec_DelResponse'/2, 'dec_ModifyDNRequest'/2, 'dec_ModifyDNResponse'/2, 'dec_CompareRequest'/2, 'dec_CompareResponse'/2, 'dec_AbandonRequest'/2, 'dec_ExtendedRequest'/2, 'dec_ExtendedResponse'/2, 'dec_PasswdModifyRequestValue'/2, 'dec_PasswdModifyResponseValue'/2 ]). -export([ 'maxInt'/0, 'passwdModifyOID'/0 ]). -export([info/0]). -export([encode/2,decode/2]). encoding_rule() -> ber. bit_string_format() -> bitstring. encode(Type,Data) -> case catch encode_disp(Type,Data) of {'EXIT',{error,Reason}} -> {error,Reason}; {'EXIT',Reason} -> {error,{asn1,Reason}}; {Bytes,_Len} -> {ok,iolist_to_binary(Bytes)}; Bytes -> {ok,iolist_to_binary(Bytes)} end. decode(Type,Data) -> case catch decode_disp(Type,element(1, ber_decode_nif(Data))) of {'EXIT',{error,Reason}} -> {error,Reason}; {'EXIT',Reason} -> {error,{asn1,Reason}}; Result -> {ok,Result} end. encode_disp('LDAPMessage',Data) -> 'enc_LDAPMessage'(Data); encode_disp('MessageID',Data) -> 'enc_MessageID'(Data); encode_disp('LDAPString',Data) -> 'enc_LDAPString'(Data); encode_disp('LDAPOID',Data) -> 'enc_LDAPOID'(Data); encode_disp('LDAPDN',Data) -> 'enc_LDAPDN'(Data); encode_disp('RelativeLDAPDN',Data) -> 'enc_RelativeLDAPDN'(Data); encode_disp('AttributeType',Data) -> 'enc_AttributeType'(Data); encode_disp('AttributeDescription',Data) -> 'enc_AttributeDescription'(Data); encode_disp('AttributeDescriptionList',Data) -> 'enc_AttributeDescriptionList'(Data); encode_disp('AttributeValue',Data) -> 'enc_AttributeValue'(Data); encode_disp('AttributeValueAssertion',Data) -> 'enc_AttributeValueAssertion'(Data); encode_disp('AssertionValue',Data) -> 'enc_AssertionValue'(Data); encode_disp('Attribute',Data) -> 'enc_Attribute'(Data); encode_disp('MatchingRuleId',Data) -> 'enc_MatchingRuleId'(Data); encode_disp('LDAPResult',Data) -> 'enc_LDAPResult'(Data); encode_disp('Referral',Data) -> 'enc_Referral'(Data); encode_disp('LDAPURL',Data) -> 'enc_LDAPURL'(Data); encode_disp('Controls',Data) -> 'enc_Controls'(Data); encode_disp('Control',Data) -> 'enc_Control'(Data); encode_disp('BindRequest',Data) -> 'enc_BindRequest'(Data); encode_disp('AuthenticationChoice',Data) -> 'enc_AuthenticationChoice'(Data); encode_disp('SaslCredentials',Data) -> 'enc_SaslCredentials'(Data); encode_disp('BindResponse',Data) -> 'enc_BindResponse'(Data); encode_disp('UnbindRequest',Data) -> 'enc_UnbindRequest'(Data); encode_disp('SearchRequest',Data) -> 'enc_SearchRequest'(Data); encode_disp('Filter',Data) -> 'enc_Filter'(Data); encode_disp('SubstringFilter',Data) -> 'enc_SubstringFilter'(Data); encode_disp('MatchingRuleAssertion',Data) -> 'enc_MatchingRuleAssertion'(Data); encode_disp('SearchResultEntry',Data) -> 'enc_SearchResultEntry'(Data); encode_disp('PartialAttributeList',Data) -> 'enc_PartialAttributeList'(Data); encode_disp('SearchResultReference',Data) -> 'enc_SearchResultReference'(Data); encode_disp('SearchResultDone',Data) -> 'enc_SearchResultDone'(Data); encode_disp('ModifyRequest',Data) -> 'enc_ModifyRequest'(Data); encode_disp('AttributeTypeAndValues',Data) -> 'enc_AttributeTypeAndValues'(Data); encode_disp('ModifyResponse',Data) -> 'enc_ModifyResponse'(Data); encode_disp('AddRequest',Data) -> 'enc_AddRequest'(Data); encode_disp('AttributeList',Data) -> 'enc_AttributeList'(Data); encode_disp('AddResponse',Data) -> 'enc_AddResponse'(Data); encode_disp('DelRequest',Data) -> 'enc_DelRequest'(Data); encode_disp('DelResponse',Data) -> 'enc_DelResponse'(Data); encode_disp('ModifyDNRequest',Data) -> 'enc_ModifyDNRequest'(Data); encode_disp('ModifyDNResponse',Data) -> 'enc_ModifyDNResponse'(Data); encode_disp('CompareRequest',Data) -> 'enc_CompareRequest'(Data); encode_disp('CompareResponse',Data) -> 'enc_CompareResponse'(Data); encode_disp('AbandonRequest',Data) -> 'enc_AbandonRequest'(Data); encode_disp('ExtendedRequest',Data) -> 'enc_ExtendedRequest'(Data); encode_disp('ExtendedResponse',Data) -> 'enc_ExtendedResponse'(Data); encode_disp('PasswdModifyRequestValue',Data) -> 'enc_PasswdModifyRequestValue'(Data); encode_disp('PasswdModifyResponseValue',Data) -> 'enc_PasswdModifyResponseValue'(Data); encode_disp(Type,_Data) -> exit({error,{asn1,{undefined_type,Type}}}). decode_disp('LDAPMessage',Data) -> 'dec_LDAPMessage'(Data); decode_disp('MessageID',Data) -> 'dec_MessageID'(Data); decode_disp('LDAPString',Data) -> 'dec_LDAPString'(Data); decode_disp('LDAPOID',Data) -> 'dec_LDAPOID'(Data); decode_disp('LDAPDN',Data) -> 'dec_LDAPDN'(Data); decode_disp('RelativeLDAPDN',Data) -> 'dec_RelativeLDAPDN'(Data); decode_disp('AttributeType',Data) -> 'dec_AttributeType'(Data); decode_disp('AttributeDescription',Data) -> 'dec_AttributeDescription'(Data); decode_disp('AttributeDescriptionList',Data) -> 'dec_AttributeDescriptionList'(Data); decode_disp('AttributeValue',Data) -> 'dec_AttributeValue'(Data); decode_disp('AttributeValueAssertion',Data) -> 'dec_AttributeValueAssertion'(Data); decode_disp('AssertionValue',Data) -> 'dec_AssertionValue'(Data); decode_disp('Attribute',Data) -> 'dec_Attribute'(Data); decode_disp('MatchingRuleId',Data) -> 'dec_MatchingRuleId'(Data); decode_disp('LDAPResult',Data) -> 'dec_LDAPResult'(Data); decode_disp('Referral',Data) -> 'dec_Referral'(Data); decode_disp('LDAPURL',Data) -> 'dec_LDAPURL'(Data); decode_disp('Controls',Data) -> 'dec_Controls'(Data); decode_disp('Control',Data) -> 'dec_Control'(Data); decode_disp('BindRequest',Data) -> 'dec_BindRequest'(Data); decode_disp('AuthenticationChoice',Data) -> 'dec_AuthenticationChoice'(Data); decode_disp('SaslCredentials',Data) -> 'dec_SaslCredentials'(Data); decode_disp('BindResponse',Data) -> 'dec_BindResponse'(Data); decode_disp('UnbindRequest',Data) -> 'dec_UnbindRequest'(Data); decode_disp('SearchRequest',Data) -> 'dec_SearchRequest'(Data); decode_disp('Filter',Data) -> 'dec_Filter'(Data); decode_disp('SubstringFilter',Data) -> 'dec_SubstringFilter'(Data); decode_disp('MatchingRuleAssertion',Data) -> 'dec_MatchingRuleAssertion'(Data); decode_disp('SearchResultEntry',Data) -> 'dec_SearchResultEntry'(Data); decode_disp('PartialAttributeList',Data) -> 'dec_PartialAttributeList'(Data); decode_disp('SearchResultReference',Data) -> 'dec_SearchResultReference'(Data); decode_disp('SearchResultDone',Data) -> 'dec_SearchResultDone'(Data); decode_disp('ModifyRequest',Data) -> 'dec_ModifyRequest'(Data); decode_disp('AttributeTypeAndValues',Data) -> 'dec_AttributeTypeAndValues'(Data); decode_disp('ModifyResponse',Data) -> 'dec_ModifyResponse'(Data); decode_disp('AddRequest',Data) -> 'dec_AddRequest'(Data); decode_disp('AttributeList',Data) -> 'dec_AttributeList'(Data); decode_disp('AddResponse',Data) -> 'dec_AddResponse'(Data); decode_disp('DelRequest',Data) -> 'dec_DelRequest'(Data); decode_disp('DelResponse',Data) -> 'dec_DelResponse'(Data); decode_disp('ModifyDNRequest',Data) -> 'dec_ModifyDNRequest'(Data); decode_disp('ModifyDNResponse',Data) -> 'dec_ModifyDNResponse'(Data); decode_disp('CompareRequest',Data) -> 'dec_CompareRequest'(Data); decode_disp('CompareResponse',Data) -> 'dec_CompareResponse'(Data); decode_disp('AbandonRequest',Data) -> 'dec_AbandonRequest'(Data); decode_disp('ExtendedRequest',Data) -> 'dec_ExtendedRequest'(Data); decode_disp('ExtendedResponse',Data) -> 'dec_ExtendedResponse'(Data); decode_disp('PasswdModifyRequestValue',Data) -> 'dec_PasswdModifyRequestValue'(Data); decode_disp('PasswdModifyResponseValue',Data) -> 'dec_PasswdModifyResponseValue'(Data); decode_disp(Type,_Data) -> exit({error,{asn1,{undefined_type,Type}}}). info() -> case ?MODULE:module_info(attributes) of Attributes when is_list(Attributes) -> case lists:keyfind(asn1_info, 1, Attributes) of {_,Info} when is_list(Info) -> Info; _ -> [] end; _ -> [] end. %%================================ %% LDAPMessage %%================================ 'enc_LDAPMessage'(Val) -> 'enc_LDAPMessage'(Val, [<<48>>]). 'enc_LDAPMessage'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3} = Val, %%------------------------------------------------- %% attribute messageID(1) with type INTEGER %%------------------------------------------------- {EncBytes1,EncLen1} = encode_integer(Cindex1, [<<2>>]), %%------------------------------------------------- %% attribute protocolOp(2) with type CHOICE %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_LDAPMessage_protocolOp'(Cindex2, []), %%------------------------------------------------- %% attribute controls(3) External ELDAPv3:Controls OPTIONAL %%------------------------------------------------- {EncBytes3,EncLen3} = case Cindex3 of asn1_NOVALUE -> {<<>>,0}; _ -> 'enc_Controls'(Cindex3, [<<160>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3], LenSoFar = EncLen1 + EncLen2 + EncLen3, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% LDAPMessage_protocolOp %%================================ 'enc_LDAPMessage_protocolOp'(Val, TagIn) -> {EncBytes,EncLen} = case element(1,Val) of bindRequest -> 'enc_BindRequest'(element(2,Val), [<<96>>]); bindResponse -> 'enc_BindResponse'(element(2,Val), [<<97>>]); unbindRequest -> encode_null(element(2,Val), [<<66>>]); searchRequest -> 'enc_SearchRequest'(element(2,Val), [<<99>>]); searchResEntry -> 'enc_SearchResultEntry'(element(2,Val), [<<100>>]); searchResDone -> 'enc_SearchResultDone'(element(2,Val), [<<101>>]); searchResRef -> 'enc_SearchResultReference'(element(2,Val), [<<115>>]); modifyRequest -> 'enc_ModifyRequest'(element(2,Val), [<<102>>]); modifyResponse -> 'enc_ModifyResponse'(element(2,Val), [<<103>>]); addRequest -> 'enc_AddRequest'(element(2,Val), [<<104>>]); addResponse -> 'enc_AddResponse'(element(2,Val), [<<105>>]); delRequest -> encode_restricted_string(element(2,Val), [<<74>>]); delResponse -> 'enc_DelResponse'(element(2,Val), [<<107>>]); modDNRequest -> 'enc_ModifyDNRequest'(element(2,Val), [<<108>>]); modDNResponse -> 'enc_ModifyDNResponse'(element(2,Val), [<<109>>]); compareRequest -> 'enc_CompareRequest'(element(2,Val), [<<110>>]); compareResponse -> 'enc_CompareResponse'(element(2,Val), [<<111>>]); abandonRequest -> encode_integer(element(2,Val), [<<80>>]); extendedReq -> 'enc_ExtendedRequest'(element(2,Val), [<<119>>]); extendedResp -> 'enc_ExtendedResponse'(element(2,Val), [<<120>>]); Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, encode_tags(TagIn, EncBytes, EncLen). 'dec_LDAPMessage_protocolOp'(Tlv, TagIn) -> Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'bindRequest' {65536, V1} -> {bindRequest, 'dec_BindRequest'(V1, [])}; %% 'bindResponse' {65537, V1} -> {bindResponse, 'dec_BindResponse'(V1, [])}; %% 'unbindRequest' {65538, V1} -> {unbindRequest, decode_null(V1,[])}; %% 'searchRequest' {65539, V1} -> {searchRequest, 'dec_SearchRequest'(V1, [])}; %% 'searchResEntry' {65540, V1} -> {searchResEntry, 'dec_SearchResultEntry'(V1, [])}; %% 'searchResDone' {65541, V1} -> {searchResDone, 'dec_SearchResultDone'(V1, [])}; %% 'searchResRef' {65555, V1} -> {searchResRef, 'dec_SearchResultReference'(V1, [])}; %% 'modifyRequest' {65542, V1} -> {modifyRequest, 'dec_ModifyRequest'(V1, [])}; %% 'modifyResponse' {65543, V1} -> {modifyResponse, 'dec_ModifyResponse'(V1, [])}; %% 'addRequest' {65544, V1} -> {addRequest, 'dec_AddRequest'(V1, [])}; %% 'addResponse' {65545, V1} -> {addResponse, 'dec_AddResponse'(V1, [])}; %% 'delRequest' {65546, V1} -> {delRequest, decode_restricted_string(V1,[])}; %% 'delResponse' {65547, V1} -> {delResponse, 'dec_DelResponse'(V1, [])}; %% 'modDNRequest' {65548, V1} -> {modDNRequest, 'dec_ModifyDNRequest'(V1, [])}; %% 'modDNResponse' {65549, V1} -> {modDNResponse, 'dec_ModifyDNResponse'(V1, [])}; %% 'compareRequest' {65550, V1} -> {compareRequest, 'dec_CompareRequest'(V1, [])}; %% 'compareResponse' {65551, V1} -> {compareResponse, 'dec_CompareResponse'(V1, [])}; %% 'abandonRequest' {65552, V1} -> {abandonRequest, decode_integer(V1,{0,2147483647},[])}; %% 'extendedReq' {65559, V1} -> {extendedReq, 'dec_ExtendedRequest'(V1, [])}; %% 'extendedResp' {65560, V1} -> {extendedResp, 'dec_ExtendedResponse'(V1, [])}; Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . 'dec_LDAPMessage'(Tlv) -> 'dec_LDAPMessage'(Tlv, [16]). 'dec_LDAPMessage'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute messageID(1) with type INTEGER %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_integer(V1,{0,2147483647},[2]), %%------------------------------------------------- %% attribute protocolOp(2) with type CHOICE %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_LDAPMessage_protocolOp'(V2, []), %%------------------------------------------------- %% attribute controls(3) External ELDAPv3:Controls OPTIONAL %%------------------------------------------------- {Term3,Tlv4} = case Tlv3 of [{131072,V3}|TempTlv4] -> {'dec_Controls'(V3, []), TempTlv4}; _ -> { asn1_NOVALUE, Tlv3} end, case Tlv4 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv4}}}) % extra fields not allowed end, {'LDAPMessage', Term1, Term2, Term3}. %%================================ %% MessageID %%================================ 'enc_MessageID'(Val) -> 'enc_MessageID'(Val, [<<2>>]). 'enc_MessageID'(Val, TagIn) -> encode_integer(Val, TagIn). 'dec_MessageID'(Tlv) -> 'dec_MessageID'(Tlv, [2]). 'dec_MessageID'(Tlv, TagIn) -> decode_integer(Tlv,{0,2147483647},TagIn). %%================================ %% LDAPString %%================================ 'enc_LDAPString'(Val) -> 'enc_LDAPString'(Val, [<<4>>]). 'enc_LDAPString'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_LDAPString'(Tlv) -> 'dec_LDAPString'(Tlv, [4]). 'dec_LDAPString'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% LDAPOID %%================================ 'enc_LDAPOID'(Val) -> 'enc_LDAPOID'(Val, [<<4>>]). 'enc_LDAPOID'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_LDAPOID'(Tlv) -> 'dec_LDAPOID'(Tlv, [4]). 'dec_LDAPOID'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% LDAPDN %%================================ 'enc_LDAPDN'(Val) -> 'enc_LDAPDN'(Val, [<<4>>]). 'enc_LDAPDN'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_LDAPDN'(Tlv) -> 'dec_LDAPDN'(Tlv, [4]). 'dec_LDAPDN'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% RelativeLDAPDN %%================================ 'enc_RelativeLDAPDN'(Val) -> 'enc_RelativeLDAPDN'(Val, [<<4>>]). 'enc_RelativeLDAPDN'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_RelativeLDAPDN'(Tlv) -> 'dec_RelativeLDAPDN'(Tlv, [4]). 'dec_RelativeLDAPDN'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% AttributeType %%================================ 'enc_AttributeType'(Val) -> 'enc_AttributeType'(Val, [<<4>>]). 'enc_AttributeType'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_AttributeType'(Tlv) -> 'dec_AttributeType'(Tlv, [4]). 'dec_AttributeType'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% AttributeDescription %%================================ 'enc_AttributeDescription'(Val) -> 'enc_AttributeDescription'(Val, [<<4>>]). 'enc_AttributeDescription'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_AttributeDescription'(Tlv) -> 'dec_AttributeDescription'(Tlv, [4]). 'dec_AttributeDescription'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% AttributeDescriptionList %%================================ 'enc_AttributeDescriptionList'(Val) -> 'enc_AttributeDescriptionList'(Val, [<<48>>]). 'enc_AttributeDescriptionList'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_AttributeDescriptionList_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_AttributeDescriptionList_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeDescriptionList_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_AttributeDescriptionList_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_AttributeDescriptionList'(Tlv) -> 'dec_AttributeDescriptionList'(Tlv, [16]). 'dec_AttributeDescriptionList'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. %%================================ %% AttributeValue %%================================ 'enc_AttributeValue'(Val) -> 'enc_AttributeValue'(Val, [<<4>>]). 'enc_AttributeValue'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_AttributeValue'(Tlv) -> 'dec_AttributeValue'(Tlv, [4]). 'dec_AttributeValue'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% AttributeValueAssertion %%================================ 'enc_AttributeValueAssertion'(Val) -> 'enc_AttributeValueAssertion'(Val, [<<48>>]). 'enc_AttributeValueAssertion'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute attributeDesc(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute assertionValue(2) with type OCTET STRING %%------------------------------------------------- {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<4>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_AttributeValueAssertion'(Tlv) -> 'dec_AttributeValueAssertion'(Tlv, [16]). 'dec_AttributeValueAssertion'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute attributeDesc(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute assertionValue(2) with type OCTET STRING %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'AttributeValueAssertion', Term1, Term2}. %%================================ %% AssertionValue %%================================ 'enc_AssertionValue'(Val) -> 'enc_AssertionValue'(Val, [<<4>>]). 'enc_AssertionValue'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_AssertionValue'(Tlv) -> 'dec_AssertionValue'(Tlv, [4]). 'dec_AssertionValue'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% Attribute %%================================ 'enc_Attribute'(Val) -> 'enc_Attribute'(Val, [<<48>>]). 'enc_Attribute'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_Attribute_vals'(Cindex2, [<<49>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% Attribute_vals %%================================ 'enc_Attribute_vals'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_Attribute_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_Attribute_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Attribute_vals_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_Attribute_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_Attribute_vals'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. 'dec_Attribute'(Tlv) -> 'dec_Attribute'(Tlv, [16]). 'dec_Attribute'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_Attribute_vals'(V2, [17]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'Attribute', Term1, Term2}. %%================================ %% MatchingRuleId %%================================ 'enc_MatchingRuleId'(Val) -> 'enc_MatchingRuleId'(Val, [<<4>>]). 'enc_MatchingRuleId'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_MatchingRuleId'(Tlv) -> 'dec_MatchingRuleId'(Tlv, [4]). 'dec_MatchingRuleId'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% LDAPResult %%================================ 'enc_LDAPResult'(Val) -> 'enc_LDAPResult'(Val, [<<48>>]). 'enc_LDAPResult'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3, Cindex4} = Val, %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of success -> encode_enumerated(0, [<<10>>]); operationsError -> encode_enumerated(1, [<<10>>]); protocolError -> encode_enumerated(2, [<<10>>]); timeLimitExceeded -> encode_enumerated(3, [<<10>>]); sizeLimitExceeded -> encode_enumerated(4, [<<10>>]); compareFalse -> encode_enumerated(5, [<<10>>]); compareTrue -> encode_enumerated(6, [<<10>>]); authMethodNotSupported -> encode_enumerated(7, [<<10>>]); strongAuthRequired -> encode_enumerated(8, [<<10>>]); referral -> encode_enumerated(10, [<<10>>]); adminLimitExceeded -> encode_enumerated(11, [<<10>>]); unavailableCriticalExtension -> encode_enumerated(12, [<<10>>]); confidentialityRequired -> encode_enumerated(13, [<<10>>]); saslBindInProgress -> encode_enumerated(14, [<<10>>]); noSuchAttribute -> encode_enumerated(16, [<<10>>]); undefinedAttributeType -> encode_enumerated(17, [<<10>>]); inappropriateMatching -> encode_enumerated(18, [<<10>>]); constraintViolation -> encode_enumerated(19, [<<10>>]); attributeOrValueExists -> encode_enumerated(20, [<<10>>]); invalidAttributeSyntax -> encode_enumerated(21, [<<10>>]); noSuchObject -> encode_enumerated(32, [<<10>>]); aliasProblem -> encode_enumerated(33, [<<10>>]); invalidDNSyntax -> encode_enumerated(34, [<<10>>]); aliasDereferencingProblem -> encode_enumerated(36, [<<10>>]); inappropriateAuthentication -> encode_enumerated(48, [<<10>>]); invalidCredentials -> encode_enumerated(49, [<<10>>]); insufficientAccessRights -> encode_enumerated(50, [<<10>>]); busy -> encode_enumerated(51, [<<10>>]); unavailable -> encode_enumerated(52, [<<10>>]); unwillingToPerform -> encode_enumerated(53, [<<10>>]); loopDetect -> encode_enumerated(54, [<<10>>]); namingViolation -> encode_enumerated(64, [<<10>>]); objectClassViolation -> encode_enumerated(65, [<<10>>]); notAllowedOnNonLeaf -> encode_enumerated(66, [<<10>>]); notAllowedOnRDN -> encode_enumerated(67, [<<10>>]); entryAlreadyExists -> encode_enumerated(68, [<<10>>]); objectClassModsProhibited -> encode_enumerated(69, [<<10>>]); affectsMultipleDSAs -> encode_enumerated(71, [<<10>>]); other -> encode_enumerated(80, [<<10>>]); Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}}) end, %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<4>>]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<4>>]), %%------------------------------------------------- %% attribute referral(4) External ELDAPv3:Referral OPTIONAL %%------------------------------------------------- {EncBytes4,EncLen4} = case Cindex4 of asn1_NOVALUE -> {<<>>,0}; _ -> 'enc_Referral'(Cindex4, [<<163>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4], LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_LDAPResult'(Tlv) -> 'dec_LDAPResult'(Tlv, [16]). 'dec_LDAPResult'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]), %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[4]), %%------------------------------------------------- %% attribute referral(4) External ELDAPv3:Referral OPTIONAL %%------------------------------------------------- {Term4,Tlv5} = case Tlv4 of [{131075,V4}|TempTlv5] -> {'dec_Referral'(V4, []), TempTlv5}; _ -> { asn1_NOVALUE, Tlv4} end, case Tlv5 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv5}}}) % extra fields not allowed end, {'LDAPResult', Term1, Term2, Term3, Term4}. %%================================ %% Referral %%================================ 'enc_Referral'(Val) -> 'enc_Referral'(Val, [<<48>>]). 'enc_Referral'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_Referral_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_Referral_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Referral_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_Referral_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_Referral'(Tlv) -> 'dec_Referral'(Tlv, [16]). 'dec_Referral'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. %%================================ %% LDAPURL %%================================ 'enc_LDAPURL'(Val) -> 'enc_LDAPURL'(Val, [<<4>>]). 'enc_LDAPURL'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_LDAPURL'(Tlv) -> 'dec_LDAPURL'(Tlv, [4]). 'dec_LDAPURL'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% Controls %%================================ 'enc_Controls'(Val) -> 'enc_Controls'(Val, [<<48>>]). 'enc_Controls'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_Controls_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_Controls_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Controls_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_Control'(H, [<<48>>]), 'enc_Controls_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_Controls'(Tlv) -> 'dec_Controls'(Tlv, [16]). 'dec_Controls'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_Control'(V1, [16]) || V1 <- Tlv1]. %%================================ %% Control %%================================ 'enc_Control'(Val) -> 'enc_Control'(Val, [<<48>>]). 'enc_Control'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3} = Val, %%------------------------------------------------- %% attribute controlType(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute criticality(2) with type BOOLEAN DEFAULT = false %%------------------------------------------------- {EncBytes2,EncLen2} = case Cindex2 of asn1_DEFAULT -> {<<>>,0}; false -> {<<>>,0}; _ -> encode_boolean(Cindex2, [<<1>>]) end, %%------------------------------------------------- %% attribute controlValue(3) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes3,EncLen3} = case Cindex3 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex3, [<<4>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3], LenSoFar = EncLen1 + EncLen2 + EncLen3, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_Control'(Tlv) -> 'dec_Control'(Tlv, [16]). 'dec_Control'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute controlType(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute criticality(2) with type BOOLEAN DEFAULT = false %%------------------------------------------------- {Term2,Tlv3} = case Tlv2 of [{1,V2}|TempTlv3] -> {decode_boolean(V2,[]), TempTlv3}; _ -> {false,Tlv2} end, %%------------------------------------------------- %% attribute controlValue(3) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term3,Tlv4} = case Tlv3 of [{4,V3}|TempTlv4] -> {decode_restricted_string(V3,[]), TempTlv4}; _ -> { asn1_NOVALUE, Tlv3} end, case Tlv4 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv4}}}) % extra fields not allowed end, {'Control', Term1, Term2, Term3}. %%================================ %% BindRequest %%================================ 'enc_BindRequest'(Val) -> 'enc_BindRequest'(Val, [<<96>>]). 'enc_BindRequest'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3} = Val, %%------------------------------------------------- %% attribute version(1) with type INTEGER %%------------------------------------------------- {EncBytes1,EncLen1} = encode_integer(Cindex1, [<<2>>]), %%------------------------------------------------- %% attribute name(2) with type OCTET STRING %%------------------------------------------------- {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<4>>]), %%------------------------------------------------- %% attribute authentication(3) External ELDAPv3:AuthenticationChoice %%------------------------------------------------- {EncBytes3,EncLen3} = 'enc_AuthenticationChoice'(Cindex3, []), BytesSoFar = [EncBytes1, EncBytes2, EncBytes3], LenSoFar = EncLen1 + EncLen2 + EncLen3, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_BindRequest'(Tlv) -> 'dec_BindRequest'(Tlv, [65536]). 'dec_BindRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute version(1) with type INTEGER %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_integer(V1,{1,127},[2]), %%------------------------------------------------- %% attribute name(2) with type OCTET STRING %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute authentication(3) External ELDAPv3:AuthenticationChoice %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = 'dec_AuthenticationChoice'(V3, []), case Tlv4 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv4}}}) % extra fields not allowed end, {'BindRequest', Term1, Term2, Term3}. %%================================ %% AuthenticationChoice %%================================ 'enc_AuthenticationChoice'(Val) -> 'enc_AuthenticationChoice'(Val, []). 'enc_AuthenticationChoice'(Val, TagIn) -> {EncBytes,EncLen} = case element(1,Val) of simple -> encode_restricted_string(element(2,Val), [<<128>>]); sasl -> 'enc_SaslCredentials'(element(2,Val), [<<163>>]); Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, encode_tags(TagIn, EncBytes, EncLen). 'dec_AuthenticationChoice'(Tlv) -> 'dec_AuthenticationChoice'(Tlv, []). 'dec_AuthenticationChoice'(Tlv, TagIn) -> Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'simple' {131072, V1} -> {simple, decode_restricted_string(V1,[])}; %% 'sasl' {131075, V1} -> {sasl, 'dec_SaslCredentials'(V1, [])}; Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . %%================================ %% SaslCredentials %%================================ 'enc_SaslCredentials'(Val) -> 'enc_SaslCredentials'(Val, [<<48>>]). 'enc_SaslCredentials'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute mechanism(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute credentials(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes2,EncLen2} = case Cindex2 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex2, [<<4>>]) end, BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_SaslCredentials'(Tlv) -> 'dec_SaslCredentials'(Tlv, [16]). 'dec_SaslCredentials'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute mechanism(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute credentials(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term2,Tlv3} = case Tlv2 of [{4,V2}|TempTlv3] -> {decode_restricted_string(V2,[]), TempTlv3}; _ -> { asn1_NOVALUE, Tlv2} end, case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'SaslCredentials', Term1, Term2}. %%================================ %% BindResponse %%================================ 'enc_BindResponse'(Val) -> 'enc_BindResponse'(Val, [<<97>>]). 'enc_BindResponse'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3, Cindex4, Cindex5} = Val, %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of success -> encode_enumerated(0, [<<10>>]); operationsError -> encode_enumerated(1, [<<10>>]); protocolError -> encode_enumerated(2, [<<10>>]); timeLimitExceeded -> encode_enumerated(3, [<<10>>]); sizeLimitExceeded -> encode_enumerated(4, [<<10>>]); compareFalse -> encode_enumerated(5, [<<10>>]); compareTrue -> encode_enumerated(6, [<<10>>]); authMethodNotSupported -> encode_enumerated(7, [<<10>>]); strongAuthRequired -> encode_enumerated(8, [<<10>>]); referral -> encode_enumerated(10, [<<10>>]); adminLimitExceeded -> encode_enumerated(11, [<<10>>]); unavailableCriticalExtension -> encode_enumerated(12, [<<10>>]); confidentialityRequired -> encode_enumerated(13, [<<10>>]); saslBindInProgress -> encode_enumerated(14, [<<10>>]); noSuchAttribute -> encode_enumerated(16, [<<10>>]); undefinedAttributeType -> encode_enumerated(17, [<<10>>]); inappropriateMatching -> encode_enumerated(18, [<<10>>]); constraintViolation -> encode_enumerated(19, [<<10>>]); attributeOrValueExists -> encode_enumerated(20, [<<10>>]); invalidAttributeSyntax -> encode_enumerated(21, [<<10>>]); noSuchObject -> encode_enumerated(32, [<<10>>]); aliasProblem -> encode_enumerated(33, [<<10>>]); invalidDNSyntax -> encode_enumerated(34, [<<10>>]); aliasDereferencingProblem -> encode_enumerated(36, [<<10>>]); inappropriateAuthentication -> encode_enumerated(48, [<<10>>]); invalidCredentials -> encode_enumerated(49, [<<10>>]); insufficientAccessRights -> encode_enumerated(50, [<<10>>]); busy -> encode_enumerated(51, [<<10>>]); unavailable -> encode_enumerated(52, [<<10>>]); unwillingToPerform -> encode_enumerated(53, [<<10>>]); loopDetect -> encode_enumerated(54, [<<10>>]); namingViolation -> encode_enumerated(64, [<<10>>]); objectClassViolation -> encode_enumerated(65, [<<10>>]); notAllowedOnNonLeaf -> encode_enumerated(66, [<<10>>]); notAllowedOnRDN -> encode_enumerated(67, [<<10>>]); entryAlreadyExists -> encode_enumerated(68, [<<10>>]); objectClassModsProhibited -> encode_enumerated(69, [<<10>>]); affectsMultipleDSAs -> encode_enumerated(71, [<<10>>]); other -> encode_enumerated(80, [<<10>>]); Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}}) end, %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<4>>]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<4>>]), %%------------------------------------------------- %% attribute referral(4) External ELDAPv3:Referral OPTIONAL %%------------------------------------------------- {EncBytes4,EncLen4} = case Cindex4 of asn1_NOVALUE -> {<<>>,0}; _ -> 'enc_Referral'(Cindex4, [<<163>>]) end, %%------------------------------------------------- %% attribute serverSaslCreds(5) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes5,EncLen5} = case Cindex5 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex5, [<<135>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5], LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_BindResponse'(Tlv) -> 'dec_BindResponse'(Tlv, [65537]). 'dec_BindResponse'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]), %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[4]), %%------------------------------------------------- %% attribute referral(4) External ELDAPv3:Referral OPTIONAL %%------------------------------------------------- {Term4,Tlv5} = case Tlv4 of [{131075,V4}|TempTlv5] -> {'dec_Referral'(V4, []), TempTlv5}; _ -> { asn1_NOVALUE, Tlv4} end, %%------------------------------------------------- %% attribute serverSaslCreds(5) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term5,Tlv6} = case Tlv5 of [{131079,V5}|TempTlv6] -> {decode_restricted_string(V5,[]), TempTlv6}; _ -> { asn1_NOVALUE, Tlv5} end, case Tlv6 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv6}}}) % extra fields not allowed end, {'BindResponse', Term1, Term2, Term3, Term4, Term5}. %%================================ %% UnbindRequest %%================================ 'enc_UnbindRequest'(Val) -> 'enc_UnbindRequest'(Val, [<<66>>]). 'enc_UnbindRequest'(Val, TagIn) -> encode_null(Val, TagIn). 'dec_UnbindRequest'(Tlv) -> 'dec_UnbindRequest'(Tlv, [65538]). 'dec_UnbindRequest'(Tlv, TagIn) -> decode_null(Tlv,TagIn). %%================================ %% SearchRequest %%================================ 'enc_SearchRequest'(Val) -> 'enc_SearchRequest'(Val, [<<99>>]). 'enc_SearchRequest'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3, Cindex4, Cindex5, Cindex6, Cindex7, Cindex8} = Val, %%------------------------------------------------- %% attribute baseObject(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute scope(2) with type ENUMERATED %%------------------------------------------------- {EncBytes2,EncLen2} = case Cindex2 of baseObject -> encode_enumerated(0, [<<10>>]); singleLevel -> encode_enumerated(1, [<<10>>]); wholeSubtree -> encode_enumerated(2, [<<10>>]); Enumval2 -> exit({error,{asn1, {enumerated_not_in_range,Enumval2}}}) end, %%------------------------------------------------- %% attribute derefAliases(3) with type ENUMERATED %%------------------------------------------------- {EncBytes3,EncLen3} = case Cindex3 of neverDerefAliases -> encode_enumerated(0, [<<10>>]); derefInSearching -> encode_enumerated(1, [<<10>>]); derefFindingBaseObj -> encode_enumerated(2, [<<10>>]); derefAlways -> encode_enumerated(3, [<<10>>]); Enumval3 -> exit({error,{asn1, {enumerated_not_in_range,Enumval3}}}) end, %%------------------------------------------------- %% attribute sizeLimit(4) with type INTEGER %%------------------------------------------------- {EncBytes4,EncLen4} = encode_integer(Cindex4, [<<2>>]), %%------------------------------------------------- %% attribute timeLimit(5) with type INTEGER %%------------------------------------------------- {EncBytes5,EncLen5} = encode_integer(Cindex5, [<<2>>]), %%------------------------------------------------- %% attribute typesOnly(6) with type BOOLEAN %%------------------------------------------------- {EncBytes6,EncLen6} = encode_boolean(Cindex6, [<<1>>]), %%------------------------------------------------- %% attribute filter(7) External ELDAPv3:Filter %%------------------------------------------------- {EncBytes7,EncLen7} = 'enc_Filter'(Cindex7, []), %%------------------------------------------------- %% attribute attributes(8) External ELDAPv3:AttributeDescriptionList %%------------------------------------------------- {EncBytes8,EncLen8} = 'enc_AttributeDescriptionList'(Cindex8, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5, EncBytes6, EncBytes7, EncBytes8], LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5 + EncLen6 + EncLen7 + EncLen8, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_SearchRequest'(Tlv) -> 'dec_SearchRequest'(Tlv, [65539]). 'dec_SearchRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute baseObject(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute scope(2) with type ENUMERATED %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_enumerated(V2,[{baseObject,0},{singleLevel,1},{wholeSubtree,2}],[10]), %%------------------------------------------------- %% attribute derefAliases(3) with type ENUMERATED %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = decode_enumerated(V3,[{neverDerefAliases,0},{derefInSearching,1},{derefFindingBaseObj,2},{derefAlways,3}],[10]), %%------------------------------------------------- %% attribute sizeLimit(4) with type INTEGER %%------------------------------------------------- [V4|Tlv5] = Tlv4, Term4 = decode_integer(V4,{0,2147483647},[2]), %%------------------------------------------------- %% attribute timeLimit(5) with type INTEGER %%------------------------------------------------- [V5|Tlv6] = Tlv5, Term5 = decode_integer(V5,{0,2147483647},[2]), %%------------------------------------------------- %% attribute typesOnly(6) with type BOOLEAN %%------------------------------------------------- [V6|Tlv7] = Tlv6, Term6 = decode_boolean(V6,[1]), %%------------------------------------------------- %% attribute filter(7) External ELDAPv3:Filter %%------------------------------------------------- [V7|Tlv8] = Tlv7, Term7 = 'dec_Filter'(V7, []), %%------------------------------------------------- %% attribute attributes(8) External ELDAPv3:AttributeDescriptionList %%------------------------------------------------- [V8|Tlv9] = Tlv8, Term8 = 'dec_AttributeDescriptionList'(V8, [16]), case Tlv9 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv9}}}) % extra fields not allowed end, {'SearchRequest', Term1, Term2, Term3, Term4, Term5, Term6, Term7, Term8}. %%================================ %% Filter %%================================ 'enc_Filter'(Val) -> 'enc_Filter'(Val, []). 'enc_Filter'(Val, TagIn) -> {EncBytes,EncLen} = case element(1,Val) of 'and' -> 'enc_Filter_and'(element(2,Val), [<<160>>]); 'or' -> 'enc_Filter_or'(element(2,Val), [<<161>>]); 'not' -> 'enc_Filter'(element(2,Val), [<<162>>]); equalityMatch -> 'enc_AttributeValueAssertion'(element(2,Val), [<<163>>]); substrings -> 'enc_SubstringFilter'(element(2,Val), [<<164>>]); greaterOrEqual -> 'enc_AttributeValueAssertion'(element(2,Val), [<<165>>]); lessOrEqual -> 'enc_AttributeValueAssertion'(element(2,Val), [<<166>>]); present -> encode_restricted_string(element(2,Val), [<<135>>]); approxMatch -> 'enc_AttributeValueAssertion'(element(2,Val), [<<168>>]); extensibleMatch -> 'enc_MatchingRuleAssertion'(element(2,Val), [<<169>>]); Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, encode_tags(TagIn, EncBytes, EncLen). %%================================ %% Filter_and %%================================ 'enc_Filter_and'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_Filter_and_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_Filter_and_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Filter_and_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_Filter'(H, []), 'enc_Filter_and_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_Filter_and'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_Filter'(V1, []) || V1 <- Tlv1]. %%================================ %% Filter_or %%================================ 'enc_Filter_or'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_Filter_or_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_Filter_or_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Filter_or_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_Filter'(H, []), 'enc_Filter_or_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_Filter_or'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_Filter'(V1, []) || V1 <- Tlv1]. 'dec_Filter'(Tlv) -> 'dec_Filter'(Tlv, []). 'dec_Filter'(Tlv, TagIn) -> Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'and' {131072, V1} -> {'and', 'dec_Filter_and'(V1, [])}; %% 'or' {131073, V1} -> {'or', 'dec_Filter_or'(V1, [])}; %% 'not' {131074, V1} -> {'not', 'dec_Filter'(V1, [])}; %% 'equalityMatch' {131075, V1} -> {equalityMatch, 'dec_AttributeValueAssertion'(V1, [])}; %% 'substrings' {131076, V1} -> {substrings, 'dec_SubstringFilter'(V1, [])}; %% 'greaterOrEqual' {131077, V1} -> {greaterOrEqual, 'dec_AttributeValueAssertion'(V1, [])}; %% 'lessOrEqual' {131078, V1} -> {lessOrEqual, 'dec_AttributeValueAssertion'(V1, [])}; %% 'present' {131079, V1} -> {present, decode_restricted_string(V1,[])}; %% 'approxMatch' {131080, V1} -> {approxMatch, 'dec_AttributeValueAssertion'(V1, [])}; %% 'extensibleMatch' {131081, V1} -> {extensibleMatch, 'dec_MatchingRuleAssertion'(V1, [])}; Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . %%================================ %% SubstringFilter %%================================ 'enc_SubstringFilter'(Val) -> 'enc_SubstringFilter'(Val, [<<48>>]). 'enc_SubstringFilter'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute substrings(2) with type SEQUENCE OF %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_SubstringFilter_substrings'(Cindex2, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% SubstringFilter_substrings %%================================ 'enc_SubstringFilter_substrings'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_SubstringFilter_substrings_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_SubstringFilter_substrings_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_SubstringFilter_substrings_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_SubstringFilter_substrings_SEQOF'(H, []), 'enc_SubstringFilter_substrings_components'(T,[EncBytes|AccBytes], AccLen + EncLen). %%================================ %% SubstringFilter_substrings_SEQOF %%================================ 'enc_SubstringFilter_substrings_SEQOF'(Val, TagIn) -> {EncBytes,EncLen} = case element(1,Val) of initial -> encode_restricted_string(element(2,Val), [<<128>>]); any -> encode_restricted_string(element(2,Val), [<<129>>]); final -> encode_restricted_string(element(2,Val), [<<130>>]); Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, encode_tags(TagIn, EncBytes, EncLen). 'dec_SubstringFilter_substrings_SEQOF'(Tlv, TagIn) -> Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'initial' {131072, V1} -> {initial, decode_restricted_string(V1,[])}; %% 'any' {131073, V1} -> {any, decode_restricted_string(V1,[])}; %% 'final' {131074, V1} -> {final, decode_restricted_string(V1,[])}; Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . 'dec_SubstringFilter_substrings'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_SubstringFilter_substrings_SEQOF'(V1, []) || V1 <- Tlv1]. 'dec_SubstringFilter'(Tlv) -> 'dec_SubstringFilter'(Tlv, [16]). 'dec_SubstringFilter'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute substrings(2) with type SEQUENCE OF %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_SubstringFilter_substrings'(V2, [16]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'SubstringFilter', Term1, Term2}. %%================================ %% MatchingRuleAssertion %%================================ 'enc_MatchingRuleAssertion'(Val) -> 'enc_MatchingRuleAssertion'(Val, [<<48>>]). 'enc_MatchingRuleAssertion'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3, Cindex4} = Val, %%------------------------------------------------- %% attribute matchingRule(1) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex1, [<<129>>]) end, %%------------------------------------------------- %% attribute type(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes2,EncLen2} = case Cindex2 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex2, [<<130>>]) end, %%------------------------------------------------- %% attribute matchValue(3) with type OCTET STRING %%------------------------------------------------- {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<131>>]), %%------------------------------------------------- %% attribute dnAttributes(4) with type BOOLEAN DEFAULT = false %%------------------------------------------------- {EncBytes4,EncLen4} = case Cindex4 of asn1_DEFAULT -> {<<>>,0}; false -> {<<>>,0}; _ -> encode_boolean(Cindex4, [<<132>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4], LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_MatchingRuleAssertion'(Tlv) -> 'dec_MatchingRuleAssertion'(Tlv, [16]). 'dec_MatchingRuleAssertion'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute matchingRule(1) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term1,Tlv2} = case Tlv1 of [{131073,V1}|TempTlv2] -> {decode_restricted_string(V1,[]), TempTlv2}; _ -> { asn1_NOVALUE, Tlv1} end, %%------------------------------------------------- %% attribute type(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term2,Tlv3} = case Tlv2 of [{131074,V2}|TempTlv3] -> {decode_restricted_string(V2,[]), TempTlv3}; _ -> { asn1_NOVALUE, Tlv2} end, %%------------------------------------------------- %% attribute matchValue(3) with type OCTET STRING %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[131075]), %%------------------------------------------------- %% attribute dnAttributes(4) with type BOOLEAN DEFAULT = false %%------------------------------------------------- {Term4,Tlv5} = case Tlv4 of [{131076,V4}|TempTlv5] -> {decode_boolean(V4,[]), TempTlv5}; _ -> {false,Tlv4} end, case Tlv5 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv5}}}) % extra fields not allowed end, {'MatchingRuleAssertion', Term1, Term2, Term3, Term4}. %%================================ %% SearchResultEntry %%================================ 'enc_SearchResultEntry'(Val) -> 'enc_SearchResultEntry'(Val, [<<100>>]). 'enc_SearchResultEntry'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute objectName(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute attributes(2) External ELDAPv3:PartialAttributeList %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_PartialAttributeList'(Cindex2, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_SearchResultEntry'(Tlv) -> 'dec_SearchResultEntry'(Tlv, [65540]). 'dec_SearchResultEntry'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute objectName(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute attributes(2) External ELDAPv3:PartialAttributeList %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_PartialAttributeList'(V2, [16]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'SearchResultEntry', Term1, Term2}. %%================================ %% PartialAttributeList %%================================ 'enc_PartialAttributeList'(Val) -> 'enc_PartialAttributeList'(Val, [<<48>>]). 'enc_PartialAttributeList'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_PartialAttributeList_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_PartialAttributeList_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_PartialAttributeList_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_PartialAttributeList_SEQOF'(H, [<<48>>]), 'enc_PartialAttributeList_components'(T,[EncBytes|AccBytes], AccLen + EncLen). %%================================ %% PartialAttributeList_SEQOF %%================================ 'enc_PartialAttributeList_SEQOF'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_PartialAttributeList_SEQOF_vals'(Cindex2, [<<49>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% PartialAttributeList_SEQOF_vals %%================================ 'enc_PartialAttributeList_SEQOF_vals'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_PartialAttributeList_SEQOF_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_PartialAttributeList_SEQOF_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_PartialAttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_PartialAttributeList_SEQOF_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_PartialAttributeList_SEQOF_vals'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. 'dec_PartialAttributeList_SEQOF'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_PartialAttributeList_SEQOF_vals'(V2, [17]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'PartialAttributeList_SEQOF', Term1, Term2}. 'dec_PartialAttributeList'(Tlv) -> 'dec_PartialAttributeList'(Tlv, [16]). 'dec_PartialAttributeList'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_PartialAttributeList_SEQOF'(V1, [16]) || V1 <- Tlv1]. %%================================ %% SearchResultReference %%================================ 'enc_SearchResultReference'(Val) -> 'enc_SearchResultReference'(Val, [<<115>>]). 'enc_SearchResultReference'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_SearchResultReference_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_SearchResultReference_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_SearchResultReference_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_SearchResultReference_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_SearchResultReference'(Tlv) -> 'dec_SearchResultReference'(Tlv, [65555]). 'dec_SearchResultReference'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. %%================================ %% SearchResultDone %%================================ 'enc_SearchResultDone'(Val) -> 'enc_SearchResultDone'(Val, [<<101>>]). 'enc_SearchResultDone'(Val, TagIn) -> 'enc_LDAPResult'(Val, TagIn). 'dec_SearchResultDone'(Tlv) -> 'dec_SearchResultDone'(Tlv, [65541]). 'dec_SearchResultDone'(Tlv, TagIn) -> 'dec_LDAPResult'(Tlv, TagIn). %%================================ %% ModifyRequest %%================================ 'enc_ModifyRequest'(Val) -> 'enc_ModifyRequest'(Val, [<<102>>]). 'enc_ModifyRequest'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute object(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute modification(2) with type SEQUENCE OF %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_ModifyRequest_modification'(Cindex2, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% ModifyRequest_modification %%================================ 'enc_ModifyRequest_modification'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_ModifyRequest_modification_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_ModifyRequest_modification_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_ModifyRequest_modification_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_ModifyRequest_modification_SEQOF'(H, [<<48>>]), 'enc_ModifyRequest_modification_components'(T,[EncBytes|AccBytes], AccLen + EncLen). %%================================ %% ModifyRequest_modification_SEQOF %%================================ 'enc_ModifyRequest_modification_SEQOF'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute operation(1) with type ENUMERATED %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of add -> encode_enumerated(0, [<<10>>]); delete -> encode_enumerated(1, [<<10>>]); replace -> encode_enumerated(2, [<<10>>]); Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}}) end, %%------------------------------------------------- %% attribute modification(2) External ELDAPv3:AttributeTypeAndValues %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_AttributeTypeAndValues'(Cindex2, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ModifyRequest_modification_SEQOF'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute operation(1) with type ENUMERATED %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{add,0},{delete,1},{replace,2}],[10]), %%------------------------------------------------- %% attribute modification(2) External ELDAPv3:AttributeTypeAndValues %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeTypeAndValues'(V2, [16]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'ModifyRequest_modification_SEQOF', Term1, Term2}. 'dec_ModifyRequest_modification'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_ModifyRequest_modification_SEQOF'(V1, [16]) || V1 <- Tlv1]. 'dec_ModifyRequest'(Tlv) -> 'dec_ModifyRequest'(Tlv, [65542]). 'dec_ModifyRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute object(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute modification(2) with type SEQUENCE OF %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_ModifyRequest_modification'(V2, [16]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'ModifyRequest', Term1, Term2}. %%================================ %% AttributeTypeAndValues %%================================ 'enc_AttributeTypeAndValues'(Val) -> 'enc_AttributeTypeAndValues'(Val, [<<48>>]). 'enc_AttributeTypeAndValues'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_AttributeTypeAndValues_vals'(Cindex2, [<<49>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% AttributeTypeAndValues_vals %%================================ 'enc_AttributeTypeAndValues_vals'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_AttributeTypeAndValues_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_AttributeTypeAndValues_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeTypeAndValues_vals_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_AttributeTypeAndValues_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_AttributeTypeAndValues_vals'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. 'dec_AttributeTypeAndValues'(Tlv) -> 'dec_AttributeTypeAndValues'(Tlv, [16]). 'dec_AttributeTypeAndValues'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeTypeAndValues_vals'(V2, [17]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'AttributeTypeAndValues', Term1, Term2}. %%================================ %% ModifyResponse %%================================ 'enc_ModifyResponse'(Val) -> 'enc_ModifyResponse'(Val, [<<103>>]). 'enc_ModifyResponse'(Val, TagIn) -> 'enc_LDAPResult'(Val, TagIn). 'dec_ModifyResponse'(Tlv) -> 'dec_ModifyResponse'(Tlv, [65543]). 'dec_ModifyResponse'(Tlv, TagIn) -> 'dec_LDAPResult'(Tlv, TagIn). %%================================ %% AddRequest %%================================ 'enc_AddRequest'(Val) -> 'enc_AddRequest'(Val, [<<104>>]). 'enc_AddRequest'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute attributes(2) External ELDAPv3:AttributeList %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_AttributeList'(Cindex2, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_AddRequest'(Tlv) -> 'dec_AddRequest'(Tlv, [65544]). 'dec_AddRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute attributes(2) External ELDAPv3:AttributeList %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeList'(V2, [16]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'AddRequest', Term1, Term2}. %%================================ %% AttributeList %%================================ 'enc_AttributeList'(Val) -> 'enc_AttributeList'(Val, [<<48>>]). 'enc_AttributeList'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_AttributeList_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_AttributeList_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeList_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = 'enc_AttributeList_SEQOF'(H, [<<48>>]), 'enc_AttributeList_components'(T,[EncBytes|AccBytes], AccLen + EncLen). %%================================ %% AttributeList_SEQOF %%================================ 'enc_AttributeList_SEQOF'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_AttributeList_SEQOF_vals'(Cindex2, [<<49>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). %%================================ %% AttributeList_SEQOF_vals %%================================ 'enc_AttributeList_SEQOF_vals'(Val, TagIn) -> {EncBytes,EncLen} = 'enc_AttributeList_SEQOF_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). 'enc_AttributeList_SEQOF_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) -> {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), 'enc_AttributeList_SEQOF_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen). 'dec_AttributeList_SEQOF_vals'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. 'dec_AttributeList_SEQOF'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeList_SEQOF_vals'(V2, [17]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'AttributeList_SEQOF', Term1, Term2}. 'dec_AttributeList'(Tlv) -> 'dec_AttributeList'(Tlv, [16]). 'dec_AttributeList'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_AttributeList_SEQOF'(V1, [16]) || V1 <- Tlv1]. %%================================ %% AddResponse %%================================ 'enc_AddResponse'(Val) -> 'enc_AddResponse'(Val, [<<105>>]). 'enc_AddResponse'(Val, TagIn) -> 'enc_LDAPResult'(Val, TagIn). 'dec_AddResponse'(Tlv) -> 'dec_AddResponse'(Tlv, [65545]). 'dec_AddResponse'(Tlv, TagIn) -> 'dec_LDAPResult'(Tlv, TagIn). %%================================ %% DelRequest %%================================ 'enc_DelRequest'(Val) -> 'enc_DelRequest'(Val, [<<74>>]). 'enc_DelRequest'(Val, TagIn) -> encode_restricted_string(Val, TagIn). 'dec_DelRequest'(Tlv) -> 'dec_DelRequest'(Tlv, [65546]). 'dec_DelRequest'(Tlv, TagIn) -> decode_restricted_string(Tlv,TagIn). %%================================ %% DelResponse %%================================ 'enc_DelResponse'(Val) -> 'enc_DelResponse'(Val, [<<107>>]). 'enc_DelResponse'(Val, TagIn) -> 'enc_LDAPResult'(Val, TagIn). 'dec_DelResponse'(Tlv) -> 'dec_DelResponse'(Tlv, [65547]). 'dec_DelResponse'(Tlv, TagIn) -> 'dec_LDAPResult'(Tlv, TagIn). %%================================ %% ModifyDNRequest %%================================ 'enc_ModifyDNRequest'(Val) -> 'enc_ModifyDNRequest'(Val, [<<108>>]). 'enc_ModifyDNRequest'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3, Cindex4} = Val, %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute newrdn(2) with type OCTET STRING %%------------------------------------------------- {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<4>>]), %%------------------------------------------------- %% attribute deleteoldrdn(3) with type BOOLEAN %%------------------------------------------------- {EncBytes3,EncLen3} = encode_boolean(Cindex3, [<<1>>]), %%------------------------------------------------- %% attribute newSuperior(4) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes4,EncLen4} = case Cindex4 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex4, [<<128>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4], LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ModifyDNRequest'(Tlv) -> 'dec_ModifyDNRequest'(Tlv, [65548]). 'dec_ModifyDNRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute newrdn(2) with type OCTET STRING %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute deleteoldrdn(3) with type BOOLEAN %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = decode_boolean(V3,[1]), %%------------------------------------------------- %% attribute newSuperior(4) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term4,Tlv5} = case Tlv4 of [{131072,V4}|TempTlv5] -> {decode_restricted_string(V4,[]), TempTlv5}; _ -> { asn1_NOVALUE, Tlv4} end, case Tlv5 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv5}}}) % extra fields not allowed end, {'ModifyDNRequest', Term1, Term2, Term3, Term4}. %%================================ %% ModifyDNResponse %%================================ 'enc_ModifyDNResponse'(Val) -> 'enc_ModifyDNResponse'(Val, [<<109>>]). 'enc_ModifyDNResponse'(Val, TagIn) -> 'enc_LDAPResult'(Val, TagIn). 'dec_ModifyDNResponse'(Tlv) -> 'dec_ModifyDNResponse'(Tlv, [65549]). 'dec_ModifyDNResponse'(Tlv, TagIn) -> 'dec_LDAPResult'(Tlv, TagIn). %%================================ %% CompareRequest %%================================ 'enc_CompareRequest'(Val) -> 'enc_CompareRequest'(Val, [<<110>>]). 'enc_CompareRequest'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<4>>]), %%------------------------------------------------- %% attribute ava(2) External ELDAPv3:AttributeValueAssertion %%------------------------------------------------- {EncBytes2,EncLen2} = 'enc_AttributeValueAssertion'(Cindex2, [<<48>>]), BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_CompareRequest'(Tlv) -> 'dec_CompareRequest'(Tlv, [65550]). 'dec_CompareRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute ava(2) External ELDAPv3:AttributeValueAssertion %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeValueAssertion'(V2, [16]), case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'CompareRequest', Term1, Term2}. %%================================ %% CompareResponse %%================================ 'enc_CompareResponse'(Val) -> 'enc_CompareResponse'(Val, [<<111>>]). 'enc_CompareResponse'(Val, TagIn) -> 'enc_LDAPResult'(Val, TagIn). 'dec_CompareResponse'(Tlv) -> 'dec_CompareResponse'(Tlv, [65551]). 'dec_CompareResponse'(Tlv, TagIn) -> 'dec_LDAPResult'(Tlv, TagIn). %%================================ %% AbandonRequest %%================================ 'enc_AbandonRequest'(Val) -> 'enc_AbandonRequest'(Val, [<<80>>]). 'enc_AbandonRequest'(Val, TagIn) -> encode_integer(Val, TagIn). 'dec_AbandonRequest'(Tlv) -> 'dec_AbandonRequest'(Tlv, [65552]). 'dec_AbandonRequest'(Tlv, TagIn) -> decode_integer(Tlv,{0,2147483647},TagIn). %%================================ %% ExtendedRequest %%================================ 'enc_ExtendedRequest'(Val) -> 'enc_ExtendedRequest'(Val, [<<119>>]). 'enc_ExtendedRequest'(Val, TagIn) -> {_,Cindex1, Cindex2} = Val, %%------------------------------------------------- %% attribute requestName(1) with type OCTET STRING %%------------------------------------------------- {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<128>>]), %%------------------------------------------------- %% attribute requestValue(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes2,EncLen2} = case Cindex2 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex2, [<<129>>]) end, BytesSoFar = [EncBytes1, EncBytes2], LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ExtendedRequest'(Tlv) -> 'dec_ExtendedRequest'(Tlv, [65559]). 'dec_ExtendedRequest'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute requestName(1) with type OCTET STRING %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[131072]), %%------------------------------------------------- %% attribute requestValue(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term2,Tlv3} = case Tlv2 of [{131073,V2}|TempTlv3] -> {decode_restricted_string(V2,[]), TempTlv3}; _ -> { asn1_NOVALUE, Tlv2} end, case Tlv3 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed end, {'ExtendedRequest', Term1, Term2}. %%================================ %% ExtendedResponse %%================================ 'enc_ExtendedResponse'(Val) -> 'enc_ExtendedResponse'(Val, [<<120>>]). 'enc_ExtendedResponse'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3, Cindex4, Cindex5, Cindex6} = Val, %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of success -> encode_enumerated(0, [<<10>>]); operationsError -> encode_enumerated(1, [<<10>>]); protocolError -> encode_enumerated(2, [<<10>>]); timeLimitExceeded -> encode_enumerated(3, [<<10>>]); sizeLimitExceeded -> encode_enumerated(4, [<<10>>]); compareFalse -> encode_enumerated(5, [<<10>>]); compareTrue -> encode_enumerated(6, [<<10>>]); authMethodNotSupported -> encode_enumerated(7, [<<10>>]); strongAuthRequired -> encode_enumerated(8, [<<10>>]); referral -> encode_enumerated(10, [<<10>>]); adminLimitExceeded -> encode_enumerated(11, [<<10>>]); unavailableCriticalExtension -> encode_enumerated(12, [<<10>>]); confidentialityRequired -> encode_enumerated(13, [<<10>>]); saslBindInProgress -> encode_enumerated(14, [<<10>>]); noSuchAttribute -> encode_enumerated(16, [<<10>>]); undefinedAttributeType -> encode_enumerated(17, [<<10>>]); inappropriateMatching -> encode_enumerated(18, [<<10>>]); constraintViolation -> encode_enumerated(19, [<<10>>]); attributeOrValueExists -> encode_enumerated(20, [<<10>>]); invalidAttributeSyntax -> encode_enumerated(21, [<<10>>]); noSuchObject -> encode_enumerated(32, [<<10>>]); aliasProblem -> encode_enumerated(33, [<<10>>]); invalidDNSyntax -> encode_enumerated(34, [<<10>>]); aliasDereferencingProblem -> encode_enumerated(36, [<<10>>]); inappropriateAuthentication -> encode_enumerated(48, [<<10>>]); invalidCredentials -> encode_enumerated(49, [<<10>>]); insufficientAccessRights -> encode_enumerated(50, [<<10>>]); busy -> encode_enumerated(51, [<<10>>]); unavailable -> encode_enumerated(52, [<<10>>]); unwillingToPerform -> encode_enumerated(53, [<<10>>]); loopDetect -> encode_enumerated(54, [<<10>>]); namingViolation -> encode_enumerated(64, [<<10>>]); objectClassViolation -> encode_enumerated(65, [<<10>>]); notAllowedOnNonLeaf -> encode_enumerated(66, [<<10>>]); notAllowedOnRDN -> encode_enumerated(67, [<<10>>]); entryAlreadyExists -> encode_enumerated(68, [<<10>>]); objectClassModsProhibited -> encode_enumerated(69, [<<10>>]); affectsMultipleDSAs -> encode_enumerated(71, [<<10>>]); other -> encode_enumerated(80, [<<10>>]); Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}}) end, %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<4>>]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<4>>]), %%------------------------------------------------- %% attribute referral(4) External ELDAPv3:Referral OPTIONAL %%------------------------------------------------- {EncBytes4,EncLen4} = case Cindex4 of asn1_NOVALUE -> {<<>>,0}; _ -> 'enc_Referral'(Cindex4, [<<163>>]) end, %%------------------------------------------------- %% attribute responseName(5) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes5,EncLen5} = case Cindex5 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex5, [<<138>>]) end, %%------------------------------------------------- %% attribute response(6) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes6,EncLen6} = case Cindex6 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex6, [<<139>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5, EncBytes6], LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5 + EncLen6, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ExtendedResponse'(Tlv) -> 'dec_ExtendedResponse'(Tlv, [65560]). 'dec_ExtendedResponse'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- [V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]), %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- [V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- [V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[4]), %%------------------------------------------------- %% attribute referral(4) External ELDAPv3:Referral OPTIONAL %%------------------------------------------------- {Term4,Tlv5} = case Tlv4 of [{131075,V4}|TempTlv5] -> {'dec_Referral'(V4, []), TempTlv5}; _ -> { asn1_NOVALUE, Tlv4} end, %%------------------------------------------------- %% attribute responseName(5) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term5,Tlv6} = case Tlv5 of [{131082,V5}|TempTlv6] -> {decode_restricted_string(V5,[]), TempTlv6}; _ -> { asn1_NOVALUE, Tlv5} end, %%------------------------------------------------- %% attribute response(6) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term6,Tlv7} = case Tlv6 of [{131083,V6}|TempTlv7] -> {decode_restricted_string(V6,[]), TempTlv7}; _ -> { asn1_NOVALUE, Tlv6} end, case Tlv7 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv7}}}) % extra fields not allowed end, {'ExtendedResponse', Term1, Term2, Term3, Term4, Term5, Term6}. %%================================ %% PasswdModifyRequestValue %%================================ 'enc_PasswdModifyRequestValue'(Val) -> 'enc_PasswdModifyRequestValue'(Val, [<<48>>]). 'enc_PasswdModifyRequestValue'(Val, TagIn) -> {_,Cindex1, Cindex2, Cindex3} = Val, %%------------------------------------------------- %% attribute userIdentity(1) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex1, [<<128>>]) end, %%------------------------------------------------- %% attribute oldPasswd(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes2,EncLen2} = case Cindex2 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex2, [<<129>>]) end, %%------------------------------------------------- %% attribute newPasswd(3) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes3,EncLen3} = case Cindex3 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex3, [<<130>>]) end, BytesSoFar = [EncBytes1, EncBytes2, EncBytes3], LenSoFar = EncLen1 + EncLen2 + EncLen3, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_PasswdModifyRequestValue'(Tlv) -> 'dec_PasswdModifyRequestValue'(Tlv, [16]). 'dec_PasswdModifyRequestValue'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute userIdentity(1) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term1,Tlv2} = case Tlv1 of [{131072,V1}|TempTlv2] -> {decode_restricted_string(V1,[]), TempTlv2}; _ -> { asn1_NOVALUE, Tlv1} end, %%------------------------------------------------- %% attribute oldPasswd(2) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term2,Tlv3} = case Tlv2 of [{131073,V2}|TempTlv3] -> {decode_restricted_string(V2,[]), TempTlv3}; _ -> { asn1_NOVALUE, Tlv2} end, %%------------------------------------------------- %% attribute newPasswd(3) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term3,Tlv4} = case Tlv3 of [{131074,V3}|TempTlv4] -> {decode_restricted_string(V3,[]), TempTlv4}; _ -> { asn1_NOVALUE, Tlv3} end, case Tlv4 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv4}}}) % extra fields not allowed end, {'PasswdModifyRequestValue', Term1, Term2, Term3}. %%================================ %% PasswdModifyResponseValue %%================================ 'enc_PasswdModifyResponseValue'(Val) -> 'enc_PasswdModifyResponseValue'(Val, [<<48>>]). 'enc_PasswdModifyResponseValue'(Val, TagIn) -> {_,Cindex1} = Val, %%------------------------------------------------- %% attribute genPasswd(1) with type OCTET STRING OPTIONAL %%------------------------------------------------- {EncBytes1,EncLen1} = case Cindex1 of asn1_NOVALUE -> {<<>>,0}; _ -> encode_restricted_string(Cindex1, [<<128>>]) end, BytesSoFar = [EncBytes1], LenSoFar = EncLen1, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_PasswdModifyResponseValue'(Tlv) -> 'dec_PasswdModifyResponseValue'(Tlv, [16]). 'dec_PasswdModifyResponseValue'(Tlv, TagIn) -> %%------------------------------------------------- %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute genPasswd(1) with type OCTET STRING OPTIONAL %%------------------------------------------------- {Term1,Tlv2} = case Tlv1 of [{131072,V1}|TempTlv2] -> {decode_restricted_string(V1,[]), TempTlv2}; _ -> { asn1_NOVALUE, Tlv1} end, case Tlv2 of [] -> true;_ -> exit({error,{asn1, {unexpected,Tlv2}}}) % extra fields not allowed end, {'PasswdModifyResponseValue', Term1}. 'maxInt'() -> 2147483647. 'passwdModifyOID'() -> [49,46,51,46,54,46,49,46,52,46,49,46,52,50,48,51,46,49,46,49,49,46,49]. %%% %%% Run-time functions. %%% ber_decode_nif(B) -> asn1rt_nif:decode_ber_tlv(B). collect_parts(TlvList) -> collect_parts(TlvList, []). collect_parts([{_,L}|Rest], Acc) when is_list(L) -> collect_parts(Rest, [collect_parts(L)|Acc]); collect_parts([{3,<>}|Rest], _Acc) -> collect_parts_bit(Rest, [Bits], Unused); collect_parts([{_T,V}|Rest], Acc) -> collect_parts(Rest, [V|Acc]); collect_parts([], Acc) -> list_to_binary(lists:reverse(Acc)). collect_parts_bit([{3,<>}|Rest], Acc, Uacc) -> collect_parts_bit(Rest, [Bits|Acc], Unused + Uacc); collect_parts_bit([], Acc, Uacc) -> list_to_binary([Uacc|lists:reverse(Acc)]). decode_boolean(Tlv, TagIn) -> Val = match_tags(Tlv, TagIn), case Val of <<0:8>> -> false; <<_:8>> -> true; _ -> exit({error,{asn1,{decode_boolean,Val}}}) end. decode_enumerated(Tlv, NamedNumberList, Tags) -> Buffer = match_tags(Tlv, Tags), decode_enumerated_notag(Buffer, NamedNumberList, Tags). decode_enumerated1(Val, NamedNumberList) -> case lists:keyfind(Val, 2, NamedNumberList) of {NamedVal,_} -> NamedVal; _ -> {asn1_enum,Val} end. decode_enumerated_notag(Buffer, {NamedNumberList,ExtList}, _Tags) -> IVal = decode_integer(Buffer), case decode_enumerated1(IVal, NamedNumberList) of {asn1_enum,IVal} -> decode_enumerated1(IVal, ExtList); EVal -> EVal end; decode_enumerated_notag(Buffer, NNList, _Tags) -> IVal = decode_integer(Buffer), case decode_enumerated1(IVal, NNList) of {asn1_enum,_} -> exit({error,{asn1,{illegal_enumerated,IVal}}}); EVal -> EVal end. decode_integer(Bin) -> Len = byte_size(Bin), <> = Bin, Int. decode_integer(Tlv, Range, TagIn) -> V = match_tags(Tlv, TagIn), Int = decode_integer(V), range_check_integer(Int, Range). decode_null(Tlv, Tags) -> Val = match_tags(Tlv, Tags), case Val of <<>> -> 'NULL'; _ -> exit({error,{asn1,{decode_null,Val}}}) end. decode_restricted_string(Tlv, TagsIn) -> Bin = match_and_collect(Tlv, TagsIn), Bin. encode_boolean(true, TagIn) -> encode_tags(TagIn, [255], 1); encode_boolean(false, TagIn) -> encode_tags(TagIn, [0], 1); encode_boolean(X, _) -> exit({error,{asn1,{encode_boolean,X}}}). encode_enumerated(Val, TagIn) when is_integer(Val) -> encode_tags(TagIn, encode_integer(Val)). encode_integer(Val) -> Bytes = if Val >= 0 -> encode_integer_pos(Val, []); true -> encode_integer_neg(Val, []) end, {Bytes,length(Bytes)}. encode_integer(Val, Tag) when is_integer(Val) -> encode_tags(Tag, encode_integer(Val)); encode_integer(Val, _Tag) -> exit({error,{asn1,{encode_integer,Val}}}). encode_integer_neg(- 1, [B1|_T] = L) when B1 > 127 -> L; encode_integer_neg(N, Acc) -> encode_integer_neg(N bsr 8, [N band 255|Acc]). encode_integer_pos(0, [B|_Acc] = L) when B < 128 -> L; encode_integer_pos(N, Acc) -> encode_integer_pos(N bsr 8, [N band 255|Acc]). encode_length(L) when L =< 127 -> {[L],1}; encode_length(L) -> Oct = minimum_octets(L), Len = length(Oct), if Len =< 126 -> {[128 bor Len|Oct],Len + 1}; true -> exit({error,{asn1,too_long_length_oct,Len}}) end. encode_null(_Val, TagIn) -> encode_tags(TagIn, [], 0). encode_restricted_string(OctetList, TagIn) when is_binary(OctetList) -> encode_tags(TagIn, OctetList, byte_size(OctetList)); encode_restricted_string(OctetList, TagIn) when is_list(OctetList) -> encode_tags(TagIn, OctetList, length(OctetList)). encode_tags(TagIn, {BytesSoFar,LenSoFar}) -> encode_tags(TagIn, BytesSoFar, LenSoFar). encode_tags([Tag|Trest], BytesSoFar, LenSoFar) -> {Bytes2,L2} = encode_length(LenSoFar), encode_tags(Trest, [Tag,Bytes2|BytesSoFar], LenSoFar + byte_size(Tag) + L2); encode_tags([], BytesSoFar, LenSoFar) -> {BytesSoFar,LenSoFar}. match_and_collect(Tlv, TagsIn) -> Val = match_tags(Tlv, TagsIn), case Val of [_|_] = PartList -> collect_parts(PartList); Bin when is_binary(Bin) -> Bin end. match_tags({T,V}, [T]) -> V; match_tags({T,V}, [T|Tt]) -> match_tags(V, Tt); match_tags([{T,V}], [T|Tt]) -> match_tags(V, Tt); match_tags([{T,_V}|_] = Vlist, [T]) -> Vlist; match_tags(Tlv, []) -> Tlv; match_tags({Tag,_V} = Tlv, [T|_Tt]) -> exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}). minimum_octets(0, Acc) -> Acc; minimum_octets(Val, Acc) -> minimum_octets(Val bsr 8, [Val band 255|Acc]). minimum_octets(Val) -> minimum_octets(Val, []). range_check_integer(Int, Range) -> case Range of [] -> Int; {Lb,Ub} when Int >= Lb, Ub >= Int -> Int; {_,_} -> exit({error,{asn1,{integer_range,Range,Int}}}); Int -> Int; SingleValue when is_integer(SingleValue) -> exit({error,{asn1,{integer_range,Range,Int}}}); _ -> Int end. ejabberd-18.01/src/mod_http_upload_quota.erl0000644000232200023220000002731113225664356021535 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_http_upload_quota.erl %%% Author : Holger Weiss %%% Purpose : Quota management for HTTP File Upload (XEP-0363) %%% Created : 15 Oct 2015 by Holger Weiss %%% %%% %%% ejabberd, Copyright (C) 2015-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_http_upload_quota). -author('holger@zedat.fu-berlin.de'). -define(TIMEOUT, timer:hours(24)). -define(INITIAL_TIMEOUT, timer:minutes(10)). -define(FORMAT(Error), file:format_error(Error)). -behaviour(gen_server). -behaviour(gen_mod). %% gen_mod/supervisor callbacks. -export([start/2, stop/1, depends/2, mod_opt_type/1]). %% gen_server callbacks. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% ejabberd_hooks callback. -export([handle_slot_request/5]). -include("jid.hrl"). -include("logger.hrl"). -include_lib("kernel/include/file.hrl"). -record(state, {server_host :: binary(), access_soft_quota :: atom(), access_hard_quota :: atom(), max_days :: pos_integer() | infinity, docroot :: binary(), disk_usage = #{} :: map(), timers :: [timer:tref()]}). -type state() :: #state{}. %%-------------------------------------------------------------------- %% gen_mod/supervisor callbacks. %%-------------------------------------------------------------------- -spec start(binary(), gen_mod:opts()) -> {ok, pid()}. start(ServerHost, Opts) -> Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE), gen_mod:start_child(?MODULE, ServerHost, Opts, Proc). -spec stop(binary()) -> ok | {error, any()}. stop(ServerHost) -> Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE), gen_mod:stop_child(Proc). -spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. mod_opt_type(access_soft_quota) -> fun acl:shaper_rules_validator/1; mod_opt_type(access_hard_quota) -> fun acl:shaper_rules_validator/1; mod_opt_type(max_days) -> fun(I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(_) -> [access_soft_quota, access_hard_quota, max_days]. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> [{mod_http_upload, hard}]. %%-------------------------------------------------------------------- %% gen_server callbacks. %%-------------------------------------------------------------------- init([ServerHost, Opts]) -> process_flag(trap_exit, true), AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts, soft_upload_quota), AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts, hard_upload_quota), MaxDays = gen_mod:get_opt(max_days, Opts, infinity), DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot, <<"@HOME@/upload">>), DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)), DocRoot3 = mod_http_upload:expand_host(DocRoot2, ServerHost), Timers = if MaxDays == infinity -> []; true -> {ok, T1} = timer:send_after(?INITIAL_TIMEOUT, sweep), {ok, T2} = timer:send_interval(?TIMEOUT, sweep), [T1, T2] end, ejabberd_hooks:add(http_upload_slot_request, ServerHost, ?MODULE, handle_slot_request, 50), {ok, #state{server_host = ServerHost, access_soft_quota = AccessSoftQuota, access_hard_quota = AccessHardQuota, max_days = MaxDays, docroot = DocRoot3, timers = Timers}}. -spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}. handle_call(Request, From, State) -> ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]), {noreply, State}. -spec handle_cast(_, state()) -> {noreply, state()}. handle_cast({handle_slot_request, #jid{user = U, server = S} = JID, Path, Size}, #state{server_host = ServerHost, access_soft_quota = AccessSoftQuota, access_hard_quota = AccessHardQuota, disk_usage = DiskUsage} = State) -> HardQuota = case acl:match_rule(ServerHost, AccessHardQuota, JID) of Hard when is_integer(Hard), Hard > 0 -> Hard * 1024 * 1024; _ -> 0 end, SoftQuota = case acl:match_rule(ServerHost, AccessSoftQuota, JID) of Soft when is_integer(Soft), Soft > 0 -> Soft * 1024 * 1024; _ -> 0 end, OldSize = case maps:find({U, S}, DiskUsage) of {ok, Value} -> Value; error -> undefined end, NewSize = case {HardQuota, SoftQuota} of {0, 0} -> ?DEBUG("No quota specified for ~s", [jid:encode(JID)]), undefined; {0, _} -> ?WARNING_MSG("No hard quota specified for ~s", [jid:encode(JID)]), enforce_quota(Path, Size, OldSize, SoftQuota, SoftQuota); {_, 0} -> ?WARNING_MSG("No soft quota specified for ~s", [jid:encode(JID)]), enforce_quota(Path, Size, OldSize, HardQuota, HardQuota); _ when SoftQuota > HardQuota -> ?WARNING_MSG("Bad quota for ~s (soft: ~p, hard: ~p)", [jid:encode(JID), SoftQuota, HardQuota]), enforce_quota(Path, Size, OldSize, SoftQuota, SoftQuota); _ -> ?DEBUG("Enforcing quota for ~s", [jid:encode(JID)]), enforce_quota(Path, Size, OldSize, SoftQuota, HardQuota) end, NewDiskUsage = if is_integer(NewSize) -> maps:put({U, S}, NewSize, DiskUsage); true -> DiskUsage end, {noreply, State#state{disk_usage = NewDiskUsage}}; handle_cast(Request, State) -> ?ERROR_MSG("Got unexpected request: ~p", [Request]), {noreply, State}. -spec handle_info(_, state()) -> {noreply, state()}. handle_info(sweep, #state{server_host = ServerHost, docroot = DocRoot, max_days = MaxDays} = State) when is_integer(MaxDays), MaxDays > 0 -> ?DEBUG("Got 'sweep' message for ~s", [ServerHost]), case file:list_dir(DocRoot) of {ok, Entries} -> BackThen = secs_since_epoch() - (MaxDays * 86400), DocRootS = binary_to_list(DocRoot), PathNames = lists:map(fun(Entry) -> DocRootS ++ "/" ++ Entry end, Entries), UserDirs = lists:filter(fun filelib:is_dir/1, PathNames), lists:foreach(fun(UserDir) -> delete_old_files(UserDir, BackThen) end, UserDirs); {error, Error} -> ?ERROR_MSG("Cannot open document root ~s: ~s", [DocRoot, ?FORMAT(Error)]) end, {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("Got unexpected info: ~p", [Info]), {noreply, State}. -spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok. terminate(Reason, #state{server_host = ServerHost, timers = Timers}) -> ?DEBUG("Stopping upload quota process for ~s: ~p", [ServerHost, Reason]), ejabberd_hooks:delete(http_upload_slot_request, ServerHost, ?MODULE, handle_slot_request, 50), lists:foreach(fun timer:cancel/1, Timers). -spec code_change({down, _} | _, state(), _) -> {ok, state()}. code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) -> ?DEBUG("Updating upload quota process for ~s", [ServerHost]), {ok, State}. %%-------------------------------------------------------------------- %% ejabberd_hooks callback. %%-------------------------------------------------------------------- -spec handle_slot_request(allow | deny, jid(), binary(), non_neg_integer(), binary()) -> allow | deny. handle_slot_request(allow, #jid{lserver = ServerHost} = JID, Path, Size, _Lang) -> Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE), gen_server:cast(Proc, {handle_slot_request, JID, Path, Size}), allow; handle_slot_request(Acc, _JID, _Path, _Size, _Lang) -> Acc. %%-------------------------------------------------------------------- %% Internal functions. %%-------------------------------------------------------------------- -spec enforce_quota(file:filename_all(), non_neg_integer(), non_neg_integer() | undefined, non_neg_integer(), non_neg_integer()) -> non_neg_integer(). enforce_quota(_UserDir, SlotSize, OldSize, _MinSize, MaxSize) when is_integer(OldSize), OldSize + SlotSize =< MaxSize -> OldSize + SlotSize; enforce_quota(UserDir, SlotSize, _OldSize, MinSize, MaxSize) -> Files = lists:sort(fun({_PathA, _SizeA, TimeA}, {_PathB, _SizeB, TimeB}) -> TimeA > TimeB end, gather_file_info(UserDir)), {DelFiles, OldSize, NewSize} = lists:foldl(fun({_Path, Size, _Time}, {[], AccSize, AccSize}) when AccSize + Size + SlotSize =< MinSize -> {[], AccSize + Size, AccSize + Size}; ({Path, Size, _Time}, {[], AccSize, AccSize}) -> {[Path], AccSize + Size, AccSize}; ({Path, Size, _Time}, {AccFiles, AccSize, NewSize}) -> {[Path | AccFiles], AccSize + Size, NewSize} end, {[], 0, 0}, Files), if OldSize + SlotSize > MaxSize -> lists:foreach(fun del_file_and_dir/1, DelFiles), file:del_dir(UserDir), % In case it's empty, now. NewSize + SlotSize; true -> OldSize + SlotSize end. -spec delete_old_files(file:filename_all(), integer()) -> ok. delete_old_files(UserDir, CutOff) -> FileInfo = gather_file_info(UserDir), case [Path || {Path, _Size, Time} <- FileInfo, Time < CutOff] of [] -> ok; OldFiles -> lists:foreach(fun del_file_and_dir/1, OldFiles), file:del_dir(UserDir) % In case it's empty, now. end. -spec gather_file_info(file:filename_all()) -> [{binary(), non_neg_integer(), non_neg_integer()}]. gather_file_info(Dir) when is_binary(Dir) -> gather_file_info(binary_to_list(Dir)); gather_file_info(Dir) -> case file:list_dir(Dir) of {ok, Entries} -> lists:foldl(fun(Entry, Acc) -> Path = Dir ++ "/" ++ Entry, case file:read_file_info(Path, [{time, posix}]) of {ok, #file_info{type = directory}} -> gather_file_info(Path) ++ Acc; {ok, #file_info{type = regular, mtime = Time, size = Size}} -> [{Path, Size, Time} | Acc]; {ok, _Info} -> ?DEBUG("Won't stat(2) non-regular file ~s", [Path]), Acc; {error, Error} -> ?ERROR_MSG("Cannot stat(2) ~s: ~s", [Path, ?FORMAT(Error)]), Acc end end, [], Entries); {error, enoent} -> ?DEBUG("Directory ~s doesn't exist", [Dir]), []; {error, Error} -> ?ERROR_MSG("Cannot open directory ~s: ~s", [Dir, ?FORMAT(Error)]), [] end. -spec del_file_and_dir(file:name_all()) -> ok. del_file_and_dir(File) -> case file:delete(File) of ok -> ?INFO_MSG("Removed ~s", [File]), Dir = filename:dirname(File), case file:del_dir(Dir) of ok -> ?DEBUG("Removed ~s", [Dir]); {error, Error} -> ?DEBUG("Cannot remove ~s: ~s", [Dir, ?FORMAT(Error)]) end; {error, Error} -> ?WARNING_MSG("Cannot remove ~s: ~s", [File, ?FORMAT(Error)]) end. -spec secs_since_epoch() -> non_neg_integer(). secs_since_epoch() -> {MegaSecs, Secs, _MicroSecs} = os:timestamp(), MegaSecs * 1000000 + Secs. ejabberd-18.01/src/mod_privacy_sql.erl0000644000232200023220000003326113225664356020336 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_privacy_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_privacy_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_privacy). %% API -export([init/2, set_default/3, unset_default/2, set_lists/1, set_list/4, get_lists/2, get_list/3, remove_lists/2, remove_list/3, import/1, export/1]). -export([item_to_raw/1, raw_to_item/1]). -include("xmpp.hrl"). -include("mod_privacy.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. unset_default(LUser, LServer) -> case unset_default_privacy_list(LUser, LServer) of ok -> ok; _Err -> {error, db_failure} end. set_default(LUser, LServer, Name) -> F = fun () -> case get_privacy_list_names_t(LUser, LServer) of {selected, []} -> {error, notfound}; {selected, Names} -> case lists:member({Name}, Names) of true -> set_default_privacy_list(LUser, LServer, Name); false -> {error, notfound} end end end, transaction(LServer, F). remove_list(LUser, LServer, Name) -> F = fun () -> case get_default_privacy_list_t(LUser, LServer) of {selected, []} -> remove_privacy_list_t(LUser, LServer, Name); {selected, [{Default}]} -> if Name == Default -> {error, conflict}; true -> remove_privacy_list_t(LUser, LServer, Name) end end end, transaction(LServer, F). set_lists(#privacy{us = {LUser, LServer}, default = Default, lists = Lists}) -> F = fun() -> lists:foreach( fun({Name, List}) -> add_privacy_list(LUser, LServer, Name), {selected, [<<"id">>], [[I]]} = get_privacy_list_id_t(LUser, LServer, Name), RItems = lists:map(fun item_to_raw/1, List), set_privacy_list(I, RItems), if is_binary(Default) -> set_default_privacy_list( LUser, LServer, Default); true -> ok end end, Lists) end, transaction(LServer, F). set_list(LUser, LServer, Name, List) -> RItems = lists:map(fun item_to_raw/1, List), F = fun () -> ID = case get_privacy_list_id_t(LUser, LServer, Name) of {selected, []} -> add_privacy_list(LUser, LServer, Name), {selected, [{I}]} = get_privacy_list_id_t(LUser, LServer, Name), I; {selected, [{I}]} -> I end, set_privacy_list(ID, RItems) end, transaction(LServer, F). get_list(LUser, LServer, default) -> case get_default_privacy_list(LUser, LServer) of {selected, []} -> error; {selected, [{Default}]} -> get_list(LUser, LServer, Default); _Err -> {error, db_failure} end; get_list(LUser, LServer, Name) -> case get_privacy_list_data(LUser, LServer, Name) of {selected, []} -> error; {selected, RItems} -> {ok, {Name, lists:flatmap(fun raw_to_item/1, RItems)}}; _Err -> {error, db_failure} end. get_lists(LUser, LServer) -> case get_default_privacy_list(LUser, LServer) of {selected, Selected} -> Default = case Selected of [] -> none; [{DefName}] -> DefName end, case get_privacy_list_names(LUser, LServer) of {selected, Names} -> case lists:foldl( fun(_, {error, _} = Err) -> Err; ({Name}, Acc) -> case get_privacy_list_data(LUser, LServer, Name) of {selected, RItems} -> Items = lists:flatmap( fun raw_to_item/1, RItems), [{Name, Items}|Acc]; _Err -> {error, db_failure} end end, [], Names) of {error, Reason} -> {error, Reason}; Lists -> {ok, #privacy{default = Default, us = {LUser, LServer}, lists = Lists}} end; _Err -> {error, db_failure} end; _Err -> {error, db_failure} end. remove_lists(LUser, LServer) -> case del_privacy_lists(LUser, LServer) of ok -> ok; _Err -> {error, db_failure} end. export(Server) -> case catch ejabberd_sql:sql_query(jid:nameprep(Server), [<<"select id from privacy_list order by " "id desc limit 1;">>]) of {selected, [<<"id">>], [[I]]} -> put(id, binary_to_integer(I)); _ -> put(id, 0) end, [{privacy, fun(Host, #privacy{us = {LUser, LServer}, lists = Lists, default = Default}) when LServer == Host -> if Default /= none -> [?SQL("delete from privacy_default_list where" " username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT( "privacy_default_list", ["username=%(LUser)s", "server_host=%(LServer)s", "name=%(Default)s"])]; true -> [] end ++ lists:flatmap( fun({Name, List}) -> RItems = lists:map(fun item_to_raw/1, List), ID = get_id(), [?SQL("delete from privacy_list where" " username=%(LUser)s and %(LServer)H and" " name=%(Name)s;"), ?SQL_INSERT( "privacy_list", ["username=%(LUser)s", "server_host=%(LServer)s", "name=%(Name)s", "id=%(ID)d"]), ?SQL("delete from privacy_list_data where" " id=%(ID)d;")] ++ [?SQL("insert into privacy_list_data(id, t, " "value, action, ord, match_all, match_iq, " "match_message, match_presence_in, " "match_presence_out) " "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s," " %(Order)d, %(MatchAll)b, %(MatchIQ)b," " %(MatchMessage)b, %(MatchPresenceIn)b," " %(MatchPresenceOut)b);") || {SType, SValue, SAction, Order, MatchAll, MatchIQ, MatchMessage, MatchPresenceIn, MatchPresenceOut} <- RItems] end, Lists); (_Host, _R) -> [] end}]. get_id() -> ID = get(id), put(id, ID + 1), ID + 1. import(_) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== transaction(LServer, F) -> case ejabberd_sql:sql_transaction(LServer, F) of {atomic, Res} -> Res; {aborted, _Reason} -> {error, db_failure} end. raw_to_item({SType, SValue, SAction, Order, MatchAll, MatchIQ, MatchMessage, MatchPresenceIn, MatchPresenceOut} = Row) -> try {Type, Value} = case SType of <<"n">> -> {none, none}; <<"j">> -> JID = jid:decode(SValue), {jid, jid:tolower(JID)}; <<"g">> -> {group, SValue}; <<"s">> -> case SValue of <<"none">> -> {subscription, none}; <<"both">> -> {subscription, both}; <<"from">> -> {subscription, from}; <<"to">> -> {subscription, to} end end, Action = case SAction of <<"a">> -> allow; <<"d">> -> deny end, [#listitem{type = Type, value = Value, action = Action, order = Order, match_all = MatchAll, match_iq = MatchIQ, match_message = MatchMessage, match_presence_in = MatchPresenceIn, match_presence_out = MatchPresenceOut}] catch _:_ -> ?WARNING_MSG("failed to parse row: ~p", [Row]), [] end. item_to_raw(#listitem{type = Type, value = Value, action = Action, order = Order, match_all = MatchAll, match_iq = MatchIQ, match_message = MatchMessage, match_presence_in = MatchPresenceIn, match_presence_out = MatchPresenceOut}) -> {SType, SValue} = case Type of none -> {<<"n">>, <<"">>}; jid -> {<<"j">>, jid:encode(Value)}; group -> {<<"g">>, Value}; subscription -> case Value of none -> {<<"s">>, <<"none">>}; both -> {<<"s">>, <<"both">>}; from -> {<<"s">>, <<"from">>}; to -> {<<"s">>, <<"to">>} end end, SAction = case Action of allow -> <<"a">>; deny -> <<"d">> end, {SType, SValue, SAction, Order, MatchAll, MatchIQ, MatchMessage, MatchPresenceIn, MatchPresenceOut}. get_default_privacy_list(LUser, LServer) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(name)s from privacy_default_list " "where username=%(LUser)s and %(LServer)H")). get_default_privacy_list_t(LUser, LServer) -> ejabberd_sql:sql_query_t( ?SQL("select @(name)s from privacy_default_list " "where username=%(LUser)s and %(LServer)H")). get_privacy_list_names(LUser, LServer) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(name)s from privacy_list" " where username=%(LUser)s and %(LServer)H")). get_privacy_list_names_t(LUser, LServer) -> ejabberd_sql:sql_query_t( ?SQL("select @(name)s from privacy_list" " where username=%(LUser)s and %(LServer)H")). get_privacy_list_id_t(LUser, LServer, Name) -> ejabberd_sql:sql_query_t( ?SQL("select @(id)d from privacy_list" " where username=%(LUser)s and %(LServer)H and name=%(Name)s")). get_privacy_list_data(LUser, LServer, Name) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, " "@(match_iq)b, @(match_message)b, @(match_presence_in)b, " "@(match_presence_out)b from privacy_list_data " "where id =" " (select id from privacy_list" " where username=%(LUser)s and %(LServer)H and name=%(Name)s) " "order by ord")). set_default_privacy_list(LUser, LServer, Name) -> ?SQL_UPSERT_T( "privacy_default_list", ["!username=%(LUser)s", "!server_host=%(LServer)s", "name=%(Name)s"]). unset_default_privacy_list(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("delete from privacy_default_list" " where username=%(LUser)s and %(LServer)H")) of {updated, _} -> ok; Err -> Err end. remove_privacy_list_t(LUser, LServer, Name) -> case ejabberd_sql:sql_query_t( ?SQL("delete from privacy_list where" " username=%(LUser)s and %(LServer)H and name=%(Name)s")) of {updated, 0} -> {error, notfound}; {updated, _} -> ok; Err -> Err end. add_privacy_list(LUser, LServer, Name) -> ejabberd_sql:sql_query_t( ?SQL_INSERT( "privacy_list", ["username=%(LUser)s", "server_host=%(LServer)s", "name=%(Name)s"])). set_privacy_list(ID, RItems) -> ejabberd_sql:sql_query_t( ?SQL("delete from privacy_list_data where id=%(ID)d")), lists:foreach( fun({SType, SValue, SAction, Order, MatchAll, MatchIQ, MatchMessage, MatchPresenceIn, MatchPresenceOut}) -> ejabberd_sql:sql_query_t( ?SQL("insert into privacy_list_data(id, t, " "value, action, ord, match_all, match_iq, " "match_message, match_presence_in, match_presence_out) " "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s," " %(Order)d, %(MatchAll)b, %(MatchIQ)b," " %(MatchMessage)b, %(MatchPresenceIn)b," " %(MatchPresenceOut)b)")) end, RItems). del_privacy_lists(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("delete from privacy_list where username=%(LUser)s and %(LServer)H")) of {updated, _} -> case ejabberd_sql:sql_query( LServer, ?SQL("delete from privacy_default_list " "where username=%(LUser)s and %(LServer)H")) of {updated, _} -> ok; Err -> Err end; Err -> Err end. ejabberd-18.01/src/nodetree_dag.erl0000644000232200023220000001721513225664356017564 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : nodetree_dag.erl %%% Author : Brian Cully %%% Purpose : experimental support of XEP-248 %%% Created : 15 Jun 2009 by Brian Cully %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(nodetree_dag). -behaviour(gen_pubsub_nodetree). -author('bjc@kublai.com'). -include_lib("stdlib/include/qlc.hrl"). -include("pubsub.hrl"). -include("xmpp.hrl"). -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). -define(DEFAULT_NODETYPE, leaf). -define(DEFAULT_PARENTS, []). -define(DEFAULT_CHILDREN, []). init(Host, ServerHost, Opts) -> nodetree_tree:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> nodetree_tree:terminate(Host, ServerHost). set_node(#pubsub_node{nodeid = {Key, _}, owners = Owners, options = Options} = Node) -> Parents = find_opt(collection, ?DEFAULT_PARENTS, Options), case validate_parentage(Key, Owners, Parents) of true -> mnesia:write(Node#pubsub_node{parents = Parents}); Other -> Other end. create_node(Key, Node, Type, Owner, Options, Parents) -> OwnerJID = jid:tolower(jid:remove_resource(Owner)), case find_node(Key, Node) of false -> Nidx = pubsub_index:new(node), N = #pubsub_node{nodeid = oid(Key, Node), id = Nidx, type = Type, parents = Parents, owners = [OwnerJID], options = Options}, case set_node(N) of ok -> {ok, Nidx}; Other -> Other end; _ -> {error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)} end. delete_node(Key, Node) -> case find_node(Key, Node) of false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; Record -> lists:foreach(fun (#pubsub_node{options = Opts} = Child) -> NewOpts = remove_config_parent(Node, Opts), Parents = find_opt(collection, ?DEFAULT_PARENTS, NewOpts), ok = mnesia:write(pubsub_node, Child#pubsub_node{parents = Parents, options = NewOpts}, write) end, get_subnodes(Key, Node)), pubsub_index:free(node, Record#pubsub_node.id), mnesia:delete_object(pubsub_node, Record, write), [Record] end. options() -> nodetree_tree:options(). get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node) -> case find_node(Host, Node) of false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; Record -> Record end. get_node(Node) -> nodetree_tree:get_node(Node). get_nodes(Key, From) -> nodetree_tree:get_nodes(Key, From). get_nodes(Key) -> nodetree_tree:get_nodes(Key). get_parentnodes(Host, Node, _From) -> case find_node(Host, Node) of false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; #pubsub_node{parents = Parents} -> Q = qlc:q([N || #pubsub_node{nodeid = {NHost, NNode}} = N <- mnesia:table(pubsub_node), Parent <- Parents, Host == NHost, Parent == NNode]), qlc:e(Q) end. get_parentnodes_tree(Host, Node, _From) -> Pred = fun (NID, #pubsub_node{nodeid = {_, NNode}}) -> NID == NNode end, Tr = fun (#pubsub_node{parents = Parents}) -> Parents end, traversal_helper(Pred, Tr, Host, [Node]). get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node). get_subnodes(Host, <<>>) -> get_subnodes_helper(Host, <<>>); get_subnodes(Host, Node) -> case find_node(Host, Node) of false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; _ -> get_subnodes_helper(Host, Node) end. get_subnodes_helper(Host, Node) -> Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _}, parents = Parents} = N <- mnesia:table(pubsub_node), Host == NHost, lists:member(Node, Parents)]), qlc:e(Q). get_subnodes_tree(Host, Node, From) -> Pred = fun (NID, #pubsub_node{parents = Parents}) -> lists:member(NID, Parents) end, Tr = fun (#pubsub_node{nodeid = {_, N}}) -> [N] end, traversal_helper(Pred, Tr, 1, Host, [Node], [{0, [get_node(Host, Node, From)]}]). %%==================================================================== %% Internal functions %%==================================================================== oid(Key, Name) -> {Key, Name}. %% Key = jlib:jid() | host() %% Node = string() -spec find_node(Key :: mod_pubsub:hostPubsub(), Node :: mod_pubsub:nodeId()) -> mod_pubsub:pubsubNode() | false. find_node(Key, Node) -> case mnesia:read(pubsub_node, oid(Key, Node), read) of [] -> false; [Node] -> Node end. %% Key = jlib:jid() | host() %% Default = term() %% Options = [{Key = atom(), Value = term()}] find_opt(Key, Default, Options) -> case lists:keysearch(Key, 1, Options) of {value, {Key, Val}} -> Val; _ -> Default end. -spec traversal_helper(Pred :: fun(), Tr :: fun(), Host :: mod_pubsub:hostPubsub(), Nodes :: [mod_pubsub:nodeId(),...]) -> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]. traversal_helper(Pred, Tr, Host, Nodes) -> traversal_helper(Pred, Tr, 0, Host, Nodes, []). traversal_helper(_Pred, _Tr, _Depth, _Host, [], Acc) -> Acc; traversal_helper(Pred, Tr, Depth, Host, Nodes, Acc) -> Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _}} = N <- mnesia:table(pubsub_node), Node <- Nodes, Host == NHost, Pred(Node, N)]), Nodes = qlc:e(Q), IDs = lists:flatmap(Tr, Nodes), traversal_helper(Pred, Tr, Depth + 1, Host, IDs, [{Depth, Nodes} | Acc]). remove_config_parent(Node, Options) -> remove_config_parent(Node, Options, []). remove_config_parent(_Node, [], Acc) -> lists:reverse(Acc); remove_config_parent(Node, [{collection, Parents} | T], Acc) -> remove_config_parent(Node, T, [{collection, lists:delete(Node, Parents)} | Acc]); remove_config_parent(Node, [H | T], Acc) -> remove_config_parent(Node, T, [H | Acc]). -spec validate_parentage(Key :: mod_pubsub:hostPubsub(), Owners :: [ljid(),...], Parent_Nodes :: [mod_pubsub:nodeId()]) -> true | {error, xmlel()}. validate_parentage(_Key, _Owners, []) -> true; validate_parentage(Key, Owners, [[] | T]) -> validate_parentage(Key, Owners, T); validate_parentage(Key, Owners, [<<>> | T]) -> validate_parentage(Key, Owners, T); validate_parentage(Key, Owners, [ParentID | T]) -> case find_node(Key, ParentID) of false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; #pubsub_node{owners = POwners, options = POptions} -> NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions), MutualOwners = [O || O <- Owners, PO <- POwners, O == PO], case {MutualOwners, NodeType} of {[], _} -> {error, xmpp:err_forbidden()}; {_, collection} -> validate_parentage(Key, Owners, T); {_, _} -> {error, xmpp:err_not_allowed()} end end. ejabberd-18.01/src/mod_private_mnesia.erl0000644000232200023220000001002713225664356021003 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_private_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_private_mnesia). -behaviour(mod_private). %% API -export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2, use_cache/1, import/3]). -export([need_transform/1, transform/1]). -include("xmpp.hrl"). -include("mod_private.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, private_storage, [{disc_only_copies, [node()]}, {attributes, record_info(fields, private_storage)}]). use_cache(Host) -> case mnesia:table_info(private_storage, storage_type) of disc_only_copies -> gen_mod:get_module_opt( Host, mod_private, use_cache, ejabberd_config:use_cache(Host)); _ -> false end. set_data(LUser, LServer, Data) -> F = fun () -> lists:foreach( fun({XmlNS, Xmlel}) -> mnesia:write( #private_storage{ usns = {LUser, LServer, XmlNS}, xml = Xmlel}) end, Data) end, transaction(F). get_data(LUser, LServer, XmlNS) -> case mnesia:dirty_read(private_storage, {LUser, LServer, XmlNS}) of [#private_storage{xml = Storage_Xmlel}] -> {ok, Storage_Xmlel}; _ -> error end. get_all_data(LUser, LServer) -> case lists:flatten( mnesia:dirty_select(private_storage, [{#private_storage{usns = {LUser, LServer, '_'}, xml = '$1'}, [], ['$1']}])) of [] -> error; Res -> {ok, Res} end. del_data(LUser, LServer) -> F = fun () -> Namespaces = mnesia:select(private_storage, [{#private_storage{usns = {LUser, LServer, '$1'}, _ = '_'}, [], ['$$']}]), lists:foreach(fun ([Namespace]) -> mnesia:delete({private_storage, {LUser, LServer, Namespace}}) end, Namespaces) end, transaction(F). import(LServer, <<"private_storage">>, [LUser, XMLNS, XML, _TimeStamp]) -> El = #xmlel{} = fxml_stream:parse_element(XML), PS = #private_storage{usns = {LUser, LServer, XMLNS}, xml = El}, mnesia:dirty_write(PS). need_transform(#private_storage{usns = {U, S, NS}}) when is_list(U) orelse is_list(S) orelse is_list(NS) -> ?INFO_MSG("Mnesia table 'private_storage' will be converted to binary", []), true; need_transform(_) -> false. transform(#private_storage{usns = {U, S, NS}, xml = El} = R) -> R#private_storage{usns = {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(NS)}, xml = fxml:to_xmlel(El)}. %%%=================================================================== %%% Internal functions %%%=================================================================== transaction(F) -> case mnesia:transaction(F) of {atomic, Res} -> Res; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. ejabberd-18.01/src/node_club.erl0000644000232200023220000001333113225664356017071 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_club.erl %%% Author : Christophe Romain %%% Purpose : %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_club). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost). options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, {purge_offline, false}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, authorize}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, never}, {deliver_notifications, true}, {presence_based_delivery, false}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"delete-nodes">>, <<"delete-items">>, <<"instant-nodes">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"multi-items">>, <<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>, <<"subscription-notifications">>]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Removed) -> node_flat:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). ejabberd-18.01/src/ejabberd_s2s_in.erl0000644000232200023220000003301513225664356020153 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 12 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_s2s_in). -behaviour(xmpp_stream_in). -behaviour(xmpp_socket). %% xmpp_socket callbacks -export([start/2, start_link/2, socket_type/0]). %% ejabberd_listener callbacks -export([listen_opt_type/1]). %% xmpp_stream_in callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1, compress_methods/1, unauthenticated_stream_features/1, authenticated_stream_features/1, handle_stream_start/2, handle_stream_end/2, handle_stream_established/1, handle_auth_success/4, handle_auth_failure/4, handle_send/3, handle_recv/3, handle_cdata/2, handle_unauthenticated_packet/2, handle_authenticated_packet/2]). %% Hooks -export([handle_unexpected_info/2, handle_unexpected_cast/2, reject_unauthenticated_packet/2, process_closed/2]). %% API -export([stop/1, close/1, close/2, send/2, update_state/2, establish/1, host_up/1, host_down/1]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -type state() :: map(). -export_type([state/0]). %%%=================================================================== %%% API %%%=================================================================== start(SockData, Opts) -> case proplists:get_value(supervisor, Opts, true) of true -> case supervisor:start_child(ejabberd_s2s_in_sup, [SockData, Opts]) of {ok, undefined} -> ignore; Res -> Res end; _ -> xmpp_stream_in:start(?MODULE, [SockData, Opts], ejabberd_config:fsm_limit_opts(Opts)) end. start_link(SockData, Opts) -> xmpp_stream_in:start_link(?MODULE, [SockData, Opts], ejabberd_config:fsm_limit_opts(Opts)). close(Ref) -> xmpp_stream_in:close(Ref). close(Ref, Reason) -> xmpp_stream_in:close(Ref, Reason). stop(Ref) -> xmpp_stream_in:stop(Ref). socket_type() -> xml_stream. -spec send(pid(), xmpp_element()) -> ok; (state(), xmpp_element()) -> state(). send(Stream, Pkt) -> xmpp_stream_in:send(Stream, Pkt). -spec establish(state()) -> state(). establish(State) -> xmpp_stream_in:establish(State). -spec update_state(pid(), fun((state()) -> state()) | {module(), atom(), list()}) -> ok. update_state(Ref, Callback) -> xmpp_stream_in:cast(Ref, {update_state, Callback}). -spec host_up(binary()) -> ok. host_up(Host) -> ejabberd_hooks:add(s2s_in_closed, Host, ?MODULE, process_closed, 100), ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE, reject_unauthenticated_packet, 100), ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, handle_unexpected_info, 100), ejabberd_hooks:add(s2s_in_handle_cast, Host, ?MODULE, handle_unexpected_cast, 100). -spec host_down(binary()) -> ok. host_down(Host) -> ejabberd_hooks:delete(s2s_in_closed, Host, ?MODULE, process_closed, 100), ejabberd_hooks:delete(s2s_in_unauthenticated_packet, Host, ?MODULE, reject_unauthenticated_packet, 100), ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, handle_unexpected_info, 100), ejabberd_hooks:delete(s2s_in_handle_cast, Host, ?MODULE, handle_unexpected_cast, 100). %%%=================================================================== %%% Hooks %%%=================================================================== handle_unexpected_info(State, Info) -> ?WARNING_MSG("got unexpected info: ~p", [Info]), State. handle_unexpected_cast(State, Msg) -> ?WARNING_MSG("got unexpected cast: ~p", [Msg]), State. reject_unauthenticated_packet(State, _Pkt) -> Err = xmpp:serr_not_authorized(), send(State, Err). process_closed(State, _Reason) -> stop(State). %%%=================================================================== %%% xmpp_stream_in callbacks %%%=================================================================== tls_options(#{tls_options := TLSOpts, server_host := LServer}) -> ejabberd_s2s:tls_options(LServer, TLSOpts). tls_required(#{server_host := LServer}) -> ejabberd_s2s:tls_required(LServer). tls_verify(#{server_host := LServer}) -> ejabberd_s2s:tls_verify(LServer). tls_enabled(#{server_host := LServer}) -> ejabberd_s2s:tls_enabled(LServer). compress_methods(#{server_host := LServer}) -> case ejabberd_s2s:zlib_enabled(LServer) of true -> [<<"zlib">>]; false -> [] end. unauthenticated_stream_features(#{server_host := LServer}) -> ejabberd_hooks:run_fold(s2s_in_pre_auth_features, LServer, [], [LServer]). authenticated_stream_features(#{server_host := LServer}) -> ejabberd_hooks:run_fold(s2s_in_post_auth_features, LServer, [], [LServer]). handle_stream_start(_StreamStart, #{lserver := LServer} = State) -> case check_to(jid:make(LServer), State) of false -> send(State, xmpp:serr_host_unknown()); true -> ServerHost = ejabberd_router:host_of_route(LServer), State#{server_host => ServerHost} end. handle_stream_end(Reason, #{server_host := LServer} = State) -> State1 = State#{stop_reason => Reason}, ejabberd_hooks:run_fold(s2s_in_closed, LServer, State1, [Reason]). handle_stream_established(State) -> set_idle_timeout(State#{established => true}). handle_auth_success(RServer, Mech, _AuthModule, #{socket := Socket, ip := IP, auth_domains := AuthDomains, server_host := ServerHost, lserver := LServer} = State) -> ?INFO_MSG("(~s) Accepted inbound s2s ~s authentication ~s -> ~s (~s)", [xmpp_socket:pp(Socket), Mech, RServer, LServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), State1 = case ejabberd_s2s:allow_host(ServerHost, RServer) of true -> AuthDomains1 = sets:add_element(RServer, AuthDomains), State0 = change_shaper(State, RServer), State0#{auth_domains => AuthDomains1}; false -> State end, ejabberd_hooks:run_fold(s2s_in_auth_result, ServerHost, State1, [true, RServer]). handle_auth_failure(RServer, Mech, Reason, #{socket := Socket, ip := IP, server_host := ServerHost, lserver := LServer} = State) -> ?INFO_MSG("(~s) Failed inbound s2s ~s authentication ~s -> ~s (~s): ~s", [xmpp_socket:pp(Socket), Mech, RServer, LServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]), ejabberd_hooks:run_fold(s2s_in_auth_result, ServerHost, State, [false, RServer]). handle_unauthenticated_packet(Pkt, #{server_host := LServer} = State) -> ejabberd_hooks:run_fold(s2s_in_unauthenticated_packet, LServer, State, [Pkt]). handle_authenticated_packet(Pkt, #{server_host := LServer} = State) when not ?is_stanza(Pkt) -> ejabberd_hooks:run_fold(s2s_in_authenticated_packet, LServer, State, [Pkt]); handle_authenticated_packet(Pkt0, #{ip := {IP, _}} = State) -> Pkt = xmpp:put_meta(Pkt0, ip, IP), From = xmpp:get_from(Pkt), To = xmpp:get_to(Pkt), case check_from_to(From, To, State) of ok -> LServer = ejabberd_router:host_of_route(To#jid.lserver), State1 = ejabberd_hooks:run_fold(s2s_in_authenticated_packet, LServer, State, [Pkt]), {Pkt1, State2} = ejabberd_hooks:run_fold(s2s_receive_packet, LServer, {Pkt, State1}, []), case Pkt1 of drop -> ok; _ -> ejabberd_router:route(Pkt1) end, State2; {error, Err} -> send(State, Err) end. handle_cdata(Data, #{server_host := LServer} = State) -> ejabberd_hooks:run_fold(s2s_in_handle_cdata, LServer, State, [Data]). handle_recv(El, Pkt, #{server_host := LServer} = State) -> State1 = set_idle_timeout(State), ejabberd_hooks:run_fold(s2s_in_handle_recv, LServer, State1, [El, Pkt]). handle_send(Pkt, Result, #{server_host := LServer} = State) -> ejabberd_hooks:run_fold(s2s_in_handle_send, LServer, State, [Pkt, Result]). init([State, Opts]) -> Shaper = proplists:get_value(shaper, Opts, none), TLSOpts1 = lists:filter( fun({certfile, _}) -> true; ({ciphers, _}) -> true; ({dhfile, _}) -> true; ({cafile, _}) -> true; ({protocol_options, _}) -> true; (_) -> false end, Opts), TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of false -> [compression_none | TLSOpts1]; true -> TLSOpts1 end, State1 = State#{tls_options => TLSOpts2, auth_domains => sets:new(), xmlns => ?NS_SERVER, lang => ?MYLANG, server => ?MYNAME, lserver => ?MYNAME, server_host => ?MYNAME, established => false, shaper => Shaper}, ejabberd_hooks:run_fold(s2s_in_init, {ok, State1}, [Opts]). handle_call(Request, From, #{server_host := LServer} = State) -> ejabberd_hooks:run_fold(s2s_in_handle_call, LServer, State, [Request, From]). handle_cast({update_state, Fun}, State) -> case Fun of {M, F, A} -> erlang:apply(M, F, [State|A]); _ when is_function(Fun) -> Fun(State) end; handle_cast(Msg, #{server_host := LServer} = State) -> ejabberd_hooks:run_fold(s2s_in_handle_cast, LServer, State, [Msg]). handle_info(Info, #{server_host := LServer} = State) -> ejabberd_hooks:run_fold(s2s_in_handle_info, LServer, State, [Info]). terminate(Reason, #{auth_domains := AuthDomains, socket := Socket} = State) -> case maps:get(stop_reason, State, undefined) of {tls, _} = Err -> ?WARNING_MSG("(~s) Failed to secure inbound s2s connection: ~s", [xmpp_socket:pp(Socket), xmpp_stream_in:format_error(Err)]); _ -> ok end, case Reason of {process_limit, _} -> sets:fold( fun(Host, _) -> ejabberd_s2s:external_host_overloaded(Host) end, ok, AuthDomains); _ -> ok end. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec check_from_to(jid(), jid(), state()) -> ok | {error, stream_error()}. check_from_to(From, To, State) -> case check_from(From, State) of true -> case check_to(To, State) of true -> ok; false -> {error, xmpp:serr_host_unknown()} end; false -> {error, xmpp:serr_invalid_from()} end. -spec check_from(jid(), state()) -> boolean(). check_from(#jid{lserver = S1}, #{auth_domains := AuthDomains}) -> sets:is_element(S1, AuthDomains). -spec check_to(jid(), state()) -> boolean(). check_to(#jid{lserver = LServer}, _State) -> ejabberd_router:is_my_route(LServer). -spec set_idle_timeout(state()) -> state(). set_idle_timeout(#{server_host := LServer, established := true} = State) -> Timeout = ejabberd_s2s:get_idle_timeout(LServer), xmpp_stream_in:set_timeout(State, Timeout); set_idle_timeout(State) -> State. -spec change_shaper(state(), binary()) -> state(). change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State, RServer) -> Shaper = acl:match_rule(ServerHost, ShaperName, jid:make(RServer)), xmpp_stream_in:change_shaper(State, Shaper). -spec listen_opt_type(shaper) -> fun((any()) -> any()); (certfile) -> fun((binary()) -> binary()); (ciphers) -> fun((binary()) -> binary()); (dhfile) -> fun((binary()) -> binary()); (cafile) -> fun((binary()) -> binary()); (protocol_options) -> fun(([binary()]) -> binary()); (tls_compression) -> fun((boolean()) -> boolean()); (tls) -> fun((boolean()) -> boolean()); (supervisor) -> fun((boolean()) -> boolean()); (max_stanza_type) -> fun((timeout()) -> timeout()); (max_fsm_queue) -> fun((pos_integer()) -> pos_integer()); (atom()) -> [atom()]. listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1; listen_opt_type(certfile = Opt) -> fun(S) -> ?WARNING_MSG("Listening option '~s' for ~s is deprecated, use " "'certfiles' global option instead", [Opt, ?MODULE]), ejabberd_pkix:add_certfile(S), iolist_to_binary(S) end; listen_opt_type(ciphers) -> ejabberd_s2s:opt_type(s2s_ciphers); listen_opt_type(dhfile) -> ejabberd_s2s:opt_type(s2s_dhfile); listen_opt_type(cafile) -> ejabberd_s2s:opt_type(s2s_cafile); listen_opt_type(protocol_options) -> ejabberd_s2s:opt_type(s2s_protocol_options); listen_opt_type(tls_compression) -> ejabberd_s2s:opt_type(s2s_tls_compression); listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(max_stanza_size) -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; listen_opt_type(max_fsm_queue) -> fun(I) when is_integer(I), I>0 -> I end; listen_opt_type(_) -> [shaper, certfile, ciphers, dhfile, cafile, protocol_options, tls_compression, tls, max_fsm_queue]. ejabberd-18.01/src/mod_roster_riak.erl0000644000232200023220000001002413225664356020316 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_roster_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_roster_riak). -behaviour(mod_roster). %% API -export([init/2, read_roster_version/2, write_roster_version/4, get_roster/2, get_roster_item/3, create_roster/1, roster_subscribe/4, remove_user/2, update_roster/4, del_roster/3, read_subscription_and_groups/3, transaction/2, import/3]). -include("mod_roster.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. read_roster_version(LUser, LServer) -> case ejabberd_riak:get(roster_version, roster_version_schema(), {LUser, LServer}) of {ok, #roster_version{version = V}} -> {ok, V}; _Err -> error end. write_roster_version(LUser, LServer, _InTransaction, Ver) -> US = {LUser, LServer}, ejabberd_riak:put(#roster_version{us = US, version = Ver}, roster_version_schema()). get_roster(LUser, LServer) -> case ejabberd_riak:get_by_index(roster, roster_schema(), <<"us">>, {LUser, LServer}) of {ok, Items} -> {ok, Items}; _Err -> error end. roster_subscribe(LUser, LServer, _LJID, Item) -> ejabberd_riak:put(Item, roster_schema(), [{'2i', [{<<"us">>, {LUser, LServer}}]}]). transaction(_LServer, F) -> {atomic, F()}. get_roster_item(LUser, LServer, LJID) -> case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of {ok, I} -> {ok, I}; {error, _} -> error end. remove_user(LUser, LServer) -> ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer}). update_roster(LUser, LServer, _LJID, Item) -> ejabberd_riak:put(Item, roster_schema(), [{'2i', [{<<"us">>, {LUser, LServer}}]}]). del_roster(LUser, LServer, LJID) -> ejabberd_riak:delete(roster, {LUser, LServer, LJID}). read_subscription_and_groups(LUser, LServer, LJID) -> case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of {ok, #roster{subscription = Subscription, groups = Groups}} -> {ok, {Subscription, Groups}}; _ -> error end. create_roster(#roster{us = {LUser, LServer}} = RItem) -> ejabberd_riak:put( RItem, roster_schema(), [{'2i', [{<<"us">>, {LUser, LServer}}]}]). import(_LServer, <<"rosterusers">>, RosterItem) -> {LUser, LServer} = RosterItem#roster.us, ejabberd_riak:put(RosterItem, roster_schema(), [{'2i', [{<<"us">>, {LUser, LServer}}]}]); import(LServer, <<"roster_version">>, [LUser, Ver]) -> RV = #roster_version{us = {LUser, LServer}, version = Ver}, ejabberd_riak:put(RV, roster_version_schema()). %%%=================================================================== %%% Internal functions %%%=================================================================== roster_schema() -> {record_info(fields, roster), #roster{}}. roster_version_schema() -> {record_info(fields, roster_version), #roster_version{}}. ejabberd-18.01/src/translate.erl0000644000232200023220000002123413225664356017135 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : translate.erl %%% Author : Alexey Shchepin %%% Purpose : Localization helper %%% Created : 6 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(translate). -author('alexey@process-one.net'). -behaviour(gen_server). -export([start_link/0, reload/0, translate/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("kernel/include/file.hrl"). -define(ZERO_DATETIME, {{0,0,0}, {0,0,0}}). -record(state, {}). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> process_flag(trap_exit, true), load(), xmpp:set_tr_callback({?MODULE, translate}), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> xmpp:set_tr_callback(undefined). code_change(_OldVsn, State, _Extra) -> {ok, State}. -spec reload() -> ok. reload() -> load(true). -spec load() -> ok. load() -> load(false). -spec load(boolean()) -> ok. load(ForceCacheRebuild) -> {MsgsDirMTime, MsgsDir} = get_msg_dir(), {CacheMTime, CacheFile} = get_cache_file(), {FilesMTime, MsgFiles} = get_msg_files(MsgsDir), LastModified = lists:max([MsgsDirMTime, FilesMTime]), if ForceCacheRebuild orelse CacheMTime < LastModified -> load(MsgFiles, MsgsDir), dump_to_file(CacheFile); true -> case ets:file2tab(CacheFile) of {ok, _} -> ok; {error, {read_error, {file_error, _, enoent}}} -> load(MsgFiles, MsgsDir); {error, {read_error, {file_error, _, Reason}}} -> ?WARNING_MSG("Failed to read translation cache from ~s: ~s", [CacheFile, file:format_error(Reason)]), load(MsgFiles, MsgsDir); {error, Reason} -> ?WARNING_MSG("Failed to read translation cache from ~s: ~p", [CacheFile, Reason]), load(MsgFiles, MsgsDir) end end. -spec load([file:filename()], file:filename()) -> ok. load(Files, Dir) -> try ets:new(translations, [named_table, public]) catch _:badarg -> ok end, case Files of [] -> ?WARNING_MSG("No translation files found in ~s, " "check directory access", [Dir]); _ -> ets:delete_all_objects(translations), ?INFO_MSG("Building translation cache, this may take a while", []), lists:foreach( fun(File) -> BaseName = filename:basename(File), Lang = str:to_lower(filename:rootname(BaseName)), load_file(iolist_to_binary(Lang), File) end, Files) end. load_file(Lang, File) -> case file:open(File, [read]) of {ok, Fd} -> io:setopts(Fd, [{encoding,latin1}]), load_file_loop(Fd, 1, File, Lang), file:close(Fd); {error, Error} -> ExitText = iolist_to_binary([File, ": ", file:format_error(Error)]), ?ERROR_MSG("Problem loading translation file ~n~s", [ExitText]), exit(ExitText) end. load_file_loop(Fd, Line, File, Lang) -> case io:read(Fd, '', Line) of {ok,{Orig, Trans}, NextLine} -> Trans1 = case Trans of <<"">> -> Orig; _ -> Trans end, ets:insert(translations, {{Lang, iolist_to_binary(Orig)}, iolist_to_binary(Trans1)}), load_file_loop(Fd, NextLine, File, Lang); {ok,_, _NextLine} -> ExitText = iolist_to_binary([File, " approximately in the line ", Line]), ?ERROR_MSG("Problem loading translation file ~n~s", [ExitText]), exit(ExitText); {error, {_LineNumber, erl_parse, _ParseMessage} = Reason} -> ExitText = iolist_to_binary([File, " approximately in the line ", file:format_error(Reason)]), ?ERROR_MSG("Problem loading translation file ~n~s", [ExitText]), exit(ExitText); {error, Reason} -> ExitText = iolist_to_binary([File, ": ", file:format_error(Reason)]), ?ERROR_MSG("Problem loading translation file ~n~s", [ExitText]), exit(ExitText); {eof,_Line} -> ok end. -spec translate(binary(), binary()) -> binary(). translate(Lang, Msg) -> LLang = ascii_tolower(Lang), case ets:lookup(translations, {LLang, Msg}) of [{_, Trans}] -> Trans; _ -> ShortLang = case str:tokens(LLang, <<"-">>) of [] -> LLang; [SL | _] -> SL end, case ShortLang of <<"en">> -> Msg; LLang -> translate(Msg); _ -> case ets:lookup(translations, {ShortLang, Msg}) of [{_, Trans}] -> Trans; _ -> translate(Msg) end end end. translate(Msg) -> case ?MYLANG of <<"en">> -> Msg; Lang -> LLang = ascii_tolower(Lang), case ets:lookup(translations, {LLang, Msg}) of [{_, Trans}] -> Trans; _ -> ShortLang = case str:tokens(LLang, <<"-">>) of [] -> LLang; [SL | _] -> SL end, case ShortLang of <<"en">> -> Msg; Lang -> Msg; _ -> case ets:lookup(translations, {ShortLang, Msg}) of [{_, Trans}] -> Trans; _ -> Msg end end end end. ascii_tolower(B) -> iolist_to_binary(ascii_tolower_s(binary_to_list(B))). ascii_tolower_s([C | Cs]) when C >= $A, C =< $Z -> [C + ($a - $A) | ascii_tolower_s(Cs)]; ascii_tolower_s([C | Cs]) -> [C | ascii_tolower_s(Cs)]; ascii_tolower_s([]) -> []. -spec get_msg_dir() -> {calendar:datetime(), file:filename()}. get_msg_dir() -> Dir = case os:getenv("EJABBERD_MSGS_PATH") of false -> case code:priv_dir(ejabberd) of {error, _} -> ?MSGS_DIR; Path -> filename:join([Path, "msgs"]) end; Path -> Path end, case file:read_file_info(Dir) of {ok, #file_info{mtime = MTime}} -> {MTime, Dir}; {error, Reason} -> ?ERROR_MSG("Failed to read directory ~s: ~s", [Dir, file:format_error(Reason)]), {?ZERO_DATETIME, Dir} end. -spec get_msg_files(file:filename()) -> {calendar:datetime(), [file:filename()]}. get_msg_files(MsgsDir) -> Res = filelib:fold_files( MsgsDir, ".+\\.msg", false, fun(File, {MTime, Files} = Acc) -> case file:read_file_info(File) of {ok, #file_info{mtime = Time}} -> {lists:max([MTime, Time]), [File|Files]}; {error, Reason} -> ?ERROR_MSG("Failed to read translation file ~s: ~s", [File, file:format_error(Reason)]), Acc end end, {?ZERO_DATETIME, []}), case Res of {_, []} -> case file:list_dir(MsgsDir) of {ok, _} -> ok; {error, Reason} -> ?ERROR_MSG("Failed to read directory ~s: ~s", [MsgsDir, file:format_error(Reason)]) end; _ -> ok end, Res. -spec get_cache_file() -> {calendar:datetime(), file:filename()}. get_cache_file() -> MnesiaDir = mnesia:system_info(directory), CacheFile = filename:join(MnesiaDir, "translations.cache"), CacheMTime = case file:read_file_info(CacheFile) of {ok, #file_info{mtime = Time}} -> Time; {error, _} -> ?ZERO_DATETIME end, {CacheMTime, CacheFile}. -spec dump_to_file(file:filename()) -> ok. dump_to_file(CacheFile) -> case ets:tab2file(translations, CacheFile) of ok -> ok; {error, Reason} -> ?WARNING_MSG("Failed to create translation cache in ~s: ~p", [CacheFile, Reason]) end. ejabberd-18.01/src/ejabberd_auth_mnesia.erl0000644000232200023220000001774213225664356021264 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_auth_mnesia.erl %%% Author : Alexey Shchepin %%% Purpose : Authentification via mnesia %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_auth_mnesia). -compile([{parse_transform, ejabberd_sql_pt}]). -author('alexey@process-one.net'). -behaviour(ejabberd_auth). -export([start/1, stop/1, set_password/3, try_register/3, get_users/2, init_db/0, count_users/2, get_password/2, remove_user/2, store_type/1, import/2, plain_password_required/1, use_cache/1]). -export([need_transform/1, transform/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_auth.hrl"). -record(reg_users_counter, {vhost = <<"">> :: binary(), count = 0 :: integer() | '$1'}). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host) -> init_db(), update_reg_users_counter_table(Host), ok. stop(_Host) -> ok. init_db() -> ejabberd_mnesia:create(?MODULE, passwd, [{disc_only_copies, [node()]}, {attributes, record_info(fields, passwd)}]), ejabberd_mnesia:create(?MODULE, reg_users_counter, [{ram_copies, [node()]}, {attributes, record_info(fields, reg_users_counter)}]). update_reg_users_counter_table(Server) -> Set = get_users(Server, []), Size = length(Set), LServer = jid:nameprep(Server), F = fun () -> mnesia:write(#reg_users_counter{vhost = LServer, count = Size}) end, mnesia:sync_dirty(F). use_cache(Host) -> case mnesia:table_info(passwd, storage_type) of disc_only_copies -> ejabberd_config:get_option( {auth_use_cache, Host}, ejabberd_config:use_cache(Host)); _ -> false end. plain_password_required(Server) -> store_type(Server) == scram. store_type(Server) -> ejabberd_auth:password_format(Server). set_password(User, Server, Password) -> US = {User, Server}, F = fun () -> mnesia:write(#passwd{us = US, password = Password}) end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. try_register(User, Server, Password) -> US = {User, Server}, F = fun () -> case mnesia:read({passwd, US}) of [] -> mnesia:write(#passwd{us = US, password = Password}), mnesia:dirty_update_counter(reg_users_counter, Server, 1), ok; [_] -> {error, exists} end end, case mnesia:transaction(F) of {atomic, Res} -> Res; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. get_users(Server, []) -> mnesia:dirty_select(passwd, [{#passwd{us = '$1', _ = '_'}, [{'==', {element, 2, '$1'}, Server}], ['$1']}]); get_users(Server, [{from, Start}, {to, End}]) when is_integer(Start) and is_integer(End) -> get_users(Server, [{limit, End - Start + 1}, {offset, Start}]); get_users(Server, [{limit, Limit}, {offset, Offset}]) when is_integer(Limit) and is_integer(Offset) -> case get_users(Server, []) of [] -> []; Users -> Set = lists:keysort(1, Users), L = length(Set), Start = if Offset < 1 -> 1; Offset > L -> L; true -> Offset end, lists:sublist(Set, Start, Limit) end; get_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) -> Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)], lists:keysort(1, Set); get_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}]) when is_binary(Prefix) and is_integer(Start) and is_integer(End) -> get_users(Server, [{prefix, Prefix}, {limit, End - Start + 1}, {offset, Start}]); get_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) when is_binary(Prefix) and is_integer(Limit) and is_integer(Offset) -> case [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)] of [] -> []; Users -> Set = lists:keysort(1, Users), L = length(Set), Start = if Offset < 1 -> 1; Offset > L -> L; true -> Offset end, lists:sublist(Set, Start, Limit) end; get_users(Server, _) -> get_users(Server, []). count_users(Server, []) -> case mnesia:dirty_select( reg_users_counter, [{#reg_users_counter{vhost = Server, count = '$1'}, [], ['$1']}]) of [Count] -> Count; _ -> 0 end; count_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) -> Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)], length(Set); count_users(Server, _) -> count_users(Server, []). get_password(User, Server) -> case mnesia:dirty_read(passwd, {User, Server}) of [#passwd{password = Password}] -> {ok, Password}; _ -> error end. remove_user(User, Server) -> US = {User, Server}, F = fun () -> mnesia:delete({passwd, US}), mnesia:dirty_update_counter(reg_users_counter, Server, -1), ok end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. need_transform(#reg_users_counter{}) -> false; need_transform(#passwd{us = {U, S}, password = Pass}) -> if is_binary(Pass) -> case store_type(S) of scram -> ?INFO_MSG("Passwords in Mnesia table 'passwd' " "will be SCRAM'ed", []), true; plain -> false end; is_record(Pass, scram) -> case store_type(S) of scram -> false; plain -> ?WARNING_MSG("Some passwords were stored in the database " "as SCRAM, but 'auth_password_format' " "is not configured as 'scram': some " "authentication mechanisms such as DIGEST-MD5 " "would *fail*", []), false end; is_list(U) orelse is_list(S) orelse is_list(Pass) -> ?INFO_MSG("Mnesia table 'passwd' will be converted to binary", []), true end. transform(#passwd{us = {U, S}, password = Pass} = R) when is_list(U) orelse is_list(S) orelse is_list(Pass) -> NewUS = {iolist_to_binary(U), iolist_to_binary(S)}, NewPass = case Pass of #scram{storedkey = StoredKey, serverkey = ServerKey, salt = Salt} -> Pass#scram{ storedkey = iolist_to_binary(StoredKey), serverkey = iolist_to_binary(ServerKey), salt = iolist_to_binary(Salt)}; _ -> iolist_to_binary(Pass) end, transform(R#passwd{us = NewUS, password = NewPass}); transform(#passwd{us = {U, S}, password = Password} = P) when is_binary(Password) -> case store_type(S) of scram -> case jid:resourceprep(Password) of error -> ?ERROR_MSG("SASLprep failed for password of user ~s@~s", [U, S]), P; _ -> Scram = ejabberd_auth:password_to_scram(Password), P#passwd{password = Scram} end; plain -> P end; transform(#passwd{password = Password} = P) when is_record(Password, scram) -> P. import(LServer, [LUser, Password, _TimeStamp]) -> mnesia:dirty_write( #passwd{us = {LUser, LServer}, password = Password}). ejabberd-18.01/src/mod_blocking.erl0000644000232200023220000002113713225664356017571 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_blocking.erl %%% Author : Stephan Maka %%% Purpose : XEP-0191: Simple Communications Blocking %%% Created : 24 Aug 2008 by Stephan Maka %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_blocking). -behaviour(gen_mod). -protocol({xep, 191, '1.2'}). -export([start/2, stop/1, reload/3, process_iq/1, mod_opt_type/1, depends/2, disco_features/5]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_privacy.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING, ?MODULE, process_iq, IQDisc). stop(Host) -> ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING). reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING, ?MODULE, process_iq, IQDisc); true -> ok end. depends(_Host, _Opts) -> [{mod_privacy, hard}]. -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. disco_features({error, Err}, _From, _To, _Node, _Lang) -> {error, Err}; disco_features(empty, _From, _To, <<"">>, _Lang) -> {result, [?NS_BLOCKING]}; disco_features({result, Feats}, _From, _To, <<"">>, _Lang) -> {result, [?NS_BLOCKING|Feats]}; disco_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec process_iq(iq()) -> iq(). process_iq(#iq{type = Type, from = #jid{luser = U, lserver = S}, to = #jid{luser = U, lserver = S}} = IQ) -> case Type of get -> process_iq_get(IQ); set -> process_iq_set(IQ) end; process_iq(#iq{lang = Lang} = IQ) -> Txt = <<"Query to another users is forbidden">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)). -spec process_iq_get(iq()) -> iq(). process_iq_get(#iq{sub_els = [#block_list{}]} = IQ) -> process_get(IQ); process_iq_get(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_iq_set(iq()) -> iq(). process_iq_set(#iq{lang = Lang, sub_els = [SubEl]} = IQ) -> case SubEl of #block{items = []} -> Txt = <<"No items found in this query">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); #block{items = Items} -> JIDs = [jid:tolower(Item) || Item <- Items], process_block(IQ, JIDs); #unblock{items = []} -> process_unblock_all(IQ); #unblock{items = Items} -> JIDs = [jid:tolower(Item) || Item <- Items], process_unblock(IQ, JIDs); _ -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)) end. -spec listitems_to_jids([listitem()], [ljid()]) -> [ljid()]. listitems_to_jids([], JIDs) -> JIDs; listitems_to_jids([#listitem{type = jid, action = deny, value = JID} = Item | Items], JIDs) -> Match = case Item of #listitem{match_all = true} -> true; #listitem{match_iq = true, match_message = true, match_presence_in = true, match_presence_out = true} -> true; _ -> false end, if Match -> listitems_to_jids(Items, [JID | JIDs]); true -> listitems_to_jids(Items, JIDs) end; % Skip Privacy List items than cannot be mapped to Blocking items listitems_to_jids([_ | Items], JIDs) -> listitems_to_jids(Items, JIDs). -spec process_block(iq(), [ljid()]) -> iq(). process_block(#iq{from = From} = IQ, LJIDs) -> #jid{luser = LUser, lserver = LServer} = From, case mod_privacy:get_user_list(LUser, LServer, default) of {error, _} -> err_db_failure(IQ); Res -> {Name, List} = case Res of error -> {<<"Blocked contacts">>, []}; {ok, NameList} -> NameList end, AlreadyBlocked = listitems_to_jids(List, []), NewList = lists:foldr( fun(LJID, List1) -> case lists:member(LJID, AlreadyBlocked) of true -> List1; false -> [#listitem{type = jid, value = LJID, action = deny, order = 0, match_all = true}|List1] end end, List, LJIDs), case mod_privacy:set_list(LUser, LServer, Name, NewList) of ok -> case (if Res == error -> mod_privacy:set_default_list( LUser, LServer, Name); true -> ok end) of ok -> mod_privacy:push_list_update(From, Name), Items = [jid:make(LJID) || LJID <- LJIDs], broadcast_event(From, #block{items = Items}), xmpp:make_iq_result(IQ); {error, notfound} -> ?ERROR_MSG("Failed to set default list '~s': " "the list should exist, but not found", [Name]), err_db_failure(IQ); {error, _} -> err_db_failure(IQ) end; {error, _} -> err_db_failure(IQ) end end. -spec process_unblock_all(iq()) -> iq(). process_unblock_all(#iq{from = From} = IQ) -> #jid{luser = LUser, lserver = LServer} = From, case mod_privacy:get_user_list(LUser, LServer, default) of {ok, {Name, List}} -> NewList = lists:filter( fun(#listitem{action = A}) -> A /= deny end, List), case mod_privacy:set_list(LUser, LServer, Name, NewList) of ok -> mod_privacy:push_list_update(From, Name), broadcast_event(From, #unblock{}), xmpp:make_iq_result(IQ); {error, _} -> err_db_failure(IQ) end; error -> broadcast_event(From, #unblock{}), xmpp:make_iq_result(IQ); {error, _} -> err_db_failure(IQ) end. -spec process_unblock(iq(), [ljid()]) -> iq(). process_unblock(#iq{from = From} = IQ, LJIDs) -> #jid{luser = LUser, lserver = LServer} = From, case mod_privacy:get_user_list(LUser, LServer, default) of {ok, {Name, List}} -> NewList = lists:filter( fun(#listitem{action = deny, type = jid, value = LJID}) -> not lists:member(LJID, LJIDs); (_) -> true end, List), case mod_privacy:set_list(LUser, LServer, Name, NewList) of ok -> mod_privacy:push_list_update(From, Name), Items = [jid:make(LJID) || LJID <- LJIDs], broadcast_event(From, #unblock{items = Items}), xmpp:make_iq_result(IQ); {error, _} -> err_db_failure(IQ) end; error -> Items = [jid:make(LJID) || LJID <- LJIDs], broadcast_event(From, #unblock{items = Items}), xmpp:make_iq_result(IQ); {error, _} -> err_db_failure(IQ) end. -spec broadcast_event(jid(), block() | unblock()) -> ok. broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) -> lists:foreach( fun(R) -> To = jid:replace_resource(From, R), IQ = #iq{type = set, from = From, to = To, id = <<"push", (randoms:get_string())/binary>>, sub_els = [Event]}, ejabberd_router:route(IQ) end, ejabberd_sm:get_user_resources(LUser, LServer)). -spec process_get(iq()) -> iq(). process_get(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) -> case mod_privacy:get_user_list(LUser, LServer, default) of {ok, {_, List}} -> LJIDs = listitems_to_jids(List, []), Items = [jid:make(J) || J <- LJIDs], xmpp:make_iq_result(IQ, #block_list{items = Items}); error -> xmpp:make_iq_result(IQ, #block_list{}); {error, _} -> err_db_failure(IQ) end. err_db_failure(#iq{lang = Lang} = IQ) -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)). mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(_) -> [iqdisc]. ejabberd-18.01/src/ejabberd_hooks.erl0000644000232200023220000003240313225664356020101 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_hooks.erl %%% Author : Alexey Shchepin %%% Purpose : Manage hooks %%% Created : 8 Aug 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_hooks). -author('alexey@process-one.net'). -behaviour(gen_server). %% External exports -export([start_link/0, add/3, add/4, add/5, add_dist/5, add_dist/6, delete/3, delete/4, delete/5, delete_dist/5, delete_dist/6, run/2, run/3, run_fold/3, run_fold/4, get_handlers/2]). -export([delete_all_hooks/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, code_change/3, handle_info/2, terminate/2]). -include("logger.hrl"). -record(state, {}). -type local_hook() :: { Seq :: integer(), Module :: atom(), Function :: atom()}. -type distributed_hook() :: { Seq :: integer(), Node :: atom(), Module :: atom(), Function :: atom()}. %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []). -spec add(atom(), fun(), number()) -> ok. %% @doc See add/4. add(Hook, Function, Seq) when is_function(Function) -> add(Hook, global, undefined, Function, Seq). -spec add(atom(), HostOrModule :: binary() | atom(), fun() | atom() , number()) -> ok. add(Hook, Host, Function, Seq) when is_function(Function) -> add(Hook, Host, undefined, Function, Seq); %% @doc Add a module and function to this hook. %% The integer sequence is used to sort the calls: low number is called before high number. add(Hook, Module, Function, Seq) -> add(Hook, global, Module, Function, Seq). -spec add(atom(), binary() | global, atom(), atom() | fun(), number()) -> ok. add(Hook, Host, Module, Function, Seq) -> gen_server:call(ejabberd_hooks, {add, Hook, Host, Module, Function, Seq}). -spec add_dist(atom(), atom(), atom(), atom() | fun(), number()) -> ok. add_dist(Hook, Node, Module, Function, Seq) -> gen_server:call(ejabberd_hooks, {add, Hook, global, Node, Module, Function, Seq}). -spec add_dist(atom(), binary() | global, atom(), atom(), atom() | fun(), number()) -> ok. add_dist(Hook, Host, Node, Module, Function, Seq) -> gen_server:call(ejabberd_hooks, {add, Hook, Host, Node, Module, Function, Seq}). -spec delete(atom(), fun(), number()) -> ok. %% @doc See del/4. delete(Hook, Function, Seq) when is_function(Function) -> delete(Hook, global, undefined, Function, Seq). -spec delete(atom(), binary() | atom(), atom() | fun(), number()) -> ok. delete(Hook, Host, Function, Seq) when is_function(Function) -> delete(Hook, Host, undefined, Function, Seq); %% @doc Delete a module and function from this hook. %% It is important to indicate exactly the same information than when the call was added. delete(Hook, Module, Function, Seq) -> delete(Hook, global, Module, Function, Seq). -spec delete(atom(), binary() | global, atom(), atom() | fun(), number()) -> ok. delete(Hook, Host, Module, Function, Seq) -> gen_server:call(ejabberd_hooks, {delete, Hook, Host, Module, Function, Seq}). -spec delete_dist(atom(), atom(), atom(), atom() | fun(), number()) -> ok. delete_dist(Hook, Node, Module, Function, Seq) -> delete_dist(Hook, global, Node, Module, Function, Seq). -spec delete_dist(atom(), binary() | global, atom(), atom(), atom() | fun(), number()) -> ok. delete_dist(Hook, Host, Node, Module, Function, Seq) -> gen_server:call(ejabberd_hooks, {delete, Hook, Host, Node, Module, Function, Seq}). -spec delete_all_hooks() -> true. %% @doc Primarily for testing / instrumentation delete_all_hooks() -> gen_server:call(ejabberd_hooks, {delete_all}). -spec get_handlers(atom(), binary() | global) -> [local_hook() | distributed_hook()]. %% @doc Returns currently set handler for hook name get_handlers(Hookname, Host) -> gen_server:call(ejabberd_hooks, {get_handlers, Hookname, Host}). -spec run(atom(), list()) -> ok. %% @doc Run the calls of this hook in order, don't care about function results. %% If a call returns stop, no more calls are performed. run(Hook, Args) -> run(Hook, global, Args). -spec run(atom(), binary() | global, list()) -> ok. run(Hook, Host, Args) -> case ets:lookup(hooks, {Hook, Host}) of [{_, Ls}] -> run1(Ls, Hook, Args); [] -> ok end. -spec run_fold(atom(), any(), list()) -> any(). %% @doc Run the calls of this hook in order. %% The arguments passed to the function are: [Val | Args]. %% The result of a call is used as Val for the next call. %% If a call returns 'stop', no more calls are performed and 'stopped' is returned. %% If a call returns {stop, NewVal}, no more calls are performed and NewVal is returned. run_fold(Hook, Val, Args) -> run_fold(Hook, global, Val, Args). -spec run_fold(atom(), binary() | global, any(), list()) -> any(). run_fold(Hook, Host, Val, Args) -> case ets:lookup(hooks, {Hook, Host}) of [{_, Ls}] -> run_fold1(Ls, Hook, Val, Args); [] -> Val end. %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([]) -> ets:new(hooks, [named_table, {read_concurrency, true}]), {ok, #state{}}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Module, Function}, Reply = handle_add(Hook, Host, HookFormat), {reply, Reply, State}; handle_call({add, Hook, Host, Node, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Node, Module, Function}, Reply = handle_add(Hook, Host, HookFormat), {reply, Reply, State}; handle_call({delete, Hook, Host, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Module, Function}, Reply = handle_delete(Hook, Host, HookFormat), {reply, Reply, State}; handle_call({delete, Hook, Host, Node, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Node, Module, Function}, Reply = handle_delete(Hook, Host, HookFormat), {reply, Reply, State}; handle_call({get_handlers, Hook, Host}, _From, State) -> Reply = case ets:lookup(hooks, {Hook, Host}) of [{_, Handlers}] -> Handlers; [] -> [] end, {reply, Reply, State}; handle_call({delete_all}, _From, State) -> Reply = ets:delete_all_objects(hooks), {reply, Reply, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. -spec handle_add(atom(), atom(), local_hook() | distributed_hook()) -> ok. %% in-memory storage operation: Handle adding hook in ETS table handle_add(Hook, Host, El) -> case ets:lookup(hooks, {Hook, Host}) of [{_, Ls}] -> case lists:member(El, Ls) of true -> ok; false -> NewLs = lists:merge(Ls, [El]), ets:insert(hooks, {{Hook, Host}, NewLs}), ok end; [] -> NewLs = [El], ets:insert(hooks, {{Hook, Host}, NewLs}), ok end. -spec handle_delete(atom(), atom(), local_hook() | distributed_hook()) -> ok. %% in-memory storage operation: Handle deleting hook from ETS table handle_delete(Hook, Host, El) -> case ets:lookup(hooks, {Hook, Host}) of [{_, Ls}] -> NewLs = lists:delete(El, Ls), ets:insert(hooks, {{Hook, Host}, NewLs}), ok; [] -> ok end. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(_Msg, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info(_Info, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- -spec run1([local_hook()|distributed_hook()], atom(), list()) -> ok. run1([], _Hook, _Args) -> ok; %% Run distributed hook on target node. %% It is not attempted again in case of failure. Next hook will be executed run1([{_Seq, Node, Module, Function} | Ls], Hook, Args) -> %% MR: Should we have a safe rpc, like we have a safe apply or is bad_rpc enough ? case ejabberd_cluster:call(Node, Module, Function, Args) of timeout -> ?ERROR_MSG("Timeout on RPC to ~p~nrunning hook: ~p", [Node, {Hook, Args}]), run1(Ls, Hook, Args); {badrpc, Reason} -> ?ERROR_MSG("Bad RPC error to ~p: ~p~nrunning hook: ~p", [Node, Reason, {Hook, Args}]), run1(Ls, Hook, Args); stop -> ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" "Stop.", [self(), node(), Node]), % debug code ok; Res -> ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" "The response is:~n~s", [self(), node(), Node, Res]), % debug code run1(Ls, Hook, Args) end; run1([{_Seq, Module, Function} | Ls], Hook, Args) -> Res = safe_apply(Hook, Module, Function, Args), case Res of 'EXIT' -> run1(Ls, Hook, Args); stop -> ok; _ -> run1(Ls, Hook, Args) end. run_fold1([], _Hook, Val, _Args) -> Val; run_fold1([{_Seq, Node, Module, Function} | Ls], Hook, Val, Args) -> case ejabberd_cluster:call(Node, Module, Function, [Val | Args]) of {badrpc, Reason} -> ?ERROR_MSG("Bad RPC error to ~p: ~p~nrunning hook: ~p", [Node, Reason, {Hook, Args}]), run_fold1(Ls, Hook, Val, Args); timeout -> ?ERROR_MSG("Timeout on RPC to ~p~nrunning hook: ~p", [Node, {Hook, Args}]), run_fold1(Ls, Hook, Val, Args); stop -> stopped; {stop, NewVal} -> ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" "Stop, and the NewVal is:~n~p", [self(), node(), Node, NewVal]), % debug code NewVal; NewVal -> ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" "The NewVal is:~n~p", [self(), node(), Node, NewVal]), % debug code run_fold1(Ls, Hook, NewVal, Args) end; run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) -> Res = safe_apply(Hook, Module, Function, [Val | Args]), case Res of 'EXIT' -> run_fold1(Ls, Hook, Val, Args); stop -> stopped; {stop, NewVal} -> NewVal; NewVal -> run_fold1(Ls, Hook, NewVal, Args) end. safe_apply(Hook, Module, Function, Args) -> try if is_function(Function) -> apply(Function, Args); true -> apply(Module, Function, Args) end catch E:R when E /= exit; R /= normal -> ?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" "** Reason = ~p~n" "** Arguments = ~p", [Hook, Module, Function, length(Args), {E, R, get_stacktrace()}, Args]), 'EXIT' end. get_stacktrace() -> [{Mod, Fun, Loc, Args} || {Mod, Fun, Args, Loc} <- erlang:get_stacktrace()]. ejabberd-18.01/src/mod_carboncopy_redis.erl0000644000232200023220000001211413225664356021321 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_carboncopy_redis). -behaviour(mod_carboncopy). -behaviour(gen_server). %% API -export([init/2, enable/4, disable/3, list/2, cache_nodes/1]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("mod_carboncopy.hrl"). -define(CARBONCOPY_KEY, <<"ejabberd:carboncopy">>). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). cache_nodes(_LServer) -> [node()]. enable(LUser, LServer, LResource, NS) -> USKey = us_key(LUser, LServer), NodeKey = node_key(), JID = jid:encode({LUser, LServer, LResource}), Data = term_to_binary({NS, node()}), case ejabberd_redis:multi( fun() -> ejabberd_redis:hset(USKey, LResource, Data), ejabberd_redis:sadd(NodeKey, [JID]), ejabberd_redis:publish( ?CARBONCOPY_KEY, term_to_binary({delete, {LUser, LServer}})) end) of {ok, _} -> ok; {error, _} -> {error, db_failure} end. disable(LUser, LServer, LResource) -> USKey = us_key(LUser, LServer), NodeKey = node_key(), JID = jid:encode({LUser, LServer, LResource}), case ejabberd_redis:multi( fun() -> ejabberd_redis:hdel(USKey, [LResource]), ejabberd_redis:srem(NodeKey, [JID]), ejabberd_redis:publish( ?CARBONCOPY_KEY, term_to_binary({delete, {LUser, LServer}})) end) of {ok, _} -> ok; {error, _} -> {error, db_failure} end. list(LUser, LServer) -> USKey = us_key(LUser, LServer), case ejabberd_redis:hgetall(USKey) of {ok, Pairs} -> {ok, lists:flatmap( fun({Resource, Data}) -> try {NS, Node} = binary_to_term(Data), [{Resource, NS, Node}] catch _:_ -> ?ERROR_MSG("invalid term stored in Redis " "(key = ~s): ~p", [USKey, Data]), [] end end, Pairs)}; {error, _} -> {error, db_failure} end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> ejabberd_redis:subscribe([?CARBONCOPY_KEY]), clean_table(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({redis_message, ?CARBONCOPY_KEY, Data}, State) -> case binary_to_term(Data) of {delete, Key} -> ets_cache:delete(?CARBONCOPY_CACHE, Key); Msg -> ?WARNING_MSG("unexpected redis message: ~p", [Msg]) end, {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== clean_table() -> ?DEBUG("Cleaning Redis 'carboncopy' table...", []), NodeKey = node_key(), case ejabberd_redis:smembers(NodeKey) of {ok, JIDs} -> ejabberd_redis:multi( fun() -> lists:foreach( fun(JID) -> {U, S, R} = jid:split(jid:decode(JID)), USKey = us_key(U, S), ejabberd_redis:hdel(USKey, [R]) end, JIDs) end); {error, _} -> ok end, ejabberd_redis:del([NodeKey]), ok. us_key(LUser, LServer) -> <<"ejabberd:carboncopy:users:", LUser/binary, $@, LServer/binary>>. node_key() -> Node = erlang:atom_to_binary(node(), latin1), <<"ejabberd:carboncopy:nodes:", Node/binary>>. ejabberd-18.01/src/mod_irc_connection.erl0000644000232200023220000012313313225664356020774 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_irc_connection.erl %%% Author : Alexey Shchepin %%% Purpose : %%% Created : 15 Feb 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_irc_connection). -author('alexey@process-one.net'). -behaviour(p1_fsm). %% External exports -export([start_link/12, start/13, route_chan/4, route_nick/3]). %% gen_fsm callbacks -export([init/1, open_socket/2, wait_for_registration/2, stream_established/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -define(SETS, gb_sets). -record(state, {socket :: inet:socket() | undefined, encoding = <<"">> :: binary(), port = 0 :: inet:port_number(), password = <<"">> :: binary(), user = #jid{} :: jid(), host = <<"">> :: binary(), server = <<"">> :: binary(), remoteAddr = <<"">> :: binary(), ident = <<"">> :: binary(), realname = <<"">> :: binary(), nick = <<"">> :: binary(), channels = dict:new() :: ?TDICT, nickchannel :: binary() | undefined, webirc_password :: binary(), mod = mod_irc :: atom(), inbuf = <<"">> :: binary(), outbuf = <<"">> :: binary()}). -type state() :: #state{}. %-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- -endif. start(From, Host, ServerHost, Server, Username, Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, Mod) -> Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_irc_sup), supervisor:start_child(Supervisor, [From, Host, Server, Username, Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, Mod]). start_link(From, Host, Server, Username, Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, Mod) -> p1_fsm:start_link(?MODULE, [From, Host, Server, Username, Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, Mod], ?FSMOPTS). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, StateName, StateData} | %% {ok, StateName, StateData, Timeout} | %% ignore | %% {stop, StopReason} %%---------------------------------------------------------------------- init([From, Host, Server, Username, Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, Mod]) -> p1_fsm:send_event(self(), init), {ok, open_socket, #state{mod = Mod, encoding = Encoding, port = Port, password = Password, user = From, nick = Username, host = Host, server = Server, ident = Ident, realname = RealName, remoteAddr = RemoteAddr, webirc_password = WebircPassword }}. %%---------------------------------------------------------------------- %% Func: StateName/2 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- open_socket(init, StateData) -> Addr = StateData#state.server, Port = StateData#state.port, ?DEBUG("Connecting with IPv6 to ~s:~p", [Addr, Port]), From = StateData#state.user, #jid{user = JidUser, server = JidServer, resource = JidResource} = From, UserIP = ejabberd_sm:get_user_ip(JidUser, JidServer, JidResource), UserIPStr = inet_parse:ntoa(element(1, UserIP)), Connect6 = gen_tcp:connect(binary_to_list(Addr), Port, [inet6, binary, {packet, 0}]), Connect = case Connect6 of {error, _} -> ?DEBUG("Connection with IPv6 to ~s:~p failed. " "Now using IPv4.", [Addr, Port]), gen_tcp:connect(binary_to_list(Addr), Port, [inet, binary, {packet, 0}]); _ -> Connect6 end, case Connect of {ok, Socket} -> NewStateData = StateData#state{socket = Socket}, send_text(NewStateData, io_lib:format("WEBIRC ~s ~s ~s ~s\r\n", [StateData#state.webirc_password, JidResource, UserIPStr, UserIPStr])), if StateData#state.password /= <<"">> -> send_text(NewStateData, io_lib:format("PASS ~s\r\n", [StateData#state.password])); true -> true end, send_text(NewStateData, io_lib:format("NICK ~s\r\n", [StateData#state.nick])), send_text(NewStateData, io_lib:format("USER ~s ~s ~s :~s\r\n", [StateData#state.ident, StateData#state.nick, StateData#state.host, StateData#state.realname])), {next_state, wait_for_registration, NewStateData}; {error, Reason} -> ?DEBUG("connect return ~p~n", [Reason]), Text = case Reason of timeout -> <<"Server Connect Timeout">>; _ -> <<"Server Connect Failed">> end, bounce_messages(Text), {stop, normal, StateData} end. wait_for_registration(closed, StateData) -> {stop, normal, StateData}. stream_established({xmlstreamend, _Name}, StateData) -> {stop, normal, StateData}; stream_established(timeout, StateData) -> {stop, normal, StateData}; stream_established(closed, StateData) -> {stop, normal, StateData}. %%---------------------------------------------------------------------- %% Func: StateName/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %%---------------------------------------------------------------------- %state_name(Event, From, StateData) -> % Reply = ok, % {reply, Reply, state_name, StateData}. %%---------------------------------------------------------------------- %% Func: handle_event/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- handle_event(_Event, StateName, StateData) -> {next_state, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: handle_sync_event/4 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %%---------------------------------------------------------------------- handle_sync_event(_Event, _From, StateName, StateData) -> Reply = ok, {reply, Reply, StateName, StateData}. code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}. -define(SEND(S), if StateName == stream_established -> send_text(StateData, S), StateData; true -> StateData#state{outbuf = <<(StateData#state.outbuf)/binary, (iolist_to_binary(S))/binary>>} end). -spec get_password_from_presence(presence()) -> {true, binary()} | false. get_password_from_presence(#presence{} = Pres) -> case xmpp:get_subtag(Pres, #muc{}) of #muc{password = Password} -> {true, Password}; _ -> false end. %%---------------------------------------------------------------------- %% Func: handle_info/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- handle_info({route_chan, _, _, #presence{type = error}}, _, StateData) -> {stop, normal, StateData}; handle_info({route_chan, Channel, _, #presence{type = unavailable}}, StateName, StateData) -> send_stanza_unavailable(Channel, StateData), S1 = (?SEND((io_lib:format("PART #~s\r\n", [Channel])))), S2 = S1#state{channels = dict:erase(Channel, S1#state.channels)}, {next_state, StateName, S2}; handle_info({route_chan, Channel, Resource, #presence{type = available} = Presence}, StateName, StateData) -> Nick = case Resource of <<"">> -> StateData#state.nick; _ -> Resource end, S1 = if Nick /= StateData#state.nick -> S11 = (?SEND((io_lib:format("NICK ~s\r\n", [Nick])))), S11#state{nickchannel = Channel}; true -> StateData end, {next_state, StateName, case dict:is_key(Channel, S1#state.channels) of true -> S1; _ -> case get_password_from_presence(Presence) of {true, Password} -> S2 = ?SEND((io_lib:format("JOIN #~s ~s\r\n", [Channel, Password]))); _ -> S2 = ?SEND((io_lib:format("JOIN #~s\r\n", [Channel]))) end, S2#state{channels = dict:store(Channel, ?SETS:new(), S1#state.channels)} end}; handle_info({route_chan, Channel, _Resource, #message{type = groupchat} = Msg}, StateName, StateData) -> {next_state, StateName, case xmpp:get_text(Msg#message.subject) of <<"">> -> ejabberd_router:route( xmpp:set_from_to( Msg, jid:make( iolist_to_binary([Channel, <<"%">>, StateData#state.server]), StateData#state.host, StateData#state.nick), StateData#state.user)), Body = xmpp:get_text(Msg#message.body), case Body of <<"/quote ", Rest/binary>> -> ?SEND(<>); <<"/msg ", Rest/binary>> -> ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>); <<"/me ", Rest/binary>> -> Strings = str:tokens(Rest, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format( "PRIVMSG #~s :\001ACTION ~s\001\r\n", [Channel, S]) end, Strings)), ?SEND(Res); <<"/ctcp ", Rest/binary>> -> Words = str:tokens(Rest, <<" ">>), case Words of [CtcpDest | _] -> CtcpCmd = str:to_upper( str:substr( Rest, str:str(Rest, <<" ">>) + 1)), Res = io_lib:format("PRIVMSG ~s :\001~s\001\r\n", [CtcpDest, CtcpCmd]), ?SEND(Res); _ -> ok end; _ -> Strings = str:tokens(Body, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format("PRIVMSG #~s :~s\r\n", [Channel, S]) end, Strings)), ?SEND(Res) end; Subject -> Strings = str:tokens(Subject, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format("TOPIC #~s :~s\r\n", [Channel, S]) end, Strings)), ?SEND(Res) end}; handle_info({route_chan, _Channel, Resource, #message{type = Type} = Msg}, StateName, StateData) when Type == chat; Type == normal -> Body = xmpp:get_text(Msg#message.body), {next_state, StateName, case Body of <<"/quote ", Rest/binary>> -> ?SEND(<>); <<"/msg ", Rest/binary>> -> ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>); <<"/me ", Rest/binary>> -> Strings = str:tokens(Rest, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format( "PRIVMSG ~s :\001ACTION ~s\001\r\n", [Resource, S]) end, Strings)), ?SEND(Res); <<"/ctcp ", Rest/binary>> -> Words = str:tokens(Rest, <<" ">>), case Words of [CtcpDest | _] -> CtcpCmd = str:to_upper( str:substr( Rest, str:str(Rest, <<" ">>) + 1)), Res = io_lib:format("PRIVMSG ~s :~s\r\n", [CtcpDest, <<"\001", CtcpCmd/binary, "\001">>]), ?SEND(Res); _ -> ok end; _ -> Strings = str:tokens(Body, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format("PRIVMSG ~s :~s\r\n", [Resource, S]) end, Strings)), ?SEND(Res) end}; handle_info({route_chan, _, _, #message{type = error}}, _, StateData) -> {stop, normal, StateData}; handle_info({route_chan, Channel, Resource, #iq{type = T, sub_els = [_]} = Packet}, StateName, StateData) when T == set; T == get -> From = StateData#state.user, To = jid:make(iolist_to_binary([Channel, <<"%">>, StateData#state.server]), StateData#state.host, StateData#state.nick), try xmpp:decode_els(Packet) of #iq{sub_els = [SubEl]} = IQ -> case xmpp:get_ns(SubEl) of ?NS_MUC_ADMIN -> iq_admin(StateData, Channel, From, To, IQ); ?NS_VERSION -> Res = io_lib:format("PRIVMSG ~s :\001VERSION\001\r\n", [Resource]), _ = (?SEND(Res)), Err = xmpp:err_feature_not_implemented(), ejabberd_router:route_error(Packet, Err); ?NS_TIME -> Res = io_lib:format("PRIVMSG ~s :\001TIME\001\r\n", [Resource]), _ = (?SEND(Res)), Err = xmpp:err_feature_not_implemented(), ejabberd_router:route_error(Packet, Err); ?NS_VCARD -> Res = io_lib:format("WHOIS ~s \r\n", [Resource]), _ = (?SEND(Res)), Err = xmpp:err_feature_not_implemented(), ejabberd_router:route_error(Packet, Err); _ -> Err = xmpp:err_feature_not_implemented(), ejabberd_router:route_error(Packet, Err) end catch _:{xmpp_codec, Why} -> Err = xmpp:err_bad_request( xmpp:io_format_error(Why), xmpp:get_lang(Packet)), ejabberd_router:route_error(Packet, Err) end, {next_state, StateName, StateData}; handle_info({route_chan, _Channel, _, #iq{} = IQ}, StateName, StateData) -> Err = xmpp:err_feature_not_implemented(), ejabberd_router:route_error(IQ, Err), {next_state, StateName, StateData}; handle_info({route_nick, Nick, #message{type = chat} = Msg}, StateName, StateData) -> Body = xmpp:get_text(Msg#message.body), {next_state, StateName, case Body of <<"/quote ", Rest/binary>> -> ?SEND(<>); <<"/msg ", Rest/binary>> -> ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>); <<"/me ", Rest/binary>> -> Strings = str:tokens(Rest, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format( "PRIVMSG ~s :\001ACTION ~s\001\r\n", [Nick, S]) end, Strings)), ?SEND(Res); <<"/ctcp ", Rest/binary>> -> Words = str:tokens(Rest, <<" ">>), case Words of [CtcpDest | _] -> CtcpCmd = str:to_upper( str:substr(Rest, str:str(Rest, <<" ">>) + 1)), Res = io_lib:format("PRIVMSG ~s :~s\r\n", [CtcpDest, <<"\001", CtcpCmd/binary, "\001">>]), ?SEND(Res); _ -> ok end; _ -> Strings = str:tokens(Body, <<"\n">>), Res = iolist_to_binary( lists:map( fun (S) -> io_lib:format( "PRIVMSG ~s :~s\r\n", [Nick, S]) end, Strings)), ?SEND(Res) end}; handle_info({route_nick, _, #message{type = error}}, _, StateData) -> {stop, normal, StateData}; handle_info({route_nick, _Nick, _Packet}, StateName, StateData) -> {next_state, StateName, StateData}; handle_info({ircstring, <<$P, $I, $N, $G, $\s, ID/binary>>}, StateName, StateData) -> send_text(StateData, <<"PONG ", ID/binary, "\r\n">>), {next_state, StateName, StateData}; handle_info({ircstring, <<$:, String/binary>>}, wait_for_registration, StateData) -> Words = str:tokens(String, <<" ">>), {NewState, NewStateData} = case Words of [_, <<"001">> | _] -> send_text(StateData, io_lib:format("CODEPAGE ~s\r\n", [StateData#state.encoding])), {stream_established, StateData}; [_, <<"433">> | _] -> {error, {error, error_nick_in_use(StateData, String), StateData}}; [_, <<$4, _, _>> | _] -> {error, {error, error_unknown_num(StateData, String, cancel), StateData}}; [_, <<$5, _, _>> | _] -> {error, {error, error_unknown_num(StateData, String, cancel), StateData}}; _ -> ?DEBUG("unknown irc command '~s'~n", [String]), {wait_for_registration, StateData} end, if NewState == error -> {stop, normal, NewStateData}; true -> {next_state, NewState, NewStateData} end; handle_info({ircstring, <<$:, String/binary>>}, _StateName, StateData) -> Words = str:tokens(String, <<" ">>), NewStateData = case Words of [_, <<"353">> | Items] -> process_channel_list(StateData, Items); [_, <<"332">>, _Nick, <<$#, Chan/binary>> | _] -> process_channel_topic(StateData, Chan, String), StateData; [_, <<"333">>, _Nick, <<$#, Chan/binary>> | _] -> process_channel_topic_who(StateData, Chan, String), StateData; [_, <<"318">>, _, Nick | _] -> process_endofwhois(StateData, String, Nick), StateData; [_, <<"311">>, _, Nick, Ident, Irchost | _] -> process_whois311(StateData, String, Nick, Ident, Irchost), StateData; [_, <<"312">>, _, Nick, Ircserver | _] -> process_whois312(StateData, String, Nick, Ircserver), StateData; [_, <<"319">>, _, Nick | _] -> process_whois319(StateData, String, Nick), StateData; [_, <<"433">> | _] -> process_nick_in_use(StateData, String); % CODEPAGE isn't standard, so don't complain if it's not there. [_, <<"421">>, _, <<"CODEPAGE">> | _] -> StateData; [_, <<$4, _, _>> | _] -> process_num_error(StateData, String); [_, <<$5, _, _>> | _] -> process_num_error(StateData, String); [From, <<"PRIVMSG">>, <<$#, Chan/binary>> | _] -> process_chanprivmsg(StateData, Chan, From, String), StateData; [From, <<"NOTICE">>, <<$#, Chan/binary>> | _] -> process_channotice(StateData, Chan, From, String), StateData; [From, <<"PRIVMSG">>, Nick, <<":\001VERSION\001">> | _] -> process_version(StateData, Nick, From), StateData; [From, <<"PRIVMSG">>, Nick, <<":\001USERINFO\001">> | _] -> process_userinfo(StateData, Nick, From), StateData; [From, <<"PRIVMSG">>, Nick | _] -> process_privmsg(StateData, Nick, From, String), StateData; [From, <<"NOTICE">>, Nick | _] -> process_notice(StateData, Nick, From, String), StateData; [From, <<"TOPIC">>, <<$#, Chan/binary>> | _] -> process_topic(StateData, Chan, From, String), StateData; [From, <<"PART">>, <<$#, Chan/binary>> | _] -> process_part(StateData, Chan, From, String); [From, <<"QUIT">> | _] -> process_quit(StateData, From, String); [From, <<"JOIN">>, Chan | _] -> process_join(StateData, Chan, From, String); [From, <<"MODE">>, <<$#, Chan/binary>>, <<"+o">>, Nick | _] -> process_mode_o(StateData, Chan, From, Nick, admin, moderator), StateData; [From, <<"MODE">>, <<$#, Chan/binary>>, <<"-o">>, Nick | _] -> process_mode_o(StateData, Chan, From, Nick, member, participant), StateData; [From, <<"KICK">>, <<$#, Chan/binary>>, Nick | _] -> process_kick(StateData, Chan, From, Nick, String), StateData; [From, <<"NICK">>, Nick | _] -> process_nick(StateData, From, Nick); _ -> ?DEBUG("unknown irc command '~s'~n", [String]), StateData end, NewStateData1 = case StateData#state.outbuf of <<"">> -> NewStateData; Data -> send_text(NewStateData, Data), NewStateData#state{outbuf = <<"">>} end, {next_state, stream_established, NewStateData1}; handle_info({ircstring, <<$E, $R, $R, $O, $R, _/binary>> = String}, StateName, StateData) -> process_error(StateData, String), {next_state, StateName, StateData}; handle_info({ircstring, String}, StateName, StateData) -> ?DEBUG("unknown irc command '~s'~n", [String]), {next_state, StateName, StateData}; handle_info({send_text, Text}, StateName, StateData) -> send_text(StateData, Text), {next_state, StateName, StateData}; handle_info({tcp, _Socket, Data}, StateName, StateData) -> Buf = <<(StateData#state.inbuf)/binary, Data/binary>>, Strings = ejabberd_regexp:split(<< <> || <> <= Buf, C /= $\r >>, <<"\n">>), ?DEBUG("strings=~p~n", [Strings]), NewBuf = process_lines(StateData#state.encoding, Strings), {next_state, StateName, StateData#state{inbuf = NewBuf}}; handle_info({tcp_closed, _Socket}, StateName, StateData) -> p1_fsm:send_event(self(), closed), {next_state, StateName, StateData}; handle_info({tcp_error, _Socket, _Reason}, StateName, StateData) -> p1_fsm:send_event(self(), closed), {next_state, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: terminate/3 %% Purpose: Shutdown the fsm %% Returns: any %%---------------------------------------------------------------------- terminate(_Reason, _StateName, FullStateData) -> {Error, StateData} = case FullStateData of {error, SError, SStateData} -> {SError, SStateData}; _ -> {xmpp:err_internal_server_error( <<"Server Connect Failed">>, ?MYLANG), FullStateData} end, (StateData#state.mod):closed_connection(StateData#state.host, StateData#state.user, StateData#state.server), bounce_messages(<<"Server Connect Failed">>), lists:foreach(fun (Chan) -> Stanza = xmpp:make_error(#presence{}, Error), send_stanza(Chan, StateData, Stanza) end, dict:fetch_keys(StateData#state.channels)), case StateData#state.socket of undefined -> ok; Socket -> gen_tcp:close(Socket) end, ok. -spec send_stanza(binary(), state(), stanza()) -> ok. send_stanza(Chan, StateData, Stanza) -> ejabberd_router:route( xmpp:set_from_to( Stanza, jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, StateData#state.nick), StateData#state.user)). -spec send_stanza_unavailable(binary(), state()) -> ok. send_stanza_unavailable(Chan, StateData) -> Affiliation = member, Role = none, Stanza = #presence{ type = unavailable, sub_els = [#muc_user{ items = [#muc_item{affiliation = Affiliation, role = Role}], status_codes = [110]}]}, send_stanza(Chan, StateData, Stanza). %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- send_text(#state{socket = Socket, encoding = Encoding}, Text) -> CText = iconv:convert(<<"utf-8">>, Encoding, iolist_to_binary(Text)), gen_tcp:send(Socket, CText). bounce_messages(Reason) -> receive {send_element, El} -> Lang = xmpp:get_lang(El), Err = xmpp:err_internal_server_error(Reason, Lang), ejabberd_router:route_error(El, Err), bounce_messages(Reason) after 0 -> ok end. route_chan(Pid, Channel, Resource, Packet) -> Pid ! {route_chan, Channel, Resource, Packet}. route_nick(Pid, Nick, Packet) -> Pid ! {route_nick, Nick, Packet}. process_lines(_Encoding, [S]) -> S; process_lines(Encoding, [S | Ss]) -> self() ! {ircstring, iconv:convert(Encoding, <<"utf-8">>, S)}, process_lines(Encoding, Ss). process_channel_list(StateData, Items) -> process_channel_list_find_chan(StateData, Items). process_channel_list_find_chan(StateData, []) -> StateData; process_channel_list_find_chan(StateData, [<<$#, Chan/binary>> | Items]) -> process_channel_list_users(StateData, Chan, Items); process_channel_list_find_chan(StateData, [_ | Items]) -> process_channel_list_find_chan(StateData, Items). process_channel_list_users(StateData, _Chan, []) -> StateData; process_channel_list_users(StateData, Chan, [User | Items]) -> NewStateData = process_channel_list_user(StateData, Chan, User), process_channel_list_users(NewStateData, Chan, Items). process_channel_list_user(StateData, Chan, User) -> User1 = case User of <<$:, U1/binary>> -> U1; _ -> User end, {User2, Affiliation, Role} = case User1 of <<$@, U2/binary>> -> {U2, admin, moderator}; <<$+, U2/binary>> -> {U2, member, participant}; <<$%, U2/binary>> -> {U2, admin, moderator}; <<$&, U2/binary>> -> {U2, admin, moderator}; <<$~, U2/binary>> -> {U2, admin, moderator}; _ -> {User1, member, participant} end, ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, User2), to = StateData#state.user, sub_els = [#muc_user{items = [#muc_item{affiliation = Affiliation, role = Role}]}]}), case catch dict:update(Chan, fun (Ps) -> (?SETS):add_element(User2, Ps) end, StateData#state.channels) of {'EXIT', _} -> StateData; NS -> StateData#state{channels = NS} end. process_channel_topic(StateData, Chan, String) -> Msg = ejabberd_regexp:replace(String, <<".*332[^:]*:">>, <<"">>), Subject = filter_message(Msg), Body = <<"Topic for #", Chan/binary, ": ", Subject/binary>>, ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host), to = StateData#state.user, type = groupchat, subject = xmpp:mk_text(Subject), body = xmpp:mk_text(Body)}). process_channel_topic_who(StateData, Chan, String) -> Words = str:tokens(String, <<" ">>), Msg1 = case Words of [_, <<"333">>, _, _Chan, Whoset, Timeset] -> {Unixtimeset, _Rest} = str:to_integer(Timeset), <<"Topic for #", Chan/binary, " set by ", Whoset/binary, " at ", (unixtime2string(Unixtimeset))/binary>>; [_, <<"333">>, _, _Chan, Whoset | _] -> <<"Topic for #", Chan/binary, " set by ", Whoset/binary>>; _ -> String end, Msg2 = filter_message(Msg1), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, <<"">>), to = StateData#state.user, type = groupchat, body = xmpp:mk_text(Msg2)}). error_nick_in_use(_StateData, String) -> Msg = ejabberd_regexp:replace(String, <<".*433 +[^ ]* +">>, <<"">>), Msg1 = filter_message(Msg), xmpp:err_conflict(Msg1, ?MYLANG). process_nick_in_use(StateData, String) -> Error = error_nick_in_use(StateData, String), case StateData#state.nickchannel of undefined -> % Shouldn't happen with a well behaved server StateData; Chan -> ejabberd_router:route( #presence{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, StateData#state.nick), to = StateData#state.user, type = error, sub_els = [Error]}), StateData#state{nickchannel = undefined} end. process_num_error(StateData, String) -> Error = error_unknown_num(StateData, String, continue), lists:foreach( fun(Chan) -> ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), StateData#state.host, StateData#state.nick), to = StateData#state.user, type = error, sub_els = [Error]}) end, dict:fetch_keys(StateData#state.channels)), StateData. process_endofwhois(StateData, _String, Nick) -> ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), StateData#state.host), to = StateData#state.user, type = chat, body = xmpp:mk_text(<<"End of WHOIS">>)}). process_whois311(StateData, String, Nick, Ident, Irchost) -> Fullname = ejabberd_regexp:replace(String, <<".*311[^:]*:">>, <<"">>), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), StateData#state.host, <<"">>), to = StateData#state.user, type = chat, body = xmpp:mk_text( iolist_to_binary( [<<"WHOIS: ">>, Nick, <<" is ">>, Ident, <<"@">>, Irchost, <<" : ">>, Fullname]))}). process_whois312(StateData, String, Nick, Ircserver) -> Ircserverdesc = ejabberd_regexp:replace(String, <<".*312[^:]*:">>, <<"">>), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), StateData#state.host, <<"">>), to = StateData#state.user, type = chat, body = xmpp:mk_text( iolist_to_binary( [<<"WHOIS: ">>, Nick, <<" use ">>, Ircserver, <<" : ">>, Ircserverdesc]))}). process_whois319(StateData, String, Nick) -> Chanlist = ejabberd_regexp:replace(String, <<".*319[^:]*:">>, <<"">>), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), StateData#state.host, <<"">>), to = StateData#state.user, type = chat, body = xmpp:mk_text( iolist_to_binary( [<<"WHOIS: ">>, Nick, <<" is on ">>, Chanlist]))}). process_chanprivmsg(StateData, Chan, From, String) -> [FromUser | _] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*PRIVMSG[^:]*:">>, <<"">>), Msg1 = case Msg of <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> <<"/me ", Rest/binary>>; _ -> Msg end, Msg2 = filter_message(Msg1), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, type = groupchat, body = xmpp:mk_text(Msg2)}). process_channotice(StateData, Chan, From, String) -> [FromUser | _] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*NOTICE[^:]*:">>, <<"">>), Msg1 = case Msg of <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> <<"/me ", Rest/binary>>; _ -> <<"/me NOTICE: ", Msg/binary>> end, Msg2 = filter_message(Msg1), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, type = groupchat, body = xmpp:mk_text(Msg2)}). process_privmsg(StateData, _Nick, From, String) -> [FromUser | _] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*PRIVMSG[^:]*:">>, <<"">>), Msg1 = case Msg of <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> <<"/me ", Rest/binary>>; _ -> Msg end, Msg2 = filter_message(Msg1), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([FromUser, <<"!">>, StateData#state.server]), StateData#state.host, <<"">>), to = StateData#state.user, type = chat, body = xmpp:mk_text(Msg2)}). process_notice(StateData, _Nick, From, String) -> [FromUser | _] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*NOTICE[^:]*:">>, <<"">>), Msg1 = case Msg of <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> <<"/me ", Rest/binary>>; _ -> <<"/me NOTICE: ", Msg/binary>> end, Msg2 = filter_message(Msg1), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([FromUser, <<"!">>, StateData#state.server]), StateData#state.host), to = StateData#state.user, type = chat, body = xmpp:mk_text(Msg2)}). process_version(StateData, _Nick, From) -> [FromUser | _] = str:tokens(From, <<"!">>), send_text(StateData, io_lib:format("NOTICE ~s :\001VERSION ejabberd IRC " "transport ~s (c) Alexey Shchepin\001\r\n", [FromUser, ?VERSION]) ++ io_lib:format("NOTICE ~s :\001VERSION http://ejabberd.jabber" "studio.org/\001\r\n", [FromUser])). process_userinfo(StateData, _Nick, From) -> [FromUser | _] = str:tokens(From, <<"!">>), send_text(StateData, io_lib:format("NOTICE ~s :\001USERINFO xmpp:~s\001\r\n", [FromUser, jid:encode(StateData#state.user)])). process_topic(StateData, Chan, From, String) -> [FromUser | _] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*TOPIC[^:]*:">>, <<"">>), Msg1 = filter_message(Msg), ejabberd_router:route( #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, type = groupchat, subject = xmpp:mk_text(Msg1), body = xmpp:mk_text(<<"/me has changed the subject to: ", Msg1/binary>>)}). process_part(StateData, Chan, From, String) -> [FromUser | FromIdent] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*PART[^:]*:">>, <<"">>), Msg1 = filter_message(Msg), ejabberd_router:route( #presence{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, type = unavailable, sub_els = [#muc_user{ items = [#muc_item{affiliation = member, role = none}]}], status = xmpp:mk_text( list_to_binary([Msg1, " (", FromIdent, ")"]))}), case catch dict:update(Chan, fun (Ps) -> remove_element(FromUser, Ps) end, StateData#state.channels) of {'EXIT', _} -> StateData; NS -> StateData#state{channels = NS} end. process_quit(StateData, From, String) -> [FromUser | FromIdent] = str:tokens(From, <<"!">>), Msg = ejabberd_regexp:replace(String, <<".*QUIT[^:]*:">>, <<"">>), Msg1 = filter_message(Msg), dict:map( fun(Chan, Ps) -> case (?SETS):is_member(FromUser, Ps) of true -> ejabberd_router:route( #presence{from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, type = unavailable, sub_els = [#muc_user{ items = [#muc_item{ affiliation = member, role = none}]}], status = xmpp:mk_text( list_to_binary([Msg1, " (", FromIdent, ")"]))}), remove_element(FromUser, Ps); _ -> Ps end end, StateData#state.channels), StateData. process_join(StateData, Channel, From, _String) -> [FromUser | FromIdent] = str:tokens(From, <<"!">>), [Chan | _] = binary:split(Channel, <<":#">>), ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, sub_els = [#muc_user{items = [#muc_item{affiliation = member, role = participant}]}], status = xmpp:mk_text(list_to_binary(FromIdent))}), case catch dict:update(Chan, fun (Ps) -> (?SETS):add_element(FromUser, Ps) end, StateData#state.channels) of {'EXIT', _} -> StateData; NS -> StateData#state{channels = NS} end. process_mode_o(StateData, Chan, _From, Nick, Affiliation, Role) -> ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, Nick), to = StateData#state.user, sub_els = [#muc_user{items = [#muc_item{affiliation = Affiliation, role = Role}]}]}). process_kick(StateData, Chan, From, Nick, String) -> Msg = lists:last(str:tokens(String, <<":">>)), Msg2 = <>, ejabberd_router:route( #message{ from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host), to = StateData#state.user, type = groupchat, body = xmpp:mk_text(Msg2)}), ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), StateData#state.host, Nick), to = StateData#state.user, type = unavailable, sub_els = [#muc_user{items = [#muc_item{ affiliation = none, role = none}], status_codes = [307]}]}). process_nick(StateData, From, NewNick) -> [FromUser | _] = str:tokens(From, <<"!">>), [Nick | _] = binary:split(NewNick, <<":">>), NewChans = dict:map( fun(Chan, Ps) -> case (?SETS):is_member(FromUser, Ps) of true -> ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), StateData#state.host, FromUser), to = StateData#state.user, type = unavailable, sub_els = [#muc_user{ items = [#muc_item{ affiliation = member, role = participant, nick = Nick}], status_codes = [303]}]}), ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), StateData#state.host, Nick), to = StateData#state.user, sub_els = [#muc_user{ items = [#muc_item{ affiliation = member, role = participant}]}]}), (?SETS):add_element(Nick, remove_element(FromUser, Ps)); _ -> Ps end end, StateData#state.channels), if FromUser == StateData#state.nick -> StateData#state{nick = Nick, nickchannel = undefined, channels = NewChans}; true -> StateData#state{channels = NewChans} end. process_error(StateData, String) -> lists:foreach( fun(Chan) -> ejabberd_router:route( #presence{ from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), StateData#state.host, StateData#state.nick), to = StateData#state.user, type = error, sub_els = [xmpp:err_internal_server_error(String, ?MYLANG)]}) end, dict:fetch_keys(StateData#state.channels)). error_unknown_num(_StateData, String, Type) -> Msg = ejabberd_regexp:replace(String, <<".*[45][0-9][0-9] +[^ ]* +">>, <<"">>), Msg1 = filter_message(Msg), xmpp:err_undefined_condition(Type, Msg1, ?MYLANG). remove_element(E, Set) -> case (?SETS):is_element(E, Set) of true -> (?SETS):del_element(E, Set); _ -> Set end. iq_admin(StateData, Channel, From, _To, #iq{type = Type, sub_els = [SubEl]} = IQ) -> try process_iq_admin(StateData, Channel, Type, SubEl) of {result, Result} -> ejabberd_router:route(xmpp:make_iq_result(IQ, Result)); {error, Error} -> ejabberd_router:route_error(IQ, Error) catch E:R -> ?ERROR_MSG("failed to process admin query from ~s: ~p", [jid:encode(From), {E, {R, erlang:get_stacktrace()}}]), ejabberd_router:route_error( IQ, xmpp:err_internal_server_error()) end. process_iq_admin(_StateData, _Channel, set, #muc_admin{items = []}) -> {error, xmpp:err_bad_request()}; process_iq_admin(StateData, Channel, set, #muc_admin{items = [Item|_]}) -> process_admin(StateData, Channel, Item); process_iq_admin(_StateData, _Channel, _, _SubEl) -> {error, xmpp:err_feature_not_implemented()}. process_admin(_StateData, _Channel, #muc_item{nick = <<"">>}) -> {error, xmpp:err_feature_not_implemented()}; process_admin(StateData, Channel, #muc_item{nick = Nick, reason = Reason, role = none}) -> case Reason of <<"">> -> send_text(StateData, io_lib:format("KICK #~s ~s\r\n", [Channel, Nick])); _ -> send_text(StateData, io_lib:format("KICK #~s ~s :~s\r\n", [Channel, Nick, Reason])) end, {result, undefined}; process_admin(_StateData, _Channel, _Item) -> {error, xmpp:err_feature_not_implemented()}. filter_message(Msg) -> list_to_binary( lists:filter(fun (C) -> if (C < 32) and (C /= 9) and (C /= 10) and (C /= 13) -> false; true -> true end end, binary_to_list(filter_mirc_colors(Msg)))). filter_mirc_colors(Msg) -> ejabberd_regexp:greplace(Msg, <<"(\\003[0-9]+)(,[0-9]+)?">>, <<"">>). unixtime2string(Unixtime) -> Secs = Unixtime + calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}), {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time_to_local_time(calendar:gregorian_seconds_to_datetime(Secs)), (str:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", [Year, Month, Day, Hour, Minute, Second])). ejabberd-18.01/src/mod_fail2ban.erl0000644000232200023220000001544713225664356017466 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_fail2ban.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 15 Aug 2014 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2014-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_fail2ban). -behaviour(gen_mod). -behaviour(gen_server). %% API -export([start/2, stop/1, reload/3, c2s_auth_result/3, c2s_stream_started/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, mod_opt_type/1, depends/2]). -include_lib("stdlib/include/ms_transform.hrl"). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -define(C2S_AUTH_BAN_LIFETIME, 3600). %% 1 hour -define(C2S_MAX_AUTH_FAILURES, 20). -define(CLEAN_INTERVAL, timer:minutes(10)). -record(state, {host = <<"">> :: binary()}). %%%=================================================================== %%% API %%%=================================================================== -spec c2s_auth_result(ejabberd_c2s:state(), boolean(), binary()) -> ejabberd_c2s:state() | {stop, ejabberd_c2s:state()}. c2s_auth_result(#{ip := {Addr, _}, lserver := LServer} = State, false, _User) -> case is_whitelisted(LServer, Addr) of true -> State; false -> BanLifetime = gen_mod:get_module_opt( LServer, ?MODULE, c2s_auth_ban_lifetime, ?C2S_AUTH_BAN_LIFETIME), MaxFailures = gen_mod:get_module_opt( LServer, ?MODULE, c2s_max_auth_failures, ?C2S_MAX_AUTH_FAILURES), UnbanTS = p1_time_compat:system_time(seconds) + BanLifetime, Attempts = case ets:lookup(failed_auth, Addr) of [{Addr, N, _, _}] -> ets:insert(failed_auth, {Addr, N+1, UnbanTS, MaxFailures}), N+1; [] -> ets:insert(failed_auth, {Addr, 1, UnbanTS, MaxFailures}), 1 end, if Attempts >= MaxFailures -> log_and_disconnect(State, Attempts, UnbanTS); true -> State end end; c2s_auth_result(#{ip := {Addr, _}} = State, true, _User) -> ets:delete(failed_auth, Addr), State. -spec c2s_stream_started(ejabberd_c2s:state(), stream_start()) -> ejabberd_c2s:state() | {stop, ejabberd_c2s:state()}. c2s_stream_started(#{ip := {Addr, _}} = State, _) -> case ets:lookup(failed_auth, Addr) of [{Addr, N, TS, MaxFailures}] when N >= MaxFailures -> case TS > p1_time_compat:system_time(seconds) of true -> log_and_disconnect(State, N, TS); false -> ets:delete(failed_auth, Addr), State end; _ -> State end. %%==================================================================== %% gen_mod callbacks %%==================================================================== start(Host, Opts) -> catch ets:new(failed_auth, [named_table, public, {heir, erlang:group_leader(), none}]), gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> []. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([Host, _Opts]) -> process_flag(trap_exit, true), ejabberd_hooks:add(c2s_auth_result, Host, ?MODULE, c2s_auth_result, 100), ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE, c2s_stream_started, 100), erlang:send_after(?CLEAN_INTERVAL, self(), clean), {ok, #state{host = Host}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> ?ERROR_MSG("got unexpected cast = ~p", [_Msg]), {noreply, State}. handle_info(clean, State) -> ?DEBUG("cleaning ~p ETS table", [failed_auth]), Now = p1_time_compat:system_time(seconds), ets:select_delete( failed_auth, ets:fun2ms(fun({_, _, UnbanTS, _}) -> UnbanTS =< Now end)), erlang:send_after(?CLEAN_INTERVAL, self(), clean), {noreply, State}; handle_info(_Info, State) -> ?ERROR_MSG("got unexpected info = ~p", [_Info]), {noreply, State}. terminate(_Reason, #state{host = Host}) -> ejabberd_hooks:delete(c2s_auth_result, Host, ?MODULE, c2s_auth_result, 100), ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE, c2s_stream_started, 100), case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of true -> ok; false -> ets:delete(failed_auth) end. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec log_and_disconnect(ejabberd_c2s:state(), pos_integer(), non_neg_integer()) -> {stop, ejabberd_c2s:state()}. log_and_disconnect(#{ip := {Addr, _}, lang := Lang} = State, Attempts, UnbanTS) -> IP = misc:ip_to_list(Addr), UnbanDate = format_date( calendar:now_to_universal_time(seconds_to_now(UnbanTS))), Format = <<"Too many (~p) failed authentications " "from this IP address (~s). The address " "will be unblocked at ~s UTC">>, Args = [Attempts, IP, UnbanDate], ?INFO_MSG("Connection attempt from blacklisted IP ~s: ~s", [IP, io_lib:fwrite(Format, Args)]), Err = xmpp:serr_policy_violation({Format, Args}, Lang), {stop, ejabberd_c2s:send(State, Err)}. is_whitelisted(Host, Addr) -> Access = gen_mod:get_module_opt(Host, ?MODULE, access, none), acl:match_rule(Host, Access, Addr) == allow. seconds_to_now(Secs) -> {Secs div 1000000, Secs rem 1000000, 0}. format_date({{Year, Month, Day}, {Hour, Minute, Second}}) -> io_lib:format("~2..0w:~2..0w:~2..0w ~2..0w.~2..0w.~4..0w", [Hour, Minute, Second, Day, Month, Year]). mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(c2s_auth_ban_lifetime) -> fun (T) when is_integer(T), T > 0 -> T end; mod_opt_type(c2s_max_auth_failures) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(_) -> [access, c2s_auth_ban_lifetime, c2s_max_auth_failures]. ejabberd-18.01/src/ejabberd_idna.erl0000644000232200023220000001422213225664356017670 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_idna.erl %%% Author : Alexey Shchepin %%% Purpose : Support for IDNA (RFC3490) %%% Created : 10 Apr 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_idna). -author('alexey@process-one.net'). -export([domain_utf8_to_ascii/1, domain_ucs2_to_ascii/1, utf8_to_ucs2/1]). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. -spec domain_utf8_to_ascii(binary()) -> false | binary(). domain_utf8_to_ascii(Domain) -> domain_ucs2_to_ascii(utf8_to_ucs2(Domain)). utf8_to_ucs2(S) -> utf8_to_ucs2(binary_to_list(S), ""). utf8_to_ucs2([], R) -> lists:reverse(R); utf8_to_ucs2([C | S], R) when C < 128 -> utf8_to_ucs2(S, [C | R]); utf8_to_ucs2([C1, C2 | S], R) when C1 < 224 -> utf8_to_ucs2(S, [C1 band 31 bsl 6 bor C2 band 63 | R]); utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 240 -> utf8_to_ucs2(S, [C1 band 15 bsl 12 bor (C2 band 63 bsl 6) bor C3 band 63 | R]). -spec domain_ucs2_to_ascii(list()) -> false | binary(). domain_ucs2_to_ascii(Domain) -> case catch domain_ucs2_to_ascii1(Domain) of {'EXIT', _Reason} -> false; Res -> iolist_to_binary(Res) end. domain_ucs2_to_ascii1(Domain) -> Parts = string:tokens(Domain, [46, 12290, 65294, 65377]), ASCIIParts = lists:map(fun (P) -> to_ascii(P) end, Parts), string:strip(lists:flatmap(fun (P) -> [$. | P] end, ASCIIParts), left, $.). %% Domain names are already nameprep'ed in ejabberd, so we skiping this step to_ascii(Name) -> false = lists:any(fun (C) when (0 =< C) and (C =< 44) or (46 =< C) and (C =< 47) or (58 =< C) and (C =< 64) or (91 =< C) and (C =< 96) or (123 =< C) and (C =< 127) -> true; (_) -> false end, Name), case Name of [H | _] when H /= $- -> true = lists:last(Name) /= $- end, ASCIIName = case lists:any(fun (C) -> C > 127 end, Name) of true -> true = case Name of "xn--" ++ _ -> false; _ -> true end, "xn--" ++ punycode_encode(Name); false -> Name end, L = length(ASCIIName), true = (1 =< L) and (L =< 63), ASCIIName. %%% PUNYCODE (RFC3492) -define(BASE, 36). -define(TMIN, 1). -define(TMAX, 26). -define(SKEW, 38). -define(DAMP, 700). -define(INITIAL_BIAS, 72). -define(INITIAL_N, 128). punycode_encode(Input) -> N = (?INITIAL_N), Delta = 0, Bias = (?INITIAL_BIAS), Basic = lists:filter(fun (C) -> C =< 127 end, Input), NonBasic = lists:filter(fun (C) -> C > 127 end, Input), L = length(Input), B = length(Basic), SNonBasic = lists:usort(NonBasic), Output1 = if B > 0 -> Basic ++ "-"; true -> "" end, Output2 = punycode_encode1(Input, SNonBasic, B, B, L, N, Delta, Bias, ""), Output1 ++ Output2. punycode_encode1(Input, [M | SNonBasic], B, H, L, N, Delta, Bias, Out) when H < L -> Delta1 = Delta + (M - N) * (H + 1), % let n = m {NewDelta, NewBias, NewH, NewOut} = lists:foldl(fun (C, {ADelta, ABias, AH, AOut}) -> if C < M -> {ADelta + 1, ABias, AH, AOut}; C == M -> NewOut = punycode_encode_delta(ADelta, ABias, AOut), NewBias = adapt(ADelta, H + 1, H == B), {0, NewBias, AH + 1, NewOut}; true -> {ADelta, ABias, AH, AOut} end end, {Delta1, Bias, H, Out}, Input), punycode_encode1(Input, SNonBasic, B, NewH, L, M + 1, NewDelta + 1, NewBias, NewOut); punycode_encode1(_Input, _SNonBasic, _B, _H, _L, _N, _Delta, _Bias, Out) -> lists:reverse(Out). punycode_encode_delta(Delta, Bias, Out) -> punycode_encode_delta(Delta, Bias, Out, ?BASE). punycode_encode_delta(Delta, Bias, Out, K) -> T = if K =< Bias -> ?TMIN; K >= Bias + (?TMAX) -> ?TMAX; true -> K - Bias end, if Delta < T -> [codepoint(Delta) | Out]; true -> C = T + (Delta - T) rem ((?BASE) - T), punycode_encode_delta((Delta - T) div ((?BASE) - T), Bias, [codepoint(C) | Out], K + (?BASE)) end. adapt(Delta, NumPoints, FirstTime) -> Delta1 = if FirstTime -> Delta div (?DAMP); true -> Delta div 2 end, Delta2 = Delta1 + Delta1 div NumPoints, adapt1(Delta2, 0). adapt1(Delta, K) -> if Delta > ((?BASE) - (?TMIN)) * (?TMAX) div 2 -> adapt1(Delta div ((?BASE) - (?TMIN)), K + (?BASE)); true -> K + ((?BASE) - (?TMIN) + 1) * Delta div (Delta + (?SKEW)) end. codepoint(C) -> if (0 =< C) and (C =< 25) -> C + 97; (26 =< C) and (C =< 35) -> C + 22 end. %%%=================================================================== %%% Unit tests %%%=================================================================== -ifdef(TEST). acsii_test() -> ?assertEqual(<<"test.org">>, domain_utf8_to_ascii(<<"test.org">>)). utf8_test() -> ?assertEqual( <<"xn--d1acufc.xn--p1ai">>, domain_utf8_to_ascii( <<208,180,208,190,208,188,208,181,208,189,46,209,128,209,132>>)). -endif. ejabberd-18.01/src/mod_sip_proxy.erl0000644000232200023220000003155313225664356020040 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_sip_proxy.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 21 Apr 2014 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2014-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_sip_proxy). -ifndef(SIP). -export([]). -else. -behaviour(p1_fsm). %% API -export([start/2, start_link/2, route/3, route/4]). -export([init/1, wait_for_request/2, wait_for_response/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("esip/include/esip.hrl"). -define(SIGN_LIFETIME, 300). %% in seconds. -record(state, {host = <<"">> :: binary(), opts = [] :: [{certfile, binary()}], orig_trid, responses = [] :: [#sip{}], tr_ids = [] :: list(), orig_req = #sip{} :: #sip{}}). %%%=================================================================== %%% API %%%=================================================================== start(LServer, Opts) -> supervisor:start_child(mod_sip_proxy_sup, [LServer, Opts]). start_link(LServer, Opts) -> p1_fsm:start_link(?MODULE, [LServer, Opts], []). route(SIPMsg, _SIPSock, TrID, Pid) -> p1_fsm:send_event(Pid, {SIPMsg, TrID}). route(#sip{hdrs = Hdrs} = Req, LServer, Opts) -> case proplists:get_bool(authenticated, Opts) of true -> route_statelessly(Req, LServer, Opts); false -> ConfiguredRRoute = get_configured_record_route(LServer), case esip:get_hdrs('route', Hdrs) of [{_, URI, _}|_] -> case cmp_uri(URI, ConfiguredRRoute) of true -> case is_signed_by_me(URI#uri.user, Hdrs) of true -> route_statelessly(Req, LServer, Opts); false -> error end; false -> error end; [] -> error end end. route_statelessly(Req, LServer, Opts) -> Req1 = prepare_request(LServer, Req), case connect(Req1, add_certfile(LServer, Opts)) of {ok, SIPSocketsWithURIs} -> lists:foreach( fun({SIPSocket, _URI}) -> Req2 = add_via(SIPSocket, LServer, Req1), esip:send(SIPSocket, Req2) end, SIPSocketsWithURIs); _ -> error end. %%%=================================================================== %%% gen_fsm callbacks %%%=================================================================== init([Host, Opts]) -> Opts1 = add_certfile(Host, Opts), {ok, wait_for_request, #state{opts = Opts1, host = Host}}. wait_for_request({#sip{type = request} = Req, TrID}, State) -> Opts = State#state.opts, Req1 = prepare_request(State#state.host, Req), case connect(Req1, Opts) of {ok, SIPSocketsWithURIs} -> NewState = lists:foldl( fun(_SIPSocketWithURI, {error, _} = Err) -> Err; ({SIPSocket, URI}, #state{tr_ids = TrIDs} = AccState) -> Req2 = add_record_route_and_set_uri( URI, State#state.host, Req1), Req3 = add_via(SIPSocket, State#state.host, Req2), case esip:request(SIPSocket, Req3, {?MODULE, route, [self()]}) of {ok, ClientTrID} -> NewTrIDs = [ClientTrID|TrIDs], AccState#state{tr_ids = NewTrIDs}; Err -> cancel_pending_transactions(AccState), Err end end, State, SIPSocketsWithURIs), case NewState of {error, _} = Err -> {Status, Reason} = esip:error_status(Err), esip:reply(TrID, mod_sip:make_response( Req, #sip{type = response, status = Status, reason = Reason})), {stop, normal, State}; _ -> {next_state, wait_for_response, NewState#state{orig_req = Req, orig_trid = TrID}} end; {error, notfound} -> esip:reply(TrID, mod_sip:make_response( Req, #sip{type = response, status = 480, reason = esip:reason(480)})), {stop, normal, State}; Err -> {Status, Reason} = esip:error_status(Err), esip:reply(TrID, mod_sip:make_response( Req, #sip{type = response, status = Status, reason = Reason})), {stop, normal, State} end; wait_for_request(_Event, State) -> {next_state, wait_for_request, State}. wait_for_response({#sip{method = <<"CANCEL">>, type = request}, _TrID}, State) -> cancel_pending_transactions(State), {next_state, wait_for_response, State}; wait_for_response({Resp, TrID}, #state{orig_req = #sip{method = Method} = Req} = State) -> case Resp of {error, timeout} when Method /= <<"INVITE">> -> %% Absorb useless 408. See RFC4320 choose_best_response(State), esip:stop_transaction(State#state.orig_trid), {stop, normal, State}; {error, _} -> {Status, Reason} = esip:error_status(Resp), State1 = mark_transaction_as_complete(TrID, State), SIPResp = mod_sip:make_response(Req, #sip{type = response, status = Status, reason = Reason}), State2 = collect_response(SIPResp, State1), case State2#state.tr_ids of [] -> choose_best_response(State2), {stop, normal, State2}; _ -> {next_state, wait_for_response, State2} end; #sip{status = 100} -> {next_state, wait_for_response, State}; #sip{status = Status} -> {[_|Vias], NewHdrs} = esip:split_hdrs('via', Resp#sip.hdrs), NewResp = case Vias of [] -> Resp#sip{hdrs = NewHdrs}; _ -> Resp#sip{hdrs = [{'via', Vias}|NewHdrs]} end, if Status < 300 -> esip:reply(State#state.orig_trid, NewResp); true -> ok end, State1 = if Status >= 200 -> mark_transaction_as_complete(TrID, State); true -> State end, State2 = if Status >= 300 -> collect_response(NewResp, State1); true -> State1 end, if Status >= 600 -> cancel_pending_transactions(State2); true -> ok end, case State2#state.tr_ids of [] -> choose_best_response(State2), {stop, normal, State2}; _ -> {next_state, wait_for_response, State2} end end; wait_for_response(_Event, State) -> {next_state, wait_for_response, State}. handle_event(_Event, StateName, State) -> {next_state, StateName, State}. handle_sync_event(_Event, _From, StateName, State) -> Reply = ok, {reply, Reply, StateName, State}. handle_info(_Info, StateName, State) -> {next_state, StateName, State}. terminate(_Reason, _StateName, _State) -> ok. code_change(_OldVsn, StateName, State, _Extra) -> {ok, StateName, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== connect(#sip{hdrs = Hdrs} = Req, Opts) -> {_, ToURI, _} = esip:get_hdr('to', Hdrs), case mod_sip:at_my_host(ToURI) of true -> LUser = jid:nodeprep(ToURI#uri.user), LServer = jid:nameprep(ToURI#uri.host), case mod_sip_registrar:find_sockets(LUser, LServer) of [_|_] = SIPSocks -> {ok, SIPSocks}; [] -> {error, notfound} end; false -> case esip:connect(Req, Opts) of {ok, SIPSock} -> {ok, [{SIPSock, Req#sip.uri}]}; {error, _} = Err -> Err end end. cancel_pending_transactions(State) -> lists:foreach(fun esip:cancel/1, State#state.tr_ids). add_certfile(LServer, Opts) -> case ejabberd_pkix:get_certfile(LServer) of {ok, CertFile} -> [{certfile, CertFile}|Opts]; error -> case ejabberd_config:get_option({domain_certfile, LServer}) of CertFile when is_binary(CertFile) -> [{certfile, CertFile}|Opts]; _ -> Opts end end. add_via(#sip_socket{type = Transport}, LServer, #sip{hdrs = Hdrs} = Req) -> ConfiguredVias = get_configured_vias(LServer), {ViaHost, ViaPort} = proplists:get_value( Transport, ConfiguredVias, {LServer, undefined}), ViaTransport = case Transport of tls -> <<"TLS">>; tcp -> <<"TCP">>; udp -> <<"UDP">> end, Via = #via{transport = ViaTransport, host = ViaHost, port = ViaPort, params = [{<<"branch">>, esip:make_branch()}]}, Req#sip{hdrs = [{'via', [Via]}|Hdrs]}. add_record_route_and_set_uri(URI, LServer, #sip{hdrs = Hdrs} = Req) -> case is_request_within_dialog(Req) of false -> case need_record_route(LServer) of true -> RR_URI = get_configured_record_route(LServer), TS = (integer_to_binary(p1_time_compat:system_time(seconds))), Sign = make_sign(TS, Hdrs), User = <>, NewRR_URI = RR_URI#uri{user = User}, Hdrs1 = [{'record-route', [{<<>>, NewRR_URI, []}]}|Hdrs], Req#sip{uri = URI, hdrs = Hdrs1}; false -> Req end; true -> Req end. is_request_within_dialog(#sip{hdrs = Hdrs}) -> {_, _, Params} = esip:get_hdr('to', Hdrs), esip:has_param(<<"tag">>, Params). need_record_route(LServer) -> gen_mod:get_module_opt(LServer, mod_sip, always_record_route, true). make_sign(TS, Hdrs) -> {_, #uri{user = FUser, host = FServer}, FParams} = esip:get_hdr('from', Hdrs), {_, #uri{user = TUser, host = TServer}, _} = esip:get_hdr('to', Hdrs), LFUser = safe_nodeprep(FUser), LTUser = safe_nodeprep(TUser), LFServer = safe_nameprep(FServer), LTServer = safe_nameprep(TServer), FromTag = esip:get_param(<<"tag">>, FParams), CallID = esip:get_hdr('call-id', Hdrs), SharedKey = ejabberd_config:get_option(shared_key), str:sha([SharedKey, LFUser, LFServer, LTUser, LTServer, FromTag, CallID, TS]). is_signed_by_me(TS_Sign, Hdrs) -> try [TSBin, Sign] = str:tokens(TS_Sign, <<"-">>), TS = (binary_to_integer(TSBin)), NowTS = p1_time_compat:system_time(seconds), true = (NowTS - TS) =< ?SIGN_LIFETIME, Sign == make_sign(TSBin, Hdrs) catch _:_ -> false end. get_configured_vias(LServer) -> gen_mod:get_module_opt(LServer, mod_sip, via, []). get_configured_record_route(LServer) -> gen_mod:get_module_opt( LServer, mod_sip, record_route, #uri{host = LServer, params = [{<<"lr">>, <<"">>}]}). get_configured_routes(LServer) -> gen_mod:get_module_opt( LServer, mod_sip, routes, [#uri{host = LServer, params = [{<<"lr">>, <<"">>}]}]). mark_transaction_as_complete(TrID, State) -> NewTrIDs = lists:delete(TrID, State#state.tr_ids), State#state{tr_ids = NewTrIDs}. collect_response(Resp, #state{responses = Resps} = State) -> State#state{responses = [Resp|Resps]}. choose_best_response(#state{responses = Responses} = State) -> SortedResponses = lists:keysort(#sip.status, Responses), case lists:filter( fun(#sip{status = Status}) -> Status >= 600 end, SortedResponses) of [Resp|_] -> esip:reply(State#state.orig_trid, Resp); [] -> case SortedResponses of [Resp|_] -> esip:reply(State#state.orig_trid, Resp); [] -> ok end end. %% Just compare host part only. cmp_uri(#uri{host = H1}, #uri{host = H2}) -> jid:nameprep(H1) == jid:nameprep(H2). is_my_route(URI, URIs) -> lists:any(fun(U) -> cmp_uri(URI, U) end, URIs). prepare_request(LServer, #sip{hdrs = Hdrs} = Req) -> ConfiguredRRoute = get_configured_record_route(LServer), ConfiguredRoutes = get_configured_routes(LServer), Hdrs1 = lists:flatmap( fun({Hdr, HdrList}) when Hdr == 'route'; Hdr == 'record-route' -> case lists:filter( fun({_, URI, _}) -> not cmp_uri(URI, ConfiguredRRoute) and not is_my_route(URI, ConfiguredRoutes) end, HdrList) of [] -> []; HdrList1 -> [{Hdr, HdrList1}] end; (Hdr) -> [Hdr] end, Hdrs), MF = esip:get_hdr('max-forwards', Hdrs1), Hdrs2 = esip:set_hdr('max-forwards', MF-1, Hdrs1), Hdrs3 = lists:filter( fun({'proxy-authorization', {_, Params}}) -> Realm = esip:unquote(esip:get_param(<<"realm">>, Params)), not mod_sip:is_my_host(jid:nameprep(Realm)); (_) -> true end, Hdrs2), Req#sip{hdrs = Hdrs3}. safe_nodeprep(S) -> case jid:nodeprep(S) of error -> S; S1 -> S1 end. safe_nameprep(S) -> case jid:nameprep(S) of error -> S; S1 -> S1 end. -endif. ejabberd-18.01/src/ejabberd_pkix.erl0000644000232200023220000006600513225664356017736 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 4 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_pkix). -behaviour(gen_server). -behaviour(ejabberd_config). %% API -export([start_link/0, add_certfile/1, format_error/1, opt_type/1, get_certfile/1, try_certfile/1, route_registered/1, config_reloaded/0, certs_dir/0, ca_file/0, get_default_certfile/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include_lib("public_key/include/public_key.hrl"). -include("logger.hrl"). -record(state, {validate = true :: boolean(), notify = false :: boolean(), paths = [] :: [file:filename()], certs = #{} :: map(), graph :: digraph:graph(), keys = [] :: [public_key:private_key()]}). -type state() :: #state{}. -type cert() :: #'OTPCertificate'{}. -type priv_key() :: public_key:private_key(). -type pub_key() :: #'RSAPublicKey'{} | {integer(), #'Dss-Parms'{}} | #'ECPoint'{}. -type bad_cert_reason() :: cert_expired | invalid_issuer | invalid_signature | name_not_permitted | missing_basic_constraint | invalid_key_usage | selfsigned_peer | unknown_sig_algo | unknown_ca | missing_priv_key. -type bad_cert() :: {bad_cert, bad_cert_reason()}. -type cert_error() :: not_cert | not_der | not_pem | encrypted. -export_type([cert_error/0]). -define(CA_CACHE, ca_cache). %%%=================================================================== %%% API %%%=================================================================== -spec add_certfile(filename:filename()) -> ok | {error, cert_error() | file:posix()}. add_certfile(Path) -> gen_server:call(?MODULE, {add_certfile, prep_path(Path)}). -spec try_certfile(filename:filename()) -> binary(). try_certfile(Path0) -> Path = prep_path(Path0), case load_certfile(Path) of {ok, _, _} -> Path; {error, _} -> erlang:error(badarg) end. route_registered(Route) -> gen_server:call(?MODULE, {route_registered, Route}). -spec format_error(cert_error() | file:posix()) -> string(). format_error(not_cert) -> "no PEM encoded certificates found"; format_error(not_pem) -> "failed to decode from PEM format"; format_error(not_der) -> "failed to decode from DER format"; format_error(encrypted) -> "encrypted certificate"; format_error({bad_cert, cert_expired}) -> "certificate is no longer valid as its expiration date has passed"; format_error({bad_cert, invalid_issuer}) -> "certificate issuer name does not match the name of the " "issuer certificate"; format_error({bad_cert, invalid_signature}) -> "certificate was not signed by its issuer certificate"; format_error({bad_cert, name_not_permitted}) -> "invalid Subject Alternative Name extension"; format_error({bad_cert, missing_basic_constraint}) -> "certificate, required to have the basic constraints extension, " "does not have a basic constraints extension"; format_error({bad_cert, invalid_key_usage}) -> "certificate key is used in an invalid way according " "to the key-usage extension"; format_error({bad_cert, selfsigned_peer}) -> "self-signed certificate"; format_error({bad_cert, unknown_sig_algo}) -> "certificate is signed using unknown algorithm"; format_error({bad_cert, unknown_ca}) -> "certificate is signed by unknown CA"; format_error({bad_cert, missing_priv_key}) -> "no matching private key found for certificate in the chain"; format_error({bad_cert, Unknown}) -> lists:flatten(io_lib:format("~w", [Unknown])); format_error(Why) -> case file:format_error(Why) of "unknown POSIX error" -> atom_to_list(Why); Reason -> Reason end. -spec get_certfile(binary()) -> {ok, binary()} | error. get_certfile(Domain) -> case get_certfile_no_default(Domain) of {ok, Path} -> {ok, Path}; error -> get_default_certfile() end. -spec get_certfile_no_default(binary()) -> {ok, binary()} | error. get_certfile_no_default(Domain) -> case ejabberd_idna:domain_utf8_to_ascii(Domain) of false -> error; ASCIIDomain -> case ets:lookup(?MODULE, ASCIIDomain) of [] -> case binary:split(ASCIIDomain, <<".">>, [trim]) of [_, Host] -> case ets:lookup(?MODULE, <<"*.", Host/binary>>) of [{_, Path}|_] -> {ok, Path}; [] -> error end; _ -> error end; [{_, Path}|_] -> {ok, Path} end end. -spec get_default_certfile() -> {ok, binary()} | error. get_default_certfile() -> case ets:first(?MODULE) of '$end_of_table' -> error; Domain -> case ets:lookup(?MODULE, Domain) of [{_, Path}|_] -> {ok, Path}; [] -> error end end. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). config_reloaded() -> case use_cache() of true -> init_cache(); false -> delete_cache() end, gen_server:call(?MODULE, config_reloaded, 60000). opt_type(ca_path) -> fun(Path) -> binary_to_list(Path) end; opt_type(ca_file) -> fun(Path) -> binary_to_list(misc:try_read_file(Path)) end; opt_type(certfiles) -> fun(CertList) -> [binary_to_list(Path) || Path <- CertList] end; opt_type(O) when O == c2s_certfile; O == s2s_certfile; O == domain_certfile -> fun(File) -> ?WARNING_MSG("option '~s' is deprecated, use 'certfiles' instead", [O]), misc:try_read_file(File) end; opt_type(_) -> [ca_path, ca_file, certfiles, c2s_certfile, s2s_certfile, domain_certfile]. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> Notify = start_fs(), process_flag(trap_exit, true), ets:new(?MODULE, [named_table, public]), ejabberd_hooks:add(route_registered, ?MODULE, route_registered, 50), ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 30), Validate = case os:type() of {win32, _} -> false; _ -> code:ensure_loaded(public_key), erlang:function_exported( public_key, short_name_hash, 1) end, if Validate -> check_ca(); true -> ok end, G = digraph:new([acyclic]), init_cache(), State = #state{validate = Validate, notify = Notify, graph = G}, case filelib:ensure_dir(filename:join(certs_dir(), "foo")) of ok -> clean_dir(certs_dir()), case add_certfiles(State) of {ok, State1} -> {ok, State1}; {error, Why} -> {stop, Why} end; {error, Why} -> ?CRITICAL_MSG("Failed to create directory ~s: ~s", [certs_dir(), file:format_error(Why)]), {stop, Why} end. handle_call({add_certfile, Path}, _, State) -> case add_certfile(Path, State) of {ok, State1} -> if State /= State1 -> case build_chain_and_check(State1) of {ok, State2} -> {reply, ok, State2}; Err -> {reply, Err, State1} end; true -> {reply, ok, State1} end; {Err, State1} -> {reply, Err, State1} end; handle_call({route_registered, Host}, _, State) -> case add_certfiles(Host, State) of {ok, NewState} -> case get_certfile_no_default(Host) of {ok, _} -> ok; error -> ?WARNING_MSG("No certificate found matching '~s': strictly " "configured clients or servers will reject " "connections with this host; obtain " "a certificate for this (sub)domain from any " "trusted CA such as Let's Encrypt " "(www.letsencrypt.org)", [Host]) end, {reply, ok, NewState}; {error, _} -> {reply, ok, State} end; handle_call(config_reloaded, _From, State) -> State1 = State#state{paths = [], certs = #{}, keys = []}, case add_certfiles(State1) of {ok, State2} -> {reply, ok, State2}; {error, _} = Err -> {reply, Err, State} end; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({_, {fs, file_event}, {File, Events}}, State) -> ?DEBUG("got FS events for ~s: ~p", [File, Events]), Path = iolist_to_binary(File), case lists:member(modified, Events) of true -> case lists:member(Path, State#state.paths) of true -> handle_cast(config_reloaded, State); false -> {noreply, State} end; false -> {noreply, State} end; handle_info(_Info, State) -> ?WARNING_MSG("unexpected info: ~p", [_Info]), {noreply, State}. terminate(_Reason, _State) -> ejabberd_hooks:delete(route_registered, ?MODULE, route_registered, 50), ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 30), clean_dir(certs_dir()). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec certfiles_from_config_options() -> [atom()]. certfiles_from_config_options() -> [c2s_certfile, s2s_certfile, domain_certfile]. -spec get_certfiles_from_config_options(state()) -> [binary()]. get_certfiles_from_config_options(_State) -> Global = case ejabberd_config:get_option(certfiles) of undefined -> []; Paths -> lists:flatmap( fun(Path) -> case wildcard(Path) of [] -> ?WARNING_MSG( "Path ~s is empty, please " "make sure ejabberd has " "sufficient rights to read it", [Path]), []; Fs -> Fs end end, Paths) end, Local = lists:flatmap( fun(OptHost) -> case ejabberd_config:get_option(OptHost) of undefined -> []; Path -> [Path] end end, [{Opt, Host} || Opt <- certfiles_from_config_options(), Host <- ejabberd_config:get_myhosts()]), [iolist_to_binary(P) || P <- lists:usort(Local ++ Global)]. -spec add_certfiles(state()) -> {ok, state()} | {error, bad_cert()}. add_certfiles(State) -> ?DEBUG("Reading certificates", []), Paths = get_certfiles_from_config_options(State), State1 = lists:foldl( fun(Path, Acc) -> {_, NewAcc} = add_certfile(Path, Acc), NewAcc end, State, Paths), case build_chain_and_check(State1) of ok -> {ok, State1}; {error, _} = Err -> Err end. -spec add_certfiles(binary(), state()) -> {ok, state()} | {error, bad_cert()}. add_certfiles(Host, State) -> State1 = lists:foldl( fun(Opt, AccState) -> case ejabberd_config:get_option({Opt, Host}) of undefined -> AccState; Path -> {_, NewAccState} = add_certfile(Path, AccState), NewAccState end end, State, certfiles_from_config_options()), if State /= State1 -> case build_chain_and_check(State1) of ok -> {ok, State1}; {error, _} = Err -> Err end; true -> {ok, State} end. -spec add_certfile(file:filename_all(), state()) -> {ok, state()} | {{error, cert_error()}, state()}. add_certfile(Path, State) -> case lists:member(Path, State#state.paths) of true -> {ok, State}; false -> case load_certfile(Path) of {ok, Certs, Keys} -> NewCerts = lists:foldl( fun(Cert, Acc) -> maps:put(Cert, Path, Acc) end, State#state.certs, Certs), {ok, State#state{paths = [Path|State#state.paths], certs = NewCerts, keys = Keys ++ State#state.keys}}; {error, Why} = Err -> ?ERROR_MSG("failed to read certificate from ~s: ~s", [Path, format_error(Why)]), {Err, State} end end. -spec build_chain_and_check(state()) -> ok | {error, bad_cert()}. build_chain_and_check(State) -> ?DEBUG("Building certificates graph", []), CertPaths = get_cert_paths(maps:keys(State#state.certs), State#state.graph), ?DEBUG("Finding matched certificate keys", []), case match_cert_keys(CertPaths, State#state.keys) of {ok, Chains} -> ?DEBUG("Storing certificate chains", []), CertFilesWithDomains = store_certs(Chains, []), ets:delete_all_objects(?MODULE), lists:foreach( fun({Path, Domain}) -> fast_tls:add_certfile(Domain, Path), ets:insert(?MODULE, {Domain, Path}) end, CertFilesWithDomains), ?DEBUG("Validating certificates", []), Errors = validate(CertPaths, State#state.validate), ?DEBUG("Subscribing to file events", []), subscribe(State), lists:foreach( fun({Cert, Why}) -> Path = maps:get(Cert, State#state.certs), ?WARNING_MSG("Failed to validate certificate from ~s: ~s", [Path, format_error(Why)]) end, Errors); {error, Cert, Why} -> Path = maps:get(Cert, State#state.certs), ?ERROR_MSG("Failed to build certificate chain for ~s: ~s", [Path, format_error(Why)]), {error, Why} end. -spec store_certs([{[cert()], priv_key()}], [{binary(), binary()}]) -> [{binary(), binary()}]. store_certs([{Certs, Key}|Chains], Acc) -> CertPEMs = public_key:pem_encode( lists:map( fun(Cert) -> Type = element(1, Cert), DER = public_key:pkix_encode(Type, Cert, otp), {'Certificate', DER, not_encrypted} end, Certs)), KeyPEM = public_key:pem_encode( [{element(1, Key), public_key:der_encode(element(1, Key), Key), not_encrypted}]), PEMs = <>, Cert = hd(Certs), Domains = xmpp_stream_pkix:get_cert_domains(Cert), FileName = filename:join(certs_dir(), str:sha(PEMs)), case file:write_file(FileName, PEMs) of ok -> file:change_mode(FileName, 8#600), NewAcc = [{FileName, Domain} || Domain <- Domains] ++ Acc, store_certs(Chains, NewAcc); {error, Why} -> ?ERROR_MSG("Failed to write to ~s: ~s", [FileName, file:format_error(Why)]), store_certs(Chains, []) end; store_certs([], Acc) -> Acc. -spec load_certfile(file:filename_all()) -> {ok, [cert()], [priv_key()]} | {error, cert_error() | file:posix()}. load_certfile(Path) -> try {ok, Data} = file:read_file(Path), pem_decode(Data) catch _:{badmatch, {error, _} = Err} -> Err end. -spec pem_decode(binary()) -> {ok, [cert()], [priv_key()]} | {error, cert_error()}. pem_decode(Data) -> try public_key:pem_decode(Data) of PemEntries -> case decode_certs(PemEntries) of {error, _} = Err -> Err; Objects -> case lists:partition( fun(#'OTPCertificate'{}) -> true; (_) -> false end, Objects) of {[], []} -> {error, not_cert}; {Certs, PrivKeys} -> {ok, Certs, PrivKeys} end end catch _:_ -> {error, not_pem} end. -spec decode_certs([public_key:pem_entry()]) -> {[cert()], [priv_key()]} | {error, not_der | encrypted}. decode_certs(PemEntries) -> try lists:foldr( fun(_, {error, _} = Err) -> Err; ({_, _, Flag}, _) when Flag /= not_encrypted -> {error, encrypted}; ({'Certificate', Der, _}, Acc) -> [public_key:pkix_decode_cert(Der, otp)|Acc]; ({'PrivateKeyInfo', Der, not_encrypted}, Acc) -> #'PrivateKeyInfo'{privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{ algorithm = Algo}, privateKey = Key} = public_key:der_decode('PrivateKeyInfo', Der), case Algo of ?'rsaEncryption' -> [public_key:der_decode( 'RSAPrivateKey', iolist_to_binary(Key))|Acc]; ?'id-dsa' -> [public_key:der_decode( 'DSAPrivateKey', iolist_to_binary(Key))|Acc]; ?'id-ecPublicKey' -> [public_key:der_decode( 'ECPrivateKey', iolist_to_binary(Key))|Acc]; _ -> Acc end; ({Tag, Der, _}, Acc) when Tag == 'RSAPrivateKey'; Tag == 'DSAPrivateKey'; Tag == 'ECPrivateKey' -> [public_key:der_decode(Tag, Der)|Acc]; (_, Acc) -> Acc end, [], PemEntries) catch _:_ -> {error, not_der} end. -spec validate([{path, [cert()]}], boolean()) -> [{cert(), bad_cert()}]. validate(Paths, true) -> {ok, Re} = re:compile("^[a-f0-9]+\\.[0-9]+$", [unicode]), Hashes = case file:list_dir(ca_dir()) of {ok, Files} -> lists:foldl( fun(File, Acc) -> try re:run(File, Re) of {match, _} -> [Hash|_] = string:tokens(File, "."), Path = filename:join(ca_dir(), File), dict:append(Hash, Path, Acc); nomatch -> Acc catch _:badarg -> ?ERROR_MSG("Regexp failure on ~w", [File]), Acc end end, dict:new(), Files); {error, Why} -> ?ERROR_MSG("Failed to list directory ~s: ~s", [ca_dir(), file:format_error(Why)]), dict:new() end, lists:filtermap( fun({path, Path}) -> case validate_path(Path, Hashes) of ok -> false; {error, Cert, Reason} -> {true, {Cert, Reason}} end end, Paths); validate(_, _) -> []. -spec validate_path([cert()], dict:dict()) -> ok | {error, cert(), bad_cert()}. validate_path([Cert|_] = Certs, Cache) -> case find_local_issuer(Cert, Cache) of {ok, IssuerCert} -> try public_key:pkix_path_validation(IssuerCert, Certs, []) of {ok, _} -> ok; {error, Reason} -> {error, Cert, Reason} catch error:function_clause -> case erlang:get_stacktrace() of [{public_key, pkix_sign_types, _, _}|_] -> {error, Cert, {bad_cert, unknown_sig_algo}}; ST -> %% Bug in public_key application erlang:raise(error, function_clause, ST) end end; {error, Reason} -> case public_key:pkix_is_self_signed(Cert) of true -> {error, Cert, {bad_cert, selfsigned_peer}}; false -> {error, Cert, Reason} end end. -spec ca_dir() -> string(). ca_dir() -> ejabberd_config:get_option(ca_path, "/etc/ssl/certs"). -spec ca_file() -> string() | undefined. ca_file() -> ejabberd_config:get_option(ca_file). -spec certs_dir() -> string(). certs_dir() -> MnesiaDir = mnesia:system_info(directory), filename:join(MnesiaDir, "certs"). -spec clean_dir(file:filename_all()) -> ok. clean_dir(Dir) -> ?DEBUG("Cleaning directory ~s", [Dir]), Files = wildcard(filename:join(Dir, "*")), lists:foreach( fun(Path) -> case filelib:is_file(Path) of true -> file:delete(Path); false -> ok end end, Files). -spec check_ca() -> ok. check_ca() -> CAFile = ca_file(), case wildcard(filename:join(ca_dir(), "*.0")) of [] when CAFile == undefined -> Hint = "configuring 'ca_path' or 'ca_file' options might help", case file:list_dir(ca_dir()) of {error, Why} -> ?WARNING_MSG("failed to read CA directory ~s: ~s; ~s", [ca_dir(), file:format_error(Why), Hint]); {ok, _} -> ?WARNING_MSG("CA directory ~s doesn't contain " "hashed certificate files; ~s", [ca_dir(), Hint]) end; _ -> ok end. -spec find_local_issuer(cert(), dict:dict()) -> {ok, cert()} | {error, {bad_cert, unknown_ca}}. find_local_issuer(Cert, Hashes) -> case find_issuer_in_dir(Cert, Hashes) of {ok, IssuerCert} -> {ok, IssuerCert}; {error, Reason} -> case ca_file() of undefined -> {error, Reason}; CAFile -> find_issuer_in_file(Cert, CAFile) end end. -spec find_issuer_in_dir(cert(), dict:dict()) -> {{ok, cert()} | {error, {bad_cert, unknown_ca}}, dict:dict()}. find_issuer_in_dir(Cert, Cache) -> {ok, {_, IssuerID}} = public_key:pkix_issuer_id(Cert, self), Hash = short_name_hash(IssuerID), Files = case dict:find(Hash, Cache) of {ok, L} -> L; error -> [] end, lists:foldl( fun(_, {ok, _IssuerCert} = Acc) -> Acc; (Path, Err) -> case read_ca_file(Path) of {ok, [IssuerCert|_]} -> case public_key:pkix_is_issuer(Cert, IssuerCert) of true -> {ok, IssuerCert}; false -> Err end; error -> Err end end, {error, {bad_cert, unknown_ca}}, Files). -spec find_issuer_in_file(cert(), file:filename_all() | undefined) -> {ok, cert()} | {error, {bad_cert, unknown_ca}}. find_issuer_in_file(_Cert, undefined) -> {error, {bad_cert, unknown_ca}}; find_issuer_in_file(Cert, CAFile) -> case read_ca_file(CAFile) of {ok, IssuerCerts} -> lists:foldl( fun(_, {ok, _} = Res) -> Res; (IssuerCert, Err) -> case public_key:pkix_is_issuer(Cert, IssuerCert) of true -> {ok, IssuerCert}; false -> Err end end, {error, {bad_cert, unknown_ca}}, IssuerCerts); error -> {error, {bad_cert, unknown_ca}} end. -spec read_ca_file(file:filename_all()) -> {ok, [cert()]} | error. read_ca_file(Path) -> case use_cache() of true -> ets_cache:lookup(?CA_CACHE, Path, fun() -> do_read_ca_file(Path) end); false -> do_read_ca_file(Path) end. -spec do_read_ca_file(file:filename_all()) -> {ok, [cert()]} | error. do_read_ca_file(Path) -> try {ok, Data} = file:read_file(Path), {ok, IssuerCerts, _} = pem_decode(Data), {ok, IssuerCerts} catch _:{badmatch, {error, Why}} -> ?ERROR_MSG("Failed to read CA certificate " "from \"~s\": ~s", [Path, format_error(Why)]), error end. -spec match_cert_keys([{path, [cert()]}], [priv_key()]) -> {ok, [{cert(), priv_key()}]} | {error, {bad_cert, missing_priv_key}}. match_cert_keys(CertPaths, PrivKeys) -> KeyPairs = [{pubkey_from_privkey(PrivKey), PrivKey} || PrivKey <- PrivKeys], match_cert_keys(CertPaths, KeyPairs, []). -spec match_cert_keys([{path, [cert()]}], [{pub_key(), priv_key()}], [{cert(), priv_key()}]) -> {ok, [{[cert()], priv_key()}]} | {error, cert(), {bad_cert, missing_priv_key}}. match_cert_keys([{path, Certs}|CertPaths], KeyPairs, Result) -> [Cert|_] = RevCerts = lists:reverse(Certs), PubKey = pubkey_from_cert(Cert), case lists:keyfind(PubKey, 1, KeyPairs) of false -> {error, Cert, {bad_cert, missing_priv_key}}; {_, PrivKey} -> match_cert_keys(CertPaths, KeyPairs, [{RevCerts, PrivKey}|Result]) end; match_cert_keys([], _, Result) -> {ok, Result}. -spec pubkey_from_cert(cert()) -> pub_key(). pubkey_from_cert(Cert) -> TBSCert = Cert#'OTPCertificate'.tbsCertificate, PubKeyInfo = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, SubjPubKey = PubKeyInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey, case PubKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm of #'PublicKeyAlgorithm'{ algorithm = ?rsaEncryption} -> SubjPubKey; #'PublicKeyAlgorithm'{ algorithm = ?'id-dsa', parameters = {params, DSSParams}} -> {SubjPubKey, DSSParams}; #'PublicKeyAlgorithm'{ algorithm = ?'id-ecPublicKey'} -> SubjPubKey end. -spec pubkey_from_privkey(priv_key()) -> pub_key(). pubkey_from_privkey(#'RSAPrivateKey'{modulus = Modulus, publicExponent = Exp}) -> #'RSAPublicKey'{modulus = Modulus, publicExponent = Exp}; pubkey_from_privkey(#'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}) -> {Y, #'Dss-Parms'{p = P, q = Q, g = G}}; pubkey_from_privkey(#'ECPrivateKey'{publicKey = Key}) -> #'ECPoint'{point = Key}. -spec get_cert_paths([cert()], digraph:graph()) -> [{path, [cert()]}]. get_cert_paths(Certs, G) -> {NewCerts, OldCerts} = lists:partition( fun(Cert) -> case digraph:vertex(G, Cert) of false -> digraph:add_vertex(G, Cert), true; {_, _} -> false end end, Certs), add_edges(G, NewCerts, OldCerts), add_edges(G, OldCerts, NewCerts), add_edges(G, NewCerts, NewCerts), lists:flatmap( fun(Cert) -> case digraph:in_degree(G, Cert) of 0 -> get_cert_path(G, [Cert]); _ -> [] end end, Certs). add_edges(G, [Cert1|T], L) -> case public_key:pkix_is_self_signed(Cert1) of true -> ok; false -> lists:foreach( fun(Cert2) when Cert1 /= Cert2 -> case public_key:pkix_is_issuer(Cert1, Cert2) of true -> digraph:add_edge(G, Cert1, Cert2); false -> ok end; (_) -> ok end, L) end, add_edges(G, T, L); add_edges(_, [], _) -> ok. get_cert_path(G, [Root|_] = Acc) -> case digraph:out_edges(G, Root) of [] -> [{path, Acc}]; Es -> lists:flatmap( fun(E) -> {_, _, V, _} = digraph:edge(G, E), get_cert_path(G, [V|Acc]) end, Es) end. -spec prep_path(filename:filename()) -> binary(). prep_path(Path0) -> case filename:pathtype(Path0) of relative -> {ok, CWD} = file:get_cwd(), iolist_to_binary(filename:join(CWD, Path0)); _ -> iolist_to_binary(Path0) end. -ifdef(SHORT_NAME_HASH). short_name_hash(IssuerID) -> public_key:short_name_hash(IssuerID). -else. short_name_hash(_) -> "". -endif. -spec subscribe(state()) -> ok. subscribe(#state{notify = true} = State) -> lists:foreach( fun(Path) -> Dir = filename:dirname(Path), Name = list_to_atom(integer_to_list(erlang:phash2(Dir))), case fs:start_link(Name, Dir) of {ok, _} -> ?DEBUG("Subscribed to FS events from ~s", [Dir]), fs:subscribe(Name); {error, _} -> ok end end, State#state.paths); subscribe(_) -> ok. -spec start_fs() -> boolean(). start_fs() -> application:load(fs), application:set_env(fs, backwards_compatible, false), case application:ensure_all_started(fs) of {ok, _} -> true; {error, {already_loaded, _}} -> true; {error, Reason} -> ?ERROR_MSG("Failed to load 'fs' Erlang application: ~p; " "certificates change detection will be disabled. " "You should now manually run `ejabberdctl " "reload_config` whenever certificates are changed " "on disc", [Reason]), false end. wildcard(Path) when is_binary(Path) -> wildcard(binary_to_list(Path)); wildcard(Path) -> filelib:wildcard(Path). -spec use_cache() -> boolean(). use_cache() -> ejabberd_config:use_cache(global). -spec init_cache() -> ok. init_cache() -> ets_cache:new(?CA_CACHE, cache_opts()). -spec delete_cache() -> ok. delete_cache() -> ets_cache:delete(?CA_CACHE). -spec cache_opts() -> [proplists:property()]. cache_opts() -> MaxSize = ejabberd_config:cache_size(global), CacheMissed = ejabberd_config:cache_missed(global), LifeTime = case ejabberd_config:cache_life_time(global) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. ejabberd-18.01/src/ejabberd_web.erl0000644000232200023220000000616513225664356017541 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_web.erl %%% Author : Alexey Shchepin %%% Purpose : %%% Purpose : %%% Created : 28 Feb 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_web). -author('alexey@process-one.net'). %% External exports -export([make_xhtml/1, make_xhtml/2, error/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). %% XXX bard: there are variants of make_xhtml in ejabberd_http and %% ejabberd_web_admin. It might be a good idea to centralize it here %% and also create an ejabberd_web.hrl file holding the macros, so %% that third parties can use ejabberd_web as an "utility" library. make_xhtml(Els) -> make_xhtml([], Els). make_xhtml(HeadEls, Els) -> #xmlel{name = <<"html">>, attrs = [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}, {<<"xml:lang">>, <<"en">>}, {<<"lang">>, <<"en">>}], children = [#xmlel{name = <<"head">>, attrs = [], children = [#xmlel{name = <<"meta">>, attrs = [{<<"http-equiv">>, <<"Content-Type">>}, {<<"content">>, <<"text/html; charset=utf-8">>}], children = []} | HeadEls]}, #xmlel{name = <<"body">>, attrs = [], children = Els}]}. -define(X(Name), #xmlel{name = Name, attrs = [], children = []}). -define(XA(Name, Attrs), #xmlel{name = Name, attrs = Attrs, children = []}). -define(XE(Name, Els), #xmlel{name = Name, attrs = [], children = Els}). -define(XAE(Name, Attrs, Els), #xmlel{name = Name, attrs = Attrs, children = Els}). -define(C(Text), {xmlcdata, Text}). -define(XC(Name, Text), ?XE(Name, [?C(Text)])). -define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])). -define(LI(Els), ?XE(<<"li">>, Els)). -define(A(URL, Els), ?XAE(<<"a">>, [{<<"href">>, URL}], Els)). -define(AC(URL, Text), ?A(URL, [?C(Text)])). -define(P, ?X(<<"p">>)). -define(BR, ?X(<<"br">>)). -define(INPUT(Type, Name, Value), ?XA(<<"input">>, [{<<"type">>, Type}, {<<"name">>, Name}, {<<"value">>, Value}])). error(not_found) -> {404, [], make_xhtml([?XC(<<"h1">>, <<"404 Not Found">>)])}; error(not_allowed) -> {401, [], make_xhtml([?XC(<<"h1">>, <<"401 Unauthorized">>)])}. ejabberd-18.01/src/ejabberd_router_multicast.erl0000644000232200023220000002062113225664356022362 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_router_multicast.erl %%% Author : Badlop %%% Purpose : Multicast router %%% Created : 11 Aug 2007 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_router_multicast). -author('alexey@process-one.net'). -author('badlop@process-one.net'). -behaviour(gen_server). %% API -export([route_multicast/4, register_route/1, unregister_route/1 ]). -export([start_link/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -record(route_multicast, {domain = <<"">> :: binary() | '_', pid = self() :: pid()}). -record(state, {}). %%==================================================================== %% API %%==================================================================== %%-------------------------------------------------------------------- %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec route_multicast(jid(), binary(), [jid()], stanza()) -> ok. route_multicast(From, Domain, Destinations, Packet) -> case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From)) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, Domain, Destinations, Packet}]); _ -> ok end. -spec register_route(binary()) -> any(). register_route(Domain) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> Pid = self(), F = fun() -> mnesia:write(#route_multicast{domain = LDomain, pid = Pid}) end, mnesia:transaction(F) end. -spec unregister_route(binary()) -> any(). unregister_route(Domain) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> Pid = self(), F = fun() -> case mnesia:select(route_multicast, [{#route_multicast{pid = Pid, domain = LDomain, _ = '_'}, [], ['$_']}]) of [R] -> mnesia:delete_object(R); _ -> ok end end, mnesia:transaction(F) end. %%==================================================================== %% gen_server callbacks %%==================================================================== %%-------------------------------------------------------------------- %% Function: init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- init([]) -> ejabberd_mnesia:create(?MODULE, route_multicast, [{ram_copies, [node()]}, {type, bag}, {attributes, record_info(fields, route_multicast)}]), mnesia:subscribe({table, route_multicast, simple}), lists:foreach( fun(Pid) -> erlang:monitor(process, Pid) end, mnesia:dirty_select(route_multicast, [{{route_multicast, '_', '$1'}, [], ['$1']}])), {ok, #state{}}. %%-------------------------------------------------------------------- %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast(_Msg, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({route_multicast, Domain, Destinations, Packet}, State) -> case catch do_route(Domain, Destinations, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {Domain, Destinations, Packet}]); _ -> ok end, {noreply, State}; handle_info({mnesia_table_event, {write, #route_multicast{pid = Pid}, _ActivityId}}, State) -> erlang:monitor(process, Pid), {noreply, State}; handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> F = fun() -> Es = mnesia:select( route_multicast, [{#route_multicast{pid = Pid, _ = '_'}, [], ['$_']}]), lists:foreach( fun(E) -> mnesia:delete_object(E) end, Es) end, mnesia:transaction(F), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- terminate(_Reason, _State) -> ok. %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- %% From = #jid %% Destinations = [#jid] -spec do_route(binary(), [jid()], stanza()) -> any(). do_route(Domain, Destinations, Packet) -> ?DEBUG("route multicast:~n~s~nDomain: ~s~nDestinations: ~s~n", [xmpp:pp(Packet), Domain, str:join([jid:encode(To) || To <- Destinations], <<", ">>)]), %% Try to find an appropriate multicast service case mnesia:dirty_read(route_multicast, Domain) of %% If no multicast service is available in this server, send manually [] -> do_route_normal(Destinations, Packet); %% If some is available, send the packet using multicast service Rs when is_list(Rs) -> Pid = pick_multicast_pid(Rs), Pid ! {route_trusted, Destinations, Packet} end. -spec pick_multicast_pid([#route_multicast{}]) -> pid(). pick_multicast_pid(Rs) -> List = case [R || R <- Rs, node(R#route_multicast.pid) == node()] of [] -> Rs; RLocals -> RLocals end, (hd(List))#route_multicast.pid. -spec do_route_normal([jid()], stanza()) -> any(). do_route_normal(Destinations, Packet) -> [ejabberd_router:route(xmpp:set_to(Packet, To)) || To <- Destinations]. ejabberd-18.01/src/node_private.erl0000644000232200023220000001334013225664356017616 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_private.erl %%% Author : Christophe Romain %%% Purpose : %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_private). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost). options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, {purge_offline, false}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, whitelist}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, never}, {deliver_notifications, false}, {presence_based_delivery, false}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"delete-nodes">>, <<"delete-items">>, <<"instant-nodes">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"multi-items">>, <<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>, <<"subscription-notifications">>]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Removed) -> node_flat:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). ejabberd-18.01/src/mod_shared_roster.erl0000644000232200023220000010733013225664356020645 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_shared_roster.erl %%% Author : Alexey Shchepin %%% Purpose : Shared roster management %%% Created : 5 Mar 2005 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_shared_roster). -author('alexey@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, export/1, import_info/0, webadmin_menu/3, webadmin_page/3, get_user_roster/2, get_jid_info/4, import/5, process_item/2, import_start/2, in_subscription/6, out_subscription/4, c2s_self_presence/1, unset_presence/4, register_user/2, remove_user/2, list_groups/1, create_group/2, create_group/3, delete_group/2, get_group_opts/2, set_group_opts/3, get_group_users/2, get_group_explicit_users/2, is_user_in_group/3, add_user_to_group/3, opts_to_binary/1, remove_user_from_group/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_roster.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -include("mod_shared_roster.hrl"). -type group_options() :: [{atom(), any()}]. -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), binary(), [binary()]) -> ok. -callback list_groups(binary()) -> [binary()]. -callback groups_with_opts(binary()) -> [{binary(), group_options()}]. -callback create_group(binary(), binary(), group_options()) -> {atomic, any()}. -callback delete_group(binary(), binary()) -> {atomic, any()}. -callback get_group_opts(binary(), binary()) -> group_options() | error. -callback set_group_opts(binary(), binary(), group_options()) -> {atomic, any()}. -callback get_user_groups({binary(), binary()}, binary()) -> [binary()]. -callback get_group_explicit_users(binary(), binary()) -> [{binary(), binary()}]. -callback get_user_displayed_groups(binary(), binary(), group_options()) -> [{binary(), group_options()}]. -callback is_user_in_group({binary(), binary()}, binary(), binary()) -> boolean(). -callback add_user_to_group(binary(), {binary(), binary()}, binary()) -> any(). -callback remove_user_from_group(binary(), {binary(), binary()}, binary()) -> {atomic, any()}. start(Host, Opts) -> Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, webadmin_menu, 70), ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), ejabberd_hooks:add(roster_get, Host, ?MODULE, get_user_roster, 70), ejabberd_hooks:add(roster_in_subscription, Host, ?MODULE, in_subscription, 30), ejabberd_hooks:add(roster_out_subscription, Host, ?MODULE, out_subscription, 30), ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE, get_jid_info, 70), ejabberd_hooks:add(roster_process_item, Host, ?MODULE, process_item, 50), ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), ejabberd_hooks:add(unset_presence_hook, Host, ?MODULE, unset_presence, 50), ejabberd_hooks:add(register_user, Host, ?MODULE, register_user, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50). stop(Host) -> ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE, webadmin_menu, 70), ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), ejabberd_hooks:delete(roster_get, Host, ?MODULE, get_user_roster, 70), ejabberd_hooks:delete(roster_in_subscription, Host, ?MODULE, in_subscription, 30), ejabberd_hooks:delete(roster_out_subscription, Host, ?MODULE, out_subscription, 30), ejabberd_hooks:delete(roster_get_jid_info, Host, ?MODULE, get_jid_info, 70), ejabberd_hooks:delete(roster_process_item, Host, ?MODULE, process_item, 50), ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), ejabberd_hooks:delete(unset_presence_hook, Host, ?MODULE, unset_presence, 50), ejabberd_hooks:delete(register_user, Host, ?MODULE, register_user, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, ok. depends(_Host, _Opts) -> []. -spec get_user_roster([#roster{}], {binary(), binary()}) -> [#roster{}]. get_user_roster(Items, US) -> {U, S} = US, DisplayedGroups = get_user_displayed_groups(US), SRUsers = lists:foldl(fun (Group, Acc1) -> GroupName = get_group_name(S, Group), lists:foldl(fun (User, Acc2) -> if User == US -> Acc2; true -> dict:append(User, GroupName, Acc2) end end, Acc1, get_group_users(S, Group)) end, dict:new(), DisplayedGroups), {NewItems1, SRUsersRest} = lists:mapfoldl(fun (Item, SRUsers1) -> {_, _, {U1, S1, _}} = Item#roster.usj, US1 = {U1, S1}, case dict:find(US1, SRUsers1) of {ok, GroupNames} -> {Item#roster{subscription = both, groups = Item#roster.groups ++ GroupNames, ask = none}, dict:erase(US1, SRUsers1)}; error -> {Item, SRUsers1} end end, SRUsers, Items), SRItems = [#roster{usj = {U, S, {U1, S1, <<"">>}}, us = US, jid = {U1, S1, <<"">>}, name = get_rosteritem_name(U1, S1), subscription = both, ask = none, groups = GroupNames} || {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)], SRItems ++ NewItems1. get_rosteritem_name(U, S) -> case gen_mod:is_loaded(S, mod_vcard) of true -> SubEls = mod_vcard:get_vcard(U, S), get_rosteritem_name_vcard(SubEls); false -> <<"">> end. -spec get_rosteritem_name_vcard([xmlel()]) -> binary(). get_rosteritem_name_vcard([Vcard|_]) -> case fxml:get_path_s(Vcard, [{elem, <<"NICKNAME">>}, cdata]) of <<"">> -> fxml:get_path_s(Vcard, [{elem, <<"FN">>}, cdata]); Nickname -> Nickname end; get_rosteritem_name_vcard(_) -> <<"">>. %% This function rewrites the roster entries when moving or renaming %% them in the user contact list. -spec process_item(#roster{}, binary()) -> #roster{}. process_item(RosterItem, Host) -> USFrom = {UserFrom, ServerFrom} = RosterItem#roster.us, {UserTo, ServerTo, ResourceTo} = RosterItem#roster.jid, NameTo = RosterItem#roster.name, USTo = {UserTo, ServerTo}, DisplayedGroups = get_user_displayed_groups(USFrom), CommonGroups = lists:filter(fun (Group) -> is_user_in_group(USTo, Group, Host) end, DisplayedGroups), case CommonGroups of [] -> RosterItem; %% Roster item cannot be removed: We simply reset the original groups: _ when RosterItem#roster.subscription == remove -> GroupNames = lists:map(fun (Group) -> get_group_name(Host, Group) end, CommonGroups), RosterItem#roster{subscription = both, ask = none, groups = GroupNames}; %% Both users have at least a common shared group, %% So each user can see the other _ -> case lists:subtract(RosterItem#roster.groups, CommonGroups) of %% If it doesn't, then remove this user from any %% existing roster groups. [] -> mod_roster:out_subscription(UserTo, ServerTo, jid:make(UserFrom, ServerFrom), unsubscribe), mod_roster:in_subscription(false, UserFrom, ServerFrom, jid:make(UserTo, ServerTo), unsubscribe, <<"">>), RosterItem#roster{subscription = both, ask = none}; %% If so, it means the user wants to add that contact %% to his personal roster PersonalGroups -> set_new_rosteritems(UserFrom, ServerFrom, UserTo, ServerTo, ResourceTo, NameTo, PersonalGroups) end end. build_roster_record(User1, Server1, User2, Server2, Name2, Groups) -> USR2 = {User2, Server2, <<"">>}, #roster{usj = {User1, Server1, USR2}, us = {User1, Server1}, jid = USR2, name = Name2, subscription = both, ask = none, groups = Groups}. set_new_rosteritems(UserFrom, ServerFrom, UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) -> RIFrom = build_roster_record(UserFrom, ServerFrom, UserTo, ServerTo, NameTo, GroupsFrom), set_item(UserFrom, ServerFrom, ResourceTo, RIFrom), JIDTo = jid:make(UserTo, ServerTo), JIDFrom = jid:make(UserFrom, ServerFrom), RITo = build_roster_record(UserTo, ServerTo, UserFrom, ServerFrom, UserFrom, []), set_item(UserTo, ServerTo, <<"">>, RITo), mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe), mod_roster:in_subscription(false, UserTo, ServerTo, JIDFrom, subscribe, <<"">>), mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribed), mod_roster:in_subscription(false, UserFrom, ServerFrom, JIDTo, subscribed, <<"">>), mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribe), mod_roster:in_subscription(false, UserFrom, ServerFrom, JIDTo, subscribe, <<"">>), mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed), mod_roster:in_subscription(false, UserTo, ServerTo, JIDFrom, subscribed, <<"">>), RIFrom. set_item(User, Server, Resource, Item) -> ResIQ = #iq{from = jid:make(User, Server, Resource), to = jid:make(Server), type = set, id = <<"push", (randoms:get_string())/binary>>, sub_els = [#roster_query{ items = [mod_roster:encode_item(Item)]}]}, ejabberd_router:route(ResIQ). -spec get_jid_info({subscription(), [binary()]}, binary(), binary(), jid()) -> {subscription(), [binary()]}. get_jid_info({Subscription, Groups}, User, Server, JID) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), US = {LUser, LServer}, {U1, S1, _} = jid:tolower(JID), US1 = {U1, S1}, DisplayedGroups = get_user_displayed_groups(US), SRUsers = lists:foldl(fun (Group, Acc1) -> lists:foldl(fun (User1, Acc2) -> dict:append(User1, get_group_name(LServer, Group), Acc2) end, Acc1, get_group_users(LServer, Group)) end, dict:new(), DisplayedGroups), case dict:find(US1, SRUsers) of {ok, GroupNames} -> NewGroups = if Groups == [] -> GroupNames; true -> Groups end, {both, NewGroups}; error -> {Subscription, Groups} end. -spec in_subscription(boolean(), binary(), binary(), jid(), subscribe | subscribed | unsubscribe | unsubscribed, binary()) -> boolean(). in_subscription(Acc, User, Server, JID, Type, _Reason) -> process_subscription(in, User, Server, JID, Type, Acc). -spec out_subscription( binary(), binary(), jid(), subscribed | unsubscribed | subscribe | unsubscribe) -> boolean(). out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) -> #jid{luser = UserTo, lserver = ServerTo} = JIDTo, JIDFrom = jid:make(UserFrom, ServerFrom), mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe), mod_roster:in_subscription(false, UserFrom, ServerFrom, JIDTo, unsubscribe, <<"">>), process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false); out_subscription(User, Server, JID, Type) -> process_subscription(out, User, Server, JID, Type, false). process_subscription(Direction, User, Server, JID, _Type, Acc) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), US = {LUser, LServer}, {U1, S1, _} = jid:tolower(jid:remove_resource(JID)), US1 = {U1, S1}, DisplayedGroups = get_user_displayed_groups(US), SRUsers = lists:usort(lists:flatmap(fun (Group) -> get_group_users(LServer, Group) end, DisplayedGroups)), case lists:member(US1, SRUsers) of true -> case Direction of in -> {stop, false}; out -> stop end; false -> Acc end. list_groups(Host) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:list_groups(Host). groups_with_opts(Host) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:groups_with_opts(Host). create_group(Host, Group) -> create_group(Host, Group, []). create_group(Host, Group, Opts) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:create_group(Host, Group, Opts). delete_group(Host, Group) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:delete_group(Host, Group). get_group_opts(Host, Group) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:get_group_opts(Host, Group). set_group_opts(Host, Group, Opts) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:set_group_opts(Host, Group, Opts). get_user_groups(US) -> Host = element(2, US), Mod = gen_mod:db_mod(Host, ?MODULE), Mod:get_user_groups(US, Host) ++ get_special_users_groups(Host). is_group_enabled(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), case get_group_opts(Host, Group) of error -> false; Opts -> not lists:member(disabled, Opts) end. %% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default get_group_opt(Host, Group, Opt, Default) -> case get_group_opts(Host, Group) of error -> Default; Opts -> case lists:keysearch(Opt, 1, Opts) of {value, {_, Val}} -> Val; false -> Default end end. get_online_users(Host) -> lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]). get_group_users(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), case get_group_opt(Host, Group, all_users, false) of true -> ejabberd_auth:get_users(Host); false -> [] end ++ case get_group_opt(Host, Group, online_users, false) of true -> get_online_users(Host); false -> [] end ++ get_group_explicit_users(Host, Group). get_group_users(Host, Group, GroupOpts) -> case proplists:get_value(all_users, GroupOpts, false) of true -> ejabberd_auth:get_users(Host); false -> [] end ++ case proplists:get_value(online_users, GroupOpts, false) of true -> get_online_users(Host); false -> [] end ++ get_group_explicit_users(Host, Group). get_group_explicit_users(Host, Group) -> Mod = gen_mod:db_mod(Host, ?MODULE), Mod:get_group_explicit_users(Host, Group). get_group_name(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), get_group_opt(Host, Group, name, Group). %% Get list of names of groups that have @all@/@online@/etc in the memberlist get_special_users_groups(Host) -> lists:filter(fun (Group) -> get_group_opt(Host, Group, all_users, false) orelse get_group_opt(Host, Group, online_users, false) end, list_groups(Host)). %% Get list of names of groups that have @online@ in the memberlist get_special_users_groups_online(Host) -> lists:filter(fun (Group) -> get_group_opt(Host, Group, online_users, false) end, list_groups(Host)). %% Given two lists of groupnames and their options, %% return the list of displayed groups to the second list displayed_groups(GroupsOpts, SelectedGroupsOpts) -> DisplayedGroups = lists:usort(lists:flatmap(fun ({_Group, Opts}) -> [G || G <- proplists:get_value(displayed_groups, Opts, []), not lists:member(disabled, Opts)] end, SelectedGroupsOpts)), [G || G <- DisplayedGroups, not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))]. %% Given a list of group names with options, %% for those that have @all@ in memberlist, %% get the list of groups displayed get_special_displayed_groups(GroupsOpts) -> Groups = lists:filter(fun ({_Group, Opts}) -> proplists:get_value(all_users, Opts, false) end, GroupsOpts), displayed_groups(GroupsOpts, Groups). %% Given a username and server, and a list of group names with options, %% for the list of groups of that server that user is member %% get the list of groups displayed get_user_displayed_groups(LUser, LServer, GroupsOpts) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Groups = Mod:get_user_displayed_groups(LUser, LServer, GroupsOpts), displayed_groups(GroupsOpts, Groups). %% @doc Get the list of groups that are displayed to this user get_user_displayed_groups(US) -> Host = element(2, US), DisplayedGroups1 = lists:usort(lists:flatmap(fun (Group) -> case is_group_enabled(Host, Group) of true -> get_group_opt(Host, Group, displayed_groups, []); false -> [] end end, get_user_groups(US))), [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)]. is_user_in_group(US, Group, Host) -> Mod = gen_mod:db_mod(Host, ?MODULE), case Mod:is_user_in_group(US, Group, Host) of false -> lists:member(US, get_group_users(Host, Group)); true -> true end. %% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok} add_user_to_group(Host, US, Group) -> {LUser, LServer} = US, case ejabberd_regexp:run(LUser, <<"^@.+@\$">>) of match -> GroupOpts = mod_shared_roster:get_group_opts(Host, Group), MoreGroupOpts = case LUser of <<"@all@">> -> [{all_users, true}]; <<"@online@">> -> [{online_users, true}]; _ -> [] end, mod_shared_roster:set_group_opts(Host, Group, GroupOpts ++ MoreGroupOpts); nomatch -> DisplayedToGroups = displayed_to_groups(Group, Host), DisplayedGroups = get_displayed_groups(Group, LServer), push_user_to_displayed(LUser, LServer, Group, Host, both, DisplayedToGroups), push_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups), broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups), broadcast_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups), Mod = gen_mod:db_mod(Host, ?MODULE), Mod:add_user_to_group(Host, US, Group) end. get_displayed_groups(Group, LServer) -> GroupsOpts = groups_with_opts(LServer), GroupOpts = proplists:get_value(Group, GroupsOpts, []), proplists:get_value(displayed_groups, GroupOpts, []). broadcast_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) -> [broadcast_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups]. push_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) -> [push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups]. remove_user_from_group(Host, US, Group) -> {LUser, LServer} = US, case ejabberd_regexp:run(LUser, <<"^@.+@\$">>) of match -> GroupOpts = mod_shared_roster:get_group_opts(Host, Group), NewGroupOpts = case LUser of <<"@all@">> -> lists:filter(fun (X) -> X /= {all_users, true} end, GroupOpts); <<"@online@">> -> lists:filter(fun (X) -> X /= {online_users, true} end, GroupOpts) end, mod_shared_roster:set_group_opts(Host, Group, NewGroupOpts); nomatch -> Mod = gen_mod:db_mod(Host, ?MODULE), Result = Mod:remove_user_from_group(Host, US, Group), DisplayedToGroups = displayed_to_groups(Group, Host), DisplayedGroups = get_displayed_groups(Group, LServer), push_user_to_displayed(LUser, LServer, Group, Host, remove, DisplayedToGroups), push_displayed_to_user(LUser, LServer, Host, remove, DisplayedGroups), Result end. push_members_to_user(LUser, LServer, Group, Host, Subscription) -> GroupsOpts = groups_with_opts(LServer), GroupOpts = proplists:get_value(Group, GroupsOpts, []), GroupName = proplists:get_value(name, GroupOpts, Group), Members = get_group_users(Host, Group), lists:foreach(fun ({U, S}) -> push_roster_item(LUser, LServer, U, S, GroupName, Subscription) end, Members). broadcast_members_to_user(LUser, LServer, Group, Host, Subscription) -> Members = get_group_users(Host, Group), lists:foreach( fun({U, S}) -> broadcast_subscription(U, S, {LUser, LServer, <<"">>}, Subscription) end, Members). -spec register_user(binary(), binary()) -> ok. register_user(User, Server) -> Groups = get_user_groups({User, Server}), [push_user_to_displayed(User, Server, Group, Server, both, displayed_to_groups(Group, Server)) || Group <- Groups], ok. -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> push_user_to_members(User, Server, remove). push_user_to_members(User, Server, Subscription) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), GroupsOpts = groups_with_opts(LServer), SpecialGroups = get_special_displayed_groups(GroupsOpts), UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts), lists:foreach(fun (Group) -> remove_user_from_group(LServer, {LUser, LServer}, Group), GroupOpts = proplists:get_value(Group, GroupsOpts, []), GroupName = proplists:get_value(name, GroupOpts, Group), lists:foreach(fun ({U, S}) -> push_roster_item(U, S, LUser, LServer, GroupName, Subscription) end, get_group_users(LServer, Group, GroupOpts)) end, lists:usort(SpecialGroups ++ UserGroups)). push_user_to_displayed(LUser, LServer, Group, Host, Subscription, DisplayedToGroupsOpts) -> GroupsOpts = groups_with_opts(Host), GroupOpts = proplists:get_value(Group, GroupsOpts, []), GroupName = proplists:get_value(name, GroupOpts, Group), [push_user_to_group(LUser, LServer, GroupD, Host, GroupName, Subscription) || GroupD <- DisplayedToGroupsOpts]. broadcast_user_to_displayed(LUser, LServer, Host, Subscription, DisplayedToGroupsOpts) -> [broadcast_user_to_group(LUser, LServer, GroupD, Host, Subscription) || GroupD <- DisplayedToGroupsOpts]. push_user_to_group(LUser, LServer, Group, Host, GroupName, Subscription) -> lists:foreach(fun ({U, S}) when (U == LUser) and (S == LServer) -> ok; ({U, S}) -> push_roster_item(U, S, LUser, LServer, GroupName, Subscription) end, get_group_users(Host, Group)). broadcast_user_to_group(LUser, LServer, Group, Host, Subscription) -> lists:foreach( fun({U, S}) when (U == LUser) and (S == LServer) -> ok; ({U, S}) -> broadcast_subscription(LUser, LServer, {U, S, <<"">>}, Subscription) end, get_group_users(Host, Group)). %% Get list of groups to which this group is displayed displayed_to_groups(GroupName, LServer) -> GroupsOpts = groups_with_opts(LServer), Gs = lists:filter(fun ({_Group, Opts}) -> lists:member(GroupName, proplists:get_value(displayed_groups, Opts, [])) end, GroupsOpts), [Name || {Name, _} <- Gs]. push_item(User, Server, Item) -> Stanza = #iq{type = set, id = <<"push", (randoms:get_string())/binary>>, sub_els = [#roster_query{ items = [mod_roster:encode_item(Item)]}]}, lists:foreach(fun (Resource) -> JID = jid:make(User, Server, Resource), ejabberd_router:route( xmpp:set_from_to(Stanza, jid:remove_resource(JID), JID)) end, ejabberd_sm:get_user_resources(User, Server)). push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) -> Item = #roster{usj = {User, Server, {ContactU, ContactS, <<"">>}}, us = {User, Server}, jid = {ContactU, ContactS, <<"">>}, name = <<"">>, subscription = Subscription, ask = none, groups = [GroupName]}, push_item(User, Server, Item). -spec c2s_self_presence({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. c2s_self_presence({_, #{pres_last := _}} = Acc) -> %% This is just a presence update, nothing to do Acc; c2s_self_presence({#presence{type = available}, #{jid := New}} = Acc) -> LUser = New#jid.luser, LServer = New#jid.lserver, Resources = ejabberd_sm:get_user_resources(LUser, LServer), ?DEBUG("user_available for ~p @ ~p (~p resources)", [LUser, LServer, length(Resources)]), case length(Resources) of %% first session for this user 1 -> UserGroups = get_user_groups({LUser, LServer}), lists:foreach(fun (OG) -> ?DEBUG("user_available: pushing ~p @ ~p grp ~p", [LUser, LServer, OG]), DisplayedToGroups = displayed_to_groups(OG, LServer), DisplayedGroups = get_displayed_groups(OG, LServer), broadcast_displayed_to_user(LUser, LServer, LServer, both, DisplayedGroups), broadcast_user_to_displayed(LUser, LServer, LServer, both, DisplayedToGroups) end, UserGroups); _ -> ok end, Acc; c2s_self_presence(Acc) -> Acc. -spec unset_presence(binary(), binary(), binary(), binary()) -> ok. unset_presence(LUser, LServer, Resource, Status) -> Resources = ejabberd_sm:get_user_resources(LUser, LServer), ?DEBUG("unset_presence for ~p @ ~p / ~p -> ~p " "(~p resources)", [LUser, LServer, Resource, Status, length(Resources)]), case length(Resources) of 0 -> OnlineGroups = get_special_users_groups_online(LServer), lists:foreach(fun (OG) -> push_user_to_displayed(LUser, LServer, OG, LServer, remove, displayed_to_groups(OG, LServer)), push_displayed_to_user(LUser, LServer, LServer, remove, displayed_to_groups(OG, LServer)) end, OnlineGroups); _ -> ok end. %%--------------------- %% Web Admin %%--------------------- webadmin_menu(Acc, _Host, Lang) -> [{<<"shared-roster">>, ?T(<<"Shared Roster Groups">>)} | Acc]. webadmin_page(_, Host, #request{us = _US, path = [<<"shared-roster">>], q = Query, lang = Lang} = _Request) -> Res = list_shared_roster_groups(Host, Query, Lang), {stop, Res}; webadmin_page(_, Host, #request{us = _US, path = [<<"shared-roster">>, Group], q = Query, lang = Lang} = _Request) -> Res = shared_roster_group(Host, Group, Query, Lang), {stop, Res}; webadmin_page(Acc, _, _) -> Acc. list_shared_roster_groups(Host, Query, Lang) -> Res = list_sr_groups_parse_query(Host, Query), SRGroups = mod_shared_roster:list_groups(Host), FGroups = (?XAE(<<"table">>, [], [?XE(<<"tbody">>, (lists:map(fun (Group) -> ?XE(<<"tr">>, [?XE(<<"td">>, [?INPUT(<<"checkbox">>, <<"selected">>, Group)]), ?XE(<<"td">>, [?AC(<>, Group)])]) end, lists:sort(SRGroups)) ++ [?XE(<<"tr">>, [?X(<<"td">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"namenew">>, <<"">>)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"addnew">>, <<"Add New">>)])])]))])), (?H1GL((?T(<<"Shared Roster Groups">>)), <<"mod_shared_roster">>, <<"mod_shared_roster">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [FGroups, ?BR, ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>)])]. list_sr_groups_parse_query(Host, Query) -> case lists:keysearch(<<"addnew">>, 1, Query) of {value, _} -> list_sr_groups_parse_addnew(Host, Query); _ -> case lists:keysearch(<<"delete">>, 1, Query) of {value, _} -> list_sr_groups_parse_delete(Host, Query); _ -> nothing end end. list_sr_groups_parse_addnew(Host, Query) -> case lists:keysearch(<<"namenew">>, 1, Query) of {value, {_, Group}} when Group /= <<"">> -> mod_shared_roster:create_group(Host, Group), ok; _ -> error end. list_sr_groups_parse_delete(Host, Query) -> SRGroups = mod_shared_roster:list_groups(Host), lists:foreach(fun (Group) -> case lists:member({<<"selected">>, Group}, Query) of true -> mod_shared_roster:delete_group(Host, Group); _ -> ok end end, SRGroups), ok. shared_roster_group(Host, Group, Query, Lang) -> Res = shared_roster_group_parse_query(Host, Group, Query), GroupOpts = mod_shared_roster:get_group_opts(Host, Group), Name = get_opt(GroupOpts, name, <<"">>), Description = get_opt(GroupOpts, description, <<"">>), AllUsers = get_opt(GroupOpts, all_users, false), OnlineUsers = get_opt(GroupOpts, online_users, false), DisplayedGroups = get_opt(GroupOpts, displayed_groups, []), Members = mod_shared_roster:get_group_explicit_users(Host, Group), FMembers = iolist_to_binary( [if AllUsers -> <<"@all@\n">>; true -> <<"">> end, if OnlineUsers -> <<"@online@\n">>; true -> <<"">> end, [[us_to_list(Member), $\n] || Member <- Members]]), FDisplayedGroups = [<> || DG <- DisplayedGroups], DescNL = length(ejabberd_regexp:split(Description, <<"\n">>)), FGroup = (?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}], [?XE(<<"tbody">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Name:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"name">>, Name)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Description:">>), ?XE(<<"td">>, [?TEXTAREA(<<"description">>, integer_to_binary(lists:max([3, DescNL])), <<"20">>, Description)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Members:">>), ?XE(<<"td">>, [?TEXTAREA(<<"members">>, integer_to_binary(lists:max([3, length(Members)+3])), <<"20">>, FMembers)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Displayed Groups:">>), ?XE(<<"td">>, [?TEXTAREA(<<"dispgroups">>, integer_to_binary(lists:max([3, length(FDisplayedGroups)])), <<"20">>, list_to_binary(FDisplayedGroups))])])])])), (?H1GL((?T(<<"Shared Roster Groups">>)), <<"mod_shared_roster">>, <<"mod_shared_roster">>)) ++ [?XC(<<"h2">>, <<(?T(<<"Group ">>))/binary, Group/binary>>)] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [FGroup, ?BR, ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])]. shared_roster_group_parse_query(Host, Group, Query) -> case lists:keysearch(<<"submit">>, 1, Query) of {value, _} -> {value, {_, Name}} = lists:keysearch(<<"name">>, 1, Query), {value, {_, Description}} = lists:keysearch(<<"description">>, 1, Query), {value, {_, SMembers}} = lists:keysearch(<<"members">>, 1, Query), {value, {_, SDispGroups}} = lists:keysearch(<<"dispgroups">>, 1, Query), NameOpt = if Name == <<"">> -> []; true -> [{name, Name}] end, DescriptionOpt = if Description == <<"">> -> []; true -> [{description, Description}] end, DispGroups = str:tokens(SDispGroups, <<"\r\n">>), DispGroupsOpt = if DispGroups == [] -> []; true -> [{displayed_groups, DispGroups}] end, OldMembers = mod_shared_roster:get_group_explicit_users(Host, Group), SJIDs = str:tokens(SMembers, <<", \r\n">>), NewMembers = lists:foldl(fun (_SJID, error) -> error; (SJID, USs) -> case SJID of <<"@all@">> -> USs; <<"@online@">> -> USs; _ -> try jid:decode(SJID) of JID -> [{JID#jid.luser, JID#jid.lserver} | USs] catch _:{bad_jid, _} -> error end end end, [], SJIDs), AllUsersOpt = case lists:member(<<"@all@">>, SJIDs) of true -> [{all_users, true}]; false -> [] end, OnlineUsersOpt = case lists:member(<<"@online@">>, SJIDs) of true -> [{online_users, true}]; false -> [] end, CurrentDisplayedGroups = get_displayed_groups(Group, Host), AddedDisplayedGroups = DispGroups -- CurrentDisplayedGroups, RemovedDisplayedGroups = CurrentDisplayedGroups -- DispGroups, displayed_groups_update(OldMembers, RemovedDisplayedGroups, remove), displayed_groups_update(OldMembers, AddedDisplayedGroups, both), mod_shared_roster:set_group_opts(Host, Group, NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt ++ OnlineUsersOpt), if NewMembers == error -> error; true -> AddedMembers = NewMembers -- OldMembers, RemovedMembers = OldMembers -- NewMembers, lists:foreach(fun (US) -> mod_shared_roster:remove_user_from_group(Host, US, Group) end, RemovedMembers), lists:foreach(fun (US) -> mod_shared_roster:add_user_to_group(Host, US, Group) end, AddedMembers), ok end; _ -> nothing end. get_opt(Opts, Opt, Default) -> case lists:keysearch(Opt, 1, Opts) of {value, {_, Val}} -> Val; false -> Default end. us_to_list({User, Server}) -> jid:encode({User, Server, <<"">>}). split_grouphost(Host, Group) -> case str:tokens(Group, <<"@">>) of [GroupName, HostName] -> {HostName, GroupName}; [_] -> {Host, Group} end. broadcast_subscription(User, Server, ContactJid, Subscription) -> ejabberd_sm:route(jid:make(User, Server), {item, ContactJid, Subscription}). displayed_groups_update(Members, DisplayedGroups, Subscription) -> lists:foreach(fun({U, S}) -> push_displayed_to_user(U, S, S, Subscription, DisplayedGroups), case Subscription of both -> broadcast_displayed_to_user(U, S, S, to, DisplayedGroups), broadcast_displayed_to_user(U, S, S, from, DisplayedGroups); Subscr -> broadcast_displayed_to_user(U, S, S, Subscr, DisplayedGroups) end end, Members). opts_to_binary(Opts) -> lists:map( fun({name, Name}) -> {name, iolist_to_binary(Name)}; ({description, Desc}) -> {description, iolist_to_binary(Desc)}; ({displayed_groups, Gs}) -> {displayed_groups, [iolist_to_binary(G) || G <- Gs]}; (Opt) -> Opt end, Opts). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import_info() -> [{<<"sr_group">>, 3}, {<<"sr_user">>, 3}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). import(LServer, {sql, _}, DBType, Tab, L) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, Tab, L). mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(_) -> [db_type]. ejabberd-18.01/src/mod_bosh_mnesia.erl0000644000232200023220000001176413225664356020275 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 12 Jan 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_bosh_mnesia). -behaviour(gen_server). -behaviour(mod_bosh). %% mod_bosh API -export([init/0, open_session/2, close_session/1, find_session/1, use_cache/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("logger.hrl"). -record(bosh, {sid = <<"">> :: binary() | '_', timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_', pid = self() :: pid() | '$1'}). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== -spec init() -> ok | {error, any()}. init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). use_cache() -> false. open_session(SID, Pid) -> Session = #bosh{sid = SID, timestamp = p1_time_compat:timestamp(), pid = Pid}, lists:foreach( fun(Node) when Node == node() -> gen_server:call(?MODULE, {write, Session}); (Node) -> cluster_send({?MODULE, Node}, {write, Session}) end, ejabberd_cluster:get_nodes()). close_session(SID) -> case mnesia:dirty_read(bosh, SID) of [Session] -> lists:foreach( fun(Node) when Node == node() -> gen_server:call(?MODULE, {delete, Session}); (Node) -> cluster_send({?MODULE, Node}, {delete, Session}) end, ejabberd_cluster:get_nodes()); [] -> ok end. find_session(SID) -> case mnesia:dirty_read(bosh, SID) of [#bosh{pid = Pid}] -> {ok, Pid}; [] -> {error, notfound} end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> setup_database(), {ok, #state{}}. handle_call({write, Session}, _From, State) -> Res = write_session(Session), {reply, Res, State}; handle_call({delete, Session}, _From, State) -> Res = delete_session(Session), {reply, Res, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({write, Session}, State) -> write_session(Session), {noreply, State}; handle_info({delete, Session}, State) -> delete_session(Session), {noreply, State}; handle_info(_Info, State) -> ?ERROR_MSG("got unexpected info: ~p", [_Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== write_session(#bosh{pid = Pid1, sid = SID, timestamp = T1} = S1) -> case mnesia:dirty_read(bosh, SID) of [#bosh{pid = Pid2, timestamp = T2} = S2] -> if Pid1 == Pid2 -> mnesia:dirty_write(S1); T1 < T2 -> cluster_send(Pid2, replaced), mnesia:dirty_write(S1); true -> cluster_send(Pid1, replaced), mnesia:dirty_write(S2) end; [] -> mnesia:dirty_write(S1) end. delete_session(#bosh{sid = SID, pid = Pid1}) -> case mnesia:dirty_read(bosh, SID) of [#bosh{pid = Pid2}] -> if Pid1 == Pid2 -> mnesia:dirty_delete(bosh, SID); true -> ok end; [] -> ok end. cluster_send(NodePid, Msg) -> erlang:send(NodePid, Msg, [noconnect, nosuspend]). setup_database() -> case catch mnesia:table_info(bosh, attributes) of [sid, pid] -> mnesia:delete_table(bosh); _ -> ok end, ejabberd_mnesia:create(?MODULE, bosh, [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, bosh)}]). ejabberd-18.01/src/mod_privacy_mnesia.erl0000644000232200023220000001374013225664356021013 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_privacy_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_privacy_mnesia). -behaviour(mod_privacy). %% API -export([init/2, set_default/3, unset_default/2, set_lists/1, set_list/4, get_lists/2, get_list/3, remove_lists/2, remove_list/3, use_cache/1, import/1]). -export([need_transform/1, transform/1]). -include("xmpp.hrl"). -include("mod_privacy.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, privacy, [{disc_only_copies, [node()]}, {attributes, record_info(fields, privacy)}]). use_cache(Host) -> case mnesia:table_info(privacy, storage_type) of disc_only_copies -> gen_mod:get_module_opt( Host, mod_privacy, use_cache, ejabberd_config:use_cache(Host)); _ -> false end. unset_default(LUser, LServer) -> F = fun () -> case mnesia:read({privacy, {LUser, LServer}}) of [] -> ok; [R] -> mnesia:write(R#privacy{default = none}) end end, transaction(F). set_default(LUser, LServer, Name) -> F = fun () -> case mnesia:read({privacy, {LUser, LServer}}) of [] -> {error, notfound}; [#privacy{lists = Lists} = P] -> case lists:keymember(Name, 1, Lists) of true -> mnesia:write(P#privacy{default = Name, lists = Lists}); false -> {error, notfound} end end end, transaction(F). remove_list(LUser, LServer, Name) -> F = fun () -> case mnesia:read({privacy, {LUser, LServer}}) of [] -> {error, notfound}; [#privacy{default = Default, lists = Lists} = P] -> if Name == Default -> {error, conflict}; true -> NewLists = lists:keydelete(Name, 1, Lists), mnesia:write(P#privacy{lists = NewLists}) end end end, transaction(F). set_lists(Privacy) -> mnesia:dirty_write(Privacy). set_list(LUser, LServer, Name, List) -> F = fun () -> case mnesia:wread({privacy, {LUser, LServer}}) of [] -> NewLists = [{Name, List}], mnesia:write(#privacy{us = {LUser, LServer}, lists = NewLists}); [#privacy{lists = Lists} = P] -> NewLists1 = lists:keydelete(Name, 1, Lists), NewLists = [{Name, List} | NewLists1], mnesia:write(P#privacy{lists = NewLists}) end end, transaction(F). get_list(LUser, LServer, Name) -> case mnesia:dirty_read(privacy, {LUser, LServer}) of [#privacy{default = Default, lists = Lists}] when Name == default -> case lists:keyfind(Default, 1, Lists) of {_, List} -> {ok, {Default, List}}; false -> error end; [#privacy{lists = Lists}] -> case lists:keyfind(Name, 1, Lists) of {_, List} -> {ok, {Name, List}}; false -> error end; [] -> error end. get_lists(LUser, LServer) -> case mnesia:dirty_read(privacy, {LUser, LServer}) of [#privacy{} = P] -> {ok, P}; _ -> error end. remove_lists(LUser, LServer) -> F = fun () -> mnesia:delete({privacy, {LUser, LServer}}) end, transaction(F). import(#privacy{} = P) -> mnesia:dirty_write(P). need_transform(#privacy{us = {U, S}}) when is_list(U) orelse is_list(S) -> ?INFO_MSG("Mnesia table 'privacy' will be converted to binary", []), true; need_transform(_) -> false. transform(#privacy{us = {U, S}, default = Def, lists = Lists} = R) -> NewLists = lists:map( fun({Name, Ls}) -> NewLs = lists:map( fun(#listitem{value = Val} = L) -> NewVal = case Val of {LU, LS, LR} -> {iolist_to_binary(LU), iolist_to_binary(LS), iolist_to_binary(LR)}; none -> none; both -> both; from -> from; to -> to; _ -> iolist_to_binary(Val) end, L#listitem{value = NewVal} end, Ls), {iolist_to_binary(Name), NewLs} end, Lists), NewDef = case Def of none -> none; _ -> iolist_to_binary(Def) end, NewUS = {iolist_to_binary(U), iolist_to_binary(S)}, R#privacy{us = NewUS, default = NewDef, lists = NewLists}. %%%=================================================================== %%% Internal functions %%%=================================================================== transaction(F) -> case mnesia:transaction(F) of {atomic, Result} -> Result; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, db_failure} end. ejabberd-18.01/src/mod_announce_sql.erl0000644000232200023220000001156513225664356020472 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_announce_sql.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_announce_sql). -behaviour(mod_announce). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/2, set_motd_users/2, set_motd/2, delete_motd/1, get_motd/1, is_motd_user/2, set_motd_user/2, import/3, export/1]). -include("xmpp.hrl"). -include("mod_announce.hrl"). -include("ejabberd_sql_pt.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. set_motd_users(LServer, USRs) -> F = fun() -> lists:foreach( fun({U, _S, _R}) -> ?SQL_UPSERT_T( "motd", ["!username=%(U)s", "!server_host=%(LServer)s", "xml=''"]) end, USRs) end, transaction(LServer, F). set_motd(LServer, Packet) -> XML = fxml:element_to_binary(Packet), F = fun() -> ?SQL_UPSERT_T( "motd", ["!username=''", "!server_host=%(LServer)s", "xml=%(XML)s"]) end, transaction(LServer, F). delete_motd(LServer) -> F = fun() -> ejabberd_sql:sql_query_t( ?SQL("delete from motd where %(LServer)H")) end, transaction(LServer, F). get_motd(LServer) -> case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(xml)s from motd" " where username='' and %(LServer)H")) of {selected, [{XML}]} -> parse_element(XML); {selected, []} -> error; _ -> {error, db_failure} end. is_motd_user(LUser, LServer) -> case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(username)s from motd" " where username=%(LUser)s and %(LServer)H")) of {selected, [_|_]} -> {ok, true}; {selected, []} -> {ok, false}; _ -> {error, db_failure} end. set_motd_user(LUser, LServer) -> F = fun() -> ?SQL_UPSERT_T( "motd", ["!username=%(LUser)s", "!server_host=%(LServer)s", "xml=''"]) end, transaction(LServer, F). export(_Server) -> [{motd, fun(Host, #motd{server = LServer, packet = El}) when LServer == Host -> XML = fxml:element_to_binary(El), [?SQL("delete from motd where username='' and %(LServer)H;"), ?SQL_INSERT( "motd", ["username=''", "server_host=%(LServer)s", "xml=%(XML)s"])]; (_Host, _R) -> [] end}, {motd_users, fun(Host, #motd_users{us = {LUser, LServer}}) when LServer == Host, LUser /= <<"">> -> [?SQL("delete from motd where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT( "motd", ["username=%(LUser)s", "server_host=%(LServer)s", "xml=''"])]; (_Host, _R) -> [] end}]. import(_, _, _) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== transaction(LServer, F) -> case ejabberd_sql:sql_transaction(LServer, F) of {atomic, _} -> ok; _ -> {error, db_failure} end. parse_element(XML) -> case fxml_stream:parse_element(XML) of El when is_record(El, xmlel) -> {ok, El}; _ -> ?ERROR_MSG("malformed XML element in SQL table " "'motd' for username='': ~s", [XML]), {error, db_failure} end. ejabberd-18.01/src/mod_proxy65.erl0000644000232200023220000001070413225664356017333 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_proxy65.erl %%% Author : Evgeniy Khramtsov %%% Purpose : Main supervisor. %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_proxy65). -author('xram@jabber.ru'). -protocol({xep, 65, '1.8'}). -behaviour(gen_mod). -behaviour(supervisor). %% gen_mod callbacks. -export([start/2, stop/1, reload/3, transform_module_options/1]). %% supervisor callbacks. -export([init/1]). -export([start_link/2, mod_opt_type/1, depends/2]). -define(PROCNAME, ejabberd_mod_proxy65). -callback init() -> any(). -callback register_stream(binary(), pid()) -> ok | {error, any()}. -callback unregister_stream(binary()) -> ok | {error, any()}. -callback activate_stream(binary(), binary(), pos_integer() | infinity, node()) -> ok | {error, limit | conflict | notfound | term()}. start(Host, Opts) -> {ListenOpts, ModOpts} = lists:partition( fun({auth_type, _}) -> true; ({recbuf, _}) -> true; ({sndbuf, _}) -> true; ({shaper, _}) -> true; (_) -> false end, Opts), case mod_proxy65_service:add_listener(Host, ListenOpts) of {error, _} = Err -> Err; _ -> Mod = gen_mod:ram_db_mod(global, ?MODULE), Mod:init(), Proc = gen_mod:get_module_proc(Host, ?PROCNAME), ChildSpec = {Proc, {?MODULE, start_link, [Host, ModOpts]}, transient, infinity, supervisor, [?MODULE]}, supervisor:start_child(ejabberd_gen_mod_sup, ChildSpec) end. stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> mod_proxy65_service:delete_listener(Host); true -> ok end, Proc = gen_mod:get_module_proc(Host, ?PROCNAME), supervisor:terminate_child(ejabberd_gen_mod_sup, Proc), supervisor:delete_child(ejabberd_gen_mod_sup, Proc). reload(Host, NewOpts, OldOpts) -> Mod = gen_mod:ram_db_mod(global, ?MODULE), Mod:init(), mod_proxy65_service:reload(Host, NewOpts, OldOpts). start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), supervisor:start_link({local, Proc}, ?MODULE, [Host, Opts]). transform_module_options(Opts) -> mod_proxy65_service:transform_module_options(Opts). init([Host, Opts]) -> Service = {mod_proxy65_service, {mod_proxy65_service, start_link, [Host, Opts]}, transient, 5000, worker, [mod_proxy65_service]}, StreamSupervisor = {ejabberd_mod_proxy65_sup, {ejabberd_tmp_sup, start_link, [gen_mod:get_module_proc(Host, ejabberd_mod_proxy65_sup), mod_proxy65_stream]}, transient, infinity, supervisor, [ejabberd_tmp_sup]}, {ok, {{one_for_one, 10, 1}, [StreamSupervisor, Service]}}. depends(_Host, _Opts) -> []. mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun(L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(hostname) -> fun iolist_to_binary/1; mod_opt_type(ip) -> fun (S) -> {ok, Addr} = inet_parse:address(binary_to_list(iolist_to_binary(S))), Addr end; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(port) -> fun (P) when is_integer(P), P > 0, P < 65536 -> P end; mod_opt_type(max_connections) -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(Opt) -> case mod_proxy65_stream:listen_opt_type(Opt) of Opts when is_list(Opts) -> [access, host, hosts, hostname, ip, name, port, max_connections, ram_db_type] ++ Opts; Fun -> Fun end. ejabberd-18.01/src/mod_bosh_redis.erl0000644000232200023220000001016113225664356020115 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_bosh_redis.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2017-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_bosh_redis). -behaviour(mod_bosh). -behaviour(gen_server). %% API -export([init/0, open_session/2, close_session/1, find_session/1, cache_nodes/0]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("bosh.hrl"). -record(state, {}). -define(BOSH_KEY, <<"ejabberd:bosh">>). %%%=================================================================== %%% API %%%=================================================================== init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). open_session(SID, Pid) -> PidBin = term_to_binary(Pid), case ejabberd_redis:multi( fun() -> ejabberd_redis:hset(?BOSH_KEY, SID, PidBin), ejabberd_redis:publish(?BOSH_KEY, SID) end) of {ok, _} -> ok; {error, _} -> {error, db_failure} end. close_session(SID) -> case ejabberd_redis:multi( fun() -> ejabberd_redis:hdel(?BOSH_KEY, [SID]), ejabberd_redis:publish(?BOSH_KEY, SID) end) of {ok, _} -> ok; {error, _} -> {error, db_failure} end. find_session(SID) -> case ejabberd_redis:hget(?BOSH_KEY, SID) of {ok, undefined} -> {error, notfound}; {ok, Pid} -> try {ok, binary_to_term(Pid)} catch _:badarg -> ?ERROR_MSG("malformed data in redis (key = '~s'): ~p", [SID, Pid]), {error, db_failure} end; {error, _} -> {error, db_failure} end. cache_nodes() -> [node()]. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> clean_table(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({redis_message, ?BOSH_KEY, SID}, State) -> ets_cache:delete(?BOSH_CACHE, SID), {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== clean_table() -> ?DEBUG("Cleaning Redis BOSH sessions...", []), case ejabberd_redis:hgetall(?BOSH_KEY) of {ok, Vals} -> ejabberd_redis:multi( fun() -> lists:foreach( fun({SID, Pid}) when node(Pid) == node() -> ejabberd_redis:hdel(?BOSH_KEY, [SID]); (_) -> ok end, Vals) end), ok; {error, _} -> ?ERROR_MSG("failed to clean bosh sessions in redis", []) end. ejabberd-18.01/src/ejabberd_redis.erl0000644000232200023220000004167513225664356020077 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_redis.erl %%% Author : Evgeny Khramtsov %%% Created : 8 May 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_redis). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. -behaviour(?GEN_SERVER). -compile({no_auto_import, [get/1, put/2]}). %% API -export([start_link/1, get_proc/1, get_connection/1, q/1, qp/1, format_error/1]). %% Commands -export([multi/1, get/1, set/2, del/1, sadd/2, srem/2, smembers/1, sismember/2, scard/1, hget/2, hset/3, hdel/2, hlen/1, hgetall/1, hkeys/1, subscribe/1, publish/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -define(SERVER, ?MODULE). -define(PROCNAME, 'ejabberd_redis_client'). -define(TR_STACK, redis_transaction_stack). -define(DEFAULT_MAX_QUEUE, 10000). -define(MAX_RETRIES, 1). -define(CALL_TIMEOUT, 60*1000). %% 60 seconds -include("logger.hrl"). -include("ejabberd.hrl"). -record(state, {connection :: pid() | undefined, num :: pos_integer(), subscriptions = #{} :: map(), pending_q :: p1_queue:queue()}). -type error_reason() :: binary() | timeout | disconnected | overloaded. -type redis_error() :: {error, error_reason()}. -type redis_reply() :: binary() | [binary()]. -type redis_command() :: [binary()]. -type redis_pipeline() :: [redis_command()]. -type state() :: #state{}. -export_type([error_reason/0]). %%%=================================================================== %%% API %%%=================================================================== start_link(I) -> ?GEN_SERVER:start_link({local, get_proc(I)}, ?MODULE, [I], []). get_proc(I) -> misc:binary_to_atom( iolist_to_binary( [atom_to_list(?MODULE), $_, integer_to_list(I)])). get_connection(I) -> misc:binary_to_atom( iolist_to_binary( [atom_to_list(?MODULE), "_connection_", integer_to_list(I)])). -spec q(redis_command()) -> {ok, redis_reply()} | redis_error(). q(Command) -> call(get_rnd_id(), {q, Command}, ?MAX_RETRIES). -spec qp(redis_pipeline()) -> {ok, [redis_reply()]} | redis_error(). qp(Pipeline) -> call(get_rnd_id(), {qp, Pipeline}, ?MAX_RETRIES). -spec multi(fun(() -> any())) -> {ok, [redis_reply()]} | redis_error(). multi(F) -> case erlang:get(?TR_STACK) of undefined -> erlang:put(?TR_STACK, []), try F() of _ -> Stack = erlang:get(?TR_STACK), erlang:erase(?TR_STACK), Command = [["MULTI"]|lists:reverse([["EXEC"]|Stack])], case qp(Command) of {error, _} = Err -> Err; Result -> get_result(Result) end catch E:R -> erlang:erase(?TR_STACK), erlang:raise(E, R, erlang:get_stacktrace()) end; _ -> erlang:error(nested_transaction) end. -spec format_error(atom() | binary()) -> binary(). format_error(Reason) when is_atom(Reason) -> format_error(misc:atom_to_binary(Reason)); format_error(Reason) -> Reason. %%%=================================================================== %%% Redis commands API %%%=================================================================== -spec get(iodata()) -> {ok, undefined | binary()} | redis_error(). get(Key) -> case erlang:get(?TR_STACK) of undefined -> q([<<"GET">>, Key]); _ -> erlang:error(transaction_unsupported) end. -spec set(iodata(), iodata()) -> ok | redis_error() | queued. set(Key, Val) -> Cmd = [<<"SET">>, Key, Val], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, <<"OK">>} -> ok; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. -spec del(list()) -> {ok, non_neg_integer()} | redis_error() | queued. del([]) -> reply(0); del(Keys) -> Cmd = [<<"DEL">>|Keys], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. -spec sadd(iodata(), list()) -> {ok, non_neg_integer()} | redis_error() | queued. sadd(_Set, []) -> reply(0); sadd(Set, Members) -> Cmd = [<<"SADD">>, Set|Members], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. -spec srem(iodata(), list()) -> {ok, non_neg_integer()} | redis_error() | queued. srem(_Set, []) -> reply(0); srem(Set, Members) -> Cmd = [<<"SREM">>, Set|Members], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. -spec smembers(iodata()) -> {ok, [binary()]} | redis_error(). smembers(Set) -> case erlang:get(?TR_STACK) of undefined -> q([<<"SMEMBERS">>, Set]); _ -> erlang:error(transaction_unsupported) end. -spec sismember(iodata(), iodata()) -> boolean() | redis_error(). sismember(Set, Member) -> case erlang:get(?TR_STACK) of undefined -> case q([<<"SISMEMBER">>, Set, Member]) of {ok, Flag} -> {ok, dec_bool(Flag)}; {error, _} = Err -> Err end; _ -> erlang:error(transaction_unsupported) end. -spec scard(iodata()) -> {ok, non_neg_integer()} | redis_error(). scard(Set) -> case erlang:get(?TR_STACK) of undefined -> case q([<<"SCARD">>, Set]) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; _ -> erlang:error(transaction_unsupported) end. -spec hget(iodata(), iodata()) -> {ok, undefined | binary()} | redis_error(). hget(Key, Field) -> case erlang:get(?TR_STACK) of undefined -> q([<<"HGET">>, Key, Field]); _ -> erlang:error(transaction_unsupported) end. -spec hset(iodata(), iodata(), iodata()) -> {ok, boolean()} | redis_error() | queued. hset(Key, Field, Val) -> Cmd = [<<"HSET">>, Key, Field, Val], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, Flag} -> {ok, dec_bool(Flag)}; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. -spec hdel(iodata(), list()) -> {ok, non_neg_integer()} | redis_error() | queued. hdel(_Key, []) -> reply(0); hdel(Key, Fields) -> Cmd = [<<"HDEL">>, Key|Fields], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. -spec hgetall(iodata()) -> {ok, [{binary(), binary()}]} | redis_error(). hgetall(Key) -> case erlang:get(?TR_STACK) of undefined -> case q([<<"HGETALL">>, Key]) of {ok, Pairs} -> {ok, decode_pairs(Pairs)}; {error, _} = Err -> Err end; _ -> erlang:error(transaction_unsupported) end. -spec hlen(iodata()) -> {ok, non_neg_integer()} | redis_error(). hlen(Key) -> case erlang:get(?TR_STACK) of undefined -> case q([<<"HLEN">>, Key]) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; _ -> erlang:error(transaction_unsupported) end. -spec hkeys(iodata()) -> {ok, [binary()]} | redis_error(). hkeys(Key) -> case erlang:get(?TR_STACK) of undefined -> q([<<"HKEYS">>, Key]); _ -> erlang:error(transaction_unsupported) end. -spec subscribe([binary()]) -> ok | redis_error(). subscribe(Channels) -> try ?GEN_SERVER:call(get_proc(1), {subscribe, self(), Channels}, ?CALL_TIMEOUT) catch exit:{Why, {?GEN_SERVER, call, _}} -> Reason = case Why of timeout -> timeout; _ -> disconnected end, {error, Reason} end. -spec publish(iodata(), iodata()) -> {ok, non_neg_integer()} | redis_error() | queued. publish(Channel, Data) -> Cmd = [<<"PUBLISH">>, Channel, Data], case erlang:get(?TR_STACK) of undefined -> case q(Cmd) of {ok, N} -> {ok, binary_to_integer(N)}; {error, _} = Err -> Err end; Stack -> tr_enq(Cmd, Stack) end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([I]) -> process_flag(trap_exit, true), QueueType = get_queue_type(), Limit = max_fsm_queue(), self() ! connect, {ok, #state{num = I, pending_q = p1_queue:new(QueueType, Limit)}}. handle_call(connect, From, #state{connection = undefined, pending_q = Q} = State) -> CurrTime = p1_time_compat:monotonic_time(milli_seconds), Q2 = try p1_queue:in({From, CurrTime}, Q) catch error:full -> Q1 = clean_queue(Q, CurrTime), p1_queue:in({From, CurrTime}, Q1) end, {noreply, State#state{pending_q = Q2}}; handle_call(connect, From, #state{connection = Pid} = State) -> case is_process_alive(Pid) of true -> {reply, ok, State}; false -> self() ! connect, handle_call(connect, From, State#state{connection = undefined}) end; handle_call({subscribe, Caller, Channels}, _From, #state{connection = Pid, subscriptions = Subs} = State) -> Subs1 = lists:foldl( fun(Channel, Acc) -> Callers = maps:get(Channel, Acc, []) -- [Caller], maps:put(Channel, [Caller|Callers], Acc) end, Subs, Channels), eredis_subscribe(Pid, Channels), {reply, ok, State#state{subscriptions = Subs1}}; handle_call(Request, _From, State) -> ?WARNING_MSG("unexepected call: ~p", [Request]), {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(connect, #state{connection = undefined} = State) -> NewState = case connect(State) of {ok, Connection} -> Q1 = flush_queue(State#state.pending_q), re_subscribe(Connection, State#state.subscriptions), State#state{connection = Connection, pending_q = Q1}; {error, _} -> State end, {noreply, NewState}; handle_info(connect, State) -> %% Already connected {noreply, State}; handle_info({'EXIT', Pid, _}, State) -> case State#state.connection of Pid -> self() ! connect, {noreply, State#state{connection = undefined}}; _ -> {noreply, State} end; handle_info({subscribed, Channel, Pid}, State) -> case State#state.connection of Pid -> case maps:is_key(Channel, State#state.subscriptions) of true -> eredis_sub:ack_message(Pid); false -> ?WARNING_MSG("got subscription ack for unknown channel ~s", [Channel]) end; _ -> ok end, {noreply, State}; handle_info({message, Channel, Data, Pid}, State) -> case State#state.connection of Pid -> lists:foreach( fun(Subscriber) -> erlang:send(Subscriber, {redis_message, Channel, Data}) end, maps:get(Channel, State#state.subscriptions, [])), eredis_sub:ack_message(Pid); _ -> ok end, {noreply, State}; handle_info(Info, State) -> ?WARNING_MSG("unexpected info = ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec connect(state()) -> {ok, pid()} | {error, any()}. connect(#state{num = Num}) -> Server = ejabberd_config:get_option(redis_server, "localhost"), Port = ejabberd_config:get_option(redis_port, 6379), DB = ejabberd_config:get_option(redis_db, 0), Pass = ejabberd_config:get_option(redis_password, ""), ConnTimeout = timer:seconds( ejabberd_config:get_option( redis_connect_timeout, 1)), try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of {ok, Client} -> ?DEBUG("Connection #~p established to Redis at ~s:~p", [Num, Server, Port]), register(get_connection(Num), Client), {ok, Client}; {error, Why} -> erlang:error(Why) end catch _:Reason -> Timeout = randoms:uniform( min(10, ejabberd_redis_sup:get_pool_size())), ?ERROR_MSG("Redis connection #~p at ~s:~p has failed: ~p; " "reconnecting in ~p seconds", [Num, Server, Port, Reason, Timeout]), erlang:send_after(timer:seconds(Timeout), self(), connect), {error, Reason} end. do_connect(1, Server, Port, Pass, _DB, _ConnTimeout) -> %% First connection in the pool is always a subscriber Res = eredis_sub:start_link(Server, Port, Pass, no_reconnect, infinity, drop), case Res of {ok, Pid} -> eredis_sub:controlling_process(Pid); _ -> ok end, Res; do_connect(_, Server, Port, Pass, DB, ConnTimeout) -> eredis:start_link(Server, Port, DB, Pass, no_reconnect, ConnTimeout). -spec call(pos_integer(), {q, redis_command()}, integer()) -> {ok, redis_reply()} | redis_error(); (pos_integer(), {qp, redis_pipeline()}, integer()) -> {ok, [redis_reply()]} | redis_error(). call(I, {F, Cmd}, Retries) -> ?DEBUG("redis query: ~p", [Cmd]), Conn = get_connection(I), Res = try eredis:F(Conn, Cmd, ?CALL_TIMEOUT) of {error, Reason} when is_atom(Reason) -> try exit(whereis(Conn), kill) catch _:_ -> ok end, {error, disconnected}; Other -> Other catch exit:{timeout, _} -> {error, timeout}; exit:{_, {gen_server, call, _}} -> {error, disconnected} end, case Res of {error, disconnected} when Retries > 0 -> try ?GEN_SERVER:call(get_proc(I), connect, ?CALL_TIMEOUT) of ok -> call(I, {F, Cmd}, Retries-1); {error, _} = Err -> Err catch exit:{Why, {?GEN_SERVER, call, _}} -> Reason1 = case Why of timeout -> timeout; _ -> disconnected end, log_error(Cmd, Reason1), {error, Reason1} end; {error, Reason1} -> log_error(Cmd, Reason1), Res; _ -> Res end. -spec log_error(redis_command() | redis_pipeline(), atom() | binary()) -> ok. log_error(Cmd, Reason) -> ?ERROR_MSG("Redis request has failed:~n" "** request = ~p~n" "** response = ~s", [Cmd, format_error(Reason)]). -spec get_rnd_id() -> pos_integer(). get_rnd_id() -> randoms:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2. -spec get_result([{error, atom() | binary()} | {ok, iodata()}]) -> {ok, [redis_reply()]} | {error, binary()}. get_result([{error, _} = Err|_]) -> Err; get_result([{ok, _} = OK]) -> OK; get_result([_|T]) -> get_result(T). -spec tr_enq([iodata()], list()) -> queued. tr_enq(Cmd, Stack) -> erlang:put(?TR_STACK, [Cmd|Stack]), queued. -spec decode_pairs([binary()]) -> [{binary(), binary()}]. decode_pairs(Pairs) -> decode_pairs(Pairs, []). -spec decode_pairs([binary()], [{binary(), binary()}]) -> [{binary(), binary()}]. decode_pairs([Field, Val|Pairs], Acc) -> decode_pairs(Pairs, [{Field, Val}|Acc]); decode_pairs([], Acc) -> lists:reverse(Acc). dec_bool(<<$1>>) -> true; dec_bool(<<$0>>) -> false. -spec reply(T) -> {ok, T} | queued. reply(Val) -> case erlang:get(?TR_STACK) of undefined -> {ok, Val}; _ -> queued end. -spec max_fsm_queue() -> pos_integer(). max_fsm_queue() -> proplists:get_value(max_queue, fsm_limit_opts(), ?DEFAULT_MAX_QUEUE). fsm_limit_opts() -> ejabberd_config:fsm_limit_opts([]). get_queue_type() -> ejabberd_config:get_option( redis_queue_type, ejabberd_config:default_queue_type(global)). -spec flush_queue(p1_queue:queue()) -> p1_queue:queue(). flush_queue(Q) -> CurrTime = p1_time_compat:monotonic_time(milli_seconds), p1_queue:dropwhile( fun({From, Time}) -> if (CurrTime - Time) >= ?CALL_TIMEOUT -> ok; true -> ?GEN_SERVER:reply(From, ok) end, true end, Q). -spec clean_queue(p1_queue:queue(), integer()) -> p1_queue:queue(). clean_queue(Q, CurrTime) -> Q1 = p1_queue:dropwhile( fun({_From, Time}) -> (CurrTime - Time) >= ?CALL_TIMEOUT end, Q), Len = p1_queue:len(Q1), Limit = p1_queue:get_limit(Q1), if Len >= Limit -> ?ERROR_MSG("Redis request queue is overloaded", []), p1_queue:dropwhile( fun({From, _Time}) -> ?GEN_SERVER:reply(From, {error, overloaded}), true end, Q1); true -> Q1 end. re_subscribe(Pid, Subs) -> case maps:keys(Subs) of [] -> ok; Channels -> eredis_subscribe(Pid, Channels) end. eredis_subscribe(Pid, Channels) -> ?DEBUG("redis query: ~p", [[<<"SUBSCRIBE">>|Channels]]), eredis_sub:subscribe(Pid, Channels). ejabberd-18.01/src/ejabberd_captcha.erl0000644000232200023220000004147713225664356020374 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_captcha.erl %%% Author : Evgeniy Khramtsov %%% Purpose : CAPTCHA processing. %%% Created : 26 Apr 2008 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_captcha). -behaviour(ejabberd_config). -protocol({xep, 158, '1.0'}). -behaviour(gen_server). %% API -export([start_link/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([create_captcha/6, build_captcha_html/2, check_captcha/2, process_reply/1, process/2, is_feature_available/0, create_captcha_x/5, opt_type/1]). -include("xmpp.hrl"). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). -define(CAPTCHA_LIFETIME, 120000). -define(LIMIT_PERIOD, 60*1000*1000). -type image_error() :: efbig | enodata | limit | malformed_image | timeout. -record(state, {limits = treap:empty() :: treap:treap()}). -record(captcha, {id :: binary(), pid :: pid() | undefined, key :: binary(), tref :: reference(), args :: any()}). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec captcha_text(undefined | binary()) -> binary(). captcha_text(Lang) -> translate:translate(Lang, <<"Enter the text you see">>). -spec mk_ocr_field(binary() | undefined, binary(), binary()) -> xdata_field(). mk_ocr_field(Lang, CID, Type) -> URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>}, #xdata_field{var = <<"ocr">>, type = 'text-single', label = captcha_text(Lang), required = true, sub_els = [#media{uri = [URI]}]}. mk_field(Type, Var, Value) -> #xdata_field{type = Type, var = Var, values = [Value]}. -spec create_captcha(binary(), jid(), jid(), binary(), any(), any()) -> {error, image_error()} | {ok, binary(), [text()], [xmlel()]}. create_captcha(SID, From, To, Lang, Limiter, Args) -> case create_image(Limiter) of {ok, Type, Key, Image} -> Id = <<(randoms:get_string())/binary>>, JID = jid:encode(From), CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>, Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image}, Fs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA), mk_field(hidden, <<"from">>, jid:encode(To)), mk_field(hidden, <<"challenge">>, Id), mk_field(hidden, <<"sid">>, SID), mk_ocr_field(Lang, CID, Type)], X = #xdata{type = form, fields = Fs}, Captcha = #xcaptcha{xdata = X}, BodyString = {<<"Your messages to ~s are being blocked. " "To unblock them, visit ~s">>, [JID, get_url(Id)]}, Body = xmpp:mk_text(BodyString, Lang), OOB = #oob_x{url = get_url(Id)}, Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), ets:insert(captcha, #captcha{id = Id, pid = self(), key = Key, tref = Tref, args = Args}), {ok, Id, Body, [OOB, Captcha, Data]}; Err -> Err end. -spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) -> {ok, xdata()} | {error, image_error()}. create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) -> case create_image(Limiter) of {ok, Type, Key, Image} -> Id = <<(randoms:get_string())/binary>>, CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>, Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image}, HelpTxt = translate:translate(Lang, <<"If you don't see the CAPTCHA image here, " "visit the web page.">>), Imageurl = get_url(<>), NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++ [#xdata_field{type = fixed, values = [HelpTxt]}, #xdata_field{type = hidden, var = <<"captchahidden">>, values = [<<"workaround-for-psi">>]}, #xdata_field{type = 'text-single', var = <<"url">>, label = translate:translate( Lang, <<"CAPTCHA web page">>), values = [Imageurl]}, mk_field(hidden, <<"from">>, jid:encode(To)), mk_field(hidden, <<"challenge">>, Id), mk_field(hidden, <<"sid">>, SID), mk_ocr_field(Lang, CID, Type)], Captcha = X#xdata{type = form, fields = NewFs}, Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), ets:insert(captcha, #captcha{id = Id, key = Key, tref = Tref}), {ok, [Captcha, Data]}; Err -> Err end. -spec build_captcha_html(binary(), binary()) -> captcha_not_found | {xmlel(), {xmlel(), xmlel(), xmlel(), xmlel()}}. build_captcha_html(Id, Lang) -> case lookup_captcha(Id) of {ok, _} -> ImgEl = #xmlel{name = <<"img">>, attrs = [{<<"src">>, get_url(<>)}], children = []}, TextEl = {xmlcdata, captcha_text(Lang)}, IdEl = #xmlel{name = <<"input">>, attrs = [{<<"type">>, <<"hidden">>}, {<<"name">>, <<"id">>}, {<<"value">>, Id}], children = []}, KeyEl = #xmlel{name = <<"input">>, attrs = [{<<"type">>, <<"text">>}, {<<"name">>, <<"key">>}, {<<"size">>, <<"10">>}], children = []}, FormEl = #xmlel{name = <<"form">>, attrs = [{<<"action">>, get_url(Id)}, {<<"name">>, <<"captcha">>}, {<<"method">>, <<"POST">>}], children = [ImgEl, #xmlel{name = <<"br">>, attrs = [], children = []}, TextEl, #xmlel{name = <<"br">>, attrs = [], children = []}, IdEl, KeyEl, #xmlel{name = <<"br">>, attrs = [], children = []}, #xmlel{name = <<"input">>, attrs = [{<<"type">>, <<"submit">>}, {<<"name">>, <<"enter">>}, {<<"value">>, <<"OK">>}], children = []}]}, {FormEl, {ImgEl, TextEl, IdEl, KeyEl}}; _ -> captcha_not_found end. -spec process_reply(xmpp_element()) -> ok | {error, bad_match | not_found | malformed}. process_reply(#xdata{} = X) -> case {xmpp_util:get_xdata_values(<<"challenge">>, X), xmpp_util:get_xdata_values(<<"ocr">>, X)} of {[Id], [OCR]} -> case check_captcha(Id, OCR) of captcha_valid -> ok; captcha_non_valid -> {error, bad_match}; captcha_not_found -> {error, not_found} end; _ -> {error, malformed} end; process_reply(#xcaptcha{xdata = #xdata{} = X}) -> process_reply(X); process_reply(_) -> {error, malformed}. process(_Handlers, #request{method = 'GET', lang = Lang, path = [_, Id]}) -> case build_captcha_html(Id, Lang) of {FormEl, _} when is_tuple(FormEl) -> Form = #xmlel{name = <<"div">>, attrs = [{<<"align">>, <<"center">>}], children = [FormEl]}, ejabberd_web:make_xhtml([Form]); captcha_not_found -> ejabberd_web:error(not_found) end; process(_Handlers, #request{method = 'GET', path = [_, Id, <<"image">>], ip = IP}) -> {Addr, _Port} = IP, case lookup_captcha(Id) of {ok, #captcha{key = Key}} -> case create_image(Addr, Key) of {ok, Type, _, Img} -> {200, [{<<"Content-Type">>, Type}, {<<"Cache-Control">>, <<"no-cache">>}, {<<"Last-Modified">>, list_to_binary(httpd_util:rfc1123_date())}], Img}; {error, limit} -> ejabberd_web:error(not_allowed); _ -> ejabberd_web:error(not_found) end; _ -> ejabberd_web:error(not_found) end; process(_Handlers, #request{method = 'POST', q = Q, lang = Lang, path = [_, Id]}) -> ProvidedKey = proplists:get_value(<<"key">>, Q, none), case check_captcha(Id, ProvidedKey) of captcha_valid -> Form = #xmlel{name = <<"p">>, attrs = [], children = [{xmlcdata, translate:translate(Lang, <<"The CAPTCHA is valid.">>)}]}, ejabberd_web:make_xhtml([Form]); captcha_non_valid -> ejabberd_web:error(not_allowed); captcha_not_found -> ejabberd_web:error(not_found) end; process(_Handlers, _Request) -> ejabberd_web:error(not_found). init([]) -> mnesia:delete_table(captcha), ets:new(captcha, [named_table, public, {keypos, #captcha.id}]), check_captcha_setup(), {ok, #state{}}. handle_call({is_limited, Limiter, RateLimit}, _From, State) -> NowPriority = now_priority(), CleanPriority = NowPriority + (?LIMIT_PERIOD), Limits = clean_treap(State#state.limits, CleanPriority), case treap:lookup(Limiter, Limits) of {ok, _, Rate} when Rate >= RateLimit -> {reply, true, State#state{limits = Limits}}; {ok, Priority, Rate} -> NewLimits = treap:insert(Limiter, Priority, Rate + 1, Limits), {reply, false, State#state{limits = NewLimits}}; _ -> NewLimits = treap:insert(Limiter, NowPriority, 1, Limits), {reply, false, State#state{limits = NewLimits}} end; handle_call(_Request, _From, State) -> {reply, bad_request, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({remove_id, Id}, State) -> ?DEBUG("captcha ~p timed out", [Id]), case ets:lookup(captcha, Id) of [#captcha{args = Args, pid = Pid}] -> if is_pid(Pid) -> Pid ! {captcha_failed, Args}; true -> ok end, ets:delete(captcha, Id); _ -> ok end, {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. create_image() -> create_image(undefined). create_image(Limiter) -> Key = str:substr(randoms:get_string(), 1, 6), create_image(Limiter, Key). create_image(Limiter, Key) -> case is_limited(Limiter) of true -> {error, limit}; false -> do_create_image(Key) end. do_create_image(Key) -> FileName = get_prog_name(), Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])), case cmd(Cmd) of {ok, <<137, $P, $N, $G, $\r, $\n, 26, $\n, _/binary>> = Img} -> {ok, <<"image/png">>, Key, Img}; {ok, <<255, 216, _/binary>> = Img} -> {ok, <<"image/jpeg">>, Key, Img}; {ok, <<$G, $I, $F, $8, X, $a, _/binary>> = Img} when X == $7; X == $9 -> {ok, <<"image/gif">>, Key, Img}; {error, enodata = Reason} -> ?ERROR_MSG("Failed to process output from \"~s\". " "Maybe ImageMagick's Convert program " "is not installed.", [Cmd]), {error, Reason}; {error, Reason} -> ?ERROR_MSG("Failed to process an output from \"~s\": ~p", [Cmd, Reason]), {error, Reason}; _ -> Reason = malformed_image, ?ERROR_MSG("Failed to process an output from \"~s\": ~p", [Cmd, Reason]), {error, Reason} end. get_prog_name() -> case ejabberd_config:get_option(captcha_cmd) of undefined -> ?DEBUG("The option captcha_cmd is not configured, " "but some module wants to use the CAPTCHA " "feature.", []), false; FileName -> FileName end. get_url(Str) -> CaptchaHost = ejabberd_config:get_option(captcha_host, <<"">>), case str:tokens(CaptchaHost, <<":">>) of [Host] -> <<"http://", Host/binary, "/captcha/", Str/binary>>; [<<"http", _/binary>> = TransferProt, Host] -> <>; [Host, PortString] -> TransferProt = iolist_to_binary(atom_to_list(get_transfer_protocol(PortString))), <>; [TransferProt, Host, PortString] -> <>; _ -> <<"http://", (?MYNAME)/binary, "/captcha/", Str/binary>> end. get_transfer_protocol(PortString) -> PortNumber = binary_to_integer(PortString), PortListeners = get_port_listeners(PortNumber), get_captcha_transfer_protocol(PortListeners). get_port_listeners(PortNumber) -> AllListeners = ejabberd_config:get_option(listen, []), lists:filter( fun({{Port, _IP, _Transport}, _Module, _Opts}) -> Port == PortNumber end, AllListeners). get_captcha_transfer_protocol([]) -> throw(<<"The port number mentioned in captcha_host " "is not a ejabberd_http listener with " "'captcha' option. Change the port number " "or specify http:// in that option.">>); get_captcha_transfer_protocol([{_, ejabberd_http, Opts} | Listeners]) -> case proplists:get_bool(captcha, Opts) of true -> case proplists:get_bool(tls, Opts) of true -> https; false -> http end; false -> get_captcha_transfer_protocol(Listeners) end; get_captcha_transfer_protocol([_ | Listeners]) -> get_captcha_transfer_protocol(Listeners). is_limited(undefined) -> false; is_limited(Limiter) -> case ejabberd_config:get_option(captcha_limit) of undefined -> false; Int -> case catch gen_server:call(?MODULE, {is_limited, Limiter, Int}, 5000) of true -> true; false -> false; Err -> ?ERROR_MSG("Call failed: ~p", [Err]), false end end. -define(CMD_TIMEOUT, 5000). -define(MAX_FILE_SIZE, 64 * 1024). cmd(Cmd) -> Port = open_port({spawn, Cmd}, [stream, eof, binary]), TRef = erlang:start_timer(?CMD_TIMEOUT, self(), timeout), recv_data(Port, TRef, <<>>). recv_data(Port, TRef, Buf) -> receive {Port, {data, Bytes}} -> NewBuf = <>, if byte_size(NewBuf) > (?MAX_FILE_SIZE) -> return(Port, TRef, {error, efbig}); true -> recv_data(Port, TRef, NewBuf) end; {Port, {data, _}} -> return(Port, TRef, {error, efbig}); {Port, eof} when Buf /= <<>> -> return(Port, TRef, {ok, Buf}); {Port, eof} -> return(Port, TRef, {error, enodata}); {timeout, TRef, _} -> return(Port, TRef, {error, timeout}) end. return(Port, TRef, Result) -> case erlang:cancel_timer(TRef) of false -> receive {timeout, TRef, _} -> ok after 0 -> ok end; _ -> ok end, catch port_close(Port), Result. is_feature_available() -> case get_prog_name() of Prog when is_binary(Prog) -> true; false -> false end. check_captcha_setup() -> case is_feature_available() of true -> case create_image() of {ok, _, _, _} -> ok; _Err -> ?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, " "but it can't generate images.", []), throw({error, captcha_cmd_enabled_but_fails}) end; false -> ok end. lookup_captcha(Id) -> case ets:lookup(captcha, Id) of [C] -> {ok, C}; _ -> {error, enoent} end. -spec check_captcha(binary(), binary()) -> captcha_not_found | captcha_valid | captcha_non_valid. check_captcha(Id, ProvidedKey) -> case ets:lookup(captcha, Id) of [#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] -> ets:delete(captcha, Id), erlang:cancel_timer(Tref), if ValidKey == ProvidedKey -> if is_pid(Pid) -> Pid ! {captcha_succeed, Args}; true -> ok end, captcha_valid; true -> if is_pid(Pid) -> Pid ! {captcha_failed, Args}; true -> ok end, captcha_non_valid end; _ -> captcha_not_found end. clean_treap(Treap, CleanPriority) -> case treap:is_empty(Treap) of true -> Treap; false -> {_Key, Priority, _Value} = treap:get_root(Treap), if Priority > CleanPriority -> clean_treap(treap:delete_root(Treap), CleanPriority); true -> Treap end end. now_priority() -> -p1_time_compat:system_time(micro_seconds). -spec opt_type(captcha_cmd) -> fun((binary()) -> binary()); (captcha_host) -> fun((binary()) -> binary()); (captcha_limit) -> fun((pos_integer()) -> pos_integer()); (atom()) -> [atom()]. opt_type(captcha_cmd) -> fun (FileName) -> F = iolist_to_binary(FileName), if F /= <<"">> -> F end end; opt_type(captcha_host) -> fun iolist_to_binary/1; opt_type(captcha_limit) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(_) -> [captcha_cmd, captcha_host, captcha_limit]. ejabberd-18.01/src/ejabberd_system_monitor.erl0000644000232200023220000002523213225664356022053 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_system_monitor.erl %%% Author : Alexey Shchepin %%% Description : Ejabberd watchdog %%% Created : 21 Mar 2007 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_system_monitor). -behaviour(gen_event). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -author('ekhramtsov@process-one.net'). %% API -export([start/0, opt_type/1]). %% gen_event callbacks -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). %% We don't use ejabberd logger because lager can be overloaded %% too and alarm_handler may get stuck. %%-include("logger.hrl"). -define(CHECK_INTERVAL, timer:seconds(30)). -define(DISK_FULL_THRES, 0.99). -record(state, {tref :: reference(), mref :: reference()}). -record(proc_stat, {qlen :: non_neg_integer(), memory :: non_neg_integer(), initial_call :: mfa(), current_function :: mfa(), ancestors :: [pid() | atom()], application :: pid() | atom(), name :: pid() | atom()}). -type state() :: #state{}. -type proc_stat() :: #proc_stat{}. %%%=================================================================== %%% API %%%=================================================================== -spec start() -> ok. start() -> gen_event:add_handler(alarm_handler, ?MODULE, []), gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {?MODULE, []}), application:load(os_mon), application:set_env(os_mon, start_cpu_sup, false), application:set_env(os_mon, start_os_sup, false), application:set_env(os_mon, start_memsup, true), application:set_env(os_mon, start_disksup, true), application:set_env(os_mon, disk_almost_full_threshold, ?DISK_FULL_THRES), ejabberd:start_app(os_mon). excluded_apps() -> [os_mon, mnesia, sasl, stdlib, kernel]. %%%=================================================================== %%% gen_event callbacks %%%=================================================================== init([]) -> {ok, #state{}}. handle_event({set_alarm, {system_memory_high_watermark, _}}, State) -> error_logger:warning_msg( "More than 80% of OS memory is allocated, " "starting OOM watchdog", []), handle_overload(State), {ok, restart_timer(State)}; handle_event({clear_alarm, system_memory_high_watermark}, State) -> cancel_timer(State#state.tref), error_logger:info_msg( "Memory consumption is back to normal, " "stopping OOM watchdog", []), {ok, State#state{tref = undefined}}; handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) -> case proc_stat(Pid, get_app_pids()) of #proc_stat{name = Name} = ProcStat -> error_logger:warning_msg( "Process ~p consumes more than 5% of OS memory (~s)", [Name, format_proc(ProcStat)]), handle_overload(State), {ok, State}; _ -> {ok, State} end; handle_event({clear_alarm, process_memory_high_watermark}, State) -> {ok, State}; handle_event({set_alarm, {{disk_almost_full, MountPoint}, _}}, State) -> error_logger:warning_msg("Disk is almost full on ~p", [MountPoint]), {ok, State}; handle_event({clear_alarm, {disk_almost_full, MountPoint}}, State) -> error_logger:info_msg("Disk usage is back to normal on ~p", [MountPoint]), {ok, State}; handle_event(Event, State) -> error_logger:warning_msg("unexpected event: ~p", [Event]), {ok, State}. handle_call(_Request, State) -> {ok, {error, badarg}, State}. handle_info({timeout, _TRef, handle_overload}, State) -> handle_overload(State), {ok, restart_timer(State)}; handle_info(Info, State) -> error_logger:warning_msg("unexpected info: ~p", [Info]), {ok, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec handle_overload(state()) -> ok. handle_overload(State) -> handle_overload(State, processes()). -spec handle_overload(state(), [pid()]) -> ok. handle_overload(_State, Procs) -> AppPids = get_app_pids(), {TotalMsgs, ProcsNum, Apps, Stats} = overloaded_procs(AppPids, Procs), if TotalMsgs >= 10000 -> SortedStats = lists:reverse(lists:keysort(#proc_stat.qlen, Stats)), error_logger:warning_msg( "The system is overloaded with ~b messages " "queued by ~b process(es) (~b%) " "from the following applications: ~s; " "the top processes are:~n~s", [TotalMsgs, ProcsNum, round(ProcsNum*100/length(Procs)), format_apps(Apps), format_top_procs(SortedStats)]), kill(SortedStats, round(TotalMsgs/ProcsNum)); true -> ok end, lists:foreach(fun erlang:garbage_collect/1, Procs). -spec get_app_pids() -> map(). get_app_pids() -> try application:info() of Info -> case lists:keyfind(running, 1, Info) of {_, Apps} -> lists:foldl( fun({Name, Pid}, M) when is_pid(Pid) -> maps:put(Pid, Name, M); (_, M) -> M end, #{}, Apps); false -> #{} end catch _:_ -> #{} end. -spec overloaded_procs(map(), [pid()]) -> {non_neg_integer(), non_neg_integer(), dict:dict(), [proc_stat()]}. overloaded_procs(AppPids, AllProcs) -> lists:foldl( fun(Pid, {TotalMsgs, ProcsNum, Apps, Stats}) -> case proc_stat(Pid, AppPids) of #proc_stat{qlen = QLen, application = App} = Stat when QLen > 0 -> {TotalMsgs + QLen, ProcsNum + 1, dict:update_counter(App, QLen, Apps), [Stat|Stats]}; _ -> {TotalMsgs, ProcsNum, Apps, Stats} end end, {0, 0, dict:new(), []}, AllProcs). -spec proc_stat(pid(), map()) -> proc_stat() | undefined. proc_stat(Pid, AppPids) -> case process_info(Pid, [message_queue_len, memory, initial_call, current_function, dictionary, group_leader, registered_name]) of [{_, MsgLen}, {_, Mem}, {_, InitCall}, {_, CurrFun}, {_, Dict}, {_, GL}, {_, Name}] -> IntLen = proplists:get_value('$internal_queue_len', Dict, 0), TrueInitCall = proplists:get_value('$initial_call', Dict, InitCall), Ancestors = proplists:get_value('$ancestors', Dict, []), Len = IntLen + MsgLen, App = maps:get(GL, AppPids, kernel), RegName = case Name of [] -> Pid; _ -> Name end, #proc_stat{qlen = Len, memory = Mem, initial_call = TrueInitCall, current_function = CurrFun, ancestors = Ancestors, application = App, name = RegName}; _ -> undefined end. -spec restart_timer(#state{}) -> #state{}. restart_timer(State) -> cancel_timer(State#state.tref), TRef = erlang:start_timer(?CHECK_INTERVAL, self(), handle_overload), State#state{tref = TRef}. -spec cancel_timer(reference()) -> ok. cancel_timer(undefined) -> ok; cancel_timer(TRef) -> case erlang:cancel_timer(TRef) of false -> receive {timeout, TRef, _} -> ok after 0 -> ok end; _ -> ok end. -spec format_apps(dict:dict()) -> io:data(). format_apps(Apps) -> AppList = lists:reverse(lists:keysort(2, dict:to_list(Apps))), string:join( [io_lib:format("~p (~b msgs)", [App, Msgs]) || {App, Msgs} <- AppList], ", "). -spec format_top_procs([proc_stat()]) -> io:data(). format_top_procs(Stats) -> Stats1 = lists:sublist(Stats, 5), string:join( lists:map( fun(#proc_stat{name = Name} = Stat) -> [io_lib:format("** ~w: ", [Name]), format_proc(Stat)] end,Stats1), io_lib:nl()). -spec format_proc(proc_stat()) -> io:data(). format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall, current_function = CurrFun, ancestors = Ancs, application = App}) -> io_lib:format( "msgs = ~b, memory = ~b, initial_call = ~s, " "current_function = ~s, ancestors = ~w, application = ~w", [Len, Mem, format_mfa(InitCall), format_mfa(CurrFun), Ancs, App]). -spec format_mfa(mfa()) -> io:data(). format_mfa({M, F, A}) when is_atom(M), is_atom(F), is_integer(A) -> io_lib:format("~s:~s/~b", [M, F, A]); format_mfa(WTF) -> io_lib:format("~w", [WTF]). -spec kill([proc_stat()], non_neg_integer()) -> ok. kill(Stats, Threshold) -> case ejabberd_config:get_option(oom_killer, true) of true -> do_kill(Stats, Threshold); false -> ok end. -spec do_kill([proc_stat()], non_neg_integer()) -> ok. do_kill(Stats, Threshold) -> Killed = lists:filtermap( fun(#proc_stat{qlen = Len, name = Name, application = App}) when Len >= Threshold -> case lists:member(App, excluded_apps()) of true -> error_logger:warning_msg( "Unable to kill process ~p from whitelisted " "application ~p", [Name, App]), false; false -> case kill_proc(Name) of false -> false; Pid -> maybe_restart_app(App), {true, Pid} end end; (_) -> false end, Stats), TotalKilled = length(Killed), if TotalKilled > 0 -> error_logger:error_msg( "Killed ~b process(es) consuming more than ~b message(s) each", [TotalKilled, Threshold]); true -> ok end. -spec kill_proc(pid() | atom()) -> false | pid(). kill_proc(undefined) -> false; kill_proc(Name) when is_atom(Name) -> kill_proc(whereis(Name)); kill_proc(Pid) -> exit(Pid, kill), Pid. -spec maybe_restart_app(atom()) -> any(). maybe_restart_app(lager) -> ejabberd_logger:restart(); maybe_restart_app(_) -> ok. -spec opt_type(oom_killer) -> fun((boolean()) -> boolean()); (atom()) -> [atom()]. opt_type(oom_killer) -> fun(B) when is_boolean(B) -> B end; opt_type(_) -> [oom_killer]. ejabberd-18.01/src/ejabberd_sip.erl0000644000232200023220000000573713225664356017563 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_sip.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 30 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2013-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_sip). -ifndef(SIP). -include("logger.hrl"). -export([socket_type/0, start/2, listen_opt_type/1]). log_error() -> ?CRITICAL_MSG("ejabberd is not compiled with SIP support", []). socket_type() -> log_error(), raw. listen_opt_type(_) -> log_error(), []. start(_, _) -> log_error(), {error, sip_not_compiled}. -else. %% API -export([tcp_init/2, udp_init/2, udp_recv/5, start/2, socket_type/0, listen_opt_type/1]). -include("ejabberd.hrl"). %%%=================================================================== %%% API %%%=================================================================== tcp_init(Socket, Opts) -> ejabberd:start_app(esip), esip_socket:tcp_init(Socket, set_certfile(Opts)). udp_init(Socket, Opts) -> ejabberd:start_app(esip), esip_socket:udp_init(Socket, Opts). udp_recv(Sock, Addr, Port, Data, Opts) -> esip_socket:udp_recv(Sock, Addr, Port, Data, Opts). start(Opaque, Opts) -> esip_socket:start(Opaque, Opts). socket_type() -> raw. set_certfile(Opts) -> case lists:keymember(certfile, 1, Opts) of true -> Opts; false -> case ejabberd_pkix:get_certfile(?MYNAME) of {ok, CertFile} -> [{certfile, CertFile}|Opts]; error -> case ejabberd_config:get_option({domain_certfile, ?MYNAME}) of undefined -> Opts; CertFile -> [{certfile, CertFile}|Opts] end end end. listen_opt_type(certfile) -> fun(S) -> %% We cannot deprecate the option for now: %% I think SIP clients are too stupid to set SNI ejabberd_pkix:add_certfile(S), iolist_to_binary(S) end; listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(_) -> [tls, certfile]. %%%=================================================================== %%% Internal functions %%%=================================================================== -endif. ejabberd-18.01/src/mod_proxy65_mnesia.erl0000644000232200023220000001131313225664356020664 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 16 Jan 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_proxy65_mnesia). -behaviour(gen_server). -behaviour(mod_proxy65). %% API -export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]). -export([start_link/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("logger.hrl"). -record(bytestream, {sha1 = <<"">> :: binary() | '$1', target :: pid() | '_', initiator :: pid() | '_' | undefined, active = false :: boolean() | '_', jid_i :: undefined | binary() | '_'}). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, supervisor:start_child(ejabberd_backend_sup, Spec). register_stream(SHA1, StreamPid) -> F = fun () -> case mnesia:read(bytestream, SHA1, write) of [] -> mnesia:write(#bytestream{sha1 = SHA1, target = StreamPid}); [#bytestream{target = Pid, initiator = undefined} = ByteStream] when is_pid(Pid), Pid /= StreamPid -> mnesia:write(ByteStream#bytestream{ initiator = StreamPid}) end end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, Reason} end. unregister_stream(SHA1) -> F = fun () -> mnesia:delete({bytestream, SHA1}) end, case mnesia:transaction(F) of {atomic, ok} -> ok; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {error, Reason} end. activate_stream(SHA1, Initiator, MaxConnections, _Node) -> case gen_server:call(?MODULE, {activate_stream, SHA1, Initiator, MaxConnections}) of {atomic, {ok, IPid, TPid}} -> {ok, IPid, TPid}; {atomic, {limit, IPid, TPid}} -> {error, {limit, IPid, TPid}}; {atomic, conflict} -> {error, conflict}; {atomic, notfound} -> {error, notfound}; Err -> {error, Err} end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> ejabberd_mnesia:create(?MODULE, bytestream, [{ram_copies, [node()]}, {attributes, record_info(fields, bytestream)}]), {ok, #state{}}. handle_call({activate_stream, SHA1, Initiator, MaxConnections}, _From, State) -> F = fun () -> case mnesia:read(bytestream, SHA1, write) of [#bytestream{target = TPid, initiator = IPid} = ByteStream] when is_pid(TPid), is_pid(IPid) -> ActiveFlag = ByteStream#bytestream.active, if ActiveFlag == false -> ConnsPerJID = mnesia:select( bytestream, [{#bytestream{sha1 = '$1', jid_i = Initiator, _ = '_'}, [], ['$1']}]), if length(ConnsPerJID) < MaxConnections -> mnesia:write( ByteStream#bytestream{active = true, jid_i = Initiator}), {ok, IPid, TPid}; true -> {limit, IPid, TPid} end; true -> conflict end; _ -> notfound end end, Reply = mnesia:transaction(F), {reply, Reply, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/ejabberd_sup.erl0000644000232200023220000001347513225664356017575 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_sup.erl %%% Author : Alexey Shchepin %%% Purpose : Erlang/OTP supervisor %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sup). -author('alexey@process-one.net'). -behaviour(supervisor). -export([start_link/0, init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> Hooks = {ejabberd_hooks, {ejabberd_hooks, start_link, []}, permanent, brutal_kill, worker, [ejabberd_hooks]}, Cluster = {ejabberd_cluster, {ejabberd_cluster, start_link, []}, permanent, 5000, worker, [ejabberd_cluster]}, S2S = {ejabberd_s2s, {ejabberd_s2s, start_link, []}, permanent, brutal_kill, worker, [ejabberd_s2s]}, Captcha = {ejabberd_captcha, {ejabberd_captcha, start_link, []}, permanent, brutal_kill, worker, [ejabberd_captcha]}, Listener = {ejabberd_listener, {ejabberd_listener, start_link, []}, permanent, infinity, supervisor, [ejabberd_listener]}, S2SInSupervisor = {ejabberd_s2s_in_sup, {ejabberd_tmp_sup, start_link, [ejabberd_s2s_in_sup, ejabberd_s2s_in]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, S2SOutSupervisor = {ejabberd_s2s_out_sup, {ejabberd_tmp_sup, start_link, [ejabberd_s2s_out_sup, ejabberd_s2s_out]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, ServiceSupervisor = {ejabberd_service_sup, {ejabberd_tmp_sup, start_link, [ejabberd_service_sup, ejabberd_service]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, IQSupervisor = {ejabberd_iq_sup, {ejabberd_tmp_sup, start_link, [ejabberd_iq_sup, gen_iq_handler]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, BackendSupervisor = {ejabberd_backend_sup, {ejabberd_backend_sup, start_link, []}, permanent, infinity, supervisor, [ejabberd_backend_sup]}, ACL = {acl, {acl, start_link, []}, permanent, 5000, worker, [acl]}, Shaper = {shaper, {shaper, start_link, []}, permanent, 5000, worker, [shaper]}, SQLSupervisor = {ejabberd_rdbms, {ejabberd_rdbms, start_link, []}, permanent, infinity, supervisor, [ejabberd_rdbms]}, RiakSupervisor = {ejabberd_riak_sup, {ejabberd_riak_sup, start_link, []}, permanent, infinity, supervisor, [ejabberd_riak_sup]}, RedisSupervisor = {ejabberd_redis_sup, {ejabberd_redis_sup, start_link, []}, permanent, infinity, supervisor, [ejabberd_redis_sup]}, Router = {ejabberd_router, {ejabberd_router, start_link, []}, permanent, 5000, worker, [ejabberd_router]}, RouterMulticast = {ejabberd_router_multicast, {ejabberd_router_multicast, start_link, []}, permanent, 5000, worker, [ejabberd_router_multicast]}, Local = {ejabberd_local, {ejabberd_local, start_link, []}, permanent, 5000, worker, [ejabberd_local]}, SM = {ejabberd_sm, {ejabberd_sm, start_link, []}, permanent, 5000, worker, [ejabberd_sm]}, GenModSupervisor = {ejabberd_gen_mod_sup, {gen_mod, start_link, []}, permanent, infinity, supervisor, [gen_mod]}, ExtMod = {ext_mod, {ext_mod, start_link, []}, permanent, 5000, worker, [ext_mod]}, Auth = {ejabberd_auth, {ejabberd_auth, start_link, []}, permanent, 5000, worker, [ejabberd_auth]}, OAuth = {ejabberd_oauth, {ejabberd_oauth, start_link, []}, permanent, 5000, worker, [ejabberd_oauth]}, Translation = {translate, {translate, start_link, []}, permanent, 5000, worker, [translate]}, AccessPerms = {ejabberd_access_permissions, {ejabberd_access_permissions, start_link, []}, permanent, 5000, worker, [ejabberd_access_permissions]}, Ctl = {ejabberd_ctl, {ejabberd_ctl, start_link, []}, permanent, 5000, worker, [ejabberd_ctl]}, Commands = {ejabberd_commands, {ejabberd_commands, start_link, []}, permanent, 5000, worker, [ejabberd_commands]}, Admin = {ejabberd_admin, {ejabberd_admin, start_link, []}, permanent, 5000, worker, [ejabberd_admin]}, CyrSASL = {cyrsasl, {cyrsasl, start_link, []}, permanent, 5000, worker, [cyrsasl]}, PKIX = {ejabberd_pkix, {ejabberd_pkix, start_link, []}, permanent, 5000, worker, [ejabberd_pkix]}, ACME = {ejabberd_acme, {ejabberd_acme, start_link, []}, permanent, 5000, worker, [ejabberd_acme]}, IQ = {ejabberd_iq, {ejabberd_iq, start_link, []}, permanent, 5000, worker, [ejabberd_iq]}, {ok, {{one_for_one, 10, 1}, [Hooks, Cluster, CyrSASL, Translation, AccessPerms, Ctl, Commands, Admin, PKIX, ACME, Listener, S2S, Captcha, S2SInSupervisor, S2SOutSupervisor, ServiceSupervisor, IQSupervisor, ACL, Shaper, BackendSupervisor, SQLSupervisor, RiakSupervisor, RedisSupervisor, IQ, Router, RouterMulticast, Local, SM, ExtMod, GenModSupervisor, Auth, OAuth]}}. ejabberd-18.01/src/ejabberd_tmp_sup.erl0000644000232200023220000000271713225664356020452 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_tmp_sup.erl %%% Author : Alexey Shchepin %%% Purpose : Supervisor for temporary processess %%% Created : 18 Jul 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_tmp_sup). -author('alexey@process-one.net'). -export([start_link/2, init/1]). start_link(Name, Module) -> supervisor:start_link({local, Name}, ?MODULE, Module). init(Module) -> {ok, {{simple_one_for_one, 10, 1}, [{undefined, {Module, start_link, []}, temporary, 1000, worker, [Module]}]}}. ejabberd-18.01/src/ejabberd_commands_doc.erl0000644000232200023220000006562313225664356021416 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_commands_doc.erl %%% Author : Badlop %%% Purpose : Management of ejabberd commands %%% Created : 20 May 2008 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_commands_doc). -author('pawel@process-one.net'). -export([generate_html_output/3]). -export([generate_md_output/3]). -include("ejabberd_commands.hrl"). -include("ejabberd.hrl"). -define(RAW(V), if HTMLOutput -> fxml:crypt(iolist_to_binary(V)); true -> iolist_to_binary(V) end). -define(TAG(N), if HTMLOutput -> [<<"<", ??N, "/>">>]; true -> md_tag(N, <<"">>) end). -define(TAG(N, V), if HTMLOutput -> [<<"<", ??N, ">">>, V, <<"">>]; true -> md_tag(N, V) end). -define(TAG(N, C, V), if HTMLOutput -> [<<"<", ??N, " class='", C, "'>">>, V, <<"">>]; true -> md_tag(N, V) end). -define(TAG_R(N, V), ?TAG(N, ?RAW(V))). -define(TAG_R(N, C, V), ?TAG(N, C, ?RAW(V))). -define(SPAN(N, V), ?TAG_R(span, ??N, V)). -define(STR(A), ?SPAN(str,[<<"\"">>, A, <<"\"">>])). -define(NUM(A), ?SPAN(num,integer_to_binary(A))). -define(FIELD(A), ?SPAN(field,A)). -define(ID(A), ?SPAN(id,A)). -define(OP(A), ?SPAN(op,A)). -define(ARG(A), ?FIELD(atom_to_list(A))). -define(KW(A), ?SPAN(kw,A)). -define(BR, <<"\n">>). -define(ARG_S(A), ?STR(atom_to_list(A))). -define(RAW_L(A), ?RAW(<>)). -define(STR_L(A), ?STR(<>)). -define(FIELD_L(A), ?FIELD(<>)). -define(ID_L(A), ?ID(<>)). -define(OP_L(A), ?OP(<>)). -define(KW_L(A), ?KW(<>)). -define(STR_A(A), ?STR(atom_to_list(A))). -define(ID_A(A), ?ID(atom_to_list(A))). list_join_with([], _M) -> []; list_join_with([El|Tail], M) -> lists:reverse(lists:foldl(fun(E, Acc) -> [E, M | Acc] end, [El], Tail)). md_tag(dt, V) -> [<<"- ">>, V]; md_tag(dd, V) -> [<<" : ">>, V, <<"\n">>]; md_tag(li, V) -> [<<"- ">>, V, <<"\n">>]; md_tag(pre, V) -> [V, <<"\n">>]; md_tag(p, V) -> [<<"\n\n">>, V, <<"\n">>]; md_tag(h1, V) -> [<<"\n\n## ">>, V, <<"\n">>]; md_tag(h2, V) -> [<<"\n\n### ">>, V, <<"\n">>]; md_tag(strong, V) -> [<<"*">>, V, <<"*">>]; md_tag(_, V) -> V. perl_gen({Name, integer}, Int, _Indent, HTMLOutput) -> [?ARG(Name), ?OP_L(" => "), ?NUM(Int)]; perl_gen({Name, string}, Str, _Indent, HTMLOutput) -> [?ARG(Name), ?OP_L(" => "), ?STR(Str)]; perl_gen({Name, binary}, Str, _Indent, HTMLOutput) -> [?ARG(Name), ?OP_L(" => "), ?STR(Str)]; perl_gen({Name, atom}, Atom, _Indent, HTMLOutput) -> [?ARG(Name), ?OP_L(" => "), ?STR_A(Atom)]; perl_gen({Name, {tuple, Fields}}, Tuple, Indent, HTMLOutput) -> Res = lists:map(fun({A,B})->perl_gen(A, B, Indent, HTMLOutput) end, lists:zip(Fields, tuple_to_list(Tuple))), [?ARG(Name), ?OP_L(" => {"), list_join_with(Res, [?OP_L(", ")]), ?OP_L("}")]; perl_gen({Name, {list, ElDesc}}, List, Indent, HTMLOutput) -> Res = lists:map(fun(E) -> [?OP_L("{"), perl_gen(ElDesc, E, Indent, HTMLOutput), ?OP_L("}")] end, List), [?ARG(Name), ?OP_L(" => ["), list_join_with(Res, [?OP_L(", ")]), ?OP_L("]")]. perl_call(Name, ArgsDesc, Values, HTMLOutput) -> {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<" ">>, <<"~~~ perl\n">>} end, [Preamble, Indent, ?ID_L("XMLRPC::Lite"), ?OP_L("->"), ?ID_L("proxy"), ?OP_L("("), ?ID_L("$url"), ?OP_L(")->"), ?ID_L("call"), ?OP_L("("), ?STR_A(Name), ?OP_L(", {"), ?BR, Indent, <<" ">>, list_join_with(lists:map(fun({A,B})->perl_gen(A, B, <>, HTMLOutput) end, lists:zip(ArgsDesc, Values)), [?OP_L(","), ?BR, Indent, <<" ">>]), ?BR, Indent, ?OP_L("})->"), ?ID_L("results"), ?OP_L("()")]. java_gen_map(Vals, Indent, HTMLOutput) -> {Split, NL} = case Indent of none -> {<<" ">>, <<" ">>}; _ -> {[?BR, <<" ", Indent/binary>>], [?BR, Indent]} end, [?KW_L("new "), ?ID_L("HashMap"), ?OP_L("<"), ?ID_L("String"), ?OP_L(", "), ?ID_L("Object"), ?OP_L(">() {{"), Split, list_join_with(Vals, Split), NL, ?OP_L("}}")]. java_gen({Name, integer}, Int, _Indent, HTMLOutput) -> [?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?KW_L("new "), ?ID_L("Integer"), ?OP_L("("), ?NUM(Int), ?OP_L("));")]; java_gen({Name, string}, Str, _Indent, HTMLOutput) -> [?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?STR(Str), ?OP_L(");")]; java_gen({Name, binary}, Str, _Indent, HTMLOutput) -> [?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?STR(Str), ?OP_L(");")]; java_gen({Name, atom}, Atom, _Indent, HTMLOutput) -> [?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?STR_A(Atom), ?OP_L(");")]; java_gen({Name, {tuple, Fields}}, Tuple, Indent, HTMLOutput) -> NewIndent = <<" ", Indent/binary>>, Res = lists:map(fun({A, B}) -> [java_gen(A, B, NewIndent, HTMLOutput)] end, lists:zip(Fields, tuple_to_list(Tuple))), [?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), java_gen_map(Res, Indent, HTMLOutput), ?OP_L(")")]; java_gen({Name, {list, ElDesc}}, List, Indent, HTMLOutput) -> {NI, NI2, I} = case List of [_] -> {" ", " ", Indent}; _ -> {[?BR, <<" ", Indent/binary>>], [?BR, <<" ", Indent/binary>>], <<" ", Indent/binary>>} end, Res = lists:map(fun(E) -> java_gen_map([java_gen(ElDesc, E, I, HTMLOutput)], none, HTMLOutput) end, List), [?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?KW_L("new "), ?ID_L("Object"), ?OP_L("[] {"), NI, list_join_with(Res, [?OP_L(","), NI]), NI2, ?OP_L("});")]. java_call(Name, ArgsDesc, Values, HTMLOutput) -> {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<" ">>, <<"~~~ java\n">>} end, [Preamble, Indent, ?ID_L("XmlRpcClientConfigImpl config"), ?OP_L(" = "), ?KW_L("new "), ?ID_L("XmlRpcClientConfigImpl"), ?OP_L("();"), ?BR, Indent, ?ID_L("config"), ?OP_L("."), ?ID_L("setServerURL"), ?OP_L("("), ?ID_L("url"), ?OP_L(");"), ?BR, Indent, ?BR, Indent, ?ID_L("XmlRpcClient client"), ?OP_L(" = "), ?KW_L("new "), ?ID_L("XmlRpcClient"), ?OP_L("();"), ?BR, Indent, ?ID_L("client"), ?OP_L("."), ?ID_L("setConfig"), ?OP_L("("), ?ID_L("config"), ?OP_L(");"), ?BR, Indent, ?BR, Indent, ?ID_L("client"), ?OP_L("."), ?ID_L("execute"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), java_gen_map(lists:map(fun({A,B})->java_gen(A, B, Indent, HTMLOutput) end, lists:zip(ArgsDesc, Values)), Indent, HTMLOutput), ?OP_L(");")]. -define(XML_S(N, V), ?OP_L("<"), ?FIELD_L(??N), ?OP_L(">"), V). -define(XML_E(N), ?OP_L("")). -define(XML(N, Indent, V), ?BR, Indent, ?XML_S(N, V), ?BR, Indent, ?XML_E(N)). -define(XML(N, Indent, D, V), ?XML(N, [Indent, lists:duplicate(D, <<" ">>)], V)). -define(XML_L(N, Indent, V), ?BR, Indent, ?XML_S(N, V), ?XML_E(N)). -define(XML_L(N, Indent, D, V), ?XML_L(N, [Indent, lists:duplicate(D, <<" ">>)], V)). xml_gen({Name, integer}, Int, Indent, HTMLOutput) -> [?XML(member, Indent, [?XML_L(name, Indent, 1, ?ID_A(Name)), ?XML(value, Indent, 1, [?XML_L(integer, Indent, 2, ?ID(integer_to_binary(Int)))])])]; xml_gen({Name, string}, Str, Indent, HTMLOutput) -> [?XML(member, Indent, [?XML_L(name, Indent, 1, ?ID_A(Name)), ?XML(value, Indent, 1, [?XML_L(string, Indent, 2, ?ID(Str))])])]; xml_gen({Name, binary}, Str, Indent, HTMLOutput) -> [?XML(member, Indent, [?XML_L(name, Indent, 1, ?ID_A(Name)), ?XML(value, Indent, 1, [?XML_L(string, Indent, 2, ?ID(Str))])])]; xml_gen({Name, atom}, Atom, Indent, HTMLOutput) -> [?XML(member, Indent, [?XML_L(name, Indent, 1, ?ID_A(Name)), ?XML(value, Indent, 1, [?XML_L(string, Indent, 2, ?ID(atom_to_list(Atom)))])])]; xml_gen({Name, {tuple, Fields}}, Tuple, Indent, HTMLOutput) -> NewIndent = <<" ", Indent/binary>>, Res = lists:map(fun({A, B}) -> xml_gen(A, B, NewIndent, HTMLOutput) end, lists:zip(Fields, tuple_to_list(Tuple))), [?XML(member, Indent, [?XML_L(name, Indent, 1, ?ID_A(Name)), ?XML(value, Indent, 1, [?XML(struct, NewIndent, Res)])])]; xml_gen({Name, {list, ElDesc}}, List, Indent, HTMLOutput) -> Ind1 = <<" ", Indent/binary>>, Ind2 = <<" ", Ind1/binary>>, Res = lists:map(fun(E) -> [?XML(value, Ind1, [?XML(struct, Ind1, 1, xml_gen(ElDesc, E, Ind2, HTMLOutput))])] end, List), [?XML(member, Indent, [?XML_L(name, Indent, 1, ?ID_A(Name)), ?XML(value, Indent, 1, [?XML(array, Indent, 2, [?XML(data, Indent, 3, Res)])])])]. xml_call(Name, ArgsDesc, Values, HTMLOutput) -> {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<" ">>, <<"~~~ xml">>} end, Res = lists:map(fun({A, B}) -> xml_gen(A, B, <>, HTMLOutput) end, lists:zip(ArgsDesc, Values)), [Preamble, ?XML(methodCall, Indent, [?XML_L(methodName, Indent, 1, ?ID_A(Name)), ?XML(params, Indent, 1, [?XML(param, Indent, 2, [?XML(value, Indent, 3, [?XML(struct, Indent, 4, Res)])])])])]. % [?ARG_S(Name), ?OP_L(": "), ?STR(Str)]; json_gen({_Name, integer}, Int, _Indent, HTMLOutput) -> [?NUM(Int)]; json_gen({_Name, string}, Str, _Indent, HTMLOutput) -> [?STR(Str)]; json_gen({_Name, binary}, Str, _Indent, HTMLOutput) -> [?STR(Str)]; json_gen({_Name, atom}, Atom, _Indent, HTMLOutput) -> [?STR_A(Atom)]; json_gen({_Name, rescode}, Val, _Indent, HTMLOutput) -> [?ID_A(Val == ok orelse Val == true)]; json_gen({_Name, restuple}, {Val, Str}, _Indent, HTMLOutput) -> [?OP_L("{"), ?STR_L("res"), ?OP_L(": "), ?ID_A(Val == ok orelse Val == true), ?OP_L(", "), ?STR_L("text"), ?OP_L(": "), ?STR(Str), ?OP_L("}")]; json_gen({_Name, {list, {_, {tuple, [{_, atom}, ValFmt]}}}}, List, Indent, HTMLOutput) -> Indent2 = <<" ", Indent/binary>>, Res = lists:map(fun({N, V})->[?STR_A(N), ?OP_L(": "), json_gen(ValFmt, V, Indent2, HTMLOutput)] end, List), [?OP_L("{"), ?BR, Indent2, list_join_with(Res, [?OP_L(","), ?BR, Indent2]), ?BR, Indent, ?OP_L("}")]; json_gen({_Name, {tuple, Fields}}, Tuple, Indent, HTMLOutput) -> Indent2 = <<" ", Indent/binary>>, Res = lists:map(fun({{N, _} = A, B})->[?STR_A(N), ?OP_L(": "), json_gen(A, B, Indent2, HTMLOutput)] end, lists:zip(Fields, tuple_to_list(Tuple))), [?OP_L("{"), ?BR, Indent2, list_join_with(Res, [?OP_L(","), ?BR, Indent2]), ?BR, Indent, ?OP_L("}")]; json_gen({_Name, {list, ElDesc}}, List, Indent, HTMLOutput) -> Indent2 = <<" ", Indent/binary>>, Res = lists:map(fun(E) -> json_gen(ElDesc, E, Indent2, HTMLOutput) end, List), [?OP_L("["), ?BR, Indent2, list_join_with(Res, [?OP_L(","), ?BR, Indent2]), ?BR, Indent, ?OP_L("]")]. json_call(Name, ArgsDesc, Values, ResultDesc, Result, HTMLOutput) -> {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<" ">>, <<"~~~ json\n">>} end, {Code, ResultStr} = case {ResultDesc, Result} of {{_, rescode}, V} when V == true; V == ok -> {200, [?STR_L("")]}; {{_, rescode}, _} -> {500, [?STR_L("")]}; {{_, restuple}, {V1, Text1}} when V1 == true; V1 == ok -> {200, [?STR(Text1)]}; {{_, restuple}, {_, Text2}} -> {500, [?STR(Text2)]}; {{_, {list, _}}, _} -> {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)}; {{_, {tuple, _}}, _} -> {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)}; {{Name0, _}, _} -> {200, [Indent, ?OP_L("{"), ?STR_A(Name0), ?OP_L(": "), json_gen(ResultDesc, Result, Indent, HTMLOutput), ?OP_L("}")]} end, CodeStr = case Code of 200 -> <<" 200 OK">>; 500 -> <<" 500 Internal Server Error">> end, [Preamble, Indent, ?ID_L("POST /api/"), ?ID_A(Name), ?BR, Indent, ?OP_L("{"), ?BR, Indent, <<" ">>, list_join_with(lists:map(fun({{N,_}=A,B})->[?STR_A(N), ?OP_L(": "), json_gen(A, B, <>, HTMLOutput)] end, lists:zip(ArgsDesc, Values)), [?OP_L(","), ?BR, Indent, <<" ">>]), ?BR, Indent, ?OP_L("}"), ?BR, Indent, ?BR, Indent, ?ID_L("HTTP/1.1"), ?ID(CodeStr), ?BR, Indent, ResultStr ]. generate_example_input({_Name, integer}, {LastStr, LastNum}) -> {LastNum+1, {LastStr, LastNum+1}}; generate_example_input({_Name, string}, {LastStr, LastNum}) -> {string:chars(LastStr+1, 5), {LastStr+1, LastNum}}; generate_example_input({_Name, binary}, {LastStr, LastNum}) -> {iolist_to_binary(string:chars(LastStr+1, 5)), {LastStr+1, LastNum}}; generate_example_input({_Name, atom}, {LastStr, LastNum}) -> {list_to_atom(string:chars(LastStr+1, 5)), {LastStr+1, LastNum}}; generate_example_input({_Name, rescode}, {LastStr, LastNum}) -> {ok, {LastStr, LastNum}}; generate_example_input({_Name, restuple}, {LastStr, LastNum}) -> {{ok, <<"Success">>}, {LastStr, LastNum}}; generate_example_input({_Name, {tuple, Fields}}, Data) -> {R, D} = lists:foldl(fun(Field, {Res2, Data2}) -> {Res3, Data3} = generate_example_input(Field, Data2), {[Res3 | Res2], Data3} end, {[], Data}, Fields), {list_to_tuple(lists:reverse(R)), D}; generate_example_input({_Name, {list, Desc}}, Data) -> {R1, D1} = generate_example_input(Desc, Data), {R2, D2} = generate_example_input(Desc, D1), {[R1, R2], D2}. gen_calls(#ejabberd_commands{args_example=none, args=ArgsDesc} = C, HTMLOutput, Langs) -> {R, _} = lists:foldl(fun(Arg, {Res, Data}) -> {Res3, Data3} = generate_example_input(Arg, Data), {[Res3 | Res], Data3} end, {[], {$a-1, 0}}, ArgsDesc), gen_calls(C#ejabberd_commands{args_example=lists:reverse(R)}, HTMLOutput, Langs); gen_calls(#ejabberd_commands{result_example=none, result=ResultDesc} = C, HTMLOutput, Langs) -> {R, _} = generate_example_input(ResultDesc, {$a-1, 0}), gen_calls(C#ejabberd_commands{result_example=R}, HTMLOutput, Langs); gen_calls(#ejabberd_commands{args_example=Values, args=ArgsDesc, result_example=Result, result=ResultDesc, name=Name}, HTMLOutput, Langs) -> Perl = perl_call(Name, ArgsDesc, Values, HTMLOutput), Java = java_call(Name, ArgsDesc, Values, HTMLOutput), XML = xml_call(Name, ArgsDesc, Values, HTMLOutput), JSON = json_call(Name, ArgsDesc, Values, ResultDesc, Result, HTMLOutput), if HTMLOutput -> [?TAG(ul, "code-samples-names", [case lists:member(<<"java">>, Langs) of true -> ?TAG(li, <<"Java">>); _ -> [] end, case lists:member(<<"perl">>, Langs) of true -> ?TAG(li, <<"Perl">>); _ -> [] end, case lists:member(<<"xmlrpc">>, Langs) of true -> ?TAG(li, <<"XML">>); _ -> [] end, case lists:member(<<"json">>, Langs) of true -> ?TAG(li, <<"JSON">>); _ -> [] end]), ?TAG(ul, "code-samples", [case lists:member(<<"java">>, Langs) of true -> ?TAG(li, ?TAG(pre, Java)); _ -> [] end, case lists:member(<<"perl">>, Langs) of true -> ?TAG(li, ?TAG(pre, Perl)); _ -> [] end, case lists:member(<<"xmlrpc">>, Langs) of true -> ?TAG(li, ?TAG(pre, XML)); _ -> [] end, case lists:member(<<"json">>, Langs) of true -> ?TAG(li, ?TAG(pre, JSON)); _ -> [] end])]; true -> case Langs of Val when length(Val) == 0 orelse length(Val) == 1 -> [case lists:member(<<"java">>, Langs) of true -> [<<"\n">>, ?TAG(pre, Java), <<"~~~\n">>]; _ -> [] end, case lists:member(<<"perl">>, Langs) of true -> [<<"\n">>, ?TAG(pre, Perl), <<"~~~\n">>]; _ -> [] end, case lists:member(<<"xmlrpc">>, Langs) of true -> [<<"\n">>, ?TAG(pre, XML), <<"~~~\n">>]; _ -> [] end, case lists:member(<<"json">>, Langs) of true -> [<<"\n">>, ?TAG(pre, JSON), <<"~~~\n">>]; _ -> [] end, <<"\n\n">>]; _ -> [<<"\n">>, case lists:member(<<"java">>, Langs) of true -> <<"* Java\n">>; _ -> [] end, case lists:member(<<"perl">>, Langs) of true -> <<"* Perl\n">>; _ -> [] end, case lists:member(<<"xmlrpc">>, Langs) of true -> <<"* XmlRPC\n">>; _ -> [] end, case lists:member(<<"json">>, Langs) of true -> <<"* JSON\n">>; _ -> [] end, <<"{: .code-samples-labels}\n">>, case lists:member(<<"java">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, Java), <<"~~~\n">>]; _ -> [] end, case lists:member(<<"perl">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, Perl), <<"~~~\n">>]; _ -> [] end, case lists:member(<<"xmlrpc">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, XML), <<"~~~\n">>]; _ -> [] end, case lists:member(<<"json">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, JSON), <<"~~~\n">>]; _ -> [] end, <<"{: .code-samples-tabs}\n\n">>] end end. format_type({list, {_, {tuple, Els}}}) -> io_lib:format("[~s]", [format_type({tuple, Els})]); format_type({list, El}) -> io_lib:format("[~s]", [format_type(El)]); format_type({tuple, Els}) -> Args = [format_type(El) || El <- Els], io_lib:format("{~s}", [string:join(Args, ", ")]); format_type({Name, Type}) -> io_lib:format("~s::~s", [Name, format_type(Type)]); format_type(binary) -> "string"; format_type(atom) -> "string"; format_type(Type) -> io_lib:format("~p", [Type]). gen_param(Name, Type, undefined, HTMLOutput) -> [?TAG(li, [?TAG_R(strong, atom_to_list(Name)), <<" :: ">>, ?RAW(format_type(Type))])]; gen_param(Name, Type, Desc, HTMLOutput) -> [?TAG(dt, [?TAG_R(strong, atom_to_list(Name)), <<" :: ">>, ?RAW(format_type(Type))]), ?TAG(dd, ?RAW(Desc))]. gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc, args=Args, args_desc=ArgsDesc, result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) -> try LDesc = case LongDesc of "" -> Desc; _ -> LongDesc end, ArgsText = case ArgsDesc of none -> [?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput) || {AName, Type} <- Args])]; _ -> [?TAG(dl, "args-list", [gen_param(AName, Type, ADesc, HTMLOutput) || {{AName, Type}, ADesc} <- lists:zip(Args, ArgsDesc)])] end, ResultText = case Result of {res,rescode} -> [?TAG(dl, [gen_param(res, integer, "Status code (0 on success, 1 otherwise)", HTMLOutput)])]; {res,restuple} -> [?TAG(dl, [gen_param(res, string, "Raw result string", HTMLOutput)])]; {RName, Type} -> case ResultDesc of none -> [?TAG(ul, [gen_param(RName, Type, undefined, HTMLOutput)])]; _ -> [?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])] end end, [?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]), ?TAG(p, ?RAW(LDesc)), ?TAG(h2, <<"Arguments:">>), ArgsText, ?TAG(h2, <<"Result:">>), ResultText, ?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)] catch _:Ex -> throw(iolist_to_binary(io_lib:format( <<"Error when generating documentation for command '~p': ~p">>, [Name, Ex]))) end. find_commands_definitions() -> case code:lib_dir(ejabberd, ebin) of {error, _} -> lists:map(fun({N, _, _}) -> ejabberd_commands:get_command_definition(N) end, ejabberd_commands:list_commands()); Path -> lists:flatmap(fun(P) -> Mod = list_to_atom(filename:rootname(P)), code:ensure_loaded(Mod), case erlang:function_exported(Mod, get_commands_spec, 0) of true -> apply(Mod, get_commands_spec, []); _ -> [] end end, filelib:wildcard("*.beam", Path)) end. generate_html_output(File, RegExp, Languages) -> Cmds = find_commands_definitions(), {ok, RE} = re:compile(RegExp), Cmds2 = lists:filter(fun(#ejabberd_commands{name=Name, module=Module}) -> re:run(atom_to_list(Name), RE, [{capture, none}]) == match orelse re:run(atom_to_list(Module), RE, [{capture, none}]) == match end, Cmds), Cmds3 = lists:sort(fun(#ejabberd_commands{name=N1}, #ejabberd_commands{name=N2}) -> N1 =< N2 end, Cmds2), Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), Out = lists:map(fun(C) -> gen_doc(C, true, Langs) end, Cmds4), {ok, Fh} = file:open(File, [write]), io:format(Fh, "~s", [[html_pre(), Out, html_post()]]), file:close(Fh), ok. maybe_add_policy_arguments(#ejabberd_commands{args=Args1, policy=user}=Cmd) -> Args2 = [{user, binary}, {server, binary} | Args1], Cmd#ejabberd_commands{args = Args2}; maybe_add_policy_arguments(Cmd) -> Cmd. generate_md_output(File, RegExp, Languages) -> Cmds = find_commands_definitions(), {ok, RE} = re:compile(RegExp), Cmds2 = lists:filter(fun(#ejabberd_commands{name=Name, module=Module}) -> re:run(atom_to_list(Name), RE, [{capture, none}]) == match orelse re:run(atom_to_list(Module), RE, [{capture, none}]) == match end, Cmds), Cmds3 = lists:sort(fun(#ejabberd_commands{name=N1}, #ejabberd_commands{name=N2}) -> N1 =< N2 end, Cmds2), Langs = binary:split(Languages, <<",">>, [global]), Header = <<"---\ntitle: Administration API reference\nbodyclass: nocomment\n---">>, Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds3), {ok, Fh} = file:open(File, [write]), io:format(Fh, "~s~s", [Header, Out]), file:close(Fh), ok. html_pre() -> " ". html_post() -> " ". ejabberd-18.01/src/mod_shared_roster_mnesia.erl0000644000232200023220000001335413225664356022203 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_shared_roster_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_shared_roster_mnesia). -behaviour(mod_shared_roster). %% API -export([init/2, list_groups/1, groups_with_opts/1, create_group/3, delete_group/2, get_group_opts/2, set_group_opts/3, get_user_groups/2, get_group_explicit_users/2, get_user_displayed_groups/3, is_user_in_group/3, add_user_to_group/3, remove_user_from_group/3, import/3]). -export([need_transform/1, transform/1]). -include("mod_roster.hrl"). -include("mod_shared_roster.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, sr_group, [{disc_copies, [node()]}, {attributes, record_info(fields, sr_group)}]), ejabberd_mnesia:create(?MODULE, sr_user, [{disc_copies, [node()]}, {type, bag}, {attributes, record_info(fields, sr_user)}, {index, [group_host]}]). list_groups(Host) -> mnesia:dirty_select(sr_group, [{#sr_group{group_host = {'$1', '$2'}, _ = '_'}, [{'==', '$2', Host}], ['$1']}]). groups_with_opts(Host) -> Gs = mnesia:dirty_select(sr_group, [{#sr_group{group_host = {'$1', Host}, opts = '$2', _ = '_'}, [], [['$1', '$2']]}]), lists:map(fun ([G, O]) -> {G, O} end, Gs). create_group(Host, Group, Opts) -> R = #sr_group{group_host = {Group, Host}, opts = Opts}, F = fun () -> mnesia:write(R) end, mnesia:transaction(F). delete_group(Host, Group) -> GroupHost = {Group, Host}, F = fun () -> mnesia:delete({sr_group, GroupHost}), Users = mnesia:index_read(sr_user, GroupHost, #sr_user.group_host), lists:foreach(fun (UserEntry) -> mnesia:delete_object(UserEntry) end, Users) end, mnesia:transaction(F). get_group_opts(Host, Group) -> case catch mnesia:dirty_read(sr_group, {Group, Host}) of [#sr_group{opts = Opts}] -> Opts; _ -> error end. set_group_opts(Host, Group, Opts) -> R = #sr_group{group_host = {Group, Host}, opts = Opts}, F = fun () -> mnesia:write(R) end, mnesia:transaction(F). get_user_groups(US, Host) -> case catch mnesia:dirty_read(sr_user, US) of Rs when is_list(Rs) -> [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; _ -> [] end. get_group_explicit_users(Host, Group) -> Read = (catch mnesia:dirty_index_read(sr_user, {Group, Host}, #sr_user.group_host)), case Read of Rs when is_list(Rs) -> [R#sr_user.us || R <- Rs]; _ -> [] end. get_user_displayed_groups(LUser, LServer, GroupsOpts) -> case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of Rs when is_list(Rs) -> [{Group, proplists:get_value(Group, GroupsOpts, [])} || #sr_user{group_host = {Group, H}} <- Rs, H == LServer]; _ -> [] end. is_user_in_group(US, Group, Host) -> case mnesia:dirty_match_object( #sr_user{us = US, group_host = {Group, Host}}) of [] -> false; _ -> true end. add_user_to_group(Host, US, Group) -> R = #sr_user{us = US, group_host = {Group, Host}}, F = fun () -> mnesia:write(R) end, mnesia:transaction(F). remove_user_from_group(Host, US, Group) -> R = #sr_user{us = US, group_host = {Group, Host}}, F = fun () -> mnesia:delete_object(R) end, mnesia:transaction(F). import(LServer, <<"sr_group">>, [Group, SOpts, _TimeStamp]) -> G = #sr_group{group_host = {Group, LServer}, opts = ejabberd_sql:decode_term(SOpts)}, mnesia:dirty_write(G); import(LServer, <<"sr_user">>, [SJID, Group, _TimeStamp]) -> #jid{luser = U, lserver = S} = jid:decode(SJID), User = #sr_user{us = {U, S}, group_host = {Group, LServer}}, mnesia:dirty_write(User). need_transform(#sr_group{group_host = {G, H}}) when is_list(G) orelse is_list(H) -> ?INFO_MSG("Mnesia table 'sr_group' will be converted to binary", []), true; need_transform(#sr_user{us = {U, S}, group_host = {G, H}}) when is_list(U) orelse is_list(S) orelse is_list(G) orelse is_list(H) -> ?INFO_MSG("Mnesia table 'sr_user' will be converted to binary", []), true; need_transform(_) -> false. transform(#sr_group{group_host = {G, H}, opts = Opts} = R) -> R#sr_group{group_host = {iolist_to_binary(G), iolist_to_binary(H)}, opts = mod_shared_roster:opts_to_binary(Opts)}; transform(#sr_user{us = {U, S}, group_host = {G, H}} = R) -> R#sr_user{us = {iolist_to_binary(U), iolist_to_binary(S)}, group_host = {iolist_to_binary(G), iolist_to_binary(H)}}. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/mod_vcard_xupdate.erl0000644000232200023220000001514513225664356020634 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_vcard_xupdate.erl %%% Author : Igor Goryachev %%% Purpose : Add avatar hash in presence on behalf of client (XEP-0153) %%% Created : 9 Mar 2007 by Igor Goryachev %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_vcard_xupdate). -behaviour(gen_mod). %% gen_mod callbacks -export([start/2, stop/1, reload/3]). -export([update_presence/1, vcard_set/1, remove_user/2, user_send_packet/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -define(VCARD_XUPDATE_CACHE, vcard_xupdate_cache). %%==================================================================== %% gen_mod callbacks %%==================================================================== start(Host, Opts) -> init_cache(Host, Opts), ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, update_presence, 100), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 50), ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_set, 90), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50). stop(Host) -> ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, update_presence, 100), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 50), ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_set, 90), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50). reload(Host, NewOpts, _OldOpts) -> init_cache(Host, NewOpts). depends(_Host, _Opts) -> [{mod_vcard, hard}]. %%==================================================================== %% Hooks %%==================================================================== -spec update_presence({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. update_presence({#presence{type = available} = Pres, #{jid := #jid{luser = LUser, lserver = LServer}} = State}) -> Pres1 = case get_xupdate(LUser, LServer) of undefined -> xmpp:remove_subtag(Pres, #vcard_xupdate{}); XUpdate -> xmpp:set_subtag(Pres, XUpdate) end, {Pres1, State}; update_presence(Acc) -> Acc. -spec user_send_packet({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. user_send_packet({#presence{type = available, to = #jid{luser = U, lserver = S, lresource = <<"">>}}, #{jid := #jid{luser = U, lserver = S}}} = Acc) -> %% This is processed by update_presence/2 explicitly, we don't %% want to call this multiple times for performance reasons Acc; user_send_packet(Acc) -> update_presence(Acc). -spec vcard_set(iq()) -> iq(). vcard_set(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) -> ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}), ejabberd_sm:force_update_presence({LUser, LServer}), IQ; vcard_set(Acc) -> Acc. -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}). %%==================================================================== %% Storage %%==================================================================== -spec get_xupdate(binary(), binary()) -> vcard_xupdate() | undefined. get_xupdate(LUser, LServer) -> Result = case use_cache(LServer) of true -> ets_cache:lookup( ?VCARD_XUPDATE_CACHE, {LUser, LServer}, fun() -> db_get_xupdate(LUser, LServer) end); false -> db_get_xupdate(LUser, LServer) end, case Result of {ok, external} -> undefined; {ok, Hash} -> #vcard_xupdate{hash = Hash}; error -> #vcard_xupdate{} end. -spec db_get_xupdate(binary(), binary()) -> {ok, binary() | external} | error. db_get_xupdate(LUser, LServer) -> case mod_vcard:get_vcard(LUser, LServer) of [VCard] -> {ok, compute_hash(VCard)}; _ -> error end. -spec init_cache(binary(), gen_mod:opts()) -> ok. init_cache(Host, Opts) -> case use_cache(Host) of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?VCARD_XUPDATE_CACHE, CacheOpts); false -> ets_cache:delete(?VCARD_XUPDATE_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(binary()) -> boolean(). use_cache(Host) -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)). -spec compute_hash(xmlel()) -> binary() | external. compute_hash(VCard) -> case fxml:get_subtag(VCard, <<"PHOTO">>) of false -> <<>>; Photo -> try xmpp:decode(Photo, ?NS_VCARD, []) of #vcard_photo{binval = <<_, _/binary>> = BinVal} -> str:sha(BinVal); #vcard_photo{extval = <<_, _/binary>>} -> external; _ -> <<>> catch _:{xmpp_codec, _} -> <<>> end end. %%==================================================================== %% Options %%==================================================================== mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [cache_life_time, cache_size, use_cache, cache_missed]. ejabberd-18.01/src/mod_caps_riak.erl0000644000232200023220000000446313225664356017740 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_caps_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_caps_riak). -behaviour(mod_caps). %% API -export([init/2, caps_read/2, caps_write/3, import/3]). -include("mod_caps.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. caps_read(_LServer, Node) -> case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of {ok, #caps_features{features = Features}} -> {ok, Features}; _ -> error end. caps_write(_LServer, Node, Features) -> ejabberd_riak:put(#caps_features{node_pair = Node, features = Features}, caps_features_schema()). import(_LServer, NodePair, [I]) when is_integer(I) -> ejabberd_riak:put( #caps_features{node_pair = NodePair, features = I}, caps_features_schema()); import(_LServer, NodePair, Features) -> ejabberd_riak:put( #caps_features{node_pair = NodePair, features = Features}, caps_features_schema()). %%%=================================================================== %%% Internal functions %%%=================================================================== caps_features_schema() -> {record_info(fields, caps_features), #caps_features{}}. ejabberd-18.01/src/win32_dns.erl0000644000232200023220000001065713225664356016755 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : win32_dns.erl %%% Author : Geoff Cant %%% Purpose : Get name servers in a Windows machine %%% Created : 5 Mar 2009 by Geoff Cant %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(win32_dns). -export([get_nameservers/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -define(IF_KEY, "\\hklm\\system\\CurrentControlSet\\Services\\TcpIp\\Parameters\\Interfaces"). -define(TOP_KEY, "\\hklm\\system\\CurrentControlSet\\Services\\TcpIp\\Parameters"). get_nameservers() -> {_, Config} = pick_config(), IPTs = get_value(["NameServer"], Config), lists:filter(fun(IPTuple) -> is_good_ns(IPTuple) end, IPTs). is_good_ns(Addr) -> element(1, inet_res:nnslookup("a.root-servers.net", in, any, [{Addr,53}], timer:seconds(5) ) ) =:= ok. reg() -> {ok, R} = win32reg:open([read]), R. interfaces(R) -> ok = win32reg:change_key(R, ?IF_KEY), {ok, I} = win32reg:sub_keys(R), I. config_keys(R, Key) -> ok = win32reg:change_key(R, Key), [ {K, case win32reg:value(R, K) of {ok, V} -> try_translate(K, V); _ -> undefined end } || K <- ["Domain", "DhcpDomain", "NameServer", "DhcpNameServer", "SearchList"]]. try_translate(K, V) -> try translate(K, V) of Res -> Res catch A:B -> ?ERROR_MSG("Error '~p' translating Win32 registry~n" "K: ~p~nV: ~p~nError: ~p", [A, K, V, B]), undefined end. translate(NS, V) when NS =:= "NameServer"; NS =:= "DhcpNameServer" -> %% The IPs may be separated by commas ',' or by spaces " " %% The parts of an IP are separated by dots '.' IPsStrings = [string:tokens(IP, ".") || IP <- string:tokens(V, " ,")], [ list_to_tuple([list_to_integer(String) || String <- IpStrings]) || IpStrings <- IPsStrings]; translate(_, V) -> V. interface_configs(R) -> [{If, config_keys(R, ?IF_KEY ++ "\\" ++ If)} || If <- interfaces(R)]. sort_configs(Configs) -> lists:sort(fun ({_, A}, {_, B}) -> ANS = proplists:get_value("NameServer", A), BNS = proplists:get_value("NameServer", B), if ANS =/= undefined, BNS =:= undefined -> false; true -> count_undef(A) < count_undef(B) end end, Configs). count_undef(L) when is_list(L) -> lists:foldl(fun ({_K, undefined}, Acc) -> Acc +1; ({_K, []}, Acc) -> Acc +1; (_, Acc) -> Acc end, 0, L). all_configs() -> R = reg(), TopConfig = config_keys(R, ?TOP_KEY), Configs = [{top, TopConfig} | interface_configs(R)], win32reg:close(R), {TopConfig, Configs}. pick_config() -> {TopConfig, Configs} = all_configs(), NSConfigs = [{If, C} || {If, C} <- Configs, get_value(["DhcpNameServer","NameServer"], C) =/= undefined], case get_value(["DhcpNameServer","NameServer"], TopConfig) of %% No top level nameserver to pick interface with undefined -> hd(sort_configs(NSConfigs)); %% Top level has a nameserver - use this to select an interface. NS -> Cs = [ {If, C} || {If, C} <- Configs, lists:member(NS, [get_value(["NameServer"], C), get_value(["DhcpNameServer"], C)])], hd(sort_configs(Cs)) end. get_value([], _Config) -> undefined; get_value([K|Keys], Config) -> case proplists:get_value(K, Config) of undefined -> get_value(Keys, Config); V -> V end. ejabberd-18.01/src/gen_mod.erl0000644000232200023220000005220013225664356016545 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : gen_mod.erl %%% Author : Alexey Shchepin %%% Purpose : %%% Created : 24 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(gen_mod). -behaviour(ejabberd_config). -behaviour(supervisor). -author('alexey@process-one.net'). -export([init/1, start_link/0, start_child/3, start_child/4, stop_child/1, stop_child/2, config_reloaded/0]). -export([start_module/2, start_module/3, stop_module/2, stop_module_keep_config/2, get_opt/2, get_opt/3, get_opt_host/3, get_opt_hosts/3, opt_type/1, is_equal_opt/4, get_module_opt/3, get_module_opt/4, get_module_opt_host/3, loaded_modules/1, loaded_modules_with_opts/1, get_hosts/2, get_module_proc/2, is_loaded/2, is_loaded_elsewhere/2, start_modules/0, start_modules/1, stop_modules/0, stop_modules/1, db_mod/2, db_mod/3, ram_db_mod/2, ram_db_mod/3, db_type/2, db_type/3, ram_db_type/2, ram_db_type/3]). %% Deprecated functions -export([get_opt/4, get_module_opt/5]). -deprecated([{get_opt, 4}, {get_module_opt, 5}]). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -record(ejabberd_module, {module_host = {undefined, <<"">>} :: {atom(), binary()}, opts = [] :: opts() | '_' | '$2'}). -type opts() :: [{atom(), any()}]. -type db_type() :: atom(). -callback start(binary(), opts()) -> ok | {ok, pid()}. -callback stop(binary()) -> any(). -callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}. -callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. -callback depends(binary(), opts()) -> [{module(), hard | soft}]. -optional_callbacks([reload/3]). -export_type([opts/0]). -export_type([db_type/0]). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. start_link() -> case supervisor:start_link({local, ejabberd_gen_mod_sup}, ?MODULE, []) of {ok, Pid} -> start_modules(), {ok, Pid}; Err -> Err end. init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), ejabberd_hooks:add(host_up, ?MODULE, start_modules, 40), ejabberd_hooks:add(host_down, ?MODULE, stop_modules, 70), ets:new(ejabberd_modules, [named_table, public, {keypos, #ejabberd_module.module_host}, {read_concurrency, true}]), {ok, {{one_for_one, 10, 1}, []}}. -spec start_child(module(), binary() | global, opts()) -> ok | {error, any()}. start_child(Mod, Host, Opts) -> start_child(Mod, Host, Opts, get_module_proc(Host, Mod)). -spec start_child(module(), binary() | global, opts(), atom()) -> ok | {error, any()}. start_child(Mod, Host, Opts, Proc) -> Spec = {Proc, {?GEN_SERVER, start_link, [{local, Proc}, Mod, [Host, Opts], []]}, transient, timer:minutes(1), worker, [Mod]}, supervisor:start_child(ejabberd_gen_mod_sup, Spec). -spec stop_child(module(), binary() | global) -> ok | {error, any()}. stop_child(Mod, Host) -> stop_child(get_module_proc(Host, Mod)). -spec stop_child(atom()) -> ok | {error, any()}. stop_child(Proc) -> supervisor:terminate_child(ejabberd_gen_mod_sup, Proc), supervisor:delete_child(ejabberd_gen_mod_sup, Proc). -spec start_modules() -> any(). %% Start all the modules in all the hosts start_modules() -> lists:foreach( fun(Host) -> start_modules(Host) end, ?MYHOSTS). get_modules_options(Host) -> ejabberd_config:get_option({modules, Host}, []). sort_modules(Host, ModOpts) -> G = digraph:new([acyclic]), lists:foreach( fun({Mod, Opts}) -> digraph:add_vertex(G, Mod, Opts), Deps = try Mod:depends(Host, Opts) catch _:undef -> [] end, lists:foreach( fun({DepMod, Type}) -> case lists:keyfind(DepMod, 1, ModOpts) of false when Type == hard -> ErrTxt = io_lib:format( "failed to load module '~s' " "because it depends on module '~s' " "which is not found in the config", [Mod, DepMod]), ?ERROR_MSG(ErrTxt, []), digraph:del_vertex(G, Mod), maybe_halt_ejabberd(ErrTxt); false when Type == soft -> ?WARNING_MSG("module '~s' is recommended for " "module '~s' but is not found in " "the config", [DepMod, Mod]); {DepMod, DepOpts} -> digraph:add_vertex(G, DepMod, DepOpts), case digraph:add_edge(G, DepMod, Mod) of {error, {bad_edge, Path}} -> ?WARNING_MSG("cyclic dependency detected " "between modules: ~p", [Path]); _ -> ok end end end, Deps) end, ModOpts), Result = [digraph:vertex(G, V) || V <- digraph_utils:topsort(G)], digraph:delete(G), Result. -spec start_modules(binary()) -> ok. start_modules(Host) -> Modules = sort_modules(Host, get_modules_options(Host)), lists:foreach( fun({Module, Opts}) -> start_module(Host, Module, Opts) end, Modules). -spec start_module(binary(), atom()) -> ok | {ok, pid()} | {error, not_found_in_config}. start_module(Host, Module) -> Modules = get_modules_options(Host), case lists:keyfind(Module, 1, Modules) of {_, Opts} -> start_module(Host, Module, Opts); false -> {error, not_found_in_config} end. -spec start_module(binary(), atom(), opts()) -> ok | {ok, pid()}. start_module(Host, Module, Opts) -> start_module(Host, Module, Opts, true). -spec start_module(binary(), atom(), opts(), boolean()) -> ok | {ok, pid()}. start_module(Host, Module, Opts0, NeedValidation) -> ?DEBUG("loading ~s at ~s", [Module, Host]), Opts = if NeedValidation -> validate_opts(Host, Module, Opts0); true -> Opts0 end, store_options(Host, Module, Opts), try case Module:start(Host, Opts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; Err -> erlang:error(Err) end catch Class:Reason -> ets:delete(ejabberd_modules, {Module, Host}), ErrorText = io_lib:format("Problem starting the module ~s for host " "~s ~n options: ~p~n ~p: ~p~n~p", [Module, Host, Opts, Class, Reason, erlang:get_stacktrace()]), ?CRITICAL_MSG(ErrorText, []), maybe_halt_ejabberd(ErrorText), erlang:raise(Class, Reason, erlang:get_stacktrace()) end. -spec reload_modules(binary()) -> ok. reload_modules(Host) -> NewMods = ejabberd_config:get_option({modules, Host}, []), OldMods = ets:select( ejabberd_modules, ets:fun2ms( fun(#ejabberd_module{module_host = {M, H}, opts = O}) when H == Host -> {M, O} end)), lists:foreach( fun({Mod, _Opts}) -> case lists:keymember(Mod, 1, NewMods) of false -> stop_module(Host, Mod); true -> ok end end, OldMods), lists:foreach( fun({Mod, Opts}) -> case lists:keymember(Mod, 1, OldMods) of false -> start_module(Host, Mod, Opts); true -> ok end end, NewMods), lists:foreach( fun({Mod, OldOpts}) -> case lists:keyfind(Mod, 1, NewMods) of {_, NewOpts0} -> case validate_opts(Host, Mod, NewOpts0) of OldOpts -> ok; NewOpts -> reload_module(Host, Mod, NewOpts, OldOpts) end; _ -> ok end end, OldMods). -spec reload_module(binary(), module(), opts(), opts()) -> ok | {ok, pid()}. reload_module(Host, Module, NewOpts, OldOpts) -> case erlang:function_exported(Module, reload, 3) of true -> ?DEBUG("reloading ~s at ~s", [Module, Host]), store_options(Host, Module, NewOpts), try case Module:reload(Host, NewOpts, OldOpts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; Err -> erlang:error(Err) end catch Class:Reason -> StackTrace = erlang:get_stacktrace(), ?CRITICAL_MSG("failed to reload module ~s at ~s:~n" "** Reason = ~p", [Module, Host, {Class, {Reason, StackTrace}}]), erlang:raise(Class, Reason, StackTrace) end; false -> ?WARNING_MSG("module ~s doesn't support reloading " "and will be restarted", [Module]), stop_module(Host, Module), start_module(Host, Module, NewOpts, false) end. -spec store_options(binary(), module(), opts()) -> true. store_options(Host, Module, Opts) -> ets:insert(ejabberd_modules, #ejabberd_module{module_host = {Module, Host}, opts = Opts}). maybe_halt_ejabberd(ErrorText) -> case is_app_running(ejabberd) of false -> ?CRITICAL_MSG("ejabberd initialization was aborted " "because a module start failed.", []), timer:sleep(3000), erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199)); true -> ok end. is_app_running(AppName) -> Timeout = 15000, lists:keymember(AppName, 1, application:which_applications(Timeout)). -spec stop_modules() -> ok. stop_modules() -> lists:foreach( fun(Host) -> stop_modules(Host) end, ?MYHOSTS). -spec stop_modules(binary()) -> ok. stop_modules(Host) -> Modules = get_modules_options(Host), lists:foreach( fun({Module, _Args}) -> stop_module_keep_config(Host, Module) end, Modules). -spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}. stop_module(Host, Module) -> ?DEBUG("stopping ~s at ~s", [Module, Host]), case stop_module_keep_config(Host, Module) of error -> error; ok -> ok end. -spec stop_module_keep_config(binary(), atom()) -> error | ok. stop_module_keep_config(Host, Module) -> case catch Module:stop(Host) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), error; {wait, ProcList} when is_list(ProcList) -> lists:foreach(fun wait_for_process/1, ProcList), ets:delete(ejabberd_modules, {Module, Host}), ok; {wait, Process} -> wait_for_process(Process), ets:delete(ejabberd_modules, {Module, Host}), ok; _ -> ets:delete(ejabberd_modules, {Module, Host}), ok end. wait_for_process(Process) -> MonitorReference = erlang:monitor(process, Process), wait_for_stop(Process, MonitorReference). wait_for_stop(Process, MonitorReference) -> receive {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> catch exit(whereis(Process), kill), wait_for_stop1(MonitorReference) end. wait_for_stop1(MonitorReference) -> receive {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> ok end. -type check_fun() :: fun((any()) -> any()) | {module(), atom()}. -spec get_opt(atom() | {atom(), binary() | global}, opts()) -> any(). get_opt(Opt, Opts) -> get_opt(Opt, Opts, undefined). -spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun() | any()) -> any(). get_opt(Opt, Opts, F) when is_function(F) -> get_opt(Opt, Opts, undefined); get_opt({Opt, Host}, Opts, Default) -> case lists:keyfind(Opt, 1, Opts) of false -> ejabberd_config:get_option({Opt, Host}, Default); {_, Val} -> Val end; get_opt(Opt, Opts, Default) -> case lists:keyfind(Opt, 1, Opts) of false -> Default; {_, Val} -> Val end. -spec get_opt(atom() | {atom(), binary()}, opts(), check_fun(), any()) -> any(). get_opt(Opt, Opts, _, Default) -> get_opt(Opt, Opts, Default). -spec get_module_opt(global | binary(), atom(), atom()) -> any(). get_module_opt(Host, Module, Opt) -> get_module_opt(Host, Module, Opt, undefined). -spec get_module_opt(global | binary(), atom(), atom(), any()) -> any(). get_module_opt(Host, Module, Opt, F) when is_function(F) -> get_module_opt(Host, Module, Opt, undefined); get_module_opt(global, Module, Opt, Default) -> Hosts = (?MYHOSTS), [Value | Values] = lists:map(fun (Host) -> get_module_opt(Host, Module, Opt, Default) end, Hosts), Same_all = lists:all(fun (Other_value) -> Other_value == Value end, Values), case Same_all of true -> Value; false -> Default end; get_module_opt(Host, Module, Opt, Default) -> OptsList = ets:lookup(ejabberd_modules, {Module, Host}), case OptsList of [] -> Default; [#ejabberd_module{opts = Opts} | _] -> get_opt(Opt, Opts, Default) end. -spec get_module_opt(global | binary(), atom(), atom(), check_fun(), any()) -> any(). get_module_opt(Host, Module, Opt, _, Default) -> get_module_opt(Host, Module, Opt, Default). -spec get_module_opt_host(global | binary(), atom(), binary()) -> binary(). get_module_opt_host(Host, Module, Default) -> Val = get_module_opt(Host, Module, host, Default), ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host). -spec get_opt_host(binary(), opts(), binary()) -> binary(). get_opt_host(Host, Opts, Default) -> Val = get_opt(host, Opts, Default), ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host). -spec get_opt_hosts(binary(), opts(), binary()) -> [binary()]. get_opt_hosts(Host, Opts, Default) -> Vals = case get_opt(host, Opts, undefined) of undefined -> case get_opt(hosts, Opts, []) of [] -> [Default]; L -> L end; Val -> [Val] end, [ejabberd_regexp:greplace(V, <<"@HOST@">>, Host) || V <- Vals]. -spec get_validators(binary(), module(), opts()) -> dict:dict() | undef. get_validators(Host, Module, Opts) -> try Module:mod_opt_type('') of L -> SubMods1 = case lists:member(db_type, L) of true -> [db_mod(Host, Opts, Module)]; false -> [] end, SubMods2 = case lists:member(ram_db_type, L) of true -> [ram_db_mod(Host, Opts, Module)]; false -> [] end, lists:foldl( fun(Mod, D) -> try Mod:mod_opt_type('') of Os -> lists:foldl( fun({Opt, SubOpt} = O, Acc) -> SubF = Mod:mod_opt_type(O), F = case Mod:mod_opt_type(Opt) of F1 when is_function(F1) -> F1; _ -> fun(X) -> X end end, dict:append_list( Opt, [F, {SubOpt, [SubF]}], Acc); (O, Acc) -> F = Mod:mod_opt_type(O), dict:store(O, [F], Acc) end, D, Os) catch _:undef -> D end end, dict:new(), [Module|SubMods1 ++ SubMods2]) catch _:undef -> ?WARNING_MSG("module '~s' doesn't export mod_opt_type/1", [Module]), undef end. -spec validate_opts(binary(), module(), opts()) -> opts(). validate_opts(Host, Module, Opts) -> case get_validators(Host, Module, Opts) of undef -> Opts; Validators -> validate_opts(Host, Module, Opts, dict:to_list(Validators)) end. validate_opts(Host, Module, Opts, Validators) when is_list(Opts) -> lists:flatmap( fun({Opt, Val}) when is_atom(Opt) -> case lists:keyfind(Opt, 1, Validators) of {_, L} -> case lists:partition(fun is_function/1, L) of {[VFun|_], []} -> validate_opt(Module, Opt, Val, VFun); {[VFun|_], SubValidators} -> try validate_opts(Host, Module, Val, SubValidators) of SubOpts -> validate_opt(Module, Opt, SubOpts, VFun) catch _:bad_option -> ?ERROR_MSG("ignoring invalid value '~p' for " "option '~s' of module '~s'", [Val, Opt, Module]), [] end end; false -> case Validators of [] -> ?ERROR_MSG("unknown option '~s' for module '~s' " "will be likely ignored because the " "module doesn't have any options", [Opt, Module]); _ -> ?ERROR_MSG("unknown option '~s' for module '~s' will be" " likely ignored, available options are: ~s", [Opt, Module, misc:join_atoms([K || {K, _} <- Validators], <<", ">>)]) end, [{Opt, Val}] end; (_) -> erlang:error(bad_option) end, Opts); validate_opts(_, _, _, _) -> erlang:error(bad_option). -spec validate_opt(module(), atom(), any(), [{atom(), check_fun(), any()}]) -> [{atom(), any()}]. validate_opt(Module, Opt, Val, VFun) -> try VFun(Val) of NewVal -> [{Opt, NewVal}] catch {invalid_syntax, Error} -> ?ERROR_MSG("ignoring invalid value '~p' for " "option '~s' of module '~s': ~s", [Val, Opt, Module, Error]), []; _:_ -> ?ERROR_MSG("ignoring invalid value '~p' for " "option '~s' of module '~s'", [Val, Opt, Module]), [] end. -spec db_type(binary() | global, module()) -> db_type(); (opts(), module()) -> db_type(). db_type(Opts, Module) when is_list(Opts) -> db_type(global, Opts, Module); db_type(Host, Module) when is_atom(Module) -> case get_module_opt(Host, Module, db_type) of undefined -> ejabberd_config:default_db(Host, Module); Type -> Type end. -spec db_type(binary() | global, opts(), module()) -> db_type(). db_type(Host, Opts, Module) -> case get_opt(db_type, Opts) of undefined -> ejabberd_config:default_db(Host, Module); Type -> Type end. -spec db_mod(binary() | global | db_type(), module()) -> module(). db_mod(Type, Module) when is_atom(Type) -> list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Type)); db_mod(Host, Module) when is_binary(Host) orelse Host == global -> db_mod(db_type(Host, Module), Module). -spec db_mod(binary() | global, opts(), module()) -> module(). db_mod(Host, Opts, Module) when is_list(Opts) -> db_mod(db_type(Host, Opts, Module), Module). -spec ram_db_type(binary() | global, module()) -> db_type(); (opts(), module()) -> db_type(). ram_db_type(Opts, Module) when is_list(Opts) -> ram_db_type(global, Opts, Module); ram_db_type(Host, Module) when is_atom(Module) -> case get_module_opt(Host, Module, ram_db_type) of undefined -> ejabberd_config:default_ram_db(Host, Module); Type -> Type end. -spec ram_db_type(binary() | global, opts(), module()) -> db_type(). ram_db_type(Host, Opts, Module) -> case get_opt(ram_db_type, Opts) of undefined -> ejabberd_config:default_ram_db(Host, Module); Type -> Type end. -spec ram_db_mod(binary() | global | db_type(), module()) -> module(). ram_db_mod(Type, Module) when is_atom(Type), Type /= global -> list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Type)); ram_db_mod(Host, Module) when is_binary(Host) orelse Host == global -> ram_db_mod(ram_db_type(Host, Module), Module). -spec ram_db_mod(binary() | global, opts(), module()) -> module(). ram_db_mod(Host, Opts, Module) when is_list(Opts) -> ram_db_mod(ram_db_type(Host, Opts, Module), Module). -spec loaded_modules(binary()) -> [atom()]. loaded_modules(Host) -> ets:select(ejabberd_modules, [{#ejabberd_module{_ = '_', module_host = {'$1', Host}}, [], ['$1']}]). -spec loaded_modules_with_opts(binary()) -> [{atom(), opts()}]. loaded_modules_with_opts(Host) -> ets:select(ejabberd_modules, [{#ejabberd_module{_ = '_', module_host = {'$1', Host}, opts = '$2'}, [], [{{'$1', '$2'}}]}]). -spec get_hosts(opts(), binary()) -> [binary()]. get_hosts(Opts, Prefix) -> case get_opt(hosts, Opts) of undefined -> case get_opt(host, Opts) of undefined -> [<> || Host <- ?MYHOSTS]; Host -> [Host] end; Hosts -> Hosts end. -spec get_module_proc(binary(), {frontend, atom()} | atom()) -> atom(). get_module_proc(Host, {frontend, Base}) -> get_module_proc(<<"frontend_", Host/binary>>, Base); get_module_proc(Host, Base) -> binary_to_atom( <<(erlang:atom_to_binary(Base, latin1))/binary, "_", Host/binary>>, latin1). -spec is_loaded(binary(), atom()) -> boolean(). is_loaded(Host, Module) -> ets:member(ejabberd_modules, {Module, Host}). -spec is_loaded_elsewhere(binary(), atom()) -> boolean(). is_loaded_elsewhere(Host, Module) -> ets:select_count( ejabberd_modules, ets:fun2ms( fun(#ejabberd_module{module_host = {Mod, H}}) -> (Mod == Module) and (H /= Host) end)) /= 0. -spec config_reloaded() -> ok. config_reloaded() -> lists:foreach( fun(Host) -> reload_modules(Host) end, ?MYHOSTS). -spec is_equal_opt(atom(), opts(), opts(), any()) -> true | {false, any(), any()}. is_equal_opt(Opt, NewOpts, OldOpts, Default) -> NewVal = get_opt(Opt, NewOpts, Default), OldVal = get_opt(Opt, OldOpts, Default), if NewVal /= OldVal -> {false, NewVal, OldVal}; true -> true end. -spec opt_type(modules) -> fun(([{atom(), list()}]) -> [{atom(), list()}]); (atom()) -> [atom()]. opt_type(modules) -> fun(Mods) -> lists:map( fun({M, A}) when is_atom(M), is_list(A) -> {M, A} end, Mods) end; opt_type(_) -> [modules]. ejabberd-18.01/src/mod_stats.erl0000644000232200023220000001702013225664356017133 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_stats.erl %%% Author : Alexey Shchepin %%% Purpose : Basic statistics. %%% Created : 11 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_stats). -author('alexey@process-one.net'). -protocol({xep, 39, '0.6.0'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_STATS, ?MODULE, process_iq, IQDisc). stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_STATS). reload(Host, NewOpts, _OldOpts) -> start(Host, NewOpts). depends(_Host, _Opts) -> []. process_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_iq(#iq{type = get, to = To, lang = Lang, sub_els = [#stats{} = Stats]} = IQ) -> Node = str:tokens(Stats#stats.node, <<"/">>), Names = [Name || #stat{name = Name} <- Stats#stats.list], case get_local_stats(To#jid.server, Node, Names, Lang) of {result, List} -> xmpp:make_iq_result(IQ, Stats#stats{list = List}); {error, Error} -> xmpp:make_error(IQ, Error) end. -define(STAT(Name), #stat{name = Name}). get_local_stats(_Server, [], [], _Lang) -> {result, [?STAT(<<"users/online">>), ?STAT(<<"users/total">>), ?STAT(<<"users/all-hosts/online">>), ?STAT(<<"users/all-hosts/total">>)]}; get_local_stats(Server, [], Names, _Lang) -> {result, lists:map(fun (Name) -> get_local_stat(Server, [], Name) end, Names)}; get_local_stats(_Server, [<<"running nodes">>, _], [], _Lang) -> {result, [?STAT(<<"time/uptime">>), ?STAT(<<"time/cputime">>), ?STAT(<<"users/online">>), ?STAT(<<"transactions/committed">>), ?STAT(<<"transactions/aborted">>), ?STAT(<<"transactions/restarted">>), ?STAT(<<"transactions/logged">>)]}; get_local_stats(_Server, [<<"running nodes">>, ENode], Names, Lang) -> case search_running_node(ENode) of false -> Txt = <<"No running node found">>, {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> {result, lists:map(fun (Name) -> get_node_stat(Node, Name) end, Names)} end; get_local_stats(_Server, _, _, Lang) -> Txt = <<"No statistics found for this item">>, {error, xmpp:err_feature_not_implemented(Txt, Lang)}. -define(STATVAL(Val, Unit), #stat{name = Name, units = Unit, value = Val}). -define(STATERR(Code, Desc), #stat{name = Name, error = #stat_error{code = Code, reason = Desc}}). get_local_stat(Server, [], Name) when Name == <<"users/online">> -> case catch ejabberd_sm:get_vh_session_list(Server) of {'EXIT', _Reason} -> ?STATERR(500, <<"Internal Server Error">>); Users -> ?STATVAL((integer_to_binary(length(Users))), <<"users">>) end; get_local_stat(Server, [], Name) when Name == <<"users/total">> -> case catch ejabberd_auth:count_users(Server) of {'EXIT', _Reason} -> ?STATERR(500, <<"Internal Server Error">>); NUsers -> ?STATVAL((integer_to_binary(NUsers)), <<"users">>) end; get_local_stat(_Server, [], Name) when Name == <<"users/all-hosts/online">> -> Users = ejabberd_sm:connected_users_number(), ?STATVAL((integer_to_binary(Users)), <<"users">>); get_local_stat(_Server, [], Name) when Name == <<"users/all-hosts/total">> -> NumUsers = lists:foldl(fun (Host, Total) -> ejabberd_auth:count_users(Host) + Total end, 0, ?MYHOSTS), ?STATVAL((integer_to_binary(NumUsers)), <<"users">>); get_local_stat(_Server, _, Name) -> ?STATERR(404, <<"Not Found">>). get_node_stat(Node, Name) when Name == <<"time/uptime">> -> case catch ejabberd_cluster:call(Node, erlang, statistics, [wall_clock]) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); CPUTime -> ?STATVAL(str:format("~.3f", [element(1, CPUTime) / 1000]), <<"seconds">>) end; get_node_stat(Node, Name) when Name == <<"time/cputime">> -> case catch ejabberd_cluster:call(Node, erlang, statistics, [runtime]) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); RunTime -> ?STATVAL(str:format("~.3f", [element(1, RunTime) / 1000]), <<"seconds">>) end; get_node_stat(Node, Name) when Name == <<"users/online">> -> case catch ejabberd_cluster:call(Node, ejabberd_sm, dirty_get_my_sessions_list, []) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); Users -> ?STATVAL((integer_to_binary(length(Users))), <<"users">>) end; get_node_stat(Node, Name) when Name == <<"transactions/committed">> -> case catch ejabberd_cluster:call(Node, mnesia, system_info, [transaction_commits]) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((integer_to_binary(Transactions)), <<"transactions">>) end; get_node_stat(Node, Name) when Name == <<"transactions/aborted">> -> case catch ejabberd_cluster:call(Node, mnesia, system_info, [transaction_failures]) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((integer_to_binary(Transactions)), <<"transactions">>) end; get_node_stat(Node, Name) when Name == <<"transactions/restarted">> -> case catch ejabberd_cluster:call(Node, mnesia, system_info, [transaction_restarts]) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((integer_to_binary(Transactions)), <<"transactions">>) end; get_node_stat(Node, Name) when Name == <<"transactions/logged">> -> case catch ejabberd_cluster:call(Node, mnesia, system_info, [transaction_log_writes]) of {badrpc, _Reason} -> ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((integer_to_binary(Transactions)), <<"transactions">>) end; get_node_stat(_, Name) -> ?STATERR(404, <<"Not Found">>). search_running_node(SNode) -> search_running_node(SNode, mnesia:system_info(running_db_nodes)). search_running_node(_, []) -> false; search_running_node(SNode, [Node | Nodes]) -> case iolist_to_binary(atom_to_list(Node)) of SNode -> Node; _ -> search_running_node(SNode, Nodes) end. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(_) -> [iqdisc]. ejabberd-18.01/src/mod_echo.erl0000644000232200023220000002022613225664356016715 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_echo.erl %%% Author : Alexey Shchepin %%% Purpose : Simple ejabberd module. %%% Created : 15 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_echo). -author('alexey@process-one.net'). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, do_client_version/3]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -record(state, {hosts = [] :: [binary()]}). %%==================================================================== %% gen_mod API %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). reload(Host, NewOpts, OldOpts) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). depends(_Host, _Opts) -> []. mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun(L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(_) -> [host, hosts]. %%==================================================================== %% gen_server callbacks %%==================================================================== %%-------------------------------------------------------------------- %% Function: init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- init([Host, Opts]) -> process_flag(trap_exit, true), Hosts = gen_mod:get_opt_hosts(Host, Opts, <<"echo.@HOST@">>), lists:foreach( fun(H) -> ejabberd_router:register_route(H, Host) end, Hosts), {ok, #state{hosts = Hosts}}. %%-------------------------------------------------------------------- %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(stop, _From, State) -> {stop, normal, ok, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({reload, Host, NewOpts, OldOpts}, State) -> NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts, <<"echo.@HOST@">>), OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts, <<"echo.@HOST@">>), lists:foreach( fun(H) -> ejabberd_router:unregister_route(H) end, OldMyHosts -- NewMyHosts), lists:foreach( fun(H) -> ejabberd_router:register_route(H, Host) end, NewMyHosts -- OldMyHosts), {noreply, State#state{hosts = NewMyHosts}}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({route, Packet}, State) -> From = xmpp:get_from(Packet), To = xmpp:get_to(Packet), Packet2 = case From#jid.user of <<"">> -> Lang = xmpp:get_lang(Packet), Txt = <<"User part of JID in 'from' is empty">>, xmpp:make_error( Packet, xmpp:err_bad_request(Txt, Lang)); _ -> xmpp:set_from_to(Packet, To, From) end, do_client_version(disabled, To, From), ejabberd_router:route(Packet2), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- terminate(_Reason, State) -> lists:foreach(fun ejabberd_router:unregister_route/1, State#state.hosts). %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %% Example of routing XMPP packets using Erlang's message passing %%-------------------------------------------------------------------- %% To enable this educational example, edit the function handle_info: %% replace the argument 'disabled' with 'enabled' in the call to the %% function do_client_version. %% ejabberd provides a method to receive XMPP packets using Erlang's %% message passing mechanism. %% %% The packets received by ejabberd are sent %% to the local destination process by sending an Erlang message. %% This means that you can receive XMPP stanzas in an Erlang process %% using Erlang's Receive, as long as this process is registered in %% ejabberd as the process which handles the destination JID. %% %% This example function is called when a client queries the echo service. %% This function then sends a query to the client, and waits 5 seconds to %% receive an answer. The answer will only be accepted if it was sent %% using exactly the same JID. We add a (mostly) random resource to %% try to guarantee that the received response matches the request sent. %% Finally, the received response is printed in the ejabberd log file. %% THIS IS **NOT** HOW TO WRITE ejabberd CODE. THIS CODE IS RETARDED. do_client_version(disabled, _From, _To) -> ok; do_client_version(enabled, From, To) -> Random_resource = randoms:get_string(), From2 = From#jid{resource = Random_resource, lresource = Random_resource}, ID = randoms:get_string(), Packet = #iq{from = From2, to = To, type = get, id = randoms:get_string(), sub_els = [#version{}]}, ejabberd_router:route(Packet), receive {route, #iq{to = To, from = From2, id = ID, type = result, sub_els = [#version{} = V]}} -> ?INFO_MSG("Version of the client ~s:~n~s", [jid:encode(To), xmpp:pp(V)]) after 5000 -> % Timeout in miliseconds: 5 seconds [] end. ejabberd-18.01/src/gen_iq_handler.erl0000644000232200023220000001625513225664356020106 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : gen_iq_handler.erl %%% Author : Alexey Shchepin %%% Purpose : IQ handler support %%% Created : 22 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(gen_iq_handler). -author('alexey@process-one.net'). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. -behaviour(?GEN_SERVER). -behaviour(ejabberd_config). %% API -export([start_link/3, add_iq_handler/6, remove_iq_handler/3, stop_iq_handler/3, handle/5, process_iq/4, check_type/1, transform_module_options/1, opt_type/1, iqdisc/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -record(state, {host, module, function}). -type component() :: ejabberd_sm | ejabberd_local. -type type() :: no_queue | one_queue | pos_integer() | parallel. -type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel. -export_type([opts/0, type/0]). %%==================================================================== %% API %%==================================================================== %%-------------------------------------------------------------------- %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- start_link(Host, Module, Function) -> ?GEN_SERVER:start_link(?MODULE, [Host, Module, Function], []). -spec add_iq_handler(module(), binary(), binary(), module(), atom(), type()) -> ok. add_iq_handler(Component, Host, NS, Module, Function, Type) -> case Type of no_queue -> Component:register_iq_handler(Host, NS, Module, Function, no_queue); one_queue -> {ok, Pid} = supervisor:start_child(ejabberd_iq_sup, [Host, Module, Function]), Component:register_iq_handler(Host, NS, Module, Function, {one_queue, Pid}); N when is_integer(N) -> Pids = lists:map(fun (_) -> {ok, Pid} = supervisor:start_child(ejabberd_iq_sup, [Host, Module, Function]), Pid end, lists:seq(1, N)), Component:register_iq_handler(Host, NS, Module, Function, {queues, Pids}); parallel -> Component:register_iq_handler(Host, NS, Module, Function, parallel) end. -spec remove_iq_handler(component(), binary(), binary()) -> ok. remove_iq_handler(Component, Host, NS) -> Component:unregister_iq_handler(Host, NS). -spec stop_iq_handler(atom(), atom(), [pid()]) -> any(). stop_iq_handler(_Module, _Function, Opts) -> case Opts of {one_queue, Pid} -> ?GEN_SERVER:call(Pid, stop); {queues, Pids} -> lists:foreach(fun (Pid) -> catch ?GEN_SERVER:call(Pid, stop) end, Pids); _ -> ok end. -spec handle(binary(), atom(), atom(), opts(), iq()) -> any(). handle(Host, Module, Function, Opts, IQ) -> case Opts of no_queue -> process_iq(Host, Module, Function, IQ); {one_queue, Pid} -> Pid ! {process_iq, IQ}; {queues, Pids} -> Pid = lists:nth(erlang:phash(p1_time_compat:unique_integer(), length(Pids)), Pids), Pid ! {process_iq, IQ}; parallel -> spawn(?MODULE, process_iq, [Host, Module, Function, IQ]); _ -> todo end. -spec process_iq(binary(), atom(), atom(), iq()) -> any(). process_iq(_Host, Module, Function, IQ) -> try ResIQ = case erlang:function_exported(Module, Function, 1) of true -> process_iq(Module, Function, IQ); false -> From = xmpp:get_from(IQ), To = xmpp:get_to(IQ), process_iq(Module, Function, From, To, jlib:iq_query_info(xmpp:encode(IQ))) end, if ResIQ /= ignore -> ejabberd_router:route(ResIQ); true -> ok end catch E:R -> ?ERROR_MSG("failed to process iq:~n~s~nReason = ~p", [xmpp:pp(IQ), {E, {R, erlang:get_stacktrace()}}]), Txt = <<"Module failed to handle the query">>, Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang), ejabberd_router:route_error(IQ, Err) end. -spec process_iq(module(), atom(), iq()) -> ignore | iq(). process_iq(Module, Function, #iq{lang = Lang, sub_els = [El]} = IQ) -> try Pkt = case erlang:function_exported(Module, decode_iq_subel, 1) of true -> Module:decode_iq_subel(El); false -> xmpp:decode(El) end, Module:Function(IQ#iq{sub_els = [Pkt]}) catch error:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end. -spec process_iq(module(), atom(), jid(), jid(), term()) -> iq(). process_iq(Module, Function, From, To, IQ) -> case Module:Function(From, To, IQ) of ignore -> ignore; ResIQ -> xmpp:set_from_to( xmpp:decode(jlib:iq_to_xml(ResIQ), ?NS_CLIENT, [ignore_els]), To, From) end. -spec check_type(type()) -> type(). check_type(no_queue) -> no_queue; check_type(one_queue) -> one_queue; check_type(N) when is_integer(N), N>0 -> N; check_type(parallel) -> parallel. iqdisc(Host) -> ejabberd_config:get_option({iqdisc, Host}, no_queue). -spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}]. transform_module_options(Opts) -> lists:map( fun({iqdisc, {queues, N}}) -> {iqdisc, N}; (Opt) -> Opt end, Opts). -spec opt_type(iqdisc) -> fun((type()) -> type()); (atom()) -> [atom()]. opt_type(iqdisc) -> fun check_type/1; opt_type(_) -> [iqdisc]. %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Module, Function]) -> {ok, #state{host = Host, module = Module, function = Function}}. handle_call(stop, _From, State) -> Reply = ok, {stop, normal, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({process_iq, IQ}, #state{host = Host, module = Module, function = Function} = State) -> process_iq(Host, Module, Function, IQ), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- ejabberd-18.01/src/node_mb_sql.erl0000644000232200023220000001245213225664356017424 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_mb_sql.erl %%% Author : Holger Weiss %%% Purpose : PEP microblogging (XEP-0277) plugin with SQL backend %%% Created : 6 Sep 2016 by Holger Weiss %%% %%% %%% ejabberd, Copyright (C) 2016-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_mb_sql). -behaviour(gen_pubsub_node). -author('holger@zedat.fu-berlin.de'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, get_entity_subscriptions_for_send_last/2, get_last_items/3]). init(Host, ServerHost, Opts) -> node_pep_sql:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_pep_sql:terminate(Host, ServerHost), ok. options() -> [{sql, true}, {rsm, true} | node_mb:options()]. features() -> [<<"rsm">> | node_mb:features()]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_pep_sql:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_pep_sql:create_node(Nidx, Owner). delete_node(Removed) -> node_pep_sql:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_pep_sql:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_pep_sql:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_pep_sql:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_pep_sql:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_pep_sql:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_pep_sql:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_pep_sql:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_pep_sql:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_pep_sql:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_pep_sql:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_pep_sql:get_entity_subscriptions(Host, Owner). get_entity_subscriptions_for_send_last(Host, Owner) -> node_pep_sql:get_entity_subscriptions_for_send_last(Host, Owner). get_node_subscriptions(Nidx) -> node_pep_sql:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_pep_sql:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_pep_sql:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_pep_sql:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_pep_sql:get_states(Nidx). get_state(Nidx, JID) -> node_pep_sql:get_state(Nidx, JID). set_state(State) -> node_pep_sql:set_state(State). get_items(Nidx, From, RSM) -> node_pep_sql:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_pep_sql:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, JID, Count) -> node_pep_sql:get_last_items(Nidx, JID, Count). get_item(Nidx, ItemId) -> node_pep_sql:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_pep_sql:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_pep_sql:set_item(Item). get_item_name(Host, Node, Id) -> node_pep_sql:get_item_name(Host, Node, Id). node_to_path(Node) -> node_pep_sql:node_to_path(Node). path_to_node(Path) -> node_pep_sql:path_to_node(Path). ejabberd-18.01/src/ejabberd_redis_sup.erl0000644000232200023220000001254713225664356020762 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 6 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_redis_sup). -behaviour(supervisor). -behaviour(ejabberd_config). %% API -export([start_link/0, get_pool_size/0, host_up/1, config_reloaded/0, opt_type/1]). %% Supervisor callbacks -export([init/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -define(DEFAULT_POOL_SIZE, 10). %%%=================================================================== %%% API functions %%%=================================================================== start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). host_up(Host) -> case is_redis_configured(Host) of true -> ejabberd:start_app(eredis), lists:foreach( fun(Spec) -> supervisor:start_child(?MODULE, Spec) end, get_specs()); false -> ok end. config_reloaded() -> case is_redis_configured() of true -> ejabberd:start_app(eredis), lists:foreach( fun(Spec) -> supervisor:start_child(?MODULE, Spec) end, get_specs()), PoolSize = get_pool_size(), lists:foreach( fun({Id, _, _, _}) when Id > PoolSize -> supervisor:terminate_child(?MODULE, Id), supervisor:delete_child(?MODULE, Id); (_) -> ok end, supervisor:which_children(?MODULE)); false -> lists:foreach( fun({Id, _, _, _}) -> supervisor:terminate_child(?MODULE, Id), supervisor:delete_child(?MODULE, Id) end, supervisor:which_children(?MODULE)) end. %%%=================================================================== %%% Supervisor callbacks %%%=================================================================== init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20), ejabberd_hooks:add(host_up, ?MODULE, host_up, 20), Specs = case is_redis_configured() of true -> ejabberd:start_app(eredis), get_specs(); false -> [] end, {ok, {{one_for_one, 500, 1}, Specs}}. %%%=================================================================== %%% Internal functions %%%=================================================================== is_redis_configured() -> lists:any(fun is_redis_configured/1, ?MYHOSTS). is_redis_configured(Host) -> ServerConfigured = ejabberd_config:has_option({redis_server, Host}), PortConfigured = ejabberd_config:has_option({redis_port, Host}), DBConfigured = ejabberd_config:has_option({redis_db, Host}), PassConfigured = ejabberd_config:has_option({redis_password, Host}), PoolSize = ejabberd_config:has_option({redis_pool_size, Host}), ConnTimeoutConfigured = ejabberd_config:has_option( {redis_connect_timeout, Host}), SMConfigured = ejabberd_config:get_option({sm_db_type, Host}) == redis, RouterConfigured = ejabberd_config:get_option({router_db_type, Host}) == redis, ServerConfigured or PortConfigured or DBConfigured or PassConfigured or PoolSize or ConnTimeoutConfigured or SMConfigured or RouterConfigured. get_specs() -> lists:map( fun(I) -> {I, {ejabberd_redis, start_link, [I]}, transient, 2000, worker, [?MODULE]} end, lists:seq(1, get_pool_size())). get_pool_size() -> ejabberd_config:get_option(redis_pool_size, ?DEFAULT_POOL_SIZE) + 1. iolist_to_list(IOList) -> binary_to_list(iolist_to_binary(IOList)). -spec opt_type(redis_connect_timeout) -> fun((pos_integer()) -> pos_integer()); (redis_db) -> fun((non_neg_integer()) -> non_neg_integer()); (redis_password) -> fun((binary()) -> binary()); (redis_port) -> fun((0..65535) -> 0..65535); (redis_server) -> fun((binary()) -> binary()); (redis_pool_size) -> fun((pos_integer()) -> pos_integer()); (redis_queue_type) -> fun((ram | file) -> ram | file); (atom()) -> [atom()]. opt_type(redis_connect_timeout) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(redis_db) -> fun (I) when is_integer(I), I >= 0 -> I end; opt_type(redis_password) -> fun iolist_to_list/1; opt_type(redis_port) -> fun (P) when is_integer(P), P > 0, P < 65536 -> P end; opt_type(redis_server) -> fun iolist_to_list/1; opt_type(redis_pool_size) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(redis_queue_type) -> fun(ram) -> ram; (file) -> file end; opt_type(_) -> [redis_connect_timeout, redis_db, redis_password, redis_port, redis_pool_size, redis_server, redis_queue_type]. ejabberd-18.01/src/ejabberd_oauth_sql.erl0000644000232200023220000000473413225664356020763 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_oauth_sql.erl %%% Author : Alexey Shchepin %%% Purpose : OAUTH2 SQL backend %%% Created : 27 Jul 2016 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License %%% along with this program; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% %%%------------------------------------------------------------------- -module(ejabberd_oauth_sql). -behaviour(ejabberd_oauth). -compile([{parse_transform, ejabberd_sql_pt}]). -export([init/0, store/1, lookup/1, clean/1]). -include("ejabberd_oauth.hrl"). -include("ejabberd.hrl"). -include("ejabberd_sql_pt.hrl"). -include("jid.hrl"). -include("logger.hrl"). init() -> ok. store(R) -> Token = R#oauth_token.token, {User, Server} = R#oauth_token.us, SJID = jid:encode({User, Server, <<"">>}), Scope = str:join(R#oauth_token.scope, <<" ">>), Expire = R#oauth_token.expire, case ?SQL_UPSERT( ?MYNAME, "oauth_token", ["!token=%(Token)s", "jid=%(SJID)s", "scope=%(Scope)s", "expire=%(Expire)d"]) of ok -> ok; _ -> {error, db_failure} end. lookup(Token) -> case ejabberd_sql:sql_query( ?MYNAME, ?SQL("select @(jid)s, @(scope)s, @(expire)d" " from oauth_token where token=%(Token)s")) of {selected, [{SJID, Scope, Expire}]} -> JID = jid:decode(SJID), US = {JID#jid.luser, JID#jid.lserver}, {ok, #oauth_token{token = Token, us = US, scope = str:tokens(Scope, <<" ">>), expire = Expire}}; _ -> error end. clean(TS) -> ejabberd_sql:sql_query( ?MYNAME, ?SQL("delete from oauth_token where expire < %(TS)d")). ejabberd-18.01/src/mod_private.erl0000644000232200023220000002117713225664356017457 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_private.erl %%% Author : Alexey Shchepin %%% Purpose : Support for private storage. %%% Created : 16 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_private). -author('alexey@process-one.net'). -protocol({xep, 49, '1.2'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_sm_iq/1, import_info/0, remove_user/2, get_data/2, get_data/3, export/1, import/5, import_start/2, mod_opt_type/1, set_data/3, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_private.hrl"). -define(PRIVATE_CACHE, private_cache). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), binary(), [binary()]) -> ok. -callback set_data(binary(), binary(), [{binary(), xmlel()}]) -> ok | {error, any()}. -callback get_data(binary(), binary(), binary()) -> {ok, xmlel()} | error | {error, any()}. -callback get_all_data(binary(), binary()) -> {ok, [xmlel()]} | error | {error, any()}. -callback del_data(binary(), binary()) -> ok | {error, any()}. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq, IQDisc). stop(Host) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, init_cache(NewMod, Host, NewOpts), case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq, IQDisc); true -> ok end. -spec process_sm_iq(iq()) -> iq(). process_sm_iq(#iq{type = Type, lang = Lang, from = #jid{luser = LUser, lserver = LServer}, to = #jid{luser = LUser, lserver = LServer}, sub_els = [#private{xml_els = Els0}]} = IQ) -> case filter_xmlels(Els0) of [] -> Txt = <<"No private data found in this query">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); Data when Type == set -> case set_data(LUser, LServer, Data) of ok -> xmpp:make_iq_result(IQ); {error, _} -> Txt = <<"Database failure">>, Err = xmpp:err_internal_server_error(Txt, Lang), xmpp:make_error(IQ, Err) end; Data when Type == get -> case get_data(LUser, LServer, Data) of {error, _} -> Txt = <<"Database failure">>, Err = xmpp:err_internal_server_error(Txt, Lang), xmpp:make_error(IQ, Err); Els -> xmpp:make_iq_result(IQ, #private{xml_els = Els}) end end; process_sm_iq(#iq{lang = Lang} = IQ) -> Txt = <<"Query to another users is forbidden">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)). -spec filter_xmlels([xmlel()]) -> [{binary(), xmlel()}]. filter_xmlels(Els) -> lists:flatmap( fun(#xmlel{} = El) -> case fxml:get_tag_attr_s(<<"xmlns">>, El) of <<"">> -> []; NS -> [{NS, El}] end end, Els). -spec set_data(binary(), binary(), [{binary(), xmlel()}]) -> ok | {error, _}. set_data(LUser, LServer, Data) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:set_data(LUser, LServer, Data) of ok -> delete_cache(Mod, LUser, LServer, Data); {error, _} = Err -> Err end. -spec get_data(binary(), binary(), [{binary(), xmlel()}]) -> [xmlel()] | {error, _}. get_data(LUser, LServer, Data) -> Mod = gen_mod:db_mod(LServer, ?MODULE), lists:foldr( fun(_, {error, _} = Err) -> Err; ({NS, El}, Els) -> Res = case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?PRIVATE_CACHE, {LUser, LServer, NS}, fun() -> Mod:get_data(LUser, LServer, NS) end); false -> Mod:get_data(LUser, LServer, NS) end, case Res of {ok, StorageEl} -> [StorageEl|Els]; error -> [El|Els]; {error, _} = Err -> Err end end, [], Data). -spec get_data(binary(), binary()) -> [xmlel()] | {error, _}. get_data(LUser, LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:get_all_data(LUser, LServer) of {ok, Els} -> Els; error -> []; {error, _} = Err -> Err end. -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(Server, ?MODULE), Data = case use_cache(Mod, LServer) of true -> case Mod:get_all_data(LUser, LServer) of {ok, Els} -> filter_xmlels(Els); _ -> [] end; false -> [] end, Mod:del_data(LUser, LServer), delete_cache(Mod, LUser, LServer, Data). -spec delete_cache(module(), binary(), binary(), [{binary(), xmlel()}]) -> ok. delete_cache(Mod, LUser, LServer, Data) -> case use_cache(Mod, LServer) of true -> Nodes = cache_nodes(Mod, LServer), lists:foreach( fun({NS, _}) -> ets_cache:delete(?PRIVATE_CACHE, {LUser, LServer, NS}, Nodes) end, Data); false -> ok end. -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> case use_cache(Mod, Host) of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?PRIVATE_CACHE, CacheOpts); false -> ets_cache:delete(?PRIVATE_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(Host); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. import_info() -> [{<<"private_storage">>, 4}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import(LServer, {sql, _}, DBType, Tab, L) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, Tab, L). depends(_Host, _Opts) -> []. mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [db_type, iqdisc, cache_life_time, cache_size, use_cache, cache_missed]. ejabberd-18.01/src/mod_proxy65_service.erl0000644000232200023220000002674213225664356021064 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_proxy65_service.erl %%% Author : Evgeniy Khramtsov %%% Purpose : SOCKS5 Bytestreams XMPP service. %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_proxy65_service). -author('xram@jabber.ru'). -behaviour(gen_server). %% gen_server callbacks. -export([init/1, handle_info/2, handle_call/3, handle_cast/2, terminate/2, code_change/3]). -export([start_link/2, reload/3, add_listener/2, process_disco_info/1, process_disco_items/1, process_vcard/1, process_bytestreams/1, transform_module_options/1, delete_listener/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("translate.hrl"). -define(PROCNAME, ejabberd_mod_proxy65_service). -record(state, {myhosts = [] :: [binary()]}). %%%------------------------ %%% gen_server callbacks %%%------------------------ start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). reload(Host, NewOpts, OldOpts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). init([Host, Opts]) -> process_flag(trap_exit, true), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"proxy.@HOST@">>), lists:foreach( fun(MyHost) -> gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO, ?MODULE, process_disco_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS, ?MODULE, process_disco_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_BYTESTREAMS, ?MODULE, process_bytestreams, IQDisc), ejabberd_router:register_route(MyHost, Host) end, MyHosts), {ok, #state{myhosts = MyHosts}}. terminate(_Reason, #state{myhosts = MyHosts}) -> lists:foreach( fun(MyHost) -> ejabberd_router:unregister_route(MyHost), unregister_handlers(MyHost) end, MyHosts). handle_info({route, #iq{} = Packet}, State) -> ejabberd_router:process_iq(Packet), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) -> NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts, <<"proxy.@HOST@">>), OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts, <<"proxy.@HOST@">>), NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)), OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)), if (NewIQDisc /= OldIQDisc) -> lists:foreach( fun(NewHost) -> register_handlers(NewHost, NewIQDisc) end, NewHosts -- (NewHosts -- OldHosts)); true -> ok end, lists:foreach( fun(NewHost) -> ejabberd_router:register_route(NewHost, ServerHost), register_handlers(NewHost, NewIQDisc) end, NewHosts -- OldHosts), lists:foreach( fun(OldHost) -> ejabberd_router:unregister_route(OldHost), unregister_handlers(OldHost) end, OldHosts -- NewHosts), {noreply, State#state{myhosts = NewHosts}}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%------------------------ %%% Listener management %%%------------------------ add_listener(Host, Opts) -> NewOpts = [{server_host, Host} | Opts], ejabberd_listener:add_listener(get_port_ip(Host), mod_proxy65_stream, NewOpts). delete_listener(Host) -> catch ejabberd_listener:delete_listener(get_port_ip(Host), mod_proxy65_stream). %%%------------------------ %%% IQ Processing %%%------------------------ -spec process_disco_info(iq()) -> iq(). process_disco_info(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_info(#iq{type = get, to = To, lang = Lang} = IQ) -> Host = ejabberd_router:host_of_route(To#jid.lserver), Name = gen_mod:get_module_opt(Host, mod_proxy65, name, ?T("SOCKS5 Bytestreams")), Info = ejabberd_hooks:run_fold(disco_info, Host, [], [Host, ?MODULE, <<"">>, <<"">>]), xmpp:make_iq_result( IQ, #disco_info{xdata = Info, identities = [#identity{category = <<"proxy">>, type = <<"bytestreams">>, name = translate:translate(Lang, Name)}], features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_VCARD, ?NS_BYTESTREAMS]}). -spec process_disco_items(iq()) -> iq(). process_disco_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_items(#iq{type = get} = IQ) -> xmpp:make_iq_result(IQ, #disco_items{}). -spec process_vcard(iq()) -> iq(). process_vcard(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_vcard(#iq{type = get, lang = Lang} = IQ) -> Desc = translate:translate(Lang, <<"ejabberd SOCKS5 Bytestreams module">>), xmpp:make_iq_result( IQ, #vcard_temp{fn = <<"ejabberd/mod_proxy65">>, url = ?EJABBERD_URI, desc = <>}). -spec process_bytestreams(iq()) -> iq(). process_bytestreams(#iq{type = get, from = JID, to = To, lang = Lang} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), ACL = gen_mod:get_module_opt(ServerHost, mod_proxy65, access, all), case acl:match_rule(ServerHost, ACL, JID) of allow -> StreamHost = get_streamhost(Host, ServerHost), xmpp:make_iq_result(IQ, #bytestreams{hosts = [StreamHost]}); deny -> xmpp:make_error(IQ, xmpp:err_forbidden(<<"Access denied by service policy">>, Lang)) end; process_bytestreams(#iq{type = set, lang = Lang, sub_els = [#bytestreams{sid = SID}]} = IQ) when SID == <<"">> orelse size(SID) > 128 -> Why = {bad_attr_value, <<"sid">>, <<"query">>, ?NS_BYTESTREAMS}, Txt = xmpp:io_format_error(Why), xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); process_bytestreams(#iq{type = set, lang = Lang, sub_els = [#bytestreams{activate = undefined}]} = IQ) -> Why = {missing_cdata, <<"">>, <<"activate">>, ?NS_BYTESTREAMS}, Txt = xmpp:io_format_error(Why), xmpp:make_error(IQ, xmpp:err_jid_malformed(Txt, Lang)); process_bytestreams(#iq{type = set, lang = Lang, from = InitiatorJID, to = To, sub_els = [#bytestreams{activate = TargetJID, sid = SID}]} = IQ) -> ServerHost = ejabberd_router:host_of_route(To#jid.lserver), ACL = gen_mod:get_module_opt(ServerHost, mod_proxy65, access, all), case acl:match_rule(ServerHost, ACL, InitiatorJID) of allow -> Node = ejabberd_cluster:get_node_by_id(To#jid.lresource), Target = jid:encode(jid:tolower(TargetJID)), Initiator = jid:encode(jid:tolower(InitiatorJID)), SHA1 = str:sha(<>), Mod = gen_mod:ram_db_mod(global, mod_proxy65), MaxConnections = max_connections(ServerHost), case Mod:activate_stream(SHA1, Initiator, MaxConnections, Node) of {ok, InitiatorPid, TargetPid} -> mod_proxy65_stream:activate( {InitiatorPid, InitiatorJID}, {TargetPid, TargetJID}), xmpp:make_iq_result(IQ); {error, notfound} -> Txt = <<"Failed to activate bytestream">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)); {error, {limit, InitiatorPid, TargetPid}} -> mod_proxy65_stream:stop(InitiatorPid), mod_proxy65_stream:stop(TargetPid), Txt = <<"Too many active bytestreams">>, xmpp:make_error(IQ, xmpp:err_resource_constraint(Txt, Lang)); {error, conflict} -> Txt = <<"Bytestream already activated">>, xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang)); {error, Err} -> ?ERROR_MSG("failed to activate bytestream from ~s to ~s: ~p", [Initiator, Target, Err]), Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end; deny -> Txt = <<"Access denied by service policy">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)) end. %%%------------------------- %%% Auxiliary functions. %%%------------------------- transform_module_options(Opts) -> lists:map( fun({ip, IP}) when is_tuple(IP) -> {ip, misc:ip_to_list(IP)}; ({hostname, IP}) when is_tuple(IP) -> {hostname, misc:ip_to_list(IP)}; (Opt) -> Opt end, Opts). -spec get_streamhost(binary(), binary()) -> streamhost(). get_streamhost(Host, ServerHost) -> {Port, IP} = get_port_ip(ServerHost), HostName0 = gen_mod:get_module_opt(ServerHost, mod_proxy65, hostname, misc:ip_to_list(IP)), HostName = misc:expand_keyword(<<"@HOST@">>, HostName0, ServerHost), Resource = ejabberd_cluster:node_id(), #streamhost{jid = jid:make(<<"">>, Host, Resource), host = HostName, port = Port}. -spec get_port_ip(binary()) -> {pos_integer(), inet:ip_address()}. get_port_ip(Host) -> Port = gen_mod:get_module_opt(Host, mod_proxy65, port, 7777), IP = gen_mod:get_module_opt(Host, mod_proxy65, ip, get_my_ip()), {Port, IP}. -spec get_my_ip() -> inet:ip_address(). get_my_ip() -> {ok, MyHostName} = inet:gethostname(), case inet:getaddr(MyHostName, inet) of {ok, Addr} -> Addr; {error, _} -> {127, 0, 0, 1} end. max_connections(ServerHost) -> gen_mod:get_module_opt(ServerHost, mod_proxy65, max_connections, infinity). register_handlers(Host, IQDisc) -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, ?MODULE, process_disco_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_disco_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_BYTESTREAMS, ?MODULE, process_bytestreams, IQDisc). unregister_handlers(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_BYTESTREAMS). ejabberd-18.01/src/eldap.erl0000644000232200023220000011377213225664356016236 0ustar debalancedebalance-module(eldap). %%% -------------------------------------------------------------------- %%% Created: 12 Oct 2000 by Tobbe %%% Function: Erlang client LDAP implementation according RFC 2251. %%% The interface is based on RFC 1823, and %%% draft-ietf-asid-ldap-c-api-00.txt %%% %%% Copyright (C) 2000 Torbjorn Tornkvist, tnt@home.se %%% %%% %%% This program is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% Modified by Sean Hinde 7th Dec 2000 %%% Turned into gen_fsm, made non-blocking, added timers etc to support this. %%% Now has the concept of a name (string() or atom()) per instance which allows %%% multiple users to call by name if so desired. %%% %%% Can be configured with start_link parameters or use a config file to get %%% host to connect to, dn, password, log function etc. %%% Modified by Alexey Shchepin %%% Modified by Evgeniy Khramtsov %%% Implemented queue for bind() requests to prevent pending binds. %%% Implemented extensibleMatch/2 function. %%% Implemented LDAP Extended Operations (currently only Password Modify %%% is supported - RFC 3062). %%% Modified by Christophe Romain %%% Improve error case handling %%% Modified by Mickael Remond %%% Now use ejabberd log mechanism %%% Modified by: %%% Thomas Baden 2008 April 6th %%% Andy Harb 2008 April 28th %%% Anton Podavalov 2009 February 22th %%% Added LDAPS support, modeled off jungerl eldap.erl version. %%% NOTICE: STARTTLS is not supported. %%% -------------------------------------------------------------------- -vc('$Id$ '). %%%---------------------------------------------------------------------- %%% LDAP Client state machine. %%% Possible states are: %%% connecting - actually disconnected, but retrying periodically %%% wait_bind_response - connected and sent bind request %%% active - bound to LDAP Server and ready to handle commands %%% active_bind - sent bind() request and waiting for response %%%---------------------------------------------------------------------- -behaviour(p1_fsm). -include("ejabberd.hrl"). -include("logger.hrl"). %% External exports -export([start_link/1, start_link/6]). -export([baseObject/0, singleLevel/0, wholeSubtree/0, close/1, equalityMatch/2, greaterOrEqual/2, lessOrEqual/2, approxMatch/2, search/2, substrings/2, present/1, extensibleMatch/2, 'and'/1, 'or'/1, 'not'/1, modify/3, mod_add/2, mod_delete/2, mod_replace/2, add/3, delete/2, modify_dn/5, modify_passwd/3, bind/3]). -export([get_status/1]). -export([init/1, connecting/2, connecting/3, wait_bind_response/3, active/3, active_bind/3, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -export_type([filter/0]). -include("ELDAPv3.hrl"). -include("eldap.hrl"). -define(LDAP_VERSION, 3). -define(RETRY_TIMEOUT, 500). -define(BIND_TIMEOUT, 10000). -define(CMD_TIMEOUT, 100000). %% Used in gen_fsm sync calls. %% Used as a timeout for gen_tcp:send/2 -define(CALL_TIMEOUT, (?CMD_TIMEOUT) + (?BIND_TIMEOUT) + (?RETRY_TIMEOUT)). -define(SEND_TIMEOUT, 30000). -define(MAX_TRANSACTION_ID, 65535). -define(MIN_TRANSACTION_ID, 0). %% Grace period after "soft" LDAP bind errors: -define(GRACEFUL_RETRY_TIMEOUT, 5000). -define(SUPPORTEDEXTENSION, <<"1.3.6.1.4.1.1466.101.120.7">>). -define(SUPPORTEDEXTENSIONSYNTAX, <<"1.3.6.1.4.1.1466.115.121.1.38">>). -define(STARTTLS, <<"1.3.6.1.4.1.1466.20037">>). -type handle() :: pid() | atom() | binary(). -record(eldap, {version = ?LDAP_VERSION :: non_neg_integer(), hosts = [] :: [binary()], host = undefined :: binary() | undefined, port = 389 :: inet:port_number(), sockmod = gen_tcp :: ssl | gen_tcp, tls = none :: none | tls, tls_options = [] :: [{certfile, string()} | {cacertfile, string()} | {depth, non_neg_integer()} | {verify, non_neg_integer()}], fd :: gen_tcp:socket() | undefined, rootdn = <<"">> :: binary(), passwd = <<"">> :: binary(), id = 0 :: non_neg_integer(), bind_timer = make_ref() :: reference(), dict = dict:new() :: ?TDICT, req_q = queue:new() :: ?TQUEUE}). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Name) -> Reg_name = misc:binary_to_atom(<<"eldap_", Name/binary>>), p1_fsm:start_link({local, Reg_name}, ?MODULE, [], []). -spec start_link(binary(), [binary()], inet:port_number(), binary(), binary(), tlsopts()) -> any(). start_link(Name, Hosts, Port, Rootdn, Passwd, Opts) -> Reg_name = misc:binary_to_atom(<<"eldap_", Name/binary>>), p1_fsm:start_link({local, Reg_name}, ?MODULE, [Hosts, Port, Rootdn, Passwd, Opts], []). -spec get_status(handle()) -> any(). %%% -------------------------------------------------------------------- %%% Get status of connection. %%% -------------------------------------------------------------------- get_status(Handle) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_all_state_event(Handle1, get_status). %%% -------------------------------------------------------------------- %%% Shutdown connection (and process) asynchronous. %%% -------------------------------------------------------------------- -spec close(handle()) -> any(). close(Handle) -> Handle1 = get_handle(Handle), p1_fsm:send_all_state_event(Handle1, close). %%% -------------------------------------------------------------------- %%% Add an entry. The entry field MUST NOT exist for the AddRequest %%% to succeed. The parent of the entry MUST exist. %%% Example: %%% %%% add(Handle, %%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com", %%% [{"objectclass", ["person"]}, %%% {"cn", ["Bill Valentine"]}, %%% {"sn", ["Valentine"]}, %%% {"telephoneNumber", ["545 555 00"]}] %%% ) %%% -------------------------------------------------------------------- add(Handle, Entry, Attributes) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {add, Entry, add_attrs(Attributes)}, ?CALL_TIMEOUT). %%% Do sanity check ! add_attrs(Attrs) -> F = fun ({Type, Vals}) -> {'AddRequest_attributes', Type, Vals} end, case catch lists:map(F, Attrs) of {'EXIT', _} -> throw({error, attribute_values}); Else -> Else end. %%% -------------------------------------------------------------------- %%% Delete an entry. The entry consists of the DN of %%% the entry to be deleted. %%% Example: %%% %%% delete(Handle, %%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com" %%% ) %%% -------------------------------------------------------------------- delete(Handle, Entry) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {delete, Entry}, ?CALL_TIMEOUT). %%% -------------------------------------------------------------------- %%% Modify an entry. Given an entry a number of modification %%% operations can be performed as one atomic operation. %%% Example: %%% %%% modify(Handle, %%% "cn=Torbjorn Tornkvist, ou=people, o=Bluetail AB, dc=bluetail, dc=com", %%% [replace("telephoneNumber", ["555 555 00"]), %%% add("description", ["LDAP hacker"])] %%% ) %%% -------------------------------------------------------------------- -spec modify(handle(), any(), [add | delete | replace]) -> any(). modify(Handle, Object, Mods) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {modify, Object, Mods}, ?CALL_TIMEOUT). %%% %%% Modification operations. %%% Example: %%% replace("telephoneNumber", ["555 555 00"]) %%% mod_add(Type, Values) -> m(add, Type, Values). mod_delete(Type, Values) -> m(delete, Type, Values). %%% -------------------------------------------------------------------- %%% Modify an entry. Given an entry a number of modification %%% operations can be performed as one atomic operation. %%% Example: %%% %%% modify_dn(Handle, %%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com", %%% "cn=Ben Emerson", %%% true, %%% "" %%% ) %%% -------------------------------------------------------------------- mod_replace(Type, Values) -> m(replace, Type, Values). m(Operation, Type, Values) -> #'ModifyRequest_modification_SEQOF'{operation = Operation, modification = #'AttributeTypeAndValues'{type = Type, vals = Values}}. modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)}, ?CALL_TIMEOUT). -spec modify_passwd(handle(), binary(), binary()) -> any(). modify_passwd(Handle, DN, Passwd) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {modify_passwd, DN, Passwd}, ?CALL_TIMEOUT). %%% -------------------------------------------------------------------- %%% Bind. %%% Example: %%% %%% bind(Handle, %%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com", %%% "secret") %%% -------------------------------------------------------------------- -spec bind(handle(), binary(), binary()) -> any(). bind(Handle, RootDN, Passwd) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {bind, RootDN, Passwd}, ?CALL_TIMEOUT). %%% Sanity checks ! bool_p(Bool) when Bool == true; Bool == false -> Bool. optional([]) -> asn1_NOVALUE; optional(Value) -> Value. %%% -------------------------------------------------------------------- %%% Synchronous search of the Directory returning a %%% requested set of attributes. %%% %%% Example: %%% %%% Filter = eldap:substrings("sn", [{any,"o"}]), %%% eldap:search(S, [{base, "dc=bluetail, dc=com"}, %%% {filter, Filter}, %%% {attributes,["cn"]}])), %%% %%% Returned result: {ok, #eldap_search_result{}} %%% %%% Example: %%% %%% {ok,{eldap_search_result, %%% [{eldap_entry, %%% "cn=Magnus Froberg, dc=bluetail, dc=com", %%% [{"cn",["Magnus Froberg"]}]}, %%% {eldap_entry, %%% "cn=Torbjorn Tornkvist, dc=bluetail, dc=com", %%% [{"cn",["Torbjorn Tornkvist"]}]}], %%% []}} %%% %%% -------------------------------------------------------------------- -type search_args() :: [{base, binary()} | {filter, filter()} | {scope, scope()} | {attributes, [binary()]} | {types_only, boolean()} | {timeout, non_neg_integer()} | {limit, non_neg_integer()} | {deref_aliases, never | searching | finding | always}]. -spec search(handle(), eldap_search() | search_args()) -> any(). search(Handle, A) when is_record(A, eldap_search) -> call_search(Handle, A); search(Handle, L) when is_list(L) -> case catch parse_search_args(L) of {error, Emsg} -> {error, Emsg}; {'EXIT', Emsg} -> {error, Emsg}; A when is_record(A, eldap_search) -> call_search(Handle, A) end. call_search(Handle, A) -> Handle1 = get_handle(Handle), p1_fsm:sync_send_event(Handle1, {search, A}, ?CALL_TIMEOUT). -spec parse_search_args(search_args()) -> eldap_search(). parse_search_args(Args) -> parse_search_args(Args, #eldap_search{scope = wholeSubtree}). parse_search_args([{base, Base} | T], A) -> parse_search_args(T, A#eldap_search{base = Base}); parse_search_args([{filter, Filter} | T], A) -> parse_search_args(T, A#eldap_search{filter = Filter}); parse_search_args([{scope, Scope} | T], A) -> parse_search_args(T, A#eldap_search{scope = Scope}); parse_search_args([{attributes, Attrs} | T], A) -> parse_search_args(T, A#eldap_search{attributes = Attrs}); parse_search_args([{types_only, TypesOnly} | T], A) -> parse_search_args(T, A#eldap_search{types_only = TypesOnly}); parse_search_args([{timeout, Timeout} | T], A) when is_integer(Timeout) -> parse_search_args(T, A#eldap_search{timeout = Timeout}); parse_search_args([{limit, Limit} | T], A) when is_integer(Limit) -> parse_search_args(T, A#eldap_search{limit = Limit}); parse_search_args([{deref_aliases, never} | T], A) -> parse_search_args(T, A#eldap_search{deref_aliases = neverDerefAliases}); parse_search_args([{deref_aliases, searching} | T], A) -> parse_search_args(T, A#eldap_search{deref_aliases = derefInSearching}); parse_search_args([{deref_aliases, finding} | T], A) -> parse_search_args(T, A#eldap_search{deref_aliases = derefFindingBaseObj}); parse_search_args([{deref_aliases, always} | T], A) -> parse_search_args(T, A#eldap_search{deref_aliases = derefAlways}); parse_search_args([H | _], _) -> throw({error, {unknown_arg, H}}); parse_search_args([], A) -> A. baseObject() -> baseObject. singleLevel() -> singleLevel. %%% %%% The Scope parameter %%% wholeSubtree() -> wholeSubtree. %%% %%% Boolean filter operations %%% -type filter() :: 'and'() | 'or'() | 'not'() | equalityMatch() | greaterOrEqual() | lessOrEqual() | approxMatch() | present() | substrings() | extensibleMatch(). %%% %%% The following Filter parameters consist of an attribute %%% and an attribute value. Example: F("uid","tobbe") %%% -type 'and'() :: {'and', [filter()]}. -spec 'and'([filter()]) -> 'and'(). 'and'(ListOfFilters) when is_list(ListOfFilters) -> {'and', ListOfFilters}. -type 'or'() :: {'or', [filter()]}. -spec 'or'([filter()]) -> 'or'(). 'or'(ListOfFilters) when is_list(ListOfFilters) -> {'or', ListOfFilters}. -type 'not'() :: {'not', filter()}. -spec 'not'(filter()) -> 'not'(). 'not'(Filter) when is_tuple(Filter) -> {'not', Filter}. -type equalityMatch() :: {equalityMatch, 'AttributeValueAssertion'()}. -spec equalityMatch(binary(), binary()) -> equalityMatch(). equalityMatch(Desc, Value) -> {equalityMatch, av_assert(Desc, Value)}. -type greaterOrEqual() :: {greaterOrEqual, 'AttributeValueAssertion'()}. -spec greaterOrEqual(binary(), binary()) -> greaterOrEqual(). greaterOrEqual(Desc, Value) -> {greaterOrEqual, av_assert(Desc, Value)}. -type lessOrEqual() :: {lessOrEqual, 'AttributeValueAssertion'()}. -spec lessOrEqual(binary(), binary()) -> lessOrEqual(). lessOrEqual(Desc, Value) -> {lessOrEqual, av_assert(Desc, Value)}. -type approxMatch() :: {approxMatch, 'AttributeValueAssertion'()}. -spec approxMatch(binary(), binary()) -> approxMatch(). approxMatch(Desc, Value) -> {approxMatch, av_assert(Desc, Value)}. -type 'AttributeValueAssertion'() :: #'AttributeValueAssertion'{attributeDesc :: binary(), assertionValue :: binary()}. -spec av_assert(binary(), binary()) -> 'AttributeValueAssertion'(). av_assert(Desc, Value) -> #'AttributeValueAssertion'{attributeDesc = Desc, assertionValue = Value}. %%% %%% Filter to check for the presence of an attribute %%% -type present() :: {present, binary()}. -spec present(binary()) -> present(). %%% %%% A substring filter seem to be based on a pattern: %%% %%% InitValue*AnyValue*FinalValue %%% %%% where all three parts seem to be optional (at least when %%% talking with an OpenLDAP server). Thus, the arguments %%% to substrings/2 looks like this: %%% %%% Type ::= string( ) %%% SubStr ::= listof( {initial,Value} | {any,Value}, {final,Value}) %%% %%% Example: substrings("sn",[{initial,"To"},{any,"kv"},{final,"st"}]) %%% will match entries containing: 'sn: Tornkvist' %%% present(Attribute) -> {present, Attribute}. %%% %%% extensibleMatch filter. %%% FIXME: Describe the purpose of this filter. %%% %%% Value ::= string( ) %%% Opts ::= listof( {matchingRule, Str} | {type, Str} | {dnAttributes, true} ) %%% %%% Example: extensibleMatch("Fred", [{matchingRule, "1.2.3.4.5"}, {type, "cn"}]). %%% -type substr() :: [{initial | any | final, binary()}]. -type 'SubstringFilter'() :: #'SubstringFilter'{type :: binary(), substrings :: substr()}. -type substrings() :: {substrings, 'SubstringFilter'()}. -spec substrings(binary(), substr()) -> substrings(). substrings(Type, SubStr) -> {substrings, #'SubstringFilter'{type = Type, substrings = SubStr}}. -type match_opts() :: [{matchingRule | type, binary()} | {dnAttributes, boolean()}]. -type 'MatchingRuleAssertion'() :: #'MatchingRuleAssertion'{matchValue :: binary(), type :: asn1_NOVALUE | binary(), matchingRule :: asn1_NOVALUE | binary(), dnAttributes :: asn1_DEFAULT | true}. -type extensibleMatch() :: {extensibleMatch, 'MatchingRuleAssertion'()}. -spec extensibleMatch(binary(), match_opts()) -> extensibleMatch(). extensibleMatch(Value, Opts) -> MRA = #'MatchingRuleAssertion'{matchValue = Value}, {extensibleMatch, extensibleMatch_opts(Opts, MRA)}. extensibleMatch_opts([{matchingRule, Rule} | Opts], MRA) -> extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{matchingRule = Rule}); extensibleMatch_opts([{type, Desc} | Opts], MRA) -> extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{type = Desc}); extensibleMatch_opts([{dnAttributes, true} | Opts], MRA) -> extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{dnAttributes = true}); extensibleMatch_opts([_ | Opts], MRA) -> extensibleMatch_opts(Opts, MRA); extensibleMatch_opts([], MRA) -> MRA. get_handle(Pid) when is_pid(Pid) -> Pid; get_handle(Atom) when is_atom(Atom) -> Atom; get_handle(Name) when is_binary(Name) -> misc:binary_to_atom(<<"eldap_", Name/binary>>). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, StateName, StateData} | %% {ok, StateName, StateData, Timeout} | %% ignore | %% {stop, StopReason} %% I use the trick of setting a timeout of 0 to pass control into the %% process. %%---------------------------------------------------------------------- init([Hosts, Port, Rootdn, Passwd, Opts]) -> Encrypt = case proplists:get_value(encrypt, Opts) of tls -> tls; _ -> none end, PortTemp = case Port of undefined -> case Encrypt of tls -> ?LDAPS_PORT; _ -> ?LDAP_PORT end; PT -> PT end, CertOpts = case proplists:get_value(tls_certfile, Opts) of undefined -> []; Path1 -> [{certfile, Path1}] end, CacertOpts = case proplists:get_value(tls_cacertfile, Opts) of undefined -> []; Path2 -> [{cacertfile, Path2}] end, DepthOpts = case proplists:get_value(tls_depth, Opts) of undefined -> []; Depth -> [{depth, Depth}] end, Verify = proplists:get_value(tls_verify, Opts, false), TLSOpts = if (Verify == hard orelse Verify == soft) andalso CacertOpts == [] -> ?WARNING_MSG("TLS verification is enabled but no CA " "certfiles configured, so verification " "is disabled.", []), CertOpts; Verify == soft -> [{verify, 1}] ++ CertOpts ++ CacertOpts ++ DepthOpts; Verify == hard -> [{verify, 2}] ++ CertOpts ++ CacertOpts ++ DepthOpts; true -> [] end, {ok, connecting, #eldap{hosts = Hosts, port = PortTemp, rootdn = Rootdn, passwd = Passwd, tls = Encrypt, tls_options = TLSOpts, id = 0, dict = dict:new(), req_q = queue:new()}, 0}. connecting(timeout, S) -> {ok, NextState, NewS} = connect_bind(S), {next_state, NextState, NewS}. connecting(Event, From, S) -> Q = queue:in({Event, From}, S#eldap.req_q), {next_state, connecting, S#eldap{req_q = Q}}. wait_bind_response(Event, From, S) -> Q = queue:in({Event, From}, S#eldap.req_q), {next_state, wait_bind_response, S#eldap{req_q = Q}}. active_bind(Event, From, S) -> Q = queue:in({Event, From}, S#eldap.req_q), {next_state, active_bind, S#eldap{req_q = Q}}. active(Event, From, S) -> process_command(S, Event, From). %%---------------------------------------------------------------------- %% Func: handle_event/3 %% Called when p1_fsm:send_all_state_event/2 is invoked. %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- handle_event(close, _StateName, S) -> catch (S#eldap.sockmod):close(S#eldap.fd), {stop, normal, S}; handle_event(_Event, StateName, S) -> {next_state, StateName, S}. handle_sync_event(_Event, _From, StateName, S) -> {reply, {StateName, S}, StateName, S}. %% %% Packets arriving in various states %% handle_info({Tag, _Socket, Data}, connecting, S) when Tag == tcp; Tag == ssl -> ?DEBUG("tcp packet received when disconnected!~n~p", [Data]), {next_state, connecting, S}; handle_info({Tag, _Socket, Data}, wait_bind_response, S) when Tag == tcp; Tag == ssl -> cancel_timer(S#eldap.bind_timer), case catch recvd_wait_bind_response(Data, S) of bound -> dequeue_commands(S); {fail_bind, Reason} -> report_bind_failure(S#eldap.host, S#eldap.port, Reason), {next_state, connecting, close_and_retry(S, ?GRACEFUL_RETRY_TIMEOUT)}; {'EXIT', Reason} -> report_bind_failure(S#eldap.host, S#eldap.port, Reason), {next_state, connecting, close_and_retry(S)}; {error, Reason} -> report_bind_failure(S#eldap.host, S#eldap.port, Reason), {next_state, connecting, close_and_retry(S)} end; handle_info({Tag, _Socket, Data}, StateName, S) when (StateName == active orelse StateName == active_bind) andalso (Tag == tcp orelse Tag == ssl) -> case catch recvd_packet(Data, S) of {response, Response, RequestType} -> NewS = case Response of {reply, Reply, To, S1} -> p1_fsm:reply(To, Reply), S1; {ok, S1} -> S1 end, if StateName == active_bind andalso RequestType == bindRequest orelse StateName == active -> dequeue_commands(NewS); true -> {next_state, StateName, NewS} end; _ -> {next_state, StateName, S} end; handle_info({Tag, _Socket}, Fsm_state, S) when Tag == tcp_closed; Tag == ssl_closed -> ?WARNING_MSG("LDAP server closed the connection: ~s:~p~nIn " "State: ~p", [S#eldap.host, S#eldap.port, Fsm_state]), {next_state, connecting, close_and_retry(S)}; handle_info({Tag, _Socket, Reason}, Fsm_state, S) when Tag == tcp_error; Tag == ssl_error -> ?DEBUG("eldap received tcp_error: ~p~nIn State: ~p", [Reason, Fsm_state]), {next_state, connecting, close_and_retry(S)}; %% %% Timers %% handle_info({timeout, Timer, {cmd_timeout, Id}}, StateName, S) -> case cmd_timeout(Timer, Id, S) of {reply, To, Reason, NewS} -> p1_fsm:reply(To, Reason), {next_state, StateName, NewS}; {error, _Reason} -> {next_state, StateName, S} end; handle_info({timeout, retry_connect}, connecting, S) -> {ok, NextState, NewS} = connect_bind(S), {next_state, NextState, NewS}; handle_info({timeout, _Timer, bind_timeout}, wait_bind_response, S) -> {next_state, connecting, close_and_retry(S)}; %% %% Make sure we don't fill the message queue with rubbish %% handle_info(Info, StateName, S) -> ?DEBUG("eldap. Unexpected Info: ~p~nIn state: " "~p~n when StateData is: ~p", [Info, StateName, S]), {next_state, StateName, S}. %%---------------------------------------------------------------------- %% Func: terminate/3 %% Purpose: Shutdown the fsm %% Returns: any %%---------------------------------------------------------------------- terminate(_Reason, _StateName, _StatData) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/4 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState, NewStateData} %%---------------------------------------------------------------------- code_change(_OldVsn, StateName, S, _Extra) -> {ok, StateName, S}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- dequeue_commands(S) -> case queue:out(S#eldap.req_q) of {{value, {Event, From}}, Q} -> case process_command(S#eldap{req_q = Q}, Event, From) of {_, active, NewS} -> dequeue_commands(NewS); Res -> Res end; {empty, _} -> {next_state, active, S} end. process_command(S, Event, From) -> case send_command(Event, From, S) of {ok, NewS} -> case Event of {bind, _, _} -> {next_state, active_bind, NewS}; _ -> {next_state, active, NewS} end; {error, _Reason} -> Q = queue:in_r({Event, From}, S#eldap.req_q), NewS = close_and_retry(S#eldap{req_q = Q}), {next_state, connecting, NewS} end. send_command(Command, From, S) -> Id = bump_id(S), {Name, Request} = gen_req(Command), Message = #'LDAPMessage'{messageID = Id, protocolOp = {Name, Request}}, ?DEBUG("~p~n", [{Name, ejabberd_config:may_hide_data(Request)}]), {ok, Bytes} = 'ELDAPv3':encode('LDAPMessage', Message), case (S#eldap.sockmod):send(S#eldap.fd, Bytes) of ok -> Timer = erlang:start_timer(?CMD_TIMEOUT, self(), {cmd_timeout, Id}), New_dict = dict:store(Id, [{Timer, Command, From, Name}], S#eldap.dict), {ok, S#eldap{id = Id, dict = New_dict}}; Error -> Error end. gen_req({search, A}) -> {searchRequest, #'SearchRequest'{baseObject = A#eldap_search.base, scope = A#eldap_search.scope, derefAliases = A#eldap_search.deref_aliases, sizeLimit = A#eldap_search.limit, timeLimit = A#eldap_search.timeout, typesOnly = A#eldap_search.types_only, filter = A#eldap_search.filter, attributes = A#eldap_search.attributes}}; gen_req({add, Entry, Attrs}) -> {addRequest, #'AddRequest'{entry = Entry, attributes = Attrs}}; gen_req({delete, Entry}) -> {delRequest, Entry}; gen_req({modify, Obj, Mod}) -> {modifyRequest, #'ModifyRequest'{object = Obj, modification = Mod}}; gen_req({modify_dn, Entry, NewRDN, DelOldRDN, NewSup}) -> {modDNRequest, #'ModifyDNRequest'{entry = Entry, newrdn = NewRDN, deleteoldrdn = DelOldRDN, newSuperior = NewSup}}; gen_req({modify_passwd, DN, Passwd}) -> {ok, ReqVal} = 'ELDAPv3':encode('PasswdModifyRequestValue', #'PasswdModifyRequestValue'{userIdentity = DN, newPasswd = Passwd}), {extendedReq, #'ExtendedRequest'{requestName = ?passwdModifyOID, requestValue = iolist_to_binary(ReqVal)}}; gen_req({bind, RootDN, Passwd}) -> {bindRequest, #'BindRequest'{version = ?LDAP_VERSION, name = RootDN, authentication = {simple, Passwd}}}. %%----------------------------------------------------------------------- %% recvd_packet %% Deals with incoming packets in the active state %% Will return one of: %% {ok, NewS} - Don't reply to client yet as this is part of a search %% result and we haven't got all the answers yet. %% {reply, Result, From, NewS} - Reply with result to client From %% {error, Reason} %% {'EXIT', Reason} - Broke %%----------------------------------------------------------------------- recvd_packet(Pkt, S) -> case 'ELDAPv3':decode('LDAPMessage', Pkt) of {ok, Msg} -> Op = Msg#'LDAPMessage'.protocolOp, ?DEBUG("~p", [Op]), Dict = S#eldap.dict, Id = Msg#'LDAPMessage'.messageID, {Timer, From, Name, Result_so_far} = get_op_rec(Id, Dict), Answer = case {Name, Op} of {searchRequest, {searchResEntry, R}} when is_record(R, 'SearchResultEntry') -> New_dict = dict:append(Id, R, Dict), {ok, S#eldap{dict = New_dict}}; {searchRequest, {searchResDone, Result}} -> Reason = Result#'LDAPResult'.resultCode, if Reason == success; Reason == sizeLimitExceeded -> {Res, Ref} = polish(Result_so_far), New_dict = dict:erase(Id, Dict), cancel_timer(Timer), {reply, #eldap_search_result{entries = Res, referrals = Ref}, From, S#eldap{dict = New_dict}}; true -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), {reply, {error, Reason}, From, S#eldap{dict = New_dict}} end; {searchRequest, {searchResRef, R}} -> New_dict = dict:append(Id, R, Dict), {ok, S#eldap{dict = New_dict}}; {addRequest, {addResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {delRequest, {delResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {modifyRequest, {modifyResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {modDNRequest, {modDNResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {bindRequest, {bindResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_bind_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {extendedReq, {extendedResp, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_extended_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {OtherName, OtherResult} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), {reply, {error, {invalid_result, OtherName, OtherResult}}, From, S#eldap{dict = New_dict}} end, {response, Answer, Name}; Error -> Error end. check_reply(#'LDAPResult'{resultCode = success}, _From) -> ok; check_reply(#'LDAPResult'{resultCode = Reason}, _From) -> {error, Reason}; check_reply(Other, _From) -> {error, Other}. check_bind_reply(#'BindResponse'{resultCode = success}, _From) -> ok; check_bind_reply(#'BindResponse'{resultCode = Reason}, _From) -> {error, Reason}; check_bind_reply(Other, _From) -> {error, Other}. %% TODO: process reply depending on requestName: %% this requires BER-decoding of #'ExtendedResponse'.response check_extended_reply(#'ExtendedResponse'{resultCode = success}, _From) -> ok; check_extended_reply(#'ExtendedResponse'{resultCode = Reason}, _From) -> {error, Reason}; check_extended_reply(Other, _From) -> {error, Other}. get_op_rec(Id, Dict) -> case dict:find(Id, Dict) of {ok, [{Timer, _Command, From, Name} | Res]} -> {Timer, From, Name, Res}; error -> throw({error, unkown_id}) end. %%----------------------------------------------------------------------- %% recvd_wait_bind_response packet %% Deals with incoming packets in the wait_bind_response state %% Will return one of: %% bound - Success - move to active state %% {fail_bind, Reason} - Failed %% {error, Reason} %% {'EXIT', Reason} - Broken packet %%----------------------------------------------------------------------- recvd_wait_bind_response(Pkt, S) -> case 'ELDAPv3':decode('LDAPMessage', Pkt) of {ok, Msg} -> ?DEBUG("~p", [Msg]), check_id(S#eldap.id, Msg#'LDAPMessage'.messageID), case Msg#'LDAPMessage'.protocolOp of {bindResponse, Result} -> case Result#'BindResponse'.resultCode of success -> bound; Error -> {fail_bind, Error} end end; Else -> {fail_bind, Else} end. check_id(Id, Id) -> ok; check_id(_, _) -> throw({error, wrong_bind_id}). %%----------------------------------------------------------------------- %% General Helpers %%----------------------------------------------------------------------- cancel_timer(Timer) -> erlang:cancel_timer(Timer), receive {timeout, Timer, _} -> ok after 0 -> ok end. close_and_retry(S, Timeout) -> catch (S#eldap.sockmod):close(S#eldap.fd), Queue = dict:fold(fun (_Id, [{Timer, Command, From, _Name} | _], Q) -> cancel_timer(Timer), queue:in_r({Command, From}, Q); (_, _, Q) -> Q end, S#eldap.req_q, S#eldap.dict), erlang:send_after(Timeout, self(), {timeout, retry_connect}), S#eldap{fd = undefined, req_q = Queue, dict = dict:new()}. close_and_retry(S) -> close_and_retry(S, ?RETRY_TIMEOUT). report_bind_failure(Host, Port, Reason) -> ?WARNING_MSG("LDAP bind failed on ~s:~p~nReason: ~p", [Host, Port, Reason]). %%----------------------------------------------------------------------- %% Sort out timed out commands %%----------------------------------------------------------------------- cmd_timeout(Timer, Id, S) -> Dict = S#eldap.dict, case dict:find(Id, Dict) of {ok, [{Timer, _Command, From, Name} | Res]} -> case Name of searchRequest -> {Res1, Ref1} = polish(Res), New_dict = dict:erase(Id, Dict), {reply, From, {timeout, #eldap_search_result{entries = Res1, referrals = Ref1}}, S#eldap{dict = New_dict}}; _ -> New_dict = dict:erase(Id, Dict), {reply, From, {error, timeout}, S#eldap{dict = New_dict}} end; error -> {error, timed_out_cmd_not_in_dict} end. %%----------------------------------------------------------------------- %% Common stuff for results %%----------------------------------------------------------------------- %%% %%% Polish the returned search result %%% polish(Entries) -> polish(Entries, [], []). polish([H | T], Res, Ref) when is_record(H, 'SearchResultEntry') -> ObjectName = H#'SearchResultEntry'.objectName, F = fun ({_, A, V}) -> {A, V} end, Attrs = lists:map(F, H#'SearchResultEntry'.attributes), polish(T, [#eldap_entry{object_name = ObjectName, attributes = Attrs} | Res], Ref); polish([H | T], Res, Ref) -> % No special treatment of referrals at the moment. polish(T, Res, [H | Ref]); polish([], Res, Ref) -> {Res, Ref}. %%----------------------------------------------------------------------- %% Connect to next server in list and attempt to bind to it. %%----------------------------------------------------------------------- connect_bind(S) -> Host = next_host(S#eldap.host, S#eldap.hosts), ?INFO_MSG("LDAP connection on ~s:~p", [Host, S#eldap.port]), Opts = if S#eldap.tls == tls -> [{packet, asn1}, {active, true}, {keepalive, true}, binary | S#eldap.tls_options]; true -> [{packet, asn1}, {active, true}, {keepalive, true}, {send_timeout, ?SEND_TIMEOUT}, binary] end, HostS = binary_to_list(Host), SocketData = case S#eldap.tls of tls -> SockMod = ssl, ssl:connect(HostS, S#eldap.port, Opts); %% starttls -> %% TODO: Implement STARTTLS; _ -> SockMod = gen_tcp, gen_tcp:connect(HostS, S#eldap.port, Opts) end, case SocketData of {ok, Socket} -> case bind_request(Socket, S#eldap{sockmod = SockMod}) of {ok, NewS} -> Timer = erlang:start_timer(?BIND_TIMEOUT, self(), {timeout, bind_timeout}), {ok, wait_bind_response, NewS#eldap{fd = Socket, sockmod = SockMod, host = Host, bind_timer = Timer}}; {error, Reason} -> report_bind_failure(Host, S#eldap.port, Reason), NewS = close_and_retry(S), {ok, connecting, NewS#eldap{host = Host}} end; {error, Reason} -> ?ERROR_MSG("LDAP connection failed:~n** Server: " "~s:~p~n** Reason: ~p~n** Socket options: ~p", [Host, S#eldap.port, Reason, Opts]), NewS = close_and_retry(S), {ok, connecting, NewS#eldap{host = Host}} end. bind_request(Socket, S) -> Id = bump_id(S), Req = #'BindRequest'{version = S#eldap.version, name = S#eldap.rootdn, authentication = {simple, S#eldap.passwd}}, Message = #'LDAPMessage'{messageID = Id, protocolOp = {bindRequest, Req}}, ?DEBUG("Bind Request Message:~p~n", [ejabberd_config:may_hide_data(Message)]), {ok, Bytes} = 'ELDAPv3':encode('LDAPMessage', Message), case (S#eldap.sockmod):send(Socket, Bytes) of ok -> {ok, S#eldap{id = Id}}; Error -> Error end. %% Given last tried Server, find next one to try next_host(undefined, [H | _]) -> H; % First time, take first next_host(Host, Hosts) -> % Find next in turn next_host(Host, Hosts, Hosts). next_host(Host, [Host], Hosts) -> hd(Hosts); % Wrap back to first next_host(Host, [Host | Tail], _Hosts) -> hd(Tail); % Take next next_host(_Host, [], Hosts) -> hd(Hosts); % Never connected before? (shouldn't happen) next_host(Host, [_ | T], Hosts) -> next_host(Host, T, Hosts). bump_id(#eldap{id = Id}) when Id > (?MAX_TRANSACTION_ID) -> ?MIN_TRANSACTION_ID; bump_id(#eldap{id = Id}) -> Id + 1. ejabberd-18.01/src/ejabberd_regexp.erl0000644000232200023220000000634113225664356020252 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_regexp.erl %%% Author : Badlop %%% Purpose : Frontend to Re and Regexp OTP modules %%% Created : 8 Dec 2011 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_regexp). -export([exec/2, run/2, split/2, replace/3, greplace/3, sh_to_awk/1]). exec({ReM, ReF, ReA}, {RgM, RgF, RgA}) -> try apply(ReM, ReF, ReA) catch error:undef -> apply(RgM, RgF, RgA); A:B -> {error, {A, B}} end. -spec run(binary(), binary()) -> match | nomatch | {error, any()}. run(String, Regexp) -> case exec({re, run, [String, Regexp, [{capture, none}]]}, {regexp, first_match, [binary_to_list(String), binary_to_list(Regexp)]}) of {match, _, _} -> match; {match, _} -> match; match -> match; nomatch -> nomatch; {error, Error} -> {error, Error} end. -spec split(binary(), binary()) -> [binary()]. split(String, Regexp) -> case exec({re, split, [String, Regexp, [{return, binary}]]}, {regexp, split, [binary_to_list(String), binary_to_list(Regexp)]}) of {ok, FieldList} -> [iolist_to_binary(F) || F <- FieldList]; {error, Error} -> throw(Error); A -> A end. -spec replace(binary(), binary(), binary()) -> binary(). replace(String, Regexp, New) -> case exec({re, replace, [String, Regexp, New, [{return, binary}]]}, {regexp, sub, [binary_to_list(String), binary_to_list(Regexp), binary_to_list(New)]}) of {ok, NewString, _RepCount} -> iolist_to_binary(NewString); {error, Error} -> throw(Error); A -> A end. -spec greplace(binary(), binary(), binary()) -> binary(). greplace(String, Regexp, New) -> case exec({re, replace, [String, Regexp, New, [global, {return, binary}]]}, {regexp, sub, [binary_to_list(String), binary_to_list(Regexp), binary_to_list(New)]}) of {ok, NewString, _RepCount} -> iolist_to_binary(NewString); {error, Error} -> throw(Error); A -> A end. -spec sh_to_awk(binary()) -> binary(). sh_to_awk(ShRegExp) -> case exec({xmerl_regexp, sh_to_awk, [binary_to_list(ShRegExp)]}, {regexp, sh_to_awk, [binary_to_list(ShRegExp)]}) of A -> iolist_to_binary(A) end. ejabberd-18.01/src/ejabberd_service.erl0000644000232200023220000002544213225664356020423 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 11 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_service). -behaviour(xmpp_stream_in). -behaviour(xmpp_socket). -protocol({xep, 114, '1.6'}). %% xmpp_socket callbacks -export([start/2, start_link/2, socket_type/0, close/1, close/2]). %% ejabberd_listener callbacks -export([listen_opt_type/1, transform_listen_option/2]). %% xmpp_stream_in callbacks -export([init/1, handle_info/2, terminate/2, code_change/3]). -export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4, handle_authenticated_packet/2, get_password_fun/1, tls_options/1]). %% API -export([send/2]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). -type state() :: map(). -export_type([state/0]). %%%=================================================================== %%% API %%%=================================================================== start(SockData, Opts) -> xmpp_stream_in:start(?MODULE, [SockData, Opts], ejabberd_config:fsm_limit_opts(Opts)). start_link(SockData, Opts) -> xmpp_stream_in:start_link(?MODULE, [SockData, Opts], ejabberd_config:fsm_limit_opts(Opts)). socket_type() -> xml_stream. -spec send(pid(), xmpp_element()) -> ok; (state(), xmpp_element()) -> state(). send(Stream, Pkt) -> xmpp_stream_in:send(Stream, Pkt). -spec close(pid()) -> ok; (state()) -> state(). close(Ref) -> xmpp_stream_in:close(Ref). -spec close(pid(), atom()) -> ok; (state(), atom()) -> state(). close(Ref, Reason) -> xmpp_stream_in:close(Ref, Reason). %%%=================================================================== %%% xmpp_stream_in callbacks %%%=================================================================== tls_options(#{tls_options := TLSOptions}) -> TLSOptions. init([State, Opts]) -> Access = proplists:get_value(access, Opts, all), Shaper = proplists:get_value(shaper_rule, Opts, none), GlobalPassword = proplists:get_value(password, Opts, random_password()), HostOpts = proplists:get_value(hosts, Opts, [{global, GlobalPassword}]), HostOpts1 = lists:map( fun({Host, undefined}) -> {Host, GlobalPassword}; ({Host, Password}) -> {Host, Password} end, HostOpts), CheckFrom = proplists:get_value(check_from, Opts, true), TLSOpts1 = lists:filter( fun({certfile, _}) -> true; ({ciphers, _}) -> true; ({dhfile, _}) -> true; ({cafile, _}) -> true; ({protocol_options, _}) -> true; (_) -> false end, Opts), TLSOpts = case proplists:get_bool(tls_compression, Opts) of false -> [compression_none | TLSOpts1]; true -> TLSOpts1 end, State1 = xmpp_stream_in:change_shaper(State, Shaper), State2 = State1#{access => Access, xmlns => ?NS_COMPONENT, lang => ?MYLANG, server => ?MYNAME, host_opts => dict:from_list(HostOpts1), stream_version => undefined, tls_options => TLSOpts, check_from => CheckFrom}, ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]). handle_stream_start(_StreamStart, #{remote_server := RemoteServer, lang := Lang, host_opts := HostOpts} = State) -> case ejabberd_router:is_my_host(RemoteServer) of true -> Txt = <<"Unable to register route on existing local domain">>, xmpp_stream_in:send(State, xmpp:serr_conflict(Txt, Lang)); false -> NewHostOpts = case dict:is_key(RemoteServer, HostOpts) of true -> HostOpts; false -> case dict:find(global, HostOpts) of {ok, GlobalPass} -> dict:from_list([{RemoteServer, GlobalPass}]); error -> HostOpts end end, State#{host_opts => NewHostOpts} end. get_password_fun(#{remote_server := RemoteServer, socket := Socket, ip := IP, host_opts := HostOpts}) -> fun(_) -> case dict:find(RemoteServer, HostOpts) of {ok, Password} -> {Password, undefined}; error -> ?INFO_MSG("(~s) Domain ~s is unconfigured for " "external component from ~s", [xmpp_socket:pp(Socket), RemoteServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), {false, undefined} end end. handle_auth_success(_, Mech, _, #{remote_server := RemoteServer, host_opts := HostOpts, socket := Socket, ip := IP} = State) -> ?INFO_MSG("(~s) Accepted external component ~s authentication " "for ~s from ~s", [xmpp_socket:pp(Socket), Mech, RemoteServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), lists:foreach( fun (H) -> ejabberd_router:register_route(H, ?MYNAME), ejabberd_hooks:run(component_connected, [H]) end, dict:fetch_keys(HostOpts)), State. handle_auth_failure(_, Mech, Reason, #{remote_server := RemoteServer, socket := Socket, ip := IP} = State) -> ?INFO_MSG("(~s) Failed external component ~s authentication " "for ~s from ~s: ~s", [xmpp_socket:pp(Socket), Mech, RemoteServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]), State. handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State) when ?is_stanza(Pkt0) -> Pkt = xmpp:put_meta(Pkt0, ip, IP), From = xmpp:get_from(Pkt), case check_from(From, State) of true -> ejabberd_router:route(Pkt), State; false -> Txt = <<"Improper domain part of 'from' attribute">>, Err = xmpp:serr_invalid_from(Txt, Lang), xmpp_stream_in:send(State, Err) end; handle_authenticated_packet(_Pkt, State) -> State. handle_info({route, Packet}, #{access := Access} = State) -> case acl:match_rule(global, Access, xmpp:get_from(Packet)) of allow -> xmpp_stream_in:send(State, Packet); deny -> Lang = xmpp:get_lang(Packet), Err = xmpp:err_not_allowed(<<"Access denied by service policy">>, Lang), ejabberd_router:route_error(Packet, Err), State end; handle_info(Info, State) -> ?ERROR_MSG("Unexpected info: ~p", [Info]), State. terminate(Reason, #{stream_state := StreamState, host_opts := HostOpts}) -> case StreamState of established -> lists:foreach( fun(H) -> ejabberd_router:unregister_route(H), ejabberd_hooks:run(component_disconnected, [H, Reason]) end, dict:fetch_keys(HostOpts)); _ -> ok end. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec check_from(jid(), state()) -> boolean(). check_from(_From, #{check_from := false}) -> %% If the admin does not want to check the from field %% when accept packets from any address. %% In this case, the component can send packet of %% behalf of the server users. true; check_from(From, #{host_opts := HostOpts}) -> %% The default is the standard behaviour in XEP-0114 Server = From#jid.lserver, dict:is_key(Server, HostOpts). random_password() -> str:sha(randoms:bytes(20)). transform_listen_option({hosts, Hosts, O}, Opts) -> case lists:keyfind(hosts, 1, Opts) of {_, PrevHostOpts} -> NewHostOpts = lists:foldl( fun(H, Acc) -> dict:append_list(H, O, Acc) end, dict:from_list(PrevHostOpts), Hosts), [{hosts, dict:to_list(NewHostOpts)}| lists:keydelete(hosts, 1, Opts)]; _ -> [{hosts, [{H, O} || H <- Hosts]}|Opts] end; transform_listen_option({host, Host, Os}, Opts) -> transform_listen_option({hosts, [Host], Os}, Opts); transform_listen_option(Opt, Opts) -> [Opt|Opts]. -spec listen_opt_type(access) -> fun((any()) -> any()); (shaper_rule) -> fun((any()) -> any()); (certfile) -> fun((binary()) -> binary()); (ciphers) -> fun((binary()) -> binary()); (dhfile) -> fun((binary()) -> binary()); (cafile) -> fun((binary()) -> binary()); (protocol_options) -> fun(([binary()]) -> binary()); (tls_compression) -> fun((boolean()) -> boolean()); (tls) -> fun((boolean()) -> boolean()); (check_from) -> fun((boolean()) -> boolean()); (password) -> fun((boolean()) -> boolean()); (hosts) -> fun(([{binary(), [{password, binary()}]}]) -> [{binary(), binary() | undefined}]); (max_stanza_type) -> fun((timeout()) -> timeout()); (max_fsm_queue) -> fun((pos_integer()) -> pos_integer()); (atom()) -> [atom()]. listen_opt_type(access) -> fun acl:access_rules_validator/1; listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1; listen_opt_type(certfile) -> fun(S) -> ejabberd_pkix:add_certfile(S), iolist_to_binary(S) end; listen_opt_type(ciphers) -> fun iolist_to_binary/1; listen_opt_type(dhfile) -> fun misc:try_read_file/1; listen_opt_type(cafile) -> fun misc:try_read_file/1; listen_opt_type(protocol_options) -> fun(Options) -> str:join(Options, <<"|">>) end; listen_opt_type(tls_compression) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(check_from) -> fun(B) when is_boolean(B) -> B end; listen_opt_type(password) -> fun iolist_to_binary/1; listen_opt_type(hosts) -> fun(HostOpts) -> lists:map( fun({Host, Opts}) -> Password = case proplists:get_value(password, Opts) of undefined -> undefined; P -> iolist_to_binary(P) end, {iolist_to_binary(Host), Password} end, HostOpts) end; listen_opt_type(max_stanza_size) -> fun(I) when is_integer(I) -> I; (unlimited) -> infinity; (infinity) -> infinity end; listen_opt_type(max_fsm_queue) -> fun(I) when is_integer(I), I>0 -> I end; listen_opt_type(_) -> [access, shaper_rule, certfile, ciphers, dhfile, cafile, tls, protocol_options, tls_compression, password, hosts, check_from, max_fsm_queue]. ejabberd-18.01/src/mod_muc_riak.erl0000644000232200023220000001476613225664356017605 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_muc_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_muc_riak). -behaviour(mod_muc). -behaviour(mod_muc_room). %% API -export([init/2, import/3, store_room/5, restore_room/3, forget_room/3, can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]). -export([register_online_room/4, unregister_online_room/4, find_online_room/3, get_online_rooms/3, count_online_rooms/2, rsm_supported/0, register_online_user/4, unregister_online_user/4, count_online_rooms_by_user/3, get_online_rooms_by_user/3, get_subscribed_rooms/3]). -export([set_affiliation/6, set_affiliations/4, get_affiliation/5, get_affiliations/3, search_affiliation/4]). -include("jid.hrl"). -include("mod_muc.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. store_room(_LServer, Host, Name, Opts, _) -> {atomic, ejabberd_riak:put(#muc_room{name_host = {Name, Host}, opts = Opts}, muc_room_schema())}. restore_room(_LServer, Host, Name) -> case ejabberd_riak:get(muc_room, muc_room_schema(), {Name, Host}) of {ok, #muc_room{opts = Opts}} -> Opts; _ -> error end. forget_room(_LServer, Host, Name) -> {atomic, ejabberd_riak:delete(muc_room, {Name, Host})}. can_use_nick(_LServer, Host, JID, Nick) -> {LUser, LServer, _} = jid:tolower(JID), LUS = {LUser, LServer}, case ejabberd_riak:get_by_index(muc_registered, muc_registered_schema(), <<"nick_host">>, {Nick, Host}) of {ok, []} -> true; {ok, [#muc_registered{us_host = {U, _Host}}]} -> U == LUS; {error, _} -> true end. get_rooms(_LServer, Host) -> case ejabberd_riak:get(muc_room, muc_room_schema()) of {ok, Rs} -> lists:filter( fun(#muc_room{name_host = {_, H}}) -> Host == H end, Rs); _Err -> [] end. get_nick(LServer, Host, From) -> {LUser, LServer, _} = jid:tolower(From), US = {LUser, LServer}, case ejabberd_riak:get(muc_registered, muc_registered_schema(), {US, Host}) of {ok, #muc_registered{nick = Nick}} -> Nick; {error, _} -> error end. set_nick(LServer, Host, From, Nick) -> {LUser, LServer, _} = jid:tolower(From), LUS = {LUser, LServer}, {atomic, case Nick of <<"">> -> ejabberd_riak:delete(muc_registered, {LUS, Host}); _ -> Allow = case ejabberd_riak:get_by_index( muc_registered, muc_registered_schema(), <<"nick_host">>, {Nick, Host}) of {ok, []} -> true; {ok, [#muc_registered{us_host = {U, _Host}}]} -> U == LUS; {error, _} -> false end, if Allow -> ejabberd_riak:put(#muc_registered{us_host = {LUS, Host}, nick = Nick}, muc_registered_schema(), [{'2i', [{<<"nick_host">>, {Nick, Host}}]}]); true -> false end end}. set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) -> {error, not_implemented}. set_affiliations(_ServerHost, _Room, _Host, _Affiliations) -> {error, not_implemented}. get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) -> {error, not_implemented}. get_affiliations(_ServerHost, _Room, _Host) -> {error, not_implemented}. search_affiliation(_ServerHost, _Room, _Host, _Affiliation) -> {error, not_implemented}. register_online_room(_, _, _, _) -> erlang:error(not_implemented). unregister_online_room(_, _, _, _) -> erlang:error(not_implemented). find_online_room(_, _, _) -> erlang:error(not_implemented). count_online_rooms(_, _) -> erlang:error(not_implemented). get_online_rooms(_, _, _) -> erlang:error(not_implemented). rsm_supported() -> false. register_online_user(_, _, _, _) -> erlang:error(not_implemented). unregister_online_user(_, _, _, _) -> erlang:error(not_implemented). count_online_rooms_by_user(_, _, _) -> erlang:error(not_implemented). get_online_rooms_by_user(_, _, _) -> erlang:error(not_implemented). import(_LServer, <<"muc_room">>, [Name, RoomHost, SOpts, _TimeStamp]) -> Opts = mod_muc:opts_to_binary(ejabberd_sql:decode_term(SOpts)), ejabberd_riak:put( #muc_room{name_host = {Name, RoomHost}, opts = Opts}, muc_room_schema()); import(_LServer, <<"muc_registered">>, [J, RoomHost, Nick, _TimeStamp]) -> #jid{user = U, server = S} = jid:decode(J), R = #muc_registered{us_host = {{U, S}, RoomHost}, nick = Nick}, ejabberd_riak:put(R, muc_registered_schema(), [{'2i', [{<<"nick_host">>, {Nick, RoomHost}}]}]). get_subscribed_rooms(_, _, _) -> not_implemented. %%%=================================================================== %%% Internal functions %%%=================================================================== muc_room_schema() -> {record_info(fields, muc_room), #muc_room{}}. muc_registered_schema() -> {record_info(fields, muc_registered), #muc_registered{}}. ejabberd-18.01/src/mod_proxy65_lib.erl0000644000232200023220000000501113225664356020154 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_proxy65_lib.erl %%% Author : Evgeniy Khramtsov %%% Purpose : SOCKS5 parsing library. %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_proxy65_lib). -author('xram@jabber.ru'). -include("mod_proxy65.hrl"). -export([unpack_init_message/1, unpack_auth_request/1, unpack_request/1, make_init_reply/1, make_auth_reply/1, make_reply/1, make_error_reply/1, make_error_reply/2]). unpack_init_message(<<(?VERSION_5), N, AuthMethodList:N/binary>>) when N > 0, N < 256 -> {ok, binary_to_list(AuthMethodList)}; unpack_init_message(_) -> error. unpack_auth_request(<<1, ULen, User:ULen/binary, PLen, Pass:PLen/binary>>) when ULen < 256, PLen < 256 -> {(User), (Pass)}; unpack_auth_request(_) -> error. unpack_request(<<(?VERSION_5), CMD, RSV, (?ATYP_DOMAINNAME), 40, SHA1:40/binary, 0, 0>>) when CMD == (?CMD_CONNECT); CMD == (?CMD_UDP) -> Command = if CMD == (?CMD_CONNECT) -> connect; CMD == (?CMD_UDP) -> udp end, #s5_request{cmd = Command, rsv = RSV, sha1 = (SHA1)}; unpack_request(_) -> error. make_init_reply(Method) -> [?VERSION_5, Method]. make_auth_reply(true) -> [1, ?SUCCESS]; make_auth_reply(false) -> [1, ?ERR_NOT_ALLOWED]. make_reply(#s5_request{rsv = RSV, sha1 = SHA1}) -> [?VERSION_5, ?SUCCESS, RSV, ?ATYP_DOMAINNAME, byte_size(SHA1), SHA1, 0, 0]. make_error_reply(Request) -> make_error_reply(Request, ?ERR_NOT_ALLOWED). make_error_reply(#s5_request{rsv = RSV, sha1 = SHA1}, Reason) -> [?VERSION_5, Reason, RSV, ?ATYP_DOMAINNAME, byte_size(SHA1), SHA1, 0, 0]. ejabberd-18.01/src/eldap_filter_yecc.yrl0000644000232200023220000000541413225664356020623 0ustar debalancedebalanceNonterminals filter filtercomp filterlist item simple present substring extensible initial any final matchingrule xattr attr value. Terminals str '(' ')' '&' '|' '!' '=' '~=' '>=' '<=' '=*' '*' ':dn' ':' ':='. Rootsymbol filter. filter -> '(' filtercomp ')': '$2'. filtercomp -> '&' filterlist: 'and'('$2'). filtercomp -> '|' filterlist: 'or'('$2'). filtercomp -> '!' filter: 'not'('$2'). filtercomp -> item: '$1'. filterlist -> filter: '$1'. filterlist -> filter filterlist: flatten(['$1', '$2']). item -> simple: '$1'. item -> present: '$1'. item -> substring: '$1'. item -> extensible: '$1'. simple -> attr '=' value: equal('$1', '$3'). simple -> attr '~=' value: approx('$1', '$3'). simple -> attr '>=' value: greater('$1', '$3'). simple -> attr '<=' value: less('$1', '$3'). present -> attr '=*': present('$1'). substring -> attr '=' initial '*' any: substrings('$1', ['$3', '$5']). substring -> attr '=' '*' any final: substrings('$1', ['$4', '$5']). substring -> attr '=' initial '*' any final: substrings('$1', ['$3', '$5', '$6']). substring -> attr '=' '*' any: substrings('$1', ['$4']). any -> any value '*': 'any'('$1', '$2'). any -> '$empty': []. initial -> value: initial('$1'). final -> value: final('$1'). extensible -> xattr ':dn' ':' matchingrule ':=' value: extensible('$6', ['$1', '$4', {dnAttributes, true}]). extensible -> xattr ':' matchingrule ':=' value: extensible('$5', ['$1', '$3']). extensible -> xattr ':dn' ':=' value: extensible('$4', ['$1', {dnAttributes, true}]). extensible -> xattr ':=' value: extensible('$3', ['$1']). extensible -> ':dn' ':' matchingrule ':=' value: extensible('$5', ['$3']). extensible -> ':' matchingrule ':=' value: extensible('$4', ['$2']). xattr -> value: xattr('$1'). matchingrule -> value: matchingrule('$1'). attr -> str: value_of('$1'). value -> str: value_of('$1'). Erlang code. 'and'(Value) -> eldap:'and'(Value). 'or'(Value) -> eldap:'or'(Value). 'not'(Value) -> eldap:'not'(Value). equal(Desc, Value) -> eldap:equalityMatch(Desc, Value). approx(Desc, Value) -> eldap:approxMatch(Desc, Value). greater(Desc, Value) -> eldap:greaterOrEqual(Desc, Value). less(Desc, Value) -> eldap:lessOrEqual(Desc, Value). present(Value) -> eldap:present(Value). extensible(Value, Opts) -> eldap:extensibleMatch(Value, Opts). substrings(Desc, ValueList) -> eldap:substrings(Desc, flatten(ValueList)). initial(Value) -> {initial, Value}. final(Value) -> {final, Value}. 'any'(Token, Value) -> [Token, {any, Value}]. xattr(Value) -> {type, Value}. matchingrule(Value) -> {matchingRule, Value}. value_of(Token) -> iolist_to_binary(element(3, Token)). flatten(List) -> lists:flatten(List). ejabberd-18.01/src/node_hometree_sql.erl0000644000232200023220000001311013225664356020626 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_hometree_sql.erl %%% Author : Christophe Romain %%% Purpose : Standard tree ordered node plugin with ODBC backend %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_hometree_sql). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, get_entity_subscriptions_for_send_last/2, get_last_items/3]). init(Host, ServerHost, Opts) -> node_flat_sql:init(Host, ServerHost, Opts), Owner = mod_pubsub:service_jid(Host), mod_pubsub:create_node(Host, ServerHost, <<"/home">>, Owner, <<"hometree">>), mod_pubsub:create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, Owner, <<"hometree">>), ok. terminate(Host, ServerHost) -> node_flat_sql:terminate(Host, ServerHost). options() -> [{sql, true}, {rsm, true} | node_hometree:options()]. features() -> [<<"rsm">> | node_hometree:features()]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat_sql:create_node(Nidx, Owner). delete_node(Nodes) -> node_flat_sql:delete_node(Nodes). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat_sql:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat_sql:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat_sql:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat_sql:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat_sql:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat_sql:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat_sql:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat_sql:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat_sql:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat_sql:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat_sql:get_entity_subscriptions(Host, Owner). get_entity_subscriptions_for_send_last(Host, Owner) -> node_flat_sql:get_entity_subscriptions_for_send_last(Host, Owner). get_node_subscriptions(Nidx) -> node_flat_sql:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat_sql:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat_sql:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat_sql:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat_sql:get_states(Nidx). get_state(Nidx, JID) -> node_flat_sql:get_state(Nidx, JID). set_state(State) -> node_flat_sql:set_state(State). get_items(Nidx, From, RSM) -> node_flat_sql:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat_sql:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_item(Nidx, ItemId) -> node_flat_sql:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat_sql:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat_sql:set_item(Item). get_item_name(Host, Node, Id) -> node_flat_sql:get_item_name(Host, Node, Id). get_last_items(Nidx, From, Count) -> node_flat_sql:get_last_items(Nidx, From, Count). node_to_path(Node) -> node_hometree:node_to_path(Node). path_to_node(Path) -> node_hometree:path_to_node(Path). ejabberd-18.01/src/mod_privacy_riak.erl0000644000232200023220000001035113225664356020460 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_privacy_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_privacy_riak). -behaviour(mod_privacy). %% API -export([init/2, set_default/3, unset_default/2, set_lists/1, set_list/4, get_lists/2, get_list/3, remove_lists/2, remove_list/3, import/1]). -export([privacy_schema/0]). -include("xmpp.hrl"). -include("mod_privacy.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. unset_default(LUser, LServer) -> case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of {ok, R} -> ejabberd_riak:put(R#privacy{default = none}, privacy_schema()); {error, notfound} -> ok; Err -> Err end. set_default(LUser, LServer, Name) -> case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of {ok, #privacy{lists = Lists} = P} -> case lists:keymember(Name, 1, Lists) of true -> ejabberd_riak:put(P#privacy{default = Name, lists = Lists}, privacy_schema()); false -> {error, notfound} end; Err -> Err end. remove_list(LUser, LServer, Name) -> case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of {ok, #privacy{default = Default, lists = Lists} = P} -> if Name == Default -> {error, conflict}; true -> NewLists = lists:keydelete(Name, 1, Lists), ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema()) end; Err -> Err end. set_lists(Privacy) -> ejabberd_riak:put(Privacy, privacy_schema()). set_list(LUser, LServer, Name, List) -> case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of {ok, #privacy{lists = Lists} = P} -> NewLists1 = lists:keydelete(Name, 1, Lists), NewLists = [{Name, List} | NewLists1], ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema()); {error, notfound} -> NewLists = [{Name, List}], ejabberd_riak:put(#privacy{us = {LUser, LServer}, lists = NewLists}, privacy_schema()); Err -> Err end. get_list(LUser, LServer, Name) -> case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of {ok, #privacy{default = Default, lists = Lists}} when Name == default -> case lists:keyfind(Default, 1, Lists) of {_, List} -> {ok, {Default, List}}; false -> error end; {ok, #privacy{lists = Lists}} -> case lists:keyfind(Name, 1, Lists) of {_, List} -> {ok, {Name, List}}; false -> error end; {error, notfound} -> error; Err -> Err end. get_lists(LUser, LServer) -> case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of {ok, #privacy{} = P} -> {ok, P}; {error, notfound} -> error; Err -> Err end. remove_lists(LUser, LServer) -> ejabberd_riak:delete(privacy, {LUser, LServer}). import(#privacy{} = P) -> ejabberd_riak:put(P, privacy_schema()). %%%=================================================================== %%% Internal functions %%%=================================================================== privacy_schema() -> {record_info(fields, privacy), #privacy{}}. ejabberd-18.01/src/mod_version.erl0000644000232200023220000000574713225664356017477 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_version.erl %%% Author : Alexey Shchepin %%% Purpose : XEP-0092: Software Version %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_version). -author('alexey@process-one.net'). -protocol({xep, 92, '1.1'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_local_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VERSION, ?MODULE, process_local_iq, IQDisc). stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VERSION). reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VERSION, ?MODULE, process_local_iq, IQDisc); true -> ok end. process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_local_iq(#iq{type = get, to = To} = IQ) -> Host = To#jid.lserver, OS = case gen_mod:get_module_opt(Host, ?MODULE, show_os, true) of true -> get_os(); false -> undefined end, xmpp:make_iq_result(IQ, #version{name = <<"ejabberd">>, ver = ?VERSION, os = OS}). get_os() -> {Osfamily, Osname} = os:type(), OSType = list_to_binary([atom_to_list(Osfamily), $/, atom_to_list(Osname)]), OSVersion = case os:version() of {Major, Minor, Release} -> (str:format("~w.~w.~w", [Major, Minor, Release])); VersionString -> VersionString end, <>. depends(_Host, _Opts) -> []. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(show_os) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [iqdisc, show_os]. ejabberd-18.01/src/mod_muc.erl0000644000232200023220000011540513225664356016567 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_muc.erl %%% Author : Alexey Shchepin %%% Purpose : MUC support (XEP-0045) %%% Created : 19 Mar 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_muc). -author('alexey@process-one.net'). -protocol({xep, 45, '1.25'}). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, room_destroyed/4, store_room/4, store_room/5, restore_room/3, forget_room/3, create_room/5, shutdown_rooms/1, process_disco_info/1, process_disco_items/1, process_vcard/1, process_register/1, process_muc_unique/1, process_mucsub/1, broadcast_service_message/3, export/1, import_info/0, import/5, import_start/2, opts_to_binary/1, find_online_room/2, register_online_room/3, get_online_rooms/1, count_online_rooms/1, register_online_user/4, unregister_online_user/4, iq_set_register_info/5, count_online_rooms_by_user/3, get_online_rooms_by_user/3, can_use_nick/4]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_muc.hrl"). -include("translate.hrl"). -record(state, {hosts = [] :: [binary()], server_host = <<"">> :: binary(), access = {none, none, none, none} :: {atom(), atom(), atom(), atom()}, history_size = 20 :: non_neg_integer(), max_rooms_discoitems = 100 :: non_neg_integer(), queue_type = ram :: ram | file, default_room_opts = [] :: list(), room_shaper = none :: shaper:shaper()}). -type muc_room_opts() :: [{atom(), any()}]. -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), binary(), [binary()]) -> ok. -callback store_room(binary(), binary(), binary(), list(), list()|undefined) -> {atomic, any()}. -callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error. -callback forget_room(binary(), binary(), binary()) -> {atomic, any()}. -callback can_use_nick(binary(), binary(), jid(), binary()) -> boolean(). -callback get_rooms(binary(), binary()) -> [#muc_room{}]. -callback get_nick(binary(), binary(), jid()) -> binary() | error. -callback set_nick(binary(), binary(), jid(), binary()) -> {atomic, ok | false}. -callback register_online_room(binary(), binary(), binary(), pid()) -> any(). -callback unregister_online_room(binary(), binary(), binary(), pid()) -> any(). -callback find_online_room(binary(), binary(), binary()) -> {ok, pid()} | error. -callback get_online_rooms(binary(), binary(), undefined | rsm_set()) -> [{binary(), binary(), pid()}]. -callback count_online_rooms(binary(), binary()) -> non_neg_integer(). -callback rsm_supported() -> boolean(). -callback register_online_user(binary(), ljid(), binary(), binary()) -> any(). -callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any(). -callback count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer(). -callback get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}]. -callback get_subscribed_rooms(binary(), binary(), jid()) -> {ok, [{ljid(), binary(), [binary()]}]} | {error, any()}. %%==================================================================== %% API %%==================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> Rooms = shutdown_rooms(Host), gen_mod:stop_child(?MODULE, Host), {wait, Rooms}. reload(Host, NewOpts, OldOpts) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). depends(_Host, _Opts) -> [{mod_mam, soft}]. shutdown_rooms(Host) -> RMod = gen_mod:ram_db_mod(Host, ?MODULE), MyHost = gen_mod:get_module_opt_host(Host, mod_muc, <<"conference.@HOST@">>), Rooms = RMod:get_online_rooms(Host, MyHost, undefined), lists:flatmap( fun({_, _, Pid}) when node(Pid) == node() -> Pid ! shutdown, [Pid]; (_) -> [] end, Rooms). %% This function is called by a room in three situations: %% A) The owner of the room destroyed it %% B) The only participant of a temporary room leaves it %% C) mod_muc:stop was called, and each room is being terminated %% In this case, the mod_muc process died before the room processes %% So the message sending must be catched room_destroyed(Host, Room, Pid, ServerHost) -> catch gen_mod:get_module_proc(ServerHost, ?MODULE) ! {room_destroyed, {Room, Host}, Pid}, ok. %% @doc Create a room. %% If Opts = default, the default room options are used. %% Else use the passed options as defined in mod_muc_room. create_room(Host, Name, From, Nick, Opts) -> ServerHost = ejabberd_router:host_of_route(Host), Proc = gen_mod:get_module_proc(ServerHost, ?MODULE), gen_server:call(Proc, {create, Name, Host, From, Nick, Opts}). store_room(ServerHost, Host, Name, Opts) -> store_room(ServerHost, Host, Name, Opts, undefined). store_room(ServerHost, Host, Name, Opts, ChangesHints) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:store_room(LServer, Host, Name, Opts, ChangesHints). restore_room(ServerHost, Host, Name) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:restore_room(LServer, Host, Name). forget_room(ServerHost, Host, Name) -> LServer = jid:nameprep(ServerHost), ejabberd_hooks:run(remove_room, LServer, [LServer, Name, Host]), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:forget_room(LServer, Host, Name). can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false; can_use_nick(ServerHost, Host, JID, Nick) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:can_use_nick(LServer, Host, JID, Nick). -spec find_online_room(binary(), binary()) -> {ok, pid()} | error. find_online_room(Room, Host) -> ServerHost = ejabberd_router:host_of_route(Host), RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:find_online_room(ServerHost, Room, Host). -spec register_online_room(binary(), binary(), pid()) -> any(). register_online_room(Room, Host, Pid) -> ServerHost = ejabberd_router:host_of_route(Host), RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:register_online_room(ServerHost, Room, Host, Pid). -spec get_online_rooms(binary()) -> [{binary(), binary(), pid()}]. get_online_rooms(Host) -> ServerHost = ejabberd_router:host_of_route(Host), get_online_rooms(ServerHost, Host). -spec count_online_rooms(binary()) -> non_neg_integer(). count_online_rooms(Host) -> ServerHost = ejabberd_router:host_of_route(Host), count_online_rooms(ServerHost, Host). -spec register_online_user(binary(), ljid(), binary(), binary()) -> any(). register_online_user(ServerHost, LJID, Name, Host) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:register_online_user(ServerHost, LJID, Name, Host). -spec unregister_online_user(binary(), ljid(), binary(), binary()) -> any(). unregister_online_user(ServerHost, LJID, Name, Host) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:unregister_online_user(ServerHost, LJID, Name, Host). -spec count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer(). count_online_rooms_by_user(ServerHost, LUser, LServer) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:count_online_rooms_by_user(ServerHost, LUser, LServer). -spec get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}]. get_online_rooms_by_user(ServerHost, LUser, LServer) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:get_online_rooms_by_user(ServerHost, LUser, LServer). %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), #state{access = Access, hosts = MyHosts, history_size = HistorySize, queue_type = QueueType, room_shaper = RoomShaper} = State = init_state(Host, Opts), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), RMod = gen_mod:ram_db_mod(Host, Opts, ?MODULE), Mod:init(Host, [{hosts, MyHosts}|Opts]), RMod:init(Host, [{hosts, MyHosts}|Opts]), lists:foreach( fun(MyHost) -> register_iq_handlers(MyHost, IQDisc), ejabberd_router:register_route(MyHost, Host), load_permanent_rooms(MyHost, Host, Access, HistorySize, RoomShaper, QueueType) end, MyHosts), {ok, State}. handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call({create, Room, Host, From, Nick, Opts}, _From, #state{server_host = ServerHost, access = Access, default_room_opts = DefOpts, history_size = HistorySize, queue_type = QueueType, room_shaper = RoomShaper} = State) -> ?DEBUG("MUC: create new room '~s'~n", [Room]), NewOpts = case Opts of default -> DefOpts; _ -> Opts end, {ok, Pid} = mod_muc_room:start( Host, ServerHost, Access, Room, HistorySize, RoomShaper, From, Nick, NewOpts, QueueType), RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:register_online_room(ServerHost, Room, Host, Pid), ejabberd_hooks:run(create_room, ServerHost, [ServerHost, Room, Host]), {reply, ok, State}. handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) -> NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)), OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)), NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE), NewRMod = gen_mod:ram_db_mod(ServerHost, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE), OldRMod = gen_mod:ram_db_mod(ServerHost, OldOpts, ?MODULE), #state{hosts = NewHosts} = NewState = init_state(ServerHost, NewOpts), if NewMod /= OldMod -> NewMod:init(ServerHost, [{hosts, NewHosts}|NewOpts]); true -> ok end, if NewRMod /= OldRMod -> NewRMod:init(ServerHost, [{hosts, NewHosts}|NewOpts]); true -> ok end, if (NewIQDisc /= OldIQDisc) -> lists:foreach( fun(NewHost) -> register_iq_handlers(NewHost, NewIQDisc) end, NewHosts -- (NewHosts -- OldHosts)); true -> ok end, lists:foreach( fun(NewHost) -> ejabberd_router:register_route(NewHost, ServerHost), register_iq_handlers(NewHost, NewIQDisc) end, NewHosts -- OldHosts), lists:foreach( fun(OldHost) -> ejabberd_router:unregister_route(OldHost), unregister_iq_handlers(OldHost) end, OldHosts -- NewHosts), {noreply, NewState}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. handle_info({route, Packet}, #state{server_host = ServerHost, access = Access, default_room_opts = DefRoomOpts, history_size = HistorySize, queue_type = QueueType, max_rooms_discoitems = MaxRoomsDiscoItems, room_shaper = RoomShaper} = State) -> From = xmpp:get_from(Packet), To = xmpp:get_to(Packet), Host = To#jid.lserver, case catch do_route(Host, ServerHost, Access, HistorySize, RoomShaper, From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems, QueueType) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); _ -> ok end, {noreply, State}; handle_info({room_destroyed, {Room, Host}, Pid}, State) -> ServerHost = State#state.server_host, RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:unregister_online_room(ServerHost, Room, Host, Pid), {noreply, State}; handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, #state{hosts = MyHosts}) -> lists:foreach( fun(MyHost) -> ejabberd_router:unregister_route(MyHost), unregister_iq_handlers(MyHost) end, MyHosts). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- init_state(Host, Opts) -> MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"conference.@HOST@">>), Access = gen_mod:get_opt(access, Opts, all), AccessCreate = gen_mod:get_opt(access_create, Opts, all), AccessAdmin = gen_mod:get_opt(access_admin, Opts, none), AccessPersistent = gen_mod:get_opt(access_persistent, Opts, all), HistorySize = gen_mod:get_opt(history_size, Opts, 20), MaxRoomsDiscoItems = gen_mod:get_opt(max_rooms_discoitems, Opts, 100), DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []), QueueType = gen_mod:get_opt(queue_type, Opts, ejabberd_config:default_queue_type(Host)), RoomShaper = gen_mod:get_opt(room_shaper, Opts, none), #state{hosts = MyHosts, server_host = Host, access = {Access, AccessCreate, AccessAdmin, AccessPersistent}, default_room_opts = DefRoomOpts, queue_type = QueueType, history_size = HistorySize, max_rooms_discoitems = MaxRoomsDiscoItems, room_shaper = RoomShaper}. register_iq_handlers(Host, IQDisc) -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, ?MODULE, process_register, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUCSUB, ?MODULE, process_mucsub, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUC_UNIQUE, ?MODULE, process_muc_unique, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, ?MODULE, process_disco_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_disco_items, IQDisc). unregister_iq_handlers(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MUCSUB), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MUC_UNIQUE), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS). do_route(Host, ServerHost, Access, HistorySize, RoomShaper, From, To, Packet, DefRoomOpts, _MaxRoomsDiscoItems, QueueType) -> {AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent} = Access, case acl:match_rule(ServerHost, AccessRoute, From) of allow -> do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, From, To, Packet, DefRoomOpts, QueueType); deny -> Lang = xmpp:get_lang(Packet), ErrText = <<"Access denied by service policy">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end. do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper, _From, #jid{luser = <<"">>, lresource = <<"">>} = _To, #iq{} = IQ, _DefRoomOpts, _QueueType) -> ejabberd_local:process_iq(IQ); do_route1(Host, ServerHost, Access, _HistorySize, _RoomShaper, From, #jid{luser = <<"">>, lresource = <<"">>} = _To, #message{lang = Lang, body = Body, type = Type} = Packet, _, _) -> {_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = Access, if Type == error -> ok; true -> case acl:match_rule(ServerHost, AccessAdmin, From) of allow -> Msg = xmpp:get_text(Body), broadcast_service_message(ServerHost, Host, Msg); deny -> ErrText = <<"Only service administrators are allowed " "to send service messages">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end end; do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper, _From, #jid{luser = <<"">>} = _To, Packet, _DefRoomOpts, _) -> Err = xmpp:err_service_unavailable(), ejabberd_router:route_error(Packet, Err); do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, From, To, Packet, DefRoomOpts, QueueType) -> {_AccessRoute, AccessCreate, _AccessAdmin, _AccessPersistent} = Access, {Room, _, Nick} = jid:tolower(To), RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), case RMod:find_online_room(ServerHost, Room, Host) of error -> case is_create_request(Packet) of true -> case check_user_can_create_room( ServerHost, AccessCreate, From, Room) and check_create_roomid(ServerHost, Room) of true -> {ok, Pid} = start_new_room( Host, ServerHost, Access, Room, HistorySize, RoomShaper, From, Nick, DefRoomOpts, QueueType), RMod:register_online_room(ServerHost, Room, Host, Pid), mod_muc_room:route(Pid, Packet), ok; false -> Lang = xmpp:get_lang(Packet), ErrText = <<"Room creation is denied by service policy">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end; false -> Lang = xmpp:get_lang(Packet), ErrText = <<"Conference room does not exist">>, Err = xmpp:err_item_not_found(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end; {ok, Pid} -> ?DEBUG("MUC: send to process ~p~n", [Pid]), mod_muc_room:route(Pid, Packet), ok end. -spec process_vcard(iq()) -> iq(). process_vcard(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]} = IQ) -> Desc = translate:translate(Lang, <<"ejabberd MUC module">>), xmpp:make_iq_result( IQ, #vcard_temp{fn = <<"ejabberd/mod_muc">>, url = ?EJABBERD_URI, desc = <>}); process_vcard(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_vcard(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_register(iq()) -> iq(). process_register(#iq{type = get, from = From, to = To, lang = Lang, sub_els = [#register{}]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang)); process_register(#iq{type = set, from = From, to = To, lang = Lang, sub_els = [El = #register{}]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), case process_iq_register_set(ServerHost, Host, From, El, Lang) of {result, Result} -> xmpp:make_iq_result(IQ, Result); {error, Err} -> xmpp:make_error(IQ, Err) end. -spec process_disco_info(iq()) -> iq(). process_disco_info(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_info(#iq{type = get, to = To, lang = Lang, sub_els = [#disco_info{node = <<"">>}]} = IQ) -> ServerHost = ejabberd_router:host_of_route(To#jid.lserver), RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), X = ejabberd_hooks:run_fold(disco_info, ServerHost, [], [ServerHost, ?MODULE, <<"">>, Lang]), MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of true -> [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1, ?NS_MAM_2]; false -> [] end, RSMFeatures = case RMod:rsm_supported() of true -> [?NS_RSM]; false -> [] end, Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_REGISTER, ?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE | RSMFeatures ++ MAMFeatures], Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("Chatrooms")), Identity = #identity{category = <<"conference">>, type = <<"text">>, name = translate:translate(Lang, Name)}, xmpp:make_iq_result( IQ, #disco_info{features = Features, identities = [Identity], xdata = X}); process_disco_info(#iq{type = get, lang = Lang, sub_els = [#disco_info{}]} = IQ) -> xmpp:make_error(IQ, xmpp:err_item_not_found(<<"Node not found">>, Lang)); process_disco_info(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_disco_items(iq()) -> iq(). process_disco_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_disco_items(#iq{type = get, from = From, to = To, lang = Lang, sub_els = [#disco_items{node = Node, rsm = RSM}]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), MaxRoomsDiscoItems = gen_mod:get_module_opt( ServerHost, ?MODULE, max_rooms_discoitems, 100), case iq_disco_items(ServerHost, Host, From, Lang, MaxRoomsDiscoItems, Node, RSM) of {error, Err} -> xmpp:make_error(IQ, Err); {result, Result} -> xmpp:make_iq_result(IQ, Result) end; process_disco_items(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_muc_unique(iq()) -> iq(). process_muc_unique(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_muc_unique(#iq{from = From, type = get, sub_els = [#muc_unique{}]} = IQ) -> Name = str:sha(term_to_binary([From, p1_time_compat:timestamp(), randoms:get_string()])), xmpp:make_iq_result(IQ, #muc_unique{name = Name}). -spec process_mucsub(iq()) -> iq(). process_mucsub(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_mucsub(#iq{type = get, from = From, to = To, sub_els = [#muc_subscriptions{}]} = IQ) -> Host = To#jid.lserver, ServerHost = ejabberd_router:host_of_route(Host), RoomJIDs = get_subscribed_rooms(ServerHost, Host, From), xmpp:make_iq_result(IQ, #muc_subscriptions{list = RoomJIDs}); process_mucsub(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec is_create_request(stanza()) -> boolean(). is_create_request(#presence{type = available}) -> true; is_create_request(#iq{type = T} = IQ) when T == get; T == set -> xmpp:has_subtag(IQ, #muc_subscribe{}) orelse xmpp:has_subtag(IQ, #muc_owner{}); is_create_request(_) -> false. check_user_can_create_room(ServerHost, AccessCreate, From, _RoomID) -> case acl:match_rule(ServerHost, AccessCreate, From) of allow -> true; _ -> false end. check_create_roomid(ServerHost, RoomID) -> Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id, infinity), Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id, ""), (byte_size(RoomID) =< Max) and (re:run(RoomID, Regexp, [unicode, {capture, none}]) == match). get_rooms(ServerHost, Host) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:get_rooms(LServer, Host). load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper, QueueType) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), lists:foreach( fun(R) -> {Room, Host} = R#muc_room.name_host, case RMod:find_online_room(ServerHost, Room, Host) of error -> {ok, Pid} = mod_muc_room:start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, R#muc_room.opts, QueueType), RMod:register_online_room(ServerHost, Room, Host, Pid); {ok, _} -> ok end end, get_rooms(ServerHost, Host)). start_new_room(Host, ServerHost, Access, Room, HistorySize, RoomShaper, From, Nick, DefRoomOpts, QueueType) -> case restore_room(ServerHost, Host, Room) of error -> ?DEBUG("MUC: open new room '~s'~n", [Room]), mod_muc_room:start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, From, Nick, DefRoomOpts, QueueType); Opts -> ?DEBUG("MUC: restore room '~s'~n", [Room]), mod_muc_room:start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType) end. -spec iq_disco_items(binary(), binary(), jid(), binary(), integer(), binary(), rsm_set() | undefined) -> {result, disco_items()} | {error, stanza_error()}. iq_disco_items(ServerHost, Host, From, Lang, MaxRoomsDiscoItems, Node, RSM) when Node == <<"">>; Node == <<"nonemptyrooms">>; Node == <<"emptyrooms">> -> Count = count_online_rooms(ServerHost, Host), Query = if Node == <<"">>, RSM == undefined, Count > MaxRoomsDiscoItems -> {get_disco_item, only_non_empty, From, Lang}; Node == <<"nonemptyrooms">> -> {get_disco_item, only_non_empty, From, Lang}; Node == <<"emptyrooms">> -> {get_disco_item, 0, From, Lang}; true -> {get_disco_item, all, From, Lang} end, Items = lists:flatmap( fun(R) -> case get_room_disco_item(R, Query) of {ok, Item} -> [Item]; {error, _} -> [] end end, get_online_rooms(ServerHost, Host, RSM)), ResRSM = case Items of [_|_] when RSM /= undefined -> #disco_item{jid = #jid{luser = First}} = hd(Items), #disco_item{jid = #jid{luser = Last}} = lists:last(Items), #rsm_set{first = #rsm_first{data = First}, last = Last, count = Count}; [] when RSM /= undefined -> #rsm_set{count = Count}; _ -> undefined end, {result, #disco_items{node = Node, items = Items, rsm = ResRSM}}; iq_disco_items(_ServerHost, _Host, _From, Lang, _MaxRoomsDiscoItems, _Node, _RSM) -> {error, xmpp:err_item_not_found(<<"Node not found">>, Lang)}. -spec get_room_disco_item({binary(), binary(), pid()}, term()) -> {ok, disco_item()} | {error, timeout | notfound}. get_room_disco_item({Name, Host, Pid}, Query) -> RoomJID = jid:make(Name, Host), try p1_fsm:sync_send_all_state_event(Pid, Query, 100) of {item, Desc} -> {ok, #disco_item{jid = RoomJID, name = Desc}}; false -> {error, notfound} catch _:{timeout, {p1_fsm, _, _}} -> {error, timeout}; _:{_, {p1_fsm, _, _}} -> {error, notfound} end. get_subscribed_rooms(ServerHost, Host, From) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), BareFrom = jid:remove_resource(From), case Mod:get_subscribed_rooms(LServer, Host, BareFrom) of not_implemented -> Rooms = get_online_rooms(ServerHost, Host), lists:flatmap( fun({Name, _, Pid}) -> case p1_fsm:sync_send_all_state_event(Pid, {is_subscribed, BareFrom}) of true -> [jid:make(Name, Host)]; false -> [] end; (_) -> [] end, Rooms); V -> V end. get_nick(ServerHost, Host, From) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:get_nick(LServer, Host, From). iq_get_register_info(ServerHost, Host, From, Lang) -> {Nick, Registered} = case get_nick(ServerHost, Host, From) of error -> {<<"">>, false}; N -> {N, true} end, Title = <<(translate:translate( Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>, Inst = translate:translate(Lang, <<"Enter nickname you want to register">>), Fields = muc_register:encode([{roomnick, Nick}], Lang), X = #xdata{type = form, title = Title, instructions = [Inst], fields = Fields}, #register{nick = Nick, registered = Registered, instructions = translate:translate( Lang, <<"You need a client that supports x:data " "to register the nickname">>), xdata = X}. set_nick(ServerHost, Host, From, Nick) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:set_nick(LServer, Host, From, Nick). iq_set_register_info(ServerHost, Host, From, Nick, Lang) -> case set_nick(ServerHost, Host, From, Nick) of {atomic, ok} -> {result, undefined}; {atomic, false} -> ErrText = <<"That nickname is registered by another " "person">>, {error, xmpp:err_conflict(ErrText, Lang)}; _ -> Txt = <<"Database failure">>, {error, xmpp:err_internal_server_error(Txt, Lang)} end. process_iq_register_set(ServerHost, Host, From, #register{remove = true}, Lang) -> iq_set_register_info(ServerHost, Host, From, <<"">>, Lang); process_iq_register_set(_ServerHost, _Host, _From, #register{xdata = #xdata{type = cancel}}, _Lang) -> {result, undefined}; process_iq_register_set(ServerHost, Host, From, #register{nick = Nick, xdata = XData}, Lang) -> case XData of #xdata{type = submit, fields = Fs} -> try Options = muc_register:decode(Fs), N = proplists:get_value(roomnick, Options), iq_set_register_info(ServerHost, Host, From, N, Lang) catch _:{muc_register, Why} -> ErrText = muc_register:format_error(Why), {error, xmpp:err_bad_request(ErrText, Lang)} end; #xdata{} -> Txt = <<"Incorrect data form">>, {error, xmpp:err_bad_request(Txt, Lang)}; _ when is_binary(Nick), Nick /= <<"">> -> iq_set_register_info(ServerHost, Host, From, Nick, Lang); _ -> ErrText = <<"You must fill in field \"Nickname\" in the form">>, {error, xmpp:err_not_acceptable(ErrText, Lang)} end. -spec broadcast_service_message(binary(), binary(), binary()) -> ok. broadcast_service_message(ServerHost, Host, Msg) -> lists:foreach( fun({_, _, Pid}) -> p1_fsm:send_all_state_event( Pid, {service_message, Msg}) end, get_online_rooms(ServerHost, Host)). -spec get_online_rooms(binary(), binary()) -> [{binary(), binary(), pid()}]. get_online_rooms(ServerHost, Host) -> get_online_rooms(ServerHost, Host, undefined). -spec get_online_rooms(binary(), binary(), undefined | rsm_set()) -> [{binary(), binary(), pid()}]. get_online_rooms(ServerHost, Host, RSM) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:get_online_rooms(ServerHost, Host, RSM). -spec count_online_rooms(binary(), binary()) -> non_neg_integer(). count_online_rooms(ServerHost, Host) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:count_online_rooms(ServerHost, Host). opts_to_binary(Opts) -> lists:map( fun({title, Title}) -> {title, iolist_to_binary(Title)}; ({description, Desc}) -> {description, iolist_to_binary(Desc)}; ({password, Pass}) -> {password, iolist_to_binary(Pass)}; ({subject, [C|_] = Subj}) when is_integer(C), C >= 0, C =< 255 -> {subject, iolist_to_binary(Subj)}; ({subject_author, Author}) -> {subject_author, iolist_to_binary(Author)}; ({affiliations, Affs}) -> {affiliations, lists:map( fun({{U, S, R}, Aff}) -> NewAff = case Aff of {A, Reason} -> {A, iolist_to_binary(Reason)}; _ -> Aff end, {{iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)}, NewAff} end, Affs)}; ({captcha_whitelist, CWList}) -> {captcha_whitelist, lists:map( fun({U, S, R}) -> {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)} end, CWList)}; (Opt) -> Opt end, Opts). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import_info() -> [{<<"muc_room">>, 4}, {<<"muc_registered">>, 4}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). import(LServer, {sql, _}, DBType, Tab, L) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, Tab, L). mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(access_admin) -> fun acl:access_rules_validator/1; mod_opt_type(access_create) -> fun acl:access_rules_validator/1; mod_opt_type(access_persistent) -> fun acl:access_rules_validator/1; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(history_size) -> fun (I) when is_integer(I), I >= 0 -> I end; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(name) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun (L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(max_room_desc) -> fun (infinity) -> infinity; (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_room_id) -> fun (infinity) -> infinity; (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_rooms_discoitems) -> fun (I) when is_integer(I), I >= 0 -> I end; mod_opt_type(regexp_room_id) -> fun iolist_to_binary/1; mod_opt_type(max_room_name) -> fun (infinity) -> infinity; (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_user_conferences) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_users) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_users_admin_threshold) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_users_presence) -> fun (MUP) when is_integer(MUP) -> MUP end; mod_opt_type(min_message_interval) -> fun (MMI) when is_number(MMI), MMI >= 0 -> MMI end; mod_opt_type(min_presence_interval) -> fun (I) when is_number(I), I >= 0 -> I end; mod_opt_type(room_shaper) -> fun (A) when is_atom(A) -> A end; mod_opt_type(user_message_shaper) -> fun (A) when is_atom(A) -> A end; mod_opt_type(user_presence_shaper) -> fun (A) when is_atom(A) -> A end; mod_opt_type(queue_type) -> fun(ram) -> ram; (file) -> file end; mod_opt_type({default_room_options, allow_change_subj}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, allow_private_messages}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, allow_query_users}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, allow_user_invites}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, allow_visitor_nickchange}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, allow_visitor_status}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, anonymous}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, captcha_protected}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, logging}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, members_by_default}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, members_only}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, moderated}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, password_protected}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, persistent}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, public}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, public_list}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, mam}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, allow_subscription}) -> fun(B) when is_boolean(B) -> B end; mod_opt_type({default_room_options, password}) -> fun iolist_to_binary/1; mod_opt_type({default_room_options, title}) -> fun iolist_to_binary/1; mod_opt_type({default_room_options, allow_private_messages_from_visitors}) -> fun(anyone) -> anyone; (moderators) -> moderators; (nobody) -> nobody end; mod_opt_type({default_room_options, max_users}) -> fun(I) when is_integer(I), I > 0 -> I end; mod_opt_type({default_room_options, presence_broadcast}) -> fun(L) -> lists:map( fun(moderator) -> moderator; (participant) -> participant; (visitor) -> visitor end, L) end; mod_opt_type(_) -> [access, access_admin, access_create, access_persistent, db_type, ram_db_type, history_size, host, hosts, name, max_room_desc, max_room_id, max_room_name, max_rooms_discoitems, max_user_conferences, max_users, max_users_admin_threshold, max_users_presence, min_message_interval, min_presence_interval, queue_type, regexp_room_id, room_shaper, user_message_shaper, user_presence_shaper, {default_room_options, allow_change_subj}, {default_room_options, allow_private_messages}, {default_room_options, allow_query_users}, {default_room_options, allow_user_invites}, {default_room_options, allow_visitor_nickchange}, {default_room_options, allow_visitor_status}, {default_room_options, anonymous}, {default_room_options, captcha_protected}, {default_room_options, logging}, {default_room_options, members_by_default}, {default_room_options, members_only}, {default_room_options, moderated}, {default_room_options, password_protected}, {default_room_options, persistent}, {default_room_options, public}, {default_room_options, public_list}, {default_room_options, mam}, {default_room_options, allow_subscription}, {default_room_options, password}, {default_room_options, title}, {default_room_options, allow_private_messages_from_visitors}, {default_room_options, max_users}, {default_room_options, presence_broadcast}]. ejabberd-18.01/src/extauth.erl0000644000232200023220000001164413225664356016626 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : extauth.erl %%% Author : Leif Johansson %%% Purpose : External authentication using a simple port-driver %%% Created : 30 Jul 2004 by Leif Johansson %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(extauth). -behaviour(ejabberd_config). -author('leifj@it.su.se'). -export([start/2, stop/1, init/2, check_password/3, set_password/3, try_register/3, remove_user/2, remove_user/3, user_exists/2, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -define(INIT_TIMEOUT, 60000). -define(CALL_TIMEOUT, 10000). start(Host, ExtPrg) -> lists:foreach(fun (This) -> start_instance(get_process_name(Host, This), ExtPrg) end, lists:seq(0, get_instances(Host) - 1)). start_instance(ProcessName, ExtPrg) -> spawn(?MODULE, init, [ProcessName, ExtPrg]). restart_instance(ProcessName, ExtPrg) -> unregister(ProcessName), start_instance(ProcessName, ExtPrg). init(ProcessName, ExtPrg) -> register(ProcessName, self()), process_flag(trap_exit, true), Port = open_port({spawn, ExtPrg}, [{packet, 2}]), loop(Port, ?INIT_TIMEOUT, ProcessName, ExtPrg). stop(Host) -> lists:foreach(fun (This) -> get_process_name(Host, This) ! stop end, lists:seq(0, get_instances(Host) - 1)). get_process_name(Host, Integer) -> gen_mod:get_module_proc(iolist_to_binary([Host, integer_to_list(Integer)]), eauth). check_password(User, Server, Password) -> call_port(Server, [<<"auth">>, User, Server, Password]). user_exists(User, Server) -> call_port(Server, [<<"isuser">>, User, Server]). set_password(User, Server, Password) -> call_port(Server, [<<"setpass">>, User, Server, Password]). try_register(User, Server, Password) -> case call_port(Server, [<<"tryregister">>, User, Server, Password]) of true -> ok; false -> {error, not_allowed} end. remove_user(User, Server) -> call_port(Server, [<<"removeuser">>, User, Server]). remove_user(User, Server, Password) -> call_port(Server, [<<"removeuser3">>, User, Server, Password]). call_port(Server, Msg) -> LServer = jid:nameprep(Server), ProcessName = get_process_name(LServer, random_instance(get_instances(LServer))), ProcessName ! {call, self(), Msg}, receive {eauth, Result} -> Result end. random_instance(MaxNum) -> randoms:uniform(MaxNum) - 1. get_instances(Server) -> ejabberd_config:get_option({extauth_instances, Server}, 1). loop(Port, Timeout, ProcessName, ExtPrg) -> receive {call, Caller, Msg} -> port_command(Port, encode(Msg)), receive {Port, {data, Data}} -> ?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]), Caller ! {eauth, decode(Data)}, loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg); {Port, Other} -> ?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]), Caller ! {eauth, false}, loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg) after Timeout -> ?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]), Caller ! {eauth, false}, Pid = restart_instance(ProcessName, ExtPrg), flush_buffer_and_forward_messages(Pid), exit(port_terminated) end; stop -> Port ! {self(), close}, receive {Port, closed} -> exit(normal) end; {'EXIT', Port, Reason} -> ?CRITICAL_MSG("extauth script has exitted abruptly " "with reason '~p'", [Reason]), Pid = restart_instance(ProcessName, ExtPrg), flush_buffer_and_forward_messages(Pid), exit(port_terminated) end. flush_buffer_and_forward_messages(Pid) -> receive Message -> Pid ! Message, flush_buffer_and_forward_messages(Pid) after 0 -> true end. encode(L) -> str:join(L, <<":">>). decode([0, 0]) -> false; decode([0, 1]) -> true. -spec opt_type(extauth_instances) -> fun((pos_integer()) -> pos_integer()); (atom()) -> [atom()]. opt_type(extauth_instances) -> fun (V) when is_integer(V), V > 0 -> V end; opt_type(_) -> [extauth_instances]. ejabberd-18.01/src/mod_privacy.erl0000644000232200023220000007024613225664356017463 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_privacy.erl %%% Author : Alexey Shchepin %%% Purpose : jabber:iq:privacy support %%% Created : 21 Jul 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_privacy). -author('alexey@process-one.net'). -protocol({xep, 16, '1.6'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_iq/1, export/1, c2s_copy_session/2, push_list_update/2, disco_features/5, check_packet/4, remove_user/2, encode_list_item/1, get_user_lists/2, get_user_list/3, set_list/1, set_list/4, set_default_list/3, user_send_packet/1, user_receive_packet/1, import_start/2, import_stop/2, import/5, import_info/0, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_privacy.hrl"). -define(PRIVACY_CACHE, privacy_cache). -define(PRIVACY_LIST_CACHE, privacy_list_cache). -type c2s_state() :: ejabberd_c2s:state(). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(#privacy{}) -> ok. -callback set_default(binary(), binary(), binary()) -> ok | {error, notfound | any()}. -callback unset_default(binary(), binary()) -> ok | {error, any()}. -callback remove_list(binary(), binary(), binary()) -> ok | {error, notfound | conflict | any()}. -callback remove_lists(binary(), binary()) -> ok | {error, any()}. -callback set_lists(#privacy{}) -> ok | {error, any()}. -callback set_list(binary(), binary(), binary(), listitem()) -> ok | {error, any()}. -callback get_list(binary(), binary(), binary() | default) -> {ok, {binary(), [listitem()]}} | error | {error, any()}. -callback get_lists(binary(), binary()) -> {ok, #privacy{}} | error | {error, any()}. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/1, cache_nodes/1]). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 50), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 50), ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE, check_packet, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY, ?MODULE, process_iq, IQDisc). stop(Host) -> ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 50), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 50), ejabberd_hooks:delete(privacy_check_packet, Host, ?MODULE, check_packet, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, init_cache(NewMod, Host, NewOpts), case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY, ?MODULE, process_iq, IQDisc); true -> ok end. -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. disco_features({error, Err}, _From, _To, _Node, _Lang) -> {error, Err}; disco_features(empty, _From, _To, <<"">>, _Lang) -> {result, [?NS_PRIVACY]}; disco_features({result, Feats}, _From, _To, <<"">>, _Lang) -> {result, [?NS_PRIVACY|Feats]}; disco_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec process_iq(iq()) -> iq(). process_iq(#iq{type = Type, from = #jid{luser = U, lserver = S}, to = #jid{luser = U, lserver = S}} = IQ) -> case Type of get -> process_iq_get(IQ); set -> process_iq_set(IQ) end; process_iq(#iq{lang = Lang} = IQ) -> Txt = <<"Query to another users is forbidden">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)). -spec process_iq_get(iq()) -> iq(). process_iq_get(#iq{lang = Lang, sub_els = [#privacy_query{default = Default, active = Active}]} = IQ) when Default /= undefined; Active /= undefined -> Txt = <<"Only element is allowed in this query">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); process_iq_get(#iq{lang = Lang, sub_els = [#privacy_query{lists = Lists}]} = IQ) -> case Lists of [] -> process_lists_get(IQ); [#privacy_list{name = ListName}] -> process_list_get(IQ, ListName); _ -> Txt = <<"Too many elements">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end; process_iq_get(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_lists_get(iq()) -> iq(). process_lists_get(#iq{from = #jid{luser = LUser, lserver = LServer}, lang = Lang} = IQ) -> case get_user_lists(LUser, LServer) of {ok, #privacy{default = Default, lists = Lists}} -> Active = xmpp:get_meta(IQ, privacy_active_list, none), xmpp:make_iq_result( IQ, #privacy_query{active = Active, default = Default, lists = [#privacy_list{name = Name} || {Name, _} <- Lists]}); error -> xmpp:make_iq_result( IQ, #privacy_query{active = none, default = none}); {error, _} -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end. -spec process_list_get(iq(), binary()) -> iq(). process_list_get(#iq{from = #jid{luser = LUser, lserver = LServer}, lang = Lang} = IQ, Name) -> case get_user_list(LUser, LServer, Name) of {ok, {_, List}} -> Items = lists:map(fun encode_list_item/1, List), xmpp:make_iq_result( IQ, #privacy_query{ lists = [#privacy_list{name = Name, items = Items}]}); error -> Txt = <<"No privacy list with this name found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)); {error, _} -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end. -spec encode_list_item(listitem()) -> privacy_item(). encode_list_item(#listitem{action = Action, order = Order, type = Type, match_all = MatchAll, match_iq = MatchIQ, match_message = MatchMessage, match_presence_in = MatchPresenceIn, match_presence_out = MatchPresenceOut, value = Value}) -> Item = #privacy_item{action = Action, order = Order, type = case Type of none -> undefined; Type -> Type end, value = encode_value(Type, Value)}, case MatchAll of true -> Item; false -> Item#privacy_item{message = MatchMessage, iq = MatchIQ, presence_in = MatchPresenceIn, presence_out = MatchPresenceOut} end. -spec encode_value(listitem_type(), listitem_value()) -> binary(). encode_value(Type, Val) -> case Type of jid -> jid:encode(Val); group -> Val; subscription -> case Val of both -> <<"both">>; to -> <<"to">>; from -> <<"from">>; none -> <<"none">> end; none -> <<"">> end. -spec decode_value(jid | subscription | group | undefined, binary()) -> listitem_value(). decode_value(Type, Value) -> case Type of jid -> jid:tolower(jid:decode(Value)); subscription -> case Value of <<"from">> -> from; <<"to">> -> to; <<"both">> -> both; <<"none">> -> none end; group when Value /= <<"">> -> Value; undefined -> none end. -spec process_iq_set(iq()) -> iq(). process_iq_set(#iq{lang = Lang, sub_els = [#privacy_query{default = Default, active = Active, lists = Lists}]} = IQ) -> case Lists of [#privacy_list{items = Items, name = ListName}] when Default == undefined, Active == undefined -> process_lists_set(IQ, ListName, Items); [] when Default == undefined, Active /= undefined -> process_active_set(IQ, Active); [] when Active == undefined, Default /= undefined -> process_default_set(IQ, Default); _ -> Txt = <<"The stanza MUST contain only one element, " "one element, or one element">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end; process_iq_set(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_default_set(iq(), none | binary()) -> iq(). process_default_set(#iq{from = #jid{luser = LUser, lserver = LServer}, lang = Lang} = IQ, Value) -> case set_default_list(LUser, LServer, Value) of ok -> xmpp:make_iq_result(IQ); {error, notfound} -> Txt = <<"No privacy list with this name found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)); {error, _} -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end. -spec process_active_set(IQ, none | binary()) -> IQ. process_active_set(IQ, none) -> xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_active_list, none)); process_active_set(#iq{from = #jid{luser = LUser, lserver = LServer}, lang = Lang} = IQ, Name) -> case get_user_list(LUser, LServer, Name) of {ok, _} -> xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_active_list, Name)); error -> Txt = <<"No privacy list with this name found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)); {error, _} -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end. -spec set_list(privacy()) -> ok | {error, any()}. set_list(#privacy{us = {LUser, LServer}, lists = Lists} = Privacy) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:set_lists(Privacy) of ok -> Names = [Name || {Name, _} <- Lists], delete_cache(Mod, LUser, LServer, Names); {error, _} = Err -> Err end. -spec process_lists_set(iq(), binary(), [privacy_item()]) -> iq(). process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer}, lang = Lang} = IQ, Name, []) -> case xmpp:get_meta(IQ, privacy_active_list, none) of Name -> Txt = <<"Cannot remove active list">>, xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang)); _ -> case remove_list(LUser, LServer, Name) of ok -> xmpp:make_iq_result(IQ); {error, conflict} -> Txt = <<"Cannot remove default list">>, xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang)); {error, notfound} -> Txt = <<"No privacy list with this name found">>, xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)); {error, _} -> Txt = <<"Database failure">>, Err = xmpp:err_internal_server_error(Txt, Lang), xmpp:make_error(IQ, Err) end end; process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer} = From, lang = Lang} = IQ, Name, Items) -> case catch lists:map(fun decode_item/1, Items) of {error, Why} -> Txt = xmpp:io_format_error(Why), xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); List -> case set_list(LUser, LServer, Name, List) of ok -> push_list_update(From, Name), xmpp:make_iq_result(IQ); {error, _} -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end end. -spec push_list_update(jid(), binary()) -> ok. push_list_update(From, Name) -> BareFrom = jid:remove_resource(From), lists:foreach( fun(R) -> To = jid:replace_resource(From, R), IQ = #iq{type = set, from = BareFrom, to = To, id = <<"push", (randoms:get_string())/binary>>, sub_els = [#privacy_query{ lists = [#privacy_list{name = Name}]}]}, ejabberd_router:route(IQ) end, ejabberd_sm:get_user_resources(From#jid.luser, From#jid.lserver)). -spec decode_item(privacy_item()) -> listitem(). decode_item(#privacy_item{order = Order, action = Action, type = T, value = V, message = MatchMessage, iq = MatchIQ, presence_in = MatchPresenceIn, presence_out = MatchPresenceOut}) -> Value = try decode_value(T, V) catch _:_ -> throw({error, {bad_attr_value, <<"value">>, <<"item">>, ?NS_PRIVACY}}) end, Type = case T of undefined -> none; _ -> T end, ListItem = #listitem{order = Order, action = Action, type = Type, value = Value}, if not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) -> ListItem#listitem{match_all = true}; true -> ListItem#listitem{match_iq = MatchIQ, match_message = MatchMessage, match_presence_in = MatchPresenceIn, match_presence_out = MatchPresenceOut} end. -spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state(). c2s_copy_session(State, #{privacy_active_list := List}) -> State#{privacy_active_list => List}; c2s_copy_session(State, _) -> State. -spec user_send_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}. user_send_packet({#iq{type = Type, to = #jid{luser = U, lserver = S, lresource = <<"">>}, from = #jid{luser = U, lserver = S}, sub_els = [_]} = IQ, #{privacy_active_list := Name} = State}) when Type == get; Type == set -> NewIQ = case xmpp:has_subtag(IQ, #privacy_query{}) of true -> xmpp:put_meta(IQ, privacy_active_list, Name); false -> IQ end, {NewIQ, State}; user_send_packet(Acc) -> Acc. -spec user_receive_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}. user_receive_packet({#iq{type = result, meta = #{privacy_active_list := Name}} = IQ, State}) -> {IQ, State#{privacy_active_list => Name}}; user_receive_packet(Acc) -> Acc. -spec set_list(binary(), binary(), binary(), [listitem()]) -> ok | {error, any()}. set_list(LUser, LServer, Name, List) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:set_list(LUser, LServer, Name, List) of ok -> delete_cache(Mod, LUser, LServer, [Name]); {error, _} = Err -> Err end. -spec remove_list(binary(), binary(), binary()) -> ok | {error, conflict | notfound | any()}. remove_list(LUser, LServer, Name) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:remove_list(LUser, LServer, Name) of ok -> delete_cache(Mod, LUser, LServer, [Name]); Err -> Err end. -spec get_user_lists(binary(), binary()) -> {ok, privacy()} | error | {error, any()}. get_user_lists(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?PRIVACY_CACHE, {LUser, LServer}, fun() -> Mod:get_lists(LUser, LServer) end); false -> Mod:get_lists(LUser, LServer) end. -spec get_user_list(binary(), binary(), binary() | default) -> {ok, {binary(), [listitem()]}} | error | {error, any()}. get_user_list(LUser, LServer, Name) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case use_cache(Mod, LServer) of true -> ets_cache:lookup( ?PRIVACY_LIST_CACHE, {LUser, LServer, Name}, fun() -> case ets_cache:lookup( ?PRIVACY_CACHE, {LUser, LServer}) of {ok, Privacy} -> get_list_by_name(Privacy, Name); error -> Mod:get_list(LUser, LServer, Name) end end); false -> Mod:get_list(LUser, LServer, Name) end. -spec get_list_by_name(#privacy{}, binary() | default) -> {ok, {binary(), [listitem()]}} | error. get_list_by_name(#privacy{default = Default} = Privacy, default) -> get_list_by_name(Privacy, Default); get_list_by_name(#privacy{lists = Lists}, Name) -> case lists:keyfind(Name, 1, Lists) of {_, List} -> {ok, {Name, List}}; false -> error end. -spec set_default_list(binary(), binary(), binary() | none) -> ok | {error, notfound | any()}. set_default_list(LUser, LServer, Name) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Res = case Name of none -> Mod:unset_default(LUser, LServer); _ -> Mod:set_default(LUser, LServer, Name) end, case Res of ok -> delete_cache(Mod, LUser, LServer, []); Err -> Err end. -spec check_packet(allow | deny, c2s_state() | jid(), stanza(), in | out) -> allow | deny. check_packet(Acc, #{jid := JID} = State, Packet, Dir) -> case maps:get(privacy_active_list, State, none) of none -> check_packet(Acc, JID, Packet, Dir); ListName -> #jid{luser = LUser, lserver = LServer} = JID, case get_user_list(LUser, LServer, ListName) of {ok, {_, List}} -> do_check_packet(JID, List, Packet, Dir); _ -> ?DEBUG("Non-existing active list '~s' is set " "for user '~s'", [ListName, jid:encode(JID)]), check_packet(Acc, JID, Packet, Dir) end end; check_packet(_, JID, Packet, Dir) -> #jid{luser = LUser, lserver = LServer} = JID, case get_user_list(LUser, LServer, default) of {ok, {_, List}} -> do_check_packet(JID, List, Packet, Dir); _ -> allow end. %% From is the sender, To is the destination. %% If Dir = out, User@Server is the sender account (From). %% If Dir = in, User@Server is the destination account (To). -spec do_check_packet(jid(), [listitem()], stanza(), in | out) -> allow | deny. do_check_packet(_, [], _, _) -> allow; do_check_packet(#jid{luser = LUser, lserver = LServer}, List, Packet, Dir) -> From = xmpp:get_from(Packet), To = xmpp:get_to(Packet), case {From, To} of {#jid{luser = <<"">>, lserver = LServer}, #jid{lserver = LServer}} when Dir == in -> %% Allow any packets from local server allow; {#jid{lserver = LServer}, #jid{luser = <<"">>, lserver = LServer}} when Dir == out -> %% Allow any packets to local server allow; {#jid{luser = LUser, lserver = LServer, lresource = <<"">>}, #jid{luser = LUser, lserver = LServer}} when Dir == in -> %% Allow incoming packets from user's bare jid to his full jid allow; {#jid{luser = LUser, lserver = LServer}, #jid{luser = LUser, lserver = LServer, lresource = <<"">>}} when Dir == out -> %% Allow outgoing packets from user's full jid to his bare JID allow; _ -> PType = case Packet of #message{} -> message; #iq{} -> iq; #presence{type = available} -> presence; #presence{type = unavailable} -> presence; _ -> other end, PType2 = case {PType, Dir} of {message, in} -> message; {iq, in} -> iq; {presence, in} -> presence_in; {presence, out} -> presence_out; {_, _} -> other end, LJID = case Dir of in -> jid:tolower(From); out -> jid:tolower(To) end, {Subscription, Groups} = ejabberd_hooks:run_fold( roster_get_jid_info, LServer, {none, []}, [LUser, LServer, LJID]), check_packet_aux(List, PType2, LJID, Subscription, Groups) end. -spec check_packet_aux([listitem()], message | iq | presence_in | presence_out | other, ljid(), none | both | from | to, [binary()]) -> allow | deny. %% Ptype = mesage | iq | presence_in | presence_out | other check_packet_aux([], _PType, _JID, _Subscription, _Groups) -> allow; check_packet_aux([Item | List], PType, JID, Subscription, Groups) -> #listitem{type = Type, value = Value, action = Action} = Item, case is_ptype_match(Item, PType) of true -> case is_type_match(Type, Value, JID, Subscription, Groups) of true -> Action; false -> check_packet_aux(List, PType, JID, Subscription, Groups) end; false -> check_packet_aux(List, PType, JID, Subscription, Groups) end. -spec is_ptype_match(listitem(), message | iq | presence_in | presence_out | other) -> boolean(). is_ptype_match(Item, PType) -> case Item#listitem.match_all of true -> true; false -> case PType of message -> Item#listitem.match_message; iq -> Item#listitem.match_iq; presence_in -> Item#listitem.match_presence_in; presence_out -> Item#listitem.match_presence_out; other -> false end end. -spec is_type_match(none | jid | subscription | group, listitem_value(), ljid(), none | both | from | to, [binary()]) -> boolean(). is_type_match(none, _Value, _JID, _Subscription, _Groups) -> true; is_type_match(jid, Value, JID, _Subscription, _Groups) -> case Value of {<<"">>, Server, <<"">>} -> case JID of {_, Server, _} -> true; _ -> false end; {User, Server, <<"">>} -> case JID of {User, Server, _} -> true; _ -> false end; {<<"">>, Server, Resource} -> case JID of {_, Server, Resource} -> true; _ -> false end; _ -> Value == JID end; is_type_match(subscription, Value, _JID, Subscription, _Groups) -> Value == Subscription; is_type_match(group, Group, _JID, _Subscription, Groups) -> lists:member(Group, Groups). -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Privacy = get_user_lists(LUser, LServer), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_lists(LUser, LServer), case Privacy of {ok, #privacy{lists = Lists}} -> Names = [Name || {Name, _} <- Lists], delete_cache(Mod, LUser, LServer, Names); _ -> ok end. -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> case use_cache(Mod, Host) of true -> CacheOpts = cache_opts(Host, Opts), ets_cache:new(?PRIVACY_CACHE, CacheOpts), ets_cache:new(?PRIVACY_LIST_CACHE, CacheOpts); false -> ets_cache:delete(?PRIVACY_CACHE), ets_cache:delete(?PRIVACY_LIST_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary()) -> boolean(). use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 1) of true -> Mod:use_cache(Host); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. -spec delete_cache(module(), binary(), binary(), [binary()]) -> ok. delete_cache(Mod, LUser, LServer, Names) -> case use_cache(Mod, LServer) of true -> Nodes = cache_nodes(Mod, LServer), ets_cache:delete(?PRIVACY_CACHE, {LUser, LServer}, Nodes), lists:foreach( fun(Name) -> ets_cache:delete( ?PRIVACY_LIST_CACHE, {LUser, LServer, Name}, Nodes) end, [default|Names]); false -> ok end. numeric_to_binary(<<0, 0, _/binary>>) -> <<"0">>; numeric_to_binary(<<0, _, _:6/binary, T/binary>>) -> Res = lists:foldl( fun(X, Sum) -> Sum*10000 + X end, 0, [X || <> <= T]), integer_to_binary(Res). bool_to_binary(<<0>>) -> <<"0">>; bool_to_binary(<<1>>) -> <<"1">>. prepare_list_data(mysql, [ID|Row]) -> [binary_to_integer(ID)|Row]; prepare_list_data(pgsql, [<>, SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ, SMatchMessage, SMatchPresenceIn, SMatchPresenceOut]) -> [ID, SType, SValue, SAction, numeric_to_binary(SOrder), bool_to_binary(SMatchAll), bool_to_binary(SMatchIQ), bool_to_binary(SMatchMessage), bool_to_binary(SMatchPresenceIn), bool_to_binary(SMatchPresenceOut)]. prepare_id(mysql, ID) -> binary_to_integer(ID); prepare_id(pgsql, <>) -> ID. import_info() -> [{<<"privacy_default_list">>, 2}, {<<"privacy_list_data">>, 10}, {<<"privacy_list">>, 4}]. import_start(LServer, DBType) -> ets:new(privacy_default_list_tmp, [private, named_table]), ets:new(privacy_list_data_tmp, [private, named_table, bag]), ets:new(privacy_list_tmp, [private, named_table, bag, {keypos, #privacy.us}]), Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []). import(LServer, {sql, _}, _DBType, <<"privacy_default_list">>, [LUser, Name]) -> US = {LUser, LServer}, ets:insert(privacy_default_list_tmp, {US, Name}), ok; import(LServer, {sql, SQLType}, _DBType, <<"privacy_list_data">>, Row1) -> [ID|Row] = prepare_list_data(SQLType, Row1), case mod_privacy_sql:raw_to_item(Row) of [Item] -> IS = {ID, LServer}, ets:insert(privacy_list_data_tmp, {IS, Item}), ok; [] -> ok end; import(LServer, {sql, SQLType}, _DBType, <<"privacy_list">>, [LUser, Name, ID, _TimeStamp]) -> US = {LUser, LServer}, IS = {prepare_id(SQLType, ID), LServer}, Default = case ets:lookup(privacy_default_list_tmp, US) of [{_, Name}] -> Name; _ -> none end, case [Item || {_, Item} <- ets:lookup(privacy_list_data_tmp, IS)] of [_|_] = Items -> Privacy = #privacy{us = {LUser, LServer}, default = Default, lists = [{Name, Items}]}, ets:insert(privacy_list_tmp, Privacy), ets:delete(privacy_list_data_tmp, IS), ok; _ -> ok end. import_stop(_LServer, DBType) -> import_next(DBType, ets:first(privacy_list_tmp)), ets:delete(privacy_default_list_tmp), ets:delete(privacy_list_data_tmp), ets:delete(privacy_list_tmp), ok. import_next(_DBType, '$end_of_table') -> ok; import_next(DBType, US) -> [P|_] = Ps = ets:lookup(privacy_list_tmp, US), Lists = lists:flatmap( fun(#privacy{lists = Lists}) -> Lists end, Ps), Privacy = P#privacy{lists = Lists}, Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(Privacy), import_next(DBType, ets:next(privacy_list_tmp, US)). export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). depends(_Host, _Opts) -> []. mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [db_type, iqdisc, cache_life_time, cache_size, use_cache, cache_missed]. ejabberd-18.01/src/mod_sip_registrar.erl0000644000232200023220000004140213225664356020653 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_sip_registrar.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 23 Apr 2014 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2014-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_sip_registrar). -ifndef(SIP). -export([]). -else. -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. -behaviour(?GEN_SERVER). %% API -export([start_link/0, request/2, find_sockets/2, ping/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("esip/include/esip.hrl"). -define(CALL_TIMEOUT, timer:seconds(30)). -define(DEFAULT_EXPIRES, 3600). -define(FLOW_TIMEOUT_UDP, 29). -define(FLOW_TIMEOUT_TCP, 120). -record(sip_session, {us = {<<"">>, <<"">>} :: {binary(), binary()}, socket = #sip_socket{} :: #sip_socket{}, call_id = <<"">> :: binary(), cseq = 0 :: non_neg_integer(), timestamp = p1_time_compat:timestamp() :: erlang:timestamp(), contact :: {binary(), #uri{}, [{binary(), binary()}]}, flow_tref :: reference() | undefined, reg_tref = make_ref() :: reference(), conn_mref = make_ref() :: reference(), expires = 0 :: non_neg_integer()}). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== start_link() -> ?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []). request(#sip{hdrs = Hdrs} = Req, SIPSock) -> {_, #uri{user = U, host = S}, _} = esip:get_hdr('to', Hdrs), LUser = jid:nodeprep(U), LServer = jid:nameprep(S), {PeerIP, _} = SIPSock#sip_socket.peer, US = {LUser, LServer}, CallID = esip:get_hdr('call-id', Hdrs), CSeq = esip:get_hdr('cseq', Hdrs), Expires = esip:get_hdr('expires', Hdrs, ?DEFAULT_EXPIRES), Supported = esip:get_hdrs('supported', Hdrs), IsOutboundSupported = lists:member(<<"outbound">>, Supported), case esip:get_hdrs('contact', Hdrs) of [<<"*">>] when Expires == 0 -> case unregister_session(US, CallID, CSeq) of {ok, ContactsWithExpires} -> ?INFO_MSG("unregister SIP session for user ~s@~s from ~s", [LUser, LServer, inet_parse:ntoa(PeerIP)]), Cs = prepare_contacts_to_send(ContactsWithExpires), mod_sip:make_response( Req, #sip{type = response, status = 200, hdrs = [{'contact', Cs}]}); {error, Why} -> {Status, Reason} = make_status(Why), mod_sip:make_response( Req, #sip{type = response, status = Status, reason = Reason}) end; [{_, _URI, _Params}|_] = Contacts -> ContactsWithExpires = make_contacts_with_expires(Contacts, Expires), ContactsHaveManyRegID = contacts_have_many_reg_id(Contacts), Expires1 = lists:max([E || {_, E} <- ContactsWithExpires]), MinExpires = min_expires(), if Expires1 > 0, Expires1 < MinExpires -> mod_sip:make_response( Req, #sip{type = response, status = 423, hdrs = [{'min-expires', MinExpires}]}); ContactsHaveManyRegID -> mod_sip:make_response( Req, #sip{type = response, status = 400, reason = <<"Multiple 'reg-id' parameter">>}); true -> case register_session(US, SIPSock, CallID, CSeq, IsOutboundSupported, ContactsWithExpires) of {ok, Res} -> ?INFO_MSG("~s SIP session for user ~s@~s from ~s", [Res, LUser, LServer, inet_parse:ntoa(PeerIP)]), Cs = prepare_contacts_to_send(ContactsWithExpires), Require = case need_ob_hdrs( Contacts, IsOutboundSupported) of true -> [{'require', [<<"outbound">>]}, {'flow-timer', get_flow_timeout(LServer, SIPSock)}]; false -> [] end, mod_sip:make_response( Req, #sip{type = response, status = 200, hdrs = [{'contact', Cs}|Require]}); {error, Why} -> {Status, Reason} = make_status(Why), mod_sip:make_response( Req, #sip{type = response, status = Status, reason = Reason}) end end; [] -> case mnesia:dirty_read(sip_session, US) of [_|_] = Sessions -> ContactsWithExpires = lists:map( fun(#sip_session{contact = Contact, expires = Es}) -> {Contact, Es} end, Sessions), Cs = prepare_contacts_to_send(ContactsWithExpires), mod_sip:make_response( Req, #sip{type = response, status = 200, hdrs = [{'contact', Cs}]}); [] -> {Status, Reason} = make_status(notfound), mod_sip:make_response( Req, #sip{type = response, status = Status, reason = Reason}) end; _ -> mod_sip:make_response(Req, #sip{type = response, status = 400}) end. find_sockets(U, S) -> case mnesia:dirty_read(sip_session, {U, S}) of [_|_] = Sessions -> lists:map( fun(#sip_session{contact = {_, URI, _}, socket = Socket}) -> {Socket, URI} end, Sessions); [] -> [] end. ping(SIPSocket) -> call({ping, SIPSocket}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> process_flag(trap_exit, true), update_table(), ejabberd_mnesia:create(?MODULE, sip_session, [{ram_copies, [node()]}, {type, bag}, {attributes, record_info(fields, sip_session)}, {index, [conn_mref,socket]}]), {ok, #state{}}. handle_call({write, Sessions, Supported}, _From, State) -> Res = write_session(Sessions, Supported), {reply, Res, State}; handle_call({delete, US, CallID, CSeq}, _From, State) -> Res = delete_session(US, CallID, CSeq), {reply, Res, State}; handle_call({ping, SIPSocket}, _From, State) -> Res = process_ping(SIPSocket), {reply, Res, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({write, Sessions, Supported}, State) -> write_session(Sessions, Supported), {noreply, State}; handle_info({delete, US, CallID, CSeq}, State) -> delete_session(US, CallID, CSeq), {noreply, State}; handle_info({timeout, TRef, US}, State) -> delete_expired_session(US, TRef), {noreply, State}; handle_info({'DOWN', MRef, process, _Pid, _Reason}, State) -> case mnesia:dirty_index_read(sip_session, MRef, #sip_session.conn_mref) of [Session] -> mnesia:dirty_delete_object(Session); _ -> ok end, {noreply, State}; handle_info(_Info, State) -> ?ERROR_MSG("got unexpected info: ~p", [_Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== register_session(US, SIPSocket, CallID, CSeq, IsOutboundSupported, ContactsWithExpires) -> Sessions = lists:map( fun({Contact, Expires}) -> #sip_session{us = US, socket = SIPSocket, call_id = CallID, cseq = CSeq, timestamp = p1_time_compat:timestamp(), contact = Contact, expires = Expires} end, ContactsWithExpires), Msg = {write, Sessions, IsOutboundSupported}, call(Msg). unregister_session(US, CallID, CSeq) -> Msg = {delete, US, CallID, CSeq}, call(Msg). write_session([#sip_session{us = {U, S} = US}|_] = NewSessions, IsOutboundSupported) -> PrevSessions = mnesia:dirty_read(sip_session, US), Res = lists:foldl( fun(_, {error, _} = Err) -> Err; (#sip_session{call_id = CallID, expires = Expires, cseq = CSeq} = Session, {Add, Del}) -> case find_session(Session, PrevSessions, IsOutboundSupported) of {ok, normal, #sip_session{call_id = CallID, cseq = PrevCSeq}} when PrevCSeq > CSeq -> {error, cseq_out_of_order}; {ok, _Type, PrevSession} when Expires == 0 -> {Add, [PrevSession|Del]}; {ok, _Type, PrevSession} -> {[Session|Add], [PrevSession|Del]}; {error, notfound} when Expires == 0 -> {error, notfound}; {error, notfound} -> {[Session|Add], Del} end end, {[], []}, NewSessions), MaxSessions = ejabberd_sm:get_max_user_sessions(U, S), case Res of {error, Why} -> {error, Why}; {AddSessions, DelSessions} -> MaxSessions = ejabberd_sm:get_max_user_sessions(U, S), AllSessions = AddSessions ++ PrevSessions -- DelSessions, if length(AllSessions) > MaxSessions -> {error, too_many_sessions}; true -> lists:foreach(fun delete_session/1, DelSessions), lists:foreach( fun(Session) -> NewSession = set_monitor_and_timer( Session, IsOutboundSupported), mnesia:dirty_write(NewSession) end, AddSessions), case {AllSessions, AddSessions} of {[], _} -> {ok, unregister}; {_, []} -> {ok, unregister}; _ -> {ok, register} end end end. delete_session(US, CallID, CSeq) -> case mnesia:dirty_read(sip_session, US) of [_|_] = Sessions -> case lists:all( fun(S) when S#sip_session.call_id == CallID, S#sip_session.cseq > CSeq -> false; (_) -> true end, Sessions) of true -> ContactsWithExpires = lists:map( fun(#sip_session{contact = Contact} = Session) -> delete_session(Session), {Contact, 0} end, Sessions), {ok, ContactsWithExpires}; false -> {error, cseq_out_of_order} end; [] -> {error, notfound} end. delete_expired_session(US, TRef) -> case mnesia:dirty_read(sip_session, US) of [_|_] = Sessions -> lists:foreach( fun(#sip_session{reg_tref = T1, flow_tref = T2} = Session) when T1 == TRef; T2 == TRef -> if T2 /= undefined -> close_socket(Session); true -> ok end, delete_session(Session); (_) -> ok end, Sessions); [] -> ok end. min_expires() -> 60. to_integer(Bin, Min, Max) -> case catch (binary_to_integer(Bin)) of N when N >= Min, N =< Max -> {ok, N}; _ -> error end. call(Msg) -> case catch ?GEN_SERVER:call(?MODULE, Msg, ?CALL_TIMEOUT) of {'EXIT', {timeout, _}} -> {error, timeout}; {'EXIT', Why} -> {error, Why}; Reply -> Reply end. make_contacts_with_expires(Contacts, Expires) -> lists:map( fun({Name, URI, Params}) -> E1 = case to_integer(esip:get_param(<<"expires">>, Params), 0, (1 bsl 32)-1) of {ok, E} -> E; _ -> Expires end, Params1 = lists:keydelete(<<"expires">>, 1, Params), {{Name, URI, Params1}, E1} end, Contacts). prepare_contacts_to_send(ContactsWithExpires) -> lists:map( fun({{Name, URI, Params}, Expires}) -> Params1 = esip:set_param(<<"expires">>, list_to_binary( integer_to_list(Expires)), Params), {Name, URI, Params1} end, ContactsWithExpires). contacts_have_many_reg_id(Contacts) -> Sum = lists:foldl( fun({_Name, _URI, Params}, Acc) -> case get_ob_params(Params) of error -> Acc; {_, _} -> Acc + 1 end end, 0, Contacts), if Sum > 1 -> true; true -> false end. find_session(#sip_session{contact = {_, URI, Params}}, Sessions, IsOutboundSupported) -> if IsOutboundSupported -> case get_ob_params(Params) of {InstanceID, RegID} -> find_session_by_ob({InstanceID, RegID}, Sessions); error -> find_session_by_uri(URI, Sessions) end; true -> find_session_by_uri(URI, Sessions) end. find_session_by_ob({InstanceID, RegID}, [#sip_session{contact = {_, _, Params}} = Session|Sessions]) -> case get_ob_params(Params) of {InstanceID, RegID} -> {ok, flow, Session}; _ -> find_session_by_ob({InstanceID, RegID}, Sessions) end; find_session_by_ob(_, []) -> {error, notfound}. find_session_by_uri(URI1, [#sip_session{contact = {_, URI2, _}} = Session|Sessions]) -> case cmp_uri(URI1, URI2) of true -> {ok, normal, Session}; false -> find_session_by_uri(URI1, Sessions) end; find_session_by_uri(_, []) -> {error, notfound}. %% TODO: this is *totally* wrong. %% Rewrite this using URI comparison rules cmp_uri(#uri{user = U, host = H, port = P}, #uri{user = U, host = H, port = P}) -> true; cmp_uri(_, _) -> false. make_status(notfound) -> {404, esip:reason(404)}; make_status(cseq_out_of_order) -> {500, <<"CSeq is Out of Order">>}; make_status(timeout) -> {408, esip:reason(408)}; make_status(too_many_sessions) -> {503, <<"Too Many Registered Sessions">>}; make_status(_) -> {500, esip:reason(500)}. get_ob_params(Params) -> case esip:get_param(<<"+sip.instance">>, Params) of <<>> -> error; InstanceID -> case to_integer(esip:get_param(<<"reg-id">>, Params), 0, (1 bsl 32)-1) of {ok, RegID} -> {InstanceID, RegID}; error -> error end end. need_ob_hdrs(_Contacts, _IsOutboundSupported = false) -> false; need_ob_hdrs(Contacts, _IsOutboundSupported = true) -> lists:any( fun({_Name, _URI, Params}) -> case get_ob_params(Params) of error -> false; {_, _} -> true end end, Contacts). get_flow_timeout(LServer, #sip_socket{type = Type}) -> case Type of udp -> gen_mod:get_module_opt( LServer, mod_sip, flow_timeout_udp, ?FLOW_TIMEOUT_UDP); _ -> gen_mod:get_module_opt( LServer, mod_sip, flow_timeout_tcp, ?FLOW_TIMEOUT_TCP) end. update_table() -> Fields = record_info(fields, sip_session), case catch mnesia:table_info(sip_session, attributes) of Fields -> ok; [_|_] -> mnesia:delete_table(sip_session); {'EXIT', _} -> ok end. set_monitor_and_timer(#sip_session{socket = #sip_socket{type = Type, pid = Pid} = SIPSock, conn_mref = MRef, expires = Expires, us = {_, LServer}, contact = {_, _, Params}} = Session, IsOutboundSupported) -> RegTRef = set_timer(Session, Expires), Session1 = Session#sip_session{reg_tref = RegTRef}, if IsOutboundSupported -> case get_ob_params(Params) of error -> Session1; {_, _} -> FlowTimeout = get_flow_timeout(LServer, SIPSock), FlowTRef = set_timer(Session1, FlowTimeout), NewMRef = if Type == udp -> MRef; true -> erlang:monitor(process, Pid) end, Session1#sip_session{conn_mref = NewMRef, flow_tref = FlowTRef} end; true -> Session1 end. set_timer(#sip_session{us = US}, Timeout) -> erlang:start_timer(Timeout * 1000, self(), US). close_socket(#sip_session{socket = SIPSocket}) -> if SIPSocket#sip_socket.type /= udp -> esip_socket:close(SIPSocket); true -> ok end. delete_session(#sip_session{reg_tref = RegTRef, flow_tref = FlowTRef, conn_mref = MRef} = Session) -> erlang:cancel_timer(RegTRef), catch erlang:cancel_timer(FlowTRef), catch erlang:demonitor(MRef, [flush]), mnesia:dirty_delete_object(Session). process_ping(SIPSocket) -> ErrResponse = if SIPSocket#sip_socket.type == udp -> pang; true -> drop end, Sessions = mnesia:dirty_index_read( sip_session, SIPSocket, #sip_session.socket), lists:foldl( fun(#sip_session{flow_tref = TRef, us = {_, LServer}} = Session, _) when TRef /= undefined -> erlang:cancel_timer(TRef), mnesia:dirty_delete_object(Session), Timeout = get_flow_timeout(LServer, SIPSocket), NewTRef = set_timer(Session, Timeout), case mnesia:dirty_write( Session#sip_session{flow_tref = NewTRef}) of ok -> pong; _Err -> pang end; (_, Acc) -> Acc end, ErrResponse, Sessions). -endif. ejabberd-18.01/src/mod_s2s_dialback.erl0000644000232200023220000003237313225664356020326 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 16 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_s2s_dialback). -behaviour(gen_mod). -protocol({xep, 220, '1.1.1'}). -protocol({xep, 185, '1.0'}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% Hooks -export([s2s_out_auth_result/2, s2s_out_downgraded/2, s2s_in_packet/2, s2s_out_packet/2, s2s_in_recv/3, s2s_in_features/2, s2s_out_init/2, s2s_out_closed/2]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== start(Host, _Opts) -> case ejabberd_s2s:tls_verify(Host) of true -> ?ERROR_MSG("disabling ~s for host ~s because option " "'s2s_use_starttls' is set to 'required_trusted'", [?MODULE, Host]); false -> ejabberd_hooks:add(s2s_out_init, Host, ?MODULE, s2s_out_init, 50), ejabberd_hooks:add(s2s_out_closed, Host, ?MODULE, s2s_out_closed, 50), ejabberd_hooks:add(s2s_in_pre_auth_features, Host, ?MODULE, s2s_in_features, 50), ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE, s2s_in_features, 50), ejabberd_hooks:add(s2s_in_handle_recv, Host, ?MODULE, s2s_in_recv, 50), ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE, s2s_in_packet, 50), ejabberd_hooks:add(s2s_in_authenticated_packet, Host, ?MODULE, s2s_in_packet, 50), ejabberd_hooks:add(s2s_out_packet, Host, ?MODULE, s2s_out_packet, 50), ejabberd_hooks:add(s2s_out_downgraded, Host, ?MODULE, s2s_out_downgraded, 50), ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, s2s_out_auth_result, 50) end. stop(Host) -> ejabberd_hooks:delete(s2s_out_init, Host, ?MODULE, s2s_out_init, 50), ejabberd_hooks:delete(s2s_out_closed, Host, ?MODULE, s2s_out_closed, 50), ejabberd_hooks:delete(s2s_in_pre_auth_features, Host, ?MODULE, s2s_in_features, 50), ejabberd_hooks:delete(s2s_in_post_auth_features, Host, ?MODULE, s2s_in_features, 50), ejabberd_hooks:delete(s2s_in_handle_recv, Host, ?MODULE, s2s_in_recv, 50), ejabberd_hooks:delete(s2s_in_unauthenticated_packet, Host, ?MODULE, s2s_in_packet, 50), ejabberd_hooks:delete(s2s_in_authenticated_packet, Host, ?MODULE, s2s_in_packet, 50), ejabberd_hooks:delete(s2s_out_packet, Host, ?MODULE, s2s_out_packet, 50), ejabberd_hooks:delete(s2s_out_downgraded, Host, ?MODULE, s2s_out_downgraded, 50), ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE, s2s_out_auth_result, 50). reload(Host, NewOpts, _OldOpts) -> case ejabberd_s2s:tls_verify(Host) of false -> start(Host, NewOpts); true -> stop(Host) end. depends(_Host, _Opts) -> []. mod_opt_type(_) -> []. s2s_in_features(Acc, _) -> [#db_feature{errors = true}|Acc]. s2s_out_init({ok, State}, Opts) -> case proplists:get_value(db_verify, Opts) of {StreamID, Key, Pid} -> %% This is an outbound s2s connection created at step 1. %% The purpose of this connection is to verify dialback key ONLY. %% The connection is not registered in s2s table and thus is not %% seen by anyone. %% The connection will be closed immediately after receiving the %% verification response (at step 3) {ok, State#{db_verify => {StreamID, Key, Pid}}}; undefined -> {ok, State#{db_enabled => true}} end; s2s_out_init(Acc, _Opts) -> Acc. s2s_out_closed(#{server := LServer, remote_server := RServer, db_verify := {StreamID, _Key, _Pid}} = State, Reason) -> %% Outbound s2s verificating connection (created at step 1) is %% closed suddenly without receiving the response. %% Building a response on our own Response = #db_verify{from = RServer, to = LServer, id = StreamID, type = error, sub_els = [mk_error(Reason)]}, s2s_out_packet(State, Response); s2s_out_closed(State, _Reason) -> State. s2s_out_auth_result(#{db_verify := _} = State, _) -> %% The temporary outbound s2s connect (intended for verification) %% has passed authentication state (either successfully or not, no matter) %% and at this point we can send verification request as described %% in section 2.1.2, step 2 {stop, send_verify_request(State)}; s2s_out_auth_result(#{db_enabled := true, socket := Socket, ip := IP, server := LServer, remote_server := RServer} = State, {false, _}) -> %% SASL authentication has failed, retrying with dialback %% Sending dialback request, section 2.1.1, step 1 ?INFO_MSG("(~s) Retrying with s2s dialback authentication: ~s -> ~s (~s)", [xmpp_socket:pp(Socket), LServer, RServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), State1 = maps:remove(stop_reason, State#{on_route => queue}), {stop, send_db_request(State1)}; s2s_out_auth_result(State, _) -> State. s2s_out_downgraded(#{db_verify := _} = State, _) -> %% The verifying outbound s2s connection detected non-RFC compliant %% server, send verification request immediately without auth phase, %% section 2.1.2, step 2 {stop, send_verify_request(State)}; s2s_out_downgraded(#{db_enabled := true, socket := Socket, ip := IP, server := LServer, remote_server := RServer} = State, _) -> %% non-RFC compliant server detected, send dialback request instantly, %% section 2.1.1, step 1 ?INFO_MSG("(~s) Trying s2s dialback authentication with " "non-RFC compliant server: ~s -> ~s (~s)", [xmpp_socket:pp(Socket), LServer, RServer, ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), {stop, send_db_request(State)}; s2s_out_downgraded(State, _) -> State. s2s_in_packet(#{stream_id := StreamID} = State, #db_result{from = From, to = To, key = Key, type = undefined}) -> %% Received dialback request, section 2.2.1, step 1 try ok = check_from_to(From, To), %% We're creating a temporary outbound s2s connection to %% send verification request and to receive verification response {ok, Pid} = ejabberd_s2s_out:start( To, From, [{db_verify, {StreamID, Key, self()}}]), ejabberd_s2s_out:connect(Pid), {stop, State} catch _:{badmatch, {error, Reason}} -> {stop, send_db_result(State, #db_verify{from = From, to = To, type = error, sub_els = [mk_error(Reason)]})} end; s2s_in_packet(State, #db_verify{to = To, from = From, key = Key, id = StreamID, type = undefined}) -> %% Received verification request, section 2.2.2, step 2 Type = case make_key(To, From, StreamID) of Key -> valid; _ -> invalid end, Response = #db_verify{from = To, to = From, id = StreamID, type = Type}, {stop, ejabberd_s2s_in:send(State, Response)}; s2s_in_packet(State, Pkt) when is_record(Pkt, db_result); is_record(Pkt, db_verify) -> ?WARNING_MSG("Got stray dialback packet:~n~s", [xmpp:pp(Pkt)]), State; s2s_in_packet(State, _) -> State. s2s_in_recv(State, El, {error, Why}) -> case xmpp:get_name(El) of Tag when Tag == <<"db:result">>; Tag == <<"db:verify">> -> case xmpp:get_type(El) of T when T /= <<"valid">>, T /= <<"invalid">>, T /= <<"error">> -> Err = xmpp:make_error(El, mk_error({codec_error, Why})), {stop, ejabberd_s2s_in:send(State, Err)}; _ -> State end; _ -> State end; s2s_in_recv(State, _El, _Pkt) -> State. s2s_out_packet(#{server := LServer, remote_server := RServer, db_verify := {StreamID, _Key, Pid}} = State, #db_verify{from = RServer, to = LServer, id = StreamID, type = Type} = Response) when Type /= undefined -> %% Received verification response, section 2.1.2, step 3 %% This is a response for the request sent at step 2 ejabberd_s2s_in:update_state( Pid, fun(S) -> send_db_result(S, Response) end), %% At this point the connection is no longer needed and we can terminate it ejabberd_s2s_out:stop(State); s2s_out_packet(#{server := LServer, remote_server := RServer} = State, #db_result{to = LServer, from = RServer, type = Type} = Result) when Type /= undefined -> %% Received dialback response, section 2.1.1, step 4 %% This is a response to the request sent at step 1 State1 = maps:remove(db_enabled, State), case Type of valid -> State2 = ejabberd_s2s_out:handle_auth_success(<<"dialback">>, State1), ejabberd_s2s_out:establish(State2); _ -> Reason = str:format("Peer responded with error: ~s", [format_error(Result)]), ejabberd_s2s_out:handle_auth_failure( <<"dialback">>, {auth, Reason}, State1) end; s2s_out_packet(State, Pkt) when is_record(Pkt, db_result); is_record(Pkt, db_verify) -> ?WARNING_MSG("Got stray dialback packet:~n~s", [xmpp:pp(Pkt)]), State; s2s_out_packet(State, _) -> State. %%%=================================================================== %%% Internal functions %%%=================================================================== -spec make_key(binary(), binary(), binary()) -> binary(). make_key(From, To, StreamID) -> Secret = ejabberd_config:get_option(shared_key), str:to_hexlist( crypto:hmac(sha256, str:to_hexlist(crypto:hash(sha256, Secret)), [To, " ", From, " ", StreamID])). -spec send_verify_request(ejabberd_s2s_out:state()) -> ejabberd_s2s_out:state(). send_verify_request(#{server := LServer, remote_server := RServer, db_verify := {StreamID, Key, _Pid}} = State) -> Request = #db_verify{from = LServer, to = RServer, key = Key, id = StreamID}, ejabberd_s2s_out:send(State, Request). -spec send_db_request(ejabberd_s2s_out:state()) -> ejabberd_s2s_out:state(). send_db_request(#{server := LServer, remote_server := RServer, stream_remote_id := StreamID} = State) -> Key = make_key(LServer, RServer, StreamID), ejabberd_s2s_out:send(State, #db_result{from = LServer, to = RServer, key = Key}). -spec send_db_result(ejabberd_s2s_in:state(), db_verify()) -> ejabberd_s2s_in:state(). send_db_result(State, #db_verify{from = From, to = To, type = Type, sub_els = Els}) -> %% Sending dialback response, section 2.2.1, step 4 %% This is a response to the request received at step 1 Response = #db_result{from = To, to = From, type = Type, sub_els = Els}, State1 = ejabberd_s2s_in:send(State, Response), case Type of valid -> State2 = ejabberd_s2s_in:handle_auth_success( From, <<"dialback">>, undefined, State1), ejabberd_s2s_in:establish(State2); _ -> Reason = str:format("Verification failed: ~s", [format_error(Response)]), ejabberd_s2s_in:handle_auth_failure( From, <<"dialback">>, Reason, State1) end. -spec check_from_to(binary(), binary()) -> ok | {error, forbidden | host_unknown}. check_from_to(From, To) -> case ejabberd_router:is_my_route(To) of false -> {error, host_unknown}; true -> LServer = ejabberd_router:host_of_route(To), case ejabberd_s2s:allow_host(LServer, From) of true -> ok; false -> {error, forbidden} end end. -spec mk_error(term()) -> stanza_error(). mk_error(forbidden) -> xmpp:err_forbidden(<<"Access denied by service policy">>, ?MYLANG); mk_error(host_unknown) -> xmpp:err_not_allowed(<<"Host unknown">>, ?MYLANG); mk_error({codec_error, Why}) -> xmpp:err_bad_request(xmpp:io_format_error(Why), ?MYLANG); mk_error({_Class, _Reason} = Why) -> Txt = xmpp_stream_out:format_error(Why), xmpp:err_remote_server_not_found(Txt, ?MYLANG); mk_error(_) -> xmpp:err_internal_server_error(). -spec format_error(db_result()) -> binary(). format_error(#db_result{type = invalid}) -> <<"invalid dialback key">>; format_error(#db_result{type = error} = Result) -> case xmpp:get_error(Result) of #stanza_error{} = Err -> format_stanza_error(Err); undefined -> <<"unrecognized error">> end; format_error(_) -> <<"unexpected dialback result">>. -spec format_stanza_error(stanza_error()) -> binary(). format_stanza_error(#stanza_error{reason = Reason, text = Txt}) -> Slogan = case Reason of undefined -> <<"no reason">>; #gone{} -> <<"gone">>; #redirect{} -> <<"redirect">>; _ -> erlang:atom_to_binary(Reason, latin1) end, case xmpp:get_text(Txt) of <<"">> -> Slogan; Data -> <> end. ejabberd-18.01/src/ejabberd_acme_comm.erl0000644000232200023220000003162013225664356020676 0ustar debalancedebalance-module(ejabberd_acme_comm). -export([%% Directory directory/1, %% Account new_account/4, update_account/4, get_account/3, delete_account/3, %% Authorization new_authz/4, get_authz/1, complete_challenge/4, %% Authorization polling get_authz_until_valid/1, %% Certificate new_cert/4, get_cert/1, revoke_cert/4, get_issuer_cert/1 %% Not yet implemented %% key_roll_over/5 %% delete_authz/3 ]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_acme.hrl"). -include_lib("public_key/include/public_key.hrl"). -define(REQUEST_TIMEOUT, 5000). % 5 seconds. -define(MAX_POLL_REQUESTS, 20). -define(POLL_WAIT_TIME, 500). % 500 ms. %%% %%% This module contains functions that implement all necessary http %%% requests to the ACME Certificate Authority. Its purpose is to %%% facilitate the acme client implementation by separating the %%% handling/validating/parsing of all the needed http requests. %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Directory %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec directory(url()) -> {ok, dirs(), nonce()} | {error, _}. directory(CAUrl) -> Url = CAUrl ++ "/directory", prepare_get_request(Url, fun get_dirs/1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Account Handling %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec new_account(dirs(), jose_jwk:key(), proplist(), nonce()) -> {ok, {url(), proplist()}, nonce()} | {error, _}. new_account(Dirs, PrivateKey, Req, Nonce) -> #{"new-reg" := Url} = Dirs, EJson = {[{ <<"resource">>, <<"new-reg">>}] ++ Req}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1). -spec update_account({url(), string()}, jose_jwk:key(), proplist(), nonce()) -> {ok, proplist(), nonce()} | {error, _}. update_account({CAUrl, AccId}, PrivateKey, Req, Nonce) -> Url = CAUrl ++ "/acme/reg/" ++ AccId, EJson = {[{ <<"resource">>, <<"reg">>}] ++ Req}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1). -spec get_account({url(), string()}, jose_jwk:key(), nonce()) -> {ok, {url(), proplist()}, nonce()} | {error, _}. get_account({CAUrl, AccId}, PrivateKey, Nonce) -> Url = CAUrl ++ "/acme/reg/" ++ AccId, EJson = {[{<<"resource">>, <<"reg">>}]}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1). -spec delete_account({url(), string()}, jose_jwk:key(), nonce()) -> {ok, proplist(), nonce()} | {error, _}. delete_account({CAUrl, AccId}, PrivateKey, Nonce) -> Url = CAUrl ++ "/acme/reg/" ++ AccId, EJson = {[{<<"resource">>, <<"reg">>}, {<<"status">>, <<"deactivated">>}]}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Authorization Handling %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec new_authz(dirs(), jose_jwk:key(), proplist(), nonce()) -> {ok, {url(), proplist()}, nonce()} | {error, _}. new_authz(Dirs, PrivateKey, Req, Nonce) -> #{"new-authz" := Url} = Dirs, EJson = {[{<<"resource">>, <<"new-authz">>}] ++ Req}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_location/1). -spec get_authz({url(), string()}) -> {ok, proplist(), nonce()} | {error, _}. get_authz({CAUrl, AuthzId}) -> Url = CAUrl ++ "/acme/authz/" ++ AuthzId, prepare_get_request(Url, fun get_response/1). -spec complete_challenge({url(), string(), string()}, jose_jwk:key(), proplist(), nonce()) -> {ok, proplist(), nonce()} | {error, _}. complete_challenge({CAUrl, AuthzId, ChallId}, PrivateKey, Req, Nonce) -> Url = CAUrl ++ "/acme/challenge/" ++ AuthzId ++ "/" ++ ChallId, EJson = {[{<<"resource">>, <<"challenge">>}] ++ Req}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Certificate Handling %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec new_cert(dirs(), jose_jwk:key(), proplist(), nonce()) -> {ok, {url(), list()}, nonce()} | {error, _}. new_cert(Dirs, PrivateKey, Req, Nonce) -> #{"new-cert" := Url} = Dirs, EJson = {[{<<"resource">>, <<"new-cert">>}] ++ Req}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_link_up/1, "application/pkix-cert"). -spec get_cert({url(), string()}) -> {ok, list(), nonce()} | {error, _}. get_cert({CAUrl, CertId}) -> Url = CAUrl ++ "/acme/cert/" ++ CertId, prepare_get_request(Url, fun get_response/1, "application/pkix-cert"). -spec revoke_cert(dirs(), jose_jwk:key(), proplist(), nonce()) -> {ok, _, nonce()} | {error, _}. revoke_cert(Dirs, PrivateKey, Req, Nonce) -> #{"revoke-cert" := Url} = Dirs, EJson = {[{<<"resource">>, <<"revoke-cert">>}] ++ Req}, prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1, "application/pkix-cert"). -spec get_issuer_cert(url()) -> {ok, list(), nonce()} | {error, _}. get_issuer_cert(IssuerCertUrl) -> prepare_get_request(IssuerCertUrl, fun get_response/1, "application/pkix-cert"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Handle Response Functions %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec get_dirs({ok, proplist(), proplist()}) -> {ok, map(), nonce()}. get_dirs({ok, Head, Return}) -> NewNonce = get_nonce(Head), StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} || {X, Y} <- Return, is_bitstring(X) andalso is_bitstring(Y)], NewDirs = maps:from_list(StrDirectories), {ok, NewDirs, NewNonce}. -spec get_response({ok, proplist(), proplist()}) -> {ok, proplist(), nonce()}. get_response({ok, Head, Return}) -> NewNonce = get_nonce(Head), {ok, Return, NewNonce}. -spec get_response_tos({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}. get_response_tos({ok, Head, Return}) -> TOSUrl = get_tos(Head), NewNonce = get_nonce(Head), {ok, {TOSUrl, Return}, NewNonce}. -spec get_response_location({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}. get_response_location({ok, Head, Return}) -> Location = get_location(Head), NewNonce = get_nonce(Head), {ok, {Location, Return}, NewNonce}. -spec get_response_link_up({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}. get_response_link_up({ok, Head, Return}) -> LinkUp = get_link_up(Head), NewNonce = get_nonce(Head), {ok, {LinkUp, Return}, NewNonce}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Authorization Polling %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec get_authz_until_valid({url(), string()}) -> {ok, proplist(), nonce()} | {error, _}. get_authz_until_valid({CAUrl, AuthzId}) -> get_authz_until_valid({CAUrl, AuthzId}, ?MAX_POLL_REQUESTS). -spec get_authz_until_valid({url(), string()}, non_neg_integer()) -> {ok, proplist(), nonce()} | {error, _}. get_authz_until_valid({_CAUrl, _AuthzId}, 0) -> ?ERROR_MSG("Maximum request limit waiting for validation reached", []), {error, max_request_limit}; get_authz_until_valid({CAUrl, AuthzId}, N) -> case get_authz({CAUrl, AuthzId}) of {ok, Resp, Nonce} -> case is_authz_valid(Resp) of true -> {ok, Resp, Nonce}; false -> timer:sleep(?POLL_WAIT_TIME), get_authz_until_valid({CAUrl, AuthzId}, N-1) end; {error, _} = Err -> Err end. -spec is_authz_valid(proplist()) -> boolean(). is_authz_valid(Authz) -> case proplists:lookup(<<"status">>, Authz) of {<<"status">>, <<"valid">>} -> true; _ -> false end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Request Functions %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% TODO: Fix the duplicated code at the below 4 functions -spec make_post_request(url(), bitstring(), string()) -> {ok, proplist(), proplist()} | {error, _}. make_post_request(Url, ReqBody, ResponseType) -> Options = [], HttpOptions = [{timeout, ?REQUEST_TIMEOUT}], case httpc:request(post, {Url, [], "application/jose+json", ReqBody}, HttpOptions, Options) of {ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 -> decode_response(Head, Body, ResponseType); Error -> failed_http_request(Error, Url) end. -spec make_get_request(url(), string()) -> {ok, proplist(), proplist()} | {error, _}. make_get_request(Url, ResponseType) -> Options = [], HttpOptions = [{timeout, ?REQUEST_TIMEOUT}], case httpc:request(get, {Url, []}, HttpOptions, Options) of {ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 -> decode_response(Head, Body, ResponseType); Error -> failed_http_request(Error, Url) end. -spec prepare_post_request(url(), jose_jwk:key(), jiffy:json_value(), nonce(), handle_resp_fun()) -> {ok, _, nonce()} | {error, _}. prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun) -> prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, "application/jose+json"). -spec prepare_post_request(url(), jose_jwk:key(), jiffy:json_value(), nonce(), handle_resp_fun(), string()) -> {ok, _, nonce()} | {error, _}. prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, ResponseType) -> case encode(EJson) of {ok, ReqBody} -> FinalBody = sign_encode_json_jose(PrivateKey, ReqBody, Nonce), case make_post_request(Url, FinalBody, ResponseType) of {ok, Head, Return} -> HandleRespFun({ok, Head, Return}); Error -> Error end; {error, Reason} -> ?ERROR_MSG("Error: ~p when encoding: ~p", [Reason, EJson]), {error, Reason} end. -spec prepare_get_request(url(), handle_resp_fun()) -> {ok, _, nonce()} | {error, _}. prepare_get_request(Url, HandleRespFun) -> prepare_get_request(Url, HandleRespFun, "application/jose+json"). -spec prepare_get_request(url(), handle_resp_fun(), string()) -> {ok, _, nonce()} | {error, _}. prepare_get_request(Url, HandleRespFun, ResponseType) -> case make_get_request(Url, ResponseType) of {ok, Head, Return} -> HandleRespFun({ok, Head, Return}); Error -> Error end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Jose Json Functions %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec sign_json_jose(jose_jwk:key(), bitstring(), nonce()) -> {_, jws()}. sign_json_jose(Key, Json, Nonce) -> PubKey = ejabberd_acme:to_public(Key), {_, BinaryPubKey} = jose_jwk:to_binary(PubKey), PubKeyJson = jiffy:decode(BinaryPubKey), %% TODO: Ensure this works for all cases AlgMap = jose_jwk:signer(Key), JwsMap = #{ <<"jwk">> => PubKeyJson, %% <<"b64">> => true, <<"nonce">> => list_to_bitstring(Nonce) }, JwsObj0 = maps:merge(JwsMap, AlgMap), JwsObj = jose_jws:from(JwsObj0), jose_jws:sign(Key, Json, JwsObj). -spec sign_encode_json_jose(jose_jwk:key(), bitstring(), nonce()) -> bitstring(). sign_encode_json_jose(Key, Json, Nonce) -> {_, Signed} = sign_json_jose(Key, Json, Nonce), %% This depends on jose library, so we can consider it safe jiffy:encode(Signed). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Useful funs %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec get_nonce(proplist()) -> nonce() | 'none'. get_nonce(Head) -> case proplists:lookup("replay-nonce", Head) of {"replay-nonce", Nonce} -> Nonce; none -> none end. -spec get_location(proplist()) -> url() | 'none'. get_location(Head) -> case proplists:lookup("location", Head) of {"location", Location} -> Location; none -> none end. -spec get_tos(proplist()) -> url() | 'none'. get_tos(Head) -> get_header_link(Head, "\"terms-of-service\""). -spec get_link_up(proplist()) -> url() | 'none'. get_link_up(Head) -> get_header_link(Head, "rel=\"up\""). %% TODO: Find a more reliable way to extract this -spec get_header_link(proplist(), string()) -> url() | 'none'. get_header_link(Head, Suffix) -> try [{_, Link}] = [{K, V} || {K, V} <- Head, K =:= "link" andalso lists:suffix(Suffix, V)], [Link1, _] = string:tokens(Link, ";"), Link2 = string:strip(Link1, left, $<), string:strip(Link2, right, $>) catch _:_ -> none end. decode_response(Head, Body, "application/pkix-cert") -> {ok, Head, Body}; decode_response(Head, Body, "application/jose+json") -> case decode(Body) of {ok, Return} -> {ok, Head, Return}; {error, Reason} -> ?ERROR_MSG("Problem decoding: ~s", [Body]), {error, Reason} end. encode(EJson) -> try {ok, jiffy:encode(EJson)} catch _:Reason -> {error, Reason} end. decode(Json) -> try {Result} = jiffy:decode(Json), {ok, Result} catch _:Reason -> {error, Reason} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Handle Failed HTTP Requests %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec failed_http_request({ok, _} | {error, _}, url()) -> {error, _}. failed_http_request({ok, {{_, Code, Reason}, _Head, Body}}, Url) -> ?ERROR_MSG("Got unexpected status code from <~s>: ~B, Body: ~s", [Url, Code, Body]), throw({error, {unexpected_code, Code, Reason}}); failed_http_request({error, Reason}, Url) -> ?ERROR_MSG("Error making a request to <~s>: ~p", [Url, Reason]), throw({error, Reason}). ejabberd-18.01/src/acl.erl0000644000232200023220000006210713225664356015703 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : acl.erl %%% Author : Alexey Shchepin %%% Purpose : ACL support %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(acl). -behaviour(gen_server). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([add_access/3, clear/0]). -export([start_link/0, add/3, add_list/3, add_local/3, add_list_local/3, load_from_config/0, reload_from_config/0, match_rule/3, any_rules_allowed/3, transform_options/1, opt_type/1, acl_rule_matches/3, acl_rule_verify/1, access_matches/3, transform_access_rules_config/1, parse_ip_netmask/1, access_rules_validator/1, shaper_rules_validator/1, normalize_spec/1, resolve_access/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("jid.hrl"). -record(acl, {aclname, aclspec}). -record(access, {name :: aclname(), rules = [] :: [access_rule()]}). -record(state, {}). -type regexp() :: binary(). -type iprange() :: {inet:ip_address(), integer()} | binary(). -type glob() :: binary(). -type access_name() :: atom(). -type access_rule() :: {atom(), any()}. -type host() :: binary(). -type aclname() :: {atom(), binary() | global}. -type aclspec() :: all | none | {user, {binary(), host()} | binary()} | {server, binary()} | {resource, binary()} | {user_regexp, {regexp(), host()} | regexp()} | {shared_group, {binary(), host()} | binary()} | {user_regexp, {regexp(), host()} | regexp()} | {server_regexp, regexp()} | {resource_regexp, regexp()} | {node_regexp, {regexp(), regexp()}} | {user_glob, {glob(), host()} | glob()} | {server_glob, glob()} | {resource_glob, glob()} | {ip, iprange()} | {node_glob, {glob(), glob()}}. -type acl() :: #acl{aclname :: aclname(), aclspec :: aclspec()}. -export_type([acl/0]). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> ejabberd_mnesia:create(?MODULE, acl, [{ram_copies, [node()]}, {type, bag}, {local_content, true}, {attributes, record_info(fields, acl)}]), ejabberd_mnesia:create(?MODULE, access, [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, access)}]), ejabberd_hooks:add(config_reloaded, ?MODULE, reload_from_config, 20), load_from_config(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. -spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}. add(Host, ACLName, ACLSpec) -> {ResL, BadNodes} = ejabberd_cluster:multicall( ?MODULE, add_local, [Host, ACLName, ACLSpec]), case lists:keyfind(aborted, 1, ResL) of false when BadNodes == [] -> ok; false -> {error, {failed_nodes, BadNodes}}; Err -> {error, Err} end. add_local(Host, ACLName, ACLSpec) -> F = fun () -> mnesia:write(#acl{aclname = {ACLName, Host}, aclspec = normalize_spec(ACLSpec)}) end, case mnesia:transaction(F) of {atomic, ok} -> ok; Err -> Err end. -spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}. add_list(Host, ACLs, Clear) -> {ResL, BadNodes} = ejabberd_cluster:multicall( ?MODULE, add_list_local, [Host, ACLs, Clear]), case lists:keyfind(aborted, 1, ResL) of false when BadNodes == [] -> ok; false -> {error, {failed_nodes, BadNodes}}; Err -> {error, Err} end. add_list_local(Host, ACLs, Clear) -> F = fun () -> if Clear -> Ks = mnesia:select(acl, [{{acl, {'$1', Host}, '$2'}, [], ['$1']}]), lists:foreach(fun (K) -> mnesia:delete({acl, {K, Host}}) end, Ks); true -> ok end, lists:foreach(fun (ACL) -> case ACL of #acl{aclname = ACLName, aclspec = ACLSpec} -> mnesia:write(#acl{aclname = {ACLName, Host}, aclspec = normalize_spec(ACLSpec)}) end end, ACLs) end, mnesia:transaction(F). -spec add_access(binary() | global, access_name(), [access_rule()]) -> ok | {error, any()}. add_access(Host, Access, Rules) -> Obj = #access{name = {Access, Host}, rules = Rules}, case mnesia:transaction(fun() -> mnesia:write(Obj) end) of {atomic, ok} -> ok; Err -> {error, Err} end. -spec load_from_config() -> ok. load_from_config() -> Hosts = [global|?MYHOSTS], lists:foreach( fun(Host) -> ACLs = ejabberd_config:get_option( {acl, Host}, []), AccessRules = ejabberd_config:get_option( {access, Host}, []), AccessRulesNew = ejabberd_config:get_option( {access_rules, Host}, []), ShaperRules = ejabberd_config:get_option( {shaper_rules, Host}, []), lists:foreach( fun({ACLName, SpecList}) -> lists:foreach( fun({ACLType, ACLSpecs}) when is_list(ACLSpecs) -> lists:foreach( fun(ACLSpec) -> add(Host, ACLName, {ACLType, ACLSpec}) end, lists:flatten(ACLSpecs)); ({ACLType, ACLSpecs}) -> add(Host, ACLName, {ACLType, ACLSpecs}) end, lists:flatten(SpecList)) end, ACLs), lists:foreach( fun({Access, Rules}) -> NRules = lists:map(fun({ACL, Type}) -> {Type, [{acl, ACL}]} end, Rules), add_access(Host, Access, NRules ++ [{deny, [all]}]) end, AccessRules), lists:foreach( fun({Access, Rules}) -> add_access(Host, Access, Rules) end, AccessRulesNew), lists:foreach( fun({Access, Rules}) -> add_access(Host, Access, Rules) end, ShaperRules) end, Hosts). -spec reload_from_config() -> ok. reload_from_config() -> clear(), load_from_config(). %% Delete all previous set ACLs and Access rules clear() -> mnesia:clear_table(acl), mnesia:clear_table(access), ok. b(S) -> iolist_to_binary(S). nodeprep(S) -> jid:nodeprep(b(S)). nameprep(S) -> jid:nameprep(b(S)). resourceprep(S) -> jid:resourceprep(b(S)). split_user_server(Str, NormFunUsr, NormFunSrv) -> case binary:split(Str, <<"@">>) of [U, S] -> {NormFunUsr(U), NormFunSrv(S)}; _ -> NormFunUsr(Str) end. normalize_spec(Spec) -> case Spec of all -> all; none -> none; {acl, N} when is_atom(N) -> {acl, N}; {user, {U, S}} when is_binary(U), is_binary(S) -> {user, {nodeprep(U), nameprep(S)}}; {user, U} when is_binary(U) -> {user, split_user_server(U, fun nodeprep/1, fun nameprep/1)}; {shared_group, {G, H}} when is_binary(G), is_binary(H) -> {shared_group, {b(G), nameprep(H)}}; {shared_group, G} when is_binary(G) -> {shared_group, split_user_server(G, fun b/1, fun nameprep/1)}; {user_regexp, {UR, S}} when is_binary(UR), is_binary(S) -> {user_regexp, {b(UR), nameprep(S)}}; {user_regexp, UR} when is_binary(UR) -> {user_regexp, split_user_server(UR, fun b/1, fun nameprep/1)}; {node_regexp, {UR, SR}} when is_binary(UR), is_binary(SR) -> {node_regexp, {b(UR), b(SR)}}; {user_glob, {UR, S}} when is_binary(UR), is_binary(S) -> {user_glob, {b(UR), nameprep(S)}}; {user_glob, UR} when is_binary(UR) -> {user_glob, split_user_server(UR, fun b/1, fun nameprep/1)}; {node_glob, {UR, SR}} when is_binary(UR), is_binary(SR) -> {node_glob, {b(UR), b(SR)}}; {server, S} when is_binary(S) -> {server, nameprep(S)}; {resource, R} when is_binary(R) -> {resource, resourceprep(R)}; {server_regexp, SR} when is_binary(SR) -> {server_regexp, b(SR)}; {resource_regexp, R} when is_binary(R) -> {resource_regexp, b(R)}; {server_glob, S} when is_binary(S) -> {server_glob, b(S)}; {resource_glob, R} when is_binary(R) -> {resource_glob, b(R)}; {ip, {Net, Mask}} when is_binary(Net), is_integer(Mask) -> {ip, {Net, Mask}}; {ip, S} -> case parse_ip_netmask(b(S)) of {ok, Net, Mask} -> {ip, {Net, Mask}}; error -> ?INFO_MSG("Invalid network address: ~p", [S]), none end; BadVal -> throw({<<"Invalid acl value">>, BadVal}) end. -spec any_rules_allowed(global | binary(), [access_name()], jid() | ljid() | inet:ip_address()) -> boolean(). any_rules_allowed(Host, Access, Entity) -> lists:any(fun (Rule) -> allow == acl:match_rule(Host, Rule, Entity) end, Access). -spec match_rule(global | binary(), access_name(), jid() | ljid() | inet:ip_address()) -> any(). match_rule(Host, Access, IP) when tuple_size(IP) == 4; tuple_size(IP) == 8 -> access_matches(Access, #{ip => IP}, Host); match_rule(Host, Access, JID) -> access_matches(Access, #{usr => jid:tolower(JID)}, Host). -spec acl_rule_verify(aclspec()) -> boolean(). acl_rule_verify(all) -> true; acl_rule_verify(none) -> true; acl_rule_verify({ip, {{A,B,C,D}, Mask}}) when is_integer(A), is_integer(B), is_integer(C), is_integer(D), A >= 0, A =< 255, B >= 0, B =< 255, C >= 0, C =< 255, D >= 0, D =< 255, is_integer(Mask), Mask >= 0, Mask =< 32 -> true; acl_rule_verify({ip, {{A,B,C,D,E,F,G,H}, Mask}}) when is_integer(A), is_integer(B), is_integer(C), is_integer(D), is_integer(E), is_integer(F), is_integer(G), is_integer(H), A >= 0, A =< 65535, B >= 0, B =< 65535, C >= 0, C =< 65535, D >= 0, D =< 65535, E >= 0, E =< 65535, F >= 0, F =< 65535, G >= 0, G =< 65535, H >= 0, H =< 65535, is_integer(Mask), Mask >= 0, Mask =< 64 -> true; acl_rule_verify({user, {U, S}}) when is_binary(U), is_binary(S) -> true; acl_rule_verify({user, U}) when is_binary(U) -> true; acl_rule_verify({server, S}) when is_binary(S) -> true; acl_rule_verify({resource, R}) when is_binary(R) -> true; acl_rule_verify({shared_group, {G, H}}) when is_binary(G), is_binary(H) -> true; acl_rule_verify({shared_group, G}) when is_binary(G) -> true; acl_rule_verify({user_regexp, {UR, S}}) when is_binary(UR), is_binary(S) -> true; acl_rule_verify({user_regexp, UR}) when is_binary(UR) -> true; acl_rule_verify({server_regexp, SR}) when is_binary(SR) -> true; acl_rule_verify({resource_regexp, RR}) when is_binary(RR) -> true; acl_rule_verify({node_regexp, {UR, SR}}) when is_binary(UR), is_binary(SR) -> true; acl_rule_verify({user_glob, {UR, S}}) when is_binary(UR), is_binary(S) -> true; acl_rule_verify({user_glob, UR}) when is_binary(UR) -> true; acl_rule_verify({server_glob, SR}) when is_binary(SR) -> true; acl_rule_verify({resource_glob, RR}) when is_binary(RR) -> true; acl_rule_verify({node_glob, {UR, SR}}) when is_binary(UR), is_binary(SR) -> true; acl_rule_verify(_Spec) -> false. invalid_syntax(Msg, Data) -> throw({invalid_syntax, (str:format(Msg, Data))}). acl_rules_verify([{acl, Name} | Rest], true) when is_atom(Name) -> acl_rules_verify(Rest, true); acl_rules_verify([{acl, Name} = Rule | _Rest], false) when is_atom(Name) -> invalid_syntax(<<"Using acl: rules not allowed: ~p">>, [Rule]); acl_rules_verify([Rule | Rest], AllowAcl) -> case acl_rule_verify(Rule) of false -> invalid_syntax(<<"Invalid rule: ~p">>, [Rule]); true -> acl_rules_verify(Rest, AllowAcl) end; acl_rules_verify([], _AllowAcl) -> true; acl_rules_verify(Rules, _AllowAcl) -> invalid_syntax(<<"Not a acl rules list: ~p">>, [Rules]). all_acl_rules_matches([], _Data, _Host) -> false; all_acl_rules_matches(Rules, Data, Host) -> all_acl_rules_matches2(Rules, Data, Host). all_acl_rules_matches2([Rule | Tail], Data, Host) -> case acl_rule_matches(Rule, Data, Host) of true -> all_acl_rules_matches2(Tail, Data, Host); false -> false end; all_acl_rules_matches2([], _Data, _Host) -> true. any_acl_rules_matches([], _Data, _Host) -> false; any_acl_rules_matches([Rule|Tail], Data, Host) -> case acl_rule_matches(Rule, Data, Host) of true -> true; false -> any_acl_rules_matches(Tail, Data, Host) end. -spec acl_rule_matches(aclspec(), any(), global|binary()) -> boolean(). acl_rule_matches(all, _Data, _Host) -> true; acl_rule_matches({acl, all}, _Data, _Host) -> true; acl_rule_matches({acl, Name}, Data, Host) -> ACLs = get_aclspecs(Name, Host), RawACLs = lists:map(fun(#acl{aclspec = R}) -> R end, ACLs), any_acl_rules_matches(RawACLs, Data, Host); acl_rule_matches({ip, {Net, Mask}}, #{ip := {IP, _Port}}, _Host) -> is_ip_match(IP, Net, Mask); acl_rule_matches({ip, {Net, Mask}}, #{ip := IP}, _Host) -> is_ip_match(IP, Net, Mask); acl_rule_matches({user, {U, S}}, #{usr := {U, S, _}}, _Host) -> true; acl_rule_matches({user, U}, #{usr := {U, S, _}}, _Host) -> lists:member(S, ?MYHOSTS); acl_rule_matches({server, S}, #{usr := {_, S, _}}, _Host) -> true; acl_rule_matches({resource, R}, #{usr := {_, _, R}}, _Host) -> true; acl_rule_matches({shared_group, {G, H}}, #{usr := {U, S, _}}, _Host) -> Mod = loaded_shared_roster_module(H), Mod:is_user_in_group({U, S}, G, H); acl_rule_matches({shared_group, G}, #{usr := {U, S, _}}, Host) -> Mod = loaded_shared_roster_module(Host), Mod:is_user_in_group({U, S}, G, Host); acl_rule_matches({user_regexp, {UR, S}}, #{usr := {U, S, _}}, _Host) -> is_regexp_match(U, UR); acl_rule_matches({user_regexp, UR}, #{usr := {U, S, _}}, _Host) -> lists:member(S, ?MYHOSTS) andalso is_regexp_match(U, UR); acl_rule_matches({server_regexp, SR}, #{usr := {_, S, _}}, _Host) -> is_regexp_match(S, SR); acl_rule_matches({resource_regexp, RR}, #{usr := {_, _, R}}, _Host) -> is_regexp_match(R, RR); acl_rule_matches({node_regexp, {UR, SR}}, #{usr := {U, S, _}}, _Host) -> is_regexp_match(U, UR) andalso is_regexp_match(S, SR); acl_rule_matches({user_glob, {UR, S}}, #{usr := {U, S, _}}, _Host) -> is_glob_match(U, UR); acl_rule_matches({user_glob, UR}, #{usr := {U, S, _}}, _Host) -> lists:member(S, ?MYHOSTS) andalso is_glob_match(U, UR); acl_rule_matches({server_glob, SR}, #{usr := {_, S, _}}, _Host) -> is_glob_match(S, SR); acl_rule_matches({resource_glob, RR}, #{usr := {_, _, R}}, _Host) -> is_glob_match(R, RR); acl_rule_matches({node_glob, {UR, SR}}, #{usr := {U, S, _}}, _Host) -> is_glob_match(U, UR) andalso is_glob_match(S, SR); acl_rule_matches(_ACL, _Data, _Host) -> false. resolve_access(all, _Host) -> all; resolve_access(none, _Host) -> none; resolve_access(Name, Host) when is_atom(Name) -> GAccess = mnesia:dirty_read(access, {Name, global}), LAccess = if Host /= global -> mnesia:dirty_read(access, {Name, Host}); true -> [] end, case GAccess ++ LAccess of [] -> []; AccessList -> lists:flatmap( fun(#access{rules = Rs}) -> Rs end, AccessList) end; resolve_access(Rules, _Host) when is_list(Rules) -> Rules. -spec access_matches(atom()|list(), any(), global|binary()) -> allow|deny|atom()|integer(). access_matches(Rules, Data, Host) -> case resolve_access(Rules, Host) of all -> allow; none -> deny; RRules -> access_rules_matches(RRules, Data, Host) end. -spec access_rules_matches(list(), any(), global|binary()) -> any(). access_rules_matches(AR, Data, Host) -> access_rules_matches(AR, Data, Host, deny). access_rules_matches([{Type, Acls} | Rest], Data, Host, Default) -> case all_acl_rules_matches(Acls, Data, Host) of false -> access_rules_matches(Rest, Data, Host, Default); true -> Type end; access_rules_matches([], _Data, _Host, Default) -> Default. get_aclspecs(ACL, Host) -> mnesia:dirty_read(acl, {ACL, Host}) ++ mnesia:dirty_read(acl, {ACL, global}). is_regexp_match(String, RegExp) -> case ejabberd_regexp:run(String, RegExp) of nomatch -> false; match -> true; {error, ErrDesc} -> ?ERROR_MSG("Wrong regexp ~p in ACL: ~p", [RegExp, ErrDesc]), false end. is_glob_match(String, Glob) -> is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). is_ip_match({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) -> IPInt = ip_to_integer(IP), NetInt = ip_to_integer(Net), M = bnot (1 bsl (32 - Mask) - 1), IPInt band M =:= NetInt band M; is_ip_match({_, _, _, _, _, _, _, _} = IP, {_, _, _, _, _, _, _, _} = Net, Mask) -> IPInt = ip_to_integer(IP), NetInt = ip_to_integer(Net), M = bnot (1 bsl (128 - Mask) - 1), IPInt band M =:= NetInt band M; is_ip_match(_, _, _) -> false. ip_to_integer({IP1, IP2, IP3, IP4}) -> IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4; ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7, IP8}) -> IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16 bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8. loaded_shared_roster_module(Host) -> case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of true -> mod_shared_roster_ldap; false -> mod_shared_roster end. parse_ip_netmask(S) -> case str:tokens(S, <<"/">>) of [IPStr] -> case inet_parse:address(binary_to_list(IPStr)) of {ok, {_, _, _, _} = IP} -> {ok, IP, 32}; {ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128}; _ -> error end; [IPStr, MaskStr] -> case catch binary_to_integer(MaskStr) of Mask when is_integer(Mask), Mask >= 0 -> case inet_parse:address(binary_to_list(IPStr)) of {ok, {_, _, _, _} = IP} when Mask =< 32 -> {ok, IP, Mask}; {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 -> {ok, IP, Mask}; _ -> error end; _ -> error end; _ -> error end. transform_access_rules_config(Config) when is_list(Config) -> lists:map(fun transform_access_rules_config2/1, lists:flatten(Config)); transform_access_rules_config(Config) -> transform_access_rules_config([Config]). transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) -> {Type, [all]}; transform_access_rules_config2({Type, ACL}) when is_atom(ACL) -> {Type, [{acl, ACL}]}; transform_access_rules_config2({Res, Rules}) when is_list(Rules) -> T = lists:map(fun({Type, Args}) when is_list(Args) -> normalize_spec({Type, hd(lists:flatten(Args))}); (V) -> normalize_spec(V) end, lists:flatten(Rules)), {Res, T}; transform_access_rules_config2({Res, Rule}) -> {Res, [Rule]}. access_rules_validator(Name) when is_atom(Name) -> Name; access_rules_validator(Rules0) -> Rules = transform_access_rules_config(Rules0), access_shaper_rules_validator(Rules, fun(allow) -> true; (deny) -> true; (_) -> false end), Rules. shaper_rules_validator(Name) when is_atom(Name) -> Name; shaper_rules_validator(Rules0) -> Rules = transform_access_rules_config(Rules0), access_shaper_rules_validator(Rules, fun(V) when is_atom(V) -> true; (V2) when is_integer(V2) -> true; (_) -> false end), Rules. access_shaper_rules_validator([{Type, Acls} = Rule | Rest], RuleTypeCheck) -> case RuleTypeCheck(Type) of true -> case acl_rules_verify(Acls, true) of true -> access_shaper_rules_validator(Rest, RuleTypeCheck); Err -> Err end; false -> invalid_syntax(<<"Invalid rule type: ~p in rule ~p">>, [Type, Rule]) end; access_shaper_rules_validator([], _RuleTypeCheck) -> true; access_shaper_rules_validator(Value, _RuleTypeCheck) -> invalid_syntax(<<"Not a rule definition: ~p">>, [Value]). transform_options(Opts) -> Opts1 = lists:foldl(fun transform_options/2, [], Opts), {ACLOpts, Opts2} = lists:mapfoldl( fun({acl, Os}, Acc) -> {Os, Acc}; (O, Acc) -> {[], [O|Acc]} end, [], Opts1), {AccessOpts, Opts3} = lists:mapfoldl( fun({access, Os}, Acc) -> {Os, Acc}; (O, Acc) -> {[], [O|Acc]} end, [], Opts2), {NewAccessOpts, Opts4} = lists:mapfoldl( fun({access_rules, Os}, Acc) -> {Os, Acc}; (O, Acc) -> {[], [O|Acc]} end, [], Opts3), {ShaperOpts, Opts5} = lists:mapfoldl( fun({shaper_rules, Os}, Acc) -> {Os, Acc}; (O, Acc) -> {[], [O|Acc]} end, [], Opts4), ACLOpts1 = ejabberd_config:collect_options(lists:flatten(ACLOpts)), AccessOpts1 = case ejabberd_config:collect_options( lists:flatten(AccessOpts)) of [] -> []; L1 -> [{access, L1}] end, ACLOpts2 = case lists:map( fun({ACLName, Os}) -> {ACLName, ejabberd_config:collect_options(Os)} end, ACLOpts1) of [] -> []; L2 -> [{acl, L2}] end, NewAccessOpts1 = case lists:map( fun({NAName, Os}) -> {NAName, transform_access_rules_config(Os)} end, lists:flatten(NewAccessOpts)) of [] -> []; L3 -> [{access_rules, L3}] end, ShaperOpts1 = case lists:map( fun({SName, Ss}) -> {SName, transform_access_rules_config(Ss)} end, lists:flatten(ShaperOpts)) of [] -> []; L4 -> [{shaper_rules, L4}] end, ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5. transform_options({acl, Name, Type}, Opts) -> T = case Type of all -> all; none -> none; {user, U} -> {user, [b(U)]}; {user, U, S} -> {user, [[{b(U), b(S)}]]}; {shared_group, G} -> {shared_group, [b(G)]}; {shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]}; {user_regexp, UR} -> {user_regexp, [b(UR)]}; {user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]}; {node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]}; {user_glob, UR} -> {user_glob, [b(UR)]}; {user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]}; {node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]}; {server, S} -> {server, [b(S)]}; {resource, R} -> {resource, [b(R)]}; {server_regexp, SR} -> {server_regexp, [b(SR)]}; {server_glob, S} -> {server_glob, [b(S)]}; {ip, S} -> {ip, [b(S)]}; {resource_glob, R} -> {resource_glob, [b(R)]}; {resource_regexp, R} -> {resource_regexp, [b(R)]} end, [{acl, [{Name, [T]}]}|Opts]; transform_options({access, Name, Rules}, Opts) -> NewRules = [{ACL, Action} || {Action, ACL} <- Rules], [{access, [{Name, NewRules}]}|Opts]; transform_options(Opt, Opts) -> [Opt|Opts]. opt_type(access) -> fun (V) -> V end; opt_type(access_rules) -> fun (V) -> V end; opt_type(shaper_rules) -> fun (V) -> V end; opt_type(acl) -> fun (V) -> V end; opt_type(_) -> [access, acl, access_rules, shaper_rules]. ejabberd-18.01/src/ejabberd_router_redis.erl0000644000232200023220000001214313225664356021463 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_router_redis). -behaviour(ejabberd_router). -behaviour(gen_server). %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, get_all_routes/0]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, start_link/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_router.hrl"). -record(state, {}). -define(ROUTES_KEY, <<"ejabberd:routes">>). -define(DOMAINS_KEY, <<"ejabberd:domains">>). %%%=================================================================== %%% API %%%=================================================================== init() -> Spec = {?MODULE, {?MODULE, start_link, []}, transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; Err -> Err end. -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). register_route(Domain, ServerHost, LocalHint, _, Pid) -> DomKey = domain_key(Domain), PidKey = term_to_binary(Pid), T = term_to_binary({ServerHost, LocalHint}), case ejabberd_redis:multi( fun() -> ejabberd_redis:hset(DomKey, PidKey, T), ejabberd_redis:sadd(?DOMAINS_KEY, [Domain]), if Domain /= ServerHost -> ejabberd_redis:sadd(?ROUTES_KEY, [Domain]); true -> ok end end) of {ok, _} -> ok; {error, _} -> {error, db_failure} end. unregister_route(Domain, _, Pid) -> DomKey = domain_key(Domain), PidKey = term_to_binary(Pid), try {ok, Num} = ejabberd_redis:hdel(DomKey, [PidKey]), if Num > 0 -> {ok, Len} = ejabberd_redis:hlen(DomKey), if Len == 0 -> {ok, _} = ejabberd_redis:multi( fun() -> ejabberd_redis:del([DomKey]), ejabberd_redis:srem(?ROUTES_KEY, [Domain]), ejabberd_redis:srem(?DOMAINS_KEY, [Domain]) end), ok; true -> ok end; true -> ok end catch _:{badmatch, {error, _}} -> {error, db_failure} end. find_routes(Domain) -> DomKey = domain_key(Domain), case ejabberd_redis:hgetall(DomKey) of {ok, Vals} -> {ok, decode_routes(Domain, Vals)}; _ -> {error, db_failure} end. get_all_routes() -> case ejabberd_redis:smembers(?ROUTES_KEY) of {ok, Routes} -> {ok, Routes}; _ -> {error, db_failure} end. get_all_domains() -> case ejabberd_redis:smembers(?DOMAINS_KEY) of {ok, Domains} -> {ok, Domains}; _ -> {error, db_failure} end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> clean_table(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(Info, State) -> ?ERROR_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== clean_table() -> ?DEBUG("Cleaning Redis route entries...", []), lists:foreach( fun(#route{domain = Domain, pid = Pid}) when node(Pid) == node() -> unregister_route(Domain, undefined, Pid); (_) -> ok end, find_routes()). find_routes() -> case get_all_domains() of {ok, Domains} -> lists:flatmap( fun(Domain) -> case find_routes(Domain) of {ok, Routes} -> Routes; {error, _} -> [] end end, Domains); {error, _} -> [] end. domain_key(Domain) -> <<"ejabberd:route:", Domain/binary>>. decode_routes(Domain, Vals) -> lists:map( fun({Pid, Data}) -> {ServerHost, LocalHint} = binary_to_term(Data), #route{domain = Domain, pid = binary_to_term(Pid), server_host = ServerHost, local_hint = LocalHint} end, Vals). ejabberd-18.01/src/mod_bosh_sql.erl0000644000232200023220000000542713225664356017617 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_bosh_sql.erl %%% Author : Evgeny Khramtsov %%% Purpose : %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2017-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_bosh_sql). -behaviour(mod_bosh). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/0, open_session/2, close_session/1, find_session/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). %%%=================================================================== %%% API %%%=================================================================== init() -> Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'bosh' table...", []), case ejabberd_sql:sql_query( ?MYNAME, ?SQL("delete from bosh where node=%(Node)s")) of {updated, _} -> ok; Err -> ?ERROR_MSG("failed to clean 'route' table: ~p", [Err]), Err end. open_session(SID, Pid) -> PidS = misc:encode_pid(Pid), Node = erlang:atom_to_binary(node(Pid), latin1), case ?SQL_UPSERT(?MYNAME, "bosh", ["!sid=%(SID)s", "node=%(Node)s", "pid=%(PidS)s"]) of ok -> ok; _Err -> {error, db_failure} end. close_session(SID) -> case ejabberd_sql:sql_query( ?MYNAME, ?SQL("delete from bosh where sid=%(SID)s")) of {updated, _} -> ok; _Err -> {error, db_failure} end. find_session(SID) -> case ejabberd_sql:sql_query( ?MYNAME, ?SQL("select @(pid)s, @(node)s from bosh where sid=%(SID)s")) of {selected, [{Pid, Node}]} -> try {ok, misc:decode_pid(Pid, Node)} catch _:{bad_node, _} -> {error, notfound} end; {selected, []} -> {error, notfound}; _Err -> {error, db_failure} end. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/mod_muc_room.erl0000644000232200023220000044202313225664356017622 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_muc_room.erl %%% Author : Alexey Shchepin %%% Purpose : MUC room stuff %%% Created : 19 Mar 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_muc_room). -author('alexey@process-one.net'). -behaviour(p1_fsm). %% External exports -export([start_link/10, start_link/8, start/10, start/8, get_role/2, get_affiliation/2, is_occupant_or_admin/2, route/2]). %% gen_fsm callbacks -export([init/1, normal_state/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_muc_room.hrl"). -define(MAX_USERS_DEFAULT_LIST, [5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]). -define(DEFAULT_MAX_USERS_PRESENCE,1000). %-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). -endif. -type state() :: #state{}. -type fsm_stop() :: {stop, normal, state()}. -type fsm_next() :: {next_state, normal_state, state()}. -type fsm_transition() :: fsm_stop() | fsm_next(). -export_type([state/0]). -callback set_affiliation(binary(), binary(), binary(), jid(), affiliation(), binary()) -> ok | {error, any()}. -callback set_affiliations(binary(), binary(), binary(), ?TDICT) -> ok | {error, any()}. -callback get_affiliation(binary(), binary(), binary(), binary(), binary()) -> {ok, affiliation()} | {error, any()}. -callback get_affiliations(binary(), binary(), binary()) -> {ok, ?TDICT} | {error, any()}. -callback search_affiliation(binary(), binary(), binary(), affiliation()) -> {ok, [{ljid(), {affiliation(), binary()}}]} | {error, any()}. %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, Nick, DefRoomOpts, QueueType) -> p1_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, Nick, DefRoomOpts, QueueType], ?FSMOPTS). start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType) -> p1_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType], ?FSMOPTS). start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, Nick, DefRoomOpts, QueueType) -> p1_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, Nick, DefRoomOpts, QueueType], ?FSMOPTS). start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType) -> p1_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType], ?FSMOPTS). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts, QueueType]) -> process_flag(trap_exit, true), Shaper = shaper:new(RoomShaper), RoomQueue = room_queue_new(ServerHost, Shaper, QueueType), State = set_affiliation(Creator, owner, #state{host = Host, server_host = ServerHost, access = Access, room = Room, history = lqueue_new(HistorySize, QueueType), jid = jid:make(Room, Host), just_created = true, room_queue = RoomQueue, room_shaper = Shaper}), State1 = set_opts(DefRoomOpts, State), store_room(State1), ?INFO_MSG("Created MUC room ~s@~s by ~s", [Room, Host, jid:encode(Creator)]), add_to_log(room_existence, created, State1), add_to_log(room_existence, started, State1), {ok, normal_state, State1}; init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType]) -> process_flag(trap_exit, true), Shaper = shaper:new(RoomShaper), RoomQueue = room_queue_new(ServerHost, Shaper, QueueType), State = set_opts(Opts, #state{host = Host, server_host = ServerHost, access = Access, room = Room, history = lqueue_new(HistorySize, QueueType), jid = jid:make(Room, Host), room_queue = RoomQueue, room_shaper = Shaper}), add_to_log(room_existence, started, State), {ok, normal_state, State}. normal_state({route, <<"">>, #message{from = From, type = Type, lang = Lang} = Packet}, StateData) -> case is_user_online(From, StateData) orelse is_subscriber(From, StateData) orelse is_user_allowed_message_nonparticipant(From, StateData) of true when Type == groupchat -> Activity = get_user_activity(From, StateData), Now = p1_time_compat:system_time(micro_seconds), MinMessageInterval = trunc(gen_mod:get_module_opt( StateData#state.server_host, mod_muc, min_message_interval, 0) * 1000000), Size = element_size(Packet), {MessageShaper, MessageShaperInterval} = shaper:update(Activity#activity.message_shaper, Size), if Activity#activity.message /= undefined -> ErrText = <<"Traffic rate limit is exceeded">>, Err = xmpp:err_resource_constraint(ErrText, Lang), ejabberd_router:route_error(Packet, Err), {next_state, normal_state, StateData}; Now >= Activity#activity.message_time + MinMessageInterval, MessageShaperInterval == 0 -> {RoomShaper, RoomShaperInterval} = shaper:update(StateData#state.room_shaper, Size), RoomQueueEmpty = case StateData#state.room_queue of undefined -> true; RQ -> p1_queue:is_empty(RQ) end, if RoomShaperInterval == 0, RoomQueueEmpty -> NewActivity = Activity#activity{ message_time = Now, message_shaper = MessageShaper}, StateData1 = store_user_activity(From, NewActivity, StateData), StateData2 = StateData1#state{room_shaper = RoomShaper}, process_groupchat_message(Packet, StateData2); true -> StateData1 = if RoomQueueEmpty -> erlang:send_after(RoomShaperInterval, self(), process_room_queue), StateData#state{room_shaper = RoomShaper}; true -> StateData end, NewActivity = Activity#activity{ message_time = Now, message_shaper = MessageShaper, message = Packet}, RoomQueue = p1_queue:in({message, From}, StateData#state.room_queue), StateData2 = store_user_activity(From, NewActivity, StateData1), StateData3 = StateData2#state{room_queue = RoomQueue}, {next_state, normal_state, StateData3} end; true -> MessageInterval = (Activity#activity.message_time + MinMessageInterval - Now) div 1000, Interval = lists:max([MessageInterval, MessageShaperInterval]), erlang:send_after(Interval, self(), {process_user_message, From}), NewActivity = Activity#activity{ message = Packet, message_shaper = MessageShaper}, StateData1 = store_user_activity(From, NewActivity, StateData), {next_state, normal_state, StateData1} end; true when Type == error -> case is_user_online(From, StateData) of true -> ErrorText = <<"It is not allowed to send error messages to the" " room. The participant (~s) has sent an error " "message (~s) and got kicked from the room">>, NewState = expulse_participant(Packet, From, StateData, translate:translate(Lang, ErrorText)), close_room_if_temporary_and_empty(NewState); _ -> {next_state, normal_state, StateData} end; true when Type == chat -> ErrText = <<"It is not allowed to send private messages " "to the conference">>, Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error(Packet, Err), {next_state, normal_state, StateData}; true when Type == normal -> {next_state, normal_state, try xmpp:decode_els(Packet) of Pkt -> process_normal_message(From, Pkt, StateData) catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), StateData end}; true -> ErrText = <<"Improper message type">>, Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error(Packet, Err), {next_state, normal_state, StateData}; false when Type /= error -> handle_roommessage_from_nonparticipant(Packet, StateData, From), {next_state, normal_state, StateData}; false -> {next_state, normal_state, StateData} end; normal_state({route, <<"">>, #iq{from = From, type = Type, lang = Lang, sub_els = [_]} = IQ0}, StateData) when Type == get; Type == set -> try case ejabberd_hooks:run_fold( muc_process_iq, StateData#state.server_host, xmpp:set_from_to(xmpp:decode_els(IQ0), From, StateData#state.jid), [StateData]) of ignore -> {next_state, normal_state, StateData}; #iq{type = T} = IQRes when T == error; T == result -> ejabberd_router:route(IQRes), {next_state, normal_state, StateData}; #iq{sub_els = [SubEl]} = IQ -> Res1 = case xmpp:get_ns(SubEl) of ?NS_MUC_ADMIN -> process_iq_admin(From, IQ, StateData); ?NS_MUC_OWNER -> process_iq_owner(From, IQ, StateData); ?NS_DISCO_INFO when SubEl#disco_info.node == <<>> -> process_iq_disco_info(From, IQ, StateData); ?NS_DISCO_ITEMS -> process_iq_disco_items(From, IQ, StateData); ?NS_VCARD -> process_iq_vcard(From, IQ, StateData); ?NS_MUCSUB -> process_iq_mucsub(From, IQ, StateData); ?NS_CAPTCHA -> process_iq_captcha(From, IQ, StateData); _ -> Txt = <<"The feature requested is not " "supported by the conference">>, {error, xmpp:err_service_unavailable(Txt, Lang)} end, {IQRes, NewStateData} = case Res1 of {result, Res, SD} -> {xmpp:make_iq_result(IQ, Res), SD}; {result, Res} -> {xmpp:make_iq_result(IQ, Res), StateData}; {ignore, SD} -> {ignore, SD}; {error, Error} -> {xmpp:make_error(IQ0, Error), StateData} end, if IQRes /= ignore -> ejabberd_router:route(IQRes); true -> ok end, case NewStateData of stop -> {stop, normal, StateData}; _ when NewStateData#state.just_created -> close_room_if_temporary_and_empty(NewStateData); _ -> {next_state, normal_state, NewStateData} end end catch _:{xmpp_codec, Why} -> ErrTxt = xmpp:io_format_error(Why), Err = xmpp:err_bad_request(ErrTxt, Lang), ejabberd_router:route_error(IQ0, Err), {next_state, normal_state, StateData} end; normal_state({route, <<"">>, #iq{} = IQ}, StateData) -> Err = xmpp:err_bad_request(), ejabberd_router:route_error(IQ, Err), case StateData#state.just_created of true -> {stop, normal, StateData}; false -> {next_state, normal_state, StateData} end; normal_state({route, Nick, #presence{from = From} = Packet}, StateData) -> Activity = get_user_activity(From, StateData), Now = p1_time_compat:system_time(micro_seconds), MinPresenceInterval = trunc(gen_mod:get_module_opt(StateData#state.server_host, mod_muc, min_presence_interval, 0) * 1000000), if (Now >= Activity#activity.presence_time + MinPresenceInterval) and (Activity#activity.presence == undefined) -> NewActivity = Activity#activity{presence_time = Now}, StateData1 = store_user_activity(From, NewActivity, StateData), process_presence(Nick, Packet, StateData1); true -> if Activity#activity.presence == undefined -> Interval = (Activity#activity.presence_time + MinPresenceInterval - Now) div 1000, erlang:send_after(Interval, self(), {process_user_presence, From}); true -> ok end, NewActivity = Activity#activity{presence = {Nick, Packet}}, StateData1 = store_user_activity(From, NewActivity, StateData), {next_state, normal_state, StateData1} end; normal_state({route, ToNick, #message{from = From, type = Type, lang = Lang} = Packet}, StateData) -> case decide_fate_message(Packet, From, StateData) of {expulse_sender, Reason} -> ?DEBUG(Reason, []), ErrorText = <<"It is not allowed to send error messages to the" " room. The participant (~s) has sent an error " "message (~s) and got kicked from the room">>, NewState = expulse_participant(Packet, From, StateData, translate:translate(Lang, ErrorText)), {next_state, normal_state, NewState}; forget_message -> {next_state, normal_state, StateData}; continue_delivery -> case {(StateData#state.config)#config.allow_private_messages, is_user_online(From, StateData) orelse is_subscriber(From, StateData)} of {true, true} when Type == groupchat -> ErrText = <<"It is not allowed to send private messages " "of type \"groupchat\"">>, Err = xmpp:err_bad_request(ErrText, Lang), ejabberd_router:route_error(Packet, Err); {true, true} -> case find_jids_by_nick(ToNick, StateData) of [] -> ErrText = <<"Recipient is not in the conference room">>, Err = xmpp:err_item_not_found(ErrText, Lang), ejabberd_router:route_error(Packet, Err); ToJIDs -> SrcIsVisitor = is_visitor(From, StateData), DstIsModerator = is_moderator(hd(ToJIDs), StateData), PmFromVisitors = (StateData#state.config)#config.allow_private_messages_from_visitors, if SrcIsVisitor == false; PmFromVisitors == anyone; (PmFromVisitors == moderators) and DstIsModerator -> {FromNick, _} = get_participant_data(From, StateData), FromNickJID = jid:replace_resource(StateData#state.jid, FromNick), X = #muc_user{}, PrivMsg = xmpp:set_from( xmpp:set_subtag(Packet, X), FromNickJID), [ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID)) || ToJID <- ToJIDs]; true -> ErrText = <<"It is not allowed to send private messages">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end end; {true, false} -> ErrText = <<"Only occupants are allowed to send messages " "to the conference">>, Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error(Packet, Err); {false, _} -> ErrText = <<"It is not allowed to send private messages">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end, {next_state, normal_state, StateData} end; normal_state({route, ToNick, #iq{from = From, type = Type, lang = Lang} = Packet}, StateData) -> case {(StateData#state.config)#config.allow_query_users, (?DICT):find(jid:tolower(From), StateData#state.users)} of {true, {ok, #user{nick = FromNick}}} -> case find_jid_by_nick(ToNick, StateData) of false -> ErrText = <<"Recipient is not in the conference room">>, Err = xmpp:err_item_not_found(ErrText, Lang), ejabberd_router:route_error(Packet, Err); To -> FromJID = jid:replace_resource(StateData#state.jid, FromNick), if Type == get; Type == set -> ToJID = case is_vcard_request(Packet) of true -> jid:remove_resource(To); false -> To end, ejabberd_router:route_iq( xmpp:set_from_to(Packet, FromJID, ToJID), Packet, self()); true -> ejabberd_router:route( xmpp:set_from_to(Packet, FromJID, To)) end end; {true, error} -> ErrText = <<"Only occupants are allowed to send queries " "to the conference">>, Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error(Packet, Err); _ -> ErrText = <<"Queries to the conference members are " "not allowed in this room">>, Err = xmpp:err_not_allowed(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end, {next_state, normal_state, StateData}; normal_state(_Event, StateData) -> {next_state, normal_state, StateData}. handle_event({service_message, Msg}, _StateName, StateData) -> MessagePkt = #message{type = groupchat, body = xmpp:mk_text(Msg)}, send_wrapped_multiple( StateData#state.jid, get_users_and_subscribers(StateData), MessagePkt, ?NS_MUCSUB_NODES_MESSAGES, StateData), NSD = add_message_to_history(<<"">>, StateData#state.jid, MessagePkt, StateData), {next_state, normal_state, NSD}; handle_event({destroy, Reason}, _StateName, StateData) -> {result, undefined, stop} = destroy_room(#muc_destroy{xmlns = ?NS_MUC_OWNER, reason = Reason}, StateData), ?INFO_MSG("Destroyed MUC room ~s with reason: ~p", [jid:encode(StateData#state.jid), Reason]), add_to_log(room_existence, destroyed, StateData), {stop, shutdown, StateData}; handle_event(destroy, StateName, StateData) -> ?INFO_MSG("Destroyed MUC room ~s", [jid:encode(StateData#state.jid)]), handle_event({destroy, <<"">>}, StateName, StateData); handle_event({set_affiliations, Affiliations}, StateName, StateData) -> {next_state, StateName, StateData#state{affiliations = Affiliations}}; handle_event(_Event, StateName, StateData) -> {next_state, StateName, StateData}. handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) -> Len = ?DICT:size(StateData#state.users), Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of true -> get_roomdesc_reply(JID, StateData, get_roomdesc_tail(StateData, Lang)); false -> false end, {reply, Reply, StateName, StateData}; %% This clause is only for backwards compatibility handle_sync_event({get_disco_item, JID, Lang}, From, StateName, StateData) -> handle_sync_event({get_disco_item, any, JID, Lang}, From, StateName, StateData); handle_sync_event(get_config, _From, StateName, StateData) -> {reply, {ok, StateData#state.config}, StateName, StateData}; handle_sync_event(get_state, _From, StateName, StateData) -> {reply, {ok, StateData}, StateName, StateData}; handle_sync_event({change_config, Config}, _From, StateName, StateData) -> {result, undefined, NSD} = change_config(Config, StateData), {reply, {ok, NSD#state.config}, StateName, NSD}; handle_sync_event({change_state, NewStateData}, _From, StateName, _StateData) -> {reply, {ok, NewStateData}, StateName, NewStateData}; handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData) -> case process_item_change(Item, StateData, UJID) of {error, _} = Err -> {reply, Err, StateName, StateData}; NSD -> {reply, {ok, NSD}, StateName, NSD} end; handle_sync_event(get_subscribers, _From, StateName, StateData) -> JIDs = lists:map(fun jid:make/1, ?DICT:fetch_keys(StateData#state.subscribers)), {reply, {ok, JIDs}, StateName, StateData}; handle_sync_event({muc_subscribe, From, Nick, Nodes}, _From, StateName, StateData) -> IQ = #iq{type = set, id = randoms:get_string(), from = From, sub_els = [#muc_subscribe{nick = Nick, events = Nodes}]}, Config = StateData#state.config, CaptchaRequired = Config#config.captcha_protected, PasswordProtected = Config#config.password_protected, TmpConfig = Config#config{captcha_protected = false, password_protected = false}, TmpState = StateData#state{config = TmpConfig}, case process_iq_mucsub(From, IQ, TmpState) of {result, #muc_subscribe{events = NewNodes}, NewState} -> NewConfig = (NewState#state.config)#config{ captcha_protected = CaptchaRequired, password_protected = PasswordProtected}, {reply, {ok, NewNodes}, StateName, NewState#state{config = NewConfig}}; {ignore, NewState} -> NewConfig = (NewState#state.config)#config{ captcha_protected = CaptchaRequired, password_protected = PasswordProtected}, {reply, {error, <<"Request is ignored">>}, NewState#state{config = NewConfig}}; {error, Err} -> {reply, {error, get_error_text(Err)}, StateName, StateData} end; handle_sync_event({muc_unsubscribe, From}, _From, StateName, StateData) -> IQ = #iq{type = set, id = randoms:get_string(), from = From, sub_els = [#muc_unsubscribe{}]}, case process_iq_mucsub(From, IQ, StateData) of {result, _, NewState} -> {reply, ok, StateName, NewState}; {ignore, NewState} -> {reply, {error, <<"Request is ignored">>}, NewState}; {error, Err} -> {reply, {error, get_error_text(Err)}, StateName, StateData} end; handle_sync_event({is_subscribed, From}, _From, StateName, StateData) -> IsSubs = ?DICT:is_key(jid:split(From), StateData#state.subscribers), {reply, IsSubs, StateName, StateData}; handle_sync_event(_Event, _From, StateName, StateData) -> Reply = ok, {reply, Reply, StateName, StateData}. code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}. handle_info({process_user_presence, From}, normal_state = _StateName, StateData) -> RoomQueueEmpty = p1_queue:is_empty(StateData#state.room_queue), RoomQueue = p1_queue:in({presence, From}, StateData#state.room_queue), StateData1 = StateData#state{room_queue = RoomQueue}, if RoomQueueEmpty -> StateData2 = prepare_room_queue(StateData1), {next_state, normal_state, StateData2}; true -> {next_state, normal_state, StateData1} end; handle_info({process_user_message, From}, normal_state = _StateName, StateData) -> RoomQueueEmpty = p1_queue:is_empty(StateData#state.room_queue), RoomQueue = p1_queue:in({message, From}, StateData#state.room_queue), StateData1 = StateData#state{room_queue = RoomQueue}, if RoomQueueEmpty -> StateData2 = prepare_room_queue(StateData1), {next_state, normal_state, StateData2}; true -> {next_state, normal_state, StateData1} end; handle_info(process_room_queue, normal_state = StateName, StateData) -> case p1_queue:out(StateData#state.room_queue) of {{value, {message, From}}, RoomQueue} -> Activity = get_user_activity(From, StateData), Packet = Activity#activity.message, NewActivity = Activity#activity{message = undefined}, StateData1 = store_user_activity(From, NewActivity, StateData), StateData2 = StateData1#state{room_queue = RoomQueue}, StateData3 = prepare_room_queue(StateData2), process_groupchat_message(Packet, StateData3); {{value, {presence, From}}, RoomQueue} -> Activity = get_user_activity(From, StateData), {Nick, Packet} = Activity#activity.presence, NewActivity = Activity#activity{presence = undefined}, StateData1 = store_user_activity(From, NewActivity, StateData), StateData2 = StateData1#state{room_queue = RoomQueue}, StateData3 = prepare_room_queue(StateData2), process_presence(Nick, Packet, StateData3); {empty, _} -> {next_state, StateName, StateData} end; handle_info({captcha_succeed, From}, normal_state, StateData) -> NewState = case (?DICT):find(From, StateData#state.robots) of {ok, {Nick, Packet}} -> Robots = (?DICT):store(From, passed, StateData#state.robots), add_new_user(From, Nick, Packet, StateData#state{robots = Robots}); _ -> StateData end, {next_state, normal_state, NewState}; handle_info({captcha_failed, From}, normal_state, StateData) -> NewState = case (?DICT):find(From, StateData#state.robots) of {ok, {_Nick, Packet}} -> Robots = (?DICT):erase(From, StateData#state.robots), Txt = <<"The CAPTCHA verification has failed">>, Lang = xmpp:get_lang(Packet), Err = xmpp:err_not_authorized(Txt, Lang), ejabberd_router:route_error(Packet, Err), StateData#state{robots = Robots}; _ -> StateData end, {next_state, normal_state, NewState}; handle_info(shutdown, _StateName, StateData) -> {stop, shutdown, StateData}; handle_info({iq_reply, #iq{type = Type, sub_els = Els}, #iq{from = From, to = To} = IQ}, StateName, StateData) -> ejabberd_router:route( xmpp:set_from_to( IQ#iq{type = Type, sub_els = Els}, To, From)), {next_state, StateName, StateData}; handle_info({iq_reply, timeout, IQ}, StateName, StateData) -> Txt = <<"Request has timed out">>, Err = xmpp:err_recipient_unavailable(Txt, IQ#iq.lang), ejabberd_router:route_error(IQ, Err), {next_state, StateName, StateData}; handle_info(_Info, StateName, StateData) -> {next_state, StateName, StateData}. terminate(Reason, _StateName, StateData) -> ?INFO_MSG("Stopping MUC room ~s@~s", [StateData#state.room, StateData#state.host]), ReasonT = case Reason of shutdown -> <<"You are being removed from the room " "because of a system shutdown">>; _ -> <<"Room terminates">> end, Packet = #presence{ type = unavailable, sub_els = [#muc_user{items = [#muc_item{affiliation = none, reason = ReasonT, role = none}], status_codes = [332,110]}]}, (?DICT):fold(fun (LJID, Info, _) -> Nick = Info#user.nick, case Reason of shutdown -> send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, ?NS_MUCSUB_NODES_PARTICIPANTS, StateData); _ -> ok end, tab_remove_online_user(LJID, StateData) end, [], get_users_and_subscribers(StateData)), add_to_log(room_existence, stopped, StateData), mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(), StateData#state.server_host), ok. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- -spec route(pid(), stanza()) -> ok. route(Pid, Packet) -> #jid{lresource = Nick} = xmpp:get_to(Packet), p1_fsm:send_event(Pid, {route, Nick, Packet}). -spec process_groupchat_message(message(), state()) -> fsm_next(). process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData) -> IsSubscriber = is_subscriber(From, StateData), case is_user_online(From, StateData) orelse IsSubscriber orelse is_user_allowed_message_nonparticipant(From, StateData) of true -> {FromNick, Role} = get_participant_data(From, StateData), if (Role == moderator) or (Role == participant) or IsSubscriber or ((StateData#state.config)#config.moderated == false) -> Subject = check_subject(Packet), {NewStateData1, IsAllowed} = case Subject of [] -> {StateData, true}; _ -> case can_change_subject(Role, IsSubscriber, StateData) of true -> NSD = StateData#state{subject = Subject, subject_author = FromNick}, store_room(NSD), {NSD, true}; _ -> {StateData, false} end end, case IsAllowed of true -> case ejabberd_hooks:run_fold(muc_filter_message, StateData#state.server_host, Packet, [StateData, FromNick]) of drop -> {next_state, normal_state, StateData}; NewPacket1 -> NewPacket = xmpp:remove_subtag(NewPacket1, #nick{}), Node = if Subject == [] -> ?NS_MUCSUB_NODES_MESSAGES; true -> ?NS_MUCSUB_NODES_SUBJECT end, send_wrapped_multiple( jid:replace_resource(StateData#state.jid, FromNick), get_users_and_subscribers(StateData), NewPacket, Node, NewStateData1), NewStateData2 = case has_body_or_subject(NewPacket) of true -> add_message_to_history(FromNick, From, NewPacket, NewStateData1); false -> NewStateData1 end, {next_state, normal_state, NewStateData2} end; _ -> Err = case (StateData#state.config)#config.allow_change_subj of true -> xmpp:err_forbidden( <<"Only moderators and participants are " "allowed to change the subject in this " "room">>, Lang); _ -> xmpp:err_forbidden( <<"Only moderators are allowed to change " "the subject in this room">>, Lang) end, ejabberd_router:route_error(Packet, Err), {next_state, normal_state, StateData} end; true -> ErrText = <<"Visitors are not allowed to send messages " "to all occupants">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err), {next_state, normal_state, StateData} end; false -> ErrText = <<"Only occupants are allowed to send messages " "to the conference">>, Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error(Packet, Err), {next_state, normal_state, StateData} end. -spec process_normal_message(jid(), message(), state()) -> state(). process_normal_message(From, #message{lang = Lang} = Pkt, StateData) -> Action = lists:foldl( fun(_, {error, _} = Err) -> Err; (_, {ok, _} = Result) -> Result; (#muc_user{invites = [_|_] = Invites}, _) -> case check_invitation(From, Invites, Lang, StateData) of ok -> {ok, Invites}; {error, _} = Err -> Err end; (#xdata{type = submit, fields = Fs}, _) -> try {ok, muc_request:decode(Fs)} catch _:{muc_request, Why} -> Txt = muc_request:format_error(Why), {error, xmpp:err_bad_request(Txt, Lang)} end; (_, Acc) -> Acc end, ok, xmpp:get_els(Pkt)), case Action of {ok, [#muc_invite{}|_] = Invitations} -> lists:foldl( fun(Invitation, AccState) -> process_invitation(From, Invitation, Lang, AccState) end, StateData, Invitations); {ok, [{role, participant}]} -> process_voice_request(From, Pkt, StateData); {ok, VoiceApproval} -> process_voice_approval(From, Pkt, VoiceApproval, StateData); {error, Err} -> ejabberd_router:route_error(Pkt, Err), StateData; ok -> StateData end. -spec process_invitation(jid(), muc_invite(), binary(), state()) -> state(). process_invitation(From, Invitation, Lang, StateData) -> IJID = route_invitation(From, Invitation, Lang, StateData), Config = StateData#state.config, case Config#config.members_only of true -> case get_affiliation(IJID, StateData) of none -> NSD = set_affiliation(IJID, member, StateData), send_affiliation(IJID, member, StateData), store_room(NSD), NSD; _ -> StateData end; false -> StateData end. -spec process_voice_request(jid(), message(), state()) -> state(). process_voice_request(From, Pkt, StateData) -> Lang = xmpp:get_lang(Pkt), case (StateData#state.config)#config.allow_voice_requests of true -> MinInterval = (StateData#state.config)#config.voice_request_min_interval, BareFrom = jid:remove_resource(jid:tolower(From)), NowPriority = -p1_time_compat:system_time(micro_seconds), CleanPriority = NowPriority + MinInterval * 1000000, Times = clean_treap(StateData#state.last_voice_request_time, CleanPriority), case treap:lookup(BareFrom, Times) of error -> Times1 = treap:insert(BareFrom, NowPriority, true, Times), NSD = StateData#state{last_voice_request_time = Times1}, send_voice_request(From, Lang, NSD), NSD; {ok, _, _} -> ErrText = <<"Please, wait for a while before sending " "new voice request">>, Err = xmpp:err_resource_constraint(ErrText, Lang), ejabberd_router:route_error(Pkt, Err), StateData#state{last_voice_request_time = Times} end; false -> ErrText = <<"Voice requests are disabled in this conference">>, Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Pkt, Err), StateData end. -spec process_voice_approval(jid(), message(), [muc_request:property()], state()) -> state(). process_voice_approval(From, Pkt, VoiceApproval, StateData) -> Lang = xmpp:get_lang(Pkt), case is_moderator(From, StateData) of true -> case lists:keyfind(jid, 1, VoiceApproval) of {_, TargetJid} -> Allow = proplists:get_bool(request_allow, VoiceApproval), case is_visitor(TargetJid, StateData) of true when Allow -> Reason = <<>>, NSD = set_role(TargetJid, participant, StateData), catch send_new_presence( TargetJid, Reason, NSD, StateData), NSD; _ -> StateData end; false -> ErrText = <<"Failed to extract JID from your voice " "request approval">>, Err = xmpp:err_bad_request(ErrText, Lang), ejabberd_router:route_error(Pkt, Err), StateData end; false -> ErrText = <<"Only moderators can approve voice requests">>, Err = xmpp:err_not_allowed(ErrText, Lang), ejabberd_router:route_error(Pkt, Err), StateData end. -spec is_vcard_request(iq()) -> boolean(). is_vcard_request(#iq{type = T, sub_els = [El]}) -> (T == get orelse T == set) andalso xmpp:get_ns(El) == ?NS_VCARD; is_vcard_request(_) -> false. %% @doc Check if this non participant can send message to room. %% %% XEP-0045 v1.23: %% 7.9 Sending a Message to All Occupants %% an implementation MAY allow users with certain privileges %% (e.g., a room owner, room admin, or service-level admin) %% to send messages to the room even if those users are not occupants. -spec is_user_allowed_message_nonparticipant(jid(), state()) -> boolean(). is_user_allowed_message_nonparticipant(JID, StateData) -> case get_service_affiliation(JID, StateData) of owner -> true; _ -> false end. %% @doc Get information of this participant, or default values. %% If the JID is not a participant, return values for a service message. -spec get_participant_data(jid(), state()) -> {binary(), role()}. get_participant_data(From, StateData) -> case (?DICT):find(jid:tolower(From), StateData#state.users) of {ok, #user{nick = FromNick, role = Role}} -> {FromNick, Role}; error -> case ?DICT:find(jid:tolower(jid:remove_resource(From)), StateData#state.subscribers) of {ok, #subscriber{nick = FromNick}} -> {FromNick, none}; error -> {<<"">>, moderator} end end. -spec process_presence(binary(), presence(), state()) -> fsm_transition(). process_presence(Nick, #presence{from = From, type = Type0} = Packet0, StateData) -> IsOnline = is_user_online(From, StateData), if Type0 == available; IsOnline and ((Type0 == unavailable) or (Type0 == error)) -> case ejabberd_hooks:run_fold(muc_filter_presence, StateData#state.server_host, Packet0, [StateData, Nick]) of drop -> {next_state, normal_state, StateData}; #presence{} = Packet -> close_room_if_temporary_and_empty( do_process_presence(Nick, Packet, StateData)) end; true -> {next_state, normal_state, StateData} end. -spec do_process_presence(binary(), presence(), state()) -> state(). do_process_presence(Nick, #presence{from = From, type = available, lang = Lang} = Packet, StateData) -> case is_user_online(From, StateData) of false -> add_new_user(From, Nick, Packet, StateData); true -> case is_nick_change(From, Nick, StateData) of true -> case {nick_collision(From, Nick, StateData), mod_muc:can_use_nick(StateData#state.server_host, StateData#state.host, From, Nick), {(StateData#state.config)#config.allow_visitor_nickchange, is_visitor(From, StateData)}} of {_, _, {false, true}} -> Packet1 = Packet#presence{sub_els = [#muc{}]}, ErrText = <<"Visitors are not allowed to change their " "nicknames in this room">>, Err = xmpp:err_not_allowed(ErrText, Lang), ejabberd_router:route_error(Packet1, Err), StateData; {true, _, _} -> Packet1 = Packet#presence{sub_els = [#muc{}]}, ErrText = <<"That nickname is already in use by another " "occupant">>, Err = xmpp:err_conflict(ErrText, Lang), ejabberd_router:route_error(Packet1, Err), StateData; {_, false, _} -> Packet1 = Packet#presence{sub_els = [#muc{}]}, ErrText = <<"That nickname is registered by another " "person">>, Err = xmpp:err_conflict(ErrText, Lang), ejabberd_router:route_error(Packet1, Err), StateData; _ -> change_nick(From, Nick, StateData) end; false -> Stanza = maybe_strip_status_from_presence( From, Packet, StateData), NewState = add_user_presence(From, Stanza, StateData), case xmpp:has_subtag(Packet, #muc{}) of true -> send_initial_presences_and_messages( From, Nick, Packet, NewState, StateData); false -> send_new_presence(From, NewState, StateData) end, NewState end end; do_process_presence(Nick, #presence{from = From, type = unavailable} = Packet, StateData) -> NewPacket = case {(StateData#state.config)#config.allow_visitor_status, is_visitor(From, StateData)} of {false, true} -> strip_status(Packet); _ -> Packet end, NewState = add_user_presence_un(From, NewPacket, StateData), case (?DICT):find(Nick, StateData#state.nicks) of {ok, [_, _ | _]} -> Aff = get_affiliation(From, StateData), Item = #muc_item{affiliation = Aff, role = none, jid = From}, Pres = xmpp:set_subtag( Packet, #muc_user{items = [Item], status_codes = [110]}), send_wrapped(jid:replace_resource(StateData#state.jid, Nick), From, Pres, ?NS_MUCSUB_NODES_PRESENCE, StateData); _ -> send_new_presence(From, NewState, StateData) end, Reason = xmpp:get_text(NewPacket#presence.status), remove_online_user(From, NewState, Reason); do_process_presence(_Nick, #presence{from = From, type = error, lang = Lang} = Packet, StateData) -> ErrorText = <<"It is not allowed to send error messages to the" " room. The participant (~s) has sent an error " "message (~s) and got kicked from the room">>, expulse_participant(Packet, From, StateData, translate:translate(Lang, ErrorText)). -spec maybe_strip_status_from_presence(jid(), presence(), state()) -> presence(). maybe_strip_status_from_presence(From, Packet, StateData) -> case {(StateData#state.config)#config.allow_visitor_status, is_visitor(From, StateData)} of {false, true} -> strip_status(Packet); _Allowed -> Packet end. -spec close_room_if_temporary_and_empty(state()) -> fsm_transition(). close_room_if_temporary_and_empty(StateData1) -> case not (StateData1#state.config)#config.persistent andalso (?DICT):size(StateData1#state.users) == 0 andalso (?DICT):size(StateData1#state.subscribers) == 0 of true -> ?INFO_MSG("Destroyed MUC room ~s because it's temporary " "and empty", [jid:encode(StateData1#state.jid)]), add_to_log(room_existence, destroyed, StateData1), {stop, normal, StateData1}; _ -> {next_state, normal_state, StateData1} end. -spec get_users_and_subscribers(state()) -> ?TDICT. get_users_and_subscribers(StateData) -> OnlineSubscribers = ?DICT:fold( fun(LJID, _, Acc) -> LBareJID = jid:remove_resource(LJID), case is_subscriber(LBareJID, StateData) of true -> ?SETS:add_element(LBareJID, Acc); false -> Acc end end, ?SETS:new(), StateData#state.users), ?DICT:fold( fun(LBareJID, #subscriber{nick = Nick}, Acc) -> case ?SETS:is_element(LBareJID, OnlineSubscribers) of false -> ?DICT:store(LBareJID, #user{jid = jid:make(LBareJID), nick = Nick, role = none, last_presence = undefined}, Acc); true -> Acc end end, StateData#state.users, StateData#state.subscribers). -spec is_user_online(jid(), state()) -> boolean(). is_user_online(JID, StateData) -> LJID = jid:tolower(JID), (?DICT):is_key(LJID, StateData#state.users). -spec is_subscriber(jid(), state()) -> boolean(). is_subscriber(JID, StateData) -> LJID = jid:tolower(jid:remove_resource(JID)), (?DICT):is_key(LJID, StateData#state.subscribers). %% Check if the user is occupant of the room, or at least is an admin or owner. -spec is_occupant_or_admin(jid(), state()) -> boolean(). is_occupant_or_admin(JID, StateData) -> FAffiliation = get_affiliation(JID, StateData), FRole = get_role(JID, StateData), case FRole /= none orelse FAffiliation == member orelse FAffiliation == admin orelse FAffiliation == owner of true -> true; _ -> false end. %% Decide the fate of the message and its sender %% Returns: continue_delivery | forget_message | {expulse_sender, Reason} -spec decide_fate_message(message(), jid(), state()) -> continue_delivery | forget_message | {expulse_sender, binary()}. decide_fate_message(#message{type = error} = Msg, From, StateData) -> Err = xmpp:get_error(Msg), PD = case check_error_kick(Err) of %% If this is an error stanza and its condition matches a criteria true -> Reason = str:format("This participant is considered a ghost " "and is expulsed: ~s", [jid:encode(From)]), {expulse_sender, Reason}; false -> continue_delivery end, case PD of {expulse_sender, R} -> case is_user_online(From, StateData) of true -> {expulse_sender, R}; false -> forget_message end; Other -> Other end; decide_fate_message(_, _, _) -> continue_delivery. %% Check if the elements of this error stanza indicate %% that the sender is a dead participant. %% If so, return true to kick the participant. -spec check_error_kick(stanza_error()) -> boolean(). check_error_kick(#stanza_error{reason = Reason}) -> case Reason of #gone{} -> true; 'internal-server-error' -> true; 'item-not-found' -> true; 'jid-malformed' -> true; 'recipient-unavailable' -> true; #redirect{} -> true; 'remote-server-not-found' -> true; 'remote-server-timeout' -> true; 'service-unavailable' -> true; _ -> false end; check_error_kick(undefined) -> false. -spec get_error_condition(stanza_error()) -> string(). get_error_condition(#stanza_error{reason = Reason}) -> case Reason of #gone{} -> "gone"; #redirect{} -> "redirect"; Atom -> atom_to_list(Atom) end; get_error_condition(undefined) -> "undefined". -spec get_error_text(stanza_error()) -> binary(). get_error_text(#stanza_error{text = Txt}) -> xmpp:get_text(Txt). -spec make_reason(stanza(), jid(), state(), binary()) -> binary(). make_reason(Packet, From, StateData, Reason1) -> {ok, #user{nick = FromNick}} = (?DICT):find(jid:tolower(From), StateData#state.users), Condition = get_error_condition(xmpp:get_error(Packet)), str:format(Reason1, [FromNick, Condition]). -spec expulse_participant(stanza(), jid(), state(), binary()) -> state(). expulse_participant(Packet, From, StateData, Reason1) -> Reason2 = make_reason(Packet, From, StateData, Reason1), NewState = add_user_presence_un(From, #presence{type = unavailable, status = xmpp:mk_text(Reason2)}, StateData), LJID = jid:tolower(From), {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users), case (?DICT):find(Nick, StateData#state.nicks) of {ok, [_, _ | _]} -> Aff = get_affiliation(From, StateData), Item = #muc_item{affiliation = Aff, role = none, jid = From}, Pres = xmpp:set_subtag( Packet, #muc_user{items = [Item], status_codes = [110]}), send_wrapped(jid:replace_resource(StateData#state.jid, Nick), From, Pres, ?NS_MUCSUB_NODES_PRESENCE, StateData); _ -> send_new_presence(From, NewState, StateData) end, remove_online_user(From, NewState). -spec get_owners(state()) -> [jid:jid()]. get_owners(StateData) -> ?DICT:fold( fun(LJID, owner, Acc) -> [jid:make(LJID)|Acc]; (LJID, {owner, _}, Acc) -> [jid:make(LJID)|Acc]; (_, _, Acc) -> Acc end, [], StateData#state.affiliations). -spec set_affiliation(jid(), affiliation(), state()) -> state(). set_affiliation(JID, Affiliation, StateData) -> set_affiliation(JID, Affiliation, StateData, <<"">>). -spec set_affiliation(jid(), affiliation(), state(), binary()) -> state(). set_affiliation(JID, Affiliation, StateData, Reason) -> LJID = jid:remove_resource(jid:tolower(JID)), Affiliations = case Affiliation of none -> (?DICT):erase(LJID, StateData#state.affiliations); _ -> (?DICT):store(LJID, {Affiliation, Reason}, StateData#state.affiliations) end, StateData#state{affiliations = Affiliations}. -spec get_affiliation(jid(), state()) -> affiliation(). get_affiliation(JID, StateData) -> {_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = StateData#state.access, Res = case acl:match_rule(StateData#state.server_host, AccessAdmin, JID) of allow -> owner; _ -> LJID = jid:tolower(JID), case (?DICT):find(LJID, StateData#state.affiliations) of {ok, Affiliation} -> Affiliation; _ -> LJID1 = jid:remove_resource(LJID), case (?DICT):find(LJID1, StateData#state.affiliations) of {ok, Affiliation} -> Affiliation; _ -> LJID2 = setelement(1, LJID, <<"">>), case (?DICT):find(LJID2, StateData#state.affiliations) of {ok, Affiliation} -> Affiliation; _ -> LJID3 = jid:remove_resource(LJID2), case (?DICT):find(LJID3, StateData#state.affiliations) of {ok, Affiliation} -> Affiliation; _ -> none end end end end end, case Res of {A, _Reason} -> A; _ -> Res end. -spec get_service_affiliation(jid(), state()) -> owner | none. get_service_affiliation(JID, StateData) -> {_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = StateData#state.access, case acl:match_rule(StateData#state.server_host, AccessAdmin, JID) of allow -> owner; _ -> none end. -spec set_role(jid(), role(), state()) -> state(). set_role(JID, Role, StateData) -> LJID = jid:tolower(JID), LJIDs = case LJID of {U, S, <<"">>} -> (?DICT):fold(fun (J, _, Js) -> case J of {U, S, _} -> [J | Js]; _ -> Js end end, [], StateData#state.users); _ -> case (?DICT):is_key(LJID, StateData#state.users) of true -> [LJID]; _ -> [] end end, {Users, Nicks} = case Role of none -> lists:foldl(fun (J, {Us, Ns}) -> NewNs = case (?DICT):find(J, Us) of {ok, #user{nick = Nick}} -> (?DICT):erase(Nick, Ns); _ -> Ns end, {(?DICT):erase(J, Us), NewNs} end, {StateData#state.users, StateData#state.nicks}, LJIDs); _ -> {lists:foldl( fun (J, Us) -> {ok, User} = (?DICT):find(J, Us), if User#user.last_presence == undefined -> Us; true -> (?DICT):store(J, User#user{role = Role}, Us) end end, StateData#state.users, LJIDs), StateData#state.nicks} end, StateData#state{users = Users, nicks = Nicks}. -spec get_role(jid(), state()) -> role(). get_role(JID, StateData) -> LJID = jid:tolower(JID), case (?DICT):find(LJID, StateData#state.users) of {ok, #user{role = Role}} -> Role; _ -> none end. -spec get_default_role(affiliation(), state()) -> role(). get_default_role(Affiliation, StateData) -> case Affiliation of owner -> moderator; admin -> moderator; member -> participant; outcast -> none; none -> case (StateData#state.config)#config.members_only of true -> none; _ -> case (StateData#state.config)#config.members_by_default of true -> participant; _ -> visitor end end end. -spec is_visitor(jid(), state()) -> boolean(). is_visitor(Jid, StateData) -> get_role(Jid, StateData) =:= visitor. -spec is_moderator(jid(), state()) -> boolean(). is_moderator(Jid, StateData) -> get_role(Jid, StateData) =:= moderator. -spec get_max_users(state()) -> non_neg_integer(). get_max_users(StateData) -> MaxUsers = (StateData#state.config)#config.max_users, ServiceMaxUsers = get_service_max_users(StateData), if MaxUsers =< ServiceMaxUsers -> MaxUsers; true -> ServiceMaxUsers end. -spec get_service_max_users(state()) -> pos_integer(). get_service_max_users(StateData) -> gen_mod:get_module_opt(StateData#state.server_host, mod_muc, max_users, ?MAX_USERS_DEFAULT). -spec get_max_users_admin_threshold(state()) -> pos_integer(). get_max_users_admin_threshold(StateData) -> gen_mod:get_module_opt(StateData#state.server_host, mod_muc, max_users_admin_threshold, 5). -spec room_queue_new(binary(), shaper:shaper(), _) -> p1_queue:queue(). room_queue_new(ServerHost, Shaper, QueueType) -> HaveRoomShaper = Shaper /= none, HaveMessageShaper = gen_mod:get_module_opt( ServerHost, mod_muc, user_message_shaper, none) /= none, HavePresenceShaper = gen_mod:get_module_opt( ServerHost, mod_muc, user_presence_shaper, none) /= none, HaveMinMessageInterval = gen_mod:get_module_opt( ServerHost, mod_muc, min_message_interval, 0) /= 0, HaveMinPresenceInterval = gen_mod:get_module_opt( ServerHost, mod_muc, min_presence_interval, 0) /= 0, if HaveRoomShaper or HaveMessageShaper or HavePresenceShaper or HaveMinMessageInterval or HaveMinPresenceInterval -> p1_queue:new(QueueType); true -> undefined end. -spec get_user_activity(jid(), state()) -> #activity{}. get_user_activity(JID, StateData) -> case treap:lookup(jid:tolower(JID), StateData#state.activity) of {ok, _P, A} -> A; error -> MessageShaper = shaper:new(gen_mod:get_module_opt(StateData#state.server_host, mod_muc, user_message_shaper, none)), PresenceShaper = shaper:new(gen_mod:get_module_opt(StateData#state.server_host, mod_muc, user_presence_shaper, none)), #activity{message_shaper = MessageShaper, presence_shaper = PresenceShaper} end. -spec store_user_activity(jid(), #activity{}, state()) -> state(). store_user_activity(JID, UserActivity, StateData) -> MinMessageInterval = trunc(gen_mod:get_module_opt(StateData#state.server_host, mod_muc, min_message_interval, 0) * 1000), MinPresenceInterval = trunc(gen_mod:get_module_opt(StateData#state.server_host, mod_muc, min_presence_interval, 0) * 1000), Key = jid:tolower(JID), Now = p1_time_compat:system_time(micro_seconds), Activity1 = clean_treap(StateData#state.activity, {1, -Now}), Activity = case treap:lookup(Key, Activity1) of {ok, _P, _A} -> treap:delete(Key, Activity1); error -> Activity1 end, StateData1 = case MinMessageInterval == 0 andalso MinPresenceInterval == 0 andalso UserActivity#activity.message_shaper == none andalso UserActivity#activity.presence_shaper == none andalso UserActivity#activity.message == undefined andalso UserActivity#activity.presence == undefined of true -> StateData#state{activity = Activity}; false -> case UserActivity#activity.message == undefined andalso UserActivity#activity.presence == undefined of true -> {_, MessageShaperInterval} = shaper:update(UserActivity#activity.message_shaper, 100000), {_, PresenceShaperInterval} = shaper:update(UserActivity#activity.presence_shaper, 100000), Delay = lists:max([MessageShaperInterval, PresenceShaperInterval, MinMessageInterval, MinPresenceInterval]) * 1000, Priority = {1, -(Now + Delay)}, StateData#state{activity = treap:insert(Key, Priority, UserActivity, Activity)}; false -> Priority = {0, 0}, StateData#state{activity = treap:insert(Key, Priority, UserActivity, Activity)} end end, StateData1. -spec clean_treap(treap:treap(), integer() | {1, integer()}) -> treap:treap(). clean_treap(Treap, CleanPriority) -> case treap:is_empty(Treap) of true -> Treap; false -> {_Key, Priority, _Value} = treap:get_root(Treap), if Priority > CleanPriority -> clean_treap(treap:delete_root(Treap), CleanPriority); true -> Treap end end. -spec prepare_room_queue(state()) -> state(). prepare_room_queue(StateData) -> case p1_queue:out(StateData#state.room_queue) of {{value, {message, From}}, _RoomQueue} -> Activity = get_user_activity(From, StateData), Packet = Activity#activity.message, Size = element_size(Packet), {RoomShaper, RoomShaperInterval} = shaper:update(StateData#state.room_shaper, Size), erlang:send_after(RoomShaperInterval, self(), process_room_queue), StateData#state{room_shaper = RoomShaper}; {{value, {presence, From}}, _RoomQueue} -> Activity = get_user_activity(From, StateData), {_Nick, Packet} = Activity#activity.presence, Size = element_size(Packet), {RoomShaper, RoomShaperInterval} = shaper:update(StateData#state.room_shaper, Size), erlang:send_after(RoomShaperInterval, self(), process_room_queue), StateData#state{room_shaper = RoomShaper}; {empty, _} -> StateData end. -spec update_online_user(jid(), #user{}, state()) -> state(). update_online_user(JID, #user{nick = Nick} = User, StateData) -> LJID = jid:tolower(JID), Nicks1 = case (?DICT):find(LJID, StateData#state.users) of {ok, #user{nick = OldNick}} -> case lists:delete( LJID, ?DICT:fetch(OldNick, StateData#state.nicks)) of [] -> ?DICT:erase(OldNick, StateData#state.nicks); LJIDs -> ?DICT:store(OldNick, LJIDs, StateData#state.nicks) end; error -> StateData#state.nicks end, Nicks = (?DICT):update(Nick, fun (LJIDs) -> [LJID|LJIDs -- [LJID]] end, [LJID], Nicks1), Users = (?DICT):update(LJID, fun(U) -> U#user{nick = Nick} end, User, StateData#state.users), NewStateData = StateData#state{users = Users, nicks = Nicks}, case {?DICT:find(LJID, StateData#state.users), ?DICT:find(LJID, NewStateData#state.users)} of {{ok, #user{nick = Old}}, {ok, #user{nick = New}}} when Old /= New -> send_nick_changing(JID, Old, NewStateData, true, true); _ -> ok end, NewStateData. set_subscriber(JID, Nick, Nodes, StateData) -> BareJID = jid:remove_resource(JID), LBareJID = jid:tolower(BareJID), Subscribers = ?DICT:store(LBareJID, #subscriber{jid = BareJID, nick = Nick, nodes = Nodes}, StateData#state.subscribers), Nicks = ?DICT:store(Nick, [LBareJID], StateData#state.subscriber_nicks), NewStateData = StateData#state{subscribers = Subscribers, subscriber_nicks = Nicks}, store_room(NewStateData, [{add_subscription, BareJID, Nick, Nodes}]), case not ?DICT:is_key(LBareJID, StateData#state.subscribers) of true -> send_subscriptions_change_notifications(BareJID, Nick, subscribe, NewStateData); _ -> ok end, NewStateData. -spec add_online_user(jid(), binary(), role(), state()) -> state(). add_online_user(JID, Nick, Role, StateData) -> tab_add_online_user(JID, StateData), User = #user{jid = JID, nick = Nick, role = Role}, update_online_user(JID, User, StateData). -spec remove_online_user(jid(), state()) -> state(). remove_online_user(JID, StateData) -> remove_online_user(JID, StateData, <<"">>). -spec remove_online_user(jid(), state(), binary()) -> state(). remove_online_user(JID, StateData, Reason) -> LJID = jid:tolower(JID), {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users), add_to_log(leave, {Nick, Reason}, StateData), tab_remove_online_user(JID, StateData), Users = (?DICT):erase(LJID, StateData#state.users), Nicks = case (?DICT):find(Nick, StateData#state.nicks) of {ok, [LJID]} -> (?DICT):erase(Nick, StateData#state.nicks); {ok, U} -> (?DICT):store(Nick, U -- [LJID], StateData#state.nicks); error -> StateData#state.nicks end, StateData#state{users = Users, nicks = Nicks}. -spec filter_presence(presence()) -> presence(). filter_presence(Presence) -> Els = lists:filter( fun(El) -> XMLNS = xmpp:get_ns(El), case catch binary:part(XMLNS, 0, size(?NS_MUC)) of ?NS_MUC -> false; _ -> true end end, xmpp:get_els(Presence)), xmpp:set_els(Presence, Els). -spec strip_status(presence()) -> presence(). strip_status(Presence) -> Presence#presence{status = []}. -spec add_user_presence(jid(), presence(), state()) -> state(). add_user_presence(JID, Presence, StateData) -> LJID = jid:tolower(JID), FPresence = filter_presence(Presence), Users = (?DICT):update(LJID, fun (#user{} = User) -> User#user{last_presence = FPresence} end, StateData#state.users), StateData#state{users = Users}. -spec add_user_presence_un(jid(), presence(), state()) -> state(). add_user_presence_un(JID, Presence, StateData) -> LJID = jid:tolower(JID), FPresence = filter_presence(Presence), Users = (?DICT):update(LJID, fun (#user{} = User) -> User#user{last_presence = FPresence, role = none} end, StateData#state.users), StateData#state{users = Users}. %% Find and return a list of the full JIDs of the users of Nick. %% Return jid record. -spec find_jids_by_nick(binary(), state()) -> [jid()]. find_jids_by_nick(Nick, StateData) -> Nicks = ?DICT:merge(fun(_, Val, _) -> Val end, StateData#state.nicks, StateData#state.subscriber_nicks), case (?DICT):find(Nick, Nicks) of {ok, [User]} -> [jid:make(User)]; {ok, Users} -> [jid:make(LJID) || LJID <- Users]; error -> [] end. %% Find and return the full JID of the user of Nick with %% highest-priority presence. Return jid record. -spec find_jid_by_nick(binary(), state()) -> jid() | false. find_jid_by_nick(Nick, StateData) -> case (?DICT):find(Nick, StateData#state.nicks) of {ok, [User]} -> jid:make(User); {ok, [FirstUser | Users]} -> #user{last_presence = FirstPresence} = (?DICT):fetch(FirstUser, StateData#state.users), {LJID, _} = lists:foldl(fun (Compare, {HighestUser, HighestPresence}) -> #user{last_presence = P1} = (?DICT):fetch(Compare, StateData#state.users), case higher_presence(P1, HighestPresence) of true -> {Compare, P1}; false -> {HighestUser, HighestPresence} end end, {FirstUser, FirstPresence}, Users), jid:make(LJID); error -> false end. -spec higher_presence(undefined | presence(), undefined | presence()) -> boolean(). higher_presence(Pres1, Pres2) when Pres1 /= undefined, Pres2 /= undefined -> Pri1 = get_priority_from_presence(Pres1), Pri2 = get_priority_from_presence(Pres2), Pri1 > Pri2; higher_presence(Pres1, Pres2) -> Pres1 > Pres2. -spec get_priority_from_presence(presence()) -> integer(). get_priority_from_presence(#presence{priority = Prio}) -> case Prio of undefined -> 0; _ -> Prio end. -spec find_nick_by_jid(jid(), state()) -> binary(). find_nick_by_jid(JID, StateData) -> LJID = jid:tolower(JID), {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users), Nick. -spec is_nick_change(jid(), binary(), state()) -> boolean(). is_nick_change(JID, Nick, StateData) -> LJID = jid:tolower(JID), case Nick of <<"">> -> false; _ -> {ok, #user{nick = OldNick}} = (?DICT):find(LJID, StateData#state.users), Nick /= OldNick end. -spec nick_collision(jid(), binary(), state()) -> boolean(). nick_collision(User, Nick, StateData) -> UserOfNick = case find_jid_by_nick(Nick, StateData) of false -> case ?DICT:find(Nick, StateData#state.subscriber_nicks) of {ok, [J]} -> J; error -> false end; J -> J end, (UserOfNick /= false andalso jid:remove_resource(jid:tolower(UserOfNick)) /= jid:remove_resource(jid:tolower(User))). -spec add_new_user(jid(), binary(), presence(), state()) -> state(); (jid(), binary(), iq(), state()) -> {error, stanza_error()} | {ignore, state()} | {result, muc_subscribe(), state()}. add_new_user(From, Nick, Packet, StateData) -> Lang = xmpp:get_lang(Packet), MaxUsers = get_max_users(StateData), MaxAdminUsers = MaxUsers + get_max_users_admin_threshold(StateData), NUsers = dict:fold(fun (_, _, Acc) -> Acc + 1 end, 0, StateData#state.users), Affiliation = get_affiliation(From, StateData), ServiceAffiliation = get_service_affiliation(From, StateData), NConferences = tab_count_user(From, StateData), MaxConferences = gen_mod:get_module_opt(StateData#state.server_host, mod_muc, max_user_conferences, 10), Collision = nick_collision(From, Nick, StateData), IsSubscribeRequest = not is_record(Packet, presence), case {(ServiceAffiliation == owner orelse ((Affiliation == admin orelse Affiliation == owner) andalso NUsers < MaxAdminUsers) orelse NUsers < MaxUsers) andalso NConferences < MaxConferences, Collision, mod_muc:can_use_nick(StateData#state.server_host, StateData#state.host, From, Nick), get_default_role(Affiliation, StateData)} of {false, _, _, _} when NUsers >= MaxUsers orelse NUsers >= MaxAdminUsers -> Txt = <<"Too many users in this conference">>, Err = xmpp:err_resource_constraint(Txt, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; {false, _, _, _} when NConferences >= MaxConferences -> Txt = <<"You have joined too many conferences">>, Err = xmpp:err_resource_constraint(Txt, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; {false, _, _, _} -> Err = xmpp:err_service_unavailable(), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; {_, _, _, none} -> Err = case Affiliation of outcast -> ErrText = <<"You have been banned from this room">>, xmpp:err_forbidden(ErrText, Lang); _ -> ErrText = <<"Membership is required to enter this room">>, xmpp:err_registration_required(ErrText, Lang) end, if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; {_, true, _, _} -> ErrText = <<"That nickname is already in use by another occupant">>, Err = xmpp:err_conflict(ErrText, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; {_, _, false, _} -> ErrText = <<"That nickname is registered by another person">>, Err = xmpp:err_conflict(ErrText, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; {_, _, _, Role} -> case check_password(ServiceAffiliation, Affiliation, Packet, From, StateData) of true -> Nodes = get_subscription_nodes(Packet), NewStateData = if not IsSubscribeRequest -> NewState = add_user_presence( From, Packet, add_online_user(From, Nick, Role, StateData)), send_initial_presences_and_messages( From, Nick, Packet, NewState, StateData), NewState; true -> set_subscriber(From, Nick, Nodes, StateData) end, ResultState = case NewStateData#state.just_created of true -> NewStateData#state{just_created = false}; false -> Robots = (?DICT):erase(From, StateData#state.robots), NewStateData#state{robots = Robots} end, if not IsSubscribeRequest -> ResultState; true -> {result, subscribe_result(Packet), ResultState} end; need_password -> ErrText = <<"A password is required to enter this room">>, Err = xmpp:err_not_authorized(ErrText, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; captcha_required -> SID = xmpp:get_id(Packet), RoomJID = StateData#state.jid, To = jid:replace_resource(RoomJID, Nick), Limiter = {From#jid.luser, From#jid.lserver}, case ejabberd_captcha:create_captcha(SID, RoomJID, To, Lang, Limiter, From) of {ok, ID, Body, CaptchaEls} -> MsgPkt = #message{from = RoomJID, to = From, id = ID, body = Body, sub_els = CaptchaEls}, Robots = (?DICT):store(From, {Nick, Packet}, StateData#state.robots), ejabberd_router:route(MsgPkt), NewState = StateData#state{robots = Robots}, if not IsSubscribeRequest -> NewState; true -> {ignore, NewState} end; {error, limit} -> ErrText = <<"Too many CAPTCHA requests">>, Err = xmpp:err_resource_constraint(ErrText, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end; _ -> ErrText = <<"Unable to generate a CAPTCHA">>, Err = xmpp:err_internal_server_error(ErrText, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end end; _ -> ErrText = <<"Incorrect password">>, Err = xmpp:err_not_authorized(ErrText, Lang), if not IsSubscribeRequest -> ejabberd_router:route_error(Packet, Err), StateData; true -> {error, Err} end end end. -spec check_password(affiliation(), affiliation(), presence() | iq(), jid(), state()) -> boolean() | need_password | captcha_required. check_password(owner, _Affiliation, _Packet, _From, _StateData) -> %% Don't check pass if user is owner in MUC service (access_admin option) true; check_password(_ServiceAffiliation, Affiliation, Packet, From, StateData) -> case (StateData#state.config)#config.password_protected of false -> check_captcha(Affiliation, From, StateData); true -> Pass = extract_password(Packet), case Pass of false -> need_password; _ -> case (StateData#state.config)#config.password of Pass -> true; _ -> false end end end. -spec check_captcha(affiliation(), jid(), state()) -> true | captcha_required. check_captcha(Affiliation, From, StateData) -> case (StateData#state.config)#config.captcha_protected andalso ejabberd_captcha:is_feature_available() of true when Affiliation == none -> case (?DICT):find(From, StateData#state.robots) of {ok, passed} -> true; _ -> WList = (StateData#state.config)#config.captcha_whitelist, #jid{luser = U, lserver = S, lresource = R} = From, case (?SETS):is_element({U, S, R}, WList) of true -> true; false -> case (?SETS):is_element({U, S, <<"">>}, WList) of true -> true; false -> case (?SETS):is_element({<<"">>, S, <<"">>}, WList) of true -> true; false -> captcha_required end end end end; _ -> true end. -spec extract_password(presence() | iq()) -> binary() | false. extract_password(#presence{} = Pres) -> case xmpp:get_subtag(Pres, #muc{}) of #muc{password = Password} when is_binary(Password) -> Password; _ -> false end; extract_password(#iq{} = IQ) -> case xmpp:get_subtag(IQ, #muc_subscribe{}) of #muc_subscribe{password = Password} when Password /= <<"">> -> Password; _ -> false end. -spec get_history(binary(), stanza(), state()) -> lqueue(). get_history(Nick, Packet, #state{history = History}) -> case xmpp:get_subtag(Packet, #muc{}) of #muc{history = #muc_history{} = MUCHistory} -> Now = p1_time_compat:timestamp(), Q = History#lqueue.queue, filter_history(Q, Now, Nick, MUCHistory); _ -> p1_queue:to_list(History#lqueue.queue) end. -spec filter_history(p1_queue:queue(), erlang:timestamp(), binary(), muc_history()) -> list(). filter_history(Queue, Now, Nick, #muc_history{since = Since, seconds = Seconds, maxstanzas = MaxStanzas, maxchars = MaxChars}) -> {History, _, _} = lists:foldr( fun({_, _, _, TimeStamp, Size} = Elem, {Elems, NumStanzas, NumChars} = Acc) -> NowDiff = timer:now_diff(Now, TimeStamp) div 1000000, Chars = Size + byte_size(Nick) + 1, if (NumStanzas < MaxStanzas) andalso (TimeStamp > Since) andalso (NowDiff =< Seconds) andalso (NumChars + Chars =< MaxChars) -> {[Elem|Elems], NumStanzas + 1, NumChars + Chars}; true -> Acc end end, {[], 0, 0}, p1_queue:to_list(Queue)), History. -spec is_room_overcrowded(state()) -> boolean(). is_room_overcrowded(StateData) -> MaxUsersPresence = gen_mod:get_module_opt( StateData#state.server_host, mod_muc, max_users_presence, ?DEFAULT_MAX_USERS_PRESENCE), (?DICT):size(StateData#state.users) > MaxUsersPresence. -spec presence_broadcast_allowed(jid(), state()) -> boolean(). presence_broadcast_allowed(JID, StateData) -> Role = get_role(JID, StateData), lists:member(Role, (StateData#state.config)#config.presence_broadcast). -spec send_initial_presences_and_messages( jid(), binary(), presence(), state(), state()) -> ok. send_initial_presences_and_messages(From, Nick, Presence, NewState, OldState) -> send_existing_presences(From, NewState), send_initial_presence(From, NewState, OldState), History = get_history(Nick, Presence, NewState), send_history(From, History, NewState), send_subject(From, OldState). -spec send_initial_presence(jid(), state(), state()) -> ok. send_initial_presence(NJID, StateData, OldStateData) -> send_new_presence1(NJID, <<"">>, true, StateData, OldStateData). -spec send_update_presence(jid(), state(), state()) -> ok. send_update_presence(JID, StateData, OldStateData) -> send_update_presence(JID, <<"">>, StateData, OldStateData). -spec send_update_presence(jid(), binary(), state(), state()) -> ok. send_update_presence(JID, Reason, StateData, OldStateData) -> case is_room_overcrowded(StateData) of true -> ok; false -> send_update_presence1(JID, Reason, StateData, OldStateData) end. -spec send_update_presence1(jid(), binary(), state(), state()) -> ok. send_update_presence1(JID, Reason, StateData, OldStateData) -> LJID = jid:tolower(JID), LJIDs = case LJID of {U, S, <<"">>} -> (?DICT):fold(fun (J, _, Js) -> case J of {U, S, _} -> [J | Js]; _ -> Js end end, [], StateData#state.users); _ -> case (?DICT):is_key(LJID, StateData#state.users) of true -> [LJID]; _ -> [] end end, lists:foreach(fun (J) -> send_new_presence1(J, Reason, false, StateData, OldStateData) end, LJIDs). -spec send_new_presence(jid(), state(), state()) -> ok. send_new_presence(NJID, StateData, OldStateData) -> send_new_presence(NJID, <<"">>, false, StateData, OldStateData). -spec send_new_presence(jid(), binary(), state(), state()) -> ok. send_new_presence(NJID, Reason, StateData, OldStateData) -> send_new_presence(NJID, Reason, false, StateData, OldStateData). -spec send_new_presence(jid(), binary(), boolean(), state(), state()) -> ok. send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> case is_room_overcrowded(StateData) of true -> ok; false -> send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) end. -spec is_ra_changed(jid(), boolean(), state(), state()) -> boolean(). is_ra_changed(_, _IsInitialPresence = true, _, _) -> false; is_ra_changed(JID, _IsInitialPresence = false, NewStateData, OldStateData) -> NewRole = get_role(JID, NewStateData), NewAff = get_affiliation(JID, NewStateData), OldRole = get_role(JID, OldStateData), OldAff = get_affiliation(JID, OldStateData), if (NewRole == none) and (NewAff == OldAff) -> %% A user is leaving the room; false; true -> (NewRole /= OldRole) or (NewAff /= OldAff) end. -spec send_new_presence1(jid(), binary(), boolean(), state(), state()) -> ok. send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> LNJID = jid:tolower(NJID), #user{nick = Nick} = (?DICT):fetch(LNJID, StateData#state.users), LJID = find_jid_by_nick(Nick, StateData), {ok, #user{jid = RealJID, role = Role0, last_presence = Presence0} = UserInfo} = (?DICT):find(jid:tolower(LJID), StateData#state.users), {Role1, Presence1} = case presence_broadcast_allowed(NJID, StateData) of true -> {Role0, Presence0}; false -> {none, #presence{type = unavailable}} end, Affiliation = get_affiliation(LJID, StateData), UserList = case not (presence_broadcast_allowed(NJID, StateData) orelse presence_broadcast_allowed(NJID, OldStateData)) of true -> [{LNJID, UserInfo}]; false -> (?DICT):to_list(get_users_and_subscribers(StateData)) end, lists:foreach( fun({LUJID, Info}) -> IsSelfPresence = LNJID == LUJID, {Role, Presence} = if IsSelfPresence -> {Role0, Presence0}; true -> {Role1, Presence1} end, Item0 = #muc_item{affiliation = Affiliation, role = Role}, Item1 = case Info#user.role == moderator orelse (StateData#state.config)#config.anonymous == false orelse IsSelfPresence of true -> Item0#muc_item{jid = RealJID}; false -> Item0 end, Item = Item1#muc_item{reason = Reason}, StatusCodes = status_codes(IsInitialPresence, IsSelfPresence, StateData), Pres = if Presence == undefined -> #presence{}; true -> Presence end, Packet = xmpp:set_subtag( Pres, #muc_user{items = [Item], status_codes = StatusCodes}), Node1 = case is_ra_changed(NJID, IsInitialPresence, StateData, OldStateData) of true -> ?NS_MUCSUB_NODES_AFFILIATIONS; false -> ?NS_MUCSUB_NODES_PRESENCE end, send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, Node1, StateData), Type = xmpp:get_type(Packet), IsSubscriber = is_subscriber(Info#user.jid, StateData), IsOccupant = Info#user.last_presence /= undefined, if (IsSubscriber and not IsOccupant) and (IsInitialPresence or (Type == unavailable)) -> Node2 = ?NS_MUCSUB_NODES_PARTICIPANTS, send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, Node2, StateData); true -> ok end end, UserList). -spec send_existing_presences(jid(), state()) -> ok. send_existing_presences(ToJID, StateData) -> case is_room_overcrowded(StateData) of true -> ok; false -> send_existing_presences1(ToJID, StateData) end. -spec send_existing_presences1(jid(), state()) -> ok. send_existing_presences1(ToJID, StateData) -> LToJID = jid:tolower(ToJID), {ok, #user{jid = RealToJID, role = Role}} = (?DICT):find(LToJID, StateData#state.users), lists:foreach( fun({FromNick, _Users}) -> LJID = find_jid_by_nick(FromNick, StateData), #user{jid = FromJID, role = FromRole, last_presence = Presence} = (?DICT):fetch(jid:tolower(LJID), StateData#state.users), PresenceBroadcast = lists:member( FromRole, (StateData#state.config)#config.presence_broadcast), case {RealToJID, PresenceBroadcast} of {FromJID, _} -> ok; {_, false} -> ok; _ -> FromAffiliation = get_affiliation(LJID, StateData), Item0 = #muc_item{affiliation = FromAffiliation, role = FromRole}, Item = case Role == moderator orelse (StateData#state.config)#config.anonymous == false of true -> Item0#muc_item{jid = FromJID}; false -> Item0 end, Packet = xmpp:set_subtag( Presence, #muc_user{items = [Item]}), send_wrapped(jid:replace_resource(StateData#state.jid, FromNick), RealToJID, Packet, ?NS_MUCSUB_NODES_PRESENCE, StateData) end end, (?DICT):to_list(StateData#state.nicks)). -spec set_nick(jid(), binary(), state()) -> state(). set_nick(JID, Nick, State) -> LJID = jid:tolower(JID), {ok, #user{nick = OldNick}} = (?DICT):find(LJID, State#state.users), Users = (?DICT):update(LJID, fun (#user{} = User) -> User#user{nick = Nick} end, State#state.users), OldNickUsers = (?DICT):fetch(OldNick, State#state.nicks), NewNickUsers = case (?DICT):find(Nick, State#state.nicks) of {ok, U} -> U; error -> [] end, Nicks = case OldNickUsers of [LJID] -> (?DICT):store(Nick, [LJID | NewNickUsers -- [LJID]], (?DICT):erase(OldNick, State#state.nicks)); [_ | _] -> (?DICT):store(Nick, [LJID | NewNickUsers -- [LJID]], (?DICT):store(OldNick, OldNickUsers -- [LJID], State#state.nicks)) end, State#state{users = Users, nicks = Nicks}. -spec change_nick(jid(), binary(), state()) -> state(). change_nick(JID, Nick, StateData) -> LJID = jid:tolower(JID), {ok, #user{nick = OldNick}} = (?DICT):find(LJID, StateData#state.users), OldNickUsers = (?DICT):fetch(OldNick, StateData#state.nicks), NewNickUsers = case (?DICT):find(Nick, StateData#state.nicks) of {ok, U} -> U; error -> [] end, SendOldUnavailable = length(OldNickUsers) == 1, SendNewAvailable = SendOldUnavailable orelse NewNickUsers == [], NewStateData = set_nick(JID, Nick, StateData), case presence_broadcast_allowed(JID, NewStateData) of true -> send_nick_changing(JID, OldNick, NewStateData, SendOldUnavailable, SendNewAvailable); false -> ok end, add_to_log(nickchange, {OldNick, Nick}, StateData), NewStateData. -spec send_nick_changing(jid(), binary(), state(), boolean(), boolean()) -> ok. send_nick_changing(JID, OldNick, StateData, SendOldUnavailable, SendNewAvailable) -> {ok, #user{jid = RealJID, nick = Nick, role = Role, last_presence = Presence}} = (?DICT):find(jid:tolower(JID), StateData#state.users), Affiliation = get_affiliation(JID, StateData), lists:foreach( fun({LJID, Info}) when Presence /= undefined -> IsSelfPresence = LJID == jid:tolower(JID), Item0 = #muc_item{affiliation = Affiliation, role = Role}, Item = case Info#user.role == moderator orelse (StateData#state.config)#config.anonymous == false orelse IsSelfPresence of true -> Item0#muc_item{jid = RealJID}; false -> Item0 end, Status110 = case IsSelfPresence of true -> [110]; false -> [] end, Packet1 = #presence{ type = unavailable, sub_els = [#muc_user{ items = [Item#muc_item{nick = Nick}], status_codes = [303|Status110]}]}, Packet2 = xmpp:set_subtag(Presence, #muc_user{items = [Item], status_codes = Status110}), if SendOldUnavailable -> send_wrapped( jid:replace_resource(StateData#state.jid, OldNick), Info#user.jid, Packet1, ?NS_MUCSUB_NODES_PRESENCE, StateData); true -> ok end, if SendNewAvailable -> send_wrapped( jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet2, ?NS_MUCSUB_NODES_PRESENCE, StateData); true -> ok end; (_) -> ok end, ?DICT:to_list(get_users_and_subscribers(StateData))). -spec maybe_send_affiliation(jid(), affiliation(), state()) -> ok. maybe_send_affiliation(JID, Affiliation, StateData) -> LJID = jid:tolower(JID), Users = get_users_and_subscribers(StateData), IsOccupant = case LJID of {LUser, LServer, <<"">>} -> not (?DICT):is_empty( (?DICT):filter(fun({U, S, _}, _) -> U == LUser andalso S == LServer end, Users)); {_LUser, _LServer, _LResource} -> (?DICT):is_key(LJID, Users) end, case IsOccupant of true -> ok; % The new affiliation is published via presence. false -> send_affiliation(JID, Affiliation, StateData) end. -spec send_affiliation(jid(), affiliation(), state()) -> ok. send_affiliation(JID, Affiliation, StateData) -> Item = #muc_item{jid = JID, affiliation = Affiliation, role = none}, Message = #message{id = randoms:get_string(), sub_els = [#muc_user{items = [Item]}]}, Users = get_users_and_subscribers(StateData), Recipients = case (StateData#state.config)#config.anonymous of true -> (?DICT):filter(fun(_, #user{role = moderator}) -> true; (_, _) -> false end, Users); false -> Users end, send_wrapped_multiple(StateData#state.jid, Recipients, Message, ?NS_MUCSUB_NODES_AFFILIATIONS, StateData). -spec status_codes(boolean(), boolean(), state()) -> [pos_integer()]. status_codes(IsInitialPresence, _IsSelfPresence = true, StateData) -> S0 = [110], case IsInitialPresence of true -> S1 = case StateData#state.just_created of true -> [201|S0]; false -> S0 end, S2 = case (StateData#state.config)#config.anonymous of true -> S1; false -> [100|S1] end, S3 = case (StateData#state.config)#config.logging of true -> [170|S2]; false -> S2 end, S3; false -> S0 end; status_codes(_IsInitialPresence, _IsSelfPresence = false, _StateData) -> []. -spec lqueue_new(non_neg_integer(), ram | file) -> lqueue(). lqueue_new(Max, Type) -> #lqueue{queue = p1_queue:new(Type), max = Max}. -spec lqueue_in(term(), lqueue()) -> lqueue(). %% If the message queue limit is set to 0, do not store messages. lqueue_in(_Item, LQ = #lqueue{max = 0}) -> LQ; %% Otherwise, rotate messages in the queue store. lqueue_in(Item, #lqueue{queue = Q1, max = Max}) -> Len = p1_queue:len(Q1), Q2 = p1_queue:in(Item, Q1), if Len >= Max -> Q3 = lqueue_cut(Q2, Len - Max + 1), #lqueue{queue = Q3, max = Max}; true -> #lqueue{queue = Q2, max = Max} end. -spec lqueue_cut(p1_queue:queue(), non_neg_integer()) -> p1_queue:queue(). lqueue_cut(Q, 0) -> Q; lqueue_cut(Q, N) -> {_, Q1} = p1_queue:out(Q), lqueue_cut(Q1, N - 1). -spec add_message_to_history(binary(), jid(), message(), state()) -> state(). add_message_to_history(FromNick, FromJID, Packet, StateData) -> add_to_log(text, {FromNick, Packet}, StateData), case check_subject(Packet) of [] -> TimeStamp = p1_time_compat:timestamp(), AddrPacket = case (StateData#state.config)#config.anonymous of true -> Packet; false -> Addresses = #addresses{ list = [#address{type = ofrom, jid = FromJID}]}, xmpp:set_subtag(Packet, Addresses) end, TSPacket = xmpp_util:add_delay_info( AddrPacket, StateData#state.jid, TimeStamp), SPacket = xmpp:set_from_to( TSPacket, jid:replace_resource(StateData#state.jid, FromNick), StateData#state.jid), Size = element_size(SPacket), Q1 = lqueue_in({FromNick, TSPacket, false, TimeStamp, Size}, StateData#state.history), StateData#state{history = Q1}; _ -> StateData end. -spec send_history(jid(), list(), state()) -> ok. send_history(JID, History, StateData) -> lists:foreach( fun({Nick, Packet, _HaveSubject, _TimeStamp, _Size}) -> ejabberd_router:route( xmpp:set_from_to( Packet, jid:replace_resource(StateData#state.jid, Nick), JID)) end, History). -spec send_subject(jid(), state()) -> ok. send_subject(JID, #state{subject_author = Nick} = StateData) -> Subject = case StateData#state.subject of [] -> [#text{}]; [_|_] = S -> S end, Packet = #message{from = jid:replace_resource(StateData#state.jid, Nick), to = JID, type = groupchat, subject = Subject}, ejabberd_router:route(Packet). -spec check_subject(message()) -> [text()]. check_subject(#message{subject = [_|_] = Subj, body = [], thread = undefined}) -> Subj; check_subject(_) -> []. -spec can_change_subject(role(), boolean(), state()) -> boolean(). can_change_subject(Role, IsSubscriber, StateData) -> case (StateData#state.config)#config.allow_change_subj of true -> Role == moderator orelse Role == participant orelse IsSubscriber == true; _ -> Role == moderator end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Admin stuff -spec process_iq_admin(jid(), iq(), #state{}) -> {error, stanza_error()} | {result, undefined, #state{}} | {result, muc_admin()}. process_iq_admin(_From, #iq{lang = Lang, sub_els = [#muc_admin{items = []}]}, _StateData) -> Txt = <<"No 'item' element found">>, {error, xmpp:err_bad_request(Txt, Lang)}; process_iq_admin(From, #iq{type = set, lang = Lang, sub_els = [#muc_admin{items = Items}]}, StateData) -> process_admin_items_set(From, Items, Lang, StateData); process_iq_admin(From, #iq{type = get, lang = Lang, sub_els = [#muc_admin{items = [Item]}]}, StateData) -> FAffiliation = get_affiliation(From, StateData), FRole = get_role(From, StateData), case Item of #muc_item{role = undefined, affiliation = undefined} -> Txt = <<"Neither 'role' nor 'affiliation' attribute found">>, {error, xmpp:err_bad_request(Txt, Lang)}; #muc_item{role = undefined, affiliation = Affiliation} -> if (FAffiliation == owner) or (FAffiliation == admin) or ((FAffiliation == member) and not (StateData#state.config)#config.anonymous) -> Items = items_with_affiliation(Affiliation, StateData), {result, #muc_admin{items = Items}}; true -> ErrText = <<"Administrator privileges required">>, {error, xmpp:err_forbidden(ErrText, Lang)} end; #muc_item{role = Role} -> if FRole == moderator -> Items = items_with_role(Role, StateData), {result, #muc_admin{items = Items}}; true -> ErrText = <<"Moderator privileges required">>, {error, xmpp:err_forbidden(ErrText, Lang)} end end; process_iq_admin(_From, #iq{type = get, lang = Lang}, _StateData) -> ErrText = <<"Too many elements">>, {error, xmpp:err_bad_request(ErrText, Lang)}. -spec items_with_role(role(), state()) -> [muc_item()]. items_with_role(SRole, StateData) -> lists:map(fun ({_, U}) -> user_to_item(U, StateData) end, search_role(SRole, StateData)). -spec items_with_affiliation(affiliation(), state()) -> [muc_item()]. items_with_affiliation(SAffiliation, StateData) -> lists:map( fun({JID, {Affiliation, Reason}}) -> #muc_item{affiliation = Affiliation, jid = jid:make(JID), reason = Reason}; ({JID, Affiliation}) -> #muc_item{affiliation = Affiliation, jid = jid:make(JID)} end, search_affiliation(SAffiliation, StateData)). -spec user_to_item(#user{}, state()) -> muc_item(). user_to_item(#user{role = Role, nick = Nick, jid = JID}, StateData) -> Affiliation = get_affiliation(JID, StateData), #muc_item{role = Role, affiliation = Affiliation, nick = Nick, jid = JID}. -spec search_role(role(), state()) -> [{ljid(), #user{}}]. search_role(Role, StateData) -> lists:filter(fun ({_, #user{role = R}}) -> Role == R end, (?DICT):to_list(StateData#state.users)). -spec search_affiliation(affiliation(), state()) -> [{ljid(), affiliation() | {affiliation(), binary()}}]. search_affiliation(Affiliation, StateData) -> lists:filter(fun ({_, A}) -> case A of {A1, _Reason} -> Affiliation == A1; _ -> Affiliation == A end end, (?DICT):to_list(StateData#state.affiliations)). -spec process_admin_items_set(jid(), [muc_item()], binary(), #state{}) -> {result, undefined, #state{}} | {error, stanza_error()}. process_admin_items_set(UJID, Items, Lang, StateData) -> UAffiliation = get_affiliation(UJID, StateData), URole = get_role(UJID, StateData), case catch find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, []) of {result, Res} -> ?INFO_MSG("Processing MUC admin query from ~s in " "room ~s:~n ~p", [jid:encode(UJID), jid:encode(StateData#state.jid), Res]), case lists:foldl(process_item_change(UJID), StateData, lists:flatten(Res)) of {error, _} = Err -> Err; NSD -> store_room(NSD), {result, undefined, NSD} end; {error, Err} -> {error, Err} end. -spec process_item_change(jid()) -> function(). process_item_change(UJID) -> fun(_, {error, _} = Err) -> Err; (Item, SD) -> process_item_change(Item, SD, UJID) end. -type admin_action() :: {jid(), affiliation | role, affiliation() | role(), binary()}. -spec process_item_change(admin_action(), state(), undefined | jid()) -> state() | {error, stanza_error()}. process_item_change(Item, SD, UJID) -> try case Item of {JID, affiliation, owner, _} when JID#jid.luser == <<"">> -> %% If the provided JID does not have username, %% forget the affiliation completely SD; {JID, role, none, Reason} -> send_kickban_presence(UJID, JID, Reason, 307, SD), set_role(JID, none, SD); {JID, affiliation, none, Reason} -> case (SD#state.config)#config.members_only of true -> send_kickban_presence(UJID, JID, Reason, 321, none, SD), maybe_send_affiliation(JID, none, SD), SD1 = set_affiliation(JID, none, SD), set_role(JID, none, SD1); _ -> SD1 = set_affiliation(JID, none, SD), send_update_presence(JID, Reason, SD1, SD), maybe_send_affiliation(JID, none, SD1), SD1 end; {JID, affiliation, outcast, Reason} -> send_kickban_presence(UJID, JID, Reason, 301, outcast, SD), maybe_send_affiliation(JID, outcast, SD), set_affiliation(JID, outcast, set_role(JID, none, SD), Reason); {JID, affiliation, A, Reason} when (A == admin) or (A == owner) -> SD1 = set_affiliation(JID, A, SD, Reason), SD2 = set_role(JID, moderator, SD1), send_update_presence(JID, Reason, SD2, SD), maybe_send_affiliation(JID, A, SD2), SD2; {JID, affiliation, member, Reason} -> SD1 = set_affiliation(JID, member, SD, Reason), SD2 = set_role(JID, participant, SD1), send_update_presence(JID, Reason, SD2, SD), maybe_send_affiliation(JID, member, SD2), SD2; {JID, role, Role, Reason} -> SD1 = set_role(JID, Role, SD), send_new_presence(JID, Reason, SD1, SD), SD1; {JID, affiliation, A, _Reason} -> SD1 = set_affiliation(JID, A, SD), send_update_presence(JID, SD1, SD), maybe_send_affiliation(JID, A, SD1), SD1 end catch E:R -> FromSuffix = case UJID of #jid{} -> JidString = jid:encode(UJID), <<" from ", JidString/binary>>; undefined -> <<"">> end, ?ERROR_MSG("failed to set item ~p~s: ~p", [Item, FromSuffix, {E, {R, erlang:get_stacktrace()}}]), {error, xmpp:err_internal_server_error()} end. -spec find_changed_items(jid(), affiliation(), role(), [muc_item()], binary(), state(), [admin_action()]) -> {result, [admin_action()]}. find_changed_items(_UJID, _UAffiliation, _URole, [], _Lang, _StateData, Res) -> {result, Res}; find_changed_items(_UJID, _UAffiliation, _URole, [#muc_item{jid = undefined, nick = <<"">>}|_], Lang, _StateData, _Res) -> Txt = <<"Neither 'jid' nor 'nick' attribute found">>, throw({error, xmpp:err_bad_request(Txt, Lang)}); find_changed_items(_UJID, _UAffiliation, _URole, [#muc_item{role = undefined, affiliation = undefined}|_], Lang, _StateData, _Res) -> Txt = <<"Neither 'role' nor 'affiliation' attribute found">>, throw({error, xmpp:err_bad_request(Txt, Lang)}); find_changed_items(UJID, UAffiliation, URole, [#muc_item{jid = J, nick = Nick, reason = Reason, role = Role, affiliation = Affiliation}|Items], Lang, StateData, Res) -> [JID | _] = JIDs = if J /= undefined -> [J]; Nick /= <<"">> -> case find_jids_by_nick(Nick, StateData) of [] -> ErrText = {<<"Nickname ~s does not exist in the room">>, [Nick]}, throw({error, xmpp:err_not_acceptable(ErrText, Lang)}); JIDList -> JIDList end end, {RoleOrAff, RoleOrAffValue} = if Role == undefined -> {affiliation, Affiliation}; true -> {role, Role} end, TAffiliation = get_affiliation(JID, StateData), TRole = get_role(JID, StateData), ServiceAf = get_service_affiliation(JID, StateData), CanChangeRA = case can_change_ra(UAffiliation, URole, TAffiliation, TRole, RoleOrAff, RoleOrAffValue, ServiceAf) of nothing -> nothing; true -> true; check_owner -> case search_affiliation(owner, StateData) of [{OJID, _}] -> jid:remove_resource(OJID) /= jid:tolower(jid:remove_resource(UJID)); _ -> true end; _ -> false end, case CanChangeRA of nothing -> find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res); true -> MoreRes = case RoleOrAff of affiliation -> [{jid:remove_resource(Jidx), RoleOrAff, RoleOrAffValue, Reason} || Jidx <- JIDs]; role -> [{Jidx, RoleOrAff, RoleOrAffValue, Reason} || Jidx <- JIDs] end, find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, MoreRes ++ Res); false -> Txt = <<"Changing role/affiliation is not allowed">>, throw({error, xmpp:err_not_allowed(Txt, Lang)}) end. -spec can_change_ra(affiliation(), role(), affiliation(), role(), affiliation, affiliation(), affiliation()) -> boolean() | nothing | check_owner; (affiliation(), role(), affiliation(), role(), role, role(), affiliation()) -> boolean() | nothing | check_owner. can_change_ra(_FAffiliation, _FRole, owner, _TRole, affiliation, owner, owner) -> %% A room owner tries to add as persistent owner a %% participant that is already owner because he is MUC admin true; can_change_ra(_FAffiliation, _FRole, _TAffiliation, _TRole, _RoleorAffiliation, _Value, owner) -> %% Nobody can decrease MUC admin's role/affiliation false; can_change_ra(_FAffiliation, _FRole, TAffiliation, _TRole, affiliation, Value, _ServiceAf) when TAffiliation == Value -> nothing; can_change_ra(_FAffiliation, _FRole, _TAffiliation, TRole, role, Value, _ServiceAf) when TRole == Value -> nothing; can_change_ra(FAffiliation, _FRole, outcast, _TRole, affiliation, none, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(FAffiliation, _FRole, outcast, _TRole, affiliation, member, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(owner, _FRole, outcast, _TRole, affiliation, admin, _ServiceAf) -> true; can_change_ra(owner, _FRole, outcast, _TRole, affiliation, owner, _ServiceAf) -> true; can_change_ra(FAffiliation, _FRole, none, _TRole, affiliation, outcast, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(FAffiliation, _FRole, none, _TRole, affiliation, member, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(owner, _FRole, none, _TRole, affiliation, admin, _ServiceAf) -> true; can_change_ra(owner, _FRole, none, _TRole, affiliation, owner, _ServiceAf) -> true; can_change_ra(FAffiliation, _FRole, member, _TRole, affiliation, outcast, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(FAffiliation, _FRole, member, _TRole, affiliation, none, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(owner, _FRole, member, _TRole, affiliation, admin, _ServiceAf) -> true; can_change_ra(owner, _FRole, member, _TRole, affiliation, owner, _ServiceAf) -> true; can_change_ra(owner, _FRole, admin, _TRole, affiliation, _Affiliation, _ServiceAf) -> true; can_change_ra(owner, _FRole, owner, _TRole, affiliation, _Affiliation, _ServiceAf) -> check_owner; can_change_ra(_FAffiliation, _FRole, _TAffiliation, _TRole, affiliation, _Value, _ServiceAf) -> false; can_change_ra(_FAffiliation, moderator, _TAffiliation, visitor, role, none, _ServiceAf) -> true; can_change_ra(_FAffiliation, moderator, _TAffiliation, visitor, role, participant, _ServiceAf) -> true; can_change_ra(FAffiliation, _FRole, _TAffiliation, visitor, role, moderator, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(_FAffiliation, moderator, _TAffiliation, participant, role, none, _ServiceAf) -> true; can_change_ra(_FAffiliation, moderator, _TAffiliation, participant, role, visitor, _ServiceAf) -> true; can_change_ra(FAffiliation, _FRole, _TAffiliation, participant, role, moderator, _ServiceAf) when (FAffiliation == owner) or (FAffiliation == admin) -> true; can_change_ra(_FAffiliation, _FRole, owner, moderator, role, visitor, _ServiceAf) -> false; can_change_ra(owner, _FRole, _TAffiliation, moderator, role, visitor, _ServiceAf) -> true; can_change_ra(_FAffiliation, _FRole, admin, moderator, role, visitor, _ServiceAf) -> false; can_change_ra(admin, _FRole, _TAffiliation, moderator, role, visitor, _ServiceAf) -> true; can_change_ra(_FAffiliation, _FRole, owner, moderator, role, participant, _ServiceAf) -> false; can_change_ra(owner, _FRole, _TAffiliation, moderator, role, participant, _ServiceAf) -> true; can_change_ra(_FAffiliation, _FRole, admin, moderator, role, participant, _ServiceAf) -> false; can_change_ra(admin, _FRole, _TAffiliation, moderator, role, participant, _ServiceAf) -> true; can_change_ra(_FAffiliation, _FRole, _TAffiliation, _TRole, role, _Value, _ServiceAf) -> false. -spec send_kickban_presence(undefined | jid(), jid(), binary(), pos_integer(), state()) -> ok. send_kickban_presence(UJID, JID, Reason, Code, StateData) -> NewAffiliation = get_affiliation(JID, StateData), send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation, StateData). -spec send_kickban_presence(undefined | jid(), jid(), binary(), pos_integer(), affiliation(), state()) -> ok. send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation, StateData) -> LJID = jid:tolower(JID), LJIDs = case LJID of {U, S, <<"">>} -> (?DICT):fold(fun (J, _, Js) -> case J of {U, S, _} -> [J | Js]; _ -> Js end end, [], StateData#state.users); _ -> case (?DICT):is_key(LJID, StateData#state.users) of true -> [LJID]; _ -> [] end end, lists:foreach(fun (J) -> {ok, #user{nick = Nick}} = (?DICT):find(J, StateData#state.users), add_to_log(kickban, {Nick, Reason, Code}, StateData), tab_remove_online_user(J, StateData), send_kickban_presence1(UJID, J, Reason, Code, NewAffiliation, StateData) end, LJIDs). -spec send_kickban_presence1(undefined | jid(), jid(), binary(), pos_integer(), affiliation(), state()) -> ok. send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation, StateData) -> {ok, #user{jid = RealJID, nick = Nick}} = (?DICT):find(jid:tolower(UJID), StateData#state.users), ActorNick = get_actor_nick(MJID, StateData), lists:foreach( fun({LJID, Info}) -> IsSelfPresence = jid:tolower(UJID) == LJID, Item0 = #muc_item{affiliation = Affiliation, role = none}, Item1 = case Info#user.role == moderator orelse (StateData#state.config)#config.anonymous == false orelse IsSelfPresence of true -> Item0#muc_item{jid = RealJID}; false -> Item0 end, Item2 = Item1#muc_item{reason = Reason}, Item = case ActorNick of <<"">> -> Item2; _ -> Item2#muc_item{actor = #muc_actor{nick = ActorNick}} end, Codes = if IsSelfPresence -> [110, Code]; true -> [Code] end, Packet = #presence{type = unavailable, sub_els = [#muc_user{items = [Item], status_codes = Codes}]}, RoomJIDNick = jid:replace_resource(StateData#state.jid, Nick), send_wrapped(RoomJIDNick, Info#user.jid, Packet, ?NS_MUCSUB_NODES_AFFILIATIONS, StateData), IsSubscriber = is_subscriber(Info#user.jid, StateData), IsOccupant = Info#user.last_presence /= undefined, if (IsSubscriber and not IsOccupant) -> send_wrapped(RoomJIDNick, Info#user.jid, Packet, ?NS_MUCSUB_NODES_PARTICIPANTS, StateData); true -> ok end end, (?DICT):to_list(get_users_and_subscribers(StateData))). -spec get_actor_nick(undefined | jid(), state()) -> binary(). get_actor_nick(undefined, _StateData) -> <<"">>; get_actor_nick(MJID, StateData) -> case (?DICT):find(jid:tolower(MJID), StateData#state.users) of {ok, #user{nick = ActorNick}} -> ActorNick; _ -> <<"">> end. convert_legacy_fields(Fs) -> lists:map( fun(#xdata_field{var = Var} = F) -> NewVar = case Var of <<"muc#roomconfig_allowvisitorstatus">> -> <<"allow_visitor_status">>; <<"muc#roomconfig_allowvisitornickchange">> -> <<"allow_visitor_nickchange">>; <<"muc#roomconfig_allowvoicerequests">> -> <<"allow_voice_requests">>; <<"muc#roomconfig_allow_subscription">> -> <<"allow_subscription">>; <<"muc#roomconfig_voicerequestmininterval">> -> <<"voice_request_min_interval">>; <<"muc#roomconfig_captcha_whitelist">> -> <<"captcha_whitelist">>; <<"muc#roomconfig_mam">> -> <<"mam">>; _ -> Var end, F#xdata_field{var = NewVar} end, Fs). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Owner stuff -spec process_iq_owner(jid(), iq(), state()) -> {result, undefined | muc_owner()} | {result, undefined | muc_owner(), state() | stop} | {error, stanza_error()}. process_iq_owner(From, #iq{type = set, lang = Lang, sub_els = [#muc_owner{destroy = Destroy, config = Config, items = Items}]}, StateData) -> FAffiliation = get_affiliation(From, StateData), if FAffiliation /= owner -> ErrText = <<"Owner privileges required">>, {error, xmpp:err_forbidden(ErrText, Lang)}; Destroy /= undefined, Config == undefined, Items == [] -> ?INFO_MSG("Destroyed MUC room ~s by the owner ~s", [jid:encode(StateData#state.jid), jid:encode(From)]), add_to_log(room_existence, destroyed, StateData), destroy_room(Destroy, StateData); Config /= undefined, Destroy == undefined, Items == [] -> case Config of #xdata{type = cancel} -> {result, undefined}; #xdata{type = submit, fields = Fs} -> Fs1 = convert_legacy_fields(Fs), try muc_roomconfig:decode(Fs1) of Options -> case is_allowed_log_change(Options, StateData, From) andalso is_allowed_persistent_change(Options, StateData, From) andalso is_allowed_room_name_desc_limits(Options, StateData) andalso is_password_settings_correct(Options, StateData) of true -> set_config(Options, StateData, Lang); false -> {error, xmpp:err_not_acceptable()} end catch _:{muc_roomconfig, Why} -> Txt = muc_roomconfig:format_error(Why), {error, xmpp:err_bad_request(Txt, Lang)} end; _ -> Txt = <<"Incorrect data form">>, {error, xmpp:err_bad_request(Txt, Lang)} end; Items /= [], Config == undefined, Destroy == undefined -> process_admin_items_set(From, Items, Lang, StateData); true -> {error, xmpp:err_bad_request()} end; process_iq_owner(From, #iq{type = get, lang = Lang, sub_els = [#muc_owner{destroy = Destroy, config = Config, items = Items}]}, StateData) -> FAffiliation = get_affiliation(From, StateData), if FAffiliation /= owner -> ErrText = <<"Owner privileges required">>, {error, xmpp:err_forbidden(ErrText, Lang)}; Destroy == undefined, Config == undefined -> case Items of [] -> {result, #muc_owner{config = get_config(Lang, StateData, From)}}; [#muc_item{affiliation = undefined}] -> Txt = <<"No 'affiliation' attribute found">>, {error, xmpp:err_bad_request(Txt, Lang)}; [#muc_item{affiliation = Affiliation}] -> Items = items_with_affiliation(Affiliation, StateData), {result, #muc_owner{items = Items}}; [_|_] -> Txt = <<"Too many elements">>, {error, xmpp:err_bad_request(Txt, Lang)} end; true -> {error, xmpp:err_bad_request()} end. -spec is_allowed_log_change(muc_roomconfig:result(), state(), jid()) -> boolean(). is_allowed_log_change(Options, StateData, From) -> case proplists:is_defined(enablelogging, Options) of false -> true; true -> allow == mod_muc_log:check_access_log(StateData#state.server_host, From) end. -spec is_allowed_persistent_change(muc_roomconfig:result(), state(), jid()) -> boolean(). is_allowed_persistent_change(Options, StateData, From) -> case proplists:is_defined(persistentroom, Options) of false -> true; true -> {_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access, allow == acl:match_rule(StateData#state.server_host, AccessPersistent, From) end. %% Check if the Room Name and Room Description defined in the Data Form %% are conformant to the configured limits -spec is_allowed_room_name_desc_limits(muc_roomconfig:result(), state()) -> boolean(). is_allowed_room_name_desc_limits(Options, StateData) -> RoomName = proplists:get_value(roomname, Options, <<"">>), RoomDesc = proplists:get_value(roomdesc, Options, <<"">>), MaxRoomName = gen_mod:get_module_opt( StateData#state.server_host, mod_muc, max_room_name, infinity), MaxRoomDesc = gen_mod:get_module_opt( StateData#state.server_host, mod_muc, max_room_desc, infinity), (byte_size(RoomName) =< MaxRoomName) andalso (byte_size(RoomDesc) =< MaxRoomDesc). %% Return false if: %% "the password for a password-protected room is blank" -spec is_password_settings_correct(muc_roomconfig:result(), state()) -> boolean(). is_password_settings_correct(Options, StateData) -> Config = StateData#state.config, OldProtected = Config#config.password_protected, OldPassword = Config#config.password, NewProtected = proplists:get_value(passwordprotectedroom, Options), NewPassword = proplists:get_value(roomsecret, Options), case {OldProtected, NewProtected, OldPassword, NewPassword} of {true, undefined, <<"">>, undefined} -> false; {true, undefined, _, <<"">>} -> false; {_, true, <<"">>, undefined} -> false; {_, true, _, <<"">>} -> false; _ -> true end. -spec get_default_room_maxusers(state()) -> non_neg_integer(). get_default_room_maxusers(RoomState) -> DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []), RoomState2 = set_opts(DefRoomOpts, RoomState), (RoomState2#state.config)#config.max_users. -spec get_config(binary(), state(), jid()) -> xdata(). get_config(Lang, StateData, From) -> {_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access, ServiceMaxUsers = get_service_max_users(StateData), DefaultRoomMaxUsers = get_default_room_maxusers(StateData), Config = StateData#state.config, MaxUsersRoom = get_max_users(StateData), Title = str:format( translate:translate(Lang, <<"Configuration of room ~s">>), [jid:encode(StateData#state.jid)]), Fs = [{roomname, Config#config.title}, {roomdesc, Config#config.description}] ++ case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of allow -> [{persistentroom, Config#config.persistent}]; deny -> [] end ++ [{publicroom, Config#config.public}, {public_list, Config#config.public_list}, {passwordprotectedroom, Config#config.password_protected}, {roomsecret, case Config#config.password_protected of true -> Config#config.password; false -> <<"">> end}, {maxusers, MaxUsersRoom, [if is_integer(ServiceMaxUsers) -> []; true -> [{<<"No limit">>, <<"none">>}] end] ++ [{integer_to_binary(N), N} || N <- lists:usort([ServiceMaxUsers, DefaultRoomMaxUsers, MaxUsersRoom | ?MAX_USERS_DEFAULT_LIST]), N =< ServiceMaxUsers]}, {whois, if Config#config.anonymous -> moderators; true -> anyone end}, {presencebroadcast, Config#config.presence_broadcast}, {membersonly, Config#config.members_only}, {moderatedroom, Config#config.moderated}, {members_by_default, Config#config.members_by_default}, {changesubject, Config#config.allow_change_subj}, {allow_private_messages, Config#config.allow_private_messages}, {allow_private_messages_from_visitors, Config#config.allow_private_messages_from_visitors}, {allow_query_users, Config#config.allow_query_users}, {allowinvites, Config#config.allow_user_invites}, {allow_visitor_status, Config#config.allow_visitor_status}, {allow_visitor_nickchange, Config#config.allow_visitor_nickchange}, {allow_voice_requests, Config#config.allow_voice_requests}, {allow_subscription, Config#config.allow_subscription}, {voice_request_min_interval, Config#config.voice_request_min_interval}, {pubsub, Config#config.pubsub}] ++ case ejabberd_captcha:is_feature_available() of true -> [{captcha_protected, Config#config.captcha_protected}]; false -> [] end ++ [{captcha_whitelist, lists:map(fun jid:make/1, ?SETS:to_list(Config#config.captcha_whitelist))}] ++ case mod_muc_log:check_access_log(StateData#state.server_host, From) of allow -> [{enablelogging, Config#config.logging}]; deny -> [] end, Fields = ejabberd_hooks:run_fold(get_room_config, StateData#state.server_host, Fs, [StateData, From, Lang]), #xdata{type = form, title = Title, fields = muc_roomconfig:encode(Fields, Lang)}. -spec set_config(muc_roomconfig:result(), state(), binary()) -> {error, stanza_error()} | {result, undefined, state()}. set_config(Options, StateData, Lang) -> try #config{} = Config = set_config(Options, StateData#state.config, StateData#state.server_host, Lang), {result, _, NSD} = Res = change_config(Config, StateData), Type = case {(StateData#state.config)#config.logging, Config#config.logging} of {true, false} -> roomconfig_change_disabledlogging; {false, true} -> roomconfig_change_enabledlogging; {_, _} -> roomconfig_change end, Users = [{U#user.jid, U#user.nick, U#user.role} || {_, U} <- (?DICT):to_list(StateData#state.users)], add_to_log(Type, Users, NSD), Res catch _:{badmatch, {error, #stanza_error{}} = Err} -> Err end. get_config_opt_name(Pos) -> Fs = [config|record_info(fields, config)], lists:nth(Pos, Fs). -spec set_config([muc_roomconfig:property()], #config{}, binary(), binary()) -> #config{} | {error, stanza_error()}. set_config(Opts, Config, ServerHost, Lang) -> lists:foldl( fun(_, {error, _} = Err) -> Err; ({roomname, Title}, C) -> C#config{title = Title}; ({roomdesc, Desc}, C) -> C#config{description = Desc}; ({changesubject, V}, C) -> C#config{allow_change_subj = V}; ({allow_query_users, V}, C) -> C#config{allow_query_users = V}; ({allow_private_messages, V}, C) -> C#config{allow_private_messages = V}; ({allow_private_messages_from_visitors, V}, C) -> C#config{allow_private_messages_from_visitors = V}; ({allow_visitor_status, V}, C) -> C#config{allow_visitor_status = V}; ({allow_visitor_nickchange, V}, C) -> C#config{allow_visitor_nickchange = V}; ({publicroom, V}, C) -> C#config{public = V}; ({public_list, V}, C) -> C#config{public_list = V}; ({persistentroom, V}, C) -> C#config{persistent = V}; ({moderatedroom, V}, C) -> C#config{moderated = V}; ({members_by_default, V}, C) -> C#config{members_by_default = V}; ({membersonly, V}, C) -> C#config{members_only = V}; ({captcha_protected, V}, C) -> C#config{captcha_protected = V}; ({allowinvites, V}, C) -> C#config{allow_user_invites = V}; ({allow_subscription, V}, C) -> C#config{allow_subscription = V}; ({passwordprotectedroom, V}, C) -> C#config{password_protected = V}; ({roomsecret, V}, C) -> C#config{password = V}; ({anonymous, V}, C) -> C#config{anonymous = V}; ({presencebroadcast, V}, C) -> C#config{presence_broadcast = V}; ({allow_voice_requests, V}, C) -> C#config{allow_voice_requests = V}; ({voice_request_min_interval, V}, C) -> C#config{voice_request_min_interval = V}; ({whois, moderators}, C) -> C#config{anonymous = true}; ({whois, anyone}, C) -> C#config{anonymous = false}; ({maxusers, V}, C) -> C#config{max_users = V}; ({enablelogging, V}, C) -> C#config{logging = V}; ({pubsub, V}, C) -> C#config{pubsub = V}; ({captcha_whitelist, Js}, C) -> LJIDs = [jid:tolower(J) || J <- Js], C#config{captcha_whitelist = ?SETS:from_list(LJIDs)}; ({O, V} = Opt, C) -> case ejabberd_hooks:run_fold(set_room_option, ServerHost, {0, undefined}, [Opt, Lang]) of {0, undefined} -> ?ERROR_MSG("set_room_option hook failed for " "option '~s' with value ~p", [O, V]), Txt = {<<"Failed to process option '~s'">>, [O]}, {error, xmpp:err_internal_server_error(Txt, Lang)}; {Pos, Val} -> setelement(Pos, C, Val) end end, Config, Opts). -spec change_config(#config{}, state()) -> {result, undefined, state()}. change_config(Config, StateData) -> send_config_change_info(Config, StateData), NSD = remove_subscriptions(StateData#state{config = Config}), case {(StateData#state.config)#config.persistent, Config#config.persistent} of {_, true} -> store_room(NSD); {true, false} -> mod_muc:forget_room(NSD#state.server_host, NSD#state.host, NSD#state.room); {false, false} -> ok end, case {(StateData#state.config)#config.members_only, Config#config.members_only} of {false, true} -> NSD1 = remove_nonmembers(NSD), {result, undefined, NSD1}; _ -> {result, undefined, NSD} end. -spec send_config_change_info(#config{}, state()) -> ok. send_config_change_info(Config, #state{config = Config}) -> ok; send_config_change_info(New, #state{config = Old} = StateData) -> Codes = case {Old#config.logging, New#config.logging} of {false, true} -> [170]; {true, false} -> [171]; _ -> [] end ++ case {Old#config.anonymous, New#config.anonymous} of {true, false} -> [172]; {false, true} -> [173]; _ -> [] end ++ case Old#config{anonymous = New#config.anonymous, vcard = New#config.vcard, logging = New#config.logging} of New -> []; _ -> [104] end, if Codes /= [] -> Message = #message{type = groupchat, id = randoms:get_string(), sub_els = [#muc_user{status_codes = Codes}]}, send_wrapped_multiple(StateData#state.jid, get_users_and_subscribers(StateData), Message, ?NS_MUCSUB_NODES_CONFIG, StateData); true -> ok end. -spec remove_nonmembers(state()) -> state(). remove_nonmembers(StateData) -> lists:foldl(fun ({_LJID, #user{jid = JID}}, SD) -> Affiliation = get_affiliation(JID, SD), case Affiliation of none -> catch send_kickban_presence(undefined, JID, <<"">>, 322, SD), set_role(JID, none, SD); _ -> SD end end, StateData, (?DICT):to_list(get_users_and_subscribers(StateData))). -spec set_opts([{atom(), any()}], state()) -> state(). set_opts([], StateData) -> StateData; set_opts([{Opt, Val} | Opts], StateData) -> NSD = case Opt of title -> StateData#state{config = (StateData#state.config)#config{title = Val}}; description -> StateData#state{config = (StateData#state.config)#config{description = Val}}; allow_change_subj -> StateData#state{config = (StateData#state.config)#config{allow_change_subj = Val}}; allow_query_users -> StateData#state{config = (StateData#state.config)#config{allow_query_users = Val}}; allow_private_messages -> StateData#state{config = (StateData#state.config)#config{allow_private_messages = Val}}; allow_private_messages_from_visitors -> StateData#state{config = (StateData#state.config)#config{allow_private_messages_from_visitors = Val}}; allow_visitor_nickchange -> StateData#state{config = (StateData#state.config)#config{allow_visitor_nickchange = Val}}; allow_visitor_status -> StateData#state{config = (StateData#state.config)#config{allow_visitor_status = Val}}; public -> StateData#state{config = (StateData#state.config)#config{public = Val}}; public_list -> StateData#state{config = (StateData#state.config)#config{public_list = Val}}; persistent -> StateData#state{config = (StateData#state.config)#config{persistent = Val}}; moderated -> StateData#state{config = (StateData#state.config)#config{moderated = Val}}; members_by_default -> StateData#state{config = (StateData#state.config)#config{members_by_default = Val}}; members_only -> StateData#state{config = (StateData#state.config)#config{members_only = Val}}; allow_user_invites -> StateData#state{config = (StateData#state.config)#config{allow_user_invites = Val}}; password_protected -> StateData#state{config = (StateData#state.config)#config{password_protected = Val}}; captcha_protected -> StateData#state{config = (StateData#state.config)#config{captcha_protected = Val}}; password -> StateData#state{config = (StateData#state.config)#config{password = Val}}; anonymous -> StateData#state{config = (StateData#state.config)#config{anonymous = Val}}; presence_broadcast -> StateData#state{config = (StateData#state.config)#config{presence_broadcast = Val}}; logging -> StateData#state{config = (StateData#state.config)#config{logging = Val}}; mam -> StateData#state{config = (StateData#state.config)#config{mam = Val}}; captcha_whitelist -> StateData#state{config = (StateData#state.config)#config{captcha_whitelist = (?SETS):from_list(Val)}}; allow_voice_requests -> StateData#state{config = (StateData#state.config)#config{allow_voice_requests = Val}}; voice_request_min_interval -> StateData#state{config = (StateData#state.config)#config{voice_request_min_interval = Val}}; max_users -> ServiceMaxUsers = get_service_max_users(StateData), MaxUsers = if Val =< ServiceMaxUsers -> Val; true -> ServiceMaxUsers end, StateData#state{config = (StateData#state.config)#config{max_users = MaxUsers}}; vcard -> StateData#state{config = (StateData#state.config)#config{vcard = Val}}; pubsub -> StateData#state{config = (StateData#state.config)#config{pubsub = Val}}; allow_subscription -> StateData#state{config = (StateData#state.config)#config{allow_subscription = Val}}; subscribers -> {Subscribers, Nicks} = lists:foldl( fun({JID, Nick, Nodes}, {SubAcc, NickAcc}) -> BareJID = jid:remove_resource(JID), {?DICT:store( jid:tolower(BareJID), #subscriber{jid = BareJID, nick = Nick, nodes = Nodes}, SubAcc), ?DICT:store(Nick, [jid:tolower(BareJID)], NickAcc)} end, {?DICT:new(), ?DICT:new()}, Val), StateData#state{subscribers = Subscribers, subscriber_nicks = Nicks}; affiliations -> StateData#state{affiliations = (?DICT):from_list(Val)}; subject -> Subj = if Val == <<"">> -> []; is_binary(Val) -> [#text{data = Val}]; is_list(Val) -> Val end, StateData#state{subject = Subj}; subject_author -> StateData#state{subject_author = Val}; _ -> StateData end, set_opts(Opts, NSD). -define(MAKE_CONFIG_OPT(Opt), {get_config_opt_name(Opt), element(Opt, Config)}). -spec make_opts(state()) -> [{atom(), any()}]. make_opts(StateData) -> Config = StateData#state.config, Subscribers = (?DICT):fold( fun(_LJID, Sub, Acc) -> [{Sub#subscriber.jid, Sub#subscriber.nick, Sub#subscriber.nodes}|Acc] end, [], StateData#state.subscribers), [?MAKE_CONFIG_OPT(#config.title), ?MAKE_CONFIG_OPT(#config.description), ?MAKE_CONFIG_OPT(#config.allow_change_subj), ?MAKE_CONFIG_OPT(#config.allow_query_users), ?MAKE_CONFIG_OPT(#config.allow_private_messages), ?MAKE_CONFIG_OPT(#config.allow_private_messages_from_visitors), ?MAKE_CONFIG_OPT(#config.allow_visitor_status), ?MAKE_CONFIG_OPT(#config.allow_visitor_nickchange), ?MAKE_CONFIG_OPT(#config.public), ?MAKE_CONFIG_OPT(#config.public_list), ?MAKE_CONFIG_OPT(#config.persistent), ?MAKE_CONFIG_OPT(#config.moderated), ?MAKE_CONFIG_OPT(#config.members_by_default), ?MAKE_CONFIG_OPT(#config.members_only), ?MAKE_CONFIG_OPT(#config.allow_user_invites), ?MAKE_CONFIG_OPT(#config.password_protected), ?MAKE_CONFIG_OPT(#config.captcha_protected), ?MAKE_CONFIG_OPT(#config.password), ?MAKE_CONFIG_OPT(#config.anonymous), ?MAKE_CONFIG_OPT(#config.logging), ?MAKE_CONFIG_OPT(#config.max_users), ?MAKE_CONFIG_OPT(#config.allow_voice_requests), ?MAKE_CONFIG_OPT(#config.allow_subscription), ?MAKE_CONFIG_OPT(#config.mam), ?MAKE_CONFIG_OPT(#config.presence_broadcast), ?MAKE_CONFIG_OPT(#config.voice_request_min_interval), ?MAKE_CONFIG_OPT(#config.vcard), ?MAKE_CONFIG_OPT(#config.pubsub), {captcha_whitelist, (?SETS):to_list((StateData#state.config)#config.captcha_whitelist)}, {affiliations, (?DICT):to_list(StateData#state.affiliations)}, {subject, StateData#state.subject}, {subject_author, StateData#state.subject_author}, {subscribers, Subscribers}]. -spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}. destroy_room(DEl, StateData) -> Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER}, lists:foreach( fun({_LJID, Info}) -> Nick = Info#user.nick, Item = #muc_item{affiliation = none, role = none}, Packet = #presence{ type = unavailable, sub_els = [#muc_user{items = [Item], destroy = Destroy}]}, send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, ?NS_MUCSUB_NODES_CONFIG, StateData) end, (?DICT):to_list(get_users_and_subscribers(StateData))), case (StateData#state.config)#config.persistent of true -> mod_muc:forget_room(StateData#state.server_host, StateData#state.host, StateData#state.room); false -> ok end, {result, undefined, stop}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Disco -define(CONFIG_OPT_TO_FEATURE(Opt, Fiftrue, Fiffalse), case Opt of true -> Fiftrue; false -> Fiffalse end). -spec process_iq_disco_info(jid(), iq(), state()) -> {result, disco_info()} | {error, stanza_error()}. process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, {error, xmpp:err_not_allowed(Txt, Lang)}; process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) -> Config = StateData#state.config, Feats = [?NS_VCARD, ?NS_MUC, ?CONFIG_OPT_TO_FEATURE((Config#config.public), <<"muc_public">>, <<"muc_hidden">>), ?CONFIG_OPT_TO_FEATURE((Config#config.persistent), <<"muc_persistent">>, <<"muc_temporary">>), ?CONFIG_OPT_TO_FEATURE((Config#config.members_only), <<"muc_membersonly">>, <<"muc_open">>), ?CONFIG_OPT_TO_FEATURE((Config#config.anonymous), <<"muc_semianonymous">>, <<"muc_nonanonymous">>), ?CONFIG_OPT_TO_FEATURE((Config#config.moderated), <<"muc_moderated">>, <<"muc_unmoderated">>), ?CONFIG_OPT_TO_FEATURE((Config#config.password_protected), <<"muc_passwordprotected">>, <<"muc_unsecured">>)] ++ case Config#config.allow_subscription of true -> [?NS_MUCSUB]; false -> [] end ++ case {gen_mod:is_loaded(StateData#state.server_host, mod_mam), Config#config.mam} of {true, true} -> [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1, ?NS_MAM_2, ?NS_SID_0]; _ -> [] end, {result, #disco_info{xdata = [iq_disco_info_extras(Lang, StateData)], identities = [#identity{category = <<"conference">>, type = <<"text">>, name = get_title(StateData)}], features = Feats}}. -spec iq_disco_info_extras(binary(), state()) -> xdata(). iq_disco_info_extras(Lang, StateData) -> Fs1 = [{description, (StateData#state.config)#config.description}, {occupants, ?DICT:size(StateData#state.users)}, {contactjid, get_owners(StateData)}], Fs2 = case (StateData#state.config)#config.pubsub of Node when is_binary(Node), Node /= <<"">> -> [{pubsub, Node}|Fs1]; _ -> Fs1 end, #xdata{type = result, fields = muc_roominfo:encode(Fs2, Lang)}. -spec process_iq_disco_items(jid(), iq(), state()) -> {error, stanza_error()} | {result, disco_items()}. process_iq_disco_items(_From, #iq{type = set, lang = Lang}, _StateData) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, {error, xmpp:err_not_allowed(Txt, Lang)}; process_iq_disco_items(From, #iq{type = get}, StateData) -> case (StateData#state.config)#config.public_list of true -> {result, get_mucroom_disco_items(StateData)}; _ -> case is_occupant_or_admin(From, StateData) of true -> {result, get_mucroom_disco_items(StateData)}; _ -> %% If the list of occupants is private, %% the room MUST return an empty element %% (http://xmpp.org/extensions/xep-0045.html#disco-roomitems) {result, #disco_items{}} end end. -spec process_iq_captcha(jid(), iq(), state()) -> {error, stanza_error()} | {result, undefined}. process_iq_captcha(_From, #iq{type = get, lang = Lang}, _StateData) -> Txt = <<"Value 'get' of 'type' attribute is not allowed">>, {error, xmpp:err_not_allowed(Txt, Lang)}; process_iq_captcha(_From, #iq{type = set, lang = Lang, sub_els = [SubEl]}, _StateData) -> case ejabberd_captcha:process_reply(SubEl) of ok -> {result, undefined}; {error, malformed} -> Txt = <<"Incorrect CAPTCHA submit">>, {error, xmpp:err_bad_request(Txt, Lang)}; _ -> Txt = <<"The CAPTCHA verification has failed">>, {error, xmpp:err_not_allowed(Txt, Lang)} end. -spec process_iq_vcard(jid(), iq(), state()) -> {result, vcard_temp() | xmlel()} | {result, undefined, state()} | {error, stanza_error()}. process_iq_vcard(_From, #iq{type = get}, StateData) -> #state{config = #config{vcard = VCardRaw}} = StateData, case fxml_stream:parse_element(VCardRaw) of #xmlel{} = VCard -> {result, VCard}; {error, _} -> {error, xmpp:err_item_not_found()} end; process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [SubEl]}, StateData) -> case get_affiliation(From, StateData) of owner -> VCardRaw = fxml:element_to_binary(xmpp:encode(SubEl)), Config = StateData#state.config, NewConfig = Config#config{vcard = VCardRaw}, change_config(NewConfig, StateData); _ -> ErrText = <<"Owner privileges required">>, {error, xmpp:err_forbidden(ErrText, Lang)} end. -spec process_iq_mucsub(jid(), iq(), state()) -> {error, stanza_error()} | {result, undefined | muc_subscribe() | muc_subscriptions(), state()} | {ignore, state()}. process_iq_mucsub(_From, #iq{type = set, lang = Lang, sub_els = [#muc_subscribe{}]}, #state{just_created = false, config = #config{allow_subscription = false}}) -> {error, xmpp:err_not_allowed(<<"Subscriptions are not allowed">>, Lang)}; process_iq_mucsub(From, #iq{type = set, lang = Lang, sub_els = [#muc_subscribe{jid = #jid{} = SubJid} = Mucsub]}, StateData) -> FAffiliation = get_affiliation(From, StateData), FRole = get_role(From, StateData), if FRole == moderator; FAffiliation == owner; FAffiliation == admin -> process_iq_mucsub(SubJid, #iq{type = set, lang = Lang, sub_els = [Mucsub#muc_subscribe{jid = undefined}]}, StateData); true -> Txt = <<"Moderator privileges required">>, {error, xmpp:err_forbidden(Txt, Lang)} end; process_iq_mucsub(From, #iq{type = set, lang = Lang, sub_els = [#muc_subscribe{nick = Nick}]} = Packet, StateData) -> LBareJID = jid:tolower(jid:remove_resource(From)), case (?DICT):find(LBareJID, StateData#state.subscribers) of {ok, #subscriber{nick = Nick1}} when Nick1 /= Nick -> Nodes = get_subscription_nodes(Packet), case {nick_collision(From, Nick, StateData), mod_muc:can_use_nick(StateData#state.server_host, StateData#state.host, From, Nick)} of {true, _} -> ErrText = <<"That nickname is already in use by another occupant">>, {error, xmpp:err_conflict(ErrText, Lang)}; {_, false} -> ErrText = <<"That nickname is registered by another person">>, {error, xmpp:err_conflict(ErrText, Lang)}; _ -> NewStateData = set_subscriber(From, Nick, Nodes, StateData), {result, subscribe_result(Packet), NewStateData} end; {ok, #subscriber{}} -> Nodes = get_subscription_nodes(Packet), NewStateData = set_subscriber(From, Nick, Nodes, StateData), {result, subscribe_result(Packet), NewStateData}; error -> SD2 = StateData#state{config = (StateData#state.config)#config{allow_subscription = true}}, add_new_user(From, Nick, Packet, SD2) end; process_iq_mucsub(From, #iq{type = set, lang = Lang, sub_els = [#muc_unsubscribe{jid = #jid{} = UnsubJid}]}, StateData) -> FAffiliation = get_affiliation(From, StateData), FRole = get_role(From, StateData), if FRole == moderator; FAffiliation == owner; FAffiliation == admin -> process_iq_mucsub(UnsubJid, #iq{type = set, lang = Lang, sub_els = [#muc_unsubscribe{jid = undefined}]}, StateData); true -> Txt = <<"Moderator privileges required">>, {error, xmpp:err_forbidden(Txt, Lang)} end; process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]}, StateData) -> LBareJID = jid:tolower(jid:remove_resource(From)), case ?DICT:find(LBareJID, StateData#state.subscribers) of {ok, #subscriber{nick = Nick}} -> Nicks = ?DICT:erase(Nick, StateData#state.subscriber_nicks), Subscribers = ?DICT:erase(LBareJID, StateData#state.subscribers), NewStateData = StateData#state{subscribers = Subscribers, subscriber_nicks = Nicks}, store_room(NewStateData, [{del_subscription, LBareJID}]), send_subscriptions_change_notifications(LBareJID, Nick, unsubscribe, StateData), NewStateData2 = case close_room_if_temporary_and_empty(NewStateData) of {stop, normal, _} -> stop; {next_state, normal_state, SD} -> SD end, {result, undefined, NewStateData2}; _ -> {result, undefined, StateData} end; process_iq_mucsub(From, #iq{type = get, lang = Lang, sub_els = [#muc_subscriptions{}]}, StateData) -> FAffiliation = get_affiliation(From, StateData), FRole = get_role(From, StateData), if FRole == moderator; FAffiliation == owner; FAffiliation == admin -> JIDs = dict:fold( fun(_, #subscriber{jid = J}, Acc) -> [J|Acc] end, [], StateData#state.subscribers), {result, #muc_subscriptions{list = JIDs}, StateData}; true -> Txt = <<"Moderator privileges required">>, {error, xmpp:err_forbidden(Txt, Lang)} end; process_iq_mucsub(_From, #iq{type = get, lang = Lang}, _StateData) -> Txt = <<"Value 'get' of 'type' attribute is not allowed">>, {error, xmpp:err_bad_request(Txt, Lang)}. remove_subscriptions(StateData) -> if not (StateData#state.config)#config.allow_subscription -> StateData#state{subscribers = ?DICT:new(), subscriber_nicks = ?DICT:new()}; true -> StateData end. -spec get_subscription_nodes(stanza()) -> [binary()]. get_subscription_nodes(#iq{sub_els = [#muc_subscribe{events = Nodes}]}) -> lists:filter( fun(Node) -> lists:member(Node, [?NS_MUCSUB_NODES_PRESENCE, ?NS_MUCSUB_NODES_MESSAGES, ?NS_MUCSUB_NODES_AFFILIATIONS, ?NS_MUCSUB_NODES_SUBJECT, ?NS_MUCSUB_NODES_CONFIG, ?NS_MUCSUB_NODES_PARTICIPANTS, ?NS_MUCSUB_NODES_SUBSCRIBERS]) end, Nodes); get_subscription_nodes(_) -> []. -spec subscribe_result(iq()) -> muc_subscribe(). subscribe_result(#iq{sub_els = [#muc_subscribe{nick = Nick}]} = Packet) -> #muc_subscribe{nick = Nick, events = get_subscription_nodes(Packet)}. -spec get_title(state()) -> binary(). get_title(StateData) -> case (StateData#state.config)#config.title of <<"">> -> StateData#state.room; Name -> Name end. -spec get_roomdesc_reply(jid(), state(), binary()) -> {item, binary()} | false. get_roomdesc_reply(JID, StateData, Tail) -> IsOccupantOrAdmin = is_occupant_or_admin(JID, StateData), if (StateData#state.config)#config.public or IsOccupantOrAdmin -> if (StateData#state.config)#config.public_list or IsOccupantOrAdmin -> {item, <<(get_title(StateData))/binary,Tail/binary>>}; true -> {item, get_title(StateData)} end; true -> false end. -spec get_roomdesc_tail(state(), binary()) -> binary(). get_roomdesc_tail(StateData, Lang) -> Desc = case (StateData#state.config)#config.public of true -> <<"">>; _ -> translate:translate(Lang, <<"private, ">>) end, Len = (?DICT):size(StateData#state.users), <<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>. -spec get_mucroom_disco_items(state()) -> disco_items(). get_mucroom_disco_items(StateData) -> Items = lists:map( fun({_LJID, Info}) -> Nick = Info#user.nick, #disco_item{jid = jid:make(StateData#state.room, StateData#state.host, Nick), name = Nick} end, (?DICT):to_list(StateData#state.users)), #disco_items{items = Items}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Voice request support -spec prepare_request_form(jid(), binary(), binary()) -> message(). prepare_request_form(Requester, Nick, Lang) -> Title = translate:translate(Lang, <<"Voice request">>), Instruction = translate:translate( Lang, <<"Either approve or decline the voice request.">>), Fs = muc_request:encode([{role, participant}, {jid, Requester}, {roomnick, Nick}, {request_allow, false}], Lang), #message{type = normal, sub_els = [#xdata{type = form, title = Title, instructions = [Instruction], fields = Fs}]}. -spec send_voice_request(jid(), binary(), state()) -> ok. send_voice_request(From, Lang, StateData) -> Moderators = search_role(moderator, StateData), FromNick = find_nick_by_jid(From, StateData), lists:foreach( fun({_, User}) -> ejabberd_router:route( xmpp:set_from_to( prepare_request_form(From, FromNick, Lang), StateData#state.jid, User#user.jid)) end, Moderators). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support -spec check_invitation(jid(), [muc_invite()], binary(), state()) -> ok | {error, stanza_error()}. check_invitation(From, Invitations, Lang, StateData) -> FAffiliation = get_affiliation(From, StateData), CanInvite = (StateData#state.config)#config.allow_user_invites orelse FAffiliation == admin orelse FAffiliation == owner, case CanInvite of true -> case lists:all( fun(#muc_invite{to = #jid{}}) -> true; (_) -> false end, Invitations) of true -> ok; false -> Txt = <<"No 'to' attribute found in the invitation">>, {error, xmpp:err_bad_request(Txt, Lang)} end; false -> Txt = <<"Invitations are not allowed in this conference">>, {error, xmpp:err_not_allowed(Txt, Lang)} end. -spec route_invitation(jid(), muc_invite(), binary(), state()) -> jid(). route_invitation(From, Invitation, Lang, StateData) -> #muc_invite{to = JID, reason = Reason} = Invitation, Invite = Invitation#muc_invite{to = undefined, from = From}, Password = case (StateData#state.config)#config.password_protected of true -> (StateData#state.config)#config.password; false -> undefined end, XUser = #muc_user{password = Password, invites = [Invite]}, XConference = #x_conference{jid = jid:make(StateData#state.room, StateData#state.host), reason = Reason}, Body = iolist_to_binary( [io_lib:format( translate:translate( Lang, <<"~s invites you to the room ~s">>), [jid:encode(From), jid:encode({StateData#state.room, StateData#state.host, <<"">>})]), case (StateData#state.config)#config.password_protected of true -> <<", ", (translate:translate( Lang, <<"the password is">>))/binary, " '", ((StateData#state.config)#config.password)/binary, "'">>; _ -> <<"">> end, case Reason of <<"">> -> <<"">>; _ -> <<" (", Reason/binary, ") ">> end]), Msg = #message{from = StateData#state.jid, to = JID, type = normal, body = xmpp:mk_text(Body), sub_els = [XUser, XConference]}, ejabberd_hooks:run(muc_invite, StateData#state.server_host, [StateData#state.jid, StateData#state.config, From, JID, Reason]), ejabberd_router:route(Msg), JID. %% Handle a message sent to the room by a non-participant. %% If it is a decline, send to the inviter. %% Otherwise, an error message is sent to the sender. -spec handle_roommessage_from_nonparticipant(message(), state(), jid()) -> ok. handle_roommessage_from_nonparticipant(Packet, StateData, From) -> try xmpp:try_subtag(Packet, #muc_user{}) of #muc_user{decline = #muc_decline{to = #jid{} = To} = Decline} = XUser -> NewDecline = Decline#muc_decline{to = undefined, from = From}, NewXUser = XUser#muc_user{decline = NewDecline}, NewPacket = xmpp:set_subtag(Packet, NewXUser), ejabberd_router:route( xmpp:set_from_to(NewPacket, StateData#state.jid, To)); _ -> ErrText = <<"Only occupants are allowed to send messages " "to the conference">>, Err = xmpp:err_not_acceptable(ErrText, xmpp:get_lang(Packet)), ejabberd_router:route_error(Packet, Err) catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Err = xmpp:err_bad_request(Txt, xmpp:get_lang(Packet)), ejabberd_router:route_error(Packet, Err) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Logging add_to_log(Type, Data, StateData) when Type == roomconfig_change_disabledlogging -> mod_muc_log:add_to_log(StateData#state.server_host, roomconfig_change, Data, StateData#state.jid, make_opts(StateData)); add_to_log(Type, Data, StateData) -> case (StateData#state.config)#config.logging of true -> mod_muc_log:add_to_log(StateData#state.server_host, Type, Data, StateData#state.jid, make_opts(StateData)); false -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Users number checking -spec tab_add_online_user(jid(), state()) -> any(). tab_add_online_user(JID, StateData) -> Room = StateData#state.room, Host = StateData#state.host, ServerHost = StateData#state.server_host, ejabberd_hooks:run(join_room, ServerHost, [ServerHost, Room, Host, JID]), mod_muc:register_online_user(ServerHost, jid:tolower(JID), Room, Host). -spec tab_remove_online_user(jid(), state()) -> any(). tab_remove_online_user(JID, StateData) -> Room = StateData#state.room, Host = StateData#state.host, ServerHost = StateData#state.server_host, ejabberd_hooks:run(leave_room, ServerHost, [ServerHost, Room, Host, JID]), mod_muc:unregister_online_user(ServerHost, jid:tolower(JID), Room, Host). -spec tab_count_user(jid(), state()) -> non_neg_integer(). tab_count_user(JID, StateData) -> ServerHost = StateData#state.server_host, {LUser, LServer, _} = jid:tolower(JID), mod_muc:count_online_rooms_by_user(ServerHost, LUser, LServer). -spec element_size(stanza()) -> non_neg_integer(). element_size(El) -> byte_size(fxml:element_to_binary(xmpp:encode(El, ?NS_CLIENT))). -spec store_room(state()) -> ok. store_room(StateData) -> store_room(StateData, []). store_room(StateData, ChangesHints) -> if (StateData#state.config)#config.persistent -> mod_muc:store_room(StateData#state.server_host, StateData#state.host, StateData#state.room, make_opts(StateData), ChangesHints); true -> ok end. -spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok. send_subscriptions_change_notifications(From, Nick, Type, State) -> ?DICT:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) -> case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of true -> ShowJid = case (State#state.config)#config.anonymous == false orelse get_role(JID, State) == moderator orelse get_default_role(get_affiliation(JID, State), State) == moderator of true -> true; _ -> false end, Payload = case {Type, ShowJid} of {subscribe, true} -> #muc_subscribe{jid = From, nick = Nick}; {subscribe, _} -> #muc_subscribe{nick = Nick}; {unsubscribe, true} -> #muc_unsubscribe{jid = From, nick = Nick}; {unsubscribe, _} -> #muc_unsubscribe{nick = Nick} end, Packet = #message{ sub_els = [#ps_event{ items = #ps_items{ node = ?NS_MUCSUB_NODES_SUBSCRIBERS, items = [#ps_item{ id = randoms:get_string(), xml_els = [xmpp:encode(Payload)]}]}}]}, ejabberd_router:route(xmpp:set_from_to(Packet, From, JID)); false -> ok end end, ok, State#state.subscribers). -spec send_wrapped(jid(), jid(), stanza(), binary(), state()) -> ok. send_wrapped(From, To, Packet, Node, State) -> LTo = jid:tolower(To), LBareTo = jid:tolower(jid:remove_resource(To)), IsOffline = case ?DICT:find(LTo, State#state.users) of {ok, #user{last_presence = undefined}} -> true; error -> true; _ -> false end, if IsOffline -> case ?DICT:find(LBareTo, State#state.subscribers) of {ok, #subscriber{nodes = Nodes, jid = JID}} -> case lists:member(Node, Nodes) of true -> NewPacket = wrap(From, JID, Packet, Node), ejabberd_router:route( xmpp:set_from_to(NewPacket, State#state.jid, JID)); false -> ok end; _ -> ok end; true -> ejabberd_router:route(xmpp:set_from_to(Packet, From, To)) end. -spec wrap(jid(), jid(), stanza(), binary()) -> message(). wrap(From, To, Packet, Node) -> El = xmpp:encode(xmpp:set_from_to(Packet, From, To)), #message{ sub_els = [#ps_event{ items = #ps_items{ node = Node, items = [#ps_item{ id = randoms:get_string(), xml_els = [El]}]}}]}. %% -spec send_multiple(jid(), binary(), [#user{}], stanza()) -> ok. %% send_multiple(From, Server, Users, Packet) -> %% JIDs = [ User#user.jid || {_, User} <- ?DICT:to_list(Users)], %% ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet). -spec send_wrapped_multiple(jid(), ?TDICT, stanza(), binary(), state()) -> ok. send_wrapped_multiple(From, Users, Packet, Node, State) -> lists:foreach( fun({_, #user{jid = To}}) -> send_wrapped(From, To, Packet, Node, State) end, ?DICT:to_list(Users)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Detect messange stanzas that don't have meaninful content -spec has_body_or_subject(message()) -> boolean(). has_body_or_subject(#message{body = Body, subject = Subj}) -> Body /= [] orelse Subj /= []. ejabberd-18.01/src/shaper.erl0000644000232200023220000001202213225664356016415 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : shaper.erl %%% Author : Alexey Shchepin %%% Purpose : Functions to control connections traffic %%% Created : 9 Feb 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(shaper). -behaviour(gen_server). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([start_link/0, new/1, new1/1, update/2, get_max_rate/1, transform_options/1, load_from_config/0, opt_type/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -record(maxrate, {maxrate = 0 :: integer(), lastrate = 0.0 :: float(), lasttime = 0 :: integer()}). -record(shaper, {name :: {atom(), global}, maxrate :: integer()}). -record(state, {}). -type shaper() :: none | #maxrate{}. -export_type([shaper/0]). -spec start_link() -> {ok, pid()} | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> ejabberd_mnesia:create(?MODULE, shaper, [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, shaper)}]), ejabberd_hooks:add(config_reloaded, ?MODULE, load_from_config, 20), load_from_config(), {ok, #state{}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. -spec load_from_config() -> ok | {error, any()}. load_from_config() -> Shapers = ejabberd_config:get_option(shaper, []), case mnesia:transaction( fun() -> lists:foreach( fun({Name, MaxRate}) -> mnesia:write(#shaper{name = {Name, global}, maxrate = MaxRate}) end, Shapers) end) of {atomic, ok} -> ok; Err -> {error, Err} end. -spec get_max_rate(atom()) -> none | non_neg_integer(). get_max_rate(none) -> none; get_max_rate(Name) -> case ets:lookup(shaper, {Name, global}) of [#shaper{maxrate = R}] -> R; [] -> none end. -spec new(atom()) -> shaper(). new(none) -> none; new(Name) -> MaxRate = case ets:lookup(shaper, {Name, global}) of [#shaper{maxrate = R}] -> R; [] -> none end, new1(MaxRate). -spec new1(none | integer()) -> shaper(). new1(none) -> none; new1(MaxRate) -> #maxrate{maxrate = MaxRate, lastrate = 0.0, lasttime = p1_time_compat:system_time(micro_seconds)}. -spec update(shaper(), integer()) -> {shaper(), integer()}. update(none, _Size) -> {none, 0}; update(#maxrate{} = State, Size) -> MinInterv = 1000 * Size / (2 * State#maxrate.maxrate - State#maxrate.lastrate), Interv = (p1_time_compat:system_time(micro_seconds) - State#maxrate.lasttime) / 1000, ?DEBUG("State: ~p, Size=~p~nM=~p, I=~p~n", [State, Size, MinInterv, Interv]), Pause = if MinInterv > Interv -> 1 + trunc(MinInterv - Interv); true -> 0 end, NextNow = p1_time_compat:system_time(micro_seconds) + Pause * 1000, Div = case NextNow - State#maxrate.lasttime of 0 -> 1; V -> V end, {State#maxrate{lastrate = (State#maxrate.lastrate + 1000000 * Size / Div) / 2, lasttime = NextNow}, Pause}. transform_options(Opts) -> lists:foldl(fun transform_options/2, [], Opts). transform_options({OptName, Name, {maxrate, N}}, Opts) when OptName == shaper -> [{shaper, [{Name, N}]}|Opts]; transform_options({OptName, Name, none}, Opts) when OptName == shaper -> [{shaper, [{Name, none}]}|Opts]; transform_options(Opt, Opts) -> [Opt|Opts]. -spec opt_type(shaper) -> fun((any()) -> any()); (atom()) -> [atom()]. opt_type(shaper) -> fun (V) -> V end; opt_type(_) -> [shaper]. ejabberd-18.01/src/eldap_pool.erl0000644000232200023220000000547313225664356017265 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : eldap_pool.erl %%% Author : Evgeniy Khramtsov %%% Purpose : LDAP connections pool %%% Created : 12 Nov 2006 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(eldap_pool). -author('xram@jabber.ru'). %% API -export([start_link/7, bind/3, search/2, modify_passwd/3]). -include("ejabberd.hrl"). -include("logger.hrl"). %%==================================================================== %% API %%==================================================================== bind(PoolName, DN, Passwd) -> do_request(PoolName, {bind, [DN, Passwd]}). search(PoolName, Opts) -> do_request(PoolName, {search, [Opts]}). modify_passwd(PoolName, DN, Passwd) -> do_request(PoolName, {modify_passwd, [DN, Passwd]}). start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Opts) -> PoolName = make_id(Name), pg2:create(PoolName), lists:foreach(fun (Host) -> ID = list_to_binary(erlang:ref_to_list(make_ref())), case catch eldap:start_link(ID, [Host | Backups], Port, Rootdn, Passwd, Opts) of {ok, Pid} -> pg2:join(PoolName, Pid); Err -> ?INFO_MSG("Err = ~p", [Err]), error end end, Hosts). %%==================================================================== %% Internal functions %%==================================================================== do_request(Name, {F, Args}) -> case pg2:get_closest_pid(make_id(Name)) of Pid when is_pid(Pid) -> case catch apply(eldap, F, [Pid | Args]) of {'EXIT', {timeout, _}} -> ?ERROR_MSG("LDAP request failed: timed out", []); {'EXIT', Reason} -> ?ERROR_MSG("LDAP request failed: eldap:~p(~p)~nReason: ~p", [F, Args, Reason]), {error, Reason}; Reply -> Reply end; Err -> Err end. make_id(Name) -> misc:binary_to_atom(<<"eldap_pool_", Name/binary>>). ejabberd-18.01/src/mod_proxy65_riak.erl0000644000232200023220000000701113225664356020336 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 15 Apr 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_proxy65_riak). -behaviour(mod_proxy65). %% API -export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]). -include("logger.hrl"). -record(proxy65, {sid :: binary(), pid_t :: pid(), pid_i :: pid() | undefined, jid_i :: binary() | undefined}). %%%=================================================================== %%% API %%%=================================================================== init() -> clean_table(). register_stream(SID, Pid) -> case ejabberd_riak:get(proxy65, proxy65_schema(), SID) of {error, notfound} -> ejabberd_riak:put(#proxy65{sid = SID, pid_t = Pid}, proxy65_schema()); {ok, #proxy65{pid_i = undefined} = R} -> ejabberd_riak:put(R#proxy65{pid_i = Pid}, proxy65_schema()); {ok, _} -> {error, conflict}; {error, _} -> {error, db_failure} end. unregister_stream(SID) -> case ejabberd_riak:delete(proxy65, SID) of ok -> ok; {error, _} -> {error, db_failure} end. activate_stream(SID, IJID, MaxConnections, _Node) -> try case ejabberd_riak:get(proxy65, proxy65_schema(), SID) of {ok, #proxy65{pid_t = TPid, pid_i = IPid, jid_i = undefined} = R} when is_pid(IPid) -> {ok, Num} = ejabberd_riak:count_by_index( proxy65, <<"jid_i">>, IJID), if Num >= MaxConnections -> {error, {limit, IPid, TPid}}; true -> ok = ejabberd_riak:put( R#proxy65{jid_i = IJID}, proxy65_schema(), [{'2i', [{<<"jid_i">>, IJID}]}]), {ok, IPid, TPid} end; {ok, #proxy65{jid_i = JID}} -> if is_binary(JID) -> {error, conflict}; true -> {error, notfound} end; {error, _} = Err -> Err end catch _:{badmatch, {error, _}} -> {error, db_failure} end. %%%=================================================================== %%% Internal functions %%%=================================================================== proxy65_schema() -> {record_info(fields, proxy65), #proxy65{}}. clean_table() -> ?DEBUG("Cleaning Riak 'proxy65' table...", []), case ejabberd_riak:get(proxy65, proxy65_schema()) of {ok, Rs} -> lists:foreach( fun(#proxy65{sid = SID, pid_t = TPid, pid_i = IPid}) -> if node(TPid) == node() orelse (is_pid(IPid) andalso node(IPid) == node()) -> ejabberd_riak:delete(proxy65, SID); true -> ok end end, Rs); {error, Reason} = Err -> ?ERROR_MSG("Failed to clean Riak 'proxy65' table: ~p", [Reason]), Err end. ejabberd-18.01/src/mod_vcard_mnesia.erl0000644000232200023220000002176113225664356020437 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_vcard_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_vcard_mnesia). -behaviour(mod_vcard). %% API -export([init/2, stop/1, import/3, get_vcard/2, set_vcard/4, search/4, search_fields/1, search_reported/1, remove_user/2]). -export([is_search_supported/1]). -export([need_transform/1, transform/1]). -include("ejabberd.hrl"). -include("xmpp.hrl"). -include("mod_vcard.hrl"). -include("logger.hrl"). -include("translate.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, vcard, [{disc_only_copies, [node()]}, {attributes, record_info(fields, vcard)}]), ejabberd_mnesia:create(?MODULE, vcard_search, [{disc_copies, [node()]}, {attributes, record_info(fields, vcard_search)}, {index, [ luser, lfn, lfamily, lgiven, lmiddle, lnickname, lbday, lctry, llocality, lemail, lorgname, lorgunit ]}]). stop(_Host) -> ok. is_search_supported(_ServerHost) -> true. get_vcard(LUser, LServer) -> US = {LUser, LServer}, Rs = mnesia:dirty_read(vcard, US), {ok, lists:map(fun (R) -> R#vcard.vcard end, Rs)}. set_vcard(LUser, LServer, VCARD, VCardSearch) -> US = {LUser, LServer}, F = fun () -> mnesia:write(#vcard{us = US, vcard = VCARD}), mnesia:write(VCardSearch) end, mnesia:transaction(F). search(LServer, Data, AllowReturnAll, MaxMatch) -> MatchSpec = make_matchspec(LServer, Data), if (MatchSpec == #vcard_search{_ = '_'}) and not AllowReturnAll -> []; true -> case catch mnesia:dirty_select(vcard_search, [{MatchSpec, [], ['$_']}]) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), []; Rs -> Fields = lists:map(fun record_to_item/1, Rs), case MaxMatch of infinity -> Fields; Val -> lists:sublist(Fields, Val) end end end. search_fields(_LServer) -> [{?T("User"), <<"user">>}, {?T("Full Name"), <<"fn">>}, {?T("Name"), <<"first">>}, {?T("Middle Name"), <<"middle">>}, {?T("Family Name"), <<"last">>}, {?T("Nickname"), <<"nick">>}, {?T("Birthday"), <<"bday">>}, {?T("Country"), <<"ctry">>}, {?T("City"), <<"locality">>}, {?T("Email"), <<"email">>}, {?T("Organization Name"), <<"orgname">>}, {?T("Organization Unit"), <<"orgunit">>}]. search_reported(_LServer) -> [{?T("Jabber ID"), <<"jid">>}, {?T("Full Name"), <<"fn">>}, {?T("Name"), <<"first">>}, {?T("Middle Name"), <<"middle">>}, {?T("Family Name"), <<"last">>}, {?T("Nickname"), <<"nick">>}, {?T("Birthday"), <<"bday">>}, {?T("Country"), <<"ctry">>}, {?T("City"), <<"locality">>}, {?T("Email"), <<"email">>}, {?T("Organization Name"), <<"orgname">>}, {?T("Organization Unit"), <<"orgunit">>}]. remove_user(LUser, LServer) -> US = {LUser, LServer}, F = fun () -> mnesia:delete({vcard, US}), mnesia:delete({vcard_search, US}) end, mnesia:transaction(F). import(LServer, <<"vcard">>, [LUser, XML, _TimeStamp]) -> #xmlel{} = El = fxml_stream:parse_element(XML), VCard = #vcard{us = {LUser, LServer}, vcard = El}, mnesia:dirty_write(VCard); import(LServer, <<"vcard_search">>, [User, LUser, FN, LFN, Family, LFamily, Given, LGiven, Middle, LMiddle, Nickname, LNickname, BDay, LBDay, CTRY, LCTRY, Locality, LLocality, EMail, LEMail, OrgName, LOrgName, OrgUnit, LOrgUnit]) -> mnesia:dirty_write( #vcard_search{us = {LUser, LServer}, user = {User, LServer}, luser = LUser, fn = FN, lfn = LFN, family = Family, lfamily = LFamily, given = Given, lgiven = LGiven, middle = Middle, lmiddle = LMiddle, nickname = Nickname, lnickname = LNickname, bday = BDay, lbday = LBDay, ctry = CTRY, lctry = LCTRY, locality = Locality, llocality = LLocality, email = EMail, lemail = LEMail, orgname = OrgName, lorgname = LOrgName, orgunit = OrgUnit, lorgunit = LOrgUnit}). need_transform(#vcard{us = {U, S}}) when is_list(U) orelse is_list(S) -> ?INFO_MSG("Mnesia table 'vcard' will be converted to binary", []), true; need_transform(#vcard_search{us = {U, S}}) when is_list(U) orelse is_list(S) -> ?INFO_MSG("Mnesia table 'vcard_search' will be converted to binary", []), true; need_transform(_) -> false. transform(#vcard{us = {U, S}, vcard = El} = R) -> R#vcard{us = {iolist_to_binary(U), iolist_to_binary(S)}, vcard = fxml:to_xmlel(El)}; transform(#vcard_search{} = VS) -> [vcard_search | L] = tuple_to_list(VS), NewL = lists:map( fun({U, S}) -> {iolist_to_binary(U), iolist_to_binary(S)}; (Str) -> iolist_to_binary(Str) end, L), list_to_tuple([vcard_search | NewL]). %%%=================================================================== %%% Internal functions %%%=================================================================== make_matchspec(LServer, Data) -> GlobMatch = #vcard_search{_ = '_'}, Match = filter_fields(Data, GlobMatch, LServer), Match. filter_fields([], Match, _LServer) -> Match; filter_fields([{SVar, [Val]} | Ds], Match, LServer) when is_binary(Val) and (Val /= <<"">>) -> LVal = mod_vcard:string2lower(Val), NewMatch = case SVar of <<"user">> -> case gen_mod:get_module_opt(LServer, ?MODULE, search_all_hosts, true) of true -> Match#vcard_search{luser = make_val(LVal)}; false -> Host = find_my_host(LServer), Match#vcard_search{us = {make_val(LVal), Host}} end; <<"fn">> -> Match#vcard_search{lfn = make_val(LVal)}; <<"last">> -> Match#vcard_search{lfamily = make_val(LVal)}; <<"first">> -> Match#vcard_search{lgiven = make_val(LVal)}; <<"middle">> -> Match#vcard_search{lmiddle = make_val(LVal)}; <<"nick">> -> Match#vcard_search{lnickname = make_val(LVal)}; <<"bday">> -> Match#vcard_search{lbday = make_val(LVal)}; <<"ctry">> -> Match#vcard_search{lctry = make_val(LVal)}; <<"locality">> -> Match#vcard_search{llocality = make_val(LVal)}; <<"email">> -> Match#vcard_search{lemail = make_val(LVal)}; <<"orgname">> -> Match#vcard_search{lorgname = make_val(LVal)}; <<"orgunit">> -> Match#vcard_search{lorgunit = make_val(LVal)}; _ -> Match end, filter_fields(Ds, NewMatch, LServer); filter_fields([_ | Ds], Match, LServer) -> filter_fields(Ds, Match, LServer). make_val(Val) -> case str:suffix(<<"*">>, Val) of true -> [str:substr(Val, 1, byte_size(Val) - 1)] ++ '_'; _ -> Val end. find_my_host(LServer) -> Parts = str:tokens(LServer, <<".">>), find_my_host(Parts, ?MYHOSTS). find_my_host([], _Hosts) -> ?MYNAME; find_my_host([_ | Tail] = Parts, Hosts) -> Domain = parts_to_string(Parts), case lists:member(Domain, Hosts) of true -> Domain; false -> find_my_host(Tail, Hosts) end. parts_to_string(Parts) -> str:strip(list_to_binary( lists:map(fun (S) -> <> end, Parts)), right, $.). -spec record_to_item(#vcard_search{}) -> [{binary(), binary()}]. record_to_item(R) -> {User, Server} = R#vcard_search.user, [{<<"jid">>, <>}, {<<"fn">>, (R#vcard_search.fn)}, {<<"last">>, (R#vcard_search.family)}, {<<"first">>, (R#vcard_search.given)}, {<<"middle">>, (R#vcard_search.middle)}, {<<"nick">>, (R#vcard_search.nickname)}, {<<"bday">>, (R#vcard_search.bday)}, {<<"ctry">>, (R#vcard_search.ctry)}, {<<"locality">>, (R#vcard_search.locality)}, {<<"email">>, (R#vcard_search.email)}, {<<"orgname">>, (R#vcard_search.orgname)}, {<<"orgunit">>, (R#vcard_search.orgunit)}]. ejabberd-18.01/src/ejabberd_web_admin.erl0000644000232200023220000025736613225664356020724 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_web_admin.erl %%% Author : Alexey Shchepin %%% Purpose : Administration web interface %%% Created : 9 Apr 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%%% definitions -module(ejabberd_web_admin). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([process/2, list_users/4, list_users_in_diapason/4, pretty_print_xml/1, term_to_id/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -define(INPUTATTRS(Type, Name, Value, Attrs), ?XA(<<"input">>, (Attrs ++ [{<<"type">>, Type}, {<<"name">>, Name}, {<<"value">>, Value}]))). %%%================================== %%%% get_acl_access %% @spec (Path::[string()], Method) -> {HostOfRule, [AccessRule]} %% where Method = 'GET' | 'POST' %% All accounts can access those URLs get_acl_rule([], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"style.css">>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"logo.png">>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"logo-fill.png">>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"favicon.ico">>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"additions.js">>], _) -> {<<"localhost">>, [all]}; %% This page only displays vhosts that the user is admin: get_acl_rule([<<"vhosts">>], _) -> {<<"localhost">>, [all]}; %% The pages of a vhost are only accesible if the user is admin of that vhost: get_acl_rule([<<"server">>, VHost | _RPath], Method) when Method =:= 'GET' orelse Method =:= 'HEAD' -> AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin, access, configure), ACR = gen_mod:get_module_opt(VHost, ejabberd_web_admin, access_readonly, webadmin_view), {VHost, [AC, ACR]}; get_acl_rule([<<"server">>, VHost | _RPath], 'POST') -> AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin, access, configure), {VHost, [AC]}; %% Default rule: only global admins can access any other random page get_acl_rule(_RPath, Method) when Method =:= 'GET' orelse Method =:= 'HEAD' -> AC = gen_mod:get_module_opt(global, ejabberd_web_admin, access, configure), ACR = gen_mod:get_module_opt(global, ejabberd_web_admin, access_readonly, webadmin_view), {global, [AC, ACR]}; get_acl_rule(_RPath, 'POST') -> AC = gen_mod:get_module_opt(global, ejabberd_web_admin, access, configure), {global, [AC]}. %%%================================== %%%% Menu Items Access get_jid(Auth, HostHTTP, Method) -> case get_auth_admin(Auth, HostHTTP, [], Method) of {ok, {User, Server}} -> jid:make(User, Server); {unauthorized, Error} -> ?ERROR_MSG("Unauthorized ~p: ~p", [Auth, Error]), throw({unauthorized, Auth}) end. get_menu_items(global, cluster, Lang, JID) -> {Base, _, Items} = make_server_menu([], [], Lang, JID), lists:map(fun ({URI, Name}) -> {<>, Name}; ({URI, Name, _SubMenu}) -> {<>, Name} end, Items); get_menu_items(Host, cluster, Lang, JID) -> {Base, _, Items} = make_host_menu(Host, [], Lang, JID), lists:map(fun ({URI, Name}) -> {<>, Name}; ({URI, Name, _SubMenu}) -> {<>, Name} end, Items). %% get_menu_items(Host, Node, Lang, JID) -> %% {Base, _, Items} = make_host_node_menu(Host, Node, Lang, JID), %% lists:map( %% fun({URI, Name}) -> %% {Base++URI++"/", Name}; %% ({URI, Name, _SubMenu}) -> %% {Base++URI++"/", Name} %% end, %% Items %% ). is_allowed_path(BasePath, {Path, _}, JID) -> is_allowed_path(BasePath ++ [Path], JID); is_allowed_path(BasePath, {Path, _, _}, JID) -> is_allowed_path(BasePath ++ [Path], JID). is_allowed_path([<<"admin">> | Path], JID) -> is_allowed_path(Path, JID); is_allowed_path(Path, JID) -> {HostOfRule, AccessRule} = get_acl_rule(Path, 'GET'), acl:any_rules_allowed(HostOfRule, AccessRule, JID). %% @spec(Path) -> URL %% where Path = [string()] %% URL = string() %% Convert ["admin", "user", "tom"] -> "/admin/user/tom/" %%path_to_url(Path) -> %% "/" ++ string:join(Path, "/") ++ "/". %% @spec(URL) -> Path %% where Path = [string()] %% URL = string() %% Convert "admin/user/tom" -> ["admin", "user", "tom"] url_to_path(URL) -> str:tokens(URL, <<"/">>). %%%================================== %%%% process/2 process([<<"doc">>, LocalFile], _Request) -> DocPath = case os:getenv("EJABBERD_DOC_PATH") of P when is_list(P) -> P; false -> <<"/share/doc/ejabberd/">> end, FileName = filename:join(DocPath, LocalFile), case file:read_file(FileName) of {ok, FileContents} -> ?DEBUG("Delivering content.", []), {200, [{<<"Server">>, <<"ejabberd">>}], FileContents}; {error, Error} -> Help = <<" ", FileName/binary, " - Try to specify the path to ejabberd " "documentation with the environment variable " "EJABBERD_DOC_PATH. Check the ejabberd " "Guide for more information.">>, ?INFO_MSG("Problem '~p' accessing the local Guide file ~s", [Error, Help]), case Error of eacces -> {403, [], <<"Forbidden", Help/binary>>}; enoent -> {307, [{<<"Location">>, <<"http://docs.ejabberd.im/admin/guide/configuration/">>}], <<"Not found", Help/binary>>}; _Else -> {404, [], <<(iolist_to_binary(atom_to_list(Error)))/binary, Help/binary>>} end end; process([<<"server">>, SHost | RPath] = Path, #request{auth = Auth, lang = Lang, host = HostHTTP, method = Method} = Request) -> Host = jid:nameprep(SHost), case ejabberd_router:is_my_host(Host) of true -> case get_auth_admin(Auth, HostHTTP, Path, Method) of {ok, {User, Server}} -> AJID = get_jid(Auth, HostHTTP, Method), process_admin(Host, Request#request{path = RPath, auth = {auth_jid, Auth, AJID}, us = {User, Server}}); {unauthorized, <<"no-auth-provided">>} -> {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"ejabberd\"">>}], ejabberd_web:make_xhtml([?XCT(<<"h1">>, <<"Unauthorized">>)])}; {unauthorized, Error} -> {BadUser, _BadPass} = Auth, {IPT, _Port} = Request#request.ip, IPS = ejabberd_config:may_hide_data(misc:ip_to_list(IPT)), ?WARNING_MSG("Access of ~p from ~p failed with error: ~p", [BadUser, IPS, Error]), {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"auth error, retry login " "to ejabberd\"">>}], ejabberd_web:make_xhtml([?XCT(<<"h1">>, <<"Unauthorized">>)])} end; false -> ejabberd_web:error(not_found) end; process(RPath, #request{auth = Auth, lang = Lang, host = HostHTTP, method = Method} = Request) -> case get_auth_admin(Auth, HostHTTP, RPath, Method) of {ok, {User, Server}} -> AJID = get_jid(Auth, HostHTTP, Method), process_admin(global, Request#request{path = RPath, auth = {auth_jid, Auth, AJID}, us = {User, Server}}); {unauthorized, <<"no-auth-provided">>} -> {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"ejabberd\"">>}], ejabberd_web:make_xhtml([?XCT(<<"h1">>, <<"Unauthorized">>)])}; {unauthorized, Error} -> {BadUser, _BadPass} = Auth, {IPT, _Port} = Request#request.ip, IPS = ejabberd_config:may_hide_data(misc:ip_to_list(IPT)), ?WARNING_MSG("Access of ~p from ~p failed with error: ~p", [BadUser, IPS, Error]), {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"auth error, retry login " "to ejabberd\"">>}], ejabberd_web:make_xhtml([?XCT(<<"h1">>, <<"Unauthorized">>)])} end. get_auth_admin(Auth, HostHTTP, RPath, Method) -> case Auth of {SJID, Pass} -> {HostOfRule, AccessRule} = get_acl_rule(RPath, Method), try jid:decode(SJID) of #jid{user = <<"">>, server = User} -> get_auth_account(HostOfRule, AccessRule, User, HostHTTP, Pass); #jid{user = User, server = Server} -> get_auth_account(HostOfRule, AccessRule, User, Server, Pass) catch _:{bad_jid, _} -> {unauthorized, <<"badformed-jid">>} end; undefined -> {unauthorized, <<"no-auth-provided">>} end. get_auth_account(HostOfRule, AccessRule, User, Server, Pass) -> case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of true -> case acl:any_rules_allowed(HostOfRule, AccessRule, jid:make(User, Server)) of false -> {unauthorized, <<"unprivileged-account">>}; true -> {ok, {User, Server}} end; false -> case ejabberd_auth:user_exists(User, Server) of true -> {unauthorized, <<"bad-password">>}; false -> {unauthorized, <<"inexistent-account">>} end end. %%%================================== %%%% make_xhtml make_xhtml(Els, Host, Lang, JID) -> make_xhtml(Els, Host, cluster, Lang, JID). %% @spec (Els, Host, Node, Lang, JID) -> {200, [html], xmlelement()} %% where Host = global | string() %% Node = cluster | atom() %% JID = jid() make_xhtml(Els, Host, Node, Lang, JID) -> Base = get_base_path(Host, cluster), MenuItems = make_navigation(Host, Node, Lang, JID), {200, [html], #xmlel{name = <<"html">>, attrs = [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}, {<<"xml:lang">>, Lang}, {<<"lang">>, Lang}]++direction(Lang), children = [#xmlel{name = <<"head">>, attrs = [], children = [?XCT(<<"title">>, <<"ejabberd Web Admin">>), #xmlel{name = <<"meta">>, attrs = [{<<"http-equiv">>, <<"Content-Type">>}, {<<"content">>, <<"text/html; charset=utf-8">>}], children = []}, #xmlel{name = <<"script">>, attrs = [{<<"src">>, <>}, {<<"type">>, <<"text/javascript">>}], children = [?C(<<" ">>)]}, #xmlel{name = <<"link">>, attrs = [{<<"href">>, <>}, {<<"type">>, <<"image/x-icon">>}, {<<"rel">>, <<"shortcut icon">>}], children = []}, #xmlel{name = <<"link">>, attrs = [{<<"href">>, <>}, {<<"type">>, <<"text/css">>}, {<<"rel">>, <<"stylesheet">>}], children = []}]}, ?XE(<<"body">>, [?XAE(<<"div">>, [{<<"id">>, <<"container">>}], [?XAE(<<"div">>, [{<<"id">>, <<"header">>}], [?XE(<<"h1">>, [?ACT(<<"/admin/">>, <<"ejabberd Web Admin">>)])]), ?XAE(<<"div">>, [{<<"id">>, <<"navigation">>}], [?XE(<<"ul">>, MenuItems)]), ?XAE(<<"div">>, [{<<"id">>, <<"content">>}], Els), ?XAE(<<"div">>, [{<<"id">>, <<"clearcopyright">>}], [{xmlcdata, <<"">>}])]), ?XAE(<<"div">>, [{<<"id">>, <<"copyrightouter">>}], [?XAE(<<"div">>, [{<<"id">>, <<"copyright">>}], [?XE(<<"p">>, [?AC(<<"https://www.ejabberd.im/">>, <<"ejabberd">>), ?C(<<" (c) 2002-2018 ">>), ?AC(<<"https://www.process-one.net/">>, <<"ProcessOne, leader in messaging and push solutions">>)] )])])])]}}. direction(ltr) -> [{<<"dir">>, <<"ltr">>}]; direction(<<"he">>) -> [{<<"dir">>, <<"rtl">>}]; direction(_) -> []. get_base_path(global, cluster) -> <<"/admin/">>; get_base_path(Host, cluster) -> <<"/admin/server/", Host/binary, "/">>; get_base_path(global, Node) -> <<"/admin/node/", (iolist_to_binary(atom_to_list(Node)))/binary, "/">>; get_base_path(Host, Node) -> <<"/admin/server/", Host/binary, "/node/", (iolist_to_binary(atom_to_list(Node)))/binary, "/">>. %%%================================== %%%% css & images additions_js() -> case misc:read_js("admin.js") of {ok, JS} -> JS; {error, _} -> <<>> end. css(Host) -> case misc:read_css("admin.css") of {ok, CSS} -> Base = get_base_path(Host, cluster), re:replace(CSS, <<"@BASE@">>, Base, [{return, binary}]); {error, _} -> <<>> end. favicon() -> case misc:read_img("favicon.png") of {ok, ICO} -> ICO; {error, _} -> <<>> end. logo() -> case misc:read_img("admin-logo.png") of {ok, Img} -> Img; {error, _} -> <<>> end. logo_fill() -> case misc:read_img("admin-logo-fill.png") of {ok, Img} -> Img; {error, _} -> <<>> end. %%%================================== %%%% process_admin process_admin(global, #request{path = [], auth = {_, _, AJID}, lang = Lang}) -> make_xhtml((?H1GL((?T(<<"Administration">>)), <<"">>, <<"Contents">>)) ++ [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- get_menu_items(global, cluster, Lang, AJID)])], global, Lang, AJID); process_admin(Host, #request{path = [], auth = {_, _Auth, AJID}, lang = Lang}) -> make_xhtml([?XCT(<<"h1">>, <<"Administration">>), ?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- get_menu_items(Host, cluster, Lang, AJID)])], Host, Lang, AJID); process_admin(Host, #request{path = [<<"style.css">>]}) -> {200, [{<<"Content-Type">>, <<"text/css">>}, last_modified(), cache_control_public()], css(Host)}; process_admin(_Host, #request{path = [<<"favicon.ico">>]}) -> {200, [{<<"Content-Type">>, <<"image/x-icon">>}, last_modified(), cache_control_public()], favicon()}; process_admin(_Host, #request{path = [<<"logo.png">>]}) -> {200, [{<<"Content-Type">>, <<"image/png">>}, last_modified(), cache_control_public()], logo()}; process_admin(_Host, #request{path = [<<"logo-fill.png">>]}) -> {200, [{<<"Content-Type">>, <<"image/png">>}, last_modified(), cache_control_public()], logo_fill()}; process_admin(_Host, #request{path = [<<"additions.js">>]}) -> {200, [{<<"Content-Type">>, <<"text/javascript">>}, last_modified(), cache_control_public()], additions_js()}; process_admin(Host, #request{path = [<<"acls-raw">>], q = Query, auth = {_, _Auth, AJID}, lang = Lang}) -> Res = case lists:keysearch(<<"acls">>, 1, Query) of {value, {_, String}} -> case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, NewACLs} -> case catch acl:add_list(Host, NewACLs, true) of ok -> ok; _ -> error end; _ -> error end; _ -> error end; _ -> nothing end, ACLs = lists:keysort(2, mnesia:dirty_select(acl, [{{acl, {'$1', Host}, '$2'}, [], [{{acl, '$1', '$2'}}]}])), {NumLines, ACLsP} = term_to_paragraph(ACLs, 80), make_xhtml((?H1GL((?T(<<"Access Control Lists">>)), <<"acldefinition">>, <<"ACL Definition">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr), [?TEXTAREA(<<"acls">>, (integer_to_binary(lists:max([16, NumLines]))), <<"80">>, <<(iolist_to_binary(ACLsP))/binary, ".">>), ?BR, ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])], Host, Lang, AJID); process_admin(Host, #request{method = Method, path = [<<"acls">>], auth = {_, _Auth, AJID}, q = Query, lang = Lang}) -> ?DEBUG("query: ~p", [Query]), Res = case Method of 'POST' -> case catch acl_parse_query(Host, Query) of {'EXIT', _} -> error; NewACLs -> ?INFO_MSG("NewACLs at ~s: ~p", [Host, NewACLs]), case catch acl:add_list(Host, NewACLs, true) of ok -> ok; _ -> error end end; _ -> nothing end, ACLs = lists:keysort(2, mnesia:dirty_select(acl, [{{acl, {'$1', Host}, '$2'}, [], [{{acl, '$1', '$2'}}]}])), make_xhtml((?H1GL((?T(<<"Access Control Lists">>)), <<"acldefinition">>, <<"ACL Definition">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"p">>, direction(ltr), [?ACT(<<"../acls-raw/">>, <<"Raw">>)])] ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr), [acls_to_xhtml(ACLs), ?BR, ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])], Host, Lang, AJID); process_admin(Host, #request{path = [<<"access-raw">>], auth = {_, _Auth, AJID}, q = Query, lang = Lang}) -> SetAccess = fun (Rs) -> mnesia:transaction(fun () -> Os = mnesia:select(access, [{{access, {'$1', Host}, '$2'}, [], ['$_']}]), lists:foreach(fun (O) -> mnesia:delete_object(O) end, Os), lists:foreach(fun ({access, Name, Rules}) -> mnesia:write({access, {Name, Host}, Rules}) end, Rs) end) end, Res = case lists:keysearch(<<"access">>, 1, Query) of {value, {_, String}} -> case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, Rs} -> case SetAccess(Rs) of {atomic, _} -> ok; _ -> error end; _ -> error end; _ -> error end; _ -> nothing end, Access = mnesia:dirty_select(access, [{{access, {'$1', Host}, '$2'}, [], [{{access, '$1', '$2'}}]}]), {NumLines, AccessP} = term_to_paragraph(lists:keysort(2,Access), 80), make_xhtml((?H1GL((?T(<<"Access Rules">>)), <<"accessrights">>, <<"Access Rights">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr), [?TEXTAREA(<<"access">>, (integer_to_binary(lists:max([16, NumLines]))), <<"80">>, <<(iolist_to_binary(AccessP))/binary, ".">>), ?BR, ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])], Host, Lang, AJID); process_admin(Host, #request{method = Method, path = [<<"access">>], q = Query, auth = {_, _Auth, AJID}, lang = Lang}) -> ?DEBUG("query: ~p", [Query]), Res = case Method of 'POST' -> case catch access_parse_query(Host, Query) of {'EXIT', _} -> error; ok -> ok end; _ -> nothing end, AccessRules = mnesia:dirty_select(access, [{{access, {'$1', Host}, '$2'}, [], [{{access, '$1', '$2'}}]}]), make_xhtml((?H1GL((?T(<<"Access Rules">>)), <<"accessrights">>, <<"Access Rights">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"p">>, direction(ltr), [?ACT(<<"../access-raw/">>, <<"Raw">>)])] ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr), [access_rules_to_xhtml(AccessRules, Lang), ?BR, ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>)])], Host, Lang, AJID); process_admin(Host, #request{path = [<<"access">>, SName], q = Query, auth = {_, _Auth, AJID}, lang = Lang}) -> ?DEBUG("query: ~p", [Query]), Name = misc:binary_to_atom(SName), Res = case lists:keysearch(<<"rules">>, 1, Query) of {value, {_, String}} -> case parse_access_rule(String) of {ok, Rs} -> ejabberd_config:add_option({access, Name, Host}, Rs), ok; _ -> error end; _ -> nothing end, Rules = ejabberd_config:get_option({access, Name, Host}, []), make_xhtml([?XC(<<"h1">>, (str:format( ?T(<<"~s access rule configuration">>), [SName])))] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [access_rule_to_xhtml(Rules), ?BR, ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])], Host, Lang, AJID); process_admin(global, #request{path = [<<"vhosts">>], auth = {_, _Auth, AJID}, lang = Lang}) -> Res = list_vhosts(Lang, AJID), make_xhtml((?H1GL((?T(<<"Virtual Hosts">>)), <<"virtualhosting">>, <<"Virtual Hosting">>)) ++ Res, global, Lang, AJID); process_admin(Host, #request{path = [<<"users">>], q = Query, auth = {_, _Auth, AJID}, lang = Lang}) when is_binary(Host) -> Res = list_users(Host, Query, Lang, fun url_func/1), make_xhtml([?XCT(<<"h1">>, <<"Users">>)] ++ Res, Host, Lang, AJID); process_admin(Host, #request{path = [<<"users">>, Diap], auth = {_, _Auth, AJID}, lang = Lang}) when is_binary(Host) -> Res = list_users_in_diapason(Host, Diap, Lang, fun url_func/1), make_xhtml([?XCT(<<"h1">>, <<"Users">>)] ++ Res, Host, Lang, AJID); process_admin(Host, #request{path = [<<"online-users">>], auth = {_, _Auth, AJID}, lang = Lang}) when is_binary(Host) -> Res = list_online_users(Host, Lang), make_xhtml([?XCT(<<"h1">>, <<"Online Users">>)] ++ Res, Host, Lang, AJID); process_admin(Host, #request{path = [<<"last-activity">>], auth = {_, _Auth, AJID}, q = Query, lang = Lang}) when is_binary(Host) -> ?DEBUG("query: ~p", [Query]), Month = case lists:keysearch(<<"period">>, 1, Query) of {value, {_, Val}} -> Val; _ -> <<"month">> end, Res = case lists:keysearch(<<"ordinary">>, 1, Query) of {value, {_, _}} -> list_last_activity(Host, Lang, false, Month); _ -> list_last_activity(Host, Lang, true, Month) end, make_xhtml([?XCT(<<"h1">>, <<"Users Last Activity">>)] ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?CT(<<"Period: ">>), ?XAE(<<"select">>, [{<<"name">>, <<"period">>}], (lists:map(fun ({O, V}) -> Sel = if O == Month -> [{<<"selected">>, <<"selected">>}]; true -> [] end, ?XAC(<<"option">>, (Sel ++ [{<<"value">>, O}]), V) end, [{<<"month">>, ?T(<<"Last month">>)}, {<<"year">>, ?T(<<"Last year">>)}, {<<"all">>, ?T(<<"All activity">>)}]))), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"ordinary">>, <<"Show Ordinary Table">>), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"integral">>, <<"Show Integral Table">>)])] ++ Res, Host, Lang, AJID); process_admin(Host, #request{path = [<<"stats">>], auth = {_, _Auth, AJID}, lang = Lang}) -> Res = get_stats(Host, Lang), make_xhtml([?XCT(<<"h1">>, <<"Statistics">>)] ++ Res, Host, Lang, AJID); process_admin(Host, #request{path = [<<"user">>, U], auth = {_, _Auth, AJID}, q = Query, lang = Lang}) -> case ejabberd_auth:user_exists(U, Host) of true -> Res = user_info(U, Host, Query, Lang), make_xhtml(Res, Host, Lang, AJID); false -> make_xhtml([?XCT(<<"h1">>, <<"Not Found">>)], Host, Lang, AJID) end; process_admin(Host, #request{path = [<<"nodes">>], auth = {_, _Auth, AJID}, lang = Lang}) -> Res = get_nodes(Lang), make_xhtml(Res, Host, Lang, AJID); process_admin(Host, #request{path = [<<"node">>, SNode | NPath], auth = {_, _Auth, AJID}, q = Query, lang = Lang}) -> case search_running_node(SNode) of false -> make_xhtml([?XCT(<<"h1">>, <<"Node not found">>)], Host, Lang, AJID); Node -> Res = get_node(Host, Node, NPath, Query, Lang), make_xhtml(Res, Host, Node, Lang, AJID) end; %%%================================== %%%% process_admin default case process_admin(Host, #request{lang = Lang, auth = {_, _Auth, AJID}} = Request) -> Res = case Host of global -> ejabberd_hooks:run_fold( webadmin_page_main, Host, [], [Request]); _ -> ejabberd_hooks:run_fold( webadmin_page_host, Host, [], [Host, Request]) end, case Res of [] -> setelement(1, make_xhtml([?XC(<<"h1">>, <<"Not Found">>)], Host, Lang, AJID), 404); _ -> make_xhtml(Res, Host, Lang, AJID) end. %%%================================== %%%% acl acls_to_xhtml(ACLs) -> ?XAE(<<"table">>, [], [?XE(<<"tbody">>, (lists:map(fun ({acl, Name, Spec} = ACL) -> SName = iolist_to_binary(atom_to_list(Name)), ID = term_to_id(ACL), ?XE(<<"tr">>, ([?XE(<<"td">>, [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), ?XC(<<"td">>, SName)] ++ acl_spec_to_xhtml(ID, Spec))) end, ACLs) ++ [?XE(<<"tr">>, ([?X(<<"td">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"namenew">>, <<"">>)])] ++ acl_spec_to_xhtml(<<"new">>, {user, <<"">>})))]))]). acl_spec_to_text({user, {U, S}}) -> {user, <>}; acl_spec_to_text({user, U}) -> {user, U}; acl_spec_to_text({server, S}) -> {server, S}; acl_spec_to_text({user_regexp, {RU, S}}) -> {user_regexp, <>}; acl_spec_to_text({user_regexp, RU}) -> {user_regexp, RU}; acl_spec_to_text({server_regexp, RS}) -> {server_regexp, RS}; acl_spec_to_text({node_regexp, {RU, RS}}) -> {node_regexp, <>}; acl_spec_to_text({user_glob, {RU, S}}) -> {user_glob, <>}; acl_spec_to_text({user_glob, RU}) -> {user_glob, RU}; acl_spec_to_text({server_glob, RS}) -> {server_glob, RS}; acl_spec_to_text({node_glob, {RU, RS}}) -> {node_glob, <>}; acl_spec_to_text(all) -> {all, <<"">>}; acl_spec_to_text({ip, {IP, L}}) -> {ip, <<(misc:ip_to_list(IP))/binary, "/", (integer_to_binary(L))/binary>>}; acl_spec_to_text(Spec) -> {raw, term_to_string(Spec)}. acl_spec_to_xhtml(ID, Spec) -> {Type, Str} = acl_spec_to_text(Spec), [acl_spec_select(ID, Type), ?ACLINPUT(Str)]. acl_spec_select(ID, Opt) -> ?XE(<<"td">>, [?XAE(<<"select">>, [{<<"name">>, <<"type", ID/binary>>}], (lists:map(fun (O) -> Sel = if O == Opt -> [{<<"selected">>, <<"selected">>}]; true -> [] end, ?XAC(<<"option">>, (Sel ++ [{<<"value">>, iolist_to_binary(atom_to_list(O))}]), (iolist_to_binary(atom_to_list(O)))) end, [user, server, user_regexp, server_regexp, node_regexp, user_glob, server_glob, node_glob, all, ip, raw])))]). %% @spec (T::any()) -> StringLine::string() term_to_string(T) -> StringParagraph = (str:format("~1000000p", [T])), ejabberd_regexp:greplace(StringParagraph, <<"\\n ">>, <<"">>). %% @spec (T::any(), Cols::integer()) -> {NumLines::integer(), Paragraph::string()} term_to_paragraph(T, Cols) -> Paragraph = iolist_to_binary(io_lib:print(T, 1, Cols, -1)), FieldList = ejabberd_regexp:split(Paragraph, <<"\n">>), NumLines = length(FieldList), {NumLines, Paragraph}. term_to_id(T) -> base64:encode((term_to_binary(T))). acl_parse_query(Host, Query) -> ACLs = mnesia:dirty_select(acl, [{{acl, {'$1', Host}, '$2'}, [], [{{acl, '$1', '$2'}}]}]), case lists:keysearch(<<"submit">>, 1, Query) of {value, _} -> acl_parse_submit(ACLs, Query); _ -> case lists:keysearch(<<"delete">>, 1, Query) of {value, _} -> acl_parse_delete(ACLs, Query) end end. acl_parse_submit(ACLs, Query) -> NewACLs = lists:map(fun ({acl, Name, Spec} = ACL) -> ID = term_to_id(ACL), case {lists:keysearch(<<"type", ID/binary>>, 1, Query), lists:keysearch(<<"value", ID/binary>>, 1, Query)} of {{value, {_, T}}, {value, {_, V}}} -> {Type, Str} = acl_spec_to_text(Spec), case {iolist_to_binary(atom_to_list(Type)), Str} of {T, V} -> ACL; _ -> NewSpec = string_to_spec(T, V), {acl, Name, NewSpec} end; _ -> ACL end end, ACLs), NewACL = case {lists:keysearch(<<"namenew">>, 1, Query), lists:keysearch(<<"typenew">>, 1, Query), lists:keysearch(<<"valuenew">>, 1, Query)} of {{value, {_, <<"">>}}, _, _} -> []; {{value, {_, N}}, {value, {_, T}}, {value, {_, V}}} -> NewName = misc:binary_to_atom(N), NewSpec = string_to_spec(T, V), [{acl, NewName, NewSpec}]; _ -> [] end, NewACLs ++ NewACL. string_to_spec(<<"user">>, Val) -> string_to_spec2(user, Val); string_to_spec(<<"server">>, Val) -> {server, Val}; string_to_spec(<<"user_regexp">>, Val) -> string_to_spec2(user_regexp, Val); string_to_spec(<<"server_regexp">>, Val) -> {server_regexp, Val}; string_to_spec(<<"node_regexp">>, Val) -> #jid{luser = U, lserver = S, resource = <<"">>} = jid:decode(Val), {node_regexp, U, S}; string_to_spec(<<"user_glob">>, Val) -> string_to_spec2(user_glob, Val); string_to_spec(<<"server_glob">>, Val) -> {server_glob, Val}; string_to_spec(<<"node_glob">>, Val) -> #jid{luser = U, lserver = S, resource = <<"">>} = jid:decode(Val), {node_glob, U, S}; string_to_spec(<<"ip">>, Val) -> [IPs, Ms] = str:tokens(Val, <<"/">>), {ok, IP} = inet_parse:address(binary_to_list(IPs)), {ip, {IP, binary_to_integer(Ms)}}; string_to_spec(<<"all">>, _) -> all; string_to_spec(<<"raw">>, Val) -> {ok, Tokens, _} = erl_scan:string(binary_to_list(<>)), {ok, NewSpec} = erl_parse:parse_term(Tokens), NewSpec. string_to_spec2(ACLName, Val) -> #jid{luser = U, lserver = S, resource = <<"">>} = jid:decode(Val), case U of <<"">> -> {ACLName, S}; _ -> {ACLName, {U, S}} end. acl_parse_delete(ACLs, Query) -> NewACLs = lists:filter(fun ({acl, _Name, _Spec} = ACL) -> ID = term_to_id(ACL), not lists:member({<<"selected">>, ID}, Query) end, ACLs), NewACLs. access_rules_to_xhtml(AccessRules, Lang) -> ?XAE(<<"table">>, [], [?XE(<<"tbody">>, (lists:map(fun ({access, Name, Rules} = Access) -> SName = iolist_to_binary(atom_to_list(Name)), ID = term_to_id(Access), ?XE(<<"tr">>, [?XE(<<"td">>, [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), ?XE(<<"td">>, [?AC(<>, SName)]), ?XC(<<"td">>, (term_to_string(Rules)))]) end, lists:keysort(2,AccessRules)) ++ [?XE(<<"tr">>, [?X(<<"td">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"namenew">>, <<"">>)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"addnew">>, <<"Add New">>)])])]))]). access_parse_query(Host, Query) -> AccessRules = mnesia:dirty_select(access, [{{access, {'$1', Host}, '$2'}, [], [{{access, '$1', '$2'}}]}]), case lists:keysearch(<<"addnew">>, 1, Query) of {value, _} -> access_parse_addnew(AccessRules, Host, Query); _ -> case lists:keysearch(<<"delete">>, 1, Query) of {value, _} -> access_parse_delete(AccessRules, Host, Query) end end. access_parse_addnew(_AccessRules, Host, Query) -> case lists:keysearch(<<"namenew">>, 1, Query) of {value, {_, String}} when String /= <<"">> -> Name = misc:binary_to_atom(String), ejabberd_config:add_option({access, Name, Host}, []), ok end. access_parse_delete(AccessRules, Host, Query) -> lists:foreach(fun ({access, Name, _Rules} = AccessRule) -> ID = term_to_id(AccessRule), case lists:member({<<"selected">>, ID}, Query) of true -> mnesia:transaction(fun () -> mnesia:delete({access, {Name, Host}}) end); _ -> ok end end, AccessRules), ok. access_rule_to_xhtml(Rules) -> Text = lists:flatmap(fun ({Access, ACL} = _Rule) -> SAccess = element_to_list(Access), SACL = atom_to_list(ACL), [SAccess, " \t", SACL, "\n"] end, Rules), ?XAC(<<"textarea">>, [{<<"name">>, <<"rules">>}, {<<"rows">>, <<"16">>}, {<<"cols">>, <<"80">>}], list_to_binary(Text)). parse_access_rule(Text) -> Strings = str:tokens(Text, <<"\r\n">>), case catch lists:flatmap(fun (String) -> case str:tokens(String, <<" \t">>) of [Access, ACL] -> [{list_to_element(Access), misc:binary_to_atom(ACL)}]; [] -> [] end end, Strings) of {'EXIT', _Reason} -> error; Rs -> {ok, Rs} end. %%%================================== %%%% list_vhosts list_vhosts(Lang, JID) -> Hosts = (?MYHOSTS), HostsAllowed = lists:filter(fun (Host) -> acl:any_rules_allowed(Host, [configure, webadmin_view], JID) end, Hosts), list_vhosts2(Lang, HostsAllowed). list_vhosts2(Lang, Hosts) -> SHosts = lists:sort(Hosts), [?XE(<<"table">>, [?XE(<<"thead">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Host">>), ?XCT(<<"td">>, <<"Registered Users">>), ?XCT(<<"td">>, <<"Online Users">>)])]), ?XE(<<"tbody">>, (lists:map(fun (Host) -> OnlineUsers = length(ejabberd_sm:get_vh_session_list(Host)), RegisteredUsers = ejabberd_auth:count_users(Host), ?XE(<<"tr">>, [?XE(<<"td">>, [?AC(<<"../server/", Host/binary, "/">>, Host)]), ?XC(<<"td">>, (pretty_string_int(RegisteredUsers))), ?XC(<<"td">>, (pretty_string_int(OnlineUsers)))]) end, SHosts)))])]. %%%================================== %%%% list_users list_users(Host, Query, Lang, URLFunc) -> Res = list_users_parse_query(Query, Host), Users = ejabberd_auth:get_users(Host), SUsers = lists:sort([{S, U} || {U, S} <- Users]), FUsers = case length(SUsers) of N when N =< 100 -> [list_given_users(Host, SUsers, <<"../">>, Lang, URLFunc)]; N -> NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) + 1, M = trunc(N / NParts) + 1, lists:flatmap(fun (K) -> L = K + M - 1, Last = if L < N -> su_to_list(lists:nth(L, SUsers)); true -> su_to_list(lists:last(SUsers)) end, Name = <<(su_to_list(lists:nth(K, SUsers)))/binary, $\s, 226, 128, 148, $\s, Last/binary>>, [?AC((URLFunc({user_diapason, K, L})), Name), ?BR] end, lists:seq(1, N, M)) end, case Res of %% Parse user creation query and try register: ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], ([?XE(<<"table">>, [?XE(<<"tr">>, [?XC(<<"td">>, <<(?T(<<"User">>))/binary, ":">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"newusername">>, <<"">>)]), ?XE(<<"td">>, [?C(<<" @ ", Host/binary>>)])]), ?XE(<<"tr">>, [?XC(<<"td">>, <<(?T(<<"Password">>))/binary, ":">>), ?XE(<<"td">>, [?INPUT(<<"password">>, <<"newuserpassword">>, <<"">>)]), ?X(<<"td">>)]), ?XE(<<"tr">>, [?X(<<"td">>), ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], [?INPUTT(<<"submit">>, <<"addnewuser">>, <<"Add User">>)]), ?X(<<"td">>)])]), ?P] ++ FUsers))]. list_users_parse_query(Query, Host) -> case lists:keysearch(<<"addnewuser">>, 1, Query) of {value, _} -> {value, {_, Username}} = lists:keysearch(<<"newusername">>, 1, Query), {value, {_, Password}} = lists:keysearch(<<"newuserpassword">>, 1, Query), try jid:decode(<>) of #jid{user = User, server = Server} -> case ejabberd_auth:try_register(User, Server, Password) of {error, _Reason} -> error; _ -> ok end catch _:{bad_jid, _} -> error end; false -> nothing end. list_users_in_diapason(Host, Diap, Lang, URLFunc) -> Users = ejabberd_auth:get_users(Host), SUsers = lists:sort([{S, U} || {U, S} <- Users]), [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), N1 = binary_to_integer(S1), N2 = binary_to_integer(S2), Sub = lists:sublist(SUsers, N1, N2 - N1 + 1), [list_given_users(Host, Sub, <<"../../">>, Lang, URLFunc)]. list_given_users(Host, Users, Prefix, Lang, URLFunc) -> ModOffline = get_offlinemsg_module(Host), ?XE(<<"table">>, [?XE(<<"thead">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"User">>), ?XCT(<<"td">>, <<"Offline Messages">>), ?XCT(<<"td">>, <<"Last Activity">>)])]), ?XE(<<"tbody">>, (lists:map(fun (_SU = {Server, User}) -> US = {User, Server}, QueueLenStr = get_offlinemsg_length(ModOffline, User, Server), FQueueLen = [?AC((URLFunc({users_queue, Prefix, User, Server})), QueueLenStr)], FLast = case ejabberd_sm:get_user_resources(User, Server) of [] -> case mod_last:get_last_info(User, Server) of not_found -> ?T(<<"Never">>); {ok, Shift, _Status} -> TimeStamp = {Shift div 1000000, Shift rem 1000000, 0}, {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(TimeStamp), (str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second])) end; _ -> ?T(<<"Online">>) end, ?XE(<<"tr">>, [?XE(<<"td">>, [?AC((URLFunc({user, Prefix, ejabberd_http:url_encode(User), Server})), (us_to_list(US)))]), ?XE(<<"td">>, FQueueLen), ?XC(<<"td">>, FLast)]) end, Users)))]). get_offlinemsg_length(ModOffline, User, Server) -> case ModOffline of none -> <<"disabled">>; _ -> pretty_string_int(ModOffline:count_offline_messages(User,Server)) end. get_offlinemsg_module(Server) -> case gen_mod:is_loaded(Server, mod_offline) of true -> mod_offline; false -> none end. get_lastactivity_menuitem_list(Server) -> case gen_mod:db_type(Server, mod_last) of mnesia -> [{<<"last-activity">>, <<"Last Activity">>}]; _ -> [] end. us_to_list({User, Server}) -> jid:encode({User, Server, <<"">>}). su_to_list({Server, User}) -> jid:encode({User, Server, <<"">>}). %%%================================== %%%% get_stats get_stats(global, Lang) -> OnlineUsers = ejabberd_sm:connected_users_number(), RegisteredUsers = lists:foldl(fun (Host, Total) -> ejabberd_auth:count_users(Host) + Total end, 0, ?MYHOSTS), OutS2SNumber = ejabberd_s2s:outgoing_s2s_number(), InS2SNumber = ejabberd_s2s:incoming_s2s_number(), [?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Registered Users:">>), ?XC(<<"td">>, (pretty_string_int(RegisteredUsers)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Online Users:">>), ?XC(<<"td">>, (pretty_string_int(OnlineUsers)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Outgoing s2s Connections:">>), ?XC(<<"td">>, (pretty_string_int(OutS2SNumber)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Incoming s2s Connections:">>), ?XC(<<"td">>, (pretty_string_int(InS2SNumber)))])])])]; get_stats(Host, Lang) -> OnlineUsers = length(ejabberd_sm:get_vh_session_list(Host)), RegisteredUsers = ejabberd_auth:count_users(Host), [?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Registered Users:">>), ?XC(<<"td">>, (pretty_string_int(RegisteredUsers)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Online Users:">>), ?XC(<<"td">>, (pretty_string_int(OnlineUsers)))])])])]. list_online_users(Host, _Lang) -> Users = [{S, U} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Host)], SUsers = lists:usort(Users), lists:flatmap(fun ({_S, U} = SU) -> [?AC(<<"../user/", (ejabberd_http:url_encode(U))/binary, "/">>, (su_to_list(SU))), ?BR] end, SUsers). user_info(User, Server, Query, Lang) -> LServer = jid:nameprep(Server), US = {jid:nodeprep(User), LServer}, Res = user_parse_query(User, Server, Query), Resources = ejabberd_sm:get_user_resources(User, Server), FResources = case Resources of [] -> [?CT(<<"None">>)]; _ -> [?XE(<<"ul">>, (lists:map( fun (R) -> FIP = case ejabberd_sm:get_user_info(User, Server, R) of offline -> <<"">>; Info when is_list(Info) -> Node = proplists:get_value(node, Info), Conn = proplists:get_value(conn, Info), {IP, Port} = proplists:get_value(ip, Info), ConnS = case Conn of c2s -> <<"plain">>; c2s_tls -> <<"tls">>; c2s_compressed -> <<"zlib">>; c2s_compressed_tls -> <<"tls+zlib">>; http_bind -> <<"http-bind">>; websocket -> <<"websocket">>; _ -> <<"unknown">> end, <> end, case direction(Lang) of [{_, <<"rtl">>}] -> ?LI([?C((<>))]); _ -> ?LI([?C((<>))]) end end, lists:sort(Resources))))] end, FPassword = [?INPUT(<<"text">>, <<"password">>, <<"">>), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"chpassword">>, <<"Change Password">>)], UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [], [User, Server, Lang]), LastActivity = case ejabberd_sm:get_user_resources(User, Server) of [] -> case mod_last:get_last_info(User, Server) of not_found -> ?T(<<"Never">>); {ok, Shift, _Status} -> TimeStamp = {Shift div 1000000, Shift rem 1000000, 0}, {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(TimeStamp), (str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second])) end; _ -> ?T(<<"Online">>) end, [?XC(<<"h1">>, (str:format(?T(<<"User ~s">>), [us_to_list(US)])))] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], ([?XCT(<<"h3">>, <<"Connected Resources:">>)] ++ FResources ++ [?XCT(<<"h3">>, <<"Password:">>)] ++ FPassword ++ [?XCT(<<"h3">>, <<"Last Activity">>)] ++ [?C(LastActivity)] ++ UserItems ++ [?P, ?INPUTT(<<"submit">>, <<"removeuser">>, <<"Remove User">>)]))]. user_parse_query(User, Server, Query) -> lists:foldl(fun ({Action, _Value}, Acc) when Acc == nothing -> user_parse_query1(Action, User, Server, Query); ({_Action, _Value}, Acc) -> Acc end, nothing, Query). user_parse_query1(<<"password">>, _User, _Server, _Query) -> nothing; user_parse_query1(<<"chpassword">>, User, Server, Query) -> case lists:keysearch(<<"password">>, 1, Query) of {value, {_, Password}} -> ejabberd_auth:set_password(User, Server, Password), ok; _ -> error end; user_parse_query1(<<"removeuser">>, User, Server, _Query) -> ejabberd_auth:remove_user(User, Server), ok; user_parse_query1(Action, User, Server, Query) -> case ejabberd_hooks:run_fold(webadmin_user_parse_query, Server, [], [Action, User, Server, Query]) of [] -> nothing; Res -> Res end. list_last_activity(Host, Lang, Integral, Period) -> TimeStamp = p1_time_compat:system_time(seconds), case Period of <<"all">> -> TS = 0, Days = infinity; <<"year">> -> TS = TimeStamp - 366 * 86400, Days = 366; _ -> TS = TimeStamp - 31 * 86400, Days = 31 end, case catch mnesia:dirty_select(last_activity, [{{last_activity, {'_', Host}, '$1', '_'}, [{'>', '$1', TS}], [{trunc, {'/', {'-', TimeStamp, '$1'}, 86400}}]}]) of {'EXIT', _Reason} -> []; Vals -> Hist = histogram(Vals, Integral), if Hist == [] -> [?CT(<<"No Data">>)]; true -> Left = if Days == infinity -> 0; true -> Days - length(Hist) end, Tail = if Integral -> lists:duplicate(Left, lists:last(Hist)); true -> lists:duplicate(Left, 0) end, Max = lists:max(Hist), [?XAE(<<"ol">>, [{<<"id">>, <<"lastactivity">>}, {<<"start">>, <<"0">>}], [?XAE(<<"li">>, [{<<"style">>, <<"width:", (integer_to_binary(trunc(90 * V / Max)))/binary, "%;">>}], [{xmlcdata, pretty_string_int(V)}]) || V <- Hist ++ Tail])] end end. histogram(Values, Integral) -> histogram(lists:sort(Values), Integral, 0, 0, []). histogram([H | T], Integral, Current, Count, Hist) when Current == H -> histogram(T, Integral, Current, Count + 1, Hist); histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H -> if Integral -> histogram(Values, Integral, Current + 1, Count, [Count | Hist]); true -> histogram(Values, Integral, Current + 1, 0, [Count | Hist]) end; histogram([], _Integral, _Current, Count, Hist) -> if Count > 0 -> lists:reverse([Count | Hist]); true -> lists:reverse(Hist) end. %%%================================== %%%% get_nodes get_nodes(Lang) -> RunningNodes = ejabberd_cluster:get_nodes(), StoppedNodes = ejabberd_cluster:get_known_nodes() -- RunningNodes, FRN = if RunningNodes == [] -> ?CT(<<"None">>); true -> ?XE(<<"ul">>, (lists:map(fun (N) -> S = iolist_to_binary(atom_to_list(N)), ?LI([?AC(<<"../node/", S/binary, "/">>, S)]) end, lists:sort(RunningNodes)))) end, FSN = if StoppedNodes == [] -> ?CT(<<"None">>); true -> ?XE(<<"ul">>, (lists:map(fun (N) -> S = iolist_to_binary(atom_to_list(N)), ?LI([?C(S)]) end, lists:sort(StoppedNodes)))) end, [?XCT(<<"h1">>, <<"Nodes">>), ?XCT(<<"h3">>, <<"Running Nodes">>), FRN, ?XCT(<<"h3">>, <<"Stopped Nodes">>), FSN]. search_running_node(SNode) -> RunningNodes = ejabberd_cluster:get_nodes(), search_running_node(SNode, RunningNodes). search_running_node(_, []) -> false; search_running_node(SNode, [Node | Nodes]) -> case iolist_to_binary(atom_to_list(Node)) of SNode -> Node; _ -> search_running_node(SNode, Nodes) end. get_node(global, Node, [], Query, Lang) -> Res = node_parse_query(Node, Query), Base = get_base_path(global, Node), MenuItems2 = make_menu_items(global, Node, Base, Lang), [?XC(<<"h1">>, (str:format(?T(<<"Node ~p">>), [Node])))] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XE(<<"ul">>, ([?LI([?ACT(<>, <<"Database">>)]), ?LI([?ACT(<>, <<"Backup">>)]), ?LI([?ACT(<>, <<"Listened Ports">>)]), ?LI([?ACT(<>, <<"Statistics">>)]), ?LI([?ACT(<>, <<"Update">>)])] ++ MenuItems2)), ?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?INPUTT(<<"submit">>, <<"restart">>, <<"Restart">>), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"stop">>, <<"Stop">>)])]; get_node(Host, Node, [], _Query, Lang) -> Base = get_base_path(Host, Node), MenuItems2 = make_menu_items(Host, Node, Base, Lang), [?XC(<<"h1">>, (str:format(?T(<<"Node ~p">>), [Node]))), ?XE(<<"ul">>, ([?LI([?ACT(<>, <<"Modules">>)])] ++ MenuItems2))]; get_node(global, Node, [<<"db">>], Query, Lang) -> case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of {badrpc, _Reason} -> [?XCT(<<"h1">>, <<"RPC Call Error">>)]; Tables -> ResS = case node_db_parse_query(Node, Tables, Query) of nothing -> []; ok -> [?XREST(<<"Submitted">>)] end, STables = lists:sort(Tables), Rows = lists:map(fun (Table) -> STable = iolist_to_binary(atom_to_list(Table)), TInfo = case ejabberd_cluster:call(Node, mnesia, table_info, [Table, all]) of {badrpc, _} -> []; I -> I end, {Type, Size, Memory} = case {lists:keysearch(storage_type, 1, TInfo), lists:keysearch(size, 1, TInfo), lists:keysearch(memory, 1, TInfo)} of {{value, {storage_type, T}}, {value, {size, S}}, {value, {memory, M}}} -> {T, S, M}; _ -> {unknown, 0, 0} end, ?XE(<<"tr">>, [?XC(<<"td">>, STable), ?XE(<<"td">>, [db_storage_select(STable, Type, Lang)]), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(Size))), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(Memory)))]) end, STables), [?XC(<<"h1">>, (str:format(?T(<<"Database Tables at ~p">>), [Node])) )] ++ ResS ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XAE(<<"table">>, [], [?XE(<<"thead">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Name">>), ?XCT(<<"td">>, <<"Storage Type">>), ?XCT(<<"td">>, <<"Elements">>), ?XCT(<<"td">>, <<"Memory">>)])]), ?XE(<<"tbody">>, (Rows ++ [?XE(<<"tr">>, [?XAE(<<"td">>, [{<<"colspan">>, <<"4">>}, {<<"class">>, <<"alignright">>}], [?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])])]))])])] end; get_node(global, Node, [<<"backup">>], Query, Lang) -> HomeDirRaw = case {os:getenv("HOME"), os:type()} of {EnvHome, _} when is_list(EnvHome) -> list_to_binary(EnvHome); {false, {win32, _Osname}} -> <<"C:/">>; {false, _} -> <<"/tmp/">> end, HomeDir = filename:nativename(HomeDirRaw), ResS = case node_backup_parse_query(Node, Query) of nothing -> []; ok -> [?XREST(<<"Submitted">>)]; {error, Error} -> [?XRES(<<(?T(<<"Error">>))/binary, ": ", ((str:format("~p", [Error])))/binary>>)] end, [?XC(<<"h1">>, (str:format(?T(<<"Backup of ~p">>), [Node])))] ++ ResS ++ [?XCT(<<"p">>, <<"Please note that these options will " "only backup the builtin Mnesia database. " "If you are using the ODBC module, you " "also need to backup your SQL database " "separately.">>), ?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Store binary backup:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"storepath">>, (filename:join(HomeDir, "ejabberd.backup")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"store">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Restore binary backup immediately:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"restorepath">>, (filename:join(HomeDir, "ejabberd.backup")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"restore">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Restore binary backup after next ejabberd " "restart (requires less memory):">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"fallbackpath">>, (filename:join(HomeDir, "ejabberd.backup")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"fallback">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Store plain text backup:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"dumppath">>, (filename:join(HomeDir, "ejabberd.dump")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"dump">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Restore plain text backup immediately:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"loadpath">>, (filename:join(HomeDir, "ejabberd.dump")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"load">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Import users data from a PIEFXIS file " "(XEP-0227):">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"import_piefxis_filepath">>, (filename:join(HomeDir, "users.xml")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"import_piefxis_file">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Export data of all users in the server " "to PIEFXIS files (XEP-0227):">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"export_piefxis_dirpath">>, HomeDir)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"export_piefxis_dir">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XE(<<"td">>, [?CT(<<"Export data of users in a host to PIEFXIS " "files (XEP-0227):">>), ?C(<<" ">>), ?INPUT(<<"text">>, <<"export_piefxis_host_dirhost">>, (?MYNAME))]), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"export_piefxis_host_dirpath">>, HomeDir)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"export_piefxis_host_dir">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XE(<<"td">>, [?CT(<<"Export all tables as SQL queries " "to a file:">>), ?C(<<" ">>), ?INPUT(<<"text">>, <<"export_sql_filehost">>, (?MYNAME))]), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"export_sql_filepath">>, (filename:join(HomeDir, "db.sql")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"export_sql_file">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Import user data from jabberd14 spool " "file:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"import_filepath">>, (filename:join(HomeDir, "user1.xml")))]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"import_file">>, <<"OK">>)])]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Import users data from jabberd14 spool " "directory:">>), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"import_dirpath">>, <<"/var/spool/jabber/">>)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"import_dir">>, <<"OK">>)])])])])])]; get_node(global, Node, [<<"ports">>], Query, Lang) -> Ports = ejabberd_cluster:call(Node, ejabberd_config, get_local_option, [listen, {ejabberd_listener, validate_cfg}, []]), Res = case catch node_ports_parse_query(Node, Ports, Query) of submitted -> ok; {'EXIT', _Reason} -> error; {is_added, ok} -> ok; {is_added, {error, Reason}} -> {error, (str:format("~p", [Reason]))}; _ -> nothing end, NewPorts = lists:sort(ejabberd_cluster:call(Node, ejabberd_config, get_local_option, [listen, {ejabberd_listener, validate_cfg}, []])), H1String = <<(?T(<<"Listened Ports at ">>))/binary, (iolist_to_binary(atom_to_list(Node)))/binary>>, (?H1GL(H1String, <<"listeningports">>, <<"Listening Ports">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; {error, ReasonT} -> [?XRES(<<(?T(<<"Error">>))/binary, ": ", ReasonT/binary>>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [node_ports_to_xhtml(NewPorts, Lang)])]; get_node(Host, Node, [<<"modules">>], Query, Lang) when is_binary(Host) -> Modules = ejabberd_cluster:call(Node, gen_mod, loaded_modules_with_opts, [Host]), Res = case catch node_modules_parse_query(Host, Node, Modules, Query) of submitted -> ok; {'EXIT', Reason} -> ?INFO_MSG("~p~n", [Reason]), error; _ -> nothing end, NewModules = lists:sort(ejabberd_cluster:call(Node, gen_mod, loaded_modules_with_opts, [Host])), H1String = (str:format(?T(<<"Modules at ~p">>), [Node])), (?H1GL(H1String, <<"modulesoverview">>, <<"Modules Overview">>)) ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [node_modules_to_xhtml(NewModules, Lang)])]; get_node(global, Node, [<<"stats">>], _Query, Lang) -> UpTime = ejabberd_cluster:call(Node, erlang, statistics, [wall_clock]), UpTimeS = (str:format("~.3f", [element(1, UpTime) / 1000])), CPUTime = ejabberd_cluster:call(Node, erlang, statistics, [runtime]), CPUTimeS = (str:format("~.3f", [element(1, CPUTime) / 1000])), OnlineUsers = ejabberd_sm:connected_users_number(), TransactionsCommitted = ejabberd_cluster:call(Node, mnesia, system_info, [transaction_commits]), TransactionsAborted = ejabberd_cluster:call(Node, mnesia, system_info, [transaction_failures]), TransactionsRestarted = ejabberd_cluster:call(Node, mnesia, system_info, [transaction_restarts]), TransactionsLogged = ejabberd_cluster:call(Node, mnesia, system_info, [transaction_log_writes]), [?XC(<<"h1">>, (str:format(?T(<<"Statistics of ~p">>), [Node]))), ?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Uptime:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], UpTimeS)]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"CPU Time:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], CPUTimeS)]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Online Users:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(OnlineUsers)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions Committed:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(TransactionsCommitted)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions Aborted:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(TransactionsAborted)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions Restarted:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(TransactionsRestarted)))]), ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions Logged:">>), ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], (pretty_string_int(TransactionsLogged)))])])])]; get_node(global, Node, [<<"update">>], Query, Lang) -> ejabberd_cluster:call(Node, code, purge, [ejabberd_update]), Res = node_update_parse_query(Node, Query), ejabberd_cluster:call(Node, code, load_file, [ejabberd_update]), {ok, _Dir, UpdatedBeams, Script, LowLevelScript, Check} = ejabberd_cluster:call(Node, ejabberd_update, update_info, []), Mods = case UpdatedBeams of [] -> ?CT(<<"None">>); _ -> BeamsLis = lists:map(fun (Beam) -> BeamString = iolist_to_binary(atom_to_list(Beam)), ?LI([?INPUT(<<"checkbox">>, <<"selected">>, BeamString), ?C(BeamString)]) end, UpdatedBeams), SelectButtons = [?BR, ?INPUTATTRS(<<"button">>, <<"selectall">>, <<"Select All">>, [{<<"onClick">>, <<"selectAll()">>}]), ?C(<<" ">>), ?INPUTATTRS(<<"button">>, <<"unselectall">>, <<"Unselect All">>, [{<<"onClick">>, <<"unSelectAll()">>}])], ?XAE(<<"ul">>, [{<<"class">>, <<"nolistyle">>}], (BeamsLis ++ SelectButtons)) end, FmtScript = (?XC(<<"pre">>, (str:format("~p", [Script])))), FmtLowLevelScript = (?XC(<<"pre">>, (str:format("~p", [LowLevelScript])))), [?XC(<<"h1">>, (str:format(?T(<<"Update ~p">>), [Node])))] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; {error, ErrorText} -> [?XREST(<<"Error: ", ErrorText/binary>>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?XCT(<<"h2">>, <<"Update plan">>), ?XCT(<<"h3">>, <<"Modified modules">>), Mods, ?XCT(<<"h3">>, <<"Update script">>), FmtScript, ?XCT(<<"h3">>, <<"Low level update script">>), FmtLowLevelScript, ?XCT(<<"h3">>, <<"Script check">>), ?XC(<<"pre">>, (misc:atom_to_binary(Check))), ?BR, ?INPUTT(<<"submit">>, <<"update">>, <<"Update">>)])]; get_node(Host, Node, NPath, Query, Lang) -> Res = case Host of global -> ejabberd_hooks:run_fold(webadmin_page_node, Host, [], [Node, NPath, Query, Lang]); _ -> ejabberd_hooks:run_fold(webadmin_page_hostnode, Host, [], [Host, Node, NPath, Query, Lang]) end, case Res of [] -> [?XC(<<"h1">>, <<"Not Found">>)]; _ -> Res end. %%%================================== %%%% node parse node_parse_query(Node, Query) -> case lists:keysearch(<<"restart">>, 1, Query) of {value, _} -> case ejabberd_cluster:call(Node, init, restart, []) of {badrpc, _Reason} -> error; _ -> ok end; _ -> case lists:keysearch(<<"stop">>, 1, Query) of {value, _} -> case ejabberd_cluster:call(Node, init, stop, []) of {badrpc, _Reason} -> error; _ -> ok end; _ -> nothing end end. db_storage_select(ID, Opt, Lang) -> ?XAE(<<"select">>, [{<<"name">>, <<"table", ID/binary>>}], (lists:map(fun ({O, Desc}) -> Sel = if O == Opt -> [{<<"selected">>, <<"selected">>}]; true -> [] end, ?XACT(<<"option">>, (Sel ++ [{<<"value">>, iolist_to_binary(atom_to_list(O))}]), Desc) end, [{ram_copies, <<"RAM copy">>}, {disc_copies, <<"RAM and disc copy">>}, {disc_only_copies, <<"Disc only copy">>}, {unknown, <<"Remote copy">>}, {delete_content, <<"Delete content">>}, {delete_table, <<"Delete table">>}]))). node_db_parse_query(_Node, _Tables, [{nokey, <<>>}]) -> nothing; node_db_parse_query(Node, Tables, Query) -> lists:foreach(fun (Table) -> STable = iolist_to_binary(atom_to_list(Table)), case lists:keysearch(<<"table", STable/binary>>, 1, Query) of {value, {_, SType}} -> Type = case SType of <<"unknown">> -> unknown; <<"ram_copies">> -> ram_copies; <<"disc_copies">> -> disc_copies; <<"disc_only_copies">> -> disc_only_copies; <<"delete_content">> -> delete_content; <<"delete_table">> -> delete_table; _ -> false end, if Type == false -> ok; Type == delete_content -> mnesia:clear_table(Table); Type == delete_table -> mnesia:delete_table(Table); Type == unknown -> mnesia:del_table_copy(Table, Node); true -> case mnesia:add_table_copy(Table, Node, Type) of {aborted, _} -> mnesia:change_table_copy_type(Table, Node, Type); _ -> ok end end; _ -> ok end end, Tables), ok. node_backup_parse_query(_Node, [{nokey, <<>>}]) -> nothing; node_backup_parse_query(Node, Query) -> lists:foldl(fun (Action, nothing) -> case lists:keysearch(Action, 1, Query) of {value, _} -> case lists:keysearch(<>, 1, Query) of {value, {_, Path}} -> Res = case Action of <<"store">> -> ejabberd_cluster:call(Node, mnesia, backup, [binary_to_list(Path)]); <<"restore">> -> ejabberd_cluster:call(Node, ejabberd_admin, restore, [Path]); <<"fallback">> -> ejabberd_cluster:call(Node, mnesia, install_fallback, [binary_to_list(Path)]); <<"dump">> -> ejabberd_cluster:call(Node, ejabberd_admin, dump_to_textfile, [Path]); <<"load">> -> ejabberd_cluster:call(Node, mnesia, load_textfile, [binary_to_list(Path)]); <<"import_piefxis_file">> -> ejabberd_cluster:call(Node, ejabberd_piefxis, import_file, [Path]); <<"export_piefxis_dir">> -> ejabberd_cluster:call(Node, ejabberd_piefxis, export_server, [Path]); <<"export_piefxis_host_dir">> -> {value, {_, Host}} = lists:keysearch(<>, 1, Query), ejabberd_cluster:call(Node, ejabberd_piefxis, export_host, [Path, Host]); <<"export_sql_file">> -> {value, {_, Host}} = lists:keysearch(<>, 1, Query), ejabberd_cluster:call(Node, ejd2sql, export, [Host, Path]); <<"import_file">> -> ejabberd_cluster:call(Node, ejabberd_admin, import_file, [Path]); <<"import_dir">> -> ejabberd_cluster:call(Node, ejabberd_admin, import_dir, [Path]) end, case Res of {error, Reason} -> {error, Reason}; {badrpc, Reason} -> {badrpc, Reason}; _ -> ok end; OtherError -> {error, OtherError} end; _ -> nothing end; (_Action, Res) -> Res end, nothing, [<<"store">>, <<"restore">>, <<"fallback">>, <<"dump">>, <<"load">>, <<"import_file">>, <<"import_dir">>, <<"import_piefxis_file">>, <<"export_piefxis_dir">>, <<"export_piefxis_host_dir">>, <<"export_sql_file">>]). node_ports_to_xhtml(Ports, Lang) -> ?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}], [?XE(<<"thead">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Port">>), ?XCT(<<"td">>, <<"IP">>), ?XCT(<<"td">>, <<"Protocol">>), ?XCT(<<"td">>, <<"Module">>), ?XCT(<<"td">>, <<"Options">>)])]), ?XE(<<"tbody">>, (lists:map(fun ({PortIP, Module, Opts} = _E) -> {_Port, SPort, _TIP, SIP, SSPort, NetProt, OptsClean} = get_port_data(PortIP, Opts), SModule = iolist_to_binary(atom_to_list(Module)), {NumLines, SOptsClean} = term_to_paragraph(OptsClean, 40), ?XE(<<"tr">>, [?XAE(<<"td">>, [{<<"size">>, <<"6">>}], [?C(SPort)]), ?XAE(<<"td">>, [{<<"size">>, <<"15">>}], [?C(SIP)]), ?XAE(<<"td">>, [{<<"size">>, <<"4">>}], [?C((iolist_to_binary(atom_to_list(NetProt))))]), ?XE(<<"td">>, [?INPUTS(<<"text">>, <<"module", SSPort/binary>>, SModule, <<"15">>)]), ?XAE(<<"td">>, direction(ltr), [?TEXTAREA(<<"opts", SSPort/binary>>, (integer_to_binary(NumLines)), <<"35">>, SOptsClean)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"add", SSPort/binary>>, <<"Restart">>)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"delete", SSPort/binary>>, <<"Stop">>)])]) end, Ports) ++ [?XE(<<"tr">>, [?XE(<<"td">>, [?INPUTS(<<"text">>, <<"portnew">>, <<"">>, <<"6">>)]), ?XE(<<"td">>, [?INPUTS(<<"text">>, <<"ipnew">>, <<"0.0.0.0">>, <<"15">>)]), ?XE(<<"td">>, [make_netprot_html(<<"tcp">>)]), ?XE(<<"td">>, [?INPUTS(<<"text">>, <<"modulenew">>, <<"">>, <<"15">>)]), ?XAE(<<"td">>, direction(ltr), [?TEXTAREA(<<"optsnew">>, <<"2">>, <<"35">>, <<"[]">>)]), ?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}], [?INPUTT(<<"submit">>, <<"addnew">>, <<"Start">>)])])]))]). make_netprot_html(NetProt) -> ?XAE(<<"select">>, [{<<"name">>, <<"netprotnew">>}], (lists:map(fun (O) -> Sel = if O == NetProt -> [{<<"selected">>, <<"selected">>}]; true -> [] end, ?XAC(<<"option">>, (Sel ++ [{<<"value">>, O}]), O) end, [<<"tcp">>, <<"udp">>]))). get_port_data(PortIP, Opts) -> {Port, IPT, IPS, _IPV, NetProt, OptsClean} = ejabberd_listener:parse_listener_portip(PortIP, Opts), SPort = integer_to_binary(Port), SSPort = list_to_binary( lists:map(fun (N) -> io_lib:format("~.16b", [N]) end, binary_to_list( erlang:md5( [SPort, IPS, atom_to_list(NetProt)])))), {Port, SPort, IPT, IPS, SSPort, NetProt, OptsClean}. node_ports_parse_query(Node, Ports, Query) -> lists:foreach(fun ({PortIpNetp, Module1, Opts1}) -> {Port, _SPort, TIP, _SIP, SSPort, NetProt, _OptsClean} = get_port_data(PortIpNetp, Opts1), case lists:keysearch(<<"add", SSPort/binary>>, 1, Query) of {value, _} -> PortIpNetp2 = {Port, TIP, NetProt}, {{value, {_, SModule}}, {value, {_, SOpts}}} = {lists:keysearch(<<"module", SSPort/binary>>, 1, Query), lists:keysearch(<<"opts", SSPort/binary>>, 1, Query)}, Module = misc:binary_to_atom(SModule), {ok, Tokens, _} = erl_scan:string(binary_to_list(SOpts) ++ "."), {ok, Opts} = erl_parse:parse_term(Tokens), ejabberd_cluster:call(Node, ejabberd_listener, delete_listener, [PortIpNetp2, Module1]), R = ejabberd_cluster:call(Node, ejabberd_listener, add_listener, [PortIpNetp2, Module, Opts]), throw({is_added, R}); _ -> case lists:keysearch(<<"delete", SSPort/binary>>, 1, Query) of {value, _} -> ejabberd_cluster:call(Node, ejabberd_listener, delete_listener, [PortIpNetp, Module1]), throw(submitted); _ -> ok end end end, Ports), case lists:keysearch(<<"addnew">>, 1, Query) of {value, _} -> {{value, {_, SPort}}, {value, {_, STIP}}, {value, {_, SNetProt}}, {value, {_, SModule}}, {value, {_, SOpts}}} = {lists:keysearch(<<"portnew">>, 1, Query), lists:keysearch(<<"ipnew">>, 1, Query), lists:keysearch(<<"netprotnew">>, 1, Query), lists:keysearch(<<"modulenew">>, 1, Query), lists:keysearch(<<"optsnew">>, 1, Query)}, {ok, Toks, _} = erl_scan:string(binary_to_list(<>)), {ok, Port2} = erl_parse:parse_term(Toks), {ok, ToksIP, _} = erl_scan:string(binary_to_list(<>)), STIP2 = case erl_parse:parse_term(ToksIP) of {ok, IPTParsed} -> IPTParsed; {error, _} -> STIP end, Module = misc:binary_to_atom(SModule), NetProt2 = misc:binary_to_atom(SNetProt), {ok, Tokens, _} = erl_scan:string(binary_to_list(<>)), {ok, Opts} = erl_parse:parse_term(Tokens), {Port2, _SPort, IP2, _SIP, _SSPort, NetProt2, OptsClean} = get_port_data({Port2, STIP2, NetProt2}, Opts), R = ejabberd_cluster:call(Node, ejabberd_listener, add_listener, [{Port2, IP2, NetProt2}, Module, OptsClean]), throw({is_added, R}); _ -> ok end. node_modules_to_xhtml(Modules, Lang) -> ?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}], [?XE(<<"thead">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Module">>), ?XCT(<<"td">>, <<"Options">>)])]), ?XE(<<"tbody">>, (lists:map(fun ({Module, Opts} = _E) -> SModule = iolist_to_binary(atom_to_list(Module)), {NumLines, SOpts} = term_to_paragraph(Opts, 40), ?XE(<<"tr">>, [?XC(<<"td">>, SModule), ?XAE(<<"td">>, direction(ltr), [?TEXTAREA(<<"opts", SModule/binary>>, (integer_to_binary(NumLines)), <<"40">>, SOpts)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"restart", SModule/binary>>, <<"Restart">>)]), ?XE(<<"td">>, [?INPUTT(<<"submit">>, <<"stop", SModule/binary>>, <<"Stop">>)])]) end, Modules) ++ [?XE(<<"tr">>, [?XE(<<"td">>, [?INPUT(<<"text">>, <<"modulenew">>, <<"">>)]), ?XAE(<<"td">>, direction(ltr), [?TEXTAREA(<<"optsnew">>, <<"2">>, <<"40">>, <<"[]">>)]), ?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}], [?INPUTT(<<"submit">>, <<"start">>, <<"Start">>)])])]))]). node_modules_parse_query(Host, Node, Modules, Query) -> lists:foreach(fun ({Module, _Opts1}) -> SModule = iolist_to_binary(atom_to_list(Module)), case lists:keysearch(<<"restart", SModule/binary>>, 1, Query) of {value, _} -> {value, {_, SOpts}} = lists:keysearch(<<"opts", SModule/binary>>, 1, Query), {ok, Tokens, _} = erl_scan:string(binary_to_list(<>)), {ok, Opts} = erl_parse:parse_term(Tokens), ejabberd_cluster:call(Node, gen_mod, stop_module, [Host, Module]), ejabberd_cluster:call(Node, gen_mod, start_module, [Host, Module, Opts]), throw(submitted); _ -> case lists:keysearch(<<"stop", SModule/binary>>, 1, Query) of {value, _} -> ejabberd_cluster:call(Node, gen_mod, stop_module, [Host, Module]), throw(submitted); _ -> ok end end end, Modules), case lists:keysearch(<<"start">>, 1, Query) of {value, _} -> {{value, {_, SModule}}, {value, {_, SOpts}}} = {lists:keysearch(<<"modulenew">>, 1, Query), lists:keysearch(<<"optsnew">>, 1, Query)}, Module = misc:binary_to_atom(SModule), {ok, Tokens, _} = erl_scan:string(binary_to_list(<>)), {ok, Opts} = erl_parse:parse_term(Tokens), ejabberd_cluster:call(Node, gen_mod, start_module, [Host, Module, Opts]), throw(submitted); _ -> ok end. node_update_parse_query(Node, Query) -> case lists:keysearch(<<"update">>, 1, Query) of {value, _} -> ModulesToUpdateStrings = proplists:get_all_values(<<"selected">>, Query), ModulesToUpdate = [misc:binary_to_atom(M) || M <- ModulesToUpdateStrings], case ejabberd_cluster:call(Node, ejabberd_update, update, [ModulesToUpdate]) of {ok, _} -> ok; {error, Error} -> ?ERROR_MSG("~p~n", [Error]), {error, (str:format("~p", [Error]))}; {badrpc, Error} -> ?ERROR_MSG("Bad RPC: ~p~n", [Error]), {error, <<"Bad RPC: ", ((str:format("~p", [Error])))/binary>>} end; _ -> nothing end. pretty_print_xml(El) -> list_to_binary(pretty_print_xml(El, <<"">>)). pretty_print_xml({xmlcdata, CData}, Prefix) -> IsBlankCData = lists:all( fun($\f) -> true; ($\r) -> true; ($\n) -> true; ($\t) -> true; ($\v) -> true; ($ ) -> true; (_) -> false end, binary_to_list(CData)), if IsBlankCData -> []; true -> [Prefix, CData, $\n] end; pretty_print_xml(#xmlel{name = Name, attrs = Attrs, children = Els}, Prefix) -> [Prefix, $<, Name, case Attrs of [] -> []; [{Attr, Val} | RestAttrs] -> AttrPrefix = [Prefix, str:copies(<<" ">>, byte_size(Name) + 2)], [$\s, Attr, $=, $', fxml:crypt(Val) | [$', lists:map(fun ({Attr1, Val1}) -> [$\n, AttrPrefix, Attr1, $=, $', fxml:crypt(Val1), $'] end, RestAttrs)]] end, if Els == [] -> <<"/>\n">>; true -> OnlyCData = lists:all(fun ({xmlcdata, _}) -> true; (#xmlel{}) -> false end, Els), if OnlyCData -> [$>, fxml:get_cdata(Els), $<, $/, Name, $>, $\n]; true -> [$>, $\n, lists:map(fun (E) -> pretty_print_xml(E, [Prefix, <<" ">>]) end, Els), Prefix, $<, $/, Name, $>, $\n] end end]. element_to_list(X) when is_atom(X) -> iolist_to_binary(atom_to_list(X)); element_to_list(X) when is_integer(X) -> integer_to_binary(X). list_to_element(Bin) -> {ok, Tokens, _} = erl_scan:string(binary_to_list(Bin)), [{_, _, Element}] = Tokens, Element. url_func({user_diapason, From, To}) -> <<(integer_to_binary(From))/binary, "-", (integer_to_binary(To))/binary, "/">>; url_func({users_queue, Prefix, User, _Server}) -> <>; url_func({user, Prefix, User, _Server}) -> <>. last_modified() -> {<<"Last-Modified">>, <<"Mon, 25 Feb 2008 13:23:30 GMT">>}. cache_control_public() -> {<<"Cache-Control">>, <<"public">>}. %% Transform 1234567890 into "1,234,567,890" pretty_string_int(Integer) when is_integer(Integer) -> pretty_string_int(integer_to_binary(Integer)); pretty_string_int(String) when is_binary(String) -> {_, Result} = lists:foldl(fun (NewNumber, {3, Result}) -> {1, <>}; (NewNumber, {CountAcc, Result}) -> {CountAcc + 1, <>} end, {0, <<"">>}, lists:reverse(binary_to_list(String))), Result. %%%================================== %%%% navigation menu %% @spec (Host, Node, Lang, JID::jid()) -> [LI] make_navigation(Host, Node, Lang, JID) -> Menu = make_navigation_menu(Host, Node, Lang, JID), make_menu_items(Lang, Menu). %% @spec (Host, Node, Lang, JID::jid()) -> Menu %% where Host = global | string() %% Node = cluster | string() %% Lang = string() %% Menu = {URL, Title} | {URL, Title, [Menu]} %% URL = string() %% Title = string() make_navigation_menu(Host, Node, Lang, JID) -> HostNodeMenu = make_host_node_menu(Host, Node, Lang, JID), HostMenu = make_host_menu(Host, HostNodeMenu, Lang, JID), NodeMenu = make_node_menu(Host, Node, Lang), make_server_menu(HostMenu, NodeMenu, Lang, JID). %% @spec (Host, Node, Base, Lang) -> [LI] make_menu_items(global, cluster, Base, Lang) -> HookItems = get_menu_items_hook(server, Lang), make_menu_items(Lang, {Base, <<"">>, HookItems}); make_menu_items(global, Node, Base, Lang) -> HookItems = get_menu_items_hook({node, Node}, Lang), make_menu_items(Lang, {Base, <<"">>, HookItems}); make_menu_items(Host, cluster, Base, Lang) -> HookItems = get_menu_items_hook({host, Host}, Lang), make_menu_items(Lang, {Base, <<"">>, HookItems}); make_menu_items(Host, Node, Base, Lang) -> HookItems = get_menu_items_hook({hostnode, Host, Node}, Lang), make_menu_items(Lang, {Base, <<"">>, HookItems}). make_host_node_menu(global, _, _Lang, _JID) -> {<<"">>, <<"">>, []}; make_host_node_menu(_, cluster, _Lang, _JID) -> {<<"">>, <<"">>, []}; make_host_node_menu(Host, Node, Lang, JID) -> HostNodeBase = get_base_path(Host, Node), HostNodeFixed = [{<<"modules/">>, <<"Modules">>}] ++ get_menu_items_hook({hostnode, Host, Node}, Lang), HostNodeBasePath = url_to_path(HostNodeBase), HostNodeFixed2 = [Tuple || Tuple <- HostNodeFixed, is_allowed_path(HostNodeBasePath, Tuple, JID)], {HostNodeBase, iolist_to_binary(atom_to_list(Node)), HostNodeFixed2}. make_host_menu(global, _HostNodeMenu, _Lang, _JID) -> {<<"">>, <<"">>, []}; make_host_menu(Host, HostNodeMenu, Lang, JID) -> HostBase = get_base_path(Host, cluster), HostFixed = [{<<"acls">>, <<"Access Control Lists">>}, {<<"access">>, <<"Access Rules">>}, {<<"users">>, <<"Users">>}, {<<"online-users">>, <<"Online Users">>}] ++ get_lastactivity_menuitem_list(Host) ++ [{<<"nodes">>, <<"Nodes">>, HostNodeMenu}, {<<"stats">>, <<"Statistics">>}] ++ get_menu_items_hook({host, Host}, Lang), HostBasePath = url_to_path(HostBase), HostFixed2 = [Tuple || Tuple <- HostFixed, is_allowed_path(HostBasePath, Tuple, JID)], {HostBase, Host, HostFixed2}. make_node_menu(_Host, cluster, _Lang) -> {<<"">>, <<"">>, []}; make_node_menu(global, Node, Lang) -> NodeBase = get_base_path(global, Node), NodeFixed = [{<<"db/">>, <<"Database">>}, {<<"backup/">>, <<"Backup">>}, {<<"ports/">>, <<"Listened Ports">>}, {<<"stats/">>, <<"Statistics">>}, {<<"update/">>, <<"Update">>}] ++ get_menu_items_hook({node, Node}, Lang), {NodeBase, iolist_to_binary(atom_to_list(Node)), NodeFixed}; make_node_menu(_Host, _Node, _Lang) -> {<<"">>, <<"">>, []}. make_server_menu(HostMenu, NodeMenu, Lang, JID) -> Base = get_base_path(global, cluster), Fixed = [{<<"acls">>, <<"Access Control Lists">>}, {<<"access">>, <<"Access Rules">>}, {<<"vhosts">>, <<"Virtual Hosts">>, HostMenu}, {<<"nodes">>, <<"Nodes">>, NodeMenu}, {<<"stats">>, <<"Statistics">>}] ++ get_menu_items_hook(server, Lang), BasePath = url_to_path(Base), Fixed2 = [Tuple || Tuple <- Fixed, is_allowed_path(BasePath, Tuple, JID)], {Base, <<"">>, Fixed2}. get_menu_items_hook({hostnode, Host, Node}, Lang) -> ejabberd_hooks:run_fold(webadmin_menu_hostnode, Host, [], [Host, Node, Lang]); get_menu_items_hook({host, Host}, Lang) -> ejabberd_hooks:run_fold(webadmin_menu_host, Host, [], [Host, Lang]); get_menu_items_hook({node, Node}, Lang) -> ejabberd_hooks:run_fold(webadmin_menu_node, [], [Node, Lang]); get_menu_items_hook(server, Lang) -> ejabberd_hooks:run_fold(webadmin_menu_main, [], [Lang]). %% @spec (Lang::string(), Menu) -> [LI] %% where Menu = {MURI::string(), MName::string(), Items::[Item]} %% Item = {IURI::string(), IName::string()} | {IURI::string(), IName::string(), Menu} make_menu_items(Lang, Menu) -> lists:reverse(make_menu_items2(Lang, 1, Menu)). make_menu_items2(Lang, Deep, {MURI, MName, _} = Menu) -> Res = case MName of <<"">> -> []; _ -> [make_menu_item(header, Deep, MURI, MName, Lang)] end, make_menu_items2(Lang, Deep, Menu, Res). make_menu_items2(_, _Deep, {_, _, []}, Res) -> Res; make_menu_items2(Lang, Deep, {MURI, MName, [Item | Items]}, Res) -> Res2 = case Item of {IURI, IName} -> [make_menu_item(item, Deep, <>, IName, Lang) | Res]; {IURI, IName, SubMenu} -> ResTemp = [make_menu_item(item, Deep, <>, IName, Lang) | Res], ResSubMenu = make_menu_items2(Lang, Deep + 1, SubMenu), ResSubMenu ++ ResTemp end, make_menu_items2(Lang, Deep, {MURI, MName, Items}, Res2). make_menu_item(header, 1, URI, Name, _Lang) -> ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navhead">>}], [?AC(URI, Name)])]); make_menu_item(header, 2, URI, Name, _Lang) -> ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navheadsub">>}], [?AC(URI, Name)])]); make_menu_item(header, 3, URI, Name, _Lang) -> ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navheadsubsub">>}], [?AC(URI, Name)])]); make_menu_item(item, 1, URI, Name, Lang) -> ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitem">>}], [?ACT(URI, Name)])]); make_menu_item(item, 2, URI, Name, Lang) -> ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitemsub">>}], [?ACT(URI, Name)])]); make_menu_item(item, 3, URI, Name, Lang) -> ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitemsubsub">>}], [?ACT(URI, Name)])]). %%%================================== -spec opt_type(access_readonly) -> fun((any()) -> any()); (atom()) -> [atom()]. opt_type(access_readonly) -> fun acl:access_rules_validator/1; opt_type(_) -> [access_readonly]. %%% vim: set foldmethod=marker foldmarker=%%%%,%%%=: ejabberd-18.01/src/mod_proxy65_stream.erl0000644000232200023220000002301313225664356020703 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_proxy65_stream.erl %%% Author : Evgeniy Khramtsov %%% Purpose : Bytestream process. %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_proxy65_stream). -author('xram@jabber.ru'). -behaviour(p1_fsm). %% gen_fsm callbacks. -export([init/1, handle_event/3, handle_sync_event/4, code_change/4, handle_info/3, terminate/3]). %% gen_fsm states. -export([wait_for_init/2, wait_for_auth/2, wait_for_request/2, wait_for_activation/2, stream_established/2]). -export([start/2, stop/1, start_link/3, activate/2, relay/3, socket_type/0, listen_opt_type/1]). -include("mod_proxy65.hrl"). -include("ejabberd.hrl"). -include("logger.hrl"). -define(WAIT_TIMEOUT, 60000). -record(state, {socket :: inet:socket(), timer = make_ref() :: reference(), sha1 = <<"">> :: binary(), host = <<"">> :: binary(), auth_type = anonymous :: plain | anonymous, shaper = none :: shaper:shaper()}). %% Unused callbacks handle_event(_Event, StateName, StateData) -> {next_state, StateName, StateData}. code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}. %%------------------------------- start({gen_tcp, Socket}, Opts1) -> {[{server_host, Host}], Opts} = lists:partition( fun({server_host, _}) -> true; (_) -> false end, Opts1), Supervisor = gen_mod:get_module_proc(Host, ejabberd_mod_proxy65_sup), supervisor:start_child(Supervisor, [Socket, Host, Opts]). start_link(Socket, Host, Opts) -> p1_fsm:start_link(?MODULE, [Socket, Host, Opts], []). init([Socket, Host, Opts]) -> process_flag(trap_exit, true), AuthType = gen_mod:get_opt(auth_type, Opts, anonymous), Shaper = gen_mod:get_opt(shaper, Opts, none), RecvBuf = gen_mod:get_opt(recbuf, Opts, 8192), SendBuf = gen_mod:get_opt(sndbuf, Opts, 8192), TRef = erlang:send_after(?WAIT_TIMEOUT, self(), stop), inet:setopts(Socket, [{active, true}, {recbuf, RecvBuf}, {sndbuf, SendBuf}]), {ok, wait_for_init, #state{host = Host, auth_type = AuthType, socket = Socket, shaper = Shaper, timer = TRef}}. terminate(_Reason, StateName, #state{sha1 = SHA1}) -> Mod = gen_mod:ram_db_mod(global, mod_proxy65), Mod:unregister_stream(SHA1), if StateName == stream_established -> ?INFO_MSG("(~w) Bytestream terminated", [self()]); true -> ok end. %%%------------------------------ %%% API. %%%------------------------------ socket_type() -> raw. stop(StreamPid) -> StreamPid ! stop. activate({P1, J1}, {P2, J2}) -> case catch {p1_fsm:sync_send_all_state_event(P1, get_socket), p1_fsm:sync_send_all_state_event(P2, get_socket)} of {S1, S2} when is_port(S1), is_port(S2) -> P1 ! {activate, P2, S2, J1, J2}, P2 ! {activate, P1, S1, J1, J2}, JID1 = jid:encode(J1), JID2 = jid:encode(J2), ?INFO_MSG("(~w:~w) Activated bytestream for ~s " "-> ~s", [P1, P2, JID1, JID2]), ok; _ -> error end. %%%----------------------- %%% States %%%----------------------- wait_for_init(Packet, #state{socket = Socket, auth_type = AuthType} = StateData) -> case mod_proxy65_lib:unpack_init_message(Packet) of {ok, AuthMethods} -> Method = select_auth_method(AuthType, AuthMethods), gen_tcp:send(Socket, mod_proxy65_lib:make_init_reply(Method)), case Method of ?AUTH_ANONYMOUS -> {next_state, wait_for_request, StateData}; ?AUTH_PLAIN -> {next_state, wait_for_auth, StateData}; ?AUTH_NO_METHODS -> {stop, normal, StateData} end; error -> {stop, normal, StateData} end. wait_for_auth(Packet, #state{socket = Socket, host = Host} = StateData) -> case mod_proxy65_lib:unpack_auth_request(Packet) of {User, Pass} -> Result = ejabberd_auth:check_password(User, <<"">>, Host, Pass), gen_tcp:send(Socket, mod_proxy65_lib:make_auth_reply(Result)), case Result of true -> {next_state, wait_for_request, StateData}; false -> {stop, normal, StateData} end; _ -> {stop, normal, StateData} end. wait_for_request(Packet, #state{socket = Socket} = StateData) -> Request = mod_proxy65_lib:unpack_request(Packet), case Request of #s5_request{sha1 = SHA1, cmd = connect} -> Mod = gen_mod:ram_db_mod(global, mod_proxy65), case Mod:register_stream(SHA1, self()) of ok -> inet:setopts(Socket, [{active, false}]), gen_tcp:send(Socket, mod_proxy65_lib:make_reply(Request)), {next_state, wait_for_activation, StateData#state{sha1 = SHA1}}; _ -> Err = mod_proxy65_lib:make_error_reply(Request), gen_tcp:send(Socket, Err), {stop, normal, StateData} end; #s5_request{cmd = udp} -> Err = mod_proxy65_lib:make_error_reply(Request, ?ERR_COMMAND_NOT_SUPPORTED), gen_tcp:send(Socket, Err), {stop, normal, StateData}; _ -> {stop, normal, StateData} end. wait_for_activation(_Data, StateData) -> {next_state, wait_for_activation, StateData}. stream_established(_Data, StateData) -> {next_state, stream_established, StateData}. %%%----------------------- %%% Callbacks processing %%%----------------------- %% SOCKS5 packets. handle_info({tcp, _S, Data}, StateName, StateData) when StateName /= wait_for_activation -> erlang:cancel_timer(StateData#state.timer), TRef = erlang:send_after(?WAIT_TIMEOUT, self(), stop), p1_fsm:send_event(self(), Data), {next_state, StateName, StateData#state{timer = TRef}}; %% Activation message. handle_info({activate, PeerPid, PeerSocket, IJid, TJid}, wait_for_activation, StateData) -> erlang:monitor(process, PeerPid), erlang:cancel_timer(StateData#state.timer), MySocket = StateData#state.socket, Shaper = StateData#state.shaper, Host = StateData#state.host, MaxRate = find_maxrate(Shaper, IJid, TJid, Host), spawn_link(?MODULE, relay, [MySocket, PeerSocket, MaxRate]), {next_state, stream_established, StateData}; %% Socket closed handle_info({tcp_closed, _Socket}, _StateName, StateData) -> {stop, normal, StateData}; handle_info({tcp_error, _Socket, _Reason}, _StateName, StateData) -> {stop, normal, StateData}; %% Got stop message. handle_info(stop, _StateName, StateData) -> {stop, normal, StateData}; %% Either linked process or peer process died. handle_info({'EXIT', _, _}, _StateName, StateData) -> {stop, normal, StateData}; handle_info({'DOWN', _, _, _, _}, _StateName, StateData) -> {stop, normal, StateData}; %% Packets of no interest handle_info(_Info, StateName, StateData) -> {next_state, StateName, StateData}. %% Socket request. handle_sync_event(get_socket, _From, wait_for_activation, StateData) -> Socket = StateData#state.socket, {reply, Socket, wait_for_activation, StateData}; handle_sync_event(_Event, _From, StateName, StateData) -> {reply, error, StateName, StateData}. %%%------------------------------------------------- %%% Relay Process. %%%------------------------------------------------- relay(MySocket, PeerSocket, Shaper) -> case gen_tcp:recv(MySocket, 0) of {ok, Data} -> gen_tcp:send(PeerSocket, Data), {NewShaper, Pause} = shaper:update(Shaper, byte_size(Data)), if Pause > 0 -> timer:sleep(Pause); true -> pass end, relay(MySocket, PeerSocket, NewShaper); _ -> stopped end. %%%------------------------ %%% Auxiliary functions %%%------------------------ select_auth_method(plain, AuthMethods) -> case lists:member(?AUTH_PLAIN, AuthMethods) of true -> ?AUTH_PLAIN; false -> ?AUTH_NO_METHODS end; select_auth_method(anonymous, AuthMethods) -> case lists:member(?AUTH_ANONYMOUS, AuthMethods) of true -> ?AUTH_ANONYMOUS; false -> ?AUTH_NO_METHODS end. %% Obviously, we must use shaper with maximum rate. find_maxrate(Shaper, JID1, JID2, Host) -> MaxRate1 = case acl:match_rule(Host, Shaper, JID1) of deny -> none; R1 -> shaper:new(R1) end, MaxRate2 = case acl:match_rule(Host, Shaper, JID2) of deny -> none; R2 -> shaper:new(R2) end, if MaxRate1 == none; MaxRate2 == none -> none; true -> lists:max([MaxRate1, MaxRate2]) end. listen_opt_type(server_host) -> fun iolist_to_binary/1; listen_opt_type(auth_type) -> fun (plain) -> plain; (anonymous) -> anonymous end; listen_opt_type(recbuf) -> fun (I) when is_integer(I), I > 0 -> I end; listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1; listen_opt_type(sndbuf) -> fun (I) when is_integer(I), I > 0 -> I end; listen_opt_type(_) -> [auth_type, recbuf, sndbuf, shaper]. ejabberd-18.01/src/ejabberd_router_sql.erl0000644000232200023220000000774013225664356021163 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_router_sql). -behaviour(ejabberd_router). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, get_all_routes/0]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -include("ejabberd_router.hrl"). %%%=================================================================== %%% API %%%=================================================================== init() -> Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'route' table...", []), case ejabberd_sql:sql_query( ?MYNAME, ?SQL("delete from route where node=%(Node)s")) of {updated, _} -> ok; Err -> ?ERROR_MSG("failed to clean 'route' table: ~p", [Err]), Err end. register_route(Domain, ServerHost, LocalHint, _, Pid) -> PidS = misc:encode_pid(Pid), LocalHintS = enc_local_hint(LocalHint), Node = erlang:atom_to_binary(node(Pid), latin1), case ?SQL_UPSERT(?MYNAME, "route", ["!domain=%(Domain)s", "!server_host=%(ServerHost)s", "!node=%(Node)s", "!pid=%(PidS)s", "local_hint=%(LocalHintS)s"]) of ok -> ok; _ -> {error, db_failure} end. unregister_route(Domain, _, Pid) -> PidS = misc:encode_pid(Pid), Node = erlang:atom_to_binary(node(Pid), latin1), case ejabberd_sql:sql_query( ?MYNAME, ?SQL("delete from route where domain=%(Domain)s " "and pid=%(PidS)s and node=%(Node)s")) of {updated, _} -> ok; _ -> {error, db_failure} end. find_routes(Domain) -> case ejabberd_sql:sql_query( ?MYNAME, ?SQL("select @(server_host)s, @(node)s, @(pid)s, @(local_hint)s " "from route where domain=%(Domain)s")) of {selected, Rows} -> {ok, lists:flatmap( fun(Row) -> row_to_route(Domain, Row) end, Rows)}; _ -> {error, db_failure} end. get_all_routes() -> case ejabberd_sql:sql_query( ?MYNAME, ?SQL("select @(domain)s from route where domain <> server_host")) of {selected, Domains} -> {ok, [Domain || {Domain} <- Domains]}; _ -> {error, db_failure} end. %%%=================================================================== %%% Internal functions %%%=================================================================== enc_local_hint(undefined) -> <<"">>; enc_local_hint(LocalHint) -> misc:term_to_expr(LocalHint). dec_local_hint(<<"">>) -> undefined; dec_local_hint(S) -> ejabberd_sql:decode_term(S). row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) -> try [#route{domain = Domain, server_host = ServerHost, pid = misc:decode_pid(PidS, NodeS), local_hint = dec_local_hint(LocalHintS)}] catch _:{bad_node, _} -> []; E:R -> ?ERROR_MSG("failed to decode row from 'route' table:~n" "Row = ~p~n" "Domain = ~s~n" "Reason = ~p", [Row, Domain, {E, {R, erlang:get_stacktrace()}}]), [] end. ejabberd-18.01/src/node_pep.erl0000644000232200023220000001735413225664356016741 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_pep.erl %%% Author : Christophe Romain %%% Purpose : Standard PubSub PEP plugin %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc The module {@module} is the pep PubSub plugin. %%%

PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.

-module(node_pep). -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1, depends/3]). depends(_Host, _ServerHost, _Opts) -> [{mod_caps, hard}]. init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts), ok. terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost), ok. options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, false}, {purge_offline, false}, {persist_items, true}, {max_items, 1}, {subscribe, true}, {access_model, presence}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, {presence_based_delivery, true}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"auto-create">>, <<"auto-subscribe">>, <<"delete-nodes">>, <<"delete-items">>, <<"filtered-notifications">>, <<"modify-affiliations">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"publish">>, <<"publish-options">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>]. create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> LOwner = jid:tolower(Owner), {User, Server, _Resource} = LOwner, Allowed = case LOwner of {<<"">>, Host, <<"">>} -> true; % pubsub service always allowed _ -> case acl:match_rule(ServerHost, Access, LOwner) of allow -> case Host of {User, Server, _} -> true; _ -> false end; _ -> false end end, {result, Allowed}. create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Nodes) -> {result, {_, _, Result}} = node_flat:delete_node(Nodes), {result, {default, Result}}. subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> case node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of {error, Error} -> {error, Error}; {result, _} -> {result, []} end. publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> {_, D, _} = SubKey = jid:tolower(Owner), SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}), NodeTree = mod_pubsub:tree(Host), Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) -> case NodeTree:get_node(N) of #pubsub_node{nodeid = {{_, D, _}, _}} = Node -> [{Node, A} | Acc]; _ -> Acc end end, [], States), {result, Reply}. get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> {U, D, _} = SubKey = jid:tolower(Owner), GenKey = jid:remove_resource(SubKey), States = case SubKey of GenKey -> mnesia:match_object(#pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'}); _ -> mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}) ++ mnesia:match_object(#pubsub_state{stateid = {SubKey, '_'}, _ = '_'}) end, NodeTree = mod_pubsub:tree(Host), Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) -> case NodeTree:get_node(N) of #pubsub_node{nodeid = {{_, D, _}, _}} = Node -> lists:foldl(fun ({subscribed, SubId}, Acc2) -> [{Node, subscribed, SubId, J} | Acc2]; ({pending, _SubId}, Acc2) -> [{Node, pending, J} | Acc2]; (S, Acc2) -> [{Node, S, J} | Acc2] end, Acc, Ss); _ -> Acc end end, [], States), {result, Reply}. get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). ejabberd-18.01/src/mod_irc_riak.erl0000644000232200023220000000440413225664356017562 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_irc_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_irc_riak). -behaviour(mod_irc). %% API -export([init/2, get_data/3, set_data/4, import/2]). -include("jid.hrl"). -include("mod_irc.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. get_data(_LServer, Host, From) -> {U, S, _} = jid:tolower(From), case ejabberd_riak:get(irc_custom, irc_custom_schema(), {{U, S}, Host}) of {ok, #irc_custom{data = Data}} -> Data; {error, notfound} -> empty; _Err -> error end. set_data(_LServer, Host, From, Data) -> {U, S, _} = jid:tolower(From), {atomic, ejabberd_riak:put(#irc_custom{us_host = {{U, S}, Host}, data = Data}, irc_custom_schema())}. import(_LServer, #irc_custom{} = R) -> ejabberd_riak:put(R, irc_custom_schema()). %%%=================================================================== %%% Internal functions %%%=================================================================== irc_custom_schema() -> {record_info(fields, irc_custom), #irc_custom{}}. ejabberd-18.01/src/ejabberd_riak_sup.erl0000644000232200023220000001534313225664356020577 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_riak_sup.erl %%% Author : Alexey Shchepin %%% Purpose : Riak connections supervisor %%% Created : 29 Dec 2011 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_riak_sup). -behaviour(supervisor). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([start_link/0, init/1, get_pids/0, transform_options/1, get_random_pid/0, host_up/1, config_reloaded/0, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -define(DEFAULT_POOL_SIZE, 10). -define(DEFAULT_RIAK_START_INTERVAL, 30). % 30 seconds -define(DEFAULT_RIAK_HOST, "127.0.0.1"). -define(DEFAULT_RIAK_PORT, 8087). % time to wait for the supervisor to start its child before returning % a timeout error to the request -define(CONNECT_TIMEOUT, 500). % milliseconds host_up(Host) -> case is_riak_configured(Host) of true -> ejabberd:start_app(riakc), lists:foreach( fun(Spec) -> supervisor:start_child(?MODULE, Spec) end, get_specs()); false -> ok end. config_reloaded() -> case is_riak_configured() of true -> ejabberd:start_app(riakc), lists:foreach( fun(Spec) -> supervisor:start_child(?MODULE, Spec) end, get_specs()); false -> lists:foreach( fun({Id, _, _, _}) -> supervisor:terminate_child(?MODULE, Id), supervisor:delete_child(?MODULE, Id) end, supervisor:which_children(?MODULE)) end. is_riak_configured() -> lists:any(fun is_riak_configured/1, ?MYHOSTS). is_riak_configured(Host) -> ServerConfigured = ejabberd_config:has_option({riak_server, Host}), PortConfigured = ejabberd_config:has_option({riak_port, Host}), StartIntervalConfigured = ejabberd_config:has_option({riak_start_interval, Host}), PoolConfigured = ejabberd_config:has_option({riak_pool_size, Host}), CacertConfigured = ejabberd_config:has_option({riak_cacertfile, Host}), UserConfigured = ejabberd_config:has_option({riak_username, Host}), PassConfigured = ejabberd_config:has_option({riak_password, Host}), AuthConfigured = lists:member( ejabberd_auth_riak, ejabberd_auth:auth_modules(Host)), SMConfigured = ejabberd_config:get_option({sm_db_type, Host}) == riak, RouterConfigured = ejabberd_config:get_option({router_db_type, Host}) == riak, ServerConfigured or PortConfigured or StartIntervalConfigured or PoolConfigured or CacertConfigured or UserConfigured or PassConfigured or SMConfigured or RouterConfigured or AuthConfigured. start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20), ejabberd_hooks:add(host_up, ?MODULE, host_up, 20), Specs = case is_riak_configured() of true -> ejabberd:start_app(riakc), get_specs(); false -> [] end, {ok, {{one_for_one, 500, 1}, Specs}}. -spec get_specs() -> [supervisor:child_spec()]. get_specs() -> PoolSize = get_pool_size(), StartInterval = get_start_interval(), Server = get_riak_server(), Port = get_riak_port(), CACertFile = get_riak_cacertfile(), Username = get_riak_username(), Password = get_riak_password(), Options = lists:filter( fun(X) -> X /= nil end, [auto_reconnect, {keepalive, true}, if CACertFile /= nil -> {cacertfile ,CACertFile}; true -> nil end, if (Username /= nil) and (Password /= nil) -> {credentials, Username, Password}; true -> nil end]), lists:map( fun(I) -> {ejabberd_riak:get_proc(I), {ejabberd_riak, start_link, [I, Server, Port, StartInterval*1000, Options]}, transient, 2000, worker, [?MODULE]} end, lists:seq(1, PoolSize)). get_start_interval() -> ejabberd_config:get_option(riak_start_interval, ?DEFAULT_RIAK_START_INTERVAL). get_pool_size() -> ejabberd_config:get_option(riak_pool_size, ?DEFAULT_POOL_SIZE). get_riak_server() -> ejabberd_config:get_option(riak_server, ?DEFAULT_RIAK_HOST). get_riak_cacertfile() -> ejabberd_config:get_option(riak_cacertfile, nil). get_riak_username() -> ejabberd_config:get_option(riak_username, nil). get_riak_password() -> ejabberd_config:get_option(riak_password, nil). get_riak_port() -> ejabberd_config:get_option(riak_port, ?DEFAULT_RIAK_PORT). get_pids() -> [ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())]. get_random_pid() -> I = randoms:round_robin(get_pool_size()) + 1, ejabberd_riak:get_proc(I). transform_options(Opts) -> lists:foldl(fun transform_options/2, [], Opts). transform_options({riak_server, {S, P}}, Opts) -> [{riak_server, S}, {riak_port, P}|Opts]; transform_options(Opt, Opts) -> [Opt|Opts]. -spec opt_type(riak_pool_size) -> fun((pos_integer()) -> pos_integer()); (riak_port) -> fun((0..65535) -> 0..65535); (riak_server) -> fun((binary()) -> binary()); (riak_start_interval) -> fun((pos_integer()) -> pos_integer()); (riak_cacertfile) -> fun((binary()) -> binary()); (riak_username) -> fun((binary()) -> binary()); (riak_password) -> fun((binary()) -> binary()); (atom()) -> [atom()]. opt_type(riak_pool_size) -> fun (N) when is_integer(N), N >= 1 -> N end; opt_type(riak_port) -> fun(P) when is_integer(P), P > 0, P < 65536 -> P end; opt_type(riak_server) -> fun(S) -> binary_to_list(iolist_to_binary(S)) end; opt_type(riak_start_interval) -> fun (N) when is_integer(N), N >= 1 -> N end; opt_type(riak_cacertfile) -> fun(S) -> binary_to_list(iolist_to_binary(S)) end; opt_type(riak_username) -> fun(S) -> binary_to_list(iolist_to_binary(S)) end; opt_type(riak_password) -> fun(S) -> binary_to_list(iolist_to_binary(S)) end; opt_type(_) -> [riak_pool_size, riak_port, riak_server, riak_start_interval, riak_cacertfile, riak_username, riak_password]. ejabberd-18.01/src/ejabberd_rdbms.erl0000644000232200023220000000734713225664356020076 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_rdbms.erl %%% Author : Mickael Remond %%% Purpose : Manage the start of the database modules when needed %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_rdbms). -behaviour(supervisor). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -export([start_link/0, init/1, opt_type/1, config_reloaded/0, start_host/1, stop_host/1]). -include("ejabberd.hrl"). -include("logger.hrl"). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> file:delete(ejabberd_sql:freetds_config()), file:delete(ejabberd_sql:odbc_config()), file:delete(ejabberd_sql:odbcinst_config()), ejabberd_hooks:add(host_up, ?MODULE, start_host, 20), ejabberd_hooks:add(host_down, ?MODULE, stop_host, 90), ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20), {ok, {{one_for_one, 10, 1}, get_specs()}}. -spec get_specs() -> [supervisor:child_spec()]. get_specs() -> lists:flatmap( fun(Host) -> case get_spec(Host) of {ok, Spec} -> [Spec]; undefined -> [] end end, ?MYHOSTS). -spec get_spec(binary()) -> {ok, supervisor:child_spec()} | undefined. get_spec(Host) -> case needs_sql(Host) of {true, App} -> ejabberd:start_app(App), SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup), {ok, {SupName, {ejabberd_sql_sup, start_link, [Host]}, transient, infinity, supervisor, [ejabberd_sql_sup]}}; false -> undefined end. -spec config_reloaded() -> ok. config_reloaded() -> lists:foreach(fun start_host/1, ?MYHOSTS). -spec start_host(binary()) -> ok. start_host(Host) -> case get_spec(Host) of {ok, Spec} -> case supervisor:start_child(?MODULE, Spec) of {ok, _PID} -> ok; {error, {already_started, _}} -> ok; {error, _} = Err -> erlang:error(Err) end; undefined -> ok end. -spec stop_host(binary()) -> ok. stop_host(Host) -> SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup), supervisor:terminate_child(?MODULE, SupName), supervisor:delete_child(?MODULE, SupName), ok. %% Returns {true, App} if we have configured sql for the given host needs_sql(Host) -> LHost = jid:nameprep(Host), case ejabberd_config:get_option({sql_type, LHost}, undefined) of mysql -> {true, p1_mysql}; pgsql -> {true, p1_pgsql}; sqlite -> {true, sqlite3}; mssql -> {true, odbc}; odbc -> {true, odbc}; undefined -> false end. -type sql_type() :: mysql | pgsql | sqlite | mssql | odbc. -spec opt_type(sql_type) -> fun((sql_type()) -> sql_type()); (atom()) -> [atom()]. opt_type(sql_type) -> fun (mysql) -> mysql; (pgsql) -> pgsql; (sqlite) -> sqlite; (mssql) -> mssql; (odbc) -> odbc end; opt_type(_) -> [sql_type]. ejabberd-18.01/src/ejabberd_riak.erl0000644000232200023220000004467113225664356017716 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_riak.erl %%% Author : Alexey Shchepin %%% Purpose : Interface for Riak database %%% Created : 29 Dec 2011 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_riak). -behaviour(gen_server). %% API -export([start_link/5, get_proc/1, make_bucket/1, put/2, put/3, get/2, get/3, get_by_index/4, delete/1, delete/2, count_by_index/3, get_by_index_range/5, get_keys/1, get_keys_by_index/3, is_connected/0, count/1, delete_by_index/3]). %% For debugging -export([get_tables/0]). %% map/reduce exports -export([map_key/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). -include("logger.hrl"). -record(state, {pid = self() :: pid()}). -type index() :: {binary(), any()}. -type index_info() :: [{i, any()} | {'2i', [index()]}]. %% The `record_schema()' is just a tuple: %% {record_info(fields, some_record), #some_record{}} -type record_schema() :: {[atom()], tuple()}. %% The `index_info()' is used in put/delete functions: %% `i' defines a primary index, `` '2i' '' defines secondary indexes. %% There must be only one primary index. If `i' is not specified, %% the first element of the record is assumed as a primary index, %% i.e. `i' = element(2, Record). -export_type([index_info/0]). %%%=================================================================== %%% API %%%=================================================================== %% @private start_link(Num, Server, Port, _StartInterval, Options) -> gen_server:start_link({local, get_proc(Num)}, ?MODULE, [Server, Port, Options], []). %% @private is_connected() -> lists:all( fun({_Id, Pid, _Type, _Modules}) when is_pid(Pid) -> case catch riakc_pb_socket:is_connected(get_riak_pid(Pid)) of true -> true; _ -> false end; (_) -> false end, supervisor:which_children(ejabberd_riak_sup)). %% @private get_proc(I) -> misc:binary_to_atom( iolist_to_binary( [atom_to_list(?MODULE), $_, integer_to_list(I)])). -spec make_bucket(atom()) -> binary(). %% @doc Makes a bucket from a table name %% @private make_bucket(Table) -> erlang:atom_to_binary(Table, utf8). -spec put(tuple(), record_schema()) -> ok | {error, any()}. %% @equiv put(Record, []) put(Record, RecFields) -> ?MODULE:put(Record, RecFields, []). -spec put(tuple(), record_schema(), index_info()) -> ok | {error, any()}. %% @doc Stores a record `Rec' with indexes described in ``IndexInfo'' put(Rec, RecSchema, IndexInfo) -> Key = encode_key(proplists:get_value(i, IndexInfo, element(2, Rec))), SecIdxs = [encode_index_key(K, V) || {K, V} <- proplists:get_value('2i', IndexInfo, [])], Table = element(1, Rec), Value = encode_record(Rec, RecSchema), case put_raw(Table, Key, Value, SecIdxs) of ok -> ok; {error, _} = Error -> log_error(Error, put, [{record, Rec}, {index_info, IndexInfo}]), Error end. put_raw(Table, Key, Value, Indexes) -> Bucket = make_bucket(Table), Obj = riakc_obj:new(Bucket, Key, Value, "application/x-erlang-term"), Obj1 = if Indexes /= [] -> MetaData = dict:store(<<"index">>, Indexes, dict:new()), riakc_obj:update_metadata(Obj, MetaData); true -> Obj end, catch riakc_pb_socket:put(get_random_pid(), Obj1). get_object_raw(Table, Key) -> Bucket = make_bucket(Table), catch riakc_pb_socket:get(get_random_pid(), Bucket, Key). -spec get(atom(), record_schema()) -> {ok, [any()]} | {error, any()}. %% @doc Returns all objects from table `Table' get(Table, RecSchema) -> Bucket = make_bucket(Table), case catch riakc_pb_socket:mapred( get_random_pid(), Bucket, [{map, {modfun, riak_kv_mapreduce, map_object_value}, none, true}]) of {ok, [{_, Objs}]} -> {ok, lists:flatmap( fun(Obj) -> case catch decode_record(Obj, RecSchema) of {'EXIT', _} -> Error = {error, make_invalid_object(Obj)}, log_error(Error, get, [{table, Table}]), []; Term -> [Term] end end, Objs)}; {ok, []} -> {ok, []}; {error, notfound} -> {ok, []}; {error, _} = Error -> Error end. -spec get(atom(), record_schema(), any()) -> {ok, any()} | {error, any()}. %% @doc Reads record by `Key' from table `Table' get(Table, RecSchema, Key) -> case get_raw(Table, encode_key(Key)) of {ok, Val} -> case catch decode_record(Val, RecSchema) of {'EXIT', _} -> Error = {error, make_invalid_object(Val)}, log_error(Error, get, [{table, Table}, {key, Key}]), {error, notfound}; Term -> {ok, Term} end; {error, _} = Error -> log_error(Error, get, [{table, Table}, {key, Key}]), Error end. -spec get_by_index(atom(), record_schema(), binary(), any()) -> {ok, [any()]} | {error, any()}. %% @doc Reads records by `Index' and value `Key' from `Table' get_by_index(Table, RecSchema, Index, Key) -> {NewIndex, NewKey} = encode_index_key(Index, Key), case get_by_index_raw(Table, NewIndex, NewKey) of {ok, Vals} -> {ok, lists:flatmap( fun(Val) -> case catch decode_record(Val, RecSchema) of {'EXIT', _} -> Error = {error, make_invalid_object(Val)}, log_error(Error, get_by_index, [{table, Table}, {index, Index}, {key, Key}]), []; Term -> [Term] end end, Vals)}; {error, notfound} -> {ok, []}; {error, _} = Error -> log_error(Error, get_by_index, [{table, Table}, {index, Index}, {key, Key}]), Error end. -spec get_by_index_range(atom(), record_schema(), binary(), any(), any()) -> {ok, [any()]} | {error, any()}. %% @doc Reads records by `Index' in the range `FromKey'..`ToKey' from `Table' get_by_index_range(Table, RecSchema, Index, FromKey, ToKey) -> {NewIndex, NewFromKey} = encode_index_key(Index, FromKey), {NewIndex, NewToKey} = encode_index_key(Index, ToKey), case get_by_index_range_raw(Table, NewIndex, NewFromKey, NewToKey) of {ok, Vals} -> {ok, lists:flatmap( fun(Val) -> case catch decode_record(Val, RecSchema) of {'EXIT', _} -> Error = {error, make_invalid_object(Val)}, log_error(Error, get_by_index_range, [{table, Table}, {index, Index}, {start_key, FromKey}, {end_key, ToKey}]), []; Term -> [Term] end end, Vals)}; {error, notfound} -> {ok, []}; {error, _} = Error -> log_error(Error, get_by_index_range, [{table, Table}, {index, Index}, {start_key, FromKey}, {end_key, ToKey}]), Error end. get_raw(Table, Key) -> case get_object_raw(Table, Key) of {ok, Obj} -> {ok, riakc_obj:get_value(Obj)}; {error, _} = Error -> Error end. -spec get_keys(atom()) -> {ok, [any()]} | {error, any()}. %% @doc Returns a list of index values get_keys(Table) -> Bucket = make_bucket(Table), case catch riakc_pb_socket:mapred( get_random_pid(), Bucket, [{map, {modfun, ?MODULE, map_key}, none, true}]) of {ok, [{_, Keys}]} -> {ok, Keys}; {ok, []} -> {ok, []}; {error, _} = Error -> log_error(Error, get_keys, [{table, Table}]), Error end. -spec get_keys_by_index(atom(), binary(), any()) -> {ok, [any()]} | {error, any()}. %% @doc Returns a list of primary keys of objects indexed by `Key'. get_keys_by_index(Table, Index, Key) -> {NewIndex, NewKey} = encode_index_key(Index, Key), Bucket = make_bucket(Table), case catch riakc_pb_socket:mapred( get_random_pid(), {index, Bucket, NewIndex, NewKey}, [{map, {modfun, ?MODULE, map_key}, none, true}]) of {ok, [{_, Keys}]} -> {ok, Keys}; {ok, []} -> {ok, []}; {error, _} = Error -> log_error(Error, get_keys_by_index, [{table, Table}, {index, Index}, {key, Key}]), Error end. %% @hidden get_tables() -> catch riakc_pb_socket:list_buckets(get_random_pid()). get_by_index_raw(Table, Index, Key) -> Bucket = make_bucket(Table), case riakc_pb_socket:mapred( get_random_pid(), {index, Bucket, Index, Key}, [{map, {modfun, riak_kv_mapreduce, map_object_value}, none, true}]) of {ok, [{_, Objs}]} -> {ok, Objs}; {ok, []} -> {ok, []}; {error, _} = Error -> Error end. get_by_index_range_raw(Table, Index, FromKey, ToKey) -> Bucket = make_bucket(Table), case catch riakc_pb_socket:mapred( get_random_pid(), {index, Bucket, Index, FromKey, ToKey}, [{map, {modfun, riak_kv_mapreduce, map_object_value}, none, true}]) of {ok, [{_, Objs}]} -> {ok, Objs}; {ok, []} -> {ok, []}; {error, _} = Error -> Error end. -spec count(atom()) -> {ok, non_neg_integer()} | {error, any()}. %% @doc Returns the number of objects in the `Table' count(Table) -> Bucket = make_bucket(Table), case catch riakc_pb_socket:mapred( get_random_pid(), Bucket, [{reduce, {modfun, riak_kv_mapreduce, reduce_count_inputs}, none, true}]) of {ok, [{_, [Cnt]}]} -> {ok, Cnt}; {error, _} = Error -> log_error(Error, count, [{table, Table}]), Error end. -spec count_by_index(atom(), binary(), any()) -> {ok, non_neg_integer()} | {error, any()}. %% @doc Returns the number of objects in the `Table' by index count_by_index(Tab, Index, Key) -> {NewIndex, NewKey} = encode_index_key(Index, Key), case count_by_index_raw(Tab, NewIndex, NewKey) of {ok, Cnt} -> {ok, Cnt}; {error, notfound} -> {ok, 0}; {error, _} = Error -> log_error(Error, count_by_index, [{table, Tab}, {index, Index}, {key, Key}]), Error end. count_by_index_raw(Table, Index, Key) -> Bucket = make_bucket(Table), case catch riakc_pb_socket:mapred( get_random_pid(), {index, Bucket, Index, Key}, [{reduce, {modfun, riak_kv_mapreduce, reduce_count_inputs}, none, true}]) of {ok, [{_, [Cnt]}]} -> {ok, Cnt}; {error, _} = Error -> Error end. -spec delete(tuple() | atom()) -> ok | {error, any()}. %% @doc Same as delete(T, []) when T is record. %% Or deletes all elements from table if T is atom. delete(Rec) when is_tuple(Rec) -> delete(Rec, []); delete(Table) when is_atom(Table) -> try {ok, Keys} = ?MODULE:get_keys(Table), lists:foreach( fun(K) -> ok = delete(Table, K) end, Keys) catch _:{badmatch, Err} -> Err end. -spec delete(tuple() | atom(), index_info() | any()) -> ok | {error, any()}. %% @doc Delete an object delete(Rec, Opts) when is_tuple(Rec) -> Table = element(1, Rec), Key = proplists:get_value(i, Opts, element(2, Rec)), delete(Table, Key); delete(Table, Key) when is_atom(Table) -> case delete_raw(Table, encode_key(Key)) of ok -> ok; Err -> log_error(Err, delete, [{table, Table}, {key, Key}]), Err end. delete_raw(Table, Key) -> Bucket = make_bucket(Table), catch riakc_pb_socket:delete(get_random_pid(), Bucket, Key). -spec delete_by_index(atom(), binary(), any()) -> ok | {error, any()}. %% @doc Deletes objects by index delete_by_index(Table, Index, Key) -> try {ok, Keys} = get_keys_by_index(Table, Index, Key), lists:foreach( fun(K) -> ok = delete(Table, K) end, Keys) catch _:{badmatch, Err} -> Err end. %%%=================================================================== %%% map/reduce functions %%%=================================================================== %% @private map_key(Obj, _, _) -> [case riak_object:key(Obj) of <<"b_", B/binary>> -> B; <<"i_", B/binary>> -> (binary_to_integer(B)); B -> erlang:binary_to_term(B) end]. %%%=================================================================== %%% gen_server API %%%=================================================================== %% @private init([Server, Port, Options]) -> case riakc_pb_socket:start(Server, Port, Options) of {ok, Pid} -> erlang:monitor(process, Pid), {ok, #state{pid = Pid}}; Err -> {stop, Err} end. %% @private handle_call(get_pid, _From, #state{pid = Pid} = State) -> {reply, {ok, Pid}, State}; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. %% @private handle_cast(_Msg, State) -> {noreply, State}. %% @private handle_info({'DOWN', _MonitorRef, _Type, _Object, _Info}, State) -> {stop, normal, State}; handle_info(_Info, State) -> ?ERROR_MSG("unexpected info: ~p", [_Info]), {noreply, State}. %% @private terminate(_Reason, _State) -> ok. %% @private code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== encode_index_key(Idx, Key) when is_integer(Key) -> {<>, Key}; encode_index_key(Idx, Key) -> {<>, encode_key(Key)}. encode_key(Bin) when is_binary(Bin) -> <<"b_", Bin/binary>>; encode_key(Int) when is_integer(Int) -> <<"i_", ((integer_to_binary(Int)))/binary>>; encode_key(Term) -> erlang:term_to_binary(Term). log_error({error, notfound}, _, _) -> ok; log_error({error, Why} = Err, Function, Opts) -> Txt = lists:map( fun({table, Table}) -> io_lib:fwrite("** Table: ~p~n", [Table]); ({key, Key}) -> io_lib:fwrite("** Key: ~p~n", [Key]); ({index, Index}) -> io_lib:fwrite("** Index = ~p~n", [Index]); ({start_key, Key}) -> io_lib:fwrite("** Start Key: ~p~n", [Key]); ({end_key, Key}) -> io_lib:fwrite("** End Key: ~p~n", [Key]); ({record, Rec}) -> io_lib:fwrite("** Record = ~p~n", [Rec]); ({index_info, IdxInfo}) -> io_lib:fwrite("** Index info = ~p~n", [IdxInfo]); (_) -> "" end, Opts), ErrTxt = if is_binary(Why) -> io_lib:fwrite("** Error: ~s", [Why]); true -> io_lib:fwrite("** Error: ~p", [Err]) end, ?ERROR_MSG("database error:~n** Function: ~p~n~s~s", [Function, Txt, ErrTxt]); log_error(_, _, _) -> ok. make_invalid_object(Val) -> (str:format("Invalid object: ~p", [Val])). get_random_pid() -> PoolPid = ejabberd_riak_sup:get_random_pid(), get_riak_pid(PoolPid). get_riak_pid(PoolPid) -> case catch gen_server:call(PoolPid, get_pid) of {ok, Pid} -> Pid; {'EXIT', {timeout, _}} -> throw({error, timeout}); {'EXIT', Err} -> throw({error, Err}) end. encode_record(Rec, {Fields, DefRec}) -> term_to_binary(encode_record(Rec, Fields, DefRec, 2)). encode_record(Rec, [FieldName|Fields], DefRec, Pos) -> Value = element(Pos, Rec), DefValue = element(Pos, DefRec), if Value == DefValue -> encode_record(Rec, Fields, DefRec, Pos+1); true -> [{FieldName, Value}|encode_record(Rec, Fields, DefRec, Pos+1)] end; encode_record(_, [], _, _) -> []. decode_record(Bin, {Fields, DefRec}) -> decode_record(binary_to_term(Bin), Fields, DefRec, 2). decode_record(KeyVals, [FieldName|Fields], Rec, Pos) -> case lists:keyfind(FieldName, 1, KeyVals) of {_, Value} -> NewRec = setelement(Pos, Rec, Value), decode_record(KeyVals, Fields, NewRec, Pos+1); false -> decode_record(KeyVals, Fields, Rec, Pos+1) end; decode_record(_, [], Rec, _) -> Rec. ejabberd-18.01/src/ejabberd_auth_sql.erl0000644000232200023220000002517513225664356020606 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_auth_sql.erl %%% Author : Alexey Shchepin %%% Purpose : Authentification via ODBC %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_auth_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -author('alexey@process-one.net'). -behaviour(ejabberd_auth). -behaviour(ejabberd_config). -export([start/1, stop/1, set_password/3, try_register/3, get_users/2, count_users/2, get_password/2, remove_user/2, store_type/1, plain_password_required/1, convert_to_scram/1, opt_type/1, export/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -include("ejabberd_auth.hrl"). -define(SALT_LENGTH, 16). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(_Host) -> ok. stop(_Host) -> ok. plain_password_required(Server) -> store_type(Server) == scram. store_type(Server) -> ejabberd_auth:password_format(Server). set_password(User, Server, Password) -> F = fun() -> if is_record(Password, scram) -> set_password_scram_t( User, Server, Password#scram.storedkey, Password#scram.serverkey, Password#scram.salt, Password#scram.iterationcount); true -> set_password_t(User, Server, Password) end end, case ejabberd_sql:sql_transaction(Server, F) of {atomic, _} -> ok; {aborted, _} -> {error, db_failure} end. try_register(User, Server, Password) -> Res = if is_record(Password, scram) -> add_user_scram( Server, User, Password#scram.storedkey, Password#scram.serverkey, Password#scram.salt, Password#scram.iterationcount); true -> add_user(Server, User, Password) end, case Res of {updated, 1} -> ok; _ -> {error, exists} end. get_users(Server, Opts) -> case list_users(Server, Opts) of {selected, Res} -> [{U, Server} || {U} <- Res]; _ -> [] end. count_users(Server, Opts) -> case users_number(Server, Opts) of {selected, [{Res}]} -> Res; _Other -> 0 end. get_password(User, Server) -> case get_password_scram(Server, User) of {selected, [{Password, <<>>, <<>>, 0}]} -> {ok, Password}; {selected, [{StoredKey, ServerKey, Salt, IterationCount}]} -> {ok, #scram{storedkey = StoredKey, serverkey = ServerKey, salt = Salt, iterationcount = IterationCount}}; {selected, []} -> error; _ -> error end. remove_user(User, Server) -> case del_user(Server, User) of {updated, _} -> ok; _ -> {error, db_failure} end. -define(BATCH_SIZE, 1000). set_password_scram_t(LUser, LServer, StoredKey, ServerKey, Salt, IterationCount) -> ?SQL_UPSERT_T( "users", ["!username=%(LUser)s", "!server_host=%(LServer)s", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", "iterationcount=%(IterationCount)d"]). set_password_t(LUser, LServer, Password) -> ?SQL_UPSERT_T( "users", ["!username=%(LUser)s", "!server_host=%(LServer)s", "password=%(Password)s"]). get_password_scram(LServer, LUser) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d" " from users" " where username=%(LUser)s and %(LServer)H")). add_user_scram(LServer, LUser, StoredKey, ServerKey, Salt, IterationCount) -> ejabberd_sql:sql_query( LServer, ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", "iterationcount=%(IterationCount)d"])). add_user(LServer, LUser, Password) -> ejabberd_sql:sql_query( LServer, ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(Password)s"])). del_user(LServer, LUser) -> ejabberd_sql:sql_query( LServer, ?SQL("delete from users where username=%(LUser)s and %(LServer)H")). list_users(LServer, []) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(username)s from users where %(LServer)H")); list_users(LServer, [{from, Start}, {to, End}]) when is_integer(Start) and is_integer(End) -> list_users(LServer, [{limit, End - Start + 1}, {offset, Start - 1}]); list_users(LServer, [{prefix, Prefix}, {from, Start}, {to, End}]) when is_binary(Prefix) and is_integer(Start) and is_integer(End) -> list_users(LServer, [{prefix, Prefix}, {limit, End - Start + 1}, {offset, Start - 1}]); list_users(LServer, [{limit, Limit}, {offset, Offset}]) when is_integer(Limit) and is_integer(Offset) -> ejabberd_sql:sql_query( LServer, ?SQL("select @(username)s from users " "where %(LServer)H " "order by username " "limit %(Limit)d offset %(Offset)d")); list_users(LServer, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) when is_binary(Prefix) and is_integer(Limit) and is_integer(Offset) -> SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix), SPrefix2 = <>, ejabberd_sql:sql_query( LServer, ?SQL("select @(username)s from users " "where username like %(SPrefix2)s escape '^' and %(LServer)H " "order by username " "limit %(Limit)d offset %(Offset)d")). users_number(LServer) -> ejabberd_sql:sql_query( LServer, fun(pgsql, _) -> case ejabberd_config:get_option( {pgsql_users_number_estimate, LServer}, false) of true -> ejabberd_sql:sql_query_t( ?SQL("select @(reltuples :: bigint)d from pg_class" " where oid = 'users'::regclass::oid")); _ -> ejabberd_sql:sql_query_t( ?SQL("select @(count(*))d from users where %(LServer)H")) end; (_Type, _) -> ejabberd_sql:sql_query_t( ?SQL("select @(count(*))d from users where %(LServer)H")) end). users_number(LServer, [{prefix, Prefix}]) when is_binary(Prefix) -> SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix), SPrefix2 = <>, ejabberd_sql:sql_query( LServer, ?SQL("select @(count(*))d from users " "where username like %(SPrefix2)s escape '^' and %(LServer)H")); users_number(LServer, []) -> users_number(LServer). convert_to_scram(Server) -> LServer = jid:nameprep(Server), if LServer == error; LServer == <<>> -> {error, {incorrect_server_name, Server}}; true -> F = fun () -> BatchSize = ?BATCH_SIZE, case ejabberd_sql:sql_query_t( ?SQL("select @(username)s, @(password)s" " from users" " where iterationcount=0 and %(LServer)H" " limit %(BatchSize)d")) of {selected, []} -> ok; {selected, Rs} -> lists:foreach( fun({LUser, Password}) -> case jid:resourceprep(Password) of error -> ?ERROR_MSG( "SASLprep failed for " "password of user ~s@~s", [LUser, LServer]); _ -> Scram = ejabberd_auth:password_to_scram(Password), set_password_scram_t( LUser, LServer, Scram#scram.storedkey, Scram#scram.serverkey, Scram#scram.salt, Scram#scram.iterationcount) end end, Rs), continue; Err -> {bad_reply, Err} end end, case ejabberd_sql:sql_transaction(LServer, F) of {atomic, ok} -> ok; {atomic, continue} -> convert_to_scram(Server); {atomic, Error} -> {error, Error}; Error -> Error end end. export(_Server) -> [{passwd, fun(Host, #passwd{us = {LUser, LServer}, password = Password}) when LServer == Host, is_binary(Password) -> [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(Password)s"])]; (Host, #passwd{us = {LUser, LServer}, password = #scram{} = Scram}) when LServer == Host -> StoredKey = Scram#scram.storedkey, ServerKey = Scram#scram.serverkey, Salt = Scram#scram.salt, IterationCount = Scram#scram.iterationcount, [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", "iterationcount=%(IterationCount)d"])]; (_Host, _R) -> [] end}]. -spec opt_type(pgsql_users_number_estimate) -> fun((boolean()) -> boolean()); (atom()) -> [atom()]. opt_type(pgsql_users_number_estimate) -> fun (V) when is_boolean(V) -> V end; opt_type(_) -> [pgsql_users_number_estimate]. ejabberd-18.01/src/node_mb.erl0000644000232200023220000001433313225664356016545 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : node_mb.erl %%% Author : Eric Cestari %%% Purpose : PEP microglobing experimentation %%% Created : 25 Sep 2008 by Eric Cestari %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_mb). -behaviour(gen_pubsub_node). -author('ecestari@process-one.net'). -include("pubsub.hrl"). %%% @doc The module {@module} is the pep microblog PubSub plugin. %%%

To be used, mod_pubsub must be configured:

%%% mod_pubsub:
%%%   access_createnode: pubsub_createnode
%%%   ignore_pep_from_offline: false
%%%   plugins:
%%%     - "flat"
%%%     - "pep" # Requires mod_caps.
%%%     - "mb"
%%%   pep_mapping:
%%%     "urn:xmpp:microblog:0": "mb"
%%% 

%%%

PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.

-export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). init(Host, ServerHost, Opts) -> node_pep:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_pep:terminate(Host, ServerHost), ok. options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, false}, {purge_offline, false}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, presence}, {roster_groups_allowed, []}, {publish_model, publishers}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, {presence_based_delivery, true}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"auto-create">>, <<"auto-subscribe">>, <<"delete-nodes">>, <<"delete-items">>, <<"filtered-notifications">>, <<"modify-affiliations">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"multi-items">>, <<"publish">>, <<"publish-options">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_pep:create_node(Nidx, Owner). delete_node(Removed) -> node_pep:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_pep:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_pep:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_pep:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_pep:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_pep:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_pep:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_pep:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_pep:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_pep:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_pep:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_pep:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_pep:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_pep:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_pep:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_pep:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_pep:get_states(Nidx). get_state(Nidx, JID) -> node_pep:get_state(Nidx, JID). set_state(State) -> node_pep:set_state(State). get_items(Nidx, From, RSM) -> node_pep:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_pep:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_pep:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_pep:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_pep:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_pep:set_item(Item). get_item_name(Host, Node, Id) -> node_pep:get_item_name(Host, Node, Id). node_to_path(Node) -> node_pep:node_to_path(Node). path_to_node(Path) -> node_pep:path_to_node(Path). ejabberd-18.01/src/xmpp_stream_out.erl0000644000232200023220000011041013225664356020361 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% Created : 14 Dec 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(xmpp_stream_out). -define(GEN_SERVER, p1_server). -behaviour(?GEN_SERVER). -protocol({rfc, 6120}). -protocol({xep, 114, '1.6'}). -protocol({xep, 368, '1.0.0'}). %% API -export([start/3, start_link/3, call/3, cast/2, reply/2, connect/1, stop/1, send/2, close/1, close/2, establish/1, format_error/1, set_timeout/2, get_transport/1, change_shaper/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). -else. -define(FSMOPTS, []). -endif. -define(TCP_SEND_TIMEOUT, 15000). -include("xmpp.hrl"). -include_lib("kernel/include/inet.hrl"). -type state() :: map(). -type noreply() :: {noreply, state(), timeout()}. -type host_port() :: {inet:hostname(), inet:port_number(), boolean()}. -type ip_port() :: {inet:ip_address(), inet:port_number(), boolean()}. -type h_addr_list() :: {{integer(), integer(), inet:port_number(), string()}, boolean()}. -type network_error() :: {error, inet:posix() | inet_res:res_error()}. -type tls_error_reason() :: inet:posix() | atom() | binary(). -type socket_error_reason() :: inet:posix() | atom(). -type stop_reason() :: {idna, bad_string} | {dns, inet:posix() | inet_res:res_error()} | {stream, reset | {in | out, stream_error()}} | {tls, tls_error_reason()} | {pkix, binary()} | {auth, atom() | binary() | string()} | {socket, socket_error_reason()} | internal_failure. -export_type([state/0, stop_reason/0]). -callback init(list()) -> {ok, state()} | {error, term()} | ignore. -callback handle_cast(term(), state()) -> state(). -callback handle_call(term(), term(), state()) -> state(). -callback handle_info(term(), state()) -> state(). -callback terminate(term(), state()) -> any(). -callback code_change(term(), state(), term()) -> {ok, state()} | {error, term()}. -callback handle_stream_start(stream_start(), state()) -> state(). -callback handle_stream_established(state()) -> state(). -callback handle_stream_downgraded(stream_start(), state()) -> state(). -callback handle_stream_end(stop_reason(), state()) -> state(). -callback handle_cdata(binary(), state()) -> state(). -callback handle_send(xmpp_element(), ok | {error, inet:posix()}, state()) -> state(). -callback handle_recv(fxml:xmlel(), xmpp_element() | {error, term()}, state()) -> state(). -callback handle_timeout(state()) -> state(). -callback handle_authenticated_features(stream_features(), state()) -> state(). -callback handle_unauthenticated_features(stream_features(), state()) -> state(). -callback handle_auth_success(cyrsasl:mechanism(), state()) -> state(). -callback handle_auth_failure(cyrsasl:mechanism(), binary(), state()) -> state(). -callback handle_packet(xmpp_element(), state()) -> state(). -callback tls_options(state()) -> [proplists:property()]. -callback tls_required(state()) -> boolean(). -callback tls_verify(state()) -> boolean(). -callback tls_enabled(state()) -> boolean(). -callback dns_timeout(state()) -> timeout(). -callback dns_retries(state()) -> non_neg_integer(). -callback default_port(state()) -> inet:port_number(). -callback address_families(state()) -> [inet:address_family()]. -callback connect_timeout(state()) -> timeout(). -optional_callbacks([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, handle_stream_start/2, handle_stream_established/1, handle_stream_downgraded/2, handle_stream_end/2, handle_cdata/2, handle_send/3, handle_recv/3, handle_timeout/1, handle_authenticated_features/2, handle_unauthenticated_features/2, handle_auth_success/2, handle_auth_failure/3, handle_packet/2, tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1, dns_timeout/1, dns_retries/1, default_port/1, address_families/1, connect_timeout/1]). %%%=================================================================== %%% API %%%=================================================================== start(Mod, Args, Opts) -> ?GEN_SERVER:start(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS). start_link(Mod, Args, Opts) -> ?GEN_SERVER:start_link(?MODULE, [Mod|Args], Opts ++ ?FSMOPTS). call(Ref, Msg, Timeout) -> ?GEN_SERVER:call(Ref, Msg, Timeout). cast(Ref, Msg) -> ?GEN_SERVER:cast(Ref, Msg). reply(Ref, Reply) -> ?GEN_SERVER:reply(Ref, Reply). -spec connect(pid()) -> ok. connect(Ref) -> cast(Ref, connect). -spec stop(pid()) -> ok; (state()) -> no_return(). stop(Pid) when is_pid(Pid) -> cast(Pid, stop); stop(#{owner := Owner} = State) when Owner == self() -> terminate(normal, State), exit(normal); stop(_) -> erlang:error(badarg). -spec send(pid(), xmpp_element()) -> ok; (state(), xmpp_element()) -> state(). send(Pid, Pkt) when is_pid(Pid) -> cast(Pid, {send, Pkt}); send(#{owner := Owner} = State, Pkt) when Owner == self() -> send_pkt(State, Pkt); send(_, _) -> erlang:error(badarg). -spec close(pid()) -> ok; (state()) -> state(). close(Pid) when is_pid(Pid) -> close(Pid, closed); close(#{owner := Owner} = State) when Owner == self() -> close_socket(State); close(_) -> erlang:error(badarg). -spec close(pid(), atom()) -> ok. close(Pid, Reason) -> cast(Pid, {close, Reason}). -spec establish(state()) -> state(). establish(State) -> process_stream_established(State). -spec set_timeout(state(), timeout()) -> state(). set_timeout(#{owner := Owner} = State, Timeout) when Owner == self() -> case Timeout of infinity -> State#{stream_timeout => infinity}; _ -> Time = p1_time_compat:monotonic_time(milli_seconds), State#{stream_timeout => {Timeout, Time}} end; set_timeout(_, _) -> erlang:error(badarg). get_transport(#{socket := Socket, owner := Owner}) when Owner == self() -> xmpp_socket:get_transport(Socket); get_transport(_) -> erlang:error(badarg). -spec change_shaper(state(), shaper:shaper()) -> state(). change_shaper(#{socket := Socket, owner := Owner} = State, Shaper) when Owner == self() -> Socket1 = xmpp_socket:change_shaper(Socket, Shaper), State#{socket => Socket1}; change_shaper(_, _) -> erlang:error(badarg). -spec format_error(stop_reason()) -> binary(). format_error({idna, _}) -> <<"Remote domain is not an IDN hostname">>; format_error({dns, Reason}) -> format("DNS lookup failed: ~s", [format_inet_error(Reason)]); format_error({socket, Reason}) -> format("Connection failed: ~s", [format_inet_error(Reason)]); format_error({pkix, Reason}) -> {_, ErrTxt} = xmpp_stream_pkix:format_error(Reason), format("Peer certificate rejected: ~s", [ErrTxt]); format_error({stream, reset}) -> <<"Stream reset by peer">>; format_error({stream, {in, #stream_error{reason = Reason, text = Txt}}}) -> format("Stream closed by peer: ~s", [format_stream_error(Reason, Txt)]); format_error({stream, {out, #stream_error{reason = Reason, text = Txt}}}) -> format("Stream closed by us: ~s", [format_stream_error(Reason, Txt)]); format_error({tls, Reason}) -> format("TLS failed: ~s", [format_tls_error(Reason)]); format_error({auth, Reason}) -> format("Authentication failed: ~s", [Reason]); format_error(internal_failure) -> <<"Internal server error">>; format_error(Err) -> format("Unrecognized error: ~w", [Err]). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== -spec init(list()) -> {ok, state(), timeout()} | {stop, term()} | ignore. init([Mod, _SockMod, From, To, Opts]) -> Time = p1_time_compat:monotonic_time(milli_seconds), State = #{owner => self(), mod => Mod, server => From, user => <<"">>, resource => <<"">>, lang => <<"">>, remote_server => To, xmlns => ?NS_SERVER, stream_direction => out, stream_timeout => {timer:seconds(30), Time}, stream_id => new_id(), stream_encrypted => false, stream_verified => false, stream_authenticated => false, stream_restarted => false, stream_state => connecting}, case try Mod:init([State, Opts]) catch _:undef -> {ok, State} end of {ok, State1} -> {_, State2, Timeout} = noreply(State1), {ok, State2, Timeout}; {error, Reason} -> {stop, Reason}; ignore -> ignore end. -spec handle_call(term(), term(), state()) -> noreply(). handle_call(Call, From, #{mod := Mod} = State) -> noreply(try Mod:handle_call(Call, From, State) catch _:undef -> State end). -spec handle_cast(term(), state()) -> noreply(). handle_cast(connect, #{remote_server := RemoteServer, stream_state := connecting} = State) -> noreply( case idna_to_ascii(RemoteServer) of false -> process_stream_end({idna, bad_string}, State); ASCIIName -> case resolve(binary_to_list(ASCIIName), State) of {ok, AddrPorts} -> case connect(AddrPorts, State) of {ok, Socket, {Addr, Port, Encrypted}} -> SocketMonitor = xmpp_socket:monitor(Socket), State1 = State#{ip => {Addr, Port}, socket => Socket, stream_encrypted => Encrypted, socket_monitor => SocketMonitor}, State2 = State1#{stream_state => wait_for_stream}, send_header(State2); {error, {Class, Why}} -> process_stream_end({Class, Why}, State) end; {error, Why} -> process_stream_end({dns, Why}, State) end end); handle_cast(connect, State) -> %% Ignoring connection attempts in other states noreply(State); handle_cast({send, Pkt}, State) -> noreply(send_pkt(State, Pkt)); handle_cast(stop, State) -> {stop, normal, State}; handle_cast({close, Reason}, State) -> State1 = close_socket(State), noreply( case is_disconnected(State) of true -> State1; false -> process_stream_end({socket, Reason}, State) end); handle_cast(Cast, #{mod := Mod} = State) -> noreply(try Mod:handle_cast(Cast, State) catch _:undef -> State end). -spec handle_info(term(), state()) -> noreply(). handle_info({'$gen_event', {xmlstreamstart, Name, Attrs}}, #{stream_state := wait_for_stream, xmlns := XMLNS, lang := MyLang} = State) -> El = #xmlel{name = Name, attrs = Attrs}, noreply( try xmpp:decode(El, XMLNS, []) of #stream_start{} = Pkt -> process_stream(Pkt, State); _ -> send_pkt(State, xmpp:serr_invalid_xml()) catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Lang = select_lang(MyLang, xmpp:get_lang(El)), Err = xmpp:serr_invalid_xml(Txt, Lang), send_pkt(State, Err) end); handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) -> State1 = send_header(State), noreply( case is_disconnected(State1) of true -> State1; false -> Err = case Reason of <<"XML stanza is too big">> -> xmpp:serr_policy_violation(Reason, Lang); {_, Txt} -> xmpp:serr_not_well_formed(Txt, Lang) end, send_pkt(State1, Err) end); handle_info({'$gen_event', {xmlstreamelement, El}}, #{xmlns := NS, mod := Mod} = State) -> noreply( try xmpp:decode(El, NS, [ignore_els]) of Pkt -> State1 = try Mod:handle_recv(El, Pkt, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> process_element(Pkt, State1) end catch _:{xmpp_codec, Why} -> State1 = try Mod:handle_recv(El, {error, Why}, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> process_invalid_xml(State1, El, Why) end end); handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}}, #{mod := Mod} = State) -> noreply(try Mod:handle_cdata(Data, State) catch _:undef -> State end); handle_info({'$gen_event', {xmlstreamend, _}}, State) -> noreply(process_stream_end({stream, reset}, State)); handle_info({'$gen_event', closed}, State) -> noreply(process_stream_end({socket, closed}, State)); handle_info(timeout, #{mod := Mod} = State) -> Disconnected = is_disconnected(State), noreply(try Mod:handle_timeout(State) catch _:undef when not Disconnected -> send_pkt(State, xmpp:serr_connection_timeout()); _:undef -> stop(State) end); handle_info({'DOWN', MRef, _Type, _Object, _Info}, #{socket_monitor := MRef} = State) -> noreply(process_stream_end({socket, closed}, State)); handle_info({tcp, _, Data}, #{socket := Socket} = State) -> noreply( case xmpp_socket:recv(Socket, Data) of {ok, NewSocket} -> State#{socket => NewSocket}; {error, Reason} when is_atom(Reason) -> process_stream_end({socket, Reason}, State); {error, Reason} -> %% TODO: make fast_tls return atoms process_stream_end({tls, Reason}, State) end); handle_info({tcp_closed, _}, State) -> handle_info({'$gen_event', closed}, State); handle_info({tcp_error, _, Reason}, State) -> noreply(process_stream_end({socket, Reason}, State)); handle_info(Info, #{mod := Mod} = State) -> noreply(try Mod:handle_info(Info, State) catch _:undef -> State end). -spec terminate(term(), state()) -> any(). terminate(Reason, #{mod := Mod} = State) -> case get(already_terminated) of true -> State; _ -> put(already_terminated, true), try Mod:terminate(Reason, State) catch _:undef -> ok end, send_trailer(State) end. code_change(OldVsn, #{mod := Mod} = State, Extra) -> Mod:code_change(OldVsn, State, Extra). %%%=================================================================== %%% Internal functions %%%=================================================================== -spec noreply(state()) -> noreply(). noreply(#{stream_timeout := infinity} = State) -> {noreply, State, infinity}; noreply(#{stream_timeout := {MSecs, OldTime}} = State) -> NewTime = p1_time_compat:monotonic_time(milli_seconds), Timeout = max(0, MSecs - NewTime + OldTime), {noreply, State, Timeout}. -spec new_id() -> binary(). new_id() -> randoms:get_string(). -spec is_disconnected(state()) -> boolean(). is_disconnected(#{stream_state := StreamState}) -> StreamState == disconnected. -spec process_invalid_xml(state(), fxml:xmlel(), term()) -> state(). process_invalid_xml(#{lang := MyLang} = State, El, Reason) -> case xmpp:is_stanza(El) of true -> Txt = xmpp:io_format_error(Reason), Lang = select_lang(MyLang, xmpp:get_lang(El)), send_error(State, El, xmpp:err_bad_request(Txt, Lang)); false -> State end. -spec process_stream_end(stop_reason(), state()) -> state(). process_stream_end(_, #{stream_state := disconnected} = State) -> State; process_stream_end(Reason, #{mod := Mod} = State) -> State1 = State#{stream_timeout => infinity, stream_state => disconnected}, try Mod:handle_stream_end(Reason, State1) catch _:undef -> stop(State1) end. -spec process_stream(stream_start(), state()) -> state(). process_stream(#stream_start{xmlns = XML_NS, stream_xmlns = STREAM_NS}, #{xmlns := NS} = State) when XML_NS /= NS; STREAM_NS /= ?NS_STREAM -> send_pkt(State, xmpp:serr_invalid_namespace()); process_stream(#stream_start{version = {N, _}}, State) when N > 1 -> send_pkt(State, xmpp:serr_unsupported_version()); process_stream(#stream_start{lang = Lang, id = ID, version = Version} = StreamStart, #{mod := Mod} = State) -> State1 = State#{stream_remote_id => ID, lang => Lang}, State2 = try Mod:handle_stream_start(StreamStart, State1) catch _:undef -> State1 end, case is_disconnected(State2) of true -> State2; false -> case Version of {1, _} -> State2#{stream_state => wait_for_features}; _ -> process_stream_downgrade(StreamStart, State2) end end. -spec process_element(xmpp_element(), state()) -> state(). process_element(Pkt, #{stream_state := StateName} = State) -> case Pkt of #stream_features{} when StateName == wait_for_features -> process_features(Pkt, State); #starttls_proceed{} when StateName == wait_for_starttls_response -> process_starttls(State); #sasl_success{} when StateName == wait_for_sasl_response -> process_sasl_success(State); #sasl_failure{} when StateName == wait_for_sasl_response -> process_sasl_failure(Pkt, State); #stream_error{} -> process_stream_end({stream, {in, Pkt}}, State); _ when is_record(Pkt, stream_features); is_record(Pkt, starttls_proceed); is_record(Pkt, starttls); is_record(Pkt, sasl_auth); is_record(Pkt, sasl_success); is_record(Pkt, sasl_failure); is_record(Pkt, sasl_response); is_record(Pkt, sasl_abort); is_record(Pkt, compress); is_record(Pkt, handshake) -> %% Do not pass this crap upstream State; _ -> process_packet(Pkt, State) end. -spec process_features(stream_features(), state()) -> state(). process_features(StreamFeatures, #{stream_authenticated := true, mod := Mod} = State) -> State1 = try Mod:handle_authenticated_features(StreamFeatures, State) catch _:undef -> State end, process_stream_established(State1); process_features(StreamFeatures, #{stream_encrypted := Encrypted, mod := Mod, lang := Lang} = State) -> State1 = try Mod:handle_unauthenticated_features(StreamFeatures, State) catch _:undef -> State end, case is_disconnected(State1) of true -> State1; false -> TLSRequired = is_starttls_required(State1), TLSAvailable = is_starttls_available(State1), try xmpp:try_subtag(StreamFeatures, #starttls{}) of false when TLSRequired and not Encrypted -> Txt = <<"Use of STARTTLS required">>, send_pkt(State1, xmpp:serr_policy_violation(Txt, Lang)); false when not Encrypted -> process_sasl_failure( <<"Peer doesn't support STARTTLS">>, State1); #starttls{required = true} when not TLSAvailable and not Encrypted -> Txt = <<"Use of STARTTLS forbidden">>, send_pkt(State1, xmpp:serr_unsupported_feature(Txt, Lang)); #starttls{} when TLSAvailable and not Encrypted -> State2 = State1#{stream_state => wait_for_starttls_response}, send_pkt(State2, #starttls{}); #starttls{} when not Encrypted -> process_sasl_failure( <<"STARTTLS is disabled in local configuration">>, State1); _ -> State2 = process_cert_verification(State1), case is_disconnected(State2) of true -> State2; false -> try xmpp:try_subtag(StreamFeatures, #sasl_mechanisms{}) of #sasl_mechanisms{list = Mechs} -> process_sasl_mechanisms(Mechs, State2); false -> process_sasl_failure( <<"Peer provided no SASL mechanisms">>, State2) catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), process_sasl_failure(Txt, State1) end end catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), process_sasl_failure(Txt, State1) end end. -spec process_stream_established(state()) -> state(). process_stream_established(#{stream_state := StateName} = State) when StateName == disconnected; StateName == established -> State; process_stream_established(#{mod := Mod} = State) -> State1 = State#{stream_authenticated := true, stream_state => established, stream_timeout => infinity}, try Mod:handle_stream_established(State1) catch _:undef -> State1 end. -spec process_sasl_mechanisms([binary()], state()) -> state(). process_sasl_mechanisms(Mechs, #{user := User, server := Server} = State) -> %% TODO: support other mechanisms Mech = <<"EXTERNAL">>, case lists:member(<<"EXTERNAL">>, Mechs) of true -> State1 = State#{stream_state => wait_for_sasl_response}, Authzid = jid:encode(jid:make(User, Server)), send_pkt(State1, #sasl_auth{mechanism = Mech, text = Authzid}); false -> process_sasl_failure( <<"Peer doesn't support EXTERNAL authentication">>, State) end. -spec process_starttls(state()) -> state(). process_starttls(#{socket := Socket} = State) -> case starttls(Socket, State) of {ok, TLSSocket} -> State1 = State#{socket => TLSSocket, stream_id => new_id(), stream_restarted => true, stream_state => wait_for_stream, stream_encrypted => true}, send_header(State1); {error, Why} -> process_stream_end({tls, Why}, State) end. -spec process_stream_downgrade(stream_start(), state()) -> state(). process_stream_downgrade(StreamStart, #{mod := Mod, lang := Lang, stream_encrypted := Encrypted} = State) -> TLSRequired = is_starttls_required(State), if not Encrypted and TLSRequired -> Txt = <<"Use of STARTTLS required">>, send_pkt(State, xmpp:serr_policy_violation(Txt, Lang)); true -> State1 = State#{stream_state => downgraded}, try Mod:handle_stream_downgraded(StreamStart, State1) catch _:undef -> send_pkt(State1, xmpp:serr_unsupported_version()) end end. -spec process_cert_verification(state()) -> state(). process_cert_verification(#{stream_encrypted := true, stream_verified := false, mod := Mod} = State) -> case try Mod:tls_verify(State) catch _:undef -> true end of true -> case xmpp_stream_pkix:authenticate(State) of {ok, _} -> State#{stream_verified => true}; {error, Why, _Peer} -> process_stream_end({pkix, Why}, State) end; false -> State#{stream_verified => true} end; process_cert_verification(State) -> State. -spec process_sasl_success(state()) -> state(). process_sasl_success(#{mod := Mod, socket := Socket} = State) -> Socket1 = xmpp_socket:reset_stream(Socket), State0 = State#{socket => Socket1}, State1 = State0#{stream_id => new_id(), stream_restarted => true, stream_state => wait_for_stream, stream_authenticated => true}, State2 = send_header(State1), case is_disconnected(State2) of true -> State2; false -> try Mod:handle_auth_success(<<"EXTERNAL">>, State2) catch _:undef -> State2 end end. -spec process_sasl_failure(sasl_failure() | binary(), state()) -> state(). process_sasl_failure(#sasl_failure{} = Failure, State) -> Reason = format("Peer responded with error: ~s", [format_sasl_failure(Failure)]), process_sasl_failure(Reason, State); process_sasl_failure(Reason, #{mod := Mod} = State) -> try Mod:handle_auth_failure(<<"EXTERNAL">>, {auth, Reason}, State) catch _:undef -> process_stream_end({auth, Reason}, State) end. -spec process_packet(xmpp_element(), state()) -> state(). process_packet(Pkt, #{mod := Mod} = State) -> try Mod:handle_packet(Pkt, State) catch _:undef -> State end. -spec is_starttls_required(state()) -> boolean(). is_starttls_required(#{mod := Mod} = State) -> try Mod:tls_required(State) catch _:undef -> false end. -spec is_starttls_available(state()) -> boolean(). is_starttls_available(#{mod := Mod} = State) -> try Mod:tls_enabled(State) catch _:undef -> true end. -spec send_header(state()) -> state(). send_header(#{remote_server := RemoteServer, stream_encrypted := Encrypted, lang := Lang, xmlns := NS, user := User, resource := Resource, server := Server} = State) -> NS_DB = if NS == ?NS_SERVER -> ?NS_SERVER_DIALBACK; true -> <<"">> end, From = if Encrypted -> jid:make(User, Server, Resource); NS == ?NS_SERVER -> jid:make(Server); true -> undefined end, StreamStart = #stream_start{xmlns = NS, lang = Lang, stream_xmlns = ?NS_STREAM, db_xmlns = NS_DB, from = From, to = jid:make(RemoteServer), version = {1,0}}, case socket_send(State, StreamStart) of ok -> State; {error, Why} -> process_stream_end({socket, Why}, State) end. -spec send_pkt(state(), xmpp_element() | xmlel()) -> state(). send_pkt(#{mod := Mod} = State, Pkt) -> Result = socket_send(State, Pkt), State1 = try Mod:handle_send(Pkt, Result, State) catch _:undef -> State end, case Result of _ when is_record(Pkt, stream_error) -> process_stream_end({stream, {out, Pkt}}, State1); ok -> State1; {error, Why} -> process_stream_end({socket, Why}, State1) end. -spec send_error(state(), xmpp_element() | xmlel(), stanza_error()) -> state(). send_error(State, Pkt, Err) -> case xmpp:is_stanza(Pkt) of true -> case xmpp:get_type(Pkt) of result -> State; error -> State; <<"result">> -> State; <<"error">> -> State; _ -> ErrPkt = xmpp:make_error(Pkt, Err), send_pkt(State, ErrPkt) end; false -> State end. -spec socket_send(state(), xmpp_element() | xmlel() | trailer) -> ok | {error, inet:posix()}. socket_send(#{socket := Socket, xmlns := NS, stream_state := StateName}, Pkt) -> case Pkt of trailer -> xmpp_socket:send_trailer(Socket); #stream_start{} when StateName /= disconnected -> xmpp_socket:send_header(Socket, xmpp:encode(Pkt)); _ when StateName /= disconnected -> xmpp_socket:send_element(Socket, xmpp:encode(Pkt, NS)); _ -> {error, closed} end; socket_send(_, _) -> {error, closed}. -spec send_trailer(state()) -> state(). send_trailer(State) -> socket_send(State, trailer), close_socket(State). -spec close_socket(state()) -> state(). close_socket(State) -> case State of #{socket := Socket} -> xmpp_socket:close(Socket); _ -> ok end, State#{stream_timeout => infinity, stream_state => disconnected}. -spec starttls(term(), state()) -> {ok, term()} | {error, tls_error_reason()}. starttls(Socket, #{mod := Mod, xmlns := NS, remote_server := RemoteServer} = State) -> TLSOpts = try Mod:tls_options(State) catch _:undef -> [] end, SNI = idna_to_ascii(RemoteServer), ALPN = case NS of ?NS_SERVER -> <<"xmpp-server">>; ?NS_CLIENT -> <<"xmpp-client">> end, xmpp_socket:starttls(Socket, [connect, {sni, SNI}, {alpn, [ALPN]}|TLSOpts]). -spec select_lang(binary(), binary()) -> binary(). select_lang(Lang, <<"">>) -> Lang; select_lang(_, Lang) -> Lang. -spec format_inet_error(atom()) -> string(). format_inet_error(closed) -> "connection closed"; format_inet_error(Reason) -> case inet:format_error(Reason) of "unknown POSIX error" -> atom_to_list(Reason); Txt -> Txt end. -spec format_stream_error(atom() | 'see-other-host'(), [text()]) -> string(). format_stream_error(Reason, Txt) -> Slogan = case Reason of undefined -> "no reason"; #'see-other-host'{} -> "see-other-host"; _ -> atom_to_list(Reason) end, case xmpp:get_text(Txt) of <<"">> -> Slogan; Data -> binary_to_list(Data) ++ " (" ++ Slogan ++ ")" end. -spec format_tls_error(atom() | binary()) -> list(). format_tls_error(Reason) when is_atom(Reason) -> format_inet_error(Reason); format_tls_error(Reason) -> binary_to_list(Reason). format_sasl_failure(#sasl_failure{reason = Reason, text = Txt}) -> Slogan = case Reason of undefined -> "no reason"; _ -> atom_to_list(Reason) end, case xmpp:get_text(Txt) of <<"">> -> Slogan; Data -> binary_to_list(Data) ++ " (" ++ Slogan ++ ")" end. -spec format(io:format(), list()) -> binary(). format(Fmt, Args) -> iolist_to_binary(io_lib:format(Fmt, Args)). %%%=================================================================== %%% Connection stuff %%%=================================================================== -spec idna_to_ascii(binary()) -> binary() | false. idna_to_ascii(<<$[, _/binary>> = Host) -> %% This is an IPv6 address in 'IP-literal' format (as per RFC7622) %% We remove brackets here case binary:last(Host) of $] -> IPv6 = binary:part(Host, {1, size(Host)-2}), case inet:parse_ipv6strict_address(binary_to_list(IPv6)) of {ok, _} -> IPv6; {error, _} -> false end; _ -> false end; idna_to_ascii(Host) -> case inet:parse_address(binary_to_list(Host)) of {ok, _} -> Host; {error, _} -> ejabberd_idna:domain_utf8_to_ascii(Host) end. -spec resolve(string(), state()) -> {ok, [ip_port()]} | network_error(). resolve(Host, State) -> case srv_lookup(Host, State) of {error, _Reason} -> DefaultPort = get_default_port(State), a_lookup([{Host, DefaultPort, false}], State); {ok, HostPorts} -> a_lookup(HostPorts, State) end. -spec srv_lookup(string(), state()) -> {ok, [host_port()]} | network_error(). srv_lookup(_Host, #{xmlns := ?NS_COMPONENT}) -> %% Do not attempt to lookup SRV for component connections {error, nxdomain}; srv_lookup(Host, State) -> %% Only perform SRV lookups for FQDN names case string:chr(Host, $.) of 0 -> {error, nxdomain}; _ -> case inet:parse_address(Host) of {ok, _} -> {error, nxdomain}; {error, _} -> Timeout = get_dns_timeout(State), Retries = get_dns_retries(State), case srv_lookup(Host, State, Timeout, Retries) of {ok, AddrList} -> h_addr_list_to_host_ports(AddrList); {error, _} = Err -> Err end end end. srv_lookup(Host, State, Timeout, Retries) -> TLSAddrs = case is_starttls_available(State) of true -> case srv_lookup("_xmpps-server._tcp." ++ Host, Timeout, Retries) of {ok, HostEnt} -> [{A, true} || A <- HostEnt#hostent.h_addr_list]; {error, _} -> [] end; false -> [] end, case srv_lookup("_xmpp-server._tcp." ++ Host, Timeout, Retries) of {ok, HostEntry} -> Addrs = [{A, false} || A <- HostEntry#hostent.h_addr_list], {ok, TLSAddrs ++ Addrs}; {error, _} when TLSAddrs /= [] -> {ok, TLSAddrs}; {error, _} = Err -> Err end. -spec srv_lookup(string(), timeout(), integer()) -> {ok, inet:hostent()} | network_error(). srv_lookup(_SRVName, _Timeout, Retries) when Retries < 1 -> {error, timeout}; srv_lookup(SRVName, Timeout, Retries) -> case inet_res:getbyname(SRVName, srv, Timeout) of {ok, HostEntry} -> {ok, HostEntry}; {error, timeout} -> srv_lookup(SRVName, Timeout, Retries - 1); {error, _} = Err -> Err end. -spec a_lookup([host_port()], state()) -> {ok, [ip_port()]} | network_error(). a_lookup(HostPorts, State) -> HostPortFamilies = [{Host, Port, TLS, Family} || {Host, Port, TLS} <- HostPorts, Family <- get_address_families(State)], a_lookup(HostPortFamilies, State, [], {error, nxdomain}). -spec a_lookup([{inet:hostname(), inet:port_number(), boolean(), inet:address_family()}], state(), [ip_port()], network_error()) -> {ok, [ip_port()]} | network_error(). a_lookup([{Host, Port, TLS, Family}|HostPortFamilies], State, Acc, Err) -> Timeout = get_dns_timeout(State), Retries = get_dns_retries(State), case a_lookup(Host, Port, TLS, Family, Timeout, Retries) of {error, Reason} -> a_lookup(HostPortFamilies, State, Acc, {error, Reason}); {ok, AddrPorts} -> a_lookup(HostPortFamilies, State, Acc ++ AddrPorts, Err) end; a_lookup([], _State, [], Err) -> Err; a_lookup([], _State, Acc, _) -> {ok, Acc}. -spec a_lookup(inet:hostname(), inet:port_number(), boolean(), inet:address_family(), timeout(), integer()) -> {ok, [ip_port()]} | network_error(). a_lookup(_Host, _Port, _TLS, _Family, _Timeout, Retries) when Retries < 1 -> {error, timeout}; a_lookup(Host, Port, TLS, Family, Timeout, Retries) -> Start = p1_time_compat:monotonic_time(milli_seconds), case inet:gethostbyname(Host, Family, Timeout) of {error, nxdomain} = Err -> %% inet:gethostbyname/3 doesn't return {error, timeout}, %% so we should check if 'nxdomain' is in fact a result %% of a timeout. %% We also cannot use inet_res:gethostbyname/3 because %% it ignores DNS configuration settings (/etc/hosts, etc) End = p1_time_compat:monotonic_time(milli_seconds), if (End - Start) >= Timeout -> a_lookup(Host, Port, TLS, Family, Timeout, Retries - 1); true -> Err end; {error, _} = Err -> Err; {ok, HostEntry} -> host_entry_to_addr_ports(HostEntry, Port, TLS) end. -spec h_addr_list_to_host_ports(h_addr_list()) -> {ok, [host_port()]} | {error, nxdomain}. h_addr_list_to_host_ports(AddrList) -> PrioHostPorts = lists:flatmap( fun({{Priority, Weight, Port, Host}, TLS}) -> N = case Weight of 0 -> 0; _ -> (Weight + 1) * randoms:uniform() end, [{Priority * 65536 - N, Host, Port, TLS}]; (_) -> [] end, AddrList), HostPorts = [{Host, Port, TLS} || {_Priority, Host, Port, TLS} <- lists:usort(PrioHostPorts)], case HostPorts of [] -> {error, nxdomain}; _ -> {ok, HostPorts} end. -spec host_entry_to_addr_ports(inet:hostent(), inet:port_number(), boolean()) -> {ok, [ip_port()]} | {error, nxdomain}. host_entry_to_addr_ports(#hostent{h_addr_list = AddrList}, Port, TLS) -> AddrPorts = lists:flatmap( fun(Addr) -> try get_addr_type(Addr) of _ -> [{Addr, Port, TLS}] catch _:_ -> [] end end, AddrList), case AddrPorts of [] -> {error, nxdomain}; _ -> {ok, AddrPorts} end. -spec connect([ip_port()], state()) -> {ok, term(), ip_port()} | {error, {socket, socket_error_reason()}} | {error, {tls, tls_error_reason()}}. connect(AddrPorts, State) -> Timeout = get_connect_timeout(State), case connect(AddrPorts, Timeout, {error, nxdomain}) of {ok, Socket, {Addr, Port, TLS = true}} -> case starttls(Socket, State) of {ok, TLSSocket} -> {ok, TLSSocket, {Addr, Port, TLS}}; {error, Why} -> {error, {tls, Why}} end; {ok, Socket, {Addr, Port, TLS = false}} -> {ok, Socket, {Addr, Port, TLS}}; {error, Why} -> {error, {socket, Why}} end. -spec connect([ip_port()], timeout(), network_error()) -> {ok, term(), ip_port()} | network_error(). connect([{Addr, Port, TLS}|AddrPorts], Timeout, _) -> Type = get_addr_type(Addr), try xmpp_socket:connect(Addr, Port, [binary, {packet, 0}, {send_timeout, ?TCP_SEND_TIMEOUT}, {send_timeout_close, true}, {active, false}, Type], Timeout) of {ok, Socket} -> {ok, Socket, {Addr, Port, TLS}}; Err -> connect(AddrPorts, Timeout, Err) catch _:badarg -> connect(AddrPorts, Timeout, {error, einval}) end; connect([], _Timeout, Err) -> Err. -spec get_addr_type(inet:ip_address()) -> inet:address_family(). get_addr_type({_, _, _, _}) -> inet; get_addr_type({_, _, _, _, _, _, _, _}) -> inet6. -spec get_dns_timeout(state()) -> timeout(). get_dns_timeout(#{mod := Mod} = State) -> try Mod:dns_timeout(State) catch _:undef -> timer:seconds(10) end. -spec get_dns_retries(state()) -> non_neg_integer(). get_dns_retries(#{mod := Mod} = State) -> try Mod:dns_retries(State) catch _:undef -> 2 end. -spec get_default_port(state()) -> inet:port_number(). get_default_port(#{mod := Mod, xmlns := NS} = State) -> try Mod:default_port(State) catch _:undef when NS == ?NS_SERVER -> 5269; _:undef when NS == ?NS_CLIENT -> 5222 end. -spec get_address_families(state()) -> [inet:address_family()]. get_address_families(#{mod := Mod} = State) -> try Mod:address_families(State) catch _:undef -> [inet, inet6] end. -spec get_connect_timeout(state()) -> timeout(). get_connect_timeout(#{mod := Mod} = State) -> try Mod:connect_timeout(State) catch _:undef -> timer:seconds(10) end. ejabberd-18.01/src/cyrsasl_scram.erl0000644000232200023220000002130013225664356017777 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : cyrsasl_scram.erl %%% Author : Stephen Röttger %%% Purpose : SASL SCRAM authentication %%% Created : 7 Aug 2011 by Stephen Röttger %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(cyrsasl_scram). -author('stephen.roettger@googlemail.com'). -protocol({rfc, 5802}). -export([start/1, stop/0, mech_new/4, mech_step/2, format_error/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -behaviour(cyrsasl). -record(state, {step = 2 :: 2 | 4, stored_key = <<"">> :: binary(), server_key = <<"">> :: binary(), username = <<"">> :: binary(), auth_module :: module(), get_password :: fun((binary()) -> {false | ejabberd_auth:password(), module()}), auth_message = <<"">> :: binary(), client_nonce = <<"">> :: binary(), server_nonce = <<"">> :: binary()}). -define(SALT_LENGTH, 16). -define(NONCE_LENGTH, 16). -type error_reason() :: unsupported_extension | bad_username | not_authorized | saslprep_failed | parser_failed | bad_attribute | nonce_mismatch | bad_channel_binding. -export_type([error_reason/0]). start(_Opts) -> cyrsasl:register_mechanism(<<"SCRAM-SHA-1">>, ?MODULE, scram). stop() -> ok. -spec format_error(error_reason()) -> {atom(), binary()}. format_error(unsupported_extension) -> {'bad-protocol', <<"Unsupported extension">>}; format_error(bad_username) -> {'invalid-authzid', <<"Malformed username">>}; format_error(not_authorized) -> {'not-authorized', <<"Invalid username or password">>}; format_error(saslprep_failed) -> {'not-authorized', <<"SASLprep failed">>}; format_error(parser_failed) -> {'bad-protocol', <<"Response decoding failed">>}; format_error(bad_attribute) -> {'bad-protocol', <<"Malformed or unexpected attribute">>}; format_error(nonce_mismatch) -> {'bad-protocol', <<"Nonce mismatch">>}; format_error(bad_channel_binding) -> {'bad-protocol', <<"Invalid channel binding">>}. mech_new(_Host, GetPassword, _CheckPassword, _CheckPasswordDigest) -> {ok, #state{step = 2, get_password = GetPassword}}. mech_step(#state{step = 2} = State, ClientIn) -> case re:split(ClientIn, <<",">>, [{return, binary}]) of [_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _] when ExtensionAttribute /= <<"">> -> {error, unsupported_extension}; [CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _] when (CBind == <<"y">>) or (CBind == <<"n">>) -> case parse_attribute(UserNameAttribute) of {error, Reason} -> {error, Reason}; {_, EscapedUserName} -> case unescape_username(EscapedUserName) of error -> {error, bad_username}; UserName -> case parse_attribute(ClientNonceAttribute) of {$r, ClientNonce} -> {Pass, AuthModule} = (State#state.get_password)(UserName), LPass = if is_binary(Pass) -> jid:resourceprep(Pass); true -> Pass end, if Pass == false -> {error, not_authorized, UserName}; LPass == error -> {error, saslprep_failed, UserName}; true -> {StoredKey, ServerKey, Salt, IterationCount} = if is_record(Pass, scram) -> {base64:decode(Pass#scram.storedkey), base64:decode(Pass#scram.serverkey), base64:decode(Pass#scram.salt), Pass#scram.iterationcount}; true -> TempSalt = randoms:bytes(?SALT_LENGTH), SaltedPassword = scram:salted_password(Pass, TempSalt, ?SCRAM_DEFAULT_ITERATION_COUNT), {scram:stored_key(scram:client_key(SaltedPassword)), scram:server_key(SaltedPassword), TempSalt, ?SCRAM_DEFAULT_ITERATION_COUNT} end, ClientFirstMessageBare = str:substr(ClientIn, str:str(ClientIn, <<"n=">>)), ServerNonce = base64:encode(randoms:bytes(?NONCE_LENGTH)), ServerFirstMessage = iolist_to_binary( ["r=", ClientNonce, ServerNonce, ",", "s=", base64:encode(Salt), ",", "i=", integer_to_list(IterationCount)]), {continue, ServerFirstMessage, State#state{step = 4, stored_key = StoredKey, server_key = ServerKey, auth_module = AuthModule, auth_message = <>, client_nonce = ClientNonce, server_nonce = ServerNonce, username = UserName}} end; _ -> {error, bad_attribute} end end end; _Else -> {error, parser_failed} end; mech_step(#state{step = 4} = State, ClientIn) -> case str:tokens(ClientIn, <<",">>) of [GS2ChannelBindingAttribute, NonceAttribute, ClientProofAttribute] -> case parse_attribute(GS2ChannelBindingAttribute) of {$c, CVal} -> ChannelBindingSupport = try binary:first(base64:decode(CVal)) catch _:badarg -> 0 end, if (ChannelBindingSupport == $n) or (ChannelBindingSupport == $y) -> Nonce = <<(State#state.client_nonce)/binary, (State#state.server_nonce)/binary>>, case parse_attribute(NonceAttribute) of {$r, CompareNonce} when CompareNonce == Nonce -> case parse_attribute(ClientProofAttribute) of {$p, ClientProofB64} -> ClientProof = try base64:decode(ClientProofB64) catch _:badarg -> <<>> end, AuthMessage = iolist_to_binary( [State#state.auth_message, ",", str:substr(ClientIn, 1, str:str(ClientIn, <<",p=">>) - 1)]), ClientSignature = scram:client_signature(State#state.stored_key, AuthMessage), ClientKey = scram:client_key(ClientProof, ClientSignature), CompareStoredKey = scram:stored_key(ClientKey), if CompareStoredKey == State#state.stored_key -> ServerSignature = scram:server_signature(State#state.server_key, AuthMessage), {ok, [{username, State#state.username}, {auth_module, State#state.auth_module}, {authzid, State#state.username}], <<"v=", (base64:encode(ServerSignature))/binary>>}; true -> {error, not_authorized, State#state.username} end; _ -> {error, bad_attribute} end; {$r, _} -> {error, nonce_mismatch}; _ -> {error, bad_attribute} end; true -> {error, bad_channel_binding} end; _ -> {error, bad_attribute} end; _ -> {error, parser_failed} end. parse_attribute(<>) when Val /= <<>> -> case is_alpha(Name) of true -> {Name, Val}; false -> {error, bad_attribute} end; parse_attribute(_) -> {error, bad_attribute}. unescape_username(<<"">>) -> <<"">>; unescape_username(EscapedUsername) -> Pos = str:str(EscapedUsername, <<"=">>), if Pos == 0 -> EscapedUsername; true -> Start = str:substr(EscapedUsername, 1, Pos - 1), End = str:substr(EscapedUsername, Pos), EndLen = byte_size(End), if EndLen < 3 -> error; true -> case str:substr(End, 1, 3) of <<"=2C">> -> <>; <<"=3D">> -> < function() | [atom()]. %% @type macro() = {macro_key(), macro_value()} %% @type macro_key() = atom(). %% The atom must have all characters in uppercase. %% @type macro_value() = term(). start() -> ConfigFile = get_ejabberd_config_path(), ?INFO_MSG("Loading configuration from ~s", [ConfigFile]), p1_options:start_link(ejabberd_options), p1_options:start_link(ejabberd_db_modules), State1 = load_file(ConfigFile), UnixTime = p1_time_compat:system_time(seconds), SharedKey = case erlang:get_cookie() of nocookie -> str:sha(randoms:get_string()); Cookie -> str:sha(misc:atom_to_binary(Cookie)) end, State2 = set_option({node_start, global}, UnixTime, State1), State3 = set_option({shared_key, global}, SharedKey, State2), set_opts(State3). %% When starting ejabberd for testing, we sometimes want to start a %% subset of hosts from the one define in the config file. %% This function override the host list read from config file by the %% one we provide. %% Hosts to start are defined in an ejabberd application environment %% variable 'hosts' to make it easy to ignore some host in config %% file. hosts_to_start(State) -> case application:get_env(ejabberd, hosts) of undefined -> %% Start all hosts as defined in config file State; {ok, Hosts} -> set_hosts_in_options(Hosts, State) end. %% @private %% At the moment, these functions are mainly used to setup unit tests. -spec start(Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok. start(Hosts, Opts) -> p1_options:start_link(ejabberd_options), p1_options:start_link(ejabberd_db_modules), set_opts(set_hosts_in_options(Hosts, #state{opts = Opts})), ok. %% @doc Get the filename of the ejabberd configuration file. %% The filename can be specified with: erl -config "/path/to/ejabberd.yml". %% It can also be specified with the environtment variable EJABBERD_CONFIG_PATH. %% If not specified, the default value 'ejabberd.yml' is assumed. %% @spec () -> string() get_ejabberd_config_path() -> case get_env_config() of {ok, Path} -> Path; undefined -> case os:getenv("EJABBERD_CONFIG_PATH") of false -> ?CONFIG_PATH; Path -> Path end end. -spec get_env_config() -> {ok, string()} | undefined. get_env_config() -> %% First case: the filename can be specified with: erl -config "/path/to/ejabberd.yml". case application:get_env(ejabberd, config) of R = {ok, _Path} -> R; undefined -> %% Second case for embbeding ejabberd in another app, for example for Elixir: %% config :ejabberd, %% file: "config/ejabberd.yml" application:get_env(ejabberd, file) end. %% @doc Read the ejabberd configuration file. %% It also includes additional configuration files and replaces macros. %% This function will crash if finds some error in the configuration file. %% @spec (File::string()) -> #state{} read_file(File) -> read_file(File, [{replace_macros, true}, {include_files, true}, {include_modules_configs, true}]). read_file(File, Opts) -> Terms1 = case is_elixir_enabled() of true -> case 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(File) of true -> 'Elixir.Ejabberd.Config':init(File), 'Elixir.Ejabberd.Config':get_ejabberd_opts(); false -> get_plain_terms_file(File, Opts) end; false -> get_plain_terms_file(File, Opts) end, Terms_macros = case proplists:get_bool(replace_macros, Opts) of true -> replace_macros(Terms1); false -> Terms1 end, Terms = transform_terms(Terms_macros), State = lists:foldl(fun search_hosts/2, #state{}, Terms), {Head, Tail} = lists:partition( fun({host_config, _}) -> false; ({append_host_config, _}) -> false; (_) -> true end, Terms), State1 = lists:foldl(fun process_term/2, State, Head ++ Tail), State1#state{opts = compact(State1#state.opts)}. -spec load_file(string()) -> #state{}. load_file(File) -> State0 = read_file(File), State1 = hosts_to_start(State0), AllMods = get_modules(), init_module_db_table(AllMods), ModOpts = get_modules_with_options(AllMods), validate_opts(State1, ModOpts). -spec reload_file() -> ok. reload_file() -> Config = get_ejabberd_config_path(), OldHosts = get_myhosts(), State = load_file(Config), set_opts(State), NewHosts = get_myhosts(), lists:foreach( fun(Host) -> ejabberd_hooks:run(host_up, [Host]) end, NewHosts -- OldHosts), lists:foreach( fun(Host) -> ejabberd_hooks:run(host_down, [Host]) end, OldHosts -- NewHosts), ejabberd_hooks:run(config_reloaded, []). -spec convert_to_yaml(file:filename()) -> ok | {error, any()}. convert_to_yaml(File) -> convert_to_yaml(File, stdout). -spec convert_to_yaml(file:filename(), stdout | file:filename()) -> ok | {error, any()}. convert_to_yaml(File, Output) -> State = read_file(File, [{include_files, false}]), Opts = [{K, V} || #local_config{key = K, value = V} <- State#state.opts], {GOpts, HOpts} = split_by_hosts(Opts), NewOpts = GOpts ++ lists:map( fun({Host, Opts1}) -> {host_config, [{Host, Opts1}]} end, HOpts), Data = fast_yaml:encode(lists:reverse(NewOpts)), case Output of stdout -> io:format("~s~n", [Data]); FileName -> file:write_file(FileName, Data) end. %% Some Erlang apps expects env parameters to be list and not binary. %% For example, Mnesia is not able to start if mnesia dir is passed as a binary. %% However, binary is most common on Elixir, so it is easy to make a setup mistake. -spec env_binary_to_list(atom(), atom()) -> {ok, any()}|undefined. env_binary_to_list(Application, Parameter) -> %% Application need to be loaded to allow setting parameters application:load(Application), case application:get_env(Application, Parameter) of {ok, Val} when is_binary(Val) -> BVal = binary_to_list(Val), application:set_env(Application, Parameter, BVal), {ok, BVal}; Other -> Other end. %% @doc Read an ejabberd configuration file and return the terms. %% Input is an absolute or relative path to an ejabberd config file. %% Returns a list of plain terms, %% in which the options 'include_config_file' were parsed %% and the terms in those files were included. %% @spec(iolist()) -> [term()] get_plain_terms_file(File, Opts) when is_binary(File) -> get_plain_terms_file(binary_to_list(File), Opts); get_plain_terms_file(File1, Opts) -> File = get_absolute_path(File1), DontStopOnError = lists:member(dont_halt_on_error, Opts), case consult(File) of {ok, Terms} -> BinTerms1 = strings_to_binary(Terms), ModInc = case proplists:get_bool(include_modules_configs, Opts) of true -> Files = [{filename:rootname(filename:basename(F)), F} || F <- filelib:wildcard(ext_mod:config_dir() ++ "/*.{yml,yaml}") ++ filelib:wildcard(ext_mod:modules_dir() ++ "/*/conf/*.{yml,yaml}")], [proplists:get_value(F,Files) || F <- proplists:get_keys(Files)]; _ -> [] end, BinTerms = BinTerms1 ++ [{include_config_file, list_to_binary(V)} || V <- ModInc], case proplists:get_bool(include_files, Opts) of true -> include_config_files(BinTerms); false -> BinTerms end; {error, enoent, Reason} -> case DontStopOnError of true -> ?WARNING_MSG(Reason, []), []; _ -> ?ERROR_MSG(Reason, []), exit_or_halt(Reason) end; {error, Reason} -> ?ERROR_MSG(Reason, []), case DontStopOnError of true -> []; _ -> exit_or_halt(Reason) end end. consult(File) -> case filename:extension(File) of Ex when (Ex == ".yml") or (Ex == ".yaml") -> case fast_yaml:decode_from_file(File, [plain_as_atom]) of {ok, []} -> {ok, []}; {ok, [Document|_]} -> {ok, parserl(Document)}; {error, Err} -> Msg1 = "Cannot load " ++ File ++ ": ", Msg2 = fast_yaml:format_error(Err), case Err of enoent -> {error, enoent, Msg1 ++ Msg2}; _ -> {error, Msg1 ++ Msg2} end end; _ -> case file:consult(File) of {ok, Terms} -> {ok, Terms}; {error, {LineNumber, erl_parse, _ParseMessage} = Reason} -> {error, describe_config_problem(File, Reason, LineNumber)}; {error, Reason} -> case Reason of enoent -> {error, enoent, describe_config_problem(File, Reason)}; _ -> {error, describe_config_problem(File, Reason)} end end end. parserl(<<"> ", Term/binary>>) -> {ok, A2, _} = erl_scan:string(binary_to_list(Term)), {ok, A3} = erl_parse:parse_term(A2), A3; parserl({A, B}) -> {parserl(A), parserl(B)}; parserl([El|Tail]) -> [parserl(El) | parserl(Tail)]; parserl(Other) -> Other. %% @doc Convert configuration filename to absolute path. %% Input is an absolute or relative path to an ejabberd configuration file. %% And returns an absolute path to the configuration file. %% @spec (string()) -> string() get_absolute_path(File) -> case filename:pathtype(File) of absolute -> File; relative -> {ok, Dir} = file:get_cwd(), filename:absname_join(Dir, File); volumerelative -> filename:absname(File) end. search_hosts(Term, State) -> case Term of {host, Host} -> if State#state.hosts == [] -> set_hosts_in_options([Host], State); true -> ?ERROR_MSG("Can't load config file: " "too many hosts definitions", []), exit("too many hosts definitions") end; {hosts, Hosts} -> if State#state.hosts == [] -> set_hosts_in_options(Hosts, State); true -> ?ERROR_MSG("Can't load config file: " "too many hosts definitions", []), exit("too many hosts definitions") end; _ -> State end. set_hosts_in_options(Hosts, State) -> PrepHosts = normalize_hosts(Hosts), NewOpts = lists:filter(fun({local_config,{hosts,global},_}) -> false; (_) -> true end, State#state.opts), set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts, opts = NewOpts}). normalize_hosts(Hosts) -> normalize_hosts(Hosts,[]). normalize_hosts([], PrepHosts) -> lists:reverse(PrepHosts); normalize_hosts([Host|Hosts], PrepHosts) -> case jid:nodeprep(iolist_to_binary(Host)) of error -> ?ERROR_MSG("Can't load config file: " "invalid host name [~p]", [Host]), exit("invalid hostname"); PrepHost -> normalize_hosts(Hosts, [PrepHost|PrepHosts]) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Errors reading the config file describe_config_problem(Filename, Reason) -> Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename), Text2 = lists:flatten(" : " ++ file:format_error(Reason)), ExitText = Text1 ++ Text2, ExitText. describe_config_problem(Filename, Reason, LineNumber) -> Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename), Text2 = lists:flatten(" approximately in the line " ++ file:format_error(Reason)), ExitText = Text1 ++ Text2, Lines = get_config_lines(Filename, LineNumber, 10, 3), ?ERROR_MSG("The following lines from your configuration file might be" " relevant to the error: ~n~s", [Lines]), ExitText. get_config_lines(Filename, TargetNumber, PreContext, PostContext) -> {ok, Fd} = file:open(Filename, [read]), LNumbers = lists:seq(TargetNumber-PreContext, TargetNumber+PostContext), NextL = io:get_line(Fd, no_prompt), R = get_config_lines2(Fd, NextL, 1, LNumbers, []), file:close(Fd), R. get_config_lines2(_Fd, eof, _CurrLine, _LNumbers, R) -> lists:reverse(R); get_config_lines2(_Fd, _NewLine, _CurrLine, [], R) -> lists:reverse(R); get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(Data) -> NextL = io:get_line(Fd, no_prompt), if CurrLine >= NextWanted -> Line2 = [integer_to_list(CurrLine), ": " | Data], get_config_lines2(Fd, NextL, CurrLine+1, LNumbers, [Line2 | R]); true -> get_config_lines2(Fd, NextL, CurrLine+1, [NextWanted | LNumbers], R) end. %% If ejabberd isn't yet running in this node, then halt the node exit_or_halt(ExitText) -> case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of [] -> timer:sleep(1000), halt(string:substr(ExitText, 1, 199)); [_] -> exit(ExitText) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Support for 'include_config_file' get_config_option_key(Name, Val) -> if Name == listen -> case Val of {{Port, IP, Trans}, _Mod, _Opts} -> {Port, IP, Trans}; {{Port, Trans}, _Mod, _Opts} when Trans == tcp; Trans == udp -> {Port, {0,0,0,0}, Trans}; {{Port, IP}, _Mod, _Opts} -> {Port, IP, tcp}; {Port, _Mod, _Opts} -> {Port, {0,0,0,0}, tcp}; V when is_list(V) -> lists:foldl( fun({port, Port}, {_, IP, T}) -> {Port, IP, T}; ({ip, IP}, {Port, _, T}) -> {Port, IP, T}; ({transport, T}, {Port, IP, _}) -> {Port, IP, T}; (_, Res) -> Res end, {5222, {0,0,0,0}, tcp}, Val) end; is_tuple(Val) -> element(1, Val); true -> Val end. maps_to_lists(IMap) -> maps:fold(fun(Name, Map, Res) when Name == host_config orelse Name == append_host_config -> [{Name, [{Host, maps_to_lists(SMap)} || {Host,SMap} <- maps:values(Map)]} | Res]; (Name, Map, Res) when is_map(Map) -> [{Name, maps:values(Map)} | Res]; (Name, Val, Res) -> [{Name, Val} | Res] end, [], IMap). merge_configs(Terms, ResMap) -> lists:foldl(fun({Name, Val}, Map) when is_list(Val), Name =/= auth_method -> Old = maps:get(Name, Map, #{}), New = lists:foldl(fun(SVal, OMap) -> NVal = if Name == host_config orelse Name == append_host_config -> {Host, Opts} = SVal, {_, SubMap} = maps:get(Host, OMap, {Host, #{}}), {Host, merge_configs(Opts, SubMap)}; true -> SVal end, maps:put(get_config_option_key(Name, SVal), NVal, OMap) end, Old, Val), maps:put(Name, New, Map); ({Name, Val}, Map) -> maps:put(Name, Val, Map) end, ResMap, Terms). %% @doc Include additional configuration files in the list of terms. %% @spec ([term()]) -> [term()] include_config_files(Terms) -> {FileOpts, Terms1} = lists:mapfoldl( fun({include_config_file, _} = T, Ts) -> {[transform_include_option(T)], Ts}; ({include_config_file, _, _} = T, Ts) -> {[transform_include_option(T)], Ts}; (T, Ts) -> {[], [T|Ts]} end, [], Terms), Terms2 = lists:flatmap( fun({File, Opts}) -> include_config_file(File, Opts) end, lists:flatten(FileOpts)), M1 = merge_configs(Terms1, #{}), M2 = merge_configs(Terms2, M1), maps_to_lists(M2). transform_include_option({include_config_file, File}) when is_list(File) -> case is_string(File) of true -> {File, []}; false -> File end; transform_include_option({include_config_file, Filename}) -> {Filename, []}; transform_include_option({include_config_file, Filename, Options}) -> {Filename, Options}. include_config_file(Filename, Options) -> Included_terms = get_plain_terms_file(Filename, [{include_files, true}, dont_halt_on_error]), Disallow = proplists:get_value(disallow, Options, []), Included_terms2 = delete_disallowed(Disallow, Included_terms), Allow_only = proplists:get_value(allow_only, Options, all), keep_only_allowed(Allow_only, Included_terms2). %% @doc Filter from the list of terms the disallowed. %% Returns a sublist of Terms without the ones which first element is %% included in Disallowed. %% @spec (Disallowed::[atom()], Terms::[term()]) -> [term()] delete_disallowed(Disallowed, Terms) -> lists:foldl( fun(Dis, Ldis) -> delete_disallowed2(Dis, Ldis) end, Terms, Disallowed). delete_disallowed2(Disallowed, [H|T]) -> case element(1, H) of Disallowed -> ?WARNING_MSG("The option '~p' is disallowed, " "and will not be accepted", [Disallowed]), delete_disallowed2(Disallowed, T); _ -> [H|delete_disallowed2(Disallowed, T)] end; delete_disallowed2(_, []) -> []. %% @doc Keep from the list only the allowed terms. %% Returns a sublist of Terms with only the ones which first element is %% included in Allowed. %% @spec (Allowed::[atom()], Terms::[term()]) -> [term()] keep_only_allowed(all, Terms) -> Terms; keep_only_allowed(Allowed, Terms) -> {As, NAs} = lists:partition( fun(Term) -> lists:member(element(1, Term), Allowed) end, Terms), [?WARNING_MSG("This option is not allowed, " "and will not be accepted:~n~p", [NA]) || NA <- NAs], As. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Support for Macro %% @doc Replace the macros with their defined values. %% @spec (Terms::[term()]) -> [term()] replace_macros(Terms) -> {TermsOthers, Macros} = split_terms_macros(Terms), replace(TermsOthers, Macros). %% @doc Split Terms into normal terms and macro definitions. %% @spec (Terms) -> {Terms, Macros} %% Terms = [term()] %% Macros = [macro()] split_terms_macros(Terms) -> lists:foldl( fun(Term, {TOs, Ms}) -> case Term of {define_macro, Key, Value} -> case is_correct_macro({Key, Value}) of true -> {TOs, Ms++[{Key, Value}]}; false -> exit({macro_not_properly_defined, Term}) end; {define_macro, KeyVals} -> case lists:all(fun is_correct_macro/1, KeyVals) of true -> {TOs, Ms ++ KeyVals}; false -> exit({macros_not_properly_defined, Term}) end; Term -> {TOs ++ [Term], Ms} end end, {[], []}, Terms). is_correct_macro({Key, _Val}) -> is_atom(Key) and is_all_uppercase(Key); is_correct_macro(_) -> false. %% @doc Recursively replace in Terms macro usages with the defined value. %% @spec (Terms, Macros) -> Terms %% Terms = [term()] %% Macros = [macro()] replace([], _) -> []; replace([Term|Terms], Macros) -> [replace_term(Term, Macros) | replace(Terms, Macros)]; replace(Term, Macros) -> replace_term(Term, Macros). replace_term(Key, Macros) when is_atom(Key) -> case is_all_uppercase(Key) of true -> case proplists:get_value(Key, Macros) of undefined -> exit({undefined_macro, Key}); Value -> Value end; false -> Key end; replace_term({use_macro, Key, Value}, Macros) -> proplists:get_value(Key, Macros, Value); replace_term(Term, Macros) when is_list(Term) -> replace(Term, Macros); replace_term(Term, Macros) when is_tuple(Term) -> List = tuple_to_list(Term), List2 = replace(List, Macros), list_to_tuple(List2); replace_term(Term, _) -> Term. is_all_uppercase(Atom) -> String = erlang:atom_to_list(Atom), lists:all(fun(C) when C >= $a, C =< $z -> false; (_) -> true end, String). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Process terms process_term(Term, State) -> case Term of {host_config, HostTerms} -> lists:foldl( fun({Host, Terms}, AccState) -> lists:foldl(fun(T, S) -> process_host_term(T, Host, S, set) end, AccState, Terms) end, State, HostTerms); {append_host_config, HostTerms} -> lists:foldl( fun({Host, Terms}, AccState) -> lists:foldl(fun(T, S) -> process_host_term(T, Host, S, append) end, AccState, Terms) end, State, HostTerms); _ -> process_host_term(Term, global, State, set) end. process_host_term(Term, Host, State, Action) -> case Term of {modules, Modules} when Action == set -> set_option({modules, Host}, replace_modules(Modules), State); {modules, Modules} when Action == append -> append_option({modules, Host}, replace_modules(Modules), State); {host, _} -> State; {hosts, _} -> State; {Opt, Val} when Action == set -> set_option({rename_option(Opt), Host}, change_val(Opt, Val), State); {Opt, Val} when Action == append -> append_option({rename_option(Opt), Host}, change_val(Opt, Val), State); Opt -> ?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]), State end. rename_option(Option) when is_atom(Option) -> case atom_to_list(Option) of "odbc_" ++ T -> NewOption = list_to_atom("sql_" ++ T), ?WARNING_MSG("Option '~s' is obsoleted, use '~s' instead", [Option, NewOption]), NewOption; _ -> Option end; rename_option(Option) -> Option. change_val(auth_method, Val) -> prepare_opt_val(auth_method, Val, fun(V) -> L = if is_list(V) -> V; true -> [V] end, lists:map( fun(odbc) -> sql; (internal) -> mnesia; (A) when is_atom(A) -> A end, L) end, [mnesia]); change_val(_Opt, Val) -> Val. set_option(Opt, Val, State) -> State#state{opts = [#local_config{key = Opt, value = Val} | State#state.opts]}. append_option({Opt, Host}, Val, State) -> GlobalVals = lists:flatmap( fun(#local_config{key = {O, global}, value = V}) when O == Opt -> if is_list(V) -> V; true -> [V] end; (_) -> [] end, State#state.opts), NewVal = if is_list(Val) -> Val ++ GlobalVals; true -> [Val|GlobalVals] end, set_option({Opt, Host}, NewVal, State). set_opts(State) -> Opts = State#state.opts, ets:select_delete(ejabberd_options, ets:fun2ms( fun({{node_start, _}, _}) -> false; ({{shared_key, _}, _}) -> false; (_) -> true end)), lists:foreach( fun(#local_config{key = {Opt, Host}, value = Val}) -> p1_options:insert(ejabberd_options, Opt, Host, Val) end, Opts), p1_options:compile(ejabberd_options), set_log_level(). set_log_level() -> Level = get_option(loglevel, 4), ejabberd_logger:set(Level). add_global_option(Opt, Val) -> add_option(Opt, Val). add_local_option(Opt, Val) -> add_option(Opt, Val). add_option(Opt, Val) when is_atom(Opt) -> add_option({Opt, global}, Val); add_option({Opt, Host}, Val) -> p1_options:insert(ejabberd_options, Opt, Host, Val), p1_options:compile(ejabberd_options). -spec prepare_opt_val(any(), any(), check_fun(), any()) -> any(). prepare_opt_val(Opt, Val, F, Default) -> Call = case F of {Mod, Fun} -> fun() -> Mod:Fun(Val) end; _ -> fun() -> F(Val) end end, try Call() of Res -> Res catch {replace_with, NewRes} -> NewRes; {invalid_syntax, Error} -> ?WARNING_MSG("incorrect value '~s' of option '~s', " "using '~s' as fallback: ~s", [format_term(Val), format_term(Opt), format_term(Default), Error]), Default; _:_ -> ?WARNING_MSG("incorrect value '~s' of option '~s', " "using '~s' as fallback", [format_term(Val), format_term(Opt), format_term(Default)]), Default end. -type check_fun() :: fun((any()) -> any()) | {module(), atom()}. -spec get_global_option(any(), check_fun()) -> any(). get_global_option(Opt, _) -> get_option(Opt, undefined). -spec get_global_option(any(), check_fun(), any()) -> any(). get_global_option(Opt, _, Default) -> get_option(Opt, Default). -spec get_local_option(any(), check_fun()) -> any(). get_local_option(Opt, _) -> get_option(Opt, undefined). -spec get_local_option(any(), check_fun(), any()) -> any(). get_local_option(Opt, _, Default) -> get_option(Opt, Default). -spec get_option(any()) -> any(). get_option(Opt) -> get_option(Opt, undefined). -spec get_option(any(), check_fun(), any()) -> any(). get_option(Opt, _, Default) -> get_option(Opt, Default). -spec get_option(any(), check_fun() | any()) -> any(). get_option(Opt, F) when is_function(F) -> get_option(Opt, undefined); get_option(Opt, Default) when is_atom(Opt) -> get_option({Opt, global}, Default); get_option(Opt, Default) -> {Key, Host} = case Opt of {O, global} when is_atom(O) -> Opt; {O, H} when is_atom(O), is_binary(H) -> Opt; _ -> ?WARNING_MSG("Option ~p has invalid (outdated?) " "format. This is likely a bug", [Opt]), {undefined, global} end, case ejabberd_options:is_known(Key) of true -> case ejabberd_options:Key(Host) of {ok, Val} -> Val; undefined -> Default end; false -> Default end. -spec has_option(atom() | {atom(), global | binary()}) -> any(). has_option(Opt) -> get_option(Opt) /= undefined. init_module_db_table(Modules) -> %% Dirty hack for mod_pubsub p1_options:insert(ejabberd_db_modules, mod_pubsub, mnesia, true), p1_options:insert(ejabberd_db_modules, mod_pubsub, sql, true), lists:foreach( fun(M) -> case re:split(atom_to_list(M), "_", [{return, list}]) of [_] -> ok; Parts -> [H|T] = lists:reverse(Parts), Suffix = list_to_atom(H), BareMod = list_to_atom(string:join(lists:reverse(T), "_")), case is_behaviour(BareMod, M) of true -> p1_options:insert(ejabberd_db_modules, BareMod, Suffix, true); false -> ok end end end, Modules), p1_options:compile(ejabberd_db_modules). is_behaviour(Behav, Mod) -> try Mod:module_info(attributes) of [] -> %% Stripped module? true; Attrs -> lists:any( fun({behaviour, L}) -> lists:member(Behav, L); ({behavior, L}) -> lists:member(Behav, L); (_) -> false end, Attrs) catch _:_ -> true end. -spec v_db(module(), atom()) -> atom(). v_db(Mod, internal) -> v_db(Mod, mnesia); v_db(Mod, odbc) -> v_db(Mod, sql); v_db(Mod, Type) -> case ejabberd_db_modules:is_known(Mod) of true -> case ejabberd_db_modules:Mod(Type) of {ok, _} -> Type; _ -> erlang:error(badarg) end; false -> erlang:error(badarg) end. -spec v_dbs(module()) -> [atom()]. v_dbs(Mod) -> ejabberd_db_modules:get_scope(Mod). -spec v_dbs_mods(module()) -> [module()]. v_dbs_mods(Mod) -> lists:map(fun(M) -> binary_to_atom(<<(atom_to_binary(Mod, utf8))/binary, "_", (atom_to_binary(M, utf8))/binary>>, utf8) end, v_dbs(Mod)). -spec default_db(module()) -> atom(). default_db(Module) -> default_db(global, Module). -spec default_db(binary() | global, module()) -> atom(). default_db(Host, Module) -> default_db(default_db, Host, Module). -spec default_ram_db(module()) -> atom(). default_ram_db(Module) -> default_ram_db(global, Module). -spec default_ram_db(binary() | global, module()) -> atom(). default_ram_db(Host, Module) -> default_db(default_ram_db, Host, Module). -spec default_db(default_db | default_ram_db, binary() | global, module()) -> atom(). default_db(Opt, Host, Module) -> case get_option({Opt, Host}) of undefined -> mnesia; DBType -> try v_db(Module, DBType) catch error:badarg -> ?WARNING_MSG("Module '~s' doesn't support database '~s' " "defined in option '~s', using " "'mnesia' as fallback", [Module, DBType, Opt]), mnesia end end. get_modules() -> {ok, Mods} = application:get_key(ejabberd, modules), ExtMods = [Name || {Name, _Details} <- ext_mod:installed()], case application:get_env(ejabberd, external_beams) of {ok, Path} -> case lists:member(Path, code:get_path()) of true -> ok; false -> code:add_patha(Path) end, Beams = filelib:wildcard(filename:join(Path, "*\.beam")), CustMods = [list_to_atom(filename:rootname(filename:basename(Beam))) || Beam <- Beams], CustMods ++ ExtMods ++ Mods; _ -> ExtMods ++ Mods end. get_modules_with_options(Modules) -> lists:foldl( fun(Mod, D) -> case is_behaviour(?MODULE, Mod) orelse Mod == ?MODULE of true -> try Mod:opt_type('') of Opts when is_list(Opts) -> lists:foldl( fun(Opt, Acc) -> dict:append(Opt, Mod, Acc) end, D, Opts) catch _:undef -> D end; false -> D end end, dict:new(), Modules). validate_opts(#state{opts = Opts} = State, ModOpts) -> NewOpts = lists:filtermap( fun(#local_config{key = {Opt, _Host}, value = Val} = In) -> case dict:find(Opt, ModOpts) of {ok, [Mod|_]} -> VFun = Mod:opt_type(Opt), try VFun(Val) of NewVal -> {true, In#local_config{value = NewVal}} catch {invalid_syntax, Error} -> ?ERROR_MSG("ignoring option '~s' with " "invalid value: ~p: ~s", [Opt, Val, Error]), false; _:_ -> ?ERROR_MSG("ignoring option '~s' with " "invalid value: ~p", [Opt, Val]), false end; _ -> ?ERROR_MSG("unknown option '~s' will be likely" " ignored", [Opt]), true end end, Opts), State#state{opts = NewOpts}. -spec get_vh_by_auth_method(atom()) -> [binary()]. %% Return the list of hosts with a given auth method get_vh_by_auth_method(AuthMethod) -> Hosts = ejabberd_options:get_scope(auth_method), get_vh_by_auth_method(AuthMethod, Hosts, []). get_vh_by_auth_method(Method, [Host|Hosts], Result) -> Methods = get_option({auth_method, Host}, []), case lists:member(Method, Methods) of true when Host == global -> get_myhosts(); true -> get_vh_by_auth_method(Method, Hosts, [Host|Result]); false -> get_vh_by_auth_method(Method, Hosts, Result) end; get_vh_by_auth_method(_, [], Result) -> Result. %% @spec (Path::string()) -> true | false is_file_readable(Path) -> case file:read_file_info(Path) of {ok, FileInfo} -> case {FileInfo#file_info.type, FileInfo#file_info.access} of {regular, read} -> true; {regular, read_write} -> true; _ -> false end; {error, _Reason} -> false end. get_version() -> case application:get_key(ejabberd, vsn) of undefined -> ""; {ok, Vsn} -> list_to_binary(Vsn) end. -spec get_myhosts() -> [binary()]. get_myhosts() -> get_option(hosts). -spec get_mylang() -> binary(). get_mylang() -> get_lang(global). -spec get_lang(global | binary()) -> binary(). get_lang(Host) -> get_option({language, Host}, <<"en">>). replace_module(mod_announce_odbc) -> {mod_announce, sql}; replace_module(mod_blocking_odbc) -> {mod_blocking, sql}; replace_module(mod_caps_odbc) -> {mod_caps, sql}; replace_module(mod_irc_odbc) -> {mod_irc, sql}; replace_module(mod_last_odbc) -> {mod_last, sql}; replace_module(mod_muc_odbc) -> {mod_muc, sql}; replace_module(mod_offline_odbc) -> {mod_offline, sql}; replace_module(mod_privacy_odbc) -> {mod_privacy, sql}; replace_module(mod_private_odbc) -> {mod_private, sql}; replace_module(mod_roster_odbc) -> {mod_roster, sql}; replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, sql}; replace_module(mod_vcard_odbc) -> {mod_vcard, sql}; replace_module(mod_vcard_ldap) -> {mod_vcard, ldap}; replace_module(mod_vcard_xupdate_odbc) -> mod_vcard_xupdate; replace_module(mod_pubsub_odbc) -> {mod_pubsub, sql}; replace_module(mod_http_bind) -> mod_bosh; replace_module(Module) -> case is_elixir_module(Module) of true -> expand_elixir_module(Module); false -> Module end. replace_modules(Modules) -> lists:map( fun({Module, Opts}) -> case replace_module(Module) of {NewModule, DBType} -> emit_deprecation_warning(Module, NewModule, DBType), NewOpts = [{db_type, DBType} | lists:keydelete(db_type, 1, Opts)], {NewModule, transform_module_options(Module, NewOpts)}; NewModule -> if Module /= NewModule -> emit_deprecation_warning(Module, NewModule); true -> ok end, {NewModule, transform_module_options(Module, Opts)} end end, Modules). %% Elixir module naming %% ==================== -ifdef(ELIXIR_ENABLED). is_elixir_enabled() -> true. -else. is_elixir_enabled() -> false. -endif. is_using_elixir_config() -> case is_elixir_enabled() of true -> Config = get_ejabberd_config_path(), 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config); false -> false end. %% If module name start with uppercase letter, this is an Elixir module: is_elixir_module(Module) -> case atom_to_list(Module) of [H|_] when H >= 65, H =< 90 -> true; _ ->false end. %% We assume we know this is an elixir module expand_elixir_module(Module) -> case atom_to_list(Module) of %% Module name already specified as an Elixir from Erlang module name "Elixir." ++ _ -> Module; %% if start with uppercase letter, this is an Elixir module: Append 'Elixir.' to module name. ModuleString -> list_to_atom("Elixir." ++ ModuleString) end. strings_to_binary([]) -> []; strings_to_binary(L) when is_list(L) -> case is_string(L) of true -> list_to_binary(L); false -> strings_to_binary1(L) end; strings_to_binary({A, B, C, D}) when is_integer(A), is_integer(B), is_integer(C), is_integer(D) -> {A, B, C ,D}; strings_to_binary(T) when is_tuple(T) -> list_to_tuple(strings_to_binary1(tuple_to_list(T))); strings_to_binary(X) -> X. strings_to_binary1([El|L]) -> [strings_to_binary(El)|strings_to_binary1(L)]; strings_to_binary1([]) -> []; strings_to_binary1(T) -> T. is_string([C|T]) when (C >= 0) and (C =< 255) -> is_string(T); is_string([]) -> true; is_string(_) -> false. binary_to_strings(B) when is_binary(B) -> binary_to_list(B); binary_to_strings([H|T]) -> [binary_to_strings(H)|binary_to_strings(T)]; binary_to_strings(T) when is_tuple(T) -> list_to_tuple(binary_to_strings(tuple_to_list(T))); binary_to_strings(T) -> T. format_term(Bin) when is_binary(Bin) -> io_lib:format("\"~s\"", [Bin]); format_term(S) when is_list(S), S /= [] -> case lists:all(fun(C) -> (C>=0) and (C=<255) end, S) of true -> io_lib:format("\"~s\"", [S]); false -> io_lib:format("~p", [binary_to_strings(S)]) end; format_term(T) -> io_lib:format("~p", [binary_to_strings(T)]). transform_terms(Terms) -> %% We could check all ejabberd beams, but this %% slows down start-up procedure :( Mods = [mod_register, ejabberd_s2s, ejabberd_listener, ejabberd_sql_sup, shaper, ejabberd_s2s_out, acl, ejabberd_config], collect_options(transform_terms(Mods, Terms)). transform_terms([Mod|Mods], Terms) -> case catch Mod:transform_options(Terms) of {'EXIT', _} = Err -> ?ERROR_MSG("Failed to transform terms by ~p: ~p", [Mod, Err]), transform_terms(Mods, Terms); NewTerms -> transform_terms(Mods, NewTerms) end; transform_terms([], NewTerms) -> NewTerms. transform_module_options(Module, Opts) -> Opts1 = gen_iq_handler:transform_module_options(Opts), try Module:transform_module_options(Opts1) catch error:undef -> Opts1 end. compact(Cfg) -> Opts = [{K, V} || #local_config{key = K, value = V} <- Cfg], {GOpts, HOpts} = split_by_hosts(Opts), [#local_config{key = {O, global}, value = V} || {O, V} <- GOpts] ++ lists:flatmap( fun({Host, OptVal}) -> case lists:member(OptVal, GOpts) of true -> []; false -> [#local_config{key = {Opt, Host}, value = Val} || {Opt, Val} <- OptVal] end end, lists:flatten(HOpts)). split_by_hosts(Opts) -> Opts1 = orddict:to_list( lists:foldl( fun({{Opt, Host}, Val}, D) -> orddict:append(Host, {Opt, Val}, D) end, orddict:new(), Opts)), case lists:keytake(global, 1, Opts1) of {value, {global, GlobalOpts}, HostOpts} -> {GlobalOpts, HostOpts}; _ -> {[], Opts1} end. collect_options(Opts) -> {D, InvalidOpts} = lists:foldl( fun({K, V}, {D, Os}) when is_list(V) -> {orddict:append_list(K, V, D), Os}; ({K, V}, {D, Os}) -> {orddict:store(K, V, D), Os}; (Opt, {D, Os}) -> {D, [Opt|Os]} end, {orddict:new(), []}, Opts), InvalidOpts ++ orddict:to_list(D). transform_options(Opts) -> Opts1 = lists:foldl(fun transform_options/2, [], Opts), {HOpts, Opts2} = lists:mapfoldl( fun({host_config, O}, Os) -> {[O], Os}; (O, Os) -> {[], [O|Os]} end, [], Opts1), {AHOpts, Opts3} = lists:mapfoldl( fun({append_host_config, O}, Os) -> {[O], Os}; (O, Os) -> {[], [O|Os]} end, [], Opts2), HOpts1 = case collect_options(lists:flatten(HOpts)) of [] -> []; HOs -> [{host_config, [{H, transform_terms(O)} || {H, O} <- HOs]}] end, AHOpts1 = case collect_options(lists:flatten(AHOpts)) of [] -> []; AHOs -> [{append_host_config, [{H, transform_terms(O)} || {H, O} <- AHOs]}] end, HOpts1 ++ AHOpts1 ++ Opts3. transform_options({domain_certfile, Domain, CertFile}, Opts) -> ?WARNING_MSG("Option 'domain_certfile' now should be defined " "per virtual host or globally. The old format is " "still supported but it is better to fix your config", []), [{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts]; transform_options(Opt, Opts) when Opt == override_global; Opt == override_local; Opt == override_acls -> ?WARNING_MSG("Ignoring '~s' option which has no effect anymore", [Opt]), Opts; transform_options({node_start, {_, _, _} = Now}, Opts) -> ?WARNING_MSG("Old 'node_start' format detected. This is still supported " "but it is better to fix your config.", []), [{node_start, now_to_seconds(Now)}|Opts]; transform_options({host_config, Host, HOpts}, Opts) -> {AddOpts, HOpts1} = lists:mapfoldl( fun({{add, Opt}, Val}, Os) -> ?WARNING_MSG("Option 'add' is deprecated. " "The option is still supported " "but it is better to fix your config: " "use 'append_host_config' instead.", []), {[{Opt, Val}], Os}; (O, Os) -> {[], [O|Os]} end, [], HOpts), [{append_host_config, [{Host, lists:flatten(AddOpts)}]}, {host_config, [{Host, HOpts1}]}|Opts]; transform_options({define_macro, Macro, Val}, Opts) -> [{define_macro, [{Macro, Val}]}|Opts]; transform_options({include_config_file, _} = Opt, Opts) -> [{include_config_file, [transform_include_option(Opt)]} | Opts]; transform_options({include_config_file, _, _} = Opt, Opts) -> [{include_config_file, [transform_include_option(Opt)]} | Opts]; transform_options(Opt, Opts) -> [Opt|Opts]. emit_deprecation_warning(Module, NewModule, DBType) -> ?WARNING_MSG("Module ~s is deprecated, use ~s with 'db_type: ~s'" " instead", [Module, NewModule, DBType]). emit_deprecation_warning(Module, NewModule) -> case is_elixir_module(NewModule) of %% Do not emit deprecation warning for Elixir true -> ok; false -> ?WARNING_MSG("Module ~s is deprecated, use ~s instead", [Module, NewModule]) end. -spec now_to_seconds(erlang:timestamp()) -> non_neg_integer(). now_to_seconds({MegaSecs, Secs, _MicroSecs}) -> MegaSecs * 1000000 + Secs. -spec opt_type(hide_sensitive_log_data) -> fun((boolean()) -> boolean()); (hosts) -> fun(([binary()]) -> [binary()]); (language) -> fun((binary()) -> binary()); (max_fsm_queue) -> fun((pos_integer()) -> pos_integer()); (default_db) -> fun((atom()) -> atom()); (default_ram_db) -> fun((atom()) -> atom()); (loglevel) -> fun((0..5) -> 0..5); (queue_dir) -> fun((binary()) -> binary()); (queue_type) -> fun((ram | file) -> ram | file); (use_cache) -> fun((boolean()) -> boolean()); (cache_size) -> fun((timeout()) -> timeout()); (cache_missed) -> fun((boolean()) -> boolean()); (cache_life_time) -> fun((timeout()) -> timeout()); (shared_key) -> fun((binary()) -> binary()); (node_start) -> fun((non_neg_integer()) -> non_neg_integer()); (atom()) -> [atom()]. opt_type(hide_sensitive_log_data) -> fun (H) when is_boolean(H) -> H end; opt_type(hosts) -> fun(L) -> [iolist_to_binary(H) || H <- L] end; opt_type(language) -> fun iolist_to_binary/1; opt_type(max_fsm_queue) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(default_db) -> fun(T) when is_atom(T) -> T end; opt_type(default_ram_db) -> fun(T) when is_atom(T) -> T end; opt_type(loglevel) -> fun (P) when P >= 0, P =< 5 -> P end; opt_type(queue_dir) -> fun iolist_to_binary/1; opt_type(queue_type) -> fun(ram) -> ram; (file) -> file end; opt_type(use_cache) -> fun(B) when is_boolean(B) -> B end; opt_type(cache_size) -> fun(I) when is_integer(I), I>0 -> I; (infinity) -> infinity; (unlimited) -> infinity end; opt_type(cache_missed) -> fun(B) when is_boolean(B) -> B end; opt_type(cache_life_time) -> fun(I) when is_integer(I), I>0 -> I; (infinity) -> infinity; (unlimited) -> infinity end; opt_type(shared_key) -> fun iolist_to_binary/1; opt_type(node_start) -> fun(I) when is_integer(I), I>=0 -> I end; opt_type(_) -> [hide_sensitive_log_data, hosts, language, max_fsm_queue, default_db, default_ram_db, queue_type, queue_dir, loglevel, use_cache, cache_size, cache_missed, cache_life_time, shared_key, node_start]. -spec may_hide_data(any()) -> any(). may_hide_data(Data) -> case get_option(hide_sensitive_log_data, false) of false -> Data; true -> "hidden_by_ejabberd" end. -spec fsm_limit_opts([proplists:property()]) -> [{max_queue, pos_integer()}]. fsm_limit_opts(Opts) -> case lists:keyfind(max_fsm_queue, 1, Opts) of {_, I} when is_integer(I), I>0 -> [{max_queue, I}]; false -> case get_option(max_fsm_queue) of undefined -> []; N -> [{max_queue, N}] end end. -spec queue_dir() -> binary() | undefined. queue_dir() -> get_option(queue_dir). -spec default_queue_type(binary()) -> ram | file. default_queue_type(Host) -> get_option({queue_type, Host}, ram). -spec use_cache(binary() | global) -> boolean(). use_cache(Host) -> get_option({use_cache, Host}, true). -spec cache_size(binary() | global) -> pos_integer() | infinity. cache_size(Host) -> get_option({cache_size, Host}, 1000). -spec cache_missed(binary() | global) -> boolean(). cache_missed(Host) -> get_option({cache_missed, Host}, true). -spec cache_life_time(binary() | global) -> pos_integer() | infinity. %% NOTE: the integer value returned is in *seconds* cache_life_time(Host) -> get_option({cache_life_time, Host}, 3600). ejabberd-18.01/src/mod_push_sql.erl0000644000232200023220000001623213225664356017637 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_push_sql.erl %%% Author : Evgeniy Khramtsov %%% Purpose : %%% Created : 26 Oct 2017 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2017-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_push_sql). -behaviour(mod_push). -compile([{parse_transform, ejabberd_sql_pt}]). %% API -export([init/2, store_session/6, lookup_session/4, lookup_session/3, lookup_sessions/3, lookup_sessions/2, lookup_sessions/1, delete_session/3, delete_old_sessions/2, export/1]). -include("xmpp.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -include("mod_push.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. store_session(LUser, LServer, NowTS, PushJID, Node, XData) -> XML = encode_xdata(XData), TS = misc:now_to_usec(NowTS), PushLJID = jid:tolower(PushJID), Service = jid:encode(PushLJID), case ?SQL_UPSERT(LServer, "push_session", ["!username=%(LUser)s", "!server_host=%(LServer)s", "!timestamp=%(TS)d", "!service=%(Service)s", "!node=%(Node)s", "xml=%(XML)s"]) of ok -> {ok, {NowTS, PushLJID, Node, XData}}; _Err -> {error, db_failure} end. lookup_session(LUser, LServer, PushJID, Node) -> PushLJID = jid:tolower(PushJID), Service = jid:encode(PushLJID), case ejabberd_sql:sql_query( LServer, ?SQL("select @(timestamp)d, @(xml)s from push_session " "where username=%(LUser)s and %(LServer)H " "and service=%(Service)s " "and node=%(Node)s")) of {selected, [{TS, XML}]} -> NowTS = misc:usec_to_now(TS), XData = decode_xdata(XML, LUser, LServer), {ok, {NowTS, PushLJID, Node, XData}}; {selected, []} -> {error, notfound}; _Err -> {error, db_failure} end. lookup_session(LUser, LServer, NowTS) -> TS = misc:now_to_usec(NowTS), case ejabberd_sql:sql_query( LServer, ?SQL("select @(service)s, @(node)s, @(xml)s " "from push_session where username=%(LUser)s and %(LServer)H " "and timestamp=%(TS)d")) of {selected, [{Service, Node, XML}]} -> PushLJID = jid:tolower(jid:decode(Service)), XData = decode_xdata(XML, LUser, LServer), {ok, {NowTS, PushLJID, Node, XData}}; {selected, []} -> {error, notfound}; _Err -> {error, db_failure} end. lookup_sessions(LUser, LServer, PushJID) -> PushLJID = jid:tolower(PushJID), Service = jid:encode(PushLJID), case ejabberd_sql:sql_query( LServer, ?SQL("select @(timestamp)d, @(xml)s, @(node)s from push_session " "where username=%(LUser)s and %(LServer)H " "and service=%(Service)s")) of {selected, Rows} -> {ok, lists:map( fun({TS, XML, Node}) -> NowTS = misc:usec_to_now(TS), XData = decode_xdata(XML, LUser, LServer), {NowTS, PushLJID, Node, XData} end, Rows)}; _Err -> {error, db_failure} end. lookup_sessions(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(timestamp)d, @(xml)s, @(node)s, @(service)s " "from push_session " "where username=%(LUser)s and %(LServer)H")) of {selected, Rows} -> {ok, lists:map( fun({TS, XML, Node, Service}) -> NowTS = misc:usec_to_now(TS), XData = decode_xdata(XML, LUser, LServer), PushLJID = jid:tolower(jid:decode(Service)), {NowTS, PushLJID,Node, XData} end, Rows)}; _Err -> {error, db_failure} end. lookup_sessions(LServer) -> case ejabberd_sql:sql_query( LServer, ?SQL("select @(username)s, @(timestamp)d, @(xml)s, " "@(node)s, @(service)s from push_session " "where %(LServer)H")) of {selected, Rows} -> {ok, lists:map( fun({LUser, TS, XML, Node, Service}) -> NowTS = misc:usec_to_now(TS), XData = decode_xdata(XML, LUser, LServer), PushLJID = jid:tolower(jid:decode(Service)), {NowTS, PushLJID, Node, XData} end, Rows)}; _Err -> {error, db_failure} end. delete_session(LUser, LServer, NowTS) -> TS = misc:now_to_usec(NowTS), case ejabberd_sql:sql_query( LServer, ?SQL("delete from push_session where " "username=%(LUser)s and %(LServer)H and timestamp=%(TS)d")) of {updated, _} -> ok; _Err -> {error, db_failure} end. delete_old_sessions(LServer, Time) -> TS = misc:now_to_usec(Time), case ejabberd_sql:sql_query( LServer, ?SQL("delete from push_session where timestamp<%(TS)d " "and %(LServer)H")) of {updated, _} -> ok; _Err -> {error, db_failure} end. export(_Server) -> [{push_session, fun(Host, #push_session{us = {LUser, LServer}, timestamp = NowTS, service = PushLJID, node = Node, xml = XData}) when LServer == Host -> TS = misc:now_to_usec(NowTS), Service = jid:encode(PushLJID), XML = encode_xdata(XData), [?SQL("delete from push_session where " "username=%(LUser)s and %(LServer)H and " "timestamp=%(TS)d and " "service=%(Service)s and node=%(Node)s and " "xml=%(XML)s;"), ?SQL_INSERT( "push_session", ["username=%(LUser)s", "server_host=%(LServer)s", "timestamp=%(TS)d", "service=%(Service)s", "node=%(Node)s", "xml=%(XML)s"])]; (_Host, _R) -> [] end}]. %%%=================================================================== %%% Internal functions %%%=================================================================== decode_xdata(<<>>, _LUser, _LServer) -> undefined; decode_xdata(XML, LUser, LServer) -> case fxml_stream:parse_element(XML) of #xmlel{} = El -> try xmpp:decode(El) catch _:{xmpp_codec, Why} -> ?ERROR_MSG("Failed to decode ~s for user ~s@~s " "from table 'push_session': ~s", [XML, LUser, LServer, xmpp:format_error(Why)]), undefined end; Err -> ?ERROR_MSG("Failed to decode ~s for user ~s@~s from " "table 'push_session': ~p", [XML, LUser, LServer, Err]), undefined end. encode_xdata(undefined) -> <<>>; encode_xdata(XData) -> fxml:element_to_binary(xmpp:encode(XData)). ejabberd-18.01/src/mod_bosh.erl0000644000232200023220000002625113225664356016736 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_bosh.erl %%% Author : Evgeniy Khramtsov %%% Purpose : This module acts as a bridge to ejabberd_bosh which implements %%% the real stuff, this is to handle the new pluggable architecture %%% for extending ejabberd's http service. %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_bosh). -author('steve@zeank.in-berlin.de'). %%-define(ejabberd_debug, true). -behaviour(gen_mod). -export([start_link/0]). -export([start/2, stop/1, reload/3, process/2, open_session/2, close_session/1, find_session/1, clean_cache/1]). -export([depends/2, mod_opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). -include("ejabberd_http.hrl"). -include("bosh.hrl"). -callback init() -> any(). -callback open_session(binary(), pid()) -> ok | {error, any()}. -callback close_session(binary()) -> ok | {error, any()}. -callback find_session(binary()) -> {ok, pid()} | {error, any()}. -callback use_cache() -> boolean(). -callback cache_nodes() -> [node()]. -optional_callbacks([use_cache/0, cache_nodes/0]). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). process([], #request{method = 'POST', data = <<>>}) -> ?DEBUG("Bad Request: no data", []), {400, ?HEADER(?CT_XML), #xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"400 Bad Request">>}]}}; process([], #request{method = 'POST', data = Data, ip = IP, headers = Hdrs}) -> ?DEBUG("Incoming data: ~p", [Data]), Type = get_type(Hdrs), ejabberd_bosh:process_request(Data, IP, Type); process([], #request{method = 'GET', data = <<>>}) -> {200, ?HEADER(?CT_XML), get_human_html_xmlel()}; process([], #request{method = 'OPTIONS', data = <<>>}) -> {200, ?OPTIONS_HEADER, []}; process(_Path, _Request) -> ?DEBUG("Bad Request: ~p", [_Request]), {400, ?HEADER(?CT_XML), #xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"400 Bad Request">>}]}}. -spec open_session(binary(), pid()) -> ok | {error, any()}. open_session(SID, Pid) -> Mod = gen_mod:ram_db_mod(global, ?MODULE), case Mod:open_session(SID, Pid) of ok -> delete_cache(Mod, SID); {error, _} = Err -> Err end. -spec close_session(binary()) -> ok. close_session(SID) -> Mod = gen_mod:ram_db_mod(global, ?MODULE), Mod:close_session(SID), delete_cache(Mod, SID). -spec find_session(binary()) -> {ok, pid()} | error. find_session(SID) -> Mod = gen_mod:ram_db_mod(global, ?MODULE), case use_cache(Mod) of true -> ets_cache:lookup( ?BOSH_CACHE, SID, fun() -> case Mod:find_session(SID) of {ok, Pid} -> {ok, Pid}; {error, _} -> error end end); false -> case Mod:find_session(SID) of {ok, Pid} -> {ok, Pid}; {error, _} -> error end end. start(Host, Opts) -> start_jiffy(Opts), Mod = gen_mod:ram_db_mod(global, ?MODULE), init_cache(Mod), Mod:init(), clean_cache(), TmpSup = gen_mod:get_module_proc(Host, ?MODULE), TmpSupSpec = {TmpSup, {ejabberd_tmp_sup, start_link, [TmpSup, ejabberd_bosh]}, permanent, infinity, supervisor, [ejabberd_tmp_sup]}, supervisor:start_child(ejabberd_gen_mod_sup, TmpSupSpec). stop(Host) -> TmpSup = gen_mod:get_module_proc(Host, ?MODULE), supervisor:terminate_child(ejabberd_gen_mod_sup, TmpSup), supervisor:delete_child(ejabberd_gen_mod_sup, TmpSup). reload(_Host, NewOpts, _OldOpts) -> start_jiffy(NewOpts), Mod = gen_mod:ram_db_mod(global, ?MODULE), init_cache(Mod), Mod:init(), ok. %%%=================================================================== %%% Internal functions %%%=================================================================== start_jiffy(Opts) -> case gen_mod:get_opt(json, Opts, false) of false -> ok; true -> case catch ejabberd:start_app(jiffy) of ok -> ok; Err -> ?WARNING_MSG("Failed to start JSON codec (jiffy): ~p. " "JSON support will be disabled", [Err]) end end. get_type(Hdrs) -> try {_, S} = lists:keyfind('Content-Type', 1, Hdrs), [T|_] = str:tokens(S, <<";">>), [_, <<"json">>] = str:tokens(T, <<"/">>), json catch _:_ -> xml end. depends(_Host, _Opts) -> []. mod_opt_type(json) -> fun (false) -> false; (true) -> true end; mod_opt_type(max_concat) -> fun (unlimited) -> unlimited; (N) when is_integer(N), N > 0 -> N end; mod_opt_type(max_inactivity) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(max_pause) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(prebind) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(queue_type) -> fun(ram) -> ram; (file) -> file end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun(B) when is_boolean(B) -> B end; mod_opt_type(O) when O == cache_size; O == cache_life_time -> fun(I) when is_integer(I), I>0 -> I; (unlimited) -> infinity; (infinity) -> infinity end; mod_opt_type(_) -> [json, max_concat, max_inactivity, max_pause, prebind, ram_db_type, queue_type, use_cache, cache_size, cache_missed, cache_life_time]. %%%---------------------------------------------------------------------- %%% Cache stuff %%%---------------------------------------------------------------------- -spec init_cache(module()) -> ok. init_cache(Mod) -> case use_cache(Mod) of true -> ets_cache:new(?BOSH_CACHE, cache_opts()); false -> ets_cache:delete(?BOSH_CACHE) end. -spec use_cache(module()) -> boolean(). use_cache(Mod) -> case erlang:function_exported(Mod, use_cache, 0) of true -> Mod:use_cache(); false -> gen_mod:get_module_opt( global, ?MODULE, use_cache, ejabberd_config:use_cache(global)) end. -spec cache_nodes(module()) -> [node()]. cache_nodes(Mod) -> case erlang:function_exported(Mod, cache_nodes, 0) of true -> Mod:cache_nodes(); false -> ejabberd_cluster:get_nodes() end. -spec delete_cache(module(), binary()) -> ok. delete_cache(Mod, SID) -> case use_cache(Mod) of true -> ets_cache:delete(?BOSH_CACHE, SID, cache_nodes(Mod)); false -> ok end. -spec cache_opts() -> [proplists:property()]. cache_opts() -> MaxSize = gen_mod:get_module_opt( global, ?MODULE, cache_size, ejabberd_config:cache_size(global)), CacheMissed = gen_mod:get_module_opt( global, ?MODULE, cache_missed, ejabberd_config:cache_missed(global)), LifeTime = case gen_mod:get_module_opt( global, ?MODULE, cache_life_time, ejabberd_config:cache_life_time(global)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec clean_cache(node()) -> ok. clean_cache(Node) -> ets_cache:filter( ?BOSH_CACHE, fun(_, error) -> false; (_, {ok, Pid}) -> node(Pid) /= Node end). -spec clean_cache() -> ok. clean_cache() -> ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]). %%%---------------------------------------------------------------------- %%% Help Web Page %%%---------------------------------------------------------------------- get_human_html_xmlel() -> Heading = <<"ejabberd ", (iolist_to_binary(atom_to_list(?MODULE)))/binary>>, #xmlel{name = <<"html">>, attrs = [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}], children = [#xmlel{name = <<"head">>, children = [#xmlel{name = <<"title">>, children = [{xmlcdata, Heading}]}, #xmlel{name = <<"style">>, children = [{xmlcdata, get_style_cdata()}]}]}, #xmlel{name = <<"body">>, children = [#xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"container">>}], children = get_container_children(Heading)}]}]}. get_container_children(Heading) -> [#xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"section">>}], children = [#xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"block">>}], children = [#xmlel{name = <<"a">>, attrs = [{<<"href">>, <<"https://www.ejabberd.im">>}], children = [#xmlel{name = <<"img">>, attrs = [{<<"height">>, <<"32">>}, {<<"src">>, get_image_src()}]}]}]}]}, #xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"white section">>}], children = [#xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"block">>}], children = [#xmlel{name = <<"h1">>, children = [{xmlcdata, Heading}]}, #xmlel{name = <<"p">>, children = [{xmlcdata, <<"An implementation of ">>}, #xmlel{name = <<"a">>, attrs = [{<<"href">>, <<"http://xmpp.org/extensions/xep-0206.html">>}], children = [{xmlcdata, <<"XMPP over BOSH (XEP-0206)">>}]}]}, #xmlel{name = <<"p">>, children = [{xmlcdata, <<"This web page is only informative. To " "use HTTP-Bind you need a Jabber/XMPP " "client that supports it.">>}]}]}]}, #xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"section">>}], children = [#xmlel{name = <<"div">>, attrs = [{<<"class">>, <<"block">>}], children = [#xmlel{name = <<"a">>, attrs = [{<<"href">>, <<"https://www.ejabberd.im">>}, {<<"title">>, <<"ejabberd XMPP server">>}], children = [{xmlcdata, <<"ejabberd">>}]}, {xmlcdata, <<" is maintained by ">>}, #xmlel{name = <<"a">>, attrs = [{<<"href">>, <<"https://www.process-one.net">>}, {<<"title">>, <<"ProcessOne - Leader in Instant Messaging and Push Solutions">>}], children = [{xmlcdata, <<"ProcessOne">>}]} ]}]} ]. get_style_cdata() -> case misc:read_css("bosh.css") of {ok, Data} -> Data; {error, _} -> <<>> end. get_image_src() -> case misc:read_img("bosh-logo.png") of {ok, Img} -> B64Img = base64:encode(Img), <<"data:image/png;base64,", B64Img/binary>>; {error, _} -> <<>> end. ejabberd-18.01/src/node_mix.erl0000644000232200023220000001377213225664356016752 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : node_mix.erl %%% Author : Evgeny Khramtsov %%% Created : 8 Mar 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(node_mix). -behaviour(gen_pubsub_node). %% API -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, get_subscriptions/2, set_subscriptions/4, get_pending_nodes/2, get_states/1, get_state/2, set_state/1, get_items/7, get_items/3, get_item/7, get_last_items/3, get_item/2, set_item/1, get_item_name/3, node_to_path/1, path_to_node/1]). -include("pubsub.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost). options() -> [{deliver_payloads, true}, {notify_config, false}, {notify_delete, false}, {notify_retract, true}, {purge_offline, false}, {persist_items, true}, {max_items, ?MAXITEMS}, {subscribe, true}, {access_model, open}, {roster_groups_allowed, []}, {publish_model, open}, {notification_type, headline}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, {send_last_published_item, never}, {deliver_notifications, true}, {broadcast_all_resources, true}, {presence_based_delivery, false}, {itemreply, none}]. features() -> [<<"create-nodes">>, <<"delete-nodes">>, <<"delete-items">>, <<"instant-nodes">>, <<"item-ids">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"multi-items">>, <<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"retrieve-affiliations">>, <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>, <<"subscription-notifications">>]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(Nidx, Owner) -> node_flat:create_node(Nidx, Owner). delete_node(Removed) -> node_flat:delete_node(Removed). subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId). publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). purge_node(Nidx, Owner) -> node_flat:purge_node(Nidx, Owner). get_entity_affiliations(Host, Owner) -> node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(Nidx) -> node_flat:get_node_affiliations(Nidx). get_affiliation(Nidx, Owner) -> node_flat:get_affiliation(Nidx, Owner). set_affiliation(Nidx, Owner, Affiliation) -> node_flat:set_affiliation(Nidx, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(Nidx) -> node_flat:get_node_subscriptions(Nidx). get_subscriptions(Nidx, Owner) -> node_flat:get_subscriptions(Nidx, Owner). set_subscriptions(Nidx, Owner, Subscription, SubId) -> node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> node_flat:get_pending_nodes(Host, Owner). get_states(Nidx) -> node_flat:get_states(Nidx). get_state(Nidx, JID) -> node_flat:get_state(Nidx, JID). set_state(State) -> node_flat:set_state(State). get_items(Nidx, From, RSM) -> node_flat:get_items(Nidx, From, RSM). get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> node_flat:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(Nidx, From, Count) -> node_flat:get_last_items(Nidx, From, Count). get_item(Nidx, ItemId) -> node_flat:get_item(Nidx, ItemId). get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_flat:get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_flat:set_item(Item). get_item_name(Host, Node, Id) -> node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). path_to_node(Path) -> node_flat:path_to_node(Path). %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/ejabberd_oauth_rest.erl0000644000232200023220000000631613225664356021137 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_oauth_rest.erl %%% Author : Alexey Shchepin %%% Purpose : OAUTH2 REST backend %%% Created : 26 Jul 2016 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License %%% along with this program; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% %%%------------------------------------------------------------------- -module(ejabberd_oauth_rest). -behaviour(ejabberd_oauth). -behaviour(ejabberd_config). -export([init/0, store/1, lookup/1, clean/1, opt_type/1]). -include("ejabberd.hrl"). -include("ejabberd_oauth.hrl"). -include("logger.hrl"). -include("jid.hrl"). init() -> rest:start(?MYNAME), ok. store(R) -> Path = path(<<"store">>), %% Retry 2 times, with a backoff of 500millisec {User, Server} = R#oauth_token.us, SJID = jid:encode({User, Server, <<"">>}), case rest:with_retry( post, [?MYNAME, Path, [], {[{<<"token">>, R#oauth_token.token}, {<<"user">>, SJID}, {<<"scope">>, R#oauth_token.scope}, {<<"expire">>, R#oauth_token.expire} ]}], 2, 500) of {ok, Code, _} when Code == 200 orelse Code == 201 -> ok; Err -> ?ERROR_MSG("failed to store oauth record ~p: ~p", [R, Err]), {error, db_failure} end. lookup(Token) -> Path = path(<<"lookup">>), case rest:with_retry(post, [?MYNAME, Path, [], {[{<<"token">>, Token}]}], 2, 500) of {ok, 200, {Data}} -> SJID = proplists:get_value(<<"user">>, Data, <<>>), JID = jid:decode(SJID), US = {JID#jid.luser, JID#jid.lserver}, Scope = proplists:get_value(<<"scope">>, Data, []), Expire = proplists:get_value(<<"expire">>, Data, 0), {ok, #oauth_token{token = Token, us = US, scope = Scope, expire = Expire}}; {ok, 404, _Resp} -> error; Other -> ?ERROR_MSG("Unexpected response for oauth lookup: ~p", [Other]), error end. clean(_TS) -> ok. path(Path) -> Base = ejabberd_config:get_option(ext_api_path_oauth, <<"/oauth">>), <>. -spec opt_type(ext_api_path_oauth) -> fun((binary()) -> binary()); (atom()) -> [atom()]. opt_type(ext_api_path_oauth) -> fun (X) -> iolist_to_binary(X) end; opt_type(_) -> [ext_api_path_oauth]. ejabberd-18.01/src/mod_pres_counter.erl0000644000232200023220000000743613225664356020517 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_pres_counter.erl %%% Author : Ahmed Omar %%% Purpose : Presence subscription flood prevention %%% Created : 23 Sep 2010 by Ahmed Omar %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_pres_counter). -behavior(gen_mod). -export([start/2, stop/1, reload/3, check_packet/4, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -record(pres_counter, {dir, start, count, logged = false}). start(Host, _Opts) -> ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE, check_packet, 25), ok. stop(Host) -> ejabberd_hooks:delete(privacy_check_packet, Host, ?MODULE, check_packet, 25), ok. reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> []. -spec check_packet(allow | deny, ejabberd_c2s:state() | jid(), stanza(), in | out) -> allow | deny. check_packet(Acc, #{jid := JID}, Packet, Dir) -> check_packet(Acc, JID, Packet, Dir); check_packet(_, #jid{lserver = LServer}, #presence{from = From, to = To, type = Type}, Dir) -> IsSubscription = case Type of subscribe -> true; subscribed -> true; unsubscribe -> true; unsubscribed -> true; _ -> false end, if IsSubscription -> JID = case Dir of in -> To; out -> From end, update(LServer, JID, Dir); true -> allow end; check_packet(Acc, _, _, _) -> Acc. update(Server, JID, Dir) -> StormCount = gen_mod:get_module_opt(Server, ?MODULE, count, 5), TimeInterval = gen_mod:get_module_opt(Server, ?MODULE, interval, 60), TimeStamp = p1_time_compat:system_time(seconds), case read(Dir) of undefined -> write(Dir, #pres_counter{dir = Dir, start = TimeStamp, count = 1}), allow; #pres_counter{start = TimeStart, count = Count, logged = Logged} = R -> if TimeStamp - TimeStart > TimeInterval -> write(Dir, R#pres_counter{start = TimeStamp, count = 1}), allow; (Count =:= StormCount) and Logged -> {stop, deny}; Count =:= StormCount -> write(Dir, R#pres_counter{logged = true}), case Dir of in -> ?WARNING_MSG("User ~s is being flooded, ignoring received " "presence subscriptions", [jid:encode(JID)]); out -> IP = ejabberd_sm:get_user_ip(JID#jid.luser, JID#jid.lserver, JID#jid.lresource), ?WARNING_MSG("Flooder detected: ~s, on IP: ~s ignoring " "sent presence subscriptions~n", [jid:encode(JID), misc:ip_to_list(IP)]) end, {stop, deny}; true -> write(Dir, R#pres_counter{start = TimeStamp, count = Count + 1}), allow end end. read(K) -> get({pres_counter, K}). write(K, V) -> put({pres_counter, K}, V). mod_opt_type(count) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(interval) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(_) -> [count, interval]. ejabberd-18.01/src/mod_mix.erl0000644000232200023220000002657113225664356016605 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_mix.erl %%% Author : Evgeny Khramtsov %%% Created : 2 Mar 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_mix). -behaviour(gen_server). -behaviour(gen_mod). %% API -export([start/2, stop/1, process_iq/1, disco_items/5, disco_identity/5, disco_info/5, disco_features/5, mod_opt_type/1, depends/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("logger.hrl"). -include("xmpp.hrl"). -define(NODES, [?NS_MIX_NODES_MESSAGES, ?NS_MIX_NODES_PRESENCE, ?NS_MIX_NODES_PARTICIPANTS, ?NS_MIX_NODES_SUBJECT, ?NS_MIX_NODES_CONFIG]). -record(state, {server_host :: binary(), hosts :: [binary()]}). %%%=================================================================== %%% API %%%=================================================================== start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {result, [binary()]}. disco_features(_Acc, _From, _To, _Node, _Lang) -> {result, [?NS_MIX_0]}. disco_items(_Acc, _From, To, _Node, _Lang) when To#jid.luser /= <<"">> -> BareTo = jid:remove_resource(To), {result, [#disco_item{jid = BareTo, node = Node} || Node <- ?NODES]}; disco_items(_Acc, _From, _To, _Node, _Lang) -> {result, []}. disco_identity(Acc, _From, To, _Node, _Lang) when To#jid.luser == <<"">> -> Acc ++ [#identity{category = <<"conference">>, name = <<"MIX service">>, type = <<"text">>}]; disco_identity(Acc, _From, _To, _Node, _Lang) -> Acc ++ [#identity{category = <<"conference">>, type = <<"mix">>}]. -spec disco_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()]; ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()]. disco_info(_Acc, _From, To, _Node, _Lang) when is_atom(To) -> [#xdata{type = result, fields = [#xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [?NS_MIX_SERVICEINFO_0]}]}]; disco_info(Acc, _From, _To, _Node, _Lang) -> Acc. process_iq(#iq{type = set, from = From, to = To, sub_els = [#mix_join{subscribe = SubNodes}]} = IQ) -> Nodes = [Node || Node <- SubNodes, lists:member(Node, ?NODES)], case subscribe_nodes(From, To, Nodes) of {result, _} -> case publish_participant(From, To) of {result, _} -> BareFrom = jid:remove_resource(From), xmpp:make_iq_result( IQ, #mix_join{jid = BareFrom, subscribe = Nodes}); {error, Err} -> xmpp:make_error(IQ, Err) end; {error, Err} -> xmpp:make_error(IQ, Err) end; process_iq(#iq{type = set, from = From, to = To, sub_els = [#mix_leave{}]} = IQ) -> case delete_participant(From, To) of {result, _} -> case unsubscribe_nodes(From, To, ?NODES) of {result, _} -> xmpp:make_iq_result(IQ); {error, Err} -> xmpp:make_error(IQ, Err) end; {error, Err} -> xmpp:make_error(IQ, Err) end; process_iq(#iq{lang = Lang} = IQ) -> Txt = <<"Unsupported MIX query">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([ServerHost, Opts]) -> process_flag(trap_exit, true), Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"mix.@HOST@">>), IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(ServerHost)), lists:foreach( fun(Host) -> ConfigTab = gen_mod:get_module_proc(Host, config), ets:new(ConfigTab, [named_table]), ets:insert(ConfigTab, {plugins, [<<"mix">>]}), ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 100), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 100), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 100), ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, disco_items, 100), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_features, 100), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, disco_identity, 100), ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, mod_disco, process_local_iq_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, mod_disco, process_local_iq_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS, mod_disco, process_local_iq_items, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO, mod_disco, process_local_iq_info, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_0, ?MODULE, process_iq, IQDisc), ejabberd_router:register_route(Host, ServerHost) end, Hosts), {ok, #state{server_host = ServerHost, hosts = Hosts}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({route, Packet}, State) -> case catch do_route(State, Packet) of {'EXIT', _} = Err -> try ?ERROR_MSG("failed to route packet:~n~s~nReason: ~p", [xmpp:pp(Packet), Err]), Error = xmpp:err_internal_server_error(), ejabberd_router:route_error(Packet, Error) catch _:_ -> ok end; _ -> ok end, {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, #state{hosts = Hosts}) -> lists:foreach( fun(Host) -> ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 100), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 100), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 100), ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, disco_items, 100), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_features, 100), ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, disco_identity, 100), ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 100), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_0), ejabberd_router:unregister_route(Host) end, Hosts). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== do_route(_State, #iq{} = Packet) -> ejabberd_router:process_iq(Packet); do_route(_State, #presence{from = From, to = To, type = unavailable}) when To#jid.luser /= <<"">> -> delete_presence(From, To); do_route(_State, _Packet) -> ok. subscribe_nodes(From, To, Nodes) -> LTo = jid:tolower(jid:remove_resource(To)), LFrom = jid:tolower(jid:remove_resource(From)), lists:foldl( fun(_Node, {error, _} = Err) -> Err; (Node, {result, _}) -> case mod_pubsub:subscribe_node(LTo, Node, From, From, []) of {error, _} = Err -> case is_item_not_found(Err) of true -> case mod_pubsub:create_node( LTo, To#jid.lserver, Node, LFrom, <<"mix">>) of {result, _} -> mod_pubsub:subscribe_node(LTo, Node, From, From, []); Error -> Error end; false -> Err end; {result, _} = Result -> Result end end, {result, []}, Nodes). unsubscribe_nodes(From, To, Nodes) -> LTo = jid:tolower(jid:remove_resource(To)), BareFrom = jid:remove_resource(From), lists:foldl( fun(_Node, {error, _} = Err) -> Err; (Node, {result, _} = Result) -> case mod_pubsub:unsubscribe_node(LTo, Node, From, BareFrom, <<"">>) of {error, _} = Err -> case is_not_subscribed(Err) of true -> Result; _ -> Err end; {result, _} = Res -> Res end end, {result, []}, Nodes). publish_participant(From, To) -> BareFrom = jid:remove_resource(From), LFrom = jid:tolower(BareFrom), LTo = jid:tolower(jid:remove_resource(To)), Participant = #mix_participant{jid = BareFrom}, ItemID = str:sha(jid:encode(LFrom)), mod_pubsub:publish_item( LTo, To#jid.lserver, ?NS_MIX_NODES_PARTICIPANTS, From, ItemID, [xmpp:encode(Participant)]). delete_presence(From, To) -> LFrom = jid:tolower(From), LTo = jid:tolower(jid:remove_resource(To)), case mod_pubsub:get_items(LTo, ?NS_MIX_NODES_PRESENCE) of Items when is_list(Items) -> lists:foreach( fun({pubsub_item, {ItemID, _}, _, {_, LJID}, _}) when LJID == LFrom -> delete_item(From, To, ?NS_MIX_NODES_PRESENCE, ItemID); (_) -> ok end, Items); _ -> ok end. delete_participant(From, To) -> LFrom = jid:tolower(jid:remove_resource(From)), ItemID = str:sha(jid:encode(LFrom)), delete_presence(From, To), delete_item(From, To, ?NS_MIX_NODES_PARTICIPANTS, ItemID). delete_item(From, To, Node, ItemID) -> LTo = jid:tolower(jid:remove_resource(To)), case mod_pubsub:delete_item( LTo, Node, From, ItemID, true) of {result, _} = Res -> Res; {error, _} = Err -> case is_item_not_found(Err) of true -> {result, []}; false -> Err end end. -spec is_item_not_found({error, stanza_error()}) -> boolean(). is_item_not_found({error, #stanza_error{reason = 'item-not-found'}}) -> true; is_item_not_found({error, _}) -> false. -spec is_not_subscribed({error, stanza_error()}) -> boolean(). is_not_subscribed({error, StanzaError}) -> xmpp:has_subtag(StanzaError, #ps_error{type = 'not-subscribed'}). depends(_Host, _Opts) -> [{mod_pubsub, hard}]. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(host) -> fun iolist_to_binary/1; mod_opt_type(hosts) -> fun (L) -> lists:map(fun iolist_to_binary/1, L) end; mod_opt_type(_) -> [host, hosts, iqdisc]. ejabberd-18.01/src/ejabberd_sql_pt.erl0000644000232200023220000006577213225664356020277 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_sql_pt.erl %%% Author : Alexey Shchepin %%% Description : Parse transform for SQL queries %%% Created : 20 Jan 2016 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_sql_pt). %% API -export([parse_transform/2, format_error/1]). -export([parse/2]). -include("ejabberd_sql_pt.hrl"). -record(state, {loc, 'query' = [], params = [], param_pos = 0, args = [], res = [], res_vars = [], res_pos = 0, server_host_used = false, used_vars = []}). -define(QUERY_RECORD, "sql_query"). -define(ESCAPE_RECORD, "sql_escape"). -define(ESCAPE_VAR, "__SQLEscape"). -define(MOD, sql__module_). -ifdef(NEW_SQL_SCHEMA). -define(USE_NEW_SCHEMA, true). -else. -define(USE_NEW_SCHEMA, false). -endif. %%==================================================================== %% API %%==================================================================== %%-------------------------------------------------------------------- %% Function: %% Description: %%-------------------------------------------------------------------- parse_transform(AST, _Options) -> %io:format("PT: ~p~nOpts: ~p~n", [AST, Options]), put(warnings, []), NewAST = top_transform(AST), %io:format("NewPT: ~p~n", [NewAST]), NewAST ++ get(warnings). format_error(no_server_host) -> "server_host field is not used". %%==================================================================== %% Internal functions %%==================================================================== transform(Form) -> case erl_syntax:type(Form) of application -> case erl_syntax_lib:analyze_application(Form) of {?SQL_MARK, 1} -> case erl_syntax:application_arguments(Form) of [Arg] -> case erl_syntax:type(Arg) of string -> S = erl_syntax:string_value(Arg), Pos = erl_syntax:get_pos(Arg), ParseRes = parse(S, Pos), UnusedVars = case ParseRes#state.server_host_used of {true, SHVar} -> case ?USE_NEW_SCHEMA of true -> []; false -> [SHVar] end; false -> add_warning( Pos, no_server_host), [] end, set_pos( add_unused_vars( make_sql_query(ParseRes), UnusedVars), Pos); _ -> throw({error, erl_syntax:get_pos(Form), "?SQL argument must be " "a constant string"}) end; _ -> throw({error, erl_syntax:get_pos(Form), "wrong number of ?SQL args"}) end; {?SQL_UPSERT_MARK, 2} -> case erl_syntax:application_arguments(Form) of [TableArg, FieldsArg] -> case {erl_syntax:type(TableArg), erl_syntax:is_proper_list(FieldsArg)}of {string, true} -> Table = erl_syntax:string_value(TableArg), ParseRes = parse_upsert( erl_syntax:list_elements(FieldsArg)), Pos = erl_syntax:get_pos(Form), case lists:keymember( "server_host", 1, ParseRes) of true -> ok; false -> add_warning(Pos, no_server_host) end, {ParseRes2, UnusedVars} = filter_upsert_sh(Table, ParseRes), set_pos( add_unused_vars( make_sql_upsert(Table, ParseRes2, Pos), UnusedVars ), Pos); _ -> throw({error, erl_syntax:get_pos(Form), "?SQL_UPSERT arguments must be " "a constant string and a list"}) end; _ -> throw({error, erl_syntax:get_pos(Form), "wrong number of ?SQL_UPSERT args"}) end; {?SQL_INSERT_MARK, 2} -> case erl_syntax:application_arguments(Form) of [TableArg, FieldsArg] -> case {erl_syntax:type(TableArg), erl_syntax:is_proper_list(FieldsArg)}of {string, true} -> Table = erl_syntax:string_value(TableArg), ParseRes = parse_insert( erl_syntax:list_elements(FieldsArg)), Pos = erl_syntax:get_pos(Form), case lists:keymember( "server_host", 1, ParseRes) of true -> ok; false -> add_warning(Pos, no_server_host) end, {ParseRes2, UnusedVars} = filter_upsert_sh(Table, ParseRes), set_pos( add_unused_vars( make_sql_insert(Table, ParseRes2), UnusedVars ), Pos); _ -> throw({error, erl_syntax:get_pos(Form), "?SQL_INSERT arguments must be " "a constant string and a list"}) end; _ -> throw({error, erl_syntax:get_pos(Form), "wrong number of ?SQL_INSERT args"}) end; _ -> Form end; attribute -> case erl_syntax:atom_value(erl_syntax:attribute_name(Form)) of module -> case erl_syntax:attribute_arguments(Form) of [M | _] -> Module = erl_syntax:atom_value(M), %io:format("module ~p~n", [Module]), put(?MOD, Module), Form; _ -> Form end; _ -> Form end; _ -> Form end. top_transform(Forms) when is_list(Forms) -> lists:map( fun(Form) -> try Form2 = erl_syntax_lib:map( fun(Node) -> %io:format("asd ~p~n", [Node]), transform(Node) end, Form), Form3 = erl_syntax:revert(Form2), Form3 catch throw:{error, Line, Error} -> {error, {Line, erl_parse, Error}} end end, Forms). parse(S, Loc) -> parse1(S, [], #state{loc = Loc}). parse(S, ParamPos, Loc) -> parse1(S, [], #state{loc = Loc, param_pos = ParamPos}). parse1([], Acc, State) -> State1 = append_string(lists:reverse(Acc), State), State1#state{'query' = lists:reverse(State1#state.'query'), params = lists:reverse(State1#state.params), args = lists:reverse(State1#state.args), res = lists:reverse(State1#state.res), res_vars = lists:reverse(State1#state.res_vars) }; parse1([$@, $( | S], Acc, State) -> State1 = append_string(lists:reverse(Acc), State), {Name, Type, S1, State2} = parse_name(S, false, State1), Var = "__V" ++ integer_to_list(State2#state.res_pos), EVar = erl_syntax:variable(Var), Convert = case Type of integer -> erl_syntax:application( erl_syntax:atom(binary_to_integer), [EVar]); string -> EVar; boolean -> erl_syntax:application( erl_syntax:atom(ejabberd_sql), erl_syntax:atom(to_bool), [EVar]) end, State3 = append_string(Name, State2), State4 = State3#state{res_pos = State3#state.res_pos + 1, res = [Convert | State3#state.res], res_vars = [EVar | State3#state.res_vars]}, parse1(S1, [], State4); parse1([$%, $( | S], Acc, State) -> State1 = append_string(lists:reverse(Acc), State), {Name, Type, S1, State2} = parse_name(S, true, State1), Var = State2#state.param_pos, State4 = case Type of host -> State3 = State2#state{server_host_used = {true, Name}, used_vars = [Name | State2#state.used_vars]}, case ?USE_NEW_SCHEMA of true -> Convert = erl_syntax:application( erl_syntax:record_access( erl_syntax:variable(?ESCAPE_VAR), erl_syntax:atom(?ESCAPE_RECORD), erl_syntax:atom(string)), [erl_syntax:variable(Name)]), State3#state{'query' = [{var, Var}, {str, "server_host="} | State3#state.'query'], args = [Convert | State3#state.args], params = [Var | State3#state.params], param_pos = State3#state.param_pos + 1}; false -> append_string("0=0", State3) end; _ -> Convert = erl_syntax:application( erl_syntax:record_access( erl_syntax:variable(?ESCAPE_VAR), erl_syntax:atom(?ESCAPE_RECORD), erl_syntax:atom(Type)), [erl_syntax:variable(Name)]), State2#state{'query' = [{var, Var} | State2#state.'query'], args = [Convert | State2#state.args], params = [Var | State2#state.params], param_pos = State2#state.param_pos + 1, used_vars = [Name | State2#state.used_vars]} end, parse1(S1, [], State4); parse1([C | S], Acc, State) -> parse1(S, [C | Acc], State). append_string([], State) -> State; append_string(S, State) -> State#state{query = [{str, S} | State#state.query]}. parse_name(S, IsArg, State) -> parse_name(S, [], 0, IsArg, State). parse_name([], _Acc, _Depth, _IsArg, State) -> throw({error, State#state.loc, "expected ')', found end of string"}); parse_name([$), T | S], Acc, 0, IsArg, State) -> Type = case T of $d -> integer; $s -> string; $b -> boolean; $H when IsArg -> host; _ -> throw({error, State#state.loc, ["unknown type specifier '", T, "'"]}) end, {lists:reverse(Acc), Type, S, State}; parse_name([$)], _Acc, 0, _IsArg, State) -> throw({error, State#state.loc, "expected type specifier, found end of string"}); parse_name([$( = C | S], Acc, Depth, IsArg, State) -> parse_name(S, [C | Acc], Depth + 1, IsArg, State); parse_name([$) = C | S], Acc, Depth, IsArg, State) -> parse_name(S, [C | Acc], Depth - 1, IsArg, State); parse_name([C | S], Acc, Depth, IsArg, State) -> parse_name(S, [C | Acc], Depth, IsArg, State). make_var(V) -> Var = "__V" ++ integer_to_list(V), erl_syntax:variable(Var). make_sql_query(State) -> Hash = erlang:phash2(State#state{loc = undefined}), SHash = <<"Q", (integer_to_binary(Hash))/binary>>, Query = pack_query(State#state.'query'), EQuery = lists:map( fun({str, S}) -> erl_syntax:binary( [erl_syntax:binary_field( erl_syntax:string(S))]); ({var, V}) -> make_var(V) end, Query), erl_syntax:record_expr( erl_syntax:atom(?QUERY_RECORD), [erl_syntax:record_field( erl_syntax:atom(hash), %erl_syntax:abstract(SHash) erl_syntax:binary( [erl_syntax:binary_field( erl_syntax:string(binary_to_list(SHash)))])), erl_syntax:record_field( erl_syntax:atom(args), erl_syntax:fun_expr( [erl_syntax:clause( [erl_syntax:variable(?ESCAPE_VAR)], none, [erl_syntax:list(State#state.args)] )])), erl_syntax:record_field( erl_syntax:atom(format_query), erl_syntax:fun_expr( [erl_syntax:clause( [erl_syntax:list(lists:map(fun make_var/1, State#state.params))], none, [erl_syntax:list(EQuery)] )])), erl_syntax:record_field( erl_syntax:atom(format_res), erl_syntax:fun_expr( [erl_syntax:clause( [erl_syntax:list(State#state.res_vars)], none, [erl_syntax:tuple(State#state.res)] )])), erl_syntax:record_field( erl_syntax:atom(loc), erl_syntax:abstract({get(?MOD), State#state.loc})) ]). pack_query([]) -> []; pack_query([{str, S1}, {str, S2} | Rest]) -> pack_query([{str, S1 ++ S2} | Rest]); pack_query([X | Rest]) -> [X | pack_query(Rest)]. parse_upsert(Fields) -> {Fs, _} = lists:foldr( fun(F, {Acc, Param}) -> case erl_syntax:type(F) of string -> V = erl_syntax:string_value(F), {_, _, State} = Res = parse_upsert_field( V, Param, erl_syntax:get_pos(F)), {[Res | Acc], State#state.param_pos}; _ -> throw({error, erl_syntax:get_pos(F), "?SQL_UPSERT field must be " "a constant string"}) end end, {[], 0}, Fields), %io:format("upsert ~p~n", [{Fields, Fs}]), Fs. %% key | {Update} parse_upsert_field([$! | S], ParamPos, Loc) -> {Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc), {Name, key, ParseState}; parse_upsert_field([$- | S], ParamPos, Loc) -> {Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc), {Name, {false}, ParseState}; parse_upsert_field(S, ParamPos, Loc) -> {Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc), {Name, {true}, ParseState}. parse_upsert_field1([], _Acc, _ParamPos, Loc) -> throw({error, Loc, "?SQL_UPSERT fields must have the " "following form: \"[!-]name=value\""}); parse_upsert_field1([$= | S], Acc, ParamPos, Loc) -> {lists:reverse(Acc), parse(S, ParamPos, Loc)}; parse_upsert_field1([C | S], Acc, ParamPos, Loc) -> parse_upsert_field1(S, [C | Acc], ParamPos, Loc). make_sql_upsert(Table, ParseRes, Pos) -> check_upsert(ParseRes, Pos), erl_syntax:fun_expr( [erl_syntax:clause( [erl_syntax:atom(pgsql), erl_syntax:variable("__Version")], [erl_syntax:infix_expr( erl_syntax:variable("__Version"), erl_syntax:operator('>='), erl_syntax:integer(90100))], [make_sql_upsert_pgsql901(Table, ParseRes), erl_syntax:atom(ok)]), erl_syntax:clause( [erl_syntax:underscore(), erl_syntax:underscore()], none, [make_sql_upsert_generic(Table, ParseRes)]) ]). make_sql_upsert_generic(Table, ParseRes) -> Update = make_sql_query(make_sql_upsert_update(Table, ParseRes)), Insert = make_sql_query(make_sql_upsert_insert(Table, ParseRes)), InsertBranch = erl_syntax:case_expr( erl_syntax:application( erl_syntax:atom(ejabberd_sql), erl_syntax:atom(sql_query_t), [Insert]), [erl_syntax:clause( [erl_syntax:abstract({updated, 1})], none, [erl_syntax:atom(ok)]), erl_syntax:clause( [erl_syntax:variable("__UpdateRes")], none, [erl_syntax:variable("__UpdateRes")])]), erl_syntax:case_expr( erl_syntax:application( erl_syntax:atom(ejabberd_sql), erl_syntax:atom(sql_query_t), [Update]), [erl_syntax:clause( [erl_syntax:abstract({updated, 1})], none, [erl_syntax:atom(ok)]), erl_syntax:clause( [erl_syntax:underscore()], none, [InsertBranch])]). make_sql_upsert_update(Table, ParseRes) -> WPairs = lists:flatmap( fun({_Field, {_}, _ST}) -> []; ({Field, key, ST}) -> [ST#state{ 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query' }] end, ParseRes), Where = join_states(WPairs, " AND "), SPairs = lists:flatmap( fun({_Field, key, _ST}) -> []; ({_Field, {false}, _ST}) -> []; ({Field, {true}, ST}) -> [ST#state{ 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query' }] end, ParseRes), Set = join_states(SPairs, ", "), State = concat_states( [#state{'query' = [{str, "UPDATE "}, {str, Table}, {str, " SET "}]}, Set, #state{'query' = [{str, " WHERE "}]}, Where ]), State. make_sql_upsert_insert(Table, ParseRes) -> Vals = lists:map( fun({_Field, _, ST}) -> ST end, ParseRes), Fields = lists:map( fun({Field, _, _ST}) -> #state{'query' = [{str, Field}]} end, ParseRes), State = concat_states( [#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]}, join_states(Fields, ", "), #state{'query' = [{str, ") VALUES ("}]}, join_states(Vals, ", "), #state{'query' = [{str, ");"}]} ]), State. make_sql_upsert_pgsql901(Table, ParseRes) -> Update = make_sql_upsert_update(Table, ParseRes), Vals = lists:map( fun({_Field, _, ST}) -> ST end, ParseRes), Fields = lists:map( fun({Field, _, _ST}) -> #state{'query' = [{str, Field}]} end, ParseRes), Insert = concat_states( [#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]}, join_states(Fields, ", "), #state{'query' = [{str, ") SELECT "}]}, join_states(Vals, ", "), #state{'query' = [{str, " WHERE NOT EXISTS (SELECT * FROM upsert)"}]} ]), State = concat_states( [#state{'query' = [{str, "WITH upsert AS ("}]}, Update, #state{'query' = [{str, " RETURNING *) "}]}, Insert ]), Upsert = make_sql_query(State), erl_syntax:application( erl_syntax:atom(ejabberd_sql), erl_syntax:atom(sql_query_t), [Upsert]). check_upsert(ParseRes, Pos) -> Set = lists:filter( fun({_Field, Match, _ST}) -> Match /= key end, ParseRes), case Set of [] -> throw({error, Pos, "No ?SQL_UPSERT fields to set, use INSERT instead"}); _ -> ok end, ok. parse_insert(Fields) -> {Fs, _} = lists:foldr( fun(F, {Acc, Param}) -> case erl_syntax:type(F) of string -> V = erl_syntax:string_value(F), {_, _, State} = Res = parse_insert_field( V, Param, erl_syntax:get_pos(F)), {[Res | Acc], State#state.param_pos}; _ -> throw({error, erl_syntax:get_pos(F), "?SQL_INSERT field must be " "a constant string"}) end end, {[], 0}, Fields), Fs. parse_insert_field([$! | _S], _ParamPos, Loc) -> throw({error, Loc, "?SQL_INSERT fields must not start with \"!\""}); parse_insert_field([$- | _S], _ParamPos, Loc) -> throw({error, Loc, "?SQL_INSERT fields must not start with \"-\""}); parse_insert_field(S, ParamPos, Loc) -> {Name, ParseState} = parse_insert_field1(S, [], ParamPos, Loc), {Name, {true}, ParseState}. parse_insert_field1([], _Acc, _ParamPos, Loc) -> throw({error, Loc, "?SQL_INSERT fields must have the " "following form: \"name=value\""}); parse_insert_field1([$= | S], Acc, ParamPos, Loc) -> {lists:reverse(Acc), parse(S, ParamPos, Loc)}; parse_insert_field1([C | S], Acc, ParamPos, Loc) -> parse_insert_field1(S, [C | Acc], ParamPos, Loc). make_sql_insert(Table, ParseRes) -> make_sql_query(make_sql_upsert_insert(Table, ParseRes)). concat_states(States) -> lists:foldr( fun(ST11, ST2) -> ST1 = resolve_vars(ST11, ST2), ST1#state{ 'query' = ST1#state.'query' ++ ST2#state.'query', params = ST1#state.params ++ ST2#state.params, args = ST1#state.args ++ ST2#state.args, res = ST1#state.res ++ ST2#state.res, res_vars = ST1#state.res_vars ++ ST2#state.res_vars, loc = case ST1#state.loc of undefined -> ST2#state.loc; _ -> ST1#state.loc end } end, #state{}, States). resolve_vars(ST1, ST2) -> Max = lists:max([0 | ST1#state.params ++ ST2#state.params]), {Map, _} = lists:foldl( fun(Var, {Acc, New}) -> case lists:member(Var, ST2#state.params) of true -> {dict:store(Var, New, Acc), New + 1}; false -> {Acc, New} end end, {dict:new(), Max + 1}, ST1#state.params), NewParams = lists:map( fun(Var) -> case dict:find(Var, Map) of {ok, New} -> New; error -> Var end end, ST1#state.params), NewQuery = lists:map( fun({var, Var}) -> case dict:find(Var, Map) of {ok, New} -> {var, New}; error -> {var, Var} end; (S) -> S end, ST1#state.'query'), ST1#state{params = NewParams, 'query' = NewQuery}. join_states([], _Sep) -> #state{}; join_states([H | T], Sep) -> J = [[H] | [[#state{'query' = [{str, Sep}]}, X] || X <- T]], concat_states(lists:append(J)). set_pos(Tree, Pos) -> erl_syntax_lib:map( fun(Node) -> case erl_syntax:get_pos(Node) of 0 -> erl_syntax:set_pos(Node, Pos); _ -> Node end end, Tree). filter_upsert_sh(Table, ParseRes) -> case ?USE_NEW_SCHEMA of true -> {ParseRes, []}; false -> lists:foldr( fun({Field, _Match, ST} = P, {Acc, Vars}) -> if Field /= "server_host" orelse Table == "route" -> {[P | Acc], Vars}; true -> {Acc, ST#state.used_vars ++ Vars} end end, {[], []}, ParseRes) end. add_unused_vars(Tree, []) -> Tree; add_unused_vars(Tree, Vars) -> erl_syntax:block_expr( lists:map(fun erl_syntax:variable/1, Vars) ++ [Tree]). -ifdef(ENABLE_PT_WARNINGS). add_warning(Pos, Warning) -> Marker = erl_syntax:revert( erl_syntax:warning_marker({Pos, ?MODULE, Warning})), put(warnings, [Marker | get(warnings)]), ok. -else. add_warning(_Pos, _Warning) -> ok. -endif. ejabberd-18.01/src/ejd2sql.erl0000644000232200023220000002751413225664356016513 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejd2sql.erl %%% Author : Alexey Shchepin %%% Purpose : Export some mnesia tables to SQL DB %%% Created : 22 Aug 2005 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejd2sql). -author('alexey@process-one.net'). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -export([export/2, export/3, import/3, import/4, delete/1, import_info/1]). -define(MAX_RECORDS_PER_TRANSACTION, 100). -record(sql_dump, {fd, type}). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- %%% How to use: %%% A table can be converted from Mnesia to an ODBC database by calling %%% one of the API function with the following parameters: %%% - Server is the server domain you want to convert %%% - Output can be either sql to export to the configured relational %%% database or "Filename" to export to text file. modules() -> [ejabberd_auth, mod_announce, mod_caps, mod_irc, mod_last, mod_mam, mod_muc, mod_offline, mod_privacy, mod_private, mod_pubsub, mod_push, mod_roster, mod_shared_roster, mod_vcard]. export(Server, Output) -> LServer = jid:nameprep(iolist_to_binary(Server)), Modules = modules(), IO = prepare_output(Output), lists:foreach( fun(Module) -> export(LServer, IO, Module) end, Modules), close_output(Output, IO). export(Server, Output, Module1) -> Module = case Module1 of mod_pubsub -> pubsub_db; _ -> Module1 end, SQLMod = gen_mod:db_mod(sql, Module), LServer = jid:nameprep(iolist_to_binary(Server)), IO = prepare_output(Output), lists:foreach( fun({Table, ConvertFun}) -> case export(LServer, Table, IO, ConvertFun) of {atomic, ok} -> ok; {aborted, {no_exists, _}} -> ?WARNING_MSG("Ignoring export for module ~s: " "Mnesia table ~s doesn't exist (most likely " "because the module is unused)", [Module1, Table]); {aborted, Reason} -> ?ERROR_MSG("Failed export for module ~p and table ~p: ~p", [Module, Table, Reason]) end end, SQLMod:export(Server)), close_output(Output, IO). delete(Server) -> Modules = modules(), lists:foreach( fun(Module) -> delete(Server, Module) end, Modules). delete(Server, Module) -> LServer = jid:nameprep(iolist_to_binary(Server)), lists:foreach( fun({Table, ConvertFun}) -> delete(LServer, Table, ConvertFun) end, Module:export(Server)). import(Server, Dir, ToType) -> lists:foreach( fun(Mod) -> ?INFO_MSG("importing ~p...", [Mod]), import(Mod, Server, Dir, ToType) end, modules()). import(Mod, Server, Dir, ToType) -> LServer = jid:nameprep(iolist_to_binary(Server)), try Mod:import_start(LServer, ToType) catch error:undef -> ok end, lists:foreach( fun({File, Tab, _Mod, FieldsNumber}) -> FileName = filename:join([Dir, File]), case open_sql_dump(FileName) of {ok, #sql_dump{type = FromType} = Dump} -> import_rows(LServer, {sql, FromType}, ToType, Tab, Mod, Dump, FieldsNumber), close_sql_dump(Dump); {error, enoent} -> ok; eof -> ?INFO_MSG("It seems like SQL dump ~s is empty", [FileName]); Err -> ?ERROR_MSG("Failed to open SQL dump ~s: ~s", [FileName, format_error(Err)]) end end, import_info(Mod)), try Mod:import_stop(LServer, ToType) catch error:undef -> ok end. import_info(Mod) -> Info = Mod:import_info(), lists:map( fun({Tab, FieldsNum}) -> FileName = <>, {FileName, Tab, Mod, FieldsNum} end, Info). %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- export(LServer, Table, IO, ConvertFun) -> F = fun () -> mnesia:read_lock_table(Table), {_N, SQLs} = mnesia:foldl( fun(R, {N, SQLs} = Acc) -> case ConvertFun(LServer, R) of [] -> Acc; SQL1 -> SQL = format_queries(SQL1), if N < (?MAX_RECORDS_PER_TRANSACTION) - 1 -> {N + 1, [SQL | SQLs]}; true -> output(LServer, Table, IO, flatten([SQL | SQLs])), {0, []} end end end, {0, []}, Table), output(LServer, Table, IO, flatten(SQLs)) end, mnesia:transaction(F). output(_LServer, _Table, _IO, []) -> ok; output(LServer, _Table, sql, SQLs) -> {atomic, ok} = ejabberd_sql:sql_transaction(LServer, SQLs), ok; output(_LServer, Table, Fd, SQLs) -> file:write(Fd, ["-- \n-- Mnesia table: ", atom_to_list(Table), "\n--\n", SQLs]). delete(LServer, Table, ConvertFun) -> F = fun () -> mnesia:write_lock_table(Table), {_N, SQLs} = mnesia:foldl( fun(R, Acc) -> case ConvertFun(LServer, R) of [] -> Acc; _SQL -> mnesia:delete_object(R), Acc end end, {0, []}, Table), delete(LServer, Table, SQLs) end, mnesia:transaction(F). prepare_output(FileName) -> prepare_output(FileName, normal). prepare_output(FileName, Type) when is_binary(FileName) -> prepare_output(binary_to_list(FileName), Type); prepare_output(FileName, normal) when is_list(FileName) -> case file:open(FileName, [write, raw]) of {ok, Fd} -> Fd; Err -> exit(Err) end; prepare_output(Output, _Type) -> Output. close_output(FileName, Fd) when FileName /= Fd -> file:close(Fd), ok; close_output(_, _) -> ok. flatten(SQLs) -> flatten(SQLs, []). flatten([L|Ls], Acc) -> flatten(Ls, flatten1(lists:reverse(L), Acc)); flatten([], Acc) -> Acc. flatten1([H|T], Acc) -> flatten1(T, [[H, $\n]|Acc]); flatten1([], Acc) -> Acc. import_rows(LServer, FromType, ToType, Tab, Mod, Dump, FieldsNumber) -> case read_row_from_sql_dump(Dump, FieldsNumber) of {ok, Fields} -> case catch Mod:import(LServer, FromType, ToType, Tab, Fields) of ok -> ok; Err -> ?ERROR_MSG("Failed to import fields ~p for tab ~p: ~p", [Fields, Tab, Err]) end, import_rows(LServer, FromType, ToType, Tab, Mod, Dump, FieldsNumber); eof -> ok; Err -> ?ERROR_MSG("Failed to read row from SQL dump: ~s", [format_error(Err)]) end. open_sql_dump(FileName) -> case file:open(FileName, [raw, read, binary, read_ahead]) of {ok, Fd} -> case file:read(Fd, 11) of {ok, <<"PGCOPY\n", 16#ff, "\r\n", 0>>} -> case skip_pgcopy_header(Fd) of ok -> {ok, #sql_dump{fd = Fd, type = pgsql}}; Err -> Err end; {ok, _} -> file:position(Fd, 0), {ok, #sql_dump{fd = Fd, type = mysql}}; Err -> Err end; Err -> Err end. close_sql_dump(#sql_dump{fd = Fd}) -> file:close(Fd). read_row_from_sql_dump(#sql_dump{fd = Fd, type = pgsql}, _) -> case file:read(Fd, 2) of {ok, <<(-1):16/signed>>} -> eof; {ok, <>} -> read_fields(Fd, FieldsNum, []); {ok, _} -> {error, eof}; eof -> {error, eof}; {error, _} = Err -> Err end; read_row_from_sql_dump(#sql_dump{fd = Fd, type = mysql}, FieldsNum) -> read_lines(Fd, FieldsNum, <<"">>, []). skip_pgcopy_header(Fd) -> try {ok, <<_:4/binary, ExtSize:32>>} = file:read(Fd, 8), {ok, <<_:ExtSize/binary>>} = file:read(Fd, ExtSize), ok catch error:{badmatch, {error, _} = Err} -> Err; error:{badmatch, _} -> {error, eof} end. read_fields(_Fd, 0, Acc) -> {ok, lists:reverse(Acc)}; read_fields(Fd, N, Acc) -> case file:read(Fd, 4) of {ok, <<(-1):32/signed>>} -> read_fields(Fd, N-1, [null|Acc]); {ok, <>} -> case file:read(Fd, ValSize) of {ok, <>} -> read_fields(Fd, N-1, [Val|Acc]); {ok, _} -> {error, eof}; Err -> Err end; {ok, _} -> {error, eof}; eof -> {error, eof}; {error, _} = Err -> Err end. read_lines(_Fd, 0, <<"">>, Acc) -> {ok, lists:reverse(Acc)}; read_lines(Fd, N, Buf, Acc) -> case file:read_line(Fd) of {ok, Data} when size(Data) >= 2 -> Size = size(Data) - 2, case Data of <> -> NewBuf = <>, read_lines(Fd, N-1, <<"">>, [NewBuf|Acc]); _ -> NewBuf = <>, read_lines(Fd, N, NewBuf, Acc) end; {ok, Data} -> NewBuf = <>, read_lines(Fd, N, NewBuf, Acc); eof when Buf == <<"">>, Acc == [] -> eof; eof -> {error, eof}; {error, _} = Err -> Err end. format_error({error, eof}) -> "unexpected end of file"; format_error({error, Posix}) -> file:format_error(Posix). format_queries(SQLs) -> lists:map( fun(#sql_query{} = SQL) -> ejabberd_sql:sql_query_to_iolist(SQL); (SQL) -> SQL end, SQLs). ejabberd-18.01/src/mod_roster.erl0000644000232200023220000011664113225664356017324 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_roster.erl %%% Author : Alexey Shchepin %%% Purpose : Roster management %%% Created : 11 Dec 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- %%% @doc Roster management (Mnesia storage). %%% %%% Includes support for XEP-0237: Roster Versioning. %%% The roster versioning follows an all-or-nothing strategy: %%% - If the version supplied by the client is the latest, return an empty response. %%% - If not, return the entire new roster (with updated version string). %%% Roster version is a hash digest of the entire roster. %%% No additional data is stored in DB. -module(mod_roster). -protocol({xep, 237, '1.3'}). -author('alexey@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_iq/1, export/1, import_info/0, process_local_iq/1, get_user_roster/2, import/5, get_roster/2, import_start/2, import_stop/2, c2s_self_presence/1, in_subscription/6, out_subscription/4, set_items/3, remove_user/2, get_jid_info/4, encode_item/1, webadmin_page/3, webadmin_user/4, get_versioning_feature/2, roster_versioning_enabled/1, roster_version/2, mod_opt_type/1, set_roster/1, del_roster/3, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_roster.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -define(ROSTER_CACHE, roster_cache). -define(ROSTER_ITEM_CACHE, roster_item_cache). -define(ROSTER_VERSION_CACHE, roster_version_cache). -export_type([subscription/0]). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), binary(), #roster{} | [binary()]) -> ok. -callback read_roster_version(binary(), binary()) -> {ok, binary()} | error. -callback write_roster_version(binary(), binary(), boolean(), binary()) -> any(). -callback get_roster(binary(), binary()) -> {ok, [#roster{}]} | error. -callback get_roster_item(binary(), binary(), ljid()) -> {ok, #roster{}} | error. -callback read_subscription_and_groups(binary(), binary(), ljid()) -> {ok, {subscription(), [binary()]}} | error. -callback roster_subscribe(binary(), binary(), ljid(), #roster{}) -> any(). -callback transaction(binary(), function()) -> {atomic, any()} | {aborted, any()}. -callback remove_user(binary(), binary()) -> any(). -callback update_roster(binary(), binary(), ljid(), #roster{}) -> any(). -callback del_roster(binary(), binary(), ljid()) -> any(). -callback use_cache(binary(), roster | roster_version) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -optional_callbacks([use_cache/2, cache_nodes/1]). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), ejabberd_hooks:add(roster_get, Host, ?MODULE, get_user_roster, 50), ejabberd_hooks:add(roster_in_subscription, Host, ?MODULE, in_subscription, 50), ejabberd_hooks:add(roster_out_subscription, Host, ?MODULE, out_subscription, 50), ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE, get_jid_info, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, get_versioning_feature, 50), ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), ejabberd_hooks:add(webadmin_user, Host, ?MODULE, webadmin_user, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER, ?MODULE, process_iq, IQDisc). stop(Host) -> ejabberd_hooks:delete(roster_get, Host, ?MODULE, get_user_roster, 50), ejabberd_hooks:delete(roster_in_subscription, Host, ?MODULE, in_subscription, 50), ejabberd_hooks:delete(roster_out_subscription, Host, ?MODULE, out_subscription, 50), ejabberd_hooks:delete(roster_get_jid_info, Host, ?MODULE, get_jid_info, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, get_versioning_feature, 50), ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, webadmin_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER, ?MODULE, process_iq, IQDisc); true -> ok end. depends(_Host, _Opts) -> []. process_iq(#iq{from = #jid{luser = U, lserver = S}, to = #jid{luser = U, lserver = S}} = IQ) -> process_local_iq(IQ); process_iq(#iq{lang = Lang, to = To} = IQ) -> case ejabberd_hooks:run_fold(roster_remote_access, To#jid.lserver, false, [IQ]) of false -> Txt = <<"Query to another users is forbidden">>, xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); true -> process_local_iq(IQ) end. process_local_iq(#iq{type = set,lang = Lang, sub_els = [#roster_query{ items = [#roster_item{ask = Ask}]}]} = IQ) when Ask /= undefined -> Txt = <<"Possessing 'ask' attribute is not allowed by RFC6121">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); process_local_iq(#iq{type = set, from = From, lang = Lang, sub_els = [#roster_query{ items = [#roster_item{} = Item]}]} = IQ) -> case has_duplicated_groups(Item#roster_item.groups) of true -> Txt = <<"Duplicated groups are not allowed by RFC6121">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); false -> #jid{server = Server} = From, Access = gen_mod:get_module_opt(Server, ?MODULE, access, all), case acl:match_rule(Server, Access, From) of deny -> Txt = <<"Access denied by service policy">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); allow -> process_iq_set(IQ) end end; process_local_iq(#iq{type = set, lang = Lang, sub_els = [#roster_query{items = [_|_]}]} = IQ) -> Txt = <<"Multiple elements are not allowed by RFC6121">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); process_local_iq(#iq{type = get, lang = Lang, sub_els = [#roster_query{items = Items}]} = IQ) -> case Items of [] -> process_iq_get(IQ); [_|_] -> Txt = <<"The query must not contain elements">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end; process_local_iq(#iq{lang = Lang} = IQ) -> Txt = <<"No module is handling this query">>, xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). roster_hash(Items) -> str:sha(term_to_binary(lists:sort([R#roster{groups = lists:sort(Grs)} || R = #roster{groups = Grs} <- Items]))). roster_versioning_enabled(Host) -> gen_mod:get_module_opt(Host, ?MODULE, versioning, false). roster_version_on_db(Host) -> gen_mod:get_module_opt(Host, ?MODULE, store_current_id, false). %% Returns a list that may contain an xmlelement with the XEP-237 feature if it's enabled. -spec get_versioning_feature([xmpp_element()], binary()) -> [xmpp_element()]. get_versioning_feature(Acc, Host) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> case roster_versioning_enabled(Host) of true -> [#rosterver_feature{}|Acc]; false -> Acc end; false -> Acc end. roster_version(LServer, LUser) -> US = {LUser, LServer}, case roster_version_on_db(LServer) of true -> case read_roster_version(LUser, LServer) of error -> not_found; {ok, V} -> V end; false -> roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US])) end. read_roster_version(LUser, LServer) -> ets_cache:lookup( ?ROSTER_VERSION_CACHE, {LUser, LServer}, fun() -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:read_roster_version(LUser, LServer) end). write_roster_version(LUser, LServer) -> write_roster_version(LUser, LServer, false). write_roster_version_t(LUser, LServer) -> write_roster_version(LUser, LServer, true). write_roster_version(LUser, LServer, InTransaction) -> Ver = str:sha(term_to_binary(p1_time_compat:unique_integer())), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:write_roster_version(LUser, LServer, InTransaction, Ver), if InTransaction -> ok; true -> ets_cache:delete(?ROSTER_VERSION_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)) end, Ver. %% Load roster from DB only if neccesary. %% It is neccesary if %% - roster versioning is disabled in server OR %% - roster versioning is not used by the client OR %% - roster versioning is used by server and client, BUT the server isn't storing versions on db OR %% - the roster version from client don't match current version. process_iq_get(#iq{to = To, lang = Lang, sub_els = [#roster_query{ver = RequestedVersion}]} = IQ) -> LUser = To#jid.luser, LServer = To#jid.lserver, US = {LUser, LServer}, try {ItemsToSend, VersionToSend} = case {roster_versioning_enabled(LServer), roster_version_on_db(LServer)} of {true, true} when RequestedVersion /= undefined -> case read_roster_version(LUser, LServer) of error -> RosterVersion = write_roster_version(LUser, LServer), {lists:map(fun encode_item/1, ejabberd_hooks:run_fold( roster_get, To#jid.lserver, [], [US])), RosterVersion}; {ok, RequestedVersion} -> {false, false}; {ok, NewVersion} -> {lists:map(fun encode_item/1, ejabberd_hooks:run_fold( roster_get, To#jid.lserver, [], [US])), NewVersion} end; {true, false} when RequestedVersion /= undefined -> RosterItems = ejabberd_hooks:run_fold( roster_get, To#jid.lserver, [], [US]), case roster_hash(RosterItems) of RequestedVersion -> {false, false}; New -> {lists:map(fun encode_item/1, RosterItems), New} end; _ -> {lists:map(fun encode_item/1, ejabberd_hooks:run_fold( roster_get, To#jid.lserver, [], [US])), false} end, xmpp:make_iq_result( IQ, case {ItemsToSend, VersionToSend} of {false, false} -> undefined; {Items, false} -> #roster_query{items = Items}; {Items, Version} -> #roster_query{items = Items, ver = Version} end) catch E:R -> ?ERROR_MSG("failed to process roster get for ~s: ~p", [jid:encode(To), {E, {R, erlang:get_stacktrace()}}]), Txt = <<"Roster module has failed">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end. -spec get_user_roster([#roster{}], {binary(), binary()}) -> [#roster{}]. get_user_roster(Acc, {LUser, LServer}) -> Items = get_roster(LUser, LServer), lists:filter(fun (#roster{subscription = none, ask = in}) -> false; (_) -> true end, Items) ++ Acc. get_roster(LUser, LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), R = case use_cache(Mod, LServer, roster) of true -> ets_cache:lookup( ?ROSTER_CACHE, {LUser, LServer}, fun() -> Mod:get_roster(LUser, LServer) end); false -> Mod:get_roster(LUser, LServer) end, case R of {ok, Items} -> Items; error -> [] end. get_roster_item(LUser, LServer, LJID) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:get_roster_item(LUser, LServer, LJID) of {ok, Item} -> Item; error -> LBJID = jid:remove_resource(LJID), #roster{usj = {LUser, LServer, LBJID}, us = {LUser, LServer}, jid = LBJID} end. get_subscription_and_groups(LUser, LServer, LJID) -> LBJID = jid:remove_resource(LJID), Mod = gen_mod:db_mod(LServer, ?MODULE), Res = case use_cache(Mod, LServer, roster) of true -> ets_cache:lookup( ?ROSTER_ITEM_CACHE, {LUser, LServer, LBJID}, fun() -> Items = get_roster(LUser, LServer), case lists:keyfind(LBJID, #roster.jid, Items) of #roster{subscription = Sub, groups = Groups} -> {ok, {Sub, Groups}}; false -> error end end); false -> Mod:read_subscription_and_groups(LUser, LServer, LBJID) end, case Res of {ok, SubAndGroups} -> SubAndGroups; error -> {none, []} end. set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) -> transaction( LUser, LServer, [LJID], fun() -> update_roster_t(LUser, LServer, LJID, Item) end). del_roster(LUser, LServer, LJID) -> transaction( LUser, LServer, [LJID], fun() -> del_roster_t(LUser, LServer, LJID) end). encode_item(Item) -> #roster_item{jid = jid:make(Item#roster.jid), name = Item#roster.name, subscription = Item#roster.subscription, ask = case ask_to_pending(Item#roster.ask) of out -> subscribe; both -> subscribe; _ -> undefined end, groups = Item#roster.groups}. decode_item(#roster_item{subscription = remove} = Item, R, _) -> R#roster{jid = jid:tolower(Item#roster_item.jid), name = <<"">>, subscription = remove, ask = none, groups = [], askmessage = <<"">>, xs = []}; decode_item(Item, R, Managed) -> R#roster{jid = jid:tolower(Item#roster_item.jid), name = Item#roster_item.name, subscription = case Item#roster_item.subscription of Sub when Managed -> Sub; _ -> R#roster.subscription end, groups = Item#roster_item.groups}. process_iq_set(#iq{from = _From, to = To, sub_els = [#roster_query{items = [QueryItem]}]} = IQ) -> #jid{user = User, luser = LUser, lserver = LServer} = To, LJID = jid:tolower(QueryItem#roster_item.jid), F = fun () -> Item = get_roster_item(LUser, LServer, LJID), Item2 = decode_item(QueryItem, Item, false), Item3 = ejabberd_hooks:run_fold(roster_process_item, LServer, Item2, [LServer]), case Item3#roster.subscription of remove -> del_roster_t(LUser, LServer, LJID); _ -> update_roster_t(LUser, LServer, LJID, Item3) end, case roster_version_on_db(LServer) of true -> write_roster_version_t(LUser, LServer); false -> ok end, {Item, Item3} end, case transaction(LUser, LServer, [LJID], F) of {atomic, {OldItem, Item}} -> push_item(User, LServer, To, OldItem, Item), case Item#roster.subscription of remove -> send_unsubscribing_presence(To, OldItem); _ -> ok end, xmpp:make_iq_result(IQ); E -> ?ERROR_MSG("roster set failed:~nIQ = ~s~nError = ~p", [xmpp:pp(IQ), E]), xmpp:make_error(IQ, xmpp:err_internal_server_error()) end. push_item(User, Server, From, OldItem, NewItem) -> case roster_versioning_enabled(Server) of true -> push_item_version(Server, User, From, OldItem, NewItem, roster_version(Server, User)); false -> lists:foreach( fun(Resource) -> push_item(User, Server, Resource, From, OldItem, NewItem) end, ejabberd_sm:get_user_resources(User, Server)) end. push_item(User, Server, Resource, From, OldItem, NewItem) -> push_item(User, Server, Resource, From, OldItem, NewItem, undefined). push_item(User, Server, Resource, From, OldItem, NewItem, Ver) -> To = jid:make(User, Server, Resource), route_presence_change(To, OldItem, NewItem), ResIQ = #iq{type = set, from = From, to = To, id = <<"push", (randoms:get_string())/binary>>, sub_els = [#roster_query{ver = Ver, items = [encode_item(NewItem)]}]}, ejabberd_router:route(ResIQ). push_item_version(Server, User, From, OldItem, NewItem, RosterVersion) -> lists:foreach( fun(Resource) -> push_item(User, Server, Resource, From, OldItem, NewItem, RosterVersion) end, ejabberd_sm:get_user_resources(User, Server)). -spec route_presence_change(jid(), #roster{}, #roster{}) -> ok. route_presence_change(From, OldItem, NewItem) -> OldSub = OldItem#roster.subscription, NewSub = NewItem#roster.subscription, To = jid:make(NewItem#roster.jid), NewIsFrom = NewSub == both orelse NewSub == from, OldIsFrom = OldSub == both orelse OldSub == from, if NewIsFrom andalso not OldIsFrom -> case ejabberd_sm:get_session_pid( From#jid.luser, From#jid.lserver, From#jid.lresource) of none -> ok; Pid -> ejabberd_c2s:resend_presence(Pid, To) end; OldIsFrom andalso not NewIsFrom -> PU = #presence{from = From, to = To, type = unavailable}, case ejabberd_hooks:run_fold( privacy_check_packet, allow, [From, PU, out]) of deny -> ok; allow -> ejabberd_router:route(PU) end; true -> ok end. ask_to_pending(subscribe) -> out; ask_to_pending(unsubscribe) -> none; ask_to_pending(Ask) -> Ask. roster_subscribe_t(LUser, LServer, LJID, Item) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:roster_subscribe(LUser, LServer, LJID, Item). transaction(LUser, LServer, LJIDs, F) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:transaction(LServer, F) of {atomic, _} = Result -> delete_cache(Mod, LUser, LServer, LJIDs), Result; Err -> Err end. -spec in_subscription(boolean(), binary(), binary(), jid(), subscribe | subscribed | unsubscribe | unsubscribed, binary()) -> boolean(). in_subscription(_, User, Server, JID, Type, Reason) -> process_subscription(in, User, Server, JID, Type, Reason). -spec out_subscription( binary(), binary(), jid(), subscribed | unsubscribed | subscribe | unsubscribe) -> boolean(). out_subscription(User, Server, JID, Type) -> process_subscription(out, User, Server, JID, Type, <<"">>). process_subscription(Direction, User, Server, JID1, Type, Reason) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LJID = jid:tolower(jid:remove_resource(JID1)), F = fun () -> Item = get_roster_item(LUser, LServer, LJID), NewState = case Direction of out -> out_state_change(Item#roster.subscription, Item#roster.ask, Type); in -> in_state_change(Item#roster.subscription, Item#roster.ask, Type) end, AutoReply = case Direction of out -> none; in -> in_auto_reply(Item#roster.subscription, Item#roster.ask, Type) end, AskMessage = case NewState of {_, both} -> Reason; {_, in} -> Reason; _ -> <<"">> end, case NewState of none -> {none, AutoReply}; {none, none} when Item#roster.subscription == none, Item#roster.ask == in -> del_roster_t(LUser, LServer, LJID), {none, AutoReply}; {Subscription, Pending} -> NewItem = Item#roster{subscription = Subscription, ask = Pending, askmessage = AskMessage}, roster_subscribe_t(LUser, LServer, LJID, NewItem), case roster_version_on_db(LServer) of true -> write_roster_version_t(LUser, LServer); false -> ok end, {{push, Item, NewItem}, AutoReply} end end, case transaction(LUser, LServer, [LJID], F) of {atomic, {Push, AutoReply}} -> case AutoReply of none -> ok; _ -> ejabberd_router:route( #presence{type = AutoReply, from = jid:make(User, Server), to = JID1}) end, case Push of {push, OldItem, NewItem} -> if NewItem#roster.subscription == none, NewItem#roster.ask == in -> ok; true -> push_item(User, Server, jid:make(User, Server), OldItem, NewItem) end, true; none -> false end; _ -> false end. %% in_state_change(Subscription, Pending, Type) -> NewState %% NewState = none | {NewSubscription, NewPending} -ifdef(ROSTER_GATEWAY_WORKAROUND). -define(NNSD, {to, none}). -define(NISD, {to, in}). -else. -define(NNSD, none). -define(NISD, none). -endif. in_state_change(none, none, subscribe) -> {none, in}; in_state_change(none, none, subscribed) -> ?NNSD; in_state_change(none, none, unsubscribe) -> none; in_state_change(none, none, unsubscribed) -> none; in_state_change(none, out, subscribe) -> {none, both}; in_state_change(none, out, subscribed) -> {to, none}; in_state_change(none, out, unsubscribe) -> none; in_state_change(none, out, unsubscribed) -> {none, none}; in_state_change(none, in, subscribe) -> none; in_state_change(none, in, subscribed) -> ?NISD; in_state_change(none, in, unsubscribe) -> {none, none}; in_state_change(none, in, unsubscribed) -> none; in_state_change(none, both, subscribe) -> none; in_state_change(none, both, subscribed) -> {to, in}; in_state_change(none, both, unsubscribe) -> {none, out}; in_state_change(none, both, unsubscribed) -> {none, in}; in_state_change(to, none, subscribe) -> {to, in}; in_state_change(to, none, subscribed) -> none; in_state_change(to, none, unsubscribe) -> none; in_state_change(to, none, unsubscribed) -> {none, none}; in_state_change(to, in, subscribe) -> none; in_state_change(to, in, subscribed) -> none; in_state_change(to, in, unsubscribe) -> {to, none}; in_state_change(to, in, unsubscribed) -> {none, in}; in_state_change(from, none, subscribe) -> none; in_state_change(from, none, subscribed) -> {both, none}; in_state_change(from, none, unsubscribe) -> {none, none}; in_state_change(from, none, unsubscribed) -> none; in_state_change(from, out, subscribe) -> none; in_state_change(from, out, subscribed) -> {both, none}; in_state_change(from, out, unsubscribe) -> {none, out}; in_state_change(from, out, unsubscribed) -> {from, none}; in_state_change(both, none, subscribe) -> none; in_state_change(both, none, subscribed) -> none; in_state_change(both, none, unsubscribe) -> {to, none}; in_state_change(both, none, unsubscribed) -> {from, none}. out_state_change(none, none, subscribe) -> {none, out}; out_state_change(none, none, subscribed) -> none; out_state_change(none, none, unsubscribe) -> none; out_state_change(none, none, unsubscribed) -> none; out_state_change(none, out, subscribe) -> {none, out}; %% We need to resend query (RFC3921, section 9.2) out_state_change(none, out, subscribed) -> none; out_state_change(none, out, unsubscribe) -> {none, none}; out_state_change(none, out, unsubscribed) -> none; out_state_change(none, in, subscribe) -> {none, both}; out_state_change(none, in, subscribed) -> {from, none}; out_state_change(none, in, unsubscribe) -> none; out_state_change(none, in, unsubscribed) -> {none, none}; out_state_change(none, both, subscribe) -> none; out_state_change(none, both, subscribed) -> {from, out}; out_state_change(none, both, unsubscribe) -> {none, in}; out_state_change(none, both, unsubscribed) -> {none, out}; out_state_change(to, none, subscribe) -> none; out_state_change(to, none, subscribed) -> {both, none}; out_state_change(to, none, unsubscribe) -> {none, none}; out_state_change(to, none, unsubscribed) -> none; out_state_change(to, in, subscribe) -> none; out_state_change(to, in, subscribed) -> {both, none}; out_state_change(to, in, unsubscribe) -> {none, in}; out_state_change(to, in, unsubscribed) -> {to, none}; out_state_change(from, none, subscribe) -> {from, out}; out_state_change(from, none, subscribed) -> none; out_state_change(from, none, unsubscribe) -> none; out_state_change(from, none, unsubscribed) -> {none, none}; out_state_change(from, out, subscribe) -> none; out_state_change(from, out, subscribed) -> none; out_state_change(from, out, unsubscribe) -> {from, none}; out_state_change(from, out, unsubscribed) -> {none, out}; out_state_change(both, none, subscribe) -> none; out_state_change(both, none, subscribed) -> none; out_state_change(both, none, unsubscribe) -> {from, none}; out_state_change(both, none, unsubscribed) -> {to, none}. in_auto_reply(from, none, subscribe) -> subscribed; in_auto_reply(from, out, subscribe) -> subscribed; in_auto_reply(both, none, subscribe) -> subscribed; in_auto_reply(none, in, unsubscribe) -> unsubscribed; in_auto_reply(none, both, unsubscribe) -> unsubscribed; in_auto_reply(to, in, unsubscribe) -> unsubscribed; in_auto_reply(from, none, unsubscribe) -> unsubscribed; in_auto_reply(from, out, unsubscribe) -> unsubscribed; in_auto_reply(both, none, unsubscribe) -> unsubscribed; in_auto_reply(_, _, _) -> none. -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Items = get_user_roster([], {LUser, LServer}), send_unsubscription_to_rosteritems(LUser, LServer, Items), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_user(LUser, LServer), delete_cache(Mod, LUser, LServer, [Item#roster.jid || Item <- Items]). %% For each contact with Subscription: %% Both or From, send a "unsubscribed" presence stanza; %% Both or To, send a "unsubscribe" presence stanza. send_unsubscription_to_rosteritems(LUser, LServer, RosterItems) -> From = jid:make({LUser, LServer, <<"">>}), lists:foreach(fun (RosterItem) -> send_unsubscribing_presence(From, RosterItem) end, RosterItems). send_unsubscribing_presence(From, Item) -> IsTo = case Item#roster.subscription of both -> true; to -> true; _ -> false end, IsFrom = case Item#roster.subscription of both -> true; from -> true; _ -> false end, if IsTo -> ejabberd_router:route( #presence{type = unsubscribe, from = jid:remove_resource(From), to = jid:make(Item#roster.jid)}); true -> ok end, if IsFrom -> ejabberd_router:route( #presence{type = unsubscribed, from = jid:remove_resource(From), to = jid:make(Item#roster.jid)}); true -> ok end, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec set_items(binary(), binary(), roster_query()) -> any(). set_items(User, Server, #roster_query{items = Items}) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LJIDs = [jid:tolower(Item#roster_item.jid) || Item <- Items], F = fun () -> lists:foreach( fun(Item) -> process_item_set_t(LUser, LServer, Item) end, Items) end, transaction(LUser, LServer, LJIDs, F). update_roster_t(LUser, LServer, LJID, Item) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:update_roster(LUser, LServer, LJID, Item). del_roster_t(LUser, LServer, LJID) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:del_roster(LUser, LServer, LJID). process_item_set_t(LUser, LServer, #roster_item{jid = JID1} = QueryItem) -> JID = {JID1#jid.user, JID1#jid.server, <<>>}, LJID = {JID1#jid.luser, JID1#jid.lserver, <<>>}, Item = #roster{usj = {LUser, LServer, LJID}, us = {LUser, LServer}, jid = JID}, Item2 = decode_item(QueryItem, Item, _Managed = true), case Item2#roster.subscription of remove -> del_roster_t(LUser, LServer, LJID); _ -> update_roster_t(LUser, LServer, LJID, Item2) end; process_item_set_t(_LUser, _LServer, _) -> ok. -spec c2s_self_presence({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. c2s_self_presence({_, #{pres_last := _}} = Acc) -> Acc; c2s_self_presence({#presence{type = available} = Pkt, State}) -> Prio = get_priority_from_presence(Pkt), if Prio >= 0 -> State1 = resend_pending_subscriptions(State), {Pkt, State1}; true -> {Pkt, State} end; c2s_self_presence(Acc) -> Acc. -spec resend_pending_subscriptions(ejabberd_c2s:state()) -> ejabberd_c2s:state(). resend_pending_subscriptions(#{jid := JID} = State) -> BareJID = jid:remove_resource(JID), Result = get_roster(JID#jid.luser, JID#jid.lserver), lists:foldl( fun(#roster{ask = Ask} = R, AccState) when Ask == in; Ask == both -> Message = R#roster.askmessage, Status = if is_binary(Message) -> (Message); true -> <<"">> end, Sub = #presence{from = jid:make(R#roster.jid), to = BareJID, type = subscribe, status = xmpp:mk_text(Status)}, ejabberd_c2s:send(AccState, Sub); (_, AccState) -> AccState end, State, Result). -spec get_priority_from_presence(presence()) -> integer(). get_priority_from_presence(#presence{priority = Prio}) -> case Prio of undefined -> 0; _ -> Prio end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec get_jid_info({subscription(), [binary()]}, binary(), binary(), jid()) -> {subscription(), [binary()]}. get_jid_info(_, User, Server, JID) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LJID = jid:tolower(JID), get_subscription_and_groups(LUser, LServer, LJID). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% webadmin_page(_, Host, #request{us = _US, path = [<<"user">>, U, <<"roster">>], q = Query, lang = Lang} = _Request) -> Res = user_roster(U, Host, Query, Lang), {stop, Res}; webadmin_page(Acc, _, _) -> Acc. user_roster(User, Server, Query, Lang) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), US = {LUser, LServer}, Items1 = get_roster(LUser, LServer), Res = user_roster_parse_query(User, Server, Items1, Query), Items = get_roster(LUser, LServer), SItems = lists:sort(Items), FItems = case SItems of [] -> [?CT(<<"None">>)]; _ -> [?XE(<<"table">>, [?XE(<<"thead">>, [?XE(<<"tr">>, [?XCT(<<"td">>, <<"Jabber ID">>), ?XCT(<<"td">>, <<"Nickname">>), ?XCT(<<"td">>, <<"Subscription">>), ?XCT(<<"td">>, <<"Pending">>), ?XCT(<<"td">>, <<"Groups">>)])]), ?XE(<<"tbody">>, (lists:map(fun (R) -> Groups = lists:flatmap(fun (Group) -> [?C(Group), ?BR] end, R#roster.groups), Pending = ask_to_pending(R#roster.ask), TDJID = build_contact_jid_td(R#roster.jid), ?XE(<<"tr">>, [TDJID, ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], (R#roster.name)), ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], (iolist_to_binary(atom_to_list(R#roster.subscription)))), ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], (iolist_to_binary(atom_to_list(Pending)))), ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], Groups), if Pending == in -> ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], [?INPUTT(<<"submit">>, <<"validate", (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, <<"Validate">>)]); true -> ?X(<<"td">>) end, ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], [?INPUTT(<<"submit">>, <<"remove", (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, <<"Remove">>)])]) end, SItems)))])] end, [?XC(<<"h1">>, (<<(?T(<<"Roster of ">>))/binary, (us_to_list(US))/binary>>))] ++ case Res of ok -> [?XREST(<<"Submitted">>)]; error -> [?XREST(<<"Bad format">>)]; nothing -> [] end ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], (FItems ++ [?P, ?INPUT(<<"text">>, <<"newjid">>, <<"">>), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"addjid">>, <<"Add Jabber ID">>)]))]. build_contact_jid_td(RosterJID) -> ContactJID = jid:make(RosterJID), JIDURI = case {ContactJID#jid.luser, ContactJID#jid.lserver} of {<<"">>, _} -> <<"">>; {CUser, CServer} -> case lists:member(CServer, ?MYHOSTS) of false -> <<"">>; true -> <<"/admin/server/", CServer/binary, "/user/", CUser/binary, "/">> end end, case JIDURI of <<>> -> ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], (jid:encode(RosterJID))); URI when is_binary(URI) -> ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], [?AC(JIDURI, (jid:encode(RosterJID)))]) end. user_roster_parse_query(User, Server, Items, Query) -> case lists:keysearch(<<"addjid">>, 1, Query) of {value, _} -> case lists:keysearch(<<"newjid">>, 1, Query) of {value, {_, SJID}} -> try jid:decode(SJID) of JID -> user_roster_subscribe_jid(User, Server, JID), ok catch _:{bad_jid, _} -> error end; false -> error end; false -> case catch user_roster_item_parse_query(User, Server, Items, Query) of submitted -> ok; {'EXIT', _Reason} -> error; _ -> nothing end end. user_roster_subscribe_jid(User, Server, JID) -> out_subscription(User, Server, JID, subscribe), UJID = jid:make(User, Server), ejabberd_router:route(#presence{from = UJID, to = JID, type = subscribe}). user_roster_item_parse_query(User, Server, Items, Query) -> lists:foreach(fun (R) -> JID = R#roster.jid, case lists:keysearch(<<"validate", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of {value, _} -> JID1 = jid:make(JID), out_subscription(User, Server, JID1, subscribed), UJID = jid:make(User, Server), ejabberd_router:route( #presence{from = UJID, to = JID1, type = subscribed}), throw(submitted); false -> case lists:keysearch(<<"remove", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of {value, _} -> UJID = jid:make(User, Server), RosterItem = #roster_item{ jid = jid:make(JID), subscription = remove}, process_iq_set( #iq{type = set, from = UJID, to = UJID, id = randoms:get_string(), sub_els = [#roster_query{ items = [RosterItem]}]}), throw(submitted); false -> ok end end end, Items), nothing. us_to_list({User, Server}) -> jid:encode({User, Server, <<"">>}). webadmin_user(Acc, _User, _Server, Lang) -> Acc ++ [?XE(<<"h3">>, [?ACT(<<"roster/">>, <<"Roster">>)])]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% has_duplicated_groups(Groups) -> GroupsPrep = lists:usort([jid:resourceprep(G) || G <- Groups]), not (length(GroupsPrep) == length(Groups)). -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> CacheOpts = cache_opts(Host, Opts), case use_cache(Mod, Host, roster_version) of true -> ets_cache:new(?ROSTER_VERSION_CACHE, CacheOpts); false -> ets_cache:delete(?ROSTER_VERSION_CACHE) end, case use_cache(Mod, Host, roster) of true -> ets_cache:new(?ROSTER_CACHE, CacheOpts), ets_cache:new(?ROSTER_ITEM_CACHE, CacheOpts); false -> ets_cache:delete(?ROSTER_CACHE), ets_cache:delete(?ROSTER_ITEM_CACHE) end. -spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()]. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt( cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt( cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt( cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. -spec use_cache(module(), binary(), roster | roster_version) -> boolean(). use_cache(Mod, Host, Table) -> case erlang:function_exported(Mod, use_cache, 2) of true -> Mod:use_cache(Host, Table); false -> gen_mod:get_module_opt( Host, ?MODULE, use_cache, ejabberd_config:use_cache(Host)) end. -spec cache_nodes(module(), binary()) -> [node()]. cache_nodes(Mod, Host) -> case erlang:function_exported(Mod, cache_nodes, 1) of true -> Mod:cache_nodes(Host); false -> ejabberd_cluster:get_nodes() end. -spec delete_cache(module(), binary(), binary(), [ljid()]) -> ok. delete_cache(Mod, LUser, LServer, LJIDs) -> case use_cache(Mod, LServer, roster_version) of true -> ets_cache:delete(?ROSTER_VERSION_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)); false -> ok end, case use_cache(Mod, LServer, roster) of true -> Nodes = cache_nodes(Mod, LServer), ets_cache:delete(?ROSTER_CACHE, {LUser, LServer}, Nodes), lists:foreach( fun(LJID) -> ets_cache:delete( ?ROSTER_ITEM_CACHE, {LUser, LServer, jid:remove_resource(LJID)}, Nodes) end, LJIDs); false -> ok end. export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import_info() -> [{<<"roster_version">>, 2}, {<<"rostergroups">>, 3}, {<<"rosterusers">>, 10}]. import_start(LServer, DBType) -> Mod = gen_mod:db_mod(DBType, ?MODULE), ets:new(rostergroups_tmp, [private, named_table, bag]), Mod:init(LServer, []), ok. import_stop(_LServer, _DBType) -> ets:delete(rostergroups_tmp), ok. -ifdef(NEW_SQL_SCHEMA). -define(ROW_LENGTH, 10). -else. -define(ROW_LENGTH, 9). -endif. import(LServer, {sql, _}, _DBType, <<"rostergroups">>, [LUser, SJID, Group]) -> LJID = jid:tolower(jid:decode(SJID)), ets:insert(rostergroups_tmp, {{LUser, LServer, LJID}, Group}), ok; import(LServer, {sql, _}, DBType, <<"rosterusers">>, Row) -> I = mod_roster_sql:raw_to_record(LServer, lists:sublist(Row, ?ROW_LENGTH)), Groups = [G || {_, G} <- ets:lookup(rostergroups_tmp, I#roster.usj)], RosterItem = I#roster{groups = Groups}, Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, <<"rosterusers">>, RosterItem); import(LServer, {sql, _}, DBType, <<"roster_version">>, [LUser, Ver]) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, <<"roster_version">>, [LUser, Ver]). mod_opt_type(access) -> fun acl:access_rules_validator/1; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(store_current_id) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(versioning) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [access, db_type, iqdisc, store_current_id, versioning, cache_life_time, cache_size, use_cache, cache_missed]. ejabberd-18.01/src/ejabberd_update.erl0000644000232200023220000001547413225664356020251 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : ejabberd_update.erl %%% Author : Alexey Shchepin %%% Purpose : ejabberd code updater %%% Created : 27 Jan 2006 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(ejabberd_update). -author('alexey@process-one.net'). %% API -export([update/0, update/1, update_info/0]). -include("ejabberd.hrl"). -include("logger.hrl"). %%==================================================================== %% API %%==================================================================== %% Update all the modified modules update() -> case update_info() of {ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} -> Eval = eval_script( LowLevelScript, [], [{ejabberd, "", filename:join(Dir, "..")}]), ?DEBUG("eval: ~p~n", [Eval]), Eval; {error, Reason} -> {error, Reason} end. %% Update only the specified modules update(ModulesToUpdate) -> case update_info() of {ok, Dir, UpdatedBeamsAll, _Script, _LowLevelScript, _Check} -> UpdatedBeamsNow = [A || A <- UpdatedBeamsAll, B <- ModulesToUpdate, A == B], {_, LowLevelScript, _} = build_script(Dir, UpdatedBeamsNow), Eval = eval_script( LowLevelScript, [], [{ejabberd, "", filename:join(Dir, "..")}]), ?DEBUG("eval: ~p~n", [Eval]), Eval; {error, Reason} -> {error, Reason} end. eval_script(Script, Apps, LibDirs) -> release_handler_1:eval_script(Script, Apps, LibDirs, [], []). %% Get information about the modified modules update_info() -> Dir = filename:dirname(code:which(ejabberd)), case file:list_dir(Dir) of {ok, Files} -> update_info(Dir, Files); {error, Reason} -> {error, Reason} end. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- update_info(Dir, Files) -> Beams = lists:sort(get_beams(Files)), UpdatedBeams = get_updated_beams(Beams), ?DEBUG("beam files: ~p~n", [UpdatedBeams]), {Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams), {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}. get_beams(Files) -> [list_to_atom(filename:rootname(FN)) || FN <- Files, lists:suffix(".beam", FN)]. %% Return only the beams that have different version get_updated_beams(Beams) -> lists:filter( fun(Module) -> NewVsn = get_new_version(Module), case code:is_loaded(Module) of {file, _} -> CurVsn = get_current_version(Module), (NewVsn /= CurVsn andalso NewVsn /= unknown_version); false -> false end end, Beams). get_new_version(Module) -> Path = code:which(Module), VersionRes = beam_lib:version(Path), case VersionRes of {ok, {Module, NewVsn}} -> NewVsn; %% If a m1.erl has -module("m2"): _ -> unknown_version end. get_current_version(Module) -> Attrs = Module:module_info(attributes), case lists:keysearch(vsn, 1, Attrs) of {value, {vsn, CurVsn}} -> CurVsn; _ -> unknown_version end. %% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check} build_script(Dir, UpdatedBeams) -> Script = make_script(UpdatedBeams), LowLevelScript = make_low_level_script(UpdatedBeams, Script), Check = release_handler_1:check_script( LowLevelScript, [{ejabberd, "", filename:join(Dir, "..")}]), Check1 = case Check of {ok, []} -> ?DEBUG("script: ~p~n", [Script]), ?DEBUG("low level script: ~p~n", [LowLevelScript]), ?DEBUG("check: ~p~n", [Check]), ok; _ -> ?ERROR_MSG("script: ~p~n", [Script]), ?ERROR_MSG("low level script: ~p~n", [LowLevelScript]), ?ERROR_MSG("check: ~p~n", [Check]), error end, {Script, LowLevelScript, Check1}. %% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl -record(application, {name, %% Name of the application, atom(). type = permanent, %% Application start type, atom(). vsn = "", %% Version of the application, string(). id = "", %% Id of the application, string(). description = "", %% Description of application, string(). modules = [], %% [Module | {Module,Vsn}] of modules %% incorporated in the application, %% Module = atom(), Vsn = string(). uses = [], %% [Application] list of applications required %% by the application, Application = atom(). includes = [], %% [Application] list of applications included %% by the application, Application = atom(). regs = [], %% [RegNames] a list of registered process %% names used by the application, RegNames = %% atom(). env = [], %% [{Key,Value}] environment variable of %% application, Key = Value = term(). maxT = infinity, %% Max time an application may exist, %% integer() | infinity. maxP = infinity, %% Max number of processes in an application, %% integer() | infinity. mod = [], %% [] | {Mod, StartArgs}, Mod= atom(), %% StartArgs = list(). start_phases = [], %% [] | {Phase, PhaseArgs}, Phase = atom(), %% PhaseArgs = list(). dir = "" %% The directory where the .app file was %% found (internal use). }). make_script(UpdatedBeams) -> lists:map( fun(Module) -> {ok, {Module, [{attributes, NewAttrs}]}} = beam_lib:chunks(code:which(Module), [attributes]), CurAttrs = Module:module_info(attributes), case lists:keysearch(update_info, 1, NewAttrs) of {value, {_, [{update, _}]}} -> case lists:keysearch(update_info, 1, CurAttrs) of {value, {_, [{update, Extra}]}} -> {update, Module, {advanced, Extra}}; false -> {update, Module, {advanced, 0}} end; false -> {load_module, Module} end end, UpdatedBeams). make_low_level_script(UpdatedBeams, Script) -> EJDApp = #application{name = ejabberd, modules = UpdatedBeams}, {ok, LowLevelScript} = systools_rc:translate_scripts([Script], [EJDApp], [EJDApp]), LowLevelScript. ejabberd-18.01/src/mod_client_state.erl0000644000232200023220000003255213225664356020462 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_client_state.erl %%% Author : Holger Weiss %%% Purpose : Filter stanzas sent to inactive clients (XEP-0352) %%% Created : 11 Sep 2014 by Holger Weiss %%% %%% %%% ejabberd, Copyright (C) 2014-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_client_state). -author('holger@zedat.fu-berlin.de'). -protocol({xep, 85, '2.1'}). -protocol({xep, 352, '0.1'}). -behavior(gen_mod). %% gen_mod callbacks. -export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]). %% ejabberd_hooks callbacks. -export([filter_presence/1, filter_chat_states/1, filter_pep/1, filter_other/1, c2s_stream_started/2, add_stream_feature/2, c2s_copy_session/2, c2s_authenticated_packet/2, c2s_session_resumed/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -define(CSI_QUEUE_MAX, 100). -type csi_type() :: presence | chatstate | {pep, binary()}. -type csi_queue() :: {non_neg_integer(), map()}. -type csi_timestamp() :: {non_neg_integer(), erlang:timestamp()}. -type csi_key() :: {ljid(), csi_type()}. -type csi_element() :: {csi_timestamp(), stanza()}. -type c2s_state() :: ejabberd_c2s:state(). -type filter_acc() :: {stanza() | drop, c2s_state()}. %%-------------------------------------------------------------------- %% gen_mod callbacks. %%-------------------------------------------------------------------- -spec start(binary(), gen_mod:opts()) -> ok. start(Host, Opts) -> QueuePresence = gen_mod:get_opt(queue_presence, Opts, true), QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts, true), QueuePEP = gen_mod:get_opt(queue_pep, Opts, true), if QueuePresence; QueueChatStates; QueuePEP -> register_hooks(Host), if QueuePresence -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_presence, 50); true -> ok end, if QueueChatStates -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_chat_states, 50); true -> ok end, if QueuePEP -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_pep, 50); true -> ok end; true -> ok end. -spec stop(binary()) -> ok. stop(Host) -> QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence, true), QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states, true), QueuePEP = gen_mod:get_module_opt(Host, ?MODULE, queue_pep, true), if QueuePresence; QueueChatStates; QueuePEP -> unregister_hooks(Host), if QueuePresence -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_presence, 50); true -> ok end, if QueueChatStates -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_chat_states, 50); true -> ok end, if QueuePEP -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_pep, 50); true -> ok end; true -> ok end. -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. reload(Host, NewOpts, _OldOpts) -> QueuePresence = gen_mod:get_opt(queue_presence, NewOpts, true), QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts, true), QueuePEP = gen_mod:get_opt(queue_pep, NewOpts, true), if QueuePresence; QueueChatStates; QueuePEP -> register_hooks(Host); true -> unregister_hooks(Host) end, if QueuePresence -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_presence, 50); true -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_presence, 50) end, if QueueChatStates -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_chat_states, 50); true -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_chat_states, 50) end, if QueuePEP -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_pep, 50); true -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_pep, 50) end. -spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. mod_opt_type(queue_presence) -> fun(B) when is_boolean(B) -> B end; mod_opt_type(queue_chat_states) -> fun(B) when is_boolean(B) -> B end; mod_opt_type(queue_pep) -> fun(B) when is_boolean(B) -> B end; mod_opt_type(_) -> [queue_presence, queue_chat_states, queue_pep]. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> []. -spec register_hooks(binary()) -> ok. register_hooks(Host) -> ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE, c2s_stream_started, 50), ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, add_stream_feature, 50), ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE, c2s_authenticated_packet, 50), ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_other, 75). -spec unregister_hooks(binary()) -> ok. unregister_hooks(Host) -> ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE, c2s_stream_started, 50), ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, add_stream_feature, 50), ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE, c2s_authenticated_packet, 50), ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_other, 75). %%-------------------------------------------------------------------- %% ejabberd_hooks callbacks. %%-------------------------------------------------------------------- -spec c2s_stream_started(c2s_state(), stream_start()) -> c2s_state(). c2s_stream_started(State, _) -> init_csi_state(State). -spec c2s_authenticated_packet(c2s_state(), xmpp_element()) -> c2s_state(). c2s_authenticated_packet(C2SState, #csi{type = active}) -> C2SState1 = C2SState#{csi_state => active}, flush_queue(C2SState1); c2s_authenticated_packet(C2SState, #csi{type = inactive}) -> C2SState#{csi_state => inactive}; c2s_authenticated_packet(C2SState, _) -> C2SState. -spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state(). c2s_copy_session(C2SState, #{csi_queue := Q}) -> C2SState#{csi_queue => Q}; c2s_copy_session(C2SState, _) -> C2SState. -spec c2s_session_resumed(c2s_state()) -> c2s_state(). c2s_session_resumed(C2SState) -> flush_queue(C2SState). -spec filter_presence(filter_acc()) -> filter_acc(). filter_presence({#presence{meta = #{csi_resend := true}}, _} = Acc) -> Acc; filter_presence({#presence{to = To, type = Type} = Pres, #{csi_state := inactive} = C2SState}) when Type == available; Type == unavailable -> ?DEBUG("Got availability presence stanza for ~s", [jid:encode(To)]), enqueue_stanza(presence, Pres, C2SState); filter_presence(Acc) -> Acc. -spec filter_chat_states(filter_acc()) -> filter_acc(). filter_chat_states({#message{meta = #{csi_resend := true}}, _} = Acc) -> Acc; filter_chat_states({#message{from = From, to = To} = Msg, #{csi_state := inactive} = C2SState} = Acc) -> case xmpp_util:is_standalone_chat_state(Msg) of true -> case {From, To} of {#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}} -> %% Don't queue (carbon copies of) chat states from other %% resources, as they might be used to sync the state of %% conversations across clients. Acc; _ -> ?DEBUG("Got standalone chat state notification for ~s", [jid:encode(To)]), enqueue_stanza(chatstate, Msg, C2SState) end; false -> Acc end; filter_chat_states(Acc) -> Acc. -spec filter_pep(filter_acc()) -> filter_acc(). filter_pep({#message{meta = #{csi_resend := true}}, _} = Acc) -> Acc; filter_pep({#message{to = To} = Msg, #{csi_state := inactive} = C2SState} = Acc) -> case get_pep_node(Msg) of undefined -> Acc; Node -> ?DEBUG("Got PEP notification for ~s", [jid:encode(To)]), enqueue_stanza({pep, Node}, Msg, C2SState) end; filter_pep(Acc) -> Acc. -spec filter_other(filter_acc()) -> filter_acc(). filter_other({Stanza, #{jid := JID} = C2SState} = Acc) when ?is_stanza(Stanza) -> case xmpp:get_meta(Stanza) of #{csi_resend := true} -> Acc; _ -> ?DEBUG("Won't add stanza for ~s to CSI queue", [jid:encode(JID)]), From = case xmpp:get_from(Stanza) of undefined -> JID; F -> F end, C2SState1 = dequeue_sender(From, C2SState), {Stanza, C2SState1} end; filter_other(Acc) -> Acc. -spec add_stream_feature([xmpp_element()], binary()) -> [xmpp_element()]. add_stream_feature(Features, Host) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> [#feature_csi{xmlns = <<"urn:xmpp:csi:0">>} | Features]; false -> Features end. %%-------------------------------------------------------------------- %% Internal functions. %%-------------------------------------------------------------------- -spec init_csi_state(c2s_state()) -> c2s_state(). init_csi_state(C2SState) -> C2SState#{csi_state => active, csi_queue => queue_new()}. -spec enqueue_stanza(csi_type(), stanza(), c2s_state()) -> filter_acc(). enqueue_stanza(Type, Stanza, #{csi_state := inactive, csi_queue := Q} = C2SState) -> case queue_len(Q) >= ?CSI_QUEUE_MAX of true -> ?DEBUG("CSI queue too large, going to flush it", []), C2SState1 = flush_queue(C2SState), enqueue_stanza(Type, Stanza, C2SState1); false -> From = jid:tolower(xmpp:get_from(Stanza)), Q1 = queue_in({From, Type}, Stanza, Q), {stop, {drop, C2SState#{csi_queue => Q1}}} end; enqueue_stanza(_Type, Stanza, State) -> {Stanza, State}. -spec dequeue_sender(jid(), c2s_state()) -> c2s_state(). dequeue_sender(#jid{luser = U, lserver = S} = Sender, #{jid := JID} = C2SState) -> case maps:get(csi_queue, C2SState, undefined) of undefined -> %% This may happen when the module is (re)loaded in runtime init_csi_state(C2SState); Q -> ?DEBUG("Flushing packets of ~s@~s from CSI queue of ~s", [U, S, jid:encode(JID)]), {Elems, Q1} = queue_take(Sender, Q), C2SState1 = flush_stanzas(C2SState, Elems), C2SState1#{csi_queue => Q1} end. -spec flush_queue(c2s_state()) -> c2s_state(). flush_queue(#{csi_queue := Q, jid := JID} = C2SState) -> ?DEBUG("Flushing CSI queue of ~s", [jid:encode(JID)]), C2SState1 = flush_stanzas(C2SState, queue_to_list(Q)), C2SState1#{csi_queue => queue_new()}. -spec flush_stanzas(c2s_state(), [{csi_type(), csi_timestamp(), stanza()}]) -> c2s_state(). flush_stanzas(#{lserver := LServer} = C2SState, Elems) -> lists:foldl( fun({Time, Stanza}, AccState) -> Stanza1 = add_delay_info(Stanza, LServer, Time), ejabberd_c2s:send(AccState, Stanza1) end, C2SState, Elems). -spec add_delay_info(stanza(), binary(), csi_timestamp()) -> stanza(). add_delay_info(Stanza, LServer, {_Seq, TimeStamp}) -> Stanza1 = xmpp_util:add_delay_info( Stanza, jid:make(LServer), TimeStamp, <<"Client Inactive">>), xmpp:put_meta(Stanza1, csi_resend, true). -spec get_pep_node(message()) -> binary() | undefined. get_pep_node(#message{from = #jid{luser = <<>>}}) -> %% It's not PEP. undefined; get_pep_node(#message{} = Msg) -> case xmpp:get_subtag(Msg, #ps_event{}) of #ps_event{items = #ps_items{node = Node}} -> Node; _ -> undefined end. %%-------------------------------------------------------------------- %% Queue interface %%-------------------------------------------------------------------- -spec queue_new() -> csi_queue(). queue_new() -> {0, #{}}. -spec queue_in(csi_key(), stanza(), csi_queue()) -> csi_queue(). queue_in(Key, Stanza, {Seq, Q}) -> Seq1 = Seq + 1, Time = {Seq1, p1_time_compat:timestamp()}, Q1 = maps:put(Key, {Time, Stanza}, Q), {Seq1, Q1}. -spec queue_take(jid(), csi_queue()) -> {[csi_element()], csi_queue()}. queue_take(#jid{luser = LUser, lserver = LServer}, {Seq, Q}) -> {Vals, Q1} = maps:fold(fun({{U, S, _}, _} = Key, Val, {AccVals, AccQ}) when U == LUser, S == LServer -> {[Val | AccVals], maps:remove(Key, AccQ)}; (_, _, Acc) -> Acc end, {[], Q}, Q), {lists:keysort(1, Vals), {Seq, Q1}}. -spec queue_len(csi_queue()) -> non_neg_integer(). queue_len({_, Q}) -> maps:size(Q). -spec queue_to_list(csi_queue()) -> [csi_element()]. queue_to_list({_, Q}) -> lists:keysort(1, maps:values(Q)). ejabberd-18.01/src/pubsub_subscription.erl0000644000232200023220000003036413225664356021250 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pubsub_subscription.erl %%% Author : Brian Cully %%% Purpose : Handle pubsub subscriptions options %%% Created : 29 May 2009 by Brian Cully %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(pubsub_subscription). -author("bjc@kublai.com"). %% API -export([init/3, subscribe_node/3, unsubscribe_node/3, get_subscription/3, set_subscription/4, make_subid/0, get_options_xform/2, parse_options_xform/1]). % Internal function also exported for use in transactional bloc from pubsub plugins -export([add_subscription/3, delete_subscription/3, read_subscription/3, write_subscription/4]). -include("pubsub.hrl"). -include("xmpp.hrl"). -define(PUBSUB_DELIVER, <<"pubsub#deliver">>). -define(PUBSUB_DIGEST, <<"pubsub#digest">>). -define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>). -define(PUBSUB_EXPIRE, <<"pubsub#expire">>). -define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>). -define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>). -define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>). -define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>). -define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>). -define(DIGEST_LABEL, <<"Whether an entity wants to receive digests " "(aggregations) of notifications or all notifications individually">>). -define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between " "sending any two notification digests">>). -define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>). -define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an " "XMPP message body in addition to the payload format">>). -define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>). -define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>). -define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>). -define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>). -define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>). -define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>). -define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>). -define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>). -define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>). -define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>). -define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>). -define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>). %%==================================================================== %% API %%==================================================================== init(_Host, _ServerHost, _Opts) -> ok = create_table(). subscribe_node(JID, NodeId, Options) -> case catch mnesia:sync_dirty(fun add_subscription/3, [JID, NodeId, Options]) of {'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error}; Result -> {result, Result} end. unsubscribe_node(JID, NodeId, SubID) -> case catch mnesia:sync_dirty(fun delete_subscription/3, [JID, NodeId, SubID]) of {'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error}; Result -> {result, Result} end. get_subscription(JID, NodeId, SubID) -> case catch mnesia:sync_dirty(fun read_subscription/3, [JID, NodeId, SubID]) of {'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error}; Result -> {result, Result} end. set_subscription(JID, NodeId, SubID, Options) -> case catch mnesia:sync_dirty(fun write_subscription/4, [JID, NodeId, SubID, Options]) of {'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error}; Result -> {result, Result} end. get_options_xform(Lang, Options) -> Keys = [deliver, show_values, subscription_type, subscription_depth], XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys], {result, #xdata{type = form, fields = [#xdata_field{type = hidden, var = <<"FORM_TYPE">>, values = [?NS_PUBSUB_SUB_OPTIONS]}| XFields]}}. parse_options_xform(XFields) -> Opts = set_xoption(XFields, []), {result, Opts}. %%==================================================================== %% Internal functions %%==================================================================== create_table() -> case ejabberd_mnesia:create(?MODULE, pubsub_subscription, [{disc_copies, [node()]}, {attributes, record_info(fields, pubsub_subscription)}, {type, set}]) of {atomic, ok} -> ok; {aborted, {already_exists, _}} -> ok; Other -> Other end. -spec add_subscription(_JID :: ljid(), _NodeId :: mod_pubsub:nodeIdx(), Options :: [] | mod_pubsub:subOptions()) -> SubId :: mod_pubsub:subId(). add_subscription(_JID, _NodeId, []) -> make_subid(); add_subscription(_JID, _NodeId, Options) -> SubID = make_subid(), mnesia:write(#pubsub_subscription{subid = SubID, options = Options}), SubID. -spec delete_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId()) -> ok. delete_subscription(_JID, _NodeId, SubID) -> mnesia:delete({pubsub_subscription, SubID}). -spec read_subscription(_JID :: ljid(), _NodeId :: _, SubID :: mod_pubsub:subId()) -> mod_pubsub:pubsubSubscription() | {error, notfound}. read_subscription(_JID, _NodeId, SubID) -> case mnesia:read({pubsub_subscription, SubID}) of [Sub] -> Sub; _ -> {error, notfound} end. -spec write_subscription(_JID :: ljid(), _NodeId :: _, SubID :: mod_pubsub:subId(), Options :: mod_pubsub:subOptions()) -> ok. write_subscription(_JID, _NodeId, SubID, Options) -> mnesia:write(#pubsub_subscription{subid = SubID, options = Options}). -spec make_subid() -> SubId::mod_pubsub:subId(). make_subid() -> {T1, T2, T3} = p1_time_compat:timestamp(), (str:format("~.16B~.16B~.16B", [T1, T2, T3])). %% %% Subscription XForm processing. %% %% Return processed options, with types converted and so forth, using %% Opts as defaults. set_xoption([], Opts) -> Opts; set_xoption([{Var, Value} | T], Opts) -> NewOpts = case var_xfield(Var) of {error, _} -> Opts; Key -> Val = val_xfield(Key, Value), lists:keystore(Key, 1, Opts, {Key, Val}) end, set_xoption(T, NewOpts). %% Return the options list's key for an XForm var. %% Convert Values for option list's Key. var_xfield(?PUBSUB_DELIVER) -> deliver; var_xfield(?PUBSUB_DIGEST) -> digest; var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency; var_xfield(?PUBSUB_EXPIRE) -> expire; var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body; var_xfield(?PUBSUB_SHOW_VALUES) -> show_values; var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type; var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth; var_xfield(_) -> {error, badarg}. val_xfield(deliver = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(digest = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(digest_frequency = Opt, [Val]) -> case catch binary_to_integer(Val) of N when is_integer(N) -> N; _ -> Txt = {<<"Value of '~s' should be integer">>, [Opt]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)} end; val_xfield(expire = Opt, [Val]) -> try xmpp_util:decode_timestamp(Val) catch _:{bad_timestamp, _} -> Txt = {<<"Value of '~s' should be datetime string">>, [Opt]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)} end; val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(show_values, Vals) -> Vals; val_xfield(subscription_type, [<<"items">>]) -> items; val_xfield(subscription_type, [<<"nodes">>]) -> nodes; val_xfield(subscription_depth, [<<"all">>]) -> all; val_xfield(subscription_depth = Opt, [Depth]) -> case catch binary_to_integer(Depth) of N when is_integer(N) -> N; _ -> Txt = {<<"Value of '~s' should be integer">>, [Opt]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)} end. %% Convert XForm booleans to Erlang booleans. xopt_to_bool(_, <<"0">>) -> false; xopt_to_bool(_, <<"1">>) -> true; xopt_to_bool(_, <<"false">>) -> false; xopt_to_bool(_, <<"true">>) -> true; xopt_to_bool(Option, _) -> Txt = {<<"Value of '~s' should be boolean">>, [Option]}, {error, xmpp:err_not_acceptable(Txt, ?MYLANG)}. %% Return a field for an XForm for Key, with data filled in, if %% applicable, from Options. get_option_xfield(Lang, Key, Options) -> Var = xfield_var(Key), Label = xfield_label(Key), {Type, OptEls} = type_and_options(xfield_type(Key), Lang), Vals = case lists:keysearch(Key, 1, Options) of {value, {_, Val}} -> [xfield_val(Key, Val)]; false -> [] end, #xdata_field{type = Type, var = Var, label = translate:translate(Lang, Label), values = Vals, options = OptEls}. type_and_options({Type, Options}, Lang) -> {Type, [tr_xfield_options(O, Lang) || O <- Options]}; type_and_options(Type, _Lang) -> {Type, []}. tr_xfield_options({Value, Label}, Lang) -> #xdata_option{label = translate:translate(Lang, Label), value = Value}. xfield_var(deliver) -> ?PUBSUB_DELIVER; %xfield_var(digest) -> ?PUBSUB_DIGEST; %xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY; %xfield_var(expire) -> ?PUBSUB_EXPIRE; %xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY; xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES; xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE; xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH. xfield_type(deliver) -> boolean; %xfield_type(digest) -> boolean; %xfield_type(digest_frequency) -> 'text-single'; %xfield_type(expire) -> 'text-single'; %xfield_type(include_body) -> boolean; xfield_type(show_values) -> {'list-multi', [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, {<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; xfield_type(subscription_type) -> {'list-single', [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; xfield_type(subscription_depth) -> {'list-single', [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. %% Return the XForm variable label for a subscription option key. xfield_label(deliver) -> ?DELIVER_LABEL; %xfield_label(digest) -> ?DIGEST_LABEL; %xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL; %xfield_label(expire) -> ?EXPIRE_LABEL; %xfield_label(include_body) -> ?INCLUDE_BODY_LABEL; xfield_label(show_values) -> ?SHOW_VALUES_LABEL; %% Return the XForm value for a subscription option key. %% Convert erlang booleans to XForms. xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL; xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL. xfield_val(deliver, Val) -> [bool_to_xopt(Val)]; %xfield_val(digest, Val) -> [bool_to_xopt(Val)]; %xfield_val(digest_frequency, Val) -> % [integer_to_binary(Val))]; %xfield_val(expire, Val) -> % [jlib:now_to_utc_string(Val)]; %xfield_val(include_body, Val) -> [bool_to_xopt(Val)]; xfield_val(show_values, Val) -> Val; xfield_val(subscription_type, items) -> [<<"items">>]; xfield_val(subscription_type, nodes) -> [<<"nodes">>]; xfield_val(subscription_depth, all) -> [<<"all">>]; xfield_val(subscription_depth, N) -> [integer_to_binary(N)]. bool_to_xopt(true) -> <<"true">>; bool_to_xopt(false) -> <<"false">>. ejabberd-18.01/src/mod_mam.erl0000644000232200023220000010634713225664356016562 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_mam.erl %%% Author : Evgeniy Khramtsov %%% Purpose : Message Archive Management (XEP-0313) %%% Created : 4 Jul 2013 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2013-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_mam). -protocol({xep, 313, '0.6.1'}). -protocol({xep, 334, '0.2'}). -protocol({xep, 359, '0.5.0'}). -behaviour(gen_mod). %% API -export([start/2, stop/1, reload/3, depends/2]). -export([sm_receive_packet/1, user_receive_packet/1, user_send_packet/1, user_send_packet_strip_tag/1, process_iq_v0_2/1, process_iq_v0_3/1, disco_sm_features/5, remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/2, muc_filter_message/3, message_is_archived/3, delete_old_messages/2, get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/3, offline_message/1, export/1]). -include("xmpp.hrl"). -include("logger.hrl"). -include("mod_muc_room.hrl"). -include("ejabberd_commands.hrl"). -include("mod_mam.hrl"). -define(DEF_PAGE_SIZE, 50). -define(MAX_PAGE_SIZE, 250). -type c2s_state() :: ejabberd_c2s:state(). -callback init(binary(), gen_mod:opts()) -> any(). -callback remove_user(binary(), binary()) -> any(). -callback remove_room(binary(), binary(), binary()) -> any(). -callback delete_old_messages(binary() | global, erlang:timestamp(), all | chat | groupchat) -> any(). -callback extended_fields() -> [mam_query:property() | #xdata_field{}]. -callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat, jid(), binary(), recv | send, integer()) -> ok | any(). -callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any(). -callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error. -callback select(binary(), jid(), jid(), mam_query:result(), #rsm_set{} | undefined, chat | groupchat) -> {[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}. -callback use_cache(binary(), gen_mod:opts()) -> boolean(). -optional_callbacks([use_cache/2]). %%%=================================================================== %%% API %%%=================================================================== start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Host, Opts), register_iq_handlers(Host, IQDisc), ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 88), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 88), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet_strip_tag, 500), ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, offline_message, 50), ejabberd_hooks:add(muc_filter_message, Host, ?MODULE, muc_filter_message, 50), ejabberd_hooks:add(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(remove_room, Host, ?MODULE, remove_room, 50), ejabberd_hooks:add(get_room_config, Host, ?MODULE, get_room_config, 50), ejabberd_hooks:add(set_room_option, Host, ?MODULE, set_room_option, 50), case gen_mod:get_opt(assume_mam_usage, Opts, false) of true -> ejabberd_hooks:add(message_is_archived, Host, ?MODULE, message_is_archived, 50); false -> ok end, ejabberd_commands:register_commands(get_commands_spec()), ok. use_cache(Host, Opts) -> Mod = gen_mod:db_mod(Host, Opts, ?MODULE), case erlang:function_exported(Mod, use_cache, 2) of true -> Mod:use_cache(Host, Opts); false -> gen_mod:get_opt(use_cache, Opts, ejabberd_config:use_cache(Host)) end. init_cache(Host, Opts) -> case use_cache(Host, Opts) of true -> ets_cache:new(archive_prefs_cache, cache_opts(Host, Opts)); false -> ok end. cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt(cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt(cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt(cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {life_time, LifeTime}, {cache_missed, CacheMissed}]. stop(Host) -> unregister_iq_handlers(Host), ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 88), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 88), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet_strip_tag, 500), ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, offline_message, 50), ejabberd_hooks:delete(muc_filter_message, Host, ?MODULE, muc_filter_message, 50), ejabberd_hooks:delete(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:delete(remove_room, Host, ?MODULE, remove_room, 50), ejabberd_hooks:delete(get_room_config, Host, ?MODULE, get_room_config, 50), ejabberd_hooks:delete(set_room_option, Host, ?MODULE, set_room_option, 50), case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage, false) of true -> ejabberd_hooks:delete(message_is_archived, Host, ?MODULE, message_is_archived, 50); false -> ok end, case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> ejabberd_commands:unregister_commands(get_commands_spec()); true -> ok end. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if NewMod /= OldMod -> NewMod:init(Host, NewOpts); true -> ok end, ets_cache:setopts(archive_prefs_cache, cache_opts(Host, NewOpts)), case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> register_iq_handlers(Host, IQDisc); true -> ok end, case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts, false) of {false, true, _} -> ejabberd_hooks:add(message_is_archived, Host, ?MODULE, message_is_archived, 50); {false, false, _} -> ejabberd_hooks:delete(message_is_archived, Host, ?MODULE, message_is_archived, 50); true -> ok end. depends(_Host, _Opts) -> []. -spec register_iq_handlers(binary(), gen_iq_handler:type()) -> ok. register_iq_handlers(Host, IQDisc) -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_0, ?MODULE, process_iq_v0_3, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_0, ?MODULE, process_iq_v0_3, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_1, ?MODULE, process_iq_v0_3, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_1, ?MODULE, process_iq_v0_3, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_2, ?MODULE, process_iq_v0_3, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_2, ?MODULE, process_iq_v0_3, IQDisc). -spec unregister_iq_handlers(binary()) -> ok. unregister_iq_handlers(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_0), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_0), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_1), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_1), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_2), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_2). -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_user(LUser, LServer), ets_cache:delete(archive_prefs_cache, {LUser, LServer}, ejabberd_cluster:get_nodes()). -spec remove_room(binary(), binary(), binary()) -> ok. remove_room(LServer, Name, Host) -> LName = jid:nodeprep(Name), LHost = jid:nameprep(Host), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:remove_room(LServer, LName, LHost), ok. -spec get_room_config([muc_roomconfig:property()], mod_muc_room:state(), jid(), binary()) -> [muc_roomconfig:property()]. get_room_config(Fields, RoomState, _From, _Lang) -> Config = RoomState#state.config, Fields ++ [{mam, Config#config.mam}]. -spec set_room_option({pos_integer(), _}, muc_roomconfig:property(), binary()) -> {pos_integer(), _}. set_room_option(_Acc, {mam, Val}, _Lang) -> {#config.mam, Val}; set_room_option(Acc, _Property, _Lang) -> Acc. -spec sm_receive_packet(stanza()) -> stanza(). sm_receive_packet(#message{to = #jid{lserver = LServer}} = Pkt) -> init_stanza_id(Pkt, LServer); sm_receive_packet(Acc) -> Acc. -spec user_receive_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}. user_receive_packet({#message{from = Peer} = Pkt, #{jid := JID} = C2SState}) -> LUser = JID#jid.luser, LServer = JID#jid.lserver, Pkt1 = case should_archive(Pkt, LServer) of true -> case store_msg(Pkt, LUser, LServer, Peer, recv) of ok -> mark_stored_msg(Pkt, JID); _ -> Pkt end; _ -> Pkt end, {Pkt1, C2SState}; user_receive_packet(Acc) -> Acc. -spec user_send_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}. user_send_packet({#message{to = Peer} = Pkt, #{jid := JID} = C2SState}) -> LUser = JID#jid.luser, LServer = JID#jid.lserver, Pkt1 = init_stanza_id(Pkt, LServer), Pkt2 = case should_archive(Pkt1, LServer) of true -> case store_msg(xmpp:set_from_to(Pkt1, JID, Peer), LUser, LServer, Peer, send) of ok -> mark_stored_msg(Pkt1, JID); _ -> Pkt1 end; false -> Pkt1 end, {Pkt2, C2SState}; user_send_packet(Acc) -> Acc. -spec user_send_packet_strip_tag({stanza(), c2s_state()}) -> {stanza(), c2s_state()}. user_send_packet_strip_tag({#message{} = Pkt, #{jid := JID} = C2SState}) -> LServer = JID#jid.lserver, {strip_my_stanza_id(Pkt, LServer), C2SState}; user_send_packet_strip_tag(Acc) -> Acc. -spec offline_message({any(), message()}) -> {any(), message()}. offline_message({_Action, #message{from = Peer, to = To} = Pkt} = Acc) -> LUser = To#jid.luser, LServer = To#jid.lserver, case should_archive(Pkt, LServer) of true -> case store_msg(Pkt, LUser, LServer, Peer, recv) of ok -> {archived, mark_stored_msg(Pkt, To)}; _ -> Acc end; false -> Acc end. -spec muc_filter_message(message(), mod_muc_room:state(), binary()) -> message(). muc_filter_message(#message{from = From} = Pkt, #state{config = Config, jid = RoomJID} = MUCState, FromNick) -> LServer = RoomJID#jid.lserver, Pkt1 = init_stanza_id(Pkt, LServer), if Config#config.mam -> StorePkt = strip_x_jid_tags(Pkt1), case store_muc(MUCState, StorePkt, RoomJID, From, FromNick) of ok -> mark_stored_msg(Pkt1, RoomJID); _ -> Pkt1 end; true -> Pkt1 end; muc_filter_message(Acc, _MUCState, _FromNick) -> Acc. -spec get_stanza_id(stanza()) -> integer(). get_stanza_id(#message{meta = #{stanza_id := ID}}) -> ID. -spec init_stanza_id(stanza(), binary()) -> stanza(). init_stanza_id(Pkt, LServer) -> ID = p1_time_compat:system_time(micro_seconds), Pkt1 = strip_my_stanza_id(Pkt, LServer), xmpp:put_meta(Pkt1, stanza_id, ID). -spec set_stanza_id(stanza(), jid(), integer()) -> stanza(). set_stanza_id(Pkt, JID, ID) -> BareJID = jid:remove_resource(JID), Archived = #mam_archived{by = BareJID, id = ID}, StanzaID = #stanza_id{by = BareJID, id = ID}, NewEls = [Archived, StanzaID|xmpp:get_els(Pkt)], xmpp:set_els(Pkt, NewEls). -spec mark_stored_msg(message(), jid()) -> message(). mark_stored_msg(#message{meta = #{stanza_id := ID}} = Pkt, JID) -> Pkt1 = set_stanza_id(Pkt, JID, integer_to_binary(ID)), xmpp:put_meta(Pkt1, mam_archived, true). % Query archive v0.2 process_iq_v0_2(#iq{from = #jid{lserver = LServer}, to = #jid{lserver = LServer}, type = get, sub_els = [#mam_query{}]} = IQ) -> process_iq(LServer, IQ, chat); process_iq_v0_2(IQ) -> process_iq(IQ). % Query archive v0.3 process_iq_v0_3(#iq{from = #jid{lserver = LServer}, to = #jid{lserver = LServer}, type = set, sub_els = [#mam_query{}]} = IQ) -> process_iq(LServer, IQ, chat); process_iq_v0_3(#iq{from = #jid{lserver = LServer}, to = #jid{lserver = LServer}, type = get, sub_els = [#mam_query{}]} = IQ) -> process_iq(LServer, IQ); process_iq_v0_3(IQ) -> process_iq(IQ). -spec muc_process_iq(ignore | iq(), mod_muc_room:state()) -> ignore | iq(). muc_process_iq(#iq{type = T, lang = Lang, from = From, sub_els = [#mam_query{xmlns = NS}]} = IQ, MUCState) when (T == set andalso (NS /= ?NS_MAM_TMP)) orelse (T == get andalso NS == ?NS_MAM_TMP) -> case may_enter_room(From, MUCState) of true -> LServer = MUCState#state.server_host, Role = mod_muc_room:get_role(From, MUCState), process_iq(LServer, IQ, {groupchat, Role, MUCState}); false -> Text = <<"Only members may query archives of this room">>, xmpp:make_error(IQ, xmpp:err_forbidden(Text, Lang)) end; muc_process_iq(#iq{type = get, sub_els = [#mam_query{xmlns = NS}]} = IQ, MUCState) when NS /= ?NS_MAM_TMP -> LServer = MUCState#state.server_host, process_iq(LServer, IQ); muc_process_iq(IQ, _MUCState) -> IQ. parse_query(#mam_query{xmlns = ?NS_MAM_TMP, start = Start, 'end' = End, with = With, withtext = Text}, _Lang) -> {ok, [{start, Start}, {'end', End}, {with, With}, {withtext, Text}]}; parse_query(#mam_query{xdata = #xdata{}} = Query, Lang) -> X = xmpp_util:set_xdata_field( #xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [?NS_MAM_1]}, Query#mam_query.xdata), try mam_query:decode(X#xdata.fields) of Form -> {ok, Form} catch _:{mam_query, Why} -> Txt = mam_query:format_error(Why), {error, xmpp:err_bad_request(Txt, Lang)} end; parse_query(#mam_query{}, _Lang) -> {ok, []}. disco_sm_features(empty, From, To, Node, Lang) -> disco_sm_features({result, []}, From, To, Node, Lang); disco_sm_features({result, OtherFeatures}, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, <<"">>, _Lang) -> {result, [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1, ?NS_MAM_2, ?NS_SID_0 | OtherFeatures]}; disco_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec message_is_archived(boolean(), c2s_state(), message()) -> boolean(). message_is_archived(true, _C2SState, _Pkt) -> true; message_is_archived(false, #{lserver := LServer}, Pkt) -> case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage, false) of true -> is_archived(Pkt, LServer); false -> false end. delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>; TypeBin == <<"groupchat">>; TypeBin == <<"all">> -> CurrentTime = p1_time_compat:system_time(micro_seconds), Diff = Days * 24 * 60 * 60 * 1000000, TimeStamp = misc:usec_to_now(CurrentTime - Diff), Type = misc:binary_to_atom(TypeBin), DBTypes = lists:usort( lists:map( fun(Host) -> case gen_mod:db_type(Host, ?MODULE) of sql -> {sql, Host}; Other -> {Other, global} end end, ?MYHOSTS)), Results = lists:map( fun({DBType, ServerHost}) -> Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:delete_old_messages(ServerHost, TimeStamp, Type) end, DBTypes), case lists:filter(fun(Res) -> Res /= ok end, Results) of [] -> ok; [NotOk|_] -> NotOk end; delete_old_messages(_TypeBin, _Days) -> unsupported_type. export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). %%%=================================================================== %%% Internal functions %%%=================================================================== process_iq(LServer, #iq{sub_els = [#mam_query{xmlns = NS}]} = IQ) -> Mod = gen_mod:db_mod(LServer, ?MODULE), CommonFields = [{with, undefined}, {start, undefined}, {'end', undefined}], ExtendedFields = Mod:extended_fields(), Fields = mam_query:encode(CommonFields ++ ExtendedFields), X = xmpp_util:set_xdata_field( #xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [NS]}, #xdata{type = form, fields = Fields}), xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = X}). % Preference setting (both v0.2 & v0.3) process_iq(#iq{type = set, lang = Lang, sub_els = [#mam_prefs{default = undefined, xmlns = NS}]} = IQ) -> Why = {missing_attr, <<"default">>, <<"prefs">>, NS}, ErrTxt = xmpp:io_format_error(Why), xmpp:make_error(IQ, xmpp:err_bad_request(ErrTxt, Lang)); process_iq(#iq{from = #jid{luser = LUser, lserver = LServer}, to = #jid{lserver = LServer}, type = set, lang = Lang, sub_els = [#mam_prefs{xmlns = NS, default = Default, always = Always0, never = Never0}]} = IQ) -> Always = lists:usort(get_jids(Always0)), Never = lists:usort(get_jids(Never0)), case write_prefs(LUser, LServer, LServer, Default, Always, Never) of ok -> NewPrefs = prefs_el(Default, Always, Never, NS), xmpp:make_iq_result(IQ, NewPrefs); _Err -> Txt = <<"Database failure">>, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end; process_iq(#iq{from = #jid{luser = LUser, lserver = LServer}, to = #jid{lserver = LServer}, type = get, sub_els = [#mam_prefs{xmlns = NS}]} = IQ) -> Prefs = get_prefs(LUser, LServer), PrefsEl = prefs_el(Prefs#archive_prefs.default, Prefs#archive_prefs.always, Prefs#archive_prefs.never, NS), xmpp:make_iq_result(IQ, PrefsEl); process_iq(IQ) -> xmpp:make_error(IQ, xmpp:err_not_allowed()). process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang, sub_els = [SubEl]} = IQ, MsgType) -> case MsgType of chat -> maybe_activate_mam(LUser, LServer); {groupchat, _Role, _MUCState} -> ok end, case SubEl of #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) -> Txt = <<"Unsupported element">>, xmpp:make_error(IQ, xmpp:err_feature_not_implemented(Txt, Lang)); #mam_query{rsm = RSM, xmlns = NS} -> case parse_query(SubEl, Lang) of {ok, Query} -> NewRSM = limit_max(RSM, NS), select_and_send(LServer, Query, NewRSM, IQ, MsgType); {error, Err} -> xmpp:make_error(IQ, Err) end end. -spec should_archive(message(), binary()) -> boolean(). should_archive(#message{type = error}, _LServer) -> false; should_archive(#message{type = groupchat}, _LServer) -> false; should_archive(#message{meta = #{from_offline := true}}, _LServer) -> false; should_archive(#message{body = Body, subject = Subject, type = Type} = Pkt, LServer) -> case is_archived(Pkt, LServer) of true -> false; false -> case check_store_hint(Pkt) of store -> true; no_store -> false; none when Type == headline -> false; none -> xmpp:get_text(Body) /= <<>> orelse xmpp:get_text(Subject) /= <<>> end end; should_archive(_, _LServer) -> false. -spec strip_my_stanza_id(stanza(), binary()) -> stanza(). strip_my_stanza_id(Pkt, LServer) -> Els = xmpp:get_els(Pkt), NewEls = lists:filter( fun(El) -> Name = xmpp:get_name(El), NS = xmpp:get_ns(El), if (Name == <<"archived">> andalso NS == ?NS_MAM_TMP); (Name == <<"stanza-id">> andalso NS == ?NS_SID_0) -> try xmpp:decode(El) of #mam_archived{by = By} -> By#jid.lserver /= LServer; #stanza_id{by = By} -> By#jid.lserver /= LServer catch _:{xmpp_codec, _} -> false end; true -> true end end, Els), xmpp:set_els(Pkt, NewEls). -spec strip_x_jid_tags(stanza()) -> stanza(). strip_x_jid_tags(Pkt) -> Els = xmpp:get_els(Pkt), NewEls = lists:filter( fun(El) -> case xmpp:get_name(El) of <<"x">> -> NS = xmpp:get_ns(El), Items = if NS == ?NS_MUC_USER; NS == ?NS_MUC_ADMIN; NS == ?NS_MUC_OWNER -> try xmpp:decode(El) of #muc_user{items = Is} -> Is; #muc_admin{items = Is} -> Is; #muc_owner{items = Is} -> Is catch _:{xmpp_codec, _} -> [] end; true -> [] end, not lists:any( fun(#muc_item{jid = JID}) -> JID /= undefined end, Items); _ -> true end end, Els), xmpp:set_els(Pkt, NewEls). -spec should_archive_peer(binary(), binary(), #archive_prefs{}, jid()) -> boolean(). should_archive_peer(LUser, LServer, #archive_prefs{default = Default, always = Always, never = Never}, Peer) -> LPeer = jid:remove_resource(jid:tolower(Peer)), case lists:member(LPeer, Always) of true -> true; false -> case lists:member(LPeer, Never) of true -> false; false -> case Default of always -> true; never -> false; roster -> {Sub, _} = ejabberd_hooks:run_fold( roster_get_jid_info, LServer, {none, []}, [LUser, LServer, Peer]), Sub == both orelse Sub == from orelse Sub == to end end end. -spec should_archive_muc(message()) -> boolean(). should_archive_muc(#message{type = groupchat, body = Body, subject = Subj} = Pkt) -> case check_store_hint(Pkt) of store -> true; no_store -> false; none -> case xmpp:get_text(Body) of <<"">> -> case xmpp:get_text(Subj) of <<"">> -> false; _ -> true end; _ -> true end end; should_archive_muc(_) -> false. -spec check_store_hint(message()) -> store | no_store | none. check_store_hint(Pkt) -> case has_store_hint(Pkt) of true -> store; false -> case has_no_store_hint(Pkt) of true -> no_store; false -> none end end. -spec has_store_hint(message()) -> boolean(). has_store_hint(Message) -> xmpp:has_subtag(Message, #hint{type = 'store'}). -spec has_no_store_hint(message()) -> boolean(). has_no_store_hint(Message) -> xmpp:has_subtag(Message, #hint{type = 'no-store'}) orelse xmpp:has_subtag(Message, #hint{type = 'no-storage'}) orelse xmpp:has_subtag(Message, #hint{type = 'no-permanent-store'}) orelse xmpp:has_subtag(Message, #hint{type = 'no-permanent-storage'}). -spec is_archived(message(), binary()) -> boolean(). is_archived(Pkt, LServer) -> case xmpp:get_subtag(Pkt, #stanza_id{by = #jid{}}) of #stanza_id{by = #jid{lserver = LServer}} -> true; _ -> false end. -spec may_enter_room(jid(), mod_muc_room:state()) -> boolean(). may_enter_room(From, #state{config = #config{members_only = false}} = MUCState) -> mod_muc_room:get_affiliation(From, MUCState) /= outcast; may_enter_room(From, MUCState) -> mod_muc_room:is_occupant_or_admin(From, MUCState). -spec store_msg(message(), binary(), binary(), jid(), send | recv) -> ok | pass | any(). store_msg(Pkt, LUser, LServer, Peer, Dir) -> Prefs = get_prefs(LUser, LServer), case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of {true, #message{meta = #{sm_copy := true}}} -> ok; % Already stored. {true, _} -> case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt, [LUser, LServer, Peer, chat, Dir]) of drop -> pass; Pkt1 -> US = {LUser, LServer}, ID = get_stanza_id(Pkt1), El = xmpp:encode(Pkt1), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir, ID) end; {false, _} -> pass end. -spec store_muc(mod_muc_room:state(), message(), jid(), jid(), binary()) -> ok | pass | any(). store_muc(MUCState, Pkt, RoomJID, Peer, Nick) -> case should_archive_muc(Pkt) of true -> {U, S, _} = jid:tolower(RoomJID), LServer = MUCState#state.server_host, case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt, [U, S, Peer, groupchat, recv]) of drop -> pass; Pkt1 -> US = {U, S}, ID = get_stanza_id(Pkt1), El = xmpp:encode(Pkt1), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:store(El, LServer, US, groupchat, Peer, Nick, recv, ID) end; false -> pass end. write_prefs(LUser, LServer, Host, Default, Always, Never) -> Prefs = #archive_prefs{us = {LUser, LServer}, default = Default, always = Always, never = Never}, Mod = gen_mod:db_mod(Host, ?MODULE), case Mod:write_prefs(LUser, LServer, Prefs, Host) of ok -> ets_cache:delete(archive_prefs_cache, {LUser, LServer}, ejabberd_cluster:get_nodes()); _Err -> {error, db_failure} end. get_prefs(LUser, LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer}, fun() -> Mod:get_prefs(LUser, LServer) end), case Res of {ok, Prefs} -> Prefs; error -> ActivateOpt = gen_mod:get_module_opt( LServer, ?MODULE, request_activates_archiving, false), case ActivateOpt of true -> #archive_prefs{us = {LUser, LServer}, default = never}; false -> Default = gen_mod:get_module_opt( LServer, ?MODULE, default, never), #archive_prefs{us = {LUser, LServer}, default = Default} end end. prefs_el(Default, Always, Never, NS) -> #mam_prefs{default = Default, always = [jid:make(LJ) || LJ <- Always], never = [jid:make(LJ) || LJ <- Never], xmlns = NS}. maybe_activate_mam(LUser, LServer) -> ActivateOpt = gen_mod:get_module_opt( LServer, ?MODULE, request_activates_archiving, false), case ActivateOpt of true -> Mod = gen_mod:db_mod(LServer, ?MODULE), Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer}, fun() -> Mod:get_prefs(LUser, LServer) end), case Res of {ok, _Prefs} -> ok; error -> Default = gen_mod:get_module_opt( LServer, ?MODULE, default, never), write_prefs(LUser, LServer, LServer, Default, [], []) end; false -> ok end. select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) -> {Msgs, IsComplete, Count} = case MsgType of chat -> select(LServer, From, From, Query, RSM, MsgType); {groupchat, _Role, _MUCState} -> select(LServer, From, To, Query, RSM, MsgType) end, SortedMsgs = lists:keysort(2, Msgs), send(SortedMsgs, Count, IsComplete, IQ). select(_LServer, JidRequestor, JidArchive, Query, RSM, {groupchat, _Role, #state{config = #config{mam = false}, history = History}} = MsgType) -> Start = proplists:get_value(start, Query), End = proplists:get_value('end', Query), #lqueue{queue = Q} = History, L = p1_queue:len(Q), Msgs = lists:flatmap( fun({Nick, Pkt, _HaveSubject, Now, _Size}) -> TS = misc:now_to_usec(Now), case match_interval(Now, Start, End) and match_rsm(Now, RSM) of true -> case msg_to_el(#archive_msg{ id = integer_to_binary(TS), type = groupchat, timestamp = Now, peer = undefined, nick = Nick, packet = Pkt}, MsgType, JidRequestor, JidArchive) of {ok, Msg} -> [{integer_to_binary(TS), TS, Msg}]; {error, _} -> [] end; false -> [] end end, p1_queue:to_list(Q)), case RSM of #rsm_set{max = Max, before = Before} when is_binary(Before) -> {NewMsgs, IsComplete} = filter_by_max(lists:reverse(Msgs), Max), {NewMsgs, IsComplete, L}; #rsm_set{max = Max} -> {NewMsgs, IsComplete} = filter_by_max(Msgs, Max), {NewMsgs, IsComplete, L}; _ -> {Msgs, true, L} end; select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) -> case might_expose_jid(Query, MsgType) of true -> {[], true, 0}; false -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) end. msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick, peer = Peer, id = ID}, MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) -> try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of Pkt1 -> Pkt2 = set_stanza_id(Pkt1, JidArchive, ID), Pkt3 = maybe_update_from_to( Pkt2, JidRequestor, JidArchive, Peer, MsgType, Nick), Delay = #delay{stamp = TS, from = jid:make(LServer)}, {ok, #forwarded{xml_els = [xmpp:encode(Pkt3)], delay = Delay}} catch _:{xmpp_codec, Why} -> ?ERROR_MSG("Failed to decode raw element ~p from message " "archive of user ~s: ~s", [El, jid:encode(JidArchive), xmpp:format_error(Why)]), {error, invalid_xml} end. maybe_update_from_to(#message{sub_els = Els} = Pkt, JidRequestor, JidArchive, Peer, {groupchat, Role, #state{config = #config{anonymous = Anon}}}, Nick) -> ExposeJID = case {Peer, JidRequestor} of {undefined, _JidRequestor} -> false; {{U, S, _R}, #jid{luser = U, lserver = S}} -> true; {_Peer, _JidRequestor} when not Anon; Role == moderator -> true; {_Peer, _JidRequestor} -> false end, Items = case ExposeJID of true -> [#muc_user{items = [#muc_item{jid = Peer}]}]; false -> [] end, Pkt#message{from = jid:replace_resource(JidArchive, Nick), to = undefined, sub_els = Items ++ Els}; maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) -> Pkt. -spec send([{binary(), integer(), xmlel()}], non_neg_integer(), boolean(), iq()) -> iq() | ignore. send(Msgs, Count, IsComplete, #iq{from = From, to = To, sub_els = [#mam_query{id = QID, xmlns = NS}]} = IQ) -> Hint = #hint{type = 'no-store'}, Els = lists:map( fun({ID, _IDInt, El}) -> #message{from = To, to = From, sub_els = [#mam_result{xmlns = NS, id = ID, queryid = QID, sub_els = [El]}]} end, Msgs), RSMOut = make_rsm_out(Msgs, Count), Result = if NS == ?NS_MAM_TMP -> #mam_query{xmlns = NS, id = QID, rsm = RSMOut}; true -> #mam_fin{xmlns = NS, id = QID, rsm = RSMOut, complete = IsComplete} end, if NS /= ?NS_MAM_0 -> lists:foreach( fun(El) -> ejabberd_router:route(El) end, Els), xmpp:make_iq_result(IQ, Result); true -> ejabberd_router:route(xmpp:make_iq_result(IQ)), lists:foreach( fun(El) -> ejabberd_router:route(El) end, Els), ejabberd_router:route( #message{from = To, to = From, sub_els = [Result, Hint]}), ignore end. -spec make_rsm_out([{binary(), integer(), xmlel()}], non_neg_integer()) -> rsm_set(). make_rsm_out([], Count) -> #rsm_set{count = Count}; make_rsm_out([{FirstID, _, _}|_] = Msgs, Count) -> {LastID, _, _} = lists:last(Msgs), #rsm_set{first = #rsm_first{data = FirstID}, last = LastID, count = Count}. filter_by_max(Msgs, undefined) -> {Msgs, true}; filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 -> {lists:sublist(Msgs, Len), length(Msgs) =< Len}; filter_by_max(_Msgs, _Junk) -> {[], true}. -spec limit_max(rsm_set(), binary()) -> rsm_set() | undefined. limit_max(RSM, ?NS_MAM_TMP) -> RSM; % XEP-0313 v0.2 doesn't require clients to support RSM. limit_max(undefined, _NS) -> #rsm_set{max = ?DEF_PAGE_SIZE}; limit_max(#rsm_set{max = Max} = RSM, _NS) when not is_integer(Max) -> RSM#rsm_set{max = ?DEF_PAGE_SIZE}; limit_max(#rsm_set{max = Max} = RSM, _NS) when Max > ?MAX_PAGE_SIZE -> RSM#rsm_set{max = ?MAX_PAGE_SIZE}; limit_max(RSM, _NS) -> RSM. match_interval(Now, Start, undefined) -> Now >= Start; match_interval(Now, Start, End) -> (Now >= Start) and (Now =< End). match_rsm(Now, #rsm_set{'after' = ID}) when is_binary(ID), ID /= <<"">> -> Now1 = (catch misc:usec_to_now(binary_to_integer(ID))), Now > Now1; match_rsm(Now, #rsm_set{before = ID}) when is_binary(ID), ID /= <<"">> -> Now1 = (catch misc:usec_to_now(binary_to_integer(ID))), Now < Now1; match_rsm(_Now, _) -> true. might_expose_jid(Query, {groupchat, Role, #state{config = #config{anonymous = true}}}) when Role /= moderator -> proplists:is_defined(with, Query); might_expose_jid(_Query, _MsgType) -> false. get_jids(undefined) -> []; get_jids(Js) -> [jid:tolower(jid:remove_resource(J)) || J <- Js]. get_commands_spec() -> [#ejabberd_commands{name = delete_old_mam_messages, tags = [purge], desc = "Delete MAM messages older than DAYS", longdesc = "Valid message TYPEs: " "\"chat\", \"groupchat\", \"all\".", module = ?MODULE, function = delete_old_messages, args_desc = ["Type of messages to delete (chat, groupchat, all)", "Days to keep messages"], args_example = [<<"all">>, 31], args = [{type, binary}, {days, integer}], result = {res, rescode}}]. mod_opt_type(assume_mam_usage) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(default) -> fun (always) -> always; (never) -> never; (roster) -> roster end; mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(request_activates_archiving) -> fun (B) when is_boolean(B) -> B end; mod_opt_type(_) -> [assume_mam_usage, cache_life_time, cache_size, use_cache, cache_missed, db_type, default, iqdisc, request_activates_archiving]. ejabberd-18.01/src/mod_caps.erl0000644000232200023220000004464013225664356016733 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_caps.erl %%% Author : Magnus Henoch %%% Purpose : Request and cache Entity Capabilities (XEP-0115) %%% Created : 7 Oct 2006 by Magnus Henoch %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%% 2009, improvements from ProcessOne to support correct PEP handling %%% through s2s, use less memory, and speedup global caps handling %%%---------------------------------------------------------------------- -module(mod_caps). -author('henoch@dtek.chalmers.se'). -protocol({xep, 115, '1.5'}). -behaviour(gen_server). -behaviour(gen_mod). -export([read_caps/1, list_features/1, caps_stream_features/2, disco_features/5, disco_identity/5, disco_info/5, get_features/2, export/1, import_info/0, import/5, get_user_caps/2, import_start/2, import_stop/2]). %% gen_mod callbacks -export([start/2, stop/1, reload/3, depends/2]). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, handle_cast/2, terminate/2, code_change/3]). -export([user_send_packet/1, user_receive_packet/1, c2s_presence_in/2, mod_opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_caps.hrl"). -define(BAD_HASH_LIFETIME, 600). -record(state, {host = <<"">> :: binary()}). -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), {binary(), binary()}, [binary() | pos_integer()]) -> ok. -callback caps_read(binary(), {binary(), binary()}) -> {ok, non_neg_integer() | [binary()]} | error. -callback caps_write(binary(), {binary(), binary()}, non_neg_integer() | [binary()]) -> any(). start(Host, Opts) -> gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> gen_mod:stop_child(?MODULE, Host). -spec get_features(binary(), nothing | caps()) -> [binary()]. get_features(_Host, nothing) -> []; get_features(Host, #caps{node = Node, version = Version, exts = Exts}) -> SubNodes = [Version | Exts], lists:foldl(fun (SubNode, Acc) -> NodePair = {Node, SubNode}, case ets_cache:lookup(caps_features_cache, NodePair, caps_read_fun(Host, NodePair)) of {ok, Features} when is_list(Features) -> Features ++ Acc; _ -> Acc end end, [], SubNodes). -spec list_features(ejabberd_c2s:state()) -> [{ljid(), caps()}]. list_features(C2SState) -> Rs = maps:get(caps_resources, C2SState, gb_trees:empty()), gb_trees:to_list(Rs). -spec get_user_caps(jid(), ejabberd_c2s:state()) -> {ok, caps()} | error. get_user_caps(JID, C2SState) -> Rs = maps:get(caps_resources, C2SState, gb_trees:empty()), LJID = jid:tolower(JID), case gb_trees:lookup(LJID, Rs) of {value, Caps} -> {ok, Caps}; none -> error end. -spec read_caps(#presence{}) -> nothing | caps(). read_caps(Presence) -> case xmpp:get_subtag(Presence, #caps{}) of false -> nothing; Caps -> Caps end. -spec user_send_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. user_send_packet({#presence{type = available, from = #jid{luser = U, lserver = LServer} = From, to = #jid{luser = U, lserver = LServer, lresource = <<"">>}} = Pkt, #{jid := To} = State}) -> case read_caps(Pkt) of nothing -> ok; #caps{version = Version, exts = Exts} = Caps -> feature_request(LServer, From, To, Caps, [Version | Exts]) end, {Pkt, State}; user_send_packet(Acc) -> Acc. -spec user_receive_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. user_receive_packet({#presence{from = From, type = available} = Pkt, #{lserver := LServer, jid := To} = State}) -> IsRemote = not ejabberd_router:is_my_host(From#jid.lserver), if IsRemote -> case read_caps(Pkt) of nothing -> ok; #caps{version = Version, exts = Exts} = Caps -> feature_request(LServer, From, To, Caps, [Version | Exts]) end; true -> ok end, {Pkt, State}; user_receive_packet(Acc) -> Acc. -spec caps_stream_features([xmpp_element()], binary()) -> [xmpp_element()]. caps_stream_features(Acc, MyHost) -> case gen_mod:is_loaded(MyHost, ?MODULE) of true -> case make_my_disco_hash(MyHost) of <<"">> -> Acc; Hash -> [#caps{hash = <<"sha-1">>, node = ?EJABBERD_URI, version = Hash}|Acc] end; false -> Acc end. -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]} | empty. disco_features(Acc, From, To, Node, Lang) -> case is_valid_node(Node) of true -> ejabberd_hooks:run_fold(disco_local_features, To#jid.lserver, empty, [From, To, <<"">>, Lang]); false -> Acc end. -spec disco_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. disco_identity(Acc, From, To, Node, Lang) -> case is_valid_node(Node) of true -> ejabberd_hooks:run_fold(disco_local_identity, To#jid.lserver, [], [From, To, <<"">>, Lang]); false -> Acc end. -spec disco_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()]; ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()]. disco_info(Acc, Host, Module, Node, Lang) when is_atom(Module) -> case is_valid_node(Node) of true -> ejabberd_hooks:run_fold(disco_info, Host, [], [Host, Module, <<"">>, Lang]); false -> Acc end; disco_info(Acc, _, _, _Node, _Lang) -> Acc. -spec c2s_presence_in(ejabberd_c2s:state(), presence()) -> ejabberd_c2s:state(). c2s_presence_in(C2SState, #presence{from = From, to = To, type = Type} = Presence) -> {Subscription, _} = ejabberd_hooks:run_fold( roster_get_jid_info, To#jid.lserver, {none, []}, [To#jid.luser, To#jid.lserver, From]), ToSelf = (From#jid.luser == To#jid.luser) and (From#jid.lserver == To#jid.lserver), Insert = (Type == available) and ((Subscription == both) or (Subscription == to) or ToSelf), Delete = (Type == unavailable) or (Type == error), if Insert or Delete -> LFrom = jid:tolower(From), Rs = maps:get(caps_resources, C2SState, gb_trees:empty()), Caps = read_caps(Presence), NewRs = case Caps of nothing when Insert == true -> Rs; _ when Insert == true -> case gb_trees:lookup(LFrom, Rs) of {value, Caps} -> Rs; none -> ejabberd_hooks:run(caps_add, To#jid.lserver, [From, To, get_features(To#jid.lserver, Caps)]), gb_trees:insert(LFrom, Caps, Rs); _ -> ejabberd_hooks:run(caps_update, To#jid.lserver, [From, To, get_features(To#jid.lserver, Caps)]), gb_trees:update(LFrom, Caps, Rs) end; _ -> gb_trees:delete_any(LFrom, Rs) end, C2SState#{caps_resources => NewRs}; true -> C2SState end. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> []. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), if OldMod /= NewMod -> NewMod:init(Host, NewOpts); true -> ok end, case gen_mod:is_equal_opt(cache_size, NewOpts, OldOpts, ejabberd_config:cache_size(Host)) of {false, MaxSize, _} -> ets_cache:setopts(caps_features_cache, [{max_size, MaxSize}]), ets_cache:setopts(caps_requests_cache, [{max_size, MaxSize}]); true -> ok end, case gen_mod:is_equal_opt(cache_life_time, NewOpts, OldOpts, ejabberd_config:cache_life_time(Host)) of {false, Time, _} -> LifeTime = case Time of infinity -> infinity; _ -> timer:seconds(Time) end, ets_cache:setopts(caps_features_cache, [{life_time, LifeTime}]); true -> ok end. init([Host, Opts]) -> process_flag(trap_exit, true), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), init_cache(Host, Opts), Mod:init(Host, Opts), ejabberd_hooks:add(c2s_presence_in, Host, ?MODULE, c2s_presence_in, 75), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 75), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 75), ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, caps_stream_features, 75), ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE, caps_stream_features, 75), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 75), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 75), ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 75), {ok, #state{host = Host}}. handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call(_Req, _From, State) -> {reply, {error, badarg}, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({iq_reply, IQReply, {Host, From, To, Caps, SubNodes}}, State) -> feature_response(IQReply, Host, From, To, Caps, SubNodes), {noreply, State}; handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, State) -> Host = State#state.host, ejabberd_hooks:delete(c2s_presence_in, Host, ?MODULE, c2s_presence_in, 75), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 75), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 75), ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, caps_stream_features, 75), ejabberd_hooks:delete(s2s_in_post_auth_features, Host, ?MODULE, caps_stream_features, 75), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 75), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 75), ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 75), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. -spec feature_request(binary(), jid(), jid(), caps(), [binary()]) -> any(). feature_request(Host, From, To, Caps, [SubNode | Tail] = SubNodes) -> Node = Caps#caps.node, NodePair = {Node, SubNode}, case ets_cache:lookup(caps_features_cache, NodePair, caps_read_fun(Host, NodePair)) of {ok, Fs} when is_list(Fs) -> feature_request(Host, From, To, Caps, Tail); _ -> LTo = jid:tolower(To), case ets_cache:insert_new(caps_requests_cache, {LTo, NodePair}, ok) of true -> IQ = #iq{type = get, from = From, to = To, sub_els = [#disco_info{node = <>}]}, ejabberd_router:route_iq( IQ, {Host, From, To, Caps, SubNodes}, gen_mod:get_module_proc(Host, ?MODULE)); false -> ok end, feature_request(Host, From, To, Caps, Tail) end; feature_request(_Host, _From, _To, _Caps, []) -> ok. -spec feature_response(iq(), binary(), jid(), jid(), caps(), [binary()]) -> any(). feature_response(#iq{type = result, sub_els = [El]}, Host, From, To, Caps, [SubNode | SubNodes]) -> NodePair = {Caps#caps.node, SubNode}, try DiscoInfo = xmpp:decode(El), case check_hash(Caps, DiscoInfo) of true -> Features = DiscoInfo#disco_info.features, LServer = jid:nameprep(Host), Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:caps_write(LServer, NodePair, Features) of ok -> ets_cache:delete(caps_features_cache, NodePair); {error, _} -> ok end; false -> ok end catch _:{xmpp_codec, _Why} -> ok end, feature_request(Host, From, To, Caps, SubNodes); feature_response(_IQResult, Host, From, To, Caps, [_SubNode | SubNodes]) -> feature_request(Host, From, To, Caps, SubNodes). -spec caps_read_fun(binary(), {binary(), binary()}) -> fun(() -> {ok, [binary()] | non_neg_integer()} | error). caps_read_fun(Host, Node) -> LServer = jid:nameprep(Host), Mod = gen_mod:db_mod(LServer, ?MODULE), fun() -> Mod:caps_read(LServer, Node) end. -spec make_my_disco_hash(binary()) -> binary(). make_my_disco_hash(Host) -> JID = jid:make(Host), case {ejabberd_hooks:run_fold(disco_local_features, Host, empty, [JID, JID, <<"">>, <<"">>]), ejabberd_hooks:run_fold(disco_local_identity, Host, [], [JID, JID, <<"">>, <<"">>]), ejabberd_hooks:run_fold(disco_info, Host, [], [Host, undefined, <<"">>, <<"">>])} of {{result, Features}, Identities, Info} -> Feats = lists:map(fun ({{Feat, _Host}}) -> Feat; (Feat) -> Feat end, Features), DiscoInfo = #disco_info{identities = Identities, features = Feats, xdata = Info}, make_disco_hash(DiscoInfo, sha); _Err -> <<"">> end. -type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512. -spec make_disco_hash(disco_info(), digest_type()) -> binary(). make_disco_hash(DiscoInfo, Algo) -> Concat = list_to_binary([concat_identities(DiscoInfo), concat_features(DiscoInfo), concat_info(DiscoInfo)]), base64:encode(case Algo of md5 -> erlang:md5(Concat); sha -> crypto:hash(sha, Concat); sha224 -> crypto:hash(sha224, Concat); sha256 -> crypto:hash(sha256, Concat); sha384 -> crypto:hash(sha384, Concat); sha512 -> crypto:hash(sha512, Concat) end). -spec check_hash(caps(), disco_info()) -> boolean(). check_hash(Caps, DiscoInfo) -> case Caps#caps.hash of <<"md5">> -> Caps#caps.version == make_disco_hash(DiscoInfo, md5); <<"sha-1">> -> Caps#caps.version == make_disco_hash(DiscoInfo, sha); <<"sha-224">> -> Caps#caps.version == make_disco_hash(DiscoInfo, sha224); <<"sha-256">> -> Caps#caps.version == make_disco_hash(DiscoInfo, sha256); <<"sha-384">> -> Caps#caps.version == make_disco_hash(DiscoInfo, sha384); <<"sha-512">> -> Caps#caps.version == make_disco_hash(DiscoInfo, sha512); _ -> true end. -spec concat_features(disco_info()) -> iolist(). concat_features(#disco_info{features = Features}) -> lists:usort([[Feat, $<] || Feat <- Features]). -spec concat_identities(disco_info()) -> iolist(). concat_identities(#disco_info{identities = Identities}) -> lists:sort( [[Cat, $/, T, $/, Lang, $/, Name, $<] || #identity{category = Cat, type = T, lang = Lang, name = Name} <- Identities]). -spec concat_info(disco_info()) -> iolist(). concat_info(#disco_info{xdata = Xs}) -> lists:sort( [concat_xdata_fields(X) || #xdata{type = result} = X <- Xs]). -spec concat_xdata_fields(xdata()) -> iolist(). concat_xdata_fields(#xdata{fields = Fields} = X) -> Form = xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X), Res = [[Var, $<, lists:sort([[Val, $<] || Val <- Values])] || #xdata_field{var = Var, values = Values} <- Fields, is_binary(Var), Var /= <<"FORM_TYPE">>], [Form, $<, lists:sort(Res)]. -spec is_valid_node(binary()) -> boolean(). is_valid_node(Node) -> case str:tokens(Node, <<"#">>) of [?EJABBERD_URI|_] -> true; _ -> false end. init_cache(Host, Opts) -> CacheOpts = cache_opts(Host, Opts), case use_cache(Host, Opts) of true -> ets_cache:new(caps_features_cache, CacheOpts); false -> ok end, CacheSize = proplists:get_value(max_size, CacheOpts), ets_cache:new(caps_requests_cache, [{max_size, CacheSize}, {life_time, timer:seconds(?BAD_HASH_LIFETIME)}]). use_cache(Host, Opts) -> gen_mod:get_opt(use_cache, Opts, ejabberd_config:use_cache(Host)). cache_opts(Host, Opts) -> MaxSize = gen_mod:get_opt(cache_size, Opts, ejabberd_config:cache_size(Host)), CacheMissed = gen_mod:get_opt(cache_missed, Opts, ejabberd_config:cache_missed(Host)), LifeTime = case gen_mod:get_opt(cache_life_time, Opts, ejabberd_config:cache_life_time(Host)) of infinity -> infinity; I -> timer:seconds(I) end, [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). import_info() -> [{<<"caps_features">>, 4}]. import_start(LServer, DBType) -> ets:new(caps_features_tmp, [private, named_table, bag]), Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:init(LServer, []), ok. import(_LServer, {sql, _}, _DBType, <<"caps_features">>, [Node, SubNode, Feature, _TimeStamp]) -> Feature1 = case catch binary_to_integer(Feature) of I when is_integer(I), I>0 -> I; _ -> Feature end, ets:insert(caps_features_tmp, {{Node, SubNode}, Feature1}), ok. import_stop(LServer, DBType) -> import_next(LServer, DBType, ets:first(caps_features_tmp)), ets:delete(caps_features_tmp), ok. import_next(_LServer, _DBType, '$end_of_table') -> ok; import_next(LServer, DBType, NodePair) -> Features = [F || {_, F} <- ets:lookup(caps_features_tmp, NodePair)], Mod = gen_mod:db_mod(DBType, ?MODULE), Mod:import(LServer, NodePair, Features), import_next(LServer, DBType, ets:next(caps_features_tmp, NodePair)). mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; mod_opt_type(O) when O == use_cache; O == cache_missed -> fun (B) when is_boolean(B) -> B end; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(_) -> [cache_life_time, cache_size, use_cache, cache_missed, db_type]. ejabberd-18.01/src/mod_vcard_riak.erl0000644000232200023220000001322413225664356020104 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_vcard_riak.erl %%% Author : Evgeny Khramtsov %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_vcard_riak). -behaviour(mod_vcard). %% API -export([init/2, get_vcard/2, set_vcard/4, search/4, remove_user/2, search_fields/1, search_reported/1, import/3, stop/1]). -export([is_search_supported/1]). -include("xmpp.hrl"). -include("mod_vcard.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ok. stop(_Host) -> ok. is_search_supported(_LServer) -> false. get_vcard(LUser, LServer) -> case ejabberd_riak:get(vcard, vcard_schema(), {LUser, LServer}) of {ok, R} -> {ok, [R#vcard.vcard]}; {error, notfound} -> {ok, []}; _ -> error end. set_vcard(LUser, LServer, VCARD, #vcard_search{user = {User, _}, fn = FN, lfn = LFN, family = Family, lfamily = LFamily, given = Given, lgiven = LGiven, middle = Middle, lmiddle = LMiddle, nickname = Nickname, lnickname = LNickname, bday = BDay, lbday = LBDay, ctry = CTRY, lctry = LCTRY, locality = Locality, llocality = LLocality, email = EMail, lemail = LEMail, orgname = OrgName, lorgname = LOrgName, orgunit = OrgUnit, lorgunit = LOrgUnit}) -> US = {LUser, LServer}, {atomic, ejabberd_riak:put(#vcard{us = US, vcard = VCARD}, vcard_schema(), [{'2i', [{<<"user">>, User}, {<<"luser">>, LUser}, {<<"fn">>, FN}, {<<"lfn">>, LFN}, {<<"family">>, Family}, {<<"lfamily">>, LFamily}, {<<"given">>, Given}, {<<"lgiven">>, LGiven}, {<<"middle">>, Middle}, {<<"lmiddle">>, LMiddle}, {<<"nickname">>, Nickname}, {<<"lnickname">>, LNickname}, {<<"bday">>, BDay}, {<<"lbday">>, LBDay}, {<<"ctry">>, CTRY}, {<<"lctry">>, LCTRY}, {<<"locality">>, Locality}, {<<"llocality">>, LLocality}, {<<"email">>, EMail}, {<<"lemail">>, LEMail}, {<<"orgname">>, OrgName}, {<<"lorgname">>, LOrgName}, {<<"orgunit">>, OrgUnit}, {<<"lorgunit">>, LOrgUnit}]}])}. search(_LServer, _Data, _AllowReturnAll, _MaxMatch) -> []. search_fields(_LServer) -> []. search_reported(_LServer) -> []. remove_user(LUser, LServer) -> {atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}. import(LServer, <<"vcard">>, [LUser, XML, _TimeStamp]) -> El = fxml_stream:parse_element(XML), VCard = #vcard{us = {LUser, LServer}, vcard = El}, #vcard_search{fn = FN, lfn = LFN, family = Family, lfamily = LFamily, given = Given, lgiven = LGiven, middle = Middle, lmiddle = LMiddle, nickname = Nickname, lnickname = LNickname, bday = BDay, lbday = LBDay, ctry = CTRY, lctry = LCTRY, locality = Locality, llocality = LLocality, email = EMail, lemail = LEMail, orgname = OrgName, lorgname = LOrgName, orgunit = OrgUnit, lorgunit = LOrgUnit} = mod_vcard:make_vcard_search(LUser, LUser, LServer, El), ejabberd_riak:put(VCard, vcard_schema(), [{'2i', [{<<"user">>, LUser}, {<<"luser">>, LUser}, {<<"fn">>, FN}, {<<"lfn">>, LFN}, {<<"family">>, Family}, {<<"lfamily">>, LFamily}, {<<"given">>, Given}, {<<"lgiven">>, LGiven}, {<<"middle">>, Middle}, {<<"lmiddle">>, LMiddle}, {<<"nickname">>, Nickname}, {<<"lnickname">>, LNickname}, {<<"bday">>, BDay}, {<<"lbday">>, LBDay}, {<<"ctry">>, CTRY}, {<<"lctry">>, LCTRY}, {<<"locality">>, Locality}, {<<"llocality">>, LLocality}, {<<"email">>, EMail}, {<<"lemail">>, LEMail}, {<<"orgname">>, OrgName}, {<<"lorgname">>, LOrgName}, {<<"orgunit">>, OrgUnit}, {<<"lorgunit">>, LOrgUnit}]}]); import(_LServer, <<"vcard_search">>, _) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== vcard_schema() -> {record_info(fields, vcard), #vcard{}}. ejabberd-18.01/src/eldap_utils.erl0000644000232200023220000003231213225664356017444 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : eldap_utils.erl %%% Author : Mickael Remond %%% Purpose : ejabberd LDAP helper functions %%% Created : 12 Oct 2006 by Mickael Remond %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(eldap_utils). -behaviour(ejabberd_config). -author('mremond@process-one.net'). -export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1, get_ldap_attr/2, get_user_part/2, make_filter/2, get_state/2, case_insensitive_match/2, get_config/2, decode_octet_string/3, uids_domain_subst/2, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("eldap.hrl"). %% Generate an 'or' LDAP query on one or several attributes %% If there is only one attribute generate_subfilter([UID]) -> subfilter(UID); %% If there is several attributes generate_subfilter(UIDs) -> iolist_to_binary(["(|", [subfilter(UID) || UID <- UIDs], ")"]). %% Subfilter for a single attribute subfilter({UIDAttr, UIDAttrFormat}) -> %% The default UiDAttrFormat is %u <<$(, UIDAttr/binary, $=, UIDAttrFormat/binary, $)>>; %% The default UiDAttrFormat is <<"%u">> subfilter({UIDAttr}) -> <<$(, UIDAttr/binary, $=, "%u)">>. %% Not tail-recursive, but it is not very terribly. %% It stops finding on the first not empty value. -spec find_ldap_attrs([{binary()} | {binary(), binary()}], [{binary(), [binary()]}]) -> <<>> | {binary(), binary()}. find_ldap_attrs([{Attr} | Rest], Attributes) -> find_ldap_attrs([{Attr, <<"%u">>} | Rest], Attributes); find_ldap_attrs([{Attr, Format} | Rest], Attributes) -> case get_ldap_attr(Attr, Attributes) of Value when is_binary(Value), Value /= <<>> -> {Value, Format}; _ -> find_ldap_attrs(Rest, Attributes) end; find_ldap_attrs([], _) -> <<>>. -spec get_ldap_attr(binary(), [{binary(), [binary()]}]) -> binary(). get_ldap_attr(LDAPAttr, Attributes) -> Res = lists:filter( fun({Name, _}) -> case_insensitive_match(Name, LDAPAttr) end, Attributes), case Res of [{_, [Value|_]}] -> Value; _ -> <<>> end. -spec get_user_part(binary(), binary()) -> {ok, binary()} | {error, badmatch}. get_user_part(String, Pattern) -> F = fun(S, P) -> First = str:str(P, <<"%u">>), TailLength = byte_size(P) - (First+1), str:sub_string(S, First, byte_size(S) - TailLength) end, case catch F(String, Pattern) of {'EXIT', _} -> {error, badmatch}; Result -> case catch ejabberd_regexp:replace(Pattern, <<"%u">>, Result) of {'EXIT', _} -> {error, badmatch}; StringRes -> case case_insensitive_match(StringRes, String) of true -> {ok, Result}; false -> {error, badmatch} end end end. -spec make_filter([{binary(), [binary()]}], [{binary(), binary()}]) -> any(). make_filter(Data, UIDs) -> NewUIDs = [{U, eldap_filter:do_sub( UF, [{<<"%u">>, <<"*%u*">>, 1}])} || {U, UF} <- UIDs], Filter = lists:flatmap( fun({Name, [Value | _]}) -> case Name of <<"%u">> when Value /= <<"">> -> case eldap_filter:parse( generate_subfilter(NewUIDs), [{<<"%u">>, Value}]) of {ok, F} -> [F]; _ -> [] end; _ when Value /= <<"">> -> [eldap:substrings( Name, [{any, Value}])]; _ -> [] end end, Data), case Filter of [F] -> F; _ -> eldap:'and'(Filter) end. check_filter(F) -> NewF = iolist_to_binary(F), {ok, _} = eldap_filter:parse(NewF), NewF. -spec case_insensitive_match(binary(), binary()) -> boolean(). case_insensitive_match(X, Y) -> X1 = str:to_lower(X), Y1 = str:to_lower(Y), if X1 == Y1 -> true; true -> false end. get_state(Server, Module) -> Proc = gen_mod:get_module_proc(Server, Module), gen_server:call(Proc, get_state). %% From the list of uids attribute: %% we look from alias domain (%d) and make the substitution %% with the actual host domain %% This help when you need to configure many virtual domains. -spec uids_domain_subst(binary(), [{binary(), binary()}]) -> [{binary(), binary()}]. uids_domain_subst(Host, UIDs) -> lists:map(fun({U,V}) -> {U, eldap_filter:do_sub(V,[{<<"%d">>, Host}])}; (A) -> A end, UIDs). -spec get_config(binary(), list()) -> eldap_config(). get_config(Host, Opts) -> Servers = get_opt(ldap_servers, Host, Opts, [<<"localhost">>]), Backups = get_opt(ldap_backups, Host, Opts, []), Encrypt = get_opt(ldap_encrypt, Host, Opts, none), TLSVerify = get_opt(ldap_tls_verify, Host, Opts, false), TLSCertFile = get_opt(ldap_tls_certfile, Host, Opts), TLSCAFile = get_opt(ldap_tls_cacertfile, Host, Opts), TLSDepth = get_opt(ldap_tls_depth, Host, Opts), Port = get_opt(ldap_port, Host, Opts, case Encrypt of tls -> ?LDAPS_PORT; starttls -> ?LDAP_PORT; _ -> ?LDAP_PORT end), RootDN = get_opt(ldap_rootdn, Host, Opts, <<"">>), Password = get_opt(ldap_password, Host, Opts, <<"">>), Base = get_opt(ldap_base, Host, Opts, <<"">>), OldDerefAliases = get_opt(deref_aliases, Host, Opts, unspecified), DerefAliases = if OldDerefAliases == unspecified -> get_opt(ldap_deref_aliases, Host, Opts, never); true -> ?WARNING_MSG("Option 'deref_aliases' is deprecated. " "The option is still supported " "but it is better to fix your config: " "use 'ldap_deref_aliases' instead.", []), OldDerefAliases end, #eldap_config{servers = Servers, backups = Backups, tls_options = [{encrypt, Encrypt}, {tls_verify, TLSVerify}, {tls_certfile, TLSCertFile}, {tls_cacertfile, TLSCAFile}, {tls_depth, TLSDepth}], port = Port, dn = RootDN, password = Password, base = Base, deref_aliases = DerefAliases}. get_opt(Opt, Host, Opts) -> get_opt(Opt, Host, Opts, undefined). get_opt(Opt, Host, Opts, Default) -> case proplists:get_value(Opt, Opts) of undefined -> ejabberd_config:get_option({Opt, Host}, Default); Value -> Value end. %%---------------------------------------- %% Borrowed from asn1rt_ber_bin_v2.erl %%---------------------------------------- %%% The tag-number for universal types -define(N_BOOLEAN, 1). -define(N_INTEGER, 2). -define(N_BIT_STRING, 3). -define(N_OCTET_STRING, 4). -define(N_NULL, 5). -define(N_OBJECT_IDENTIFIER, 6). -define(N_OBJECT_DESCRIPTOR, 7). -define(N_EXTERNAL, 8). -define(N_REAL, 9). -define(N_ENUMERATED, 10). -define(N_EMBEDDED_PDV, 11). -define(N_SEQUENCE, 16). -define(N_SET, 17). -define(N_NumericString, 18). -define(N_PrintableString, 19). -define(N_TeletexString, 20). -define(N_VideotexString, 21). -define(N_IA5String, 22). -define(N_UTCTime, 23). -define(N_GeneralizedTime, 24). -define(N_GraphicString, 25). -define(N_VisibleString, 26). -define(N_GeneralString, 27). -define(N_UniversalString, 28). -define(N_BMPString, 30). decode_octet_string(Buffer, Range, Tags) -> % NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}), decode_restricted_string(Buffer, Range, Tags). decode_restricted_string(Tlv, Range, TagsIn) -> Val = match_tags(Tlv, TagsIn), Val2 = case Val of PartList = [_H|_T] -> % constructed val collect_parts(PartList); Bin -> Bin end, check_and_convert_restricted_string(Val2, Range). check_and_convert_restricted_string(Val, Range) -> {StrLen,NewVal} = if is_binary(Val) -> {size(Val), Val}; true -> {length(Val), list_to_binary(Val)} end, case Range of [] -> % No length constraint NewVal; {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint NewVal; {{Lb,_Ub},[]} when StrLen >= Lb -> NewVal; {{Lb,_Ub},_Ext=[Min|_]} when StrLen >= Lb; StrLen >= Min -> NewVal; {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1; StrLen =< Ub2, StrLen >= Lb2 -> NewVal; StrLen -> % fixed length constraint NewVal; {_,_} -> exit({error,{asn1,{length,Range,Val}}}); _Len when is_integer(_Len) -> exit({error,{asn1,{length,Range,Val}}}); _ -> % some strange constraint that we don't support yet NewVal end. %%---------------------------------------- %% Decode the in buffer to bits %%---------------------------------------- match_tags({T,V},[T]) -> V; match_tags({T,V}, [T|Tt]) -> match_tags(V,Tt); match_tags([{T,V}],[T|Tt]) -> match_tags(V, Tt); match_tags(Vlist = [{T,_V}|_], [T]) -> Vlist; match_tags(Tlv, []) -> Tlv; match_tags({Tag,_V},[T|_Tt]) -> {error,{asn1,{wrong_tag,{Tag,T}}}}. collect_parts(TlvList) -> collect_parts(TlvList,[]). collect_parts([{_,L}|Rest],Acc) when is_list(L) -> collect_parts(Rest,[collect_parts(L)|Acc]); collect_parts([{?N_BIT_STRING,<>}|Rest],_Acc) -> collect_parts_bit(Rest,[Bits],Unused); collect_parts([{_T,V}|Rest],Acc) -> collect_parts(Rest,[V|Acc]); collect_parts([],Acc) -> list_to_binary(lists:reverse(Acc)). collect_parts_bit([{?N_BIT_STRING,<>}|Rest],Acc,Uacc) -> collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc); collect_parts_bit([],Acc,Uacc) -> list_to_binary([Uacc|lists:reverse(Acc)]). -type deref_aliases() :: never | searching | finding | always. -type uids() :: binary() | {binary()} | {binary(), binary()}. -spec opt_type(deref_aliases) -> fun((deref_aliases()) -> deref_aliases()); (ldap_backups) -> fun(([binary()]) -> [binary()]); (ldap_base) -> fun((binary()) -> binary()); (ldap_deref_aliases) -> fun((deref_aliases()) -> deref_aliases()); (ldap_encrypt) -> fun((tls | starttls | none) -> tls | starttls | none); (ldap_password) -> fun((binary()) -> binary()); (ldap_port) -> fun((0..65535) -> 0..65535); (ldap_rootdn) -> fun((binary()) -> binary()); (ldap_servers) -> fun(([binary()]) -> [binary()]); (ldap_tls_certfile) -> fun((binary()) -> string()); (ldap_tls_cacertfile) -> fun((binary()) -> string()); (ldap_tls_depth) -> fun((non_neg_integer()) -> non_neg_integer()); (ldap_tls_verify) -> fun((hard | soft | false) -> hard | soft | false); (ldap_filter) -> fun((binary()) -> binary()); (ldap_uids) -> fun((uids()) -> uids()); (atom()) -> [atom()]. opt_type(deref_aliases) -> opt_type(ldap_deref_aliases); opt_type(ldap_backups) -> fun (L) -> [iolist_to_binary(H) || H <- L] end; opt_type(ldap_base) -> fun iolist_to_binary/1; opt_type(ldap_deref_aliases) -> fun (never) -> never; (searching) -> searching; (finding) -> finding; (always) -> always end; opt_type(ldap_encrypt) -> fun (tls) -> tls; (starttls) -> starttls; (none) -> none end; opt_type(ldap_password) -> fun iolist_to_binary/1; opt_type(ldap_port) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(ldap_rootdn) -> fun iolist_to_binary/1; opt_type(ldap_servers) -> fun (L) -> [iolist_to_binary(H) || H <- L] end; opt_type(ldap_tls_certfile) -> fun(S) -> binary_to_list(ejabberd_pkix:try_certfile(S)) end; opt_type(ldap_tls_cacertfile) -> fun(S) -> binary_to_list(misc:try_read_file(S)) end; opt_type(ldap_tls_depth) -> fun (I) when is_integer(I), I >= 0 -> I end; opt_type(ldap_tls_verify) -> fun (hard) -> hard; (soft) -> soft; (false) -> false end; opt_type(ldap_filter) -> fun check_filter/1; opt_type(ldap_uids) -> fun (Us) -> lists:map(fun ({U, P}) -> {iolist_to_binary(U), iolist_to_binary(P)}; ({U}) -> {iolist_to_binary(U)}; (U) -> {iolist_to_binary(U)} end, lists:flatten(Us)) end; opt_type(_) -> [deref_aliases, ldap_backups, ldap_base, ldap_uids, ldap_deref_aliases, ldap_encrypt, ldap_password, ldap_port, ldap_rootdn, ldap_servers, ldap_filter, ldap_tls_certfile, ldap_tls_cacertfile, ldap_tls_depth, ldap_tls_verify]. ejabberd-18.01/src/mod_irc_mnesia.erl0000644000232200023220000000533513225664356020114 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_irc_mnesia.erl %%% Author : Evgeny Khramtsov %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_irc_mnesia). -behaviour(mod_irc). %% API -export([init/2, get_data/3, set_data/4, import/2]). -export([need_transform/1, transform/1]). -include("jid.hrl"). -include("mod_irc.hrl"). -include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== init(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, irc_custom, [{disc_copies, [node()]}, {attributes, record_info(fields, irc_custom)}]). get_data(_LServer, Host, From) -> {U, S, _} = jid:tolower(From), case catch mnesia:dirty_read({irc_custom, {{U, S}, Host}}) of {'EXIT', _Reason} -> error; [] -> empty; [#irc_custom{data = Data}] -> Data end. set_data(_LServer, Host, From, Data) -> {U, S, _} = jid:tolower(From), F = fun () -> mnesia:write(#irc_custom{us_host = {{U, S}, Host}, data = Data}) end, mnesia:transaction(F). import(_LServer, #irc_custom{} = R) -> mnesia:dirty_write(R). need_transform(#irc_custom{us_host = {{U, S}, H}}) when is_list(U) orelse is_list(S) orelse is_list(H) -> ?INFO_MSG("Mnesia table 'irc_custom' will be converted to binary", []), true; need_transform(_) -> false. transform(#irc_custom{us_host = {{U, S}, H}, data = Data} = R) -> JID = jid:make(U, S), R#irc_custom{us_host = {{iolist_to_binary(U), iolist_to_binary(S)}, iolist_to_binary(H)}, data = mod_irc:data_to_binary(JID, Data)}. %%%=================================================================== %%% Internal functions %%%=================================================================== ejabberd-18.01/src/rest.erl0000644000232200023220000001534513225664356016123 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : rest.erl %%% Author : Christophe Romain %%% Purpose : Generic REST client %%% Created : 16 Oct 2014 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(rest). -behaviour(ejabberd_config). -export([start/1, stop/1, get/2, get/3, post/4, delete/2, put/4, patch/4, request/6, with_retry/4, opt_type/1]). -include("logger.hrl"). -define(HTTP_TIMEOUT, 10000). -define(CONNECT_TIMEOUT, 8000). start(Host) -> p1_http:start(), Pool_size = ejabberd_config:get_option({ext_api_http_pool_size, Host}, 100), p1_http:set_pool_size(Pool_size). stop(_Host) -> ok. with_retry(Method, Args, MaxRetries, Backoff) -> with_retry(Method, Args, 0, MaxRetries, Backoff). with_retry(Method, Args, Retries, MaxRetries, Backoff) -> case apply(?MODULE, Method, Args) of %% Only retry on timeout errors {error, {http_error,{error,Error}}} when Retries < MaxRetries andalso (Error == 'timeout' orelse Error == 'connect_timeout') -> timer:sleep(round(math:pow(2, Retries)) * Backoff), with_retry(Method, Args, Retries+1, MaxRetries, Backoff); Result -> Result end. get(Server, Path) -> request(Server, get, Path, [], "application/json", <<>>). get(Server, Path, Params) -> request(Server, get, Path, Params, "application/json", <<>>). delete(Server, Path) -> request(Server, delete, Path, [], "application/json", <<>>). post(Server, Path, Params, Content) -> Data = encode_json(Content), request(Server, post, Path, Params, "application/json", Data). put(Server, Path, Params, Content) -> Data = encode_json(Content), request(Server, put, Path, Params, "application/json", Data). patch(Server, Path, Params, Content) -> Data = encode_json(Content), request(Server, patch, Path, Params, "application/json", Data). request(Server, Method, Path, Params, Mime, Data) -> URI = url(Server, Path, Params), Opts = [{connect_timeout, ?CONNECT_TIMEOUT}, {timeout, ?HTTP_TIMEOUT}], Hdrs = [{"connection", "keep-alive"}, {"content-type", Mime}, {"User-Agent", "ejabberd"}], Begin = os:timestamp(), Result = case catch p1_http:request(Method, URI, Hdrs, Data, Opts) of {ok, Code, _, <<>>} -> {ok, Code, []}; {ok, Code, _, <<" ">>} -> {ok, Code, []}; {ok, Code, _, <<"\r\n">>} -> {ok, Code, []}; {ok, Code, _, Body} -> try jiffy:decode(Body) of JSon -> {ok, Code, JSon} catch _:Error -> ?ERROR_MSG("HTTP response decode failed:~n" "** URI = ~s~n" "** Body = ~p~n" "** Err = ~p", [URI, Body, Error]), {error, {invalid_json, Body}} end; {error, Reason} -> ?ERROR_MSG("HTTP request failed:~n" "** URI = ~s~n" "** Err = ~p", [URI, Reason]), {error, {http_error, {error, Reason}}}; {'EXIT', Reason} -> ?ERROR_MSG("HTTP request failed:~n" "** URI = ~s~n" "** Err = ~p", [URI, Reason]), {error, {http_error, {error, Reason}}} end, ejabberd_hooks:run(backend_api_call, Server, [Server, Method, Path]), case Result of {ok, _, _} -> End = os:timestamp(), Elapsed = timer:now_diff(End, Begin) div 1000, %% time in ms ejabberd_hooks:run(backend_api_response_time, Server, [Server, Method, Path, Elapsed]); {error, {http_error,{error,timeout}}} -> ejabberd_hooks:run(backend_api_timeout, Server, [Server, Method, Path]); {error, {http_error,{error,connect_timeout}}} -> ejabberd_hooks:run(backend_api_timeout, Server, [Server, Method, Path]); {error, _} -> ejabberd_hooks:run(backend_api_error, Server, [Server, Method, Path]) end, Result. %%%---------------------------------------------------------------------- %%% HTTP helpers %%%---------------------------------------------------------------------- encode_json(Content) -> case catch jiffy:encode(Content) of {'EXIT', Reason} -> ?ERROR_MSG("HTTP content encodage failed:~n" "** Content = ~p~n" "** Err = ~p", [Content, Reason]), <<>>; Encoded -> Encoded end. base_url(Server, Path) -> Tail = case iolist_to_binary(Path) of <<$/, Ok/binary>> -> Ok; Ok -> Ok end, case Tail of <<"http", _Url/binary>> -> Tail; _ -> Base = ejabberd_config:get_option({ext_api_url, Server}, <<"http://localhost/api">>), <> end. url(Server, Path, []) -> binary_to_list(base_url(Server, Path)); url(Server, Path, Params) -> Base = base_url(Server, Path), [<<$&, ParHead/binary>> | ParTail] = [<<"&", (iolist_to_binary(Key))/binary, "=", (ejabberd_http:url_encode(Value))/binary>> || {Key, Value} <- Params], Tail = iolist_to_binary([ParHead | ParTail]), binary_to_list(<>). -spec opt_type(ext_api_http_pool_size) -> fun((pos_integer()) -> pos_integer()); (ext_api_url) -> fun((binary()) -> binary()); (atom()) -> [atom()]. opt_type(ext_api_http_pool_size) -> fun (X) when is_integer(X), X > 0 -> X end; opt_type(ext_api_url) -> fun (X) -> iolist_to_binary(X) end; opt_type(_) -> [ext_api_http_pool_size, ext_api_url]. ejabberd-18.01/src/mod_time.erl0000644000232200023220000000526013225664356016736 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : mod_time.erl %%% Author : Alexey Shchepin %%% Purpose : %%% Purpose : %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(mod_time). -author('alexey@process-one.net'). -protocol({xep, 202, '2.0'}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_local_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_TIME, ?MODULE, process_local_iq, IQDisc). stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_TIME). reload(Host, NewOpts, OldOpts) -> case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of {false, IQDisc, _} -> gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_TIME, ?MODULE, process_local_iq, IQDisc); true -> ok end. process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); process_local_iq(#iq{type = get} = IQ) -> Now = p1_time_compat:timestamp(), Now_universal = calendar:now_to_universal_time(Now), Now_local = calendar:universal_time_to_local_time(Now_universal), Seconds_diff = calendar:datetime_to_gregorian_seconds(Now_local) - calendar:datetime_to_gregorian_seconds(Now_universal), {Hd, Md, _} = calendar:seconds_to_time(abs(Seconds_diff)), xmpp:make_iq_result(IQ, #time{tzo = {Hd, Md}, utc = Now}). depends(_Host, _Opts) -> []. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(_) -> [iqdisc]. ejabberd-18.01/src/ejabberd_c2s_config.erl0000644000232200023220000000445713225664356021002 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_c2s_config.erl %%% Author : Mickael Remond %%% Purpose : Functions for c2s interactions from other client %%% connector modules %%% Created : 2 Nov 2007 by Mickael Remond %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_c2s_config). -author('mremond@process-one.net'). -export([get_c2s_limits/0]). %% Get first c2s configuration limitations to apply it to other c2s %% connectors. get_c2s_limits() -> C2SFirstListen = ejabberd_config:get_option(listen, []), case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of false -> []; {value, {_Port, ejabberd_c2s, Opts}} -> select_opts_values(Opts) end. %% Only get access, shaper and max_stanza_size values select_opts_values(Opts) -> select_opts_values(Opts, []). select_opts_values([], SelectedValues) -> SelectedValues; select_opts_values([{access, Value} | Opts], SelectedValues) -> select_opts_values(Opts, [{access, Value} | SelectedValues]); select_opts_values([{shaper, Value} | Opts], SelectedValues) -> select_opts_values(Opts, [{shaper, Value} | SelectedValues]); select_opts_values([{max_stanza_size, Value} | Opts], SelectedValues) -> select_opts_values(Opts, [{max_stanza_size, Value} | SelectedValues]); select_opts_values([_Opt | Opts], SelectedValues) -> select_opts_values(Opts, SelectedValues). ejabberd-18.01/src/mod_metrics.erl0000644000232200023220000001666213225664356017456 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% File : mod_metrics.erl %%% Author : Christophe Romain %%% Purpose : Simple metrics handler for runtime statistics %%% Created : 22 Oct 2015 by Christophe Romain %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%------------------------------------------------------------------- -module(mod_metrics). -author('christophe.romain@process-one.net'). -behaviour(gen_mod). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -export([start/2, stop/1, mod_opt_type/1, depends/2, reload/3]). -export([offline_message_hook/1, sm_register_connection_hook/3, sm_remove_connection_hook/3, user_send_packet/1, user_receive_packet/1, s2s_send_packet/1, s2s_receive_packet/1, remove_user/2, register_user/2]). -define(SOCKET_NAME, mod_metrics_udp_socket). -define(SOCKET_REGISTER_RETRIES, 10). %%==================================================================== %% API %%==================================================================== start(Host, _Opts) -> ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, offline_message_hook, 20), ejabberd_hooks:add(sm_register_connection_hook, Host, ?MODULE, sm_register_connection_hook, 20), ejabberd_hooks:add(sm_remove_connection_hook, Host, ?MODULE, sm_remove_connection_hook, 20), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 20), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 20), ejabberd_hooks:add(s2s_send_packet, Host, ?MODULE, s2s_send_packet, 20), ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 20), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 20), ejabberd_hooks:add(register_user, Host, ?MODULE, register_user, 20). stop(Host) -> ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, offline_message_hook, 20), ejabberd_hooks:delete(sm_register_connection_hook, Host, ?MODULE, sm_register_connection_hook, 20), ejabberd_hooks:delete(sm_remove_connection_hook, Host, ?MODULE, sm_remove_connection_hook, 20), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 20), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 20), ejabberd_hooks:delete(s2s_send_packet, Host, ?MODULE, s2s_send_packet, 20), ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 20), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 20), ejabberd_hooks:delete(register_user, Host, ?MODULE, register_user, 20). reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> []. %%==================================================================== %% Hooks handlers %%==================================================================== -spec offline_message_hook({any(), message()}) -> {any(), message()}. offline_message_hook({_Action, #message{to = #jid{lserver = LServer}}} = Acc) -> push(LServer, offline_message), Acc. -spec sm_register_connection_hook(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> any(). sm_register_connection_hook(_SID, #jid{lserver=LServer}, _Info) -> push(LServer, sm_register_connection). -spec sm_remove_connection_hook(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> any(). sm_remove_connection_hook(_SID, #jid{lserver=LServer}, _Info) -> push(LServer, sm_remove_connection). -spec user_send_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. user_send_packet({Packet, #{jid := #jid{lserver = LServer}} = C2SState}) -> push(LServer, user_send_packet), {Packet, C2SState}. -spec user_receive_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. user_receive_packet({Packet, #{jid := #jid{lserver = LServer}} = C2SState}) -> push(LServer, user_receive_packet), {Packet, C2SState}. -spec s2s_send_packet(stanza()) -> any(). s2s_send_packet(Packet) -> #jid{lserver = LServer} = xmpp:get_from(Packet), push(LServer, s2s_send_packet). -spec s2s_receive_packet({stanza(), ejabberd_s2s_in:state()}) -> {stanza(), ejabberd_s2s_in:state()}. s2s_receive_packet({Packet, S2SState}) -> To = xmpp:get_to(Packet), LServer = ejabberd_router:host_of_route(To#jid.lserver), push(LServer, s2s_receive_packet), {Packet, S2SState}. -spec remove_user(binary(), binary()) -> any(). remove_user(_User, Server) -> push(jid:nameprep(Server), remove_user). -spec register_user(binary(), binary()) -> any(). register_user(_User, Server) -> push(jid:nameprep(Server), register_user). %%==================================================================== %% metrics push handler %%==================================================================== push(Host, Probe) -> IP = gen_mod:get_module_opt(Host, ?MODULE, ip, {127,0,0,1}), Port = gen_mod:get_module_opt(Host, ?MODULE, port, 11111), send_metrics(Host, Probe, IP, Port). send_metrics(Host, Probe, Peer, Port) -> % our default metrics handler is https://github.com/processone/grapherl % grapherl metrics are named first with service domain, then nodename % and name of the data itself, followed by type timestamp and value % example => process-one.net/xmpp-1.user_receive_packet:c/1441784958:1 [_, FQDN] = binary:split(misc:atom_to_binary(node()), <<"@">>), [Node|_] = binary:split(FQDN, <<".">>), BaseId = <>, TS = integer_to_binary(p1_time_compat:system_time(seconds)), case get_socket(?SOCKET_REGISTER_RETRIES) of {ok, Socket} -> case Probe of {Key, Val} -> BVal = integer_to_binary(Val), Data = <>, gen_udp:send(Socket, Peer, Port, Data); Key -> Data = <>, gen_udp:send(Socket, Peer, Port, Data) end; Err -> Err end. get_socket(N) -> case whereis(?SOCKET_NAME) of undefined -> case gen_udp:open(0) of {ok, Socket} -> try register(?SOCKET_NAME, Socket) of true -> {ok, Socket} catch _:badarg when N > 1 -> gen_udp:close(Socket), get_socket(N-1) end; {error, Reason} = Err -> ?ERROR_MSG("can not open udp socket to grapherl: ~s", [inet:format_error(Reason)]), Err end; Socket -> {ok, Socket} end. mod_opt_type(ip) -> fun(S) -> {ok, IP} = inet:parse_ipv4_address( binary_to_list(iolist_to_binary(S))), IP end; mod_opt_type(port) -> fun(I) when is_integer(I), I>0, I<65536 -> I end; mod_opt_type(_) -> [ip, port]. ejabberd-18.01/src/pubsub_db_sql.erl0000644000232200023220000001751513225664356017773 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pubsub_db_sql.erl %%% Author : Pablo Polvorin %%% Purpose : Provide helpers for PubSub ODBC backend %%% Created : 7 Aug 2009 by Pablo Polvorin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(pubsub_db_sql). -compile([{parse_transform, ejabberd_sql_pt}]). -author("pablo.polvorin@process-one.net"). -include("pubsub.hrl"). -include("ejabberd_sql_pt.hrl"). -export([add_subscription/1, read_subscription/1, delete_subscription/1, update_subscription/1]). -export([export/1]). %% TODO: Those -spec lines produce errors in old Erlang versions. %% They can be enabled again in ejabberd 3.0 because it uses R12B or higher. %% -spec read_subscription(SubID :: string()) -> {ok, #pubsub_subscription{}} | notfound. read_subscription(SubID) -> case ejabberd_sql:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr" "iption_opt where subid = '">>, ejabberd_sql:escape(SubID), <<"'">>]) of {selected, [<<"opt_name">>, <<"opt_value">>], []} -> notfound; {selected, [<<"opt_name">>, <<"opt_value">>], Options} -> {ok, #pubsub_subscription{subid = SubID, options = lists:map(fun subscription_opt_from_sql/1, Options)}} end. %% -spec delete_subscription(SubID :: string()) -> ok. delete_subscription(SubID) -> %% -spec update_subscription(#pubsub_subscription{}) -> ok . %% -spec add_subscription(#pubsub_subscription{}) -> ok. %% -------------- Internal utilities ----------------------- ejabberd_sql:sql_query_t([<<"delete from pubsub_subscription_opt " "where subid = '">>, ejabberd_sql:escape(SubID), <<"'">>]), ok. update_subscription(#pubsub_subscription{subid = SubId} = Sub) -> delete_subscription(SubId), add_subscription(Sub). add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) -> EscapedSubId = ejabberd_sql:escape(SubId), lists:foreach(fun (Opt) -> {OdbcOptName, OdbcOptValue} = subscription_opt_to_sql(Opt), ejabberd_sql:sql_query_t([<<"insert into pubsub_subscription_opt(subid, " "opt_name, opt_value)values ('">>, EscapedSubId, <<"','">>, OdbcOptName, <<"','">>, OdbcOptValue, <<"')">>]) end, Opts), ok. subscription_opt_from_sql([<<"DELIVER">>, Value]) -> {deliver, sql_to_boolean(Value)}; subscription_opt_from_sql([<<"DIGEST">>, Value]) -> {digest, sql_to_boolean(Value)}; subscription_opt_from_sql([<<"DIGEST_FREQUENCY">>, Value]) -> {digest_frequency, sql_to_integer(Value)}; subscription_opt_from_sql([<<"EXPIRE">>, Value]) -> {expire, sql_to_timestamp(Value)}; subscription_opt_from_sql([<<"INCLUDE_BODY">>, Value]) -> {include_body, sql_to_boolean(Value)}; %%TODO: might be > than 1 show_values value??. %% need to use compact all in only 1 opt. subscription_opt_from_sql([<<"SHOW_VALUES">>, Value]) -> {show_values, Value}; subscription_opt_from_sql([<<"SUBSCRIPTION_TYPE">>, Value]) -> {subscription_type, case Value of <<"items">> -> items; <<"nodes">> -> nodes end}; subscription_opt_from_sql([<<"SUBSCRIPTION_DEPTH">>, Value]) -> {subscription_depth, case Value of <<"all">> -> all; N -> sql_to_integer(N) end}. subscription_opt_to_sql({deliver, Bool}) -> {<<"DELIVER">>, boolean_to_sql(Bool)}; subscription_opt_to_sql({digest, Bool}) -> {<<"DIGEST">>, boolean_to_sql(Bool)}; subscription_opt_to_sql({digest_frequency, Int}) -> {<<"DIGEST_FREQUENCY">>, integer_to_sql(Int)}; subscription_opt_to_sql({expire, Timestamp}) -> {<<"EXPIRE">>, timestamp_to_sql(Timestamp)}; subscription_opt_to_sql({include_body, Bool}) -> {<<"INCLUDE_BODY">>, boolean_to_sql(Bool)}; subscription_opt_to_sql({show_values, Values}) -> {<<"SHOW_VALUES">>, Values}; subscription_opt_to_sql({subscription_type, Type}) -> {<<"SUBSCRIPTION_TYPE">>, case Type of items -> <<"items">>; nodes -> <<"nodes">> end}; subscription_opt_to_sql({subscription_depth, Depth}) -> {<<"SUBSCRIPTION_DEPTH">>, case Depth of all -> <<"all">>; N -> integer_to_sql(N) end}. integer_to_sql(N) -> integer_to_binary(N). boolean_to_sql(true) -> <<"1">>; boolean_to_sql(false) -> <<"0">>. timestamp_to_sql(T) -> xmpp_util:encode_timestamp(T). sql_to_integer(N) -> binary_to_integer(N). sql_to_boolean(B) -> B == <<"1">>. sql_to_timestamp(T) -> xmpp_util:decode_timestamp(T). export(_Server) -> [{pubsub_node, fun(_Host, #pubsub_node{nodeid = {Host, Node}, id = Nidx, parents = Parents, type = Type, options = Options}) -> H = node_flat_sql:encode_host(Host), Parent = case Parents of [] -> <<>>; [First | _] -> First end, [?SQL("delete from pubsub_node where nodeid=%(Nidx)d;"), ?SQL("delete from pubsub_node_option where nodeid=%(Nidx)d;"), ?SQL("delete from pubsub_node_owner where nodeid=%(Nidx)d;"), ?SQL("delete from pubsub_state where nodeid=%(Nidx)d;"), ?SQL("delete from pubsub_item where nodeid=%(Nidx)d;"), ?SQL("insert into pubsub_node(host,node,nodeid,parent,type)" " values (%(H)s, %(Node)s, %(Nidx)d, %(Parent)s, %(Type)s);")] ++ lists:map( fun ({Key, Value}) -> SKey = iolist_to_binary(atom_to_list(Key)), SValue = misc:term_to_expr(Value), ?SQL("insert into pubsub_node_option(nodeid,name,val)" " values (%(Nidx)d, %(SKey)s, %(SValue)s);") end, Options); (_Host, _R) -> [] end}, {pubsub_state, fun(_Host, #pubsub_state{stateid = {JID, Nidx}, affiliation = Affiliation, subscriptions = Subscriptions}) -> J = jid:encode(JID), S = node_flat_sql:encode_subscriptions(Subscriptions), A = node_flat_sql:encode_affiliation(Affiliation), [?SQL("insert into pubsub_state(nodeid,jid,affiliation,subscriptions)" " values (%(Nidx)d, %(J)s, %(A)s, %(S)s);")]; (_Host, _R) -> [] end}, {pubsub_item, fun(_Host, #pubsub_item{itemid = {ItemId, Nidx}, creation = {C, _}, modification = {M, JID}, payload = Payload}) -> P = jid:encode(JID), XML = str:join([fxml:element_to_binary(X) || X<-Payload], <<>>), SM = encode_now(M), SC = encode_now(C), [?SQL("insert into pubsub_item(itemid,nodeid,creation,modification,publisher,payload)" " values (%(ItemId)s, %(Nidx)d, %(SC)s, %(SM)s, %(P)s, %(XML)s);")]; (_Host, _R) -> [] end}]. encode_now({T1, T2, T3}) -> <<(misc:i2l(T1, 6))/binary, ":", (misc:i2l(T2, 6))/binary, ":", (misc:i2l(T3, 6))/binary>>. ejabberd-18.01/src/ejabberd_s2s.erl0000644000232200023220000006107713225664356017476 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : ejabberd_s2s.erl %%% Author : Alexey Shchepin %%% Purpose : S2S connections manager %%% Created : 7 Dec 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2018 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(ejabberd_s2s). -protocol({xep, 220, '1.1'}). -behaviour(ejabberd_config). -author('alexey@process-one.net'). -behaviour(gen_server). %% API -export([start_link/0, route/1, have_connection/1, get_connections_pids/1, try_register/1, remove_connection/2, start_connection/2, start_connection/3, dirty_get_connections/0, allow_host/2, incoming_s2s_number/0, outgoing_s2s_number/0, stop_s2s_connections/0, clean_temporarily_blocked_table/0, list_temporarily_blocked_hosts/0, external_host_overloaded/1, is_temporarly_blocked/1, get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1, tls_required/1, tls_verify/1, tls_enabled/1, tls_options/2, host_up/1, host_down/1, queue_type/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([get_info_s2s_connections/1, transform_options/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). -include("ejabberd_commands.hrl"). -include_lib("public_key/include/public_key.hrl"). -define(PKIXEXPLICIT, 'OTP-PUB-KEY'). -define(PKIXIMPLICIT, 'OTP-PUB-KEY'). -include("XmppAddr.hrl"). -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER, 1). -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE, 1). -define(S2S_OVERLOAD_BLOCK_PERIOD, 60). %% once a server is temporarly blocked, it stay blocked for 60 seconds -record(s2s, {fromto = {<<"">>, <<"">>} :: {binary(), binary()} | '_', pid = self() :: pid() | '_' | '$1'}). -record(state, {}). -record(temporarily_blocked, {host = <<"">> :: binary(), timestamp :: integer()}). -type temporarily_blocked() :: #temporarily_blocked{}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec route(stanza()) -> ok. route(Packet) -> try do_route(Packet) catch E:R -> ?ERROR_MSG("failed to route packet:~n~s~nReason = ~p", [xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}]) end. clean_temporarily_blocked_table() -> mnesia:clear_table(temporarily_blocked). -spec list_temporarily_blocked_hosts() -> [temporarily_blocked()]. list_temporarily_blocked_hosts() -> ets:tab2list(temporarily_blocked). -spec external_host_overloaded(binary()) -> {aborted, any()} | {atomic, ok}. external_host_overloaded(Host) -> ?INFO_MSG("Disabling connections from ~s for ~p " "seconds", [Host, ?S2S_OVERLOAD_BLOCK_PERIOD]), mnesia:transaction(fun () -> Time = p1_time_compat:monotonic_time(), mnesia:write(#temporarily_blocked{host = Host, timestamp = Time}) end). -spec is_temporarly_blocked(binary()) -> boolean(). is_temporarly_blocked(Host) -> case mnesia:dirty_read(temporarily_blocked, Host) of [] -> false; [#temporarily_blocked{timestamp = T} = Entry] -> Diff = p1_time_compat:monotonic_time() - T, case p1_time_compat:convert_time_unit(Diff, native, micro_seconds) of N when N > (?S2S_OVERLOAD_BLOCK_PERIOD) * 1000 * 1000 -> mnesia:dirty_delete_object(Entry), false; _ -> true end end. -spec remove_connection({binary(), binary()}, pid()) -> {atomic, ok} | ok | {aborted, any()}. remove_connection(FromTo, Pid) -> case catch mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo, pid = Pid}) of [#s2s{pid = Pid}] -> F = fun () -> mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid}) end, mnesia:transaction(F); _ -> ok end. -spec have_connection({binary(), binary()}) -> boolean(). have_connection(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of [_] -> true; _ -> false end. -spec get_connections_pids({binary(), binary()}) -> [pid()]. get_connections_pids(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of L when is_list(L) -> [Connection#s2s.pid || Connection <- L]; _ -> [] end. -spec try_register({binary(), binary()}) -> boolean(). try_register(FromTo) -> MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo), MaxS2SConnectionsNumberPerNode = max_s2s_connections_number_per_node(FromTo), F = fun () -> L = mnesia:read({s2s, FromTo}), NeededConnections = needed_connections_number(L, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode), if NeededConnections > 0 -> mnesia:write(#s2s{fromto = FromTo, pid = self()}), true; true -> false end end, case mnesia:transaction(F) of {atomic, Res} -> Res; _ -> false end. -spec dirty_get_connections() -> [{binary(), binary()}]. dirty_get_connections() -> mnesia:dirty_all_keys(s2s). -spec tls_options(binary(), [proplists:property()]) -> [proplists:property()]. tls_options(LServer, DefaultOpts) -> TLSOpts1 = case get_certfile(LServer) of undefined -> DefaultOpts; CertFile -> lists:keystore(certfile, 1, DefaultOpts, {certfile, CertFile}) end, TLSOpts2 = case ejabberd_config:get_option( {s2s_ciphers, LServer}) of undefined -> TLSOpts1; Ciphers -> lists:keystore(ciphers, 1, TLSOpts1, {ciphers, Ciphers}) end, TLSOpts3 = case ejabberd_config:get_option( {s2s_protocol_options, LServer}) of undefined -> TLSOpts2; ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2, {protocol_options, ProtoOpts}) end, TLSOpts4 = case ejabberd_config:get_option( {s2s_dhfile, LServer}) of undefined -> TLSOpts3; DHFile -> lists:keystore(dhfile, 1, TLSOpts3, {dhfile, DHFile}) end, TLSOpts5 = case get_cafile(LServer) of undefined -> TLSOpts4; CAFile -> lists:keystore(cafile, 1, TLSOpts4, {cafile, CAFile}) end, case ejabberd_config:get_option({s2s_tls_compression, LServer}) of undefined -> TLSOpts5; false -> [compression_none | TLSOpts5]; true -> lists:delete(compression_none, TLSOpts5) end. -spec tls_required(binary()) -> boolean(). tls_required(LServer) -> TLS = use_starttls(LServer), TLS == required orelse TLS == required_trusted. -spec tls_verify(binary()) -> boolean(). tls_verify(LServer) -> TLS = use_starttls(LServer), TLS == required_trusted. -spec tls_enabled(binary()) -> boolean(). tls_enabled(LServer) -> TLS = use_starttls(LServer), TLS /= false. -spec zlib_enabled(binary()) -> boolean(). zlib_enabled(LServer) -> ejabberd_config:get_option({s2s_zlib, LServer}, false). -spec use_starttls(binary()) -> boolean() | optional | required | required_trusted. use_starttls(LServer) -> ejabberd_config:get_option({s2s_use_starttls, LServer}, false). -spec get_idle_timeout(binary()) -> non_neg_integer() | infinity. get_idle_timeout(LServer) -> ejabberd_config:get_option({s2s_timeout, LServer}, timer:minutes(10)). -spec queue_type(binary()) -> ram | file. queue_type(LServer) -> ejabberd_config:get_option( {s2s_queue_type, LServer}, ejabberd_config:default_queue_type(LServer)). -spec get_certfile(binary()) -> file:filename_all() | undefined. get_certfile(LServer) -> case ejabberd_pkix:get_certfile(LServer) of {ok, CertFile} -> CertFile; error -> ejabberd_config:get_option( {domain_certfile, LServer}, ejabberd_config:get_option({s2s_certfile, LServer})) end. -spec get_cafile(binary()) -> file:filename_all() | undefined. get_cafile(LServer) -> case ejabberd_config:get_option({s2s_cafile, LServer}) of undefined -> ejabberd_pkix:ca_file(); File -> File end. %%==================================================================== %% gen_server callbacks %%==================================================================== init([]) -> update_tables(), ejabberd_mnesia:create(?MODULE, s2s, [{ram_copies, [node()]}, {type, bag}, {attributes, record_info(fields, s2s)}]), mnesia:subscribe(system), ejabberd_commands:register_commands(get_commands_spec()), ejabberd_mnesia:create(?MODULE, temporarily_blocked, [{ram_copies, [node()]}, {attributes, record_info(fields, temporarily_blocked)}]), ejabberd_hooks:add(host_up, ?MODULE, host_up, 50), ejabberd_hooks:add(host_down, ?MODULE, host_down, 60), lists:foreach(fun host_up/1, ?MYHOSTS), {ok, #state{}}. handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> clean_table_from_bad_node(Node), {noreply, State}; handle_info({route, Packet}, State) -> route(Packet), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ejabberd_commands:unregister_commands(get_commands_spec()), lists:foreach(fun host_down/1, ?MYHOSTS), ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50), ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- host_up(Host) -> ejabberd_s2s_in:host_up(Host), ejabberd_s2s_out:host_up(Host). host_down(Host) -> lists:foreach( fun(#s2s{fromto = {From, _}, pid = Pid}) when node(Pid) == node() -> case ejabberd_router:host_of_route(From) of Host -> ejabberd_s2s_out:send(Pid, xmpp:serr_system_shutdown()), ejabberd_s2s_out:stop(Pid); _ -> ok end; (_) -> ok end, ets:tab2list(s2s)), ejabberd_s2s_in:host_down(Host), ejabberd_s2s_out:host_down(Host). -spec clean_table_from_bad_node(node()) -> any(). clean_table_from_bad_node(Node) -> F = fun() -> Es = mnesia:select( s2s, [{#s2s{pid = '$1', _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}]), lists:foreach(fun(E) -> mnesia:delete_object(E) end, Es) end, mnesia:async_dirty(F). -spec do_route(stanza()) -> ok. do_route(Packet) -> ?DEBUG("local route:~n~s", [xmpp:pp(Packet)]), From = xmpp:get_from(Packet), To = xmpp:get_to(Packet), case start_connection(From, To) of {ok, Pid} when is_pid(Pid) -> ?DEBUG("sending to process ~p~n", [Pid]), #jid{lserver = MyServer} = From, ejabberd_hooks:run(s2s_send_packet, MyServer, [Packet]), ejabberd_s2s_out:route(Pid, Packet); {error, Reason} -> Lang = xmpp:get_lang(Packet), Err = case Reason of policy_violation -> xmpp:err_policy_violation( <<"Server connections to local " "subdomains are forbidden">>, Lang); forbidden -> xmpp:err_forbidden(<<"Access denied by service policy">>, Lang); internal_server_error -> xmpp:err_internal_server_error() end, ejabberd_router:route_error(Packet, Err) end. -spec start_connection(jid(), jid()) -> {ok, pid()} | {error, policy_violation | forbidden | internal_server_error}. start_connection(From, To) -> start_connection(From, To, []). -spec start_connection(jid(), jid(), [proplists:property()]) -> {ok, pid()} | {error, policy_violation | forbidden | internal_server_error}. start_connection(From, To, Opts) -> #jid{lserver = MyServer} = From, #jid{lserver = Server} = To, FromTo = {MyServer, Server}, MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo), MaxS2SConnectionsNumberPerNode = max_s2s_connections_number_per_node(FromTo), ?DEBUG("Finding connection for ~p~n", [FromTo]), case mnesia:dirty_read(s2s, FromTo) of [] -> %% We try to establish all the connections if the host is not a %% service and if the s2s host is not blacklisted or %% is in whitelist: LServer = ejabberd_router:host_of_route(MyServer), case is_service(From, To) of true -> {error, policy_violation}; false -> case allow_host(LServer, Server) of true -> NeededConnections = needed_connections_number( [], MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode), open_several_connections(NeededConnections, MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts); false -> {error, forbidden} end end; L when is_list(L) -> NeededConnections = needed_connections_number(L, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode), if NeededConnections > 0 -> %% We establish the missing connections for this pair. open_several_connections(NeededConnections, MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts); true -> %% We choose a connexion from the pool of opened ones. {ok, choose_connection(From, L)} end end. -spec choose_connection(jid(), [#s2s{}]) -> pid(). choose_connection(From, Connections) -> choose_pid(From, [C#s2s.pid || C <- Connections]). -spec choose_pid(jid(), [pid()]) -> pid(). choose_pid(From, Pids) -> Pids1 = case [P || P <- Pids, node(P) == node()] of [] -> Pids; Ps -> Ps end, Pid = lists:nth(erlang:phash(jid:remove_resource(From), length(Pids1)), Pids1), ?DEBUG("Using ejabberd_s2s_out ~p~n", [Pid]), Pid. open_several_connections(N, MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) -> case lists:flatmap( fun(_) -> new_connection(MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) end, lists:seq(1, N)) of [] -> {error, internal_server_error}; PIDs -> {ok, choose_pid(From, PIDs)} end. new_connection(MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) -> {ok, Pid} = ejabberd_s2s_out:start(MyServer, Server, Opts), F = fun() -> L = mnesia:read({s2s, FromTo}), NeededConnections = needed_connections_number(L, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode), if NeededConnections > 0 -> mnesia:write(#s2s{fromto = FromTo, pid = Pid}), Pid; true -> choose_connection(From, L) end end, TRes = mnesia:transaction(F), case TRes of {atomic, Pid1} -> if Pid1 == Pid -> ejabberd_s2s_out:connect(Pid); true -> ejabberd_s2s_out:stop(Pid) end, [Pid1]; {aborted, Reason} -> ?ERROR_MSG("failed to register connection ~s -> ~s: ~p", [MyServer, Server, Reason]), ejabberd_s2s_out:stop(Pid), [] end. -spec max_s2s_connections_number({binary(), binary()}) -> integer(). max_s2s_connections_number({From, To}) -> case acl:match_rule(From, max_s2s_connections, jid:make(To)) of Max when is_integer(Max) -> Max; _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER end. -spec max_s2s_connections_number_per_node({binary(), binary()}) -> integer(). max_s2s_connections_number_per_node({From, To}) -> case acl:match_rule(From, max_s2s_connections_per_node, jid:make(To)) of Max when is_integer(Max) -> Max; _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE end. -spec needed_connections_number([#s2s{}], integer(), integer()) -> integer(). needed_connections_number(Ls, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) -> LocalLs = [L || L <- Ls, node(L#s2s.pid) == node()], lists:min([MaxS2SConnectionsNumber - length(Ls), MaxS2SConnectionsNumberPerNode - length(LocalLs)]). %%-------------------------------------------------------------------- %% Function: is_service(From, To) -> true | false %% Description: Return true if the destination must be considered as a %% service. %% -------------------------------------------------------------------- -spec is_service(jid(), jid()) -> boolean(). is_service(From, To) -> LFromDomain = From#jid.lserver, case ejabberd_config:get_option({route_subdomains, LFromDomain}, local) of s2s -> % bypass RFC 3920 10.3 false; local -> Hosts = (?MYHOSTS), P = fun (ParentDomain) -> lists:member(ParentDomain, Hosts) end, lists:any(P, parent_domains(To#jid.lserver)) end. parent_domains(Domain) -> lists:foldl(fun (Label, []) -> [Label]; (Label, [Head | Tail]) -> [<