diff --git a/.envrc b/.envrc deleted file mode 100644 index 65adb17..0000000 --- a/.envrc +++ /dev/null @@ -1,4 +0,0 @@ -openssl=$(brew --prefix openssl) -libyaml=$(brew --prefix libyaml) -export CFLAGS="-I$openssl/include -I$libyaml/include" -export LDFLAGS="-L$openssl/lib -L$libyaml/lib" diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..07415c5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: [push, pull-request] + +jobs: + test: + runs-on: ubuntu:latest + container: + image: elixir:latest + + steps: + - uses: actions/checkout@v1 + - name: Install Dependencies + run: | + mix local.rebar --force + mix local.hex --force + mix deps.get + - name: Run Tests + run: mix tes + + services: + ejabberd: + image: rroemhild/ejabberd + ports: + - 5222:5222 + - 5269:5269 + - 5280:5280 + env: + - XMPP_DOMAIN: localhost + - EJABBERD_SKIP_MODULES_UPDATE: true + options: --name ejabberd diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..cc07491 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +elixir 1.9.1 +erlang 22.0 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e797a61..0000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: elixir - -matrix: - include: - - otp_release: 18.3 - elixir: 1.3 - - otp_release: 19.3 - elixir: 1.3 - - otp_release: 18.3 - elixir: 1.4 - - otp_release: 19.3 - elixir: 1.4 - - otp_release: 20.2 - elixir: 1.4 - - otp_release: 19.3 - elixir: 1.5 - - otp_release: 20.2 - elixir: 1.5 - - otp_release: 20.2 - elixir: 1.6 - -env: - global: - - MIX_ENV=test - - TRAVIS=false - -sudo: false - -install: - - mix local.hex --force - - mix local.rebar --force - - mix deps.get - -before_script: - - mix compile - - mix ejabberd.gen.config - -after_script: - - mix coveralls.travis diff --git a/config/config.exs b/config/config.exs deleted file mode 100644 index 515efbb..0000000 --- a/config/config.exs +++ /dev/null @@ -1,13 +0,0 @@ -use Mix.Config - -config :ex_unit, :assert_receive_timeout, 2000 - -config :logger, level: :debug - -config :mnesia, dir: 'mnesia' - -config :sasl, sasl_error_logger: false - -config :ejabberd, - file: "config/ejabberd.yml", - log_path: "logs/ejabberd.log" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1f560b6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3" + +services: + ejabberd: + image: rroemhild/ejabberd + container_name: ejabberd + ports: + - 5222:5222 + - 5269:5269 + - 5280:5280 + environment: + XMPP_DOMAIN: localhost + EJABBERD_SKIP_MODULES_UPDATE: "true" diff --git a/lib/mix/tasks/ejabberd.gen.config.ex b/lib/mix/tasks/ejabberd.gen.config.ex deleted file mode 100644 index 33e8b35..0000000 --- a/lib/mix/tasks/ejabberd.gen.config.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule Mix.Tasks.Ejabberd.Gen.Config do - use Mix.Task - - @shortdoc "Generates an ejabberd.yml file" - - @moduledoc """ - Generates an ejabberd.yml file. - - mix ejabberd.gen.config - """ - - def run([]) do - cwd = File.cwd! - source = Path.join(cwd, "priv/templates/ejabberd.yml.eex") - target = Path.join(cwd, "config/ejabberd.yml") - contents = EEx.eval_file(source, cwd: cwd) - - Mix.Generator.create_file(target, contents) - end -end diff --git a/lib/romeo/auth.ex b/lib/romeo/auth.ex index 5e8fda7..7b10d07 100644 --- a/lib/romeo/auth.ex +++ b/lib/romeo/auth.ex @@ -8,14 +8,14 @@ defmodule Romeo.Auth do defmodule Mechanism do @doc "Authenticates using the supplied mechanism" - @callback authenticate(String.t, Romeo.Connection.t) :: Romeo.Connection.t + @callback authenticate(String.t(), Romeo.Connection.t()) :: Romeo.Connection.t() end defmodule Error do defexception [:message] def exception(mechanism) do - msg = "Failed to authenticate using mechanism: #{inspect mechanism}" + msg = "Failed to authenticate using mechanism: #{inspect(mechanism)}" %Romeo.Auth.Error{message: msg} end end @@ -26,7 +26,7 @@ defmodule Romeo.Auth do If the preferred mechanism is not supported it will choose PLAIN. """ def authenticate!(conn) do - preferred = conn.preferred_auth_mechanisms + preferred = conn.preferred_auth_mechanisms mechanisms = conn.features.mechanisms preferred_mechanism(preferred, mechanisms) |> do_authenticate(conn) end @@ -42,20 +42,21 @@ defmodule Romeo.Auth do |> mod.recv(fn conn, xmlel(name: "handshake") -> conn + _conn, xmlel(name: "stream:error") -> raise Romeo.Auth.Error, "handshake error" end) end - defp do_authenticate(mechanism, conn) do {:ok, conn} = case mechanism do {name, mod} -> - Logger.info fn -> "Authenticating with extension #{name} implemented by #{mod}" end + Logger.info(fn -> "Authenticating with extension #{name} implemented by #{mod}" end) mod.authenticate(name, conn) + _ -> - Logger.info fn -> "Authenticating with #{mechanism}" end + Logger.info(fn -> "Authenticating with #{mechanism}" end) authenticate_with(mechanism, conn) end @@ -71,17 +72,18 @@ defmodule Romeo.Auth do mod.send(conn, Romeo.Stanza.auth("PLAIN", Romeo.Stanza.base64_cdata(payload))) end - defp authenticate_with("ANONYMOUS", %{transport: mod} = conn) do conn |> mod.send(Romeo.Stanza.auth("ANONYMOUS")) end defp authenticate_with(mechanism_name, _conn) do raise """ - Romeo does not include an implementation for authentication mechanism #{inspect mechanism_name}. + Romeo does not include an implementation for authentication mechanism #{ + inspect(mechanism_name) + }. Please provide an implementation such as - Romeo.Connection.start_link(preferred_auth_mechanisms: [{#{inspect mechanism_name}, SomeModule}]) + Romeo.Connection.start_link(preferred_auth_mechanisms: [{#{inspect(mechanism_name)}, SomeModule}]) where `SomeModule` implements the Romeo.Auth.Mechanism behaviour. """ @@ -91,8 +93,9 @@ defmodule Romeo.Auth do mod.recv(conn, fn conn, xmlel(name: name) -> case name do "success" -> - Logger.info fn -> "Authenticated successfully" end + Logger.info(fn -> "Authenticated successfully" end) {:ok, conn} + "failure" -> {:error, conn} end @@ -104,15 +107,17 @@ defmodule Romeo.Auth do end defp preferred_mechanism([], _), do: "PLAIN" + defp preferred_mechanism([mechanism | tail], mechanisms) do case acceptable_mechanism?(mechanism, mechanisms) do - true -> mechanism + true -> mechanism false -> preferred_mechanism(tail, mechanisms) end end defp acceptable_mechanism?({name, _mod}, mechanisms), do: acceptable_mechanism?(name, mechanisms) + defp acceptable_mechanism?(mechanism, mechanisms), do: Enum.member?(mechanisms, mechanism) end diff --git a/lib/romeo/connection.ex b/lib/romeo/connection.ex index 0ef740e..04fa9e5 100644 --- a/lib/romeo/connection.ex +++ b/lib/romeo/connection.ex @@ -75,7 +75,7 @@ defmodule Romeo.Connection do * `:timeout` - Call timeout (default: `#{@timeout}`) """ - @spec close(pid, Keyword.t) :: :ok + @spec close(pid, Keyword.t()) :: :ok def close(pid, opts \\ []) do Connection.call(pid, :close, opts[:timeout] || @timeout) end @@ -90,6 +90,7 @@ defmodule Romeo.Connection do case transport.connect(conn) do {:ok, conn} -> {:ok, conn} + {:error, _} -> {:backoff, timeout, conn} end @@ -107,14 +108,17 @@ defmodule Romeo.Connection do def handle_call(_, _, %{socket: nil} = conn) do {:reply, {:error, :closed}, conn} end + def handle_call({:send, data}, _, %{transport: transport} = conn) do case transport.send(conn, data) do {:ok, conn} -> {:reply, :ok, conn} + {:error, _} = error -> {:disconnect, error, error, conn} end end + def handle_call(:close, from, %{socket: socket, transport: transport} = conn) do transport.disconnect({:close, from}, socket) {:reply, :ok, conn} @@ -124,16 +128,20 @@ defmodule Romeo.Connection do case transport.handle_message(info, conn) do {:ok, conn, :more} -> {:noreply, conn} + {:ok, conn, stanza} -> stanza = Romeo.Stanza.Parser.parse(stanza) Kernel.send(owner, {:stanza, stanza}) {:noreply, conn} + {:error, _} = error -> {:disconnect, error, conn} + :unknown -> - Logger.debug fn -> + Logger.debug(fn -> [inspect(__MODULE__), ?\s, inspect(self()), " received message: " | inspect(info)] - end + end) + {:noreply, conn} end end diff --git a/lib/romeo/connection/features.ex b/lib/romeo/connection/features.ex index 084a525..a07a0e9 100644 --- a/lib/romeo/connection/features.ex +++ b/lib/romeo/connection/features.ex @@ -6,14 +6,12 @@ defmodule Romeo.Connection.Features do use Romeo.XML @type t :: %__MODULE__{} - defstruct [ - amp?: false, - compression?: false, - registration?: false, - stream_management?: false, - tls?: false, - mechanisms: [] - ] + defstruct amp?: false, + compression?: false, + registration?: false, + stream_management?: false, + tls?: false, + mechanisms: [] def parse_stream_features(features) do %__MODULE__{ @@ -31,7 +29,9 @@ defmodule Romeo.Connection.Features do xml when Record.is_record(xml, :xmlel) -> mechanisms = xmlel(xml, :children) for mechanism <- mechanisms, into: [], do: Romeo.XML.cdata(mechanism) - nil -> [] + + nil -> + [] end end @@ -40,13 +40,16 @@ defmodule Romeo.Connection.Features do xml when Record.is_record(xml, :xmlel) -> methods = xmlel(xml, :children) for method <- methods, into: [], do: Romeo.XML.cdata(method) - _ -> false + + _ -> + false end end + def supports?(features, feature) do case Romeo.XML.subelement(features, feature) do nil -> false - _ -> true + _ -> true end end end diff --git a/lib/romeo/error.ex b/lib/romeo/error.ex index bd89335..2948c95 100644 --- a/lib/romeo/error.ex +++ b/lib/romeo/error.ex @@ -10,6 +10,7 @@ defmodule Romeo.Error do secs = ms / 1_000 "Failed to #{step} after #{secs} seconds." end + defp translate_message(message), do: inspect(message) defp translate_connection_step(atom) do diff --git a/lib/romeo/jid.ex b/lib/romeo/jid.ex index 322068e..b68c5b1 100644 --- a/lib/romeo/jid.ex +++ b/lib/romeo/jid.ex @@ -40,9 +40,11 @@ defmodule Romeo.JID do defimpl String.Chars, for: JID do def to_string(%JID{user: "", server: server, resource: ""}), do: server + def to_string(%JID{user: user, server: server, resource: ""}) do user <> "@" <> server end + def to_string(%JID{user: user, server: server, resource: resource}) do user <> "@" <> server <> "/" <> resource end @@ -58,19 +60,19 @@ defmodule Romeo.JID do iex> Romeo.JID.bare("romeo@montague.lit/chamber") "romeo@montague.lit" """ - @spec bare(jid :: binary | JID.t) :: binary + @spec bare(jid :: binary | JID.t()) :: binary def bare(jid) when is_binary(jid), do: parse(jid) |> bare def bare(%JID{} = jid), do: to_string(%JID{jid | resource: ""}) - @spec user(jid :: binary | JID.t) :: binary + @spec user(jid :: binary | JID.t()) :: binary def user(jid) when is_binary(jid), do: parse(jid).user def user(%JID{user: user}), do: user - @spec server(jid :: binary | JID.t) :: binary + @spec server(jid :: binary | JID.t()) :: binary def server(jid) when is_binary(jid), do: parse(jid).server def server(%JID{server: server}), do: server - @spec resource(jid :: binary | JID.t) :: binary + @spec resource(jid :: binary | JID.t()) :: binary def resource(jid) when is_binary(jid), do: parse(jid).resource def resource(%JID{resource: resource}), do: resource @@ -84,13 +86,15 @@ defmodule Romeo.JID do iex> Romeo.JID.parse("romeo@montague.lit") %Romeo.JID{user: "romeo", server: "montague.lit", resource: "", full: "romeo@montague.lit"} """ - @spec parse(jid :: binary) :: JID.t + @spec parse(jid :: binary) :: JID.t() def parse(string) do case String.split(string, ["@", "/"], parts: 3) do [user, server, resource] -> %JID{user: user, server: server, resource: resource, full: string} + [user, server] -> %JID{user: user, server: server, full: string} + [server] -> %JID{server: server, full: string} end diff --git a/lib/romeo/roster.ex b/lib/romeo/roster.ex index f28492f..1ff12b0 100644 --- a/lib/romeo/roster.ex +++ b/lib/romeo/roster.ex @@ -13,8 +13,9 @@ defmodule Romeo.Roster do Returns roster items """ def items(pid) do - Logger.info fn -> "Getting roster items" end - stanza = Stanza.get_roster + Logger.info(fn -> "Getting roster items" end) + stanza = Stanza.get_roster() + case send_stanza(pid, stanza) do {:ok, %IQ{type: "result"} = result} -> result.items _ -> :error @@ -25,29 +26,32 @@ defmodule Romeo.Roster do Adds jid to roster """ def add(pid, jid) do - Logger.info fn -> "Adding #{jid} to roster items" end + Logger.info(fn -> "Adding #{jid} to roster items" end) stanza = Stanza.set_roster_item(jid) + case send_stanza(pid, stanza) do {:ok, _} -> :ok _ -> :error end end + def add(pid, jid, name) do - Logger.info fn -> "Adding #{jid} to roster items" end + Logger.info(fn -> "Adding #{jid} to roster items" end) stanza = Stanza.set_roster_item(jid, "both", name) + case send_stanza(pid, stanza) do {:ok, _} -> :ok _ -> :error end end - @doc """ Removes jid to roster """ def remove(pid, jid) do - Logger.info fn -> "Removing #{jid} from roster items" end + Logger.info(fn -> "Removing #{jid} from roster items" end) stanza = Stanza.set_roster_item(jid, "remove") + case send_stanza(pid, stanza) do {:ok, _} -> :ok _ -> :error @@ -61,8 +65,9 @@ defmodule Romeo.Roster do receive do {:stanza, %IQ{type: "result", id: ^id} = result} -> {:ok, result} {:stanza, %IQ{type: "error", id: ^id}} -> :error - after @timeout -> - {:error, :timeout} + after + @timeout -> + {:error, :timeout} end end end diff --git a/lib/romeo/roster/item.ex b/lib/romeo/roster/item.ex index 78089c8..02bf7ff 100644 --- a/lib/romeo/roster/item.ex +++ b/lib/romeo/roster/item.ex @@ -1,19 +1,17 @@ defmodule Romeo.Roster.Item do - @type jid :: Romeo.JID.t + @type jid :: Romeo.JID.t() @type t :: %__MODULE__{ - subscription: binary, - name: binary, - jid: jid, - group: binary, - xml: tuple - } + subscription: binary, + name: binary, + jid: jid, + group: binary, + xml: tuple + } - defstruct [ - subscription: nil, - name: nil, - jid: nil, - group: nil, - xml: nil - ] + defstruct subscription: nil, + name: nil, + jid: nil, + group: nil, + xml: nil end diff --git a/lib/romeo/stanza.ex b/lib/romeo/stanza.ex index 67051e3..526254b 100644 --- a/lib/romeo/stanza.ex +++ b/lib/romeo/stanza.ex @@ -11,27 +11,32 @@ defmodule Romeo.Stanza do def to_xml(record) when Record.is_record(record) do Romeo.XML.encode!(record) end + def to_xml(record) when Record.is_record(record) do Romeo.XML.encode!(record) end def to_xml(%IQ{} = stanza) do - xmlel(name: "iq", + xmlel( + name: "iq", attrs: [ {"to", to_string(stanza.to)}, {"type", stanza.type}, {"id", stanza.id} ] - ) |> to_xml() + ) + |> to_xml() end def to_xml(%Presence{} = stanza) do - xmlel(name: "presence", + xmlel( + name: "presence", attrs: [ {"to", to_string(stanza.to)}, {"type", stanza.type} ] - ) |> to_xml() + ) + |> to_xml() end def to_xml(%Message{to: to, type: type, body: body}) do @@ -52,14 +57,16 @@ defmodule Romeo.Stanza do "" """ def start_stream(server, xmlns \\ ns_jabber_client()) do - xmlstreamstart(name: "stream:stream", + xmlstreamstart( + name: "stream:stream", attrs: [ {"to", server}, {"version", "1.0"}, {"xml:lang", "en"}, {"xmlns", xmlns}, {"xmlns:stream", ns_xmpp()} - ]) + ] + ) end @doc """ @@ -83,20 +90,24 @@ defmodule Romeo.Stanza do "" """ def start_tls do - xmlel(name: "starttls", + xmlel( + name: "starttls", attrs: [ {"xmlns", ns_tls()} - ]) + ] + ) end def compress(method) do - xmlel(name: "compress", + xmlel( + name: "compress", attrs: [ {"xmlns", ns_compress()} ], children: [ xmlel(name: "method", children: [cdata(method)]) - ]) + ] + ) end def handshake(hash) do @@ -105,42 +116,58 @@ defmodule Romeo.Stanza do end def auth(mechanism), do: auth(mechanism, [], []) + def auth(mechanism, body) do auth(mechanism, body, []) end + def auth(mechanism, [], []) do - xmlel(name: "auth", + xmlel( + name: "auth", attrs: [ - {"xmlns", ns_sasl}, - {"mechanism", mechanism}, + {"xmlns", ns_sasl()}, + {"mechanism", mechanism} ], - children: []) + children: [] + ) end + def auth(mechanism, body, []) do - xmlel(name: "auth", + xmlel( + name: "auth", attrs: [ - {"xmlns", ns_sasl}, - {"mechanism", mechanism}, + {"xmlns", ns_sasl()}, + {"mechanism", mechanism} ], - children: [body]) + children: [body] + ) end + def auth(mechanism, body, additional_attrs) do - xmlel(name: "auth", + xmlel( + name: "auth", attrs: [ {"xmlns", ns_sasl()}, {"mechanism", mechanism} | additional_attrs ], - children: [body]) + children: [body] + ) end def bind(resource) do - body = xmlel(name: "bind", - attrs: [{"xmlns", ns_bind()}], - children: [ - xmlel(name: "resource", - children: [cdata(resource)]) - ]) + body = + xmlel( + name: "bind", + attrs: [{"xmlns", ns_bind()}], + children: [ + xmlel( + name: "resource", + children: [cdata(resource)] + ) + ] + ) + iq("set", body) end @@ -169,7 +196,7 @@ defmodule Romeo.Stanza do def iq(to, type, body) do iq = iq(type, body) - xmlel(iq, attrs: [{"to", to}|xmlel(iq, :attrs)]) + xmlel(iq, attrs: [{"to", to} | xmlel(iq, :attrs)]) end def get_roster do @@ -182,22 +209,31 @@ defmodule Romeo.Stanza do "" -> Romeo.JID.parse(jid).user _ -> name end + group_xmlel = case group do "" -> [] _ -> [xmlel(name: "group", children: [cdata(group)])] end - iq("set", xmlel( - name: "query", - attrs: [{"xmlns", ns_roster()}], - children: [ - xmlel(name: "item", attrs: [ - {"jid", jid}, - {"subscription", subscription}, - {"name", name_to_set} - ], children: group_xmlel) - ] - )) + + iq( + "set", + xmlel( + name: "query", + attrs: [{"xmlns", ns_roster()}], + children: [ + xmlel( + name: "item", + attrs: [ + {"jid", jid}, + {"subscription", subscription}, + {"name", name_to_set} + ], + children: group_xmlel + ) + ] + ) + ) end def get_inband_register do @@ -205,14 +241,17 @@ defmodule Romeo.Stanza do end def set_inband_register(username, password) do - iq("set", xmlel( - name: "query", - attrs: [{"xmlns", ns_inband_register()}], - children: [ - xmlel(name: "username", children: [cdata(username)]), - xmlel(name: "password", children: [cdata(password)]) - ] - )) + iq( + "set", + xmlel( + name: "query", + attrs: [{"xmlns", ns_inband_register()}], + children: [ + xmlel(name: "username", children: [cdata(username)]), + xmlel(name: "password", children: [cdata(password)]) + ] + ) + ) end def get_vcard(to) do @@ -231,12 +270,17 @@ defmodule Romeo.Stanza do Generates a stanza to join a pubsub node. (XEP-0060) """ def subscribe(to, node, jid) do - iq(to, "set", xmlel( - name: "pubsub", - attrs: [{"xmlns", ns_pubsub()}], - children: [ - xmlel(name: "subscribe", attrs: [{"node", node}, {"jid", jid}]) - ])) + iq( + to, + "set", + xmlel( + name: "pubsub", + attrs: [{"xmlns", ns_pubsub()}], + children: [ + xmlel(name: "subscribe", attrs: [{"node", node}, {"jid", jid}]) + ] + ) + ) end @doc """ @@ -262,7 +306,7 @@ defmodule Romeo.Stanza do [{:xmlel, "history", [{"maxstanzas", "0"}], []}]}]} """ def join(room, nickname, opts \\ []) do - history = Keyword.get(opts, :history) + history = Keyword.get(opts, :history) password = Keyword.get(opts, :password) password = if password, do: [muc_password(password)], else: [] @@ -270,15 +314,19 @@ defmodule Romeo.Stanza do children = history ++ password - xmlel(name: "presence", + xmlel( + name: "presence", attrs: [ {"to", "#{room}/#{nickname}"} ], children: [ - xmlel(name: "x", + xmlel( + name: "x", attrs: [{"xmlns", ns_muc()}], - children: children) - ]) + children: children + ) + ] + ) end defp history([{key, value}]) do @@ -296,54 +344,66 @@ defmodule Romeo.Stanza do def message(msg) when is_map(msg) do message(msg["to"], msg["type"], msg["body"]) end + def message(to, type, message) do - xmlel(name: "message", + xmlel( + name: "message", attrs: [ {"to", to}, {"type", type}, {"id", id()}, {"xml:lang", "en"} ], - children: generate_body(message)) + children: generate_body(message) + ) end def generate_body(data) do cond do is_list(data) -> data + is_tuple(data) -> [data] + true -> [body(data)] end end def body(data) do - xmlel(name: "body", + xmlel( + name: "body", children: [ cdata(data) - ]) + ] + ) end def xhtml_im(data) when is_binary(data) do data - |> :fxml_stream.parse_element + |> :fxml_stream.parse_element() |> xhtml_im() end + def xhtml_im(data) do - xmlel(name: "html", + xmlel( + name: "html", attrs: [ {"xmlns", ns_xhtml_im()} ], children: [ - xmlel(name: "body", + xmlel( + name: "body", attrs: [ {"xmlns", ns_xhtml()} ], children: [ data - ]) - ]) + ] + ) + ] + ) end def cdata(payload) do diff --git a/lib/romeo/stanza/iq.ex b/lib/romeo/stanza/iq.ex index 9f8fc03..8064337 100644 --- a/lib/romeo/stanza/iq.ex +++ b/lib/romeo/stanza/iq.ex @@ -1,23 +1,21 @@ defmodule Romeo.Stanza.IQ do use Romeo.XML - @type jid :: Romeo.JID.t + @type jid :: Romeo.JID.t() @type t :: %__MODULE__{ - id: binary, - to: jid, - from: jid, - type: binary, - items: list | nil, - xml: tuple - } + id: binary, + to: jid, + from: jid, + type: binary, + items: list | nil, + xml: tuple + } - defstruct [ - id: nil, - to: nil, - from: nil, - type: nil, - items: nil, - xml: nil - ] + defstruct id: nil, + to: nil, + from: nil, + type: nil, + items: nil, + xml: nil end diff --git a/lib/romeo/stanza/message.ex b/lib/romeo/stanza/message.ex index 95fedbd..d47ced4 100644 --- a/lib/romeo/stanza/message.ex +++ b/lib/romeo/stanza/message.ex @@ -1,27 +1,25 @@ defmodule Romeo.Stanza.Message do use Romeo.XML - @type jid :: Romeo.JID.t + @type jid :: Romeo.JID.t() @type t :: %__MODULE__{ - id: binary, - to: jid, - from: jid, - type: binary, - body: binary | list, - html: binary | list | nil, - xml: tuple, - delayed?: boolean - } + id: binary, + to: jid, + from: jid, + type: binary, + body: binary | list, + html: binary | list | nil, + xml: tuple, + delayed?: boolean + } - defstruct [ - id: "", - to: nil, - from: nil, - type: "normal", - body: "", - html: nil, - xml: nil, - delayed?: false - ] + defstruct id: "", + to: nil, + from: nil, + type: "normal", + body: "", + html: nil, + xml: nil, + delayed?: false end diff --git a/lib/romeo/stanza/parser.ex b/lib/romeo/stanza/parser.ex index 83352bb..b759266 100644 --- a/lib/romeo/stanza/parser.ex +++ b/lib/romeo/stanza/parser.ex @@ -9,43 +9,45 @@ defmodule Romeo.Stanza.Parser do def parse(xmlel(name: "message", attrs: attrs) = stanza) do struct(Message, parse_attrs(attrs)) - |> struct([body: get_body(stanza)]) - |> struct([html: get_html(stanza)]) - |> struct([xml: stanza]) - |> struct([delayed?: delayed?(stanza)]) + |> struct(body: get_body(stanza)) + |> struct(html: get_html(stanza)) + |> struct(xml: stanza) + |> struct(delayed?: delayed?(stanza)) end def parse(xmlel(name: "presence", attrs: attrs) = stanza) do struct(Presence, parse_attrs(attrs)) - |> struct([show: get_show(stanza)]) - |> struct([status: get_status(stanza)]) - |> struct([xml: stanza]) + |> struct(show: get_show(stanza)) + |> struct(status: get_status(stanza)) + |> struct(xml: stanza) end def parse(xmlel(name: "iq", attrs: attrs) = stanza) do case :fxml.get_path_s(stanza, [{:elem, "query"}, {:attr, "xmlns"}]) do "jabber:iq:roster" -> struct(IQ, parse_attrs(attrs)) - |> struct([items: (Romeo.XML.subelement(stanza, "query") |> parse)]) - |> struct([xml: stanza]) - _ -> struct(IQ, parse_attrs(attrs)) |> struct([xml: stanza]) + |> struct(items: Romeo.XML.subelement(stanza, "query") |> parse) + |> struct(xml: stanza) + + _ -> + struct(IQ, parse_attrs(attrs)) |> struct(xml: stanza) end end def parse(xmlel(name: "query") = stanza) do - stanza |> Romeo.XML.subelements("item") |> Enum.map(&parse/1) |> Enum.reverse + stanza |> Romeo.XML.subelements("item") |> Enum.map(&parse/1) |> Enum.reverse() end def parse(xmlel(name: "item", attrs: attrs) = stanza) do struct(Item, parse_attrs(attrs)) - |> struct([group: get_group(stanza)]) - |> struct([xml: stanza]) + |> struct(group: get_group(stanza)) + |> struct(xml: stanza) end def parse(xmlel(name: name, attrs: attrs) = stanza) do [name: name] |> Keyword.merge(parse_attrs(attrs)) - |> Keyword.merge([xml: stanza]) + |> Keyword.merge(xml: stanza) |> Enum.into(%{}) end @@ -54,17 +56,21 @@ defmodule Romeo.Stanza.Parser do def parse(stanza), do: stanza defp parse_attrs([]), do: [] + defp parse_attrs(attrs) do parse_attrs(attrs, []) end - defp parse_attrs([{k,v}|rest], acc) do - parse_attrs(rest, [parse_attr({k,v})|acc]) + + defp parse_attrs([{k, v} | rest], acc) do + parse_attrs(rest, [parse_attr({k, v}) | acc]) end + defp parse_attrs([], acc), do: acc defp parse_attr({key, value}) when key in ["to", "from", "jid"] do {String.to_atom(key), Romeo.JID.parse(value)} end + defp parse_attr({key, value}) do {String.to_atom(key), value} end @@ -78,9 +84,10 @@ defmodule Romeo.Stanza.Parser do defp get_group(stanza), do: subelement(stanza, "group") |> cdata defp delayed?(xmlel(children: children)) do - Enum.any? children, fn child -> - elem(child, 1) == "delay" || elem(child, 1) == "x" && - attr(child, "xmlns") == "jabber:x:delay" - end + Enum.any?(children, fn child -> + elem(child, 1) == "delay" || + (elem(child, 1) == "x" && + attr(child, "xmlns") == "jabber:x:delay") + end) end end diff --git a/lib/romeo/stanza/presence.ex b/lib/romeo/stanza/presence.ex index 2ac32c7..b6affe1 100644 --- a/lib/romeo/stanza/presence.ex +++ b/lib/romeo/stanza/presence.ex @@ -1,25 +1,23 @@ defmodule Romeo.Stanza.Presence do use Romeo.XML - @type jid :: Romeo.JID.t + @type jid :: Romeo.JID.t() @type t :: %__MODULE__{ - id: binary, - to: jid, - from: jid, - type: binary, - show: binary | nil, - status: binary | nil, - xml: tuple - } + id: binary, + to: jid, + from: jid, + type: binary, + show: binary | nil, + status: binary | nil, + xml: tuple + } - defstruct [ - id: nil, - to: nil, - from: nil, - type: nil, - show: nil, - status: nil, - xml: nil - ] + defstruct id: nil, + to: nil, + from: nil, + type: nil, + show: nil, + status: nil, + xml: nil end diff --git a/lib/romeo/transports/tcp.ex b/lib/romeo/transports/tcp.ex index ec56411..42d287f 100644 --- a/lib/romeo/transports/tcp.ex +++ b/lib/romeo/transports/tcp.ex @@ -4,10 +4,10 @@ defmodule Romeo.Transports.TCP do @default_port 5222 @ssl_opts [reuse_sessions: true] @socket_opts [packet: :raw, mode: :binary, active: :once] - @ns_jabber_client Romeo.XMLNS.ns_jabber_client - @ns_component_accept Romeo.XMLNS.ns_component_accept + @ns_jabber_client Romeo.XMLNS.ns_jabber_client() + @ns_component_accept Romeo.XMLNS.ns_component_accept() - @type state :: Romeo.Connection.t + @type state :: Romeo.Connection.t() use Romeo.XML @@ -18,20 +18,21 @@ defmodule Romeo.Transports.TCP do import Kernel, except: [send: 2] - @spec connect(Keyword.t) :: {:ok, state} | {:error, any} + @spec connect(Keyword.t()) :: {:ok, state} | {:error, any} def connect(%Conn{host: host, port: port, socket_opts: socket_opts, legacy_tls: tls} = conn) do host = (host || host(conn.jid)) |> to_charlist() - port = (port || @default_port) + port = port || @default_port conn = %{conn | host: host, port: port, socket_opts: socket_opts} case :gen_tcp.connect(host, port, socket_opts ++ @socket_opts, conn.timeout) do {:ok, socket} -> - Logger.info fn -> "Established connection to #{host}" end + Logger.info(fn -> "Established connection to #{host}" end) parser = :fxml_stream.new(self(), :infinity, [:no_gen_server]) conn = %{conn | parser: parser, socket: {:gen_tcp, socket}} conn = if tls, do: upgrade_to_tls(conn), else: conn start_protocol(conn) + {:error, _} = error -> error end @@ -39,11 +40,14 @@ defmodule Romeo.Transports.TCP do def disconnect(info, {mod, socket}) do :ok = mod.close(socket) + case info do {:close, from} -> Connection.reply(from, :ok) + {:error, :closed} -> :error_logger.format("Connection closed~n", []) + {:error, reason} -> reason = :inet.format_error(reason) :error_logger.format("Connection error: ~s~n", [reason]) @@ -85,27 +89,28 @@ defmodule Romeo.Transports.TCP do defp maybe_start_tls(%Conn{features: %Features{tls?: true}} = conn) do conn - |> send(Stanza.start_tls) + |> send(Stanza.start_tls()) |> recv(fn conn, xmlel(name: "proceed") -> conn end) |> upgrade_to_tls |> start_stream |> negotiate_features end + defp maybe_start_tls(%Conn{} = conn), do: conn defp upgrade_to_tls(%Conn{parser: parser, socket: {:gen_tcp, socket}} = conn) do - Logger.info fn -> "Negotiating secure connection" end + Logger.info(fn -> "Negotiating secure connection" end) {:ok, socket} = :ssl.connect(socket, conn.ssl_opts ++ @ssl_opts) parser = :fxml_stream.reset(parser) - Logger.info fn -> "Connection successfully secured" end + Logger.info(fn -> "Connection successfully secured" end) %{conn | socket: {:ssl, socket}, parser: parser} end defp authenticate(%Conn{} = conn) do conn - |> Romeo.Auth.authenticate! + |> Romeo.Auth.authenticate!() |> reset_parser |> start_stream |> negotiate_features @@ -129,17 +134,17 @@ defmodule Romeo.Transports.TCP do stanza |> Romeo.XML.subelement("bind") |> Romeo.XML.subelement("jid") - |> Romeo.XML.cdata - |> Romeo.JID.parse + |> Romeo.XML.cdata() + |> Romeo.JID.parse() - Logger.info fn -> "Bound to resource: #{resource}" end + Logger.info(fn -> "Bound to resource: #{resource}" end) Kernel.send(owner, {:resource_bound, resource}) %{conn | resource: resource} end) end defp session(%Conn{} = conn) do - stanza = Romeo.Stanza.session + stanza = Romeo.Stanza.session() id = Romeo.XML.attr(stanza, "id") conn @@ -148,7 +153,7 @@ defmodule Romeo.Transports.TCP do "result" = Romeo.XML.attr(stanza, "type") ^id = Romeo.XML.attr(stanza, "id") - Logger.info fn -> "Session established" end + Logger.info(fn -> "Session established" end) conn end) end @@ -164,7 +169,7 @@ defmodule Romeo.Transports.TCP do end defp parse_data(%Conn{jid: jid, parser: parser} = conn, data) do - Logger.debug fn -> "[#{jid}][INCOMING] #{inspect data}" end + Logger.debug(fn -> "[#{jid}][INCOMING] #{inspect(data)}" end) parser = :fxml_stream.parse(parser, data) @@ -180,52 +185,62 @@ defmodule Romeo.Transports.TCP do defp receive_stanza(timeout \\ 10) do receive do {:xmlstreamstart, _, _} = stanza -> stanza - {:xmlstreamend, _} = stanza -> stanza - {:xmlstreamraw, stanza} -> stanza - {:xmlstreamcdata, stanza} -> stanza - {:xmlstreamerror, _} = stanza -> stanza - {:xmlstreamelement, stanza} -> stanza - after timeout -> - :more + {:xmlstreamend, _} = stanza -> stanza + {:xmlstreamraw, stanza} -> stanza + {:xmlstreamcdata, stanza} -> stanza + {:xmlstreamerror, _} = stanza -> stanza + {:xmlstreamelement, stanza} -> stanza + after + timeout -> + :more end end def send(%Conn{jid: jid, socket: {mod, socket}} = conn, stanza) do stanza = Romeo.XML.encode!(stanza) - Logger.debug fn -> "[#{jid}][OUTGOING] #{inspect stanza}" end + Logger.debug(fn -> "[#{jid}][OUTGOING] #{inspect(stanza)}" end) :ok = mod.send(socket, stanza) {:ok, conn} end def recv({:ok, conn}, fun), do: recv(conn, fun) + def recv(%Conn{socket: {:gen_tcp, socket}, timeout: timeout} = conn, fun) do receive do {:xmlstreamelement, stanza} -> fun.(conn, stanza) + {:tcp, ^socket, data} -> :ok = activate({:gen_tcp, socket}) + if whitespace_only?(data) do conn else {:ok, conn, stanza} = parse_data(conn, data) fun.(conn, stanza) end + {:tcp_closed, ^socket} -> {:error, :closed} + {:tcp_error, ^socket, reason} -> {:error, reason} - after timeout -> - Kernel.send(self(), {:error, :timeout}) - conn + after + timeout -> + Kernel.send(self(), {:error, :timeout}) + conn end end + def recv(%Conn{socket: {:ssl, socket}, timeout: timeout} = conn, fun) do receive do {:xmlstreamelement, stanza} -> fun.(conn, stanza) + {:ssl, ^socket, " "} -> :ok = activate({:ssl, socket}) conn + {:ssl, ^socket, data} -> :ok = activate({:ssl, socket}) @@ -235,37 +250,47 @@ defmodule Romeo.Transports.TCP do {:ok, conn, stanza} = parse_data(conn, data) fun.(conn, stanza) end + {:ssl_closed, ^socket} -> {:error, :closed} + {:ssl_error, ^socket, reason} -> {:error, reason} - after timeout -> - Kernel.send(self(), {:error, :timeout}) - conn + after + timeout -> + Kernel.send(self(), {:error, :timeout}) + conn end end def handle_message({:tcp, socket, data}, %{socket: {:gen_tcp, socket}} = conn) do {:ok, _, _} = handle_data(data, conn) end + def handle_message({:xmlstreamelement, stanza}, conn) do {:ok, conn, stanza} end + def handle_message({:tcp_closed, socket}, %{socket: {:gen_tcp, socket}}) do {:error, :closed} end + def handle_message({:tcp_error, socket, reason}, %{socket: {:gen_tcp, socket}}) do {:error, reason} end + def handle_message({:ssl, socket, data}, %{socket: {:ssl, socket}} = conn) do {:ok, _, _} = handle_data(data, conn) end + def handle_message({:ssl_closed, socket}, %{socket: {:ssl, socket}}) do {:error, :closed} end + def handle_message({:ssl_error, socket, reason}, %{socket: {:ssl, socket}}) do {:error, reason} end + def handle_message(_, _), do: :unknown defp handle_data(data, %{socket: socket} = conn) do @@ -276,24 +301,29 @@ defmodule Romeo.Transports.TCP do defp whitespace_only?(data), do: Regex.match?(~r/^\s+$/, data) defp activate({:gen_tcp, socket}) do - case :inet.setopts(socket, [active: :once]) do + case :inet.setopts(socket, active: :once) do :ok -> :ok + {:error, :closed} -> _ = Kernel.send(self(), {:tcp_closed, socket}) :ok + {:error, reason} -> _ = Kernel.send(self(), {:tcp_error, socket, reason}) :ok end end + defp activate({:ssl, socket}) do - case :ssl.setopts(socket, [active: :once]) do + case :ssl.setopts(socket, active: :once) do :ok -> :ok + {:error, :closed} -> _ = Kernel.send(self(), {:ssl_closed, socket}) :ok + {:error, reason} -> _ = Kernel.send(self(), {:ssl_error, socket, reason}) :ok diff --git a/lib/romeo/xml.ex b/lib/romeo/xml.ex index 6eed61c..f2b1886 100644 --- a/lib/romeo/xml.ex +++ b/lib/romeo/xml.ex @@ -14,29 +14,28 @@ defmodule Romeo.XML do alias Romeo.Stanza.Message alias Romeo.Stanza.Presence - Record.defrecordp :xmlel, name: "", attrs: [], children: [] - Record.defrecordp :xmlcdata, content: [] - Record.defrecordp :xmlstreamstart, name: "", attrs: [] - Record.defrecordp :xmlstreamend, name: "" + Record.defrecordp(:xmlel, name: "", attrs: [], children: []) + Record.defrecordp(:xmlcdata, content: []) + Record.defrecordp(:xmlstreamstart, name: "", attrs: []) + Record.defrecordp(:xmlstreamend, name: "") end end - def encode!({:xmlel, _, _, _} = xml), do: - :fxml.element_to_binary(xml) - def encode!({:xmlstreamstart, name, attrs}), do: - encode!({:xmlel, name, attrs, []}) |> String.replace("/>", ">") - def encode!({:xmlstreamend, name}), do: - "" + def encode!({:xmlel, _, _, _} = xml), do: :fxml.element_to_binary(xml) - def encode!(stanza), do: - Romeo.Stanza.to_xml(stanza) + def encode!({:xmlstreamstart, name, attrs}), + do: encode!({:xmlel, name, attrs, []}) |> String.replace("/>", ">") + + def encode!({:xmlstreamend, name}), do: "" + + def encode!(stanza), do: Romeo.Stanza.to_xml(stanza) @doc """ Returns the given attribute value or default. """ def attr(element, name, default \\ nil) do case :fxml.get_tag_attr_s(name, element) do - "" -> default + "" -> default val -> val end end @@ -44,7 +43,7 @@ defmodule Romeo.XML do def subelement(element, name, default \\ nil) do case :fxml.get_subtag(element, name) do false -> default - val -> val + val -> val end end diff --git a/lib/romeo/xmlns.ex b/lib/romeo/xmlns.ex index 91bc255..20e93a9 100644 --- a/lib/romeo/xmlns.ex +++ b/lib/romeo/xmlns.ex @@ -8,7 +8,7 @@ defmodule Romeo.XMLNS do defmacro __using__([]) do quote do - import unquote __MODULE__ + import unquote(__MODULE__) end end @@ -416,6 +416,7 @@ defmodule Romeo.XMLNS do # Defined by XEP-0114: Jabber Component Protocol. def ns_component_accept, do: "jabber:component:accept" + def ns_component_connect, do: "jabber:component:connect" diff --git a/mix.exs b/mix.exs index c85ee27..7b8dd3b 100644 --- a/mix.exs +++ b/mix.exs @@ -4,22 +4,23 @@ defmodule Romeo.Mixfile do @version "0.7.0" def project do - [app: :romeo, - name: "Romeo", - version: @version, - elixir: "~> 1.1", - build_embedded: Mix.env == :prod, - start_permanent: Mix.env == :prod, - description: description(), - deps: deps(), - docs: docs(), - package: package(), - test_coverage: [tool: ExCoveralls]] + [ + app: :romeo, + name: "Romeo", + version: @version, + elixir: "~> 1.1", + build_embedded: Mix.env() == :prod, + start_permanent: Mix.env() == :prod, + description: description(), + deps: deps(), + docs: docs(), + package: package(), + test_coverage: [tool: ExCoveralls] + ] end def application do - [applications: [:logger, :connection, :fast_xml], - mod: {Romeo, []}] + [applications: [:logger, :connection, :fast_xml], mod: {Romeo, []}] end defp description do @@ -27,20 +28,20 @@ defmodule Romeo.Mixfile do end defp deps do - [{:connection, "~> 1.0"}, - {:fast_xml, "~> 1.1"}, + [ + {:connection, "~> 1.0"}, + {:fast_xml, "~> 1.1"}, - # Docs deps - {:ex_doc, "~> 0.18", only: :dev}, + # Docs deps + {:ex_doc, "~> 0.21", only: :dev}, - # Test deps - {:ejabberd, github: "scrogson/ejabberd", branch: "fix_mix_compile", only: :test}, - {:excoveralls, "~> 0.8", only: :test}] + # Test deps + {:excoveralls, "~> 0.11", only: :test} + ] end defp docs do - [extras: docs_extras(), - main: "readme"] + [extras: docs_extras(), main: "readme"] end defp docs_extras do @@ -48,9 +49,11 @@ defmodule Romeo.Mixfile do end defp package do - [files: ["lib", "mix.exs", "README.md", "LICENSE"], - maintainers: ["Sonny Scroggin"], - licenses: ["MIT"], - links: %{"GitHub" => "https://github.com/scrogson/romeo"}] + [ + files: ["lib", "mix.exs", "README.md", "LICENSE"], + maintainers: ["Sonny Scroggin"], + licenses: ["MIT"], + links: %{"GitHub" => "https://github.com/scrogson/romeo"} + ] end end diff --git a/mix.lock b/mix.lock index cc3518d..591786d 100644 --- a/mix.lock +++ b/mix.lock @@ -1,35 +1,20 @@ %{ - "cache_tab": {:hex, :cache_tab, "1.0.12", "a06a4ffbd4da8469791ba941512a6a45ed8c11865b4606a368e21b332da3638a", [], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"}, - "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"}, - "distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [], [], "hexpm"}, - "ejabberd": {:hex, :ejabberd, "18.1.0", "5bc81d975a0094c8ba9c809fdf6b8c03a90d4c62022c9c52a2adab235d5adb5d", [], [{:cache_tab, "~> 1.0", [hex: :cache_tab, repo: "hexpm", optional: false]}, {:distillery, "~> 1.0", [hex: :distillery, repo: "hexpm", optional: false]}, {:esip, "~> 1.0", [hex: :esip, repo: "hexpm", optional: false]}, {:ezlib, "~> 1.0", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.0", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:fast_yaml, "~> 1.0", [hex: :fast_yaml, repo: "hexpm", optional: false]}, {:fs, "~> 3.4", [hex: :fs, repo: "hexpm", optional: false]}, {:iconv, "~> 1.0", [hex: :iconv, repo: "hexpm", optional: false]}, {:jiffy, "~> 0.14.7", [hex: :jiffy, repo: "hexpm", optional: false]}, {:lager, "~> 3.4.0", [hex: :lager, repo: "hexpm", optional: false]}, {:p1_mysql, "~> 1.0", [hex: :p1_mysql, repo: "hexpm", optional: false]}, {:p1_oauth2, "~> 0.6.1", [hex: :p1_oauth2, repo: "hexpm", optional: false]}, {:p1_pgsql, "~> 1.1", [hex: :p1_pgsql, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0", [hex: :stringprep, repo: "hexpm", optional: false]}, {:stun, "~> 1.0", [hex: :stun, repo: "hexpm", optional: false]}, {:xmpp, "~> 1.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm"}, - "esip": {:hex, :esip, "1.0.21", "711c704337d434db6d7c70bd0da868aaacd91b252c0bb63b4580e6c896164f1f", [], [{: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", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.8.0", "99d2691d3edf8612f128be3f9869c4d44b91c67cec92186ce49470ae7a7404cf", [], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, - "ezlib": {:hex, :ezlib, "1.0.3", "c402c839ff5eab5b8a69efd9f60885c3ab3ca2489a6758bf8a67c914297597c5", [], [], "hexpm"}, - "fast_tls": {:hex, :fast_tls, "1.0.20", "edd241961ab20b71ec1e9f75a2a2c043128ff117adf3efd42e6cec94f1937539", [], [{: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", [], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "fs": {:hex, :fs, "3.4.0", "6d18575c250b415b3cad559e6f97a4c822516c7bc2c10bfbb2493a8f230f5132", [], [], "hexpm"}, - "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [], [], "hexpm"}, - "hackney": {:hex, :hackney, "1.11.0", "4951ee019df102492dabba66a09e305f61919a8a183a7860236c0fde586134b6", [], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, - "iconv": {:hex, :iconv, "1.0.6", "3b424a80039059767f1037dc6a49ff07c2f88df14068c16dc938c4f377a77b4c", [], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "jiffy": {:hex, :jiffy, "0.14.13", "225a9a35e26417832c611526567194b4d3adc4f0dfa5f2f7008f4684076f2a01", [], [], "hexpm"}, - "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [], [], "hexpm"}, - "lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"}, - "p1_mysql": {:hex, :p1_mysql, "1.0.4", "7b9d7957a9d031813a0e6bcea5a7f5e91b54db805a92709a445cf75cf934bc1d", [], [], "hexpm"}, - "p1_oauth2": {:hex, :p1_oauth2, "0.6.2", "cc381038920e3d34ef32aa10ba7eb637bdff38a946748c4fd99329ff484a3889", [], [], "hexpm"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.4", "eadbbddee8d52145694bf86bdfe8c1ae8353a55e152410146b8c2711756d6041", [], [], "hexpm"}, - "p1_utils": {:hex, :p1_utils, "1.0.10", "a6d6927114bac79cf6468a10824125492034af7071adc6ed5ebc4ddb443845d4", [], [], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"}, - "stringprep": {:hex, :stringprep, "1.0.10", "552d784eb60652220fce9131f8bb0ebc62fdffd6482c4f08f2e7d61300227c28", [], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "stun": {:hex, :stun, "1.0.20", "6b156fa11606bebb6086d02cb2f6532c84effb59c95ba93d0e2d8e2510970253", [], [{: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"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "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"}, + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.3.6", "ce1d0675e10a5bb46b007549362bd3f5f08908843957687d8484fe7f37466b19", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.11.2", "0c6f2c8db7683b0caa9d490fb8125709c54580b4255ffa7ad35f3264b075a643", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "fast_xml": {:hex, :fast_xml, "1.1.37", "e5276cd18d5ce5b179da34b6a1232805956f3057ee45833a0d62a9e414c86e07", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"}, + "p1_utils": {:hex, :p1_utils, "1.0.16", "05b5d4fb1f002d827b0d0d344eecdb4208b535bf95264d44f588affec644212b", [:rebar3], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, } diff --git a/priv/ssl/ejabberd.pem b/priv/ssl/ejabberd.pem deleted file mode 100644 index fc38fee..0000000 --- a/priv/ssl/ejabberd.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICsDCCAhmgAwIBAgIJALJmwzXUFrWmMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTI0MDYwODQwWhcNMTUxMjI0MDYwODQwWjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQDByRnfvkks1Pq7B8ndtZT8CGcqy4Zh/Uhde4DNtT4iGDzmAje5xCggMWGl8qZG -CFv4hLYuRGJwCQxwsDggabuNB7juoFpb2+OJZ6d3/DBHJfXSx0yR1zwVxA3rt/CV -JZijSRyJJn2ZC7h3Qe/TMo8eibhc9+p55cuDoq2tNoYDvwIDAQABo4GnMIGkMB0G -A1UdDgQWBBRUDEpv3D6naSZlsA4rc7tlXhaldjB1BgNVHSMEbjBsgBRUDEpv3D6n -aSZlsA4rc7tlXhaldqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt -U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALJmwzXU -FrWmMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAwABkkw6XZ1kNXA1a -stCIimJmeE+2lXa5POCh4YRg14Gvm4qCbfA8FPWO3Ld7cpJThYbiBxEwUovya8vl -u70yn0ux71xjN2M6HMI8ytuB1br3VfiZlqM5QgAC/UspEqQEVbj7Sss48M/vAYa5 -fEtglhk2xO7xgiKJR7+7DzzwwDY= ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDByRnfvkks1Pq7B8ndtZT8CGcqy4Zh/Uhde4DNtT4iGDzmAje5 -xCggMWGl8qZGCFv4hLYuRGJwCQxwsDggabuNB7juoFpb2+OJZ6d3/DBHJfXSx0yR -1zwVxA3rt/CVJZijSRyJJn2ZC7h3Qe/TMo8eibhc9+p55cuDoq2tNoYDvwIDAQAB -AoGAflzTKWocr0ZGJRWEFbWla99S3r4OZ/FQcdzp9bmcxYDGnTmO+uylObDZuuuK -bxpeVqS7Y1omUmYkHYtbXg90QvKPyqaKXiaEIVkAwSBKQvd7suJmYNYsJVvC3g9E -x/9n73GxnEX+Wk2w2bZH3VGO0pwCy14K1gnUomErGh1GLuECQQD9D4k00HQ3+1zQ -FSicmAQEFUsOiZIGKMWKDOh1swTcqOfskx1ij7Om9/YReDkkS2Ot49dRze5/4x4V -LG+0PymvAkEAxAlQMzd/Jnpmm72ABvhrL4sOjZ4DWo4sZgUlrcZ0bCLwUyrf5Ekj -4RT5ExSvQly+rnyCPcZ/cv+xLsy0qWwa8QJBAPMMWNtA2l5qLVos+DRuTG0fhlcQ -Cg+gWRmeDCX/KkxEbXvqT+651fIndU6SCU+ymKoKimMnRknN+LadVyvm/kECQFe/ -b3Wtdq2rhjhaB2+XTKsYTGhZfVjQYNE9ppL1TPGGZhpkC5msn3HFqIPA833586Q4 -uTebnTrFdvLi0E8xw5ECQQDIfmTr2TkqJOVgxexIIr09Ge8r7hHndxC3lnyscCjh -OTKGzCjzSOSaN3XVCJko9Z018eQYPUSVV7WC3sSZ4wyC ------END RSA PRIVATE KEY----- diff --git a/priv/templates/ejabberd.yml.eex b/priv/templates/ejabberd.yml.eex deleted file mode 100644 index 3e17daa..0000000 --- a/priv/templates/ejabberd.yml.eex +++ /dev/null @@ -1,123 +0,0 @@ -loglevel: 0 - -hosts: - - "localhost" - -listen: - - - port: 52222 - module: ejabberd_c2s - max_stanza_size: 65536 - shaper: c2s_shaper - access: c2s - - - port: 52225 - module: ejabberd_c2s - starttls_required: true - certfile: "<%= cwd %>/priv/ssl/ejabberd.pem" - max_stanza_size: 65536 - shaper: c2s_shaper - access: c2s - - - port: 52227 - module: ejabberd_service - hosts: - "test.localhost": - password: "secret" - -auth_method: internal - -shaper: - normal: 1000 - fast: 50000 - -max_fsm_queue: 1000 - -acl: - local: - user_regexp: "" - loopback: - ip: - - "127.0.0.0/8" - -access: - max_user_sessions: - all: 10 - max_user_offline_messages: - admin: 5000 - all: 100 - local: - local: allow - c2s: - blocked: deny - all: allow - c2s_shaper: - admin: none - all: normal - s2s_shaper: - all: fast - announce: - admin: allow - configure: - admin: allow - muc_admin: - admin: allow - muc_create: - local: allow - muc: - all: allow - pubsub_createnode: - local: allow - register: - all: allow - trusted_network: - loopback: allow - -language: "en" - -modules: - mod_adhoc: {} - mod_admin_extra: {} - mod_announce: - access: announce - mod_blocking: {} - mod_caps: {} - mod_carboncopy: {} - mod_client_state: - drop_chat_states: true - queue_presence: false - mod_configure: {} - mod_disco: {} - mod_irc: {} - mod_http_bind: {} - mod_last: {} - mod_muc: - access: muc - access_create: muc_create - access_persistent: muc_create - access_admin: muc_admin - max_user_conferences: 20 - mod_offline: - access_max_user_messages: max_user_offline_messages - mod_ping: {} - mod_privacy: {} - mod_private: {} - mod_pubsub: - access_createnode: pubsub_createnode - ignore_pep_from_offline: true - last_item_cache: false - plugins: - - "flat" - - "hometree" - - "pep" - mod_register: - ip_access: trusted_network - access: register - mod_roster: {} - mod_shared_roster: {} - mod_stats: {} - mod_time: {} - mod_vcard: {} - mod_version: {} - -allow_contrib_modules: true diff --git a/test/romeo/connection/features_test.exs b/test/romeo/connection/features_test.exs index e69de29..8b13789 100644 --- a/test/romeo/connection/features_test.exs +++ b/test/romeo/connection/features_test.exs @@ -0,0 +1 @@ + diff --git a/test/romeo/connection_test.exs b/test/romeo/connection_test.exs index be585c2..5b4a3a4 100644 --- a/test/romeo/connection_test.exs +++ b/test/romeo/connection_test.exs @@ -5,7 +5,7 @@ defmodule Romeo.ConnectionTest do use Romeo.XML setup do - romeo = build_user("romeo", tls: true) + romeo = build_user("romeo", tls: true) juliet = build_user("juliet", resource: "juliet", tls: true) setup_presence_subscriptions(romeo[:nickname], juliet[:nickname]) @@ -34,24 +34,30 @@ defmodule Romeo.ConnectionTest do assert_receive :connection_ready - assert :ok = Romeo.Connection.send(pid, Romeo.Stanza.presence) + assert :ok = Romeo.Connection.send(pid, Romeo.Stanza.presence()) assert_receive {:stanza, %Presence{from: from, to: to} = presence} assert to_string(from) == "romeo@localhost/romeo" assert to_string(to) == "romeo@localhost/romeo" - assert :ok = Romeo.Connection.send(pid, Romeo.Stanza.join("lobby@conference.localhost", "romeo")) - assert_receive {:stanza, %Presence{from: from} = presence} + assert :ok = + Romeo.Connection.send(pid, Romeo.Stanza.join("lobby@conference.localhost", "romeo")) + + assert_receive {:stanza, %Presence{from: from, to: to} = presence} + assert to_string(from) == "lobby@conference.localhost" + assert to_string(to) == "romeo@localhost/romeo" + assert_receive {:stanza, %Presence{from: from, to: to} = presence} assert to_string(from) == "lobby@conference.localhost/romeo" + assert to_string(to) == "romeo@localhost/romeo" end test "resource conflict", %{romeo: romeo} do {:ok, pid1} = Romeo.Connection.start_link(romeo) assert_receive :connection_ready - assert :ok = Romeo.Connection.send(pid1, Romeo.Stanza.presence) + assert :ok = Romeo.Connection.send(pid1, Romeo.Stanza.presence()) {:ok, pid2} = Romeo.Connection.start_link(romeo) assert_receive :connection_ready - assert :ok = Romeo.Connection.send(pid2, Romeo.Stanza.presence) + assert :ok = Romeo.Connection.send(pid2, Romeo.Stanza.presence()) assert_receive {:stanza, %{name: "stream:error"}} assert_receive {:stanza, xmlstreamend()} @@ -60,13 +66,13 @@ defmodule Romeo.ConnectionTest do test "exchanging messages with others", %{romeo: romeo, juliet: juliet} do {:ok, romeo} = Romeo.Connection.start_link(romeo) assert_receive :connection_ready - assert :ok = Romeo.Connection.send(romeo, Romeo.Stanza.presence) + assert :ok = Romeo.Connection.send(romeo, Romeo.Stanza.presence()) # Romeo receives presense from himself assert_receive {:stanza, %Presence{}} {:ok, juliet} = Romeo.Connection.start_link(juliet) assert_receive :connection_ready - assert :ok = Romeo.Connection.send(juliet, Romeo.Stanza.presence) + assert :ok = Romeo.Connection.send(juliet, Romeo.Stanza.presence()) # Juliet receives presence from herself and each receive each others' assert_receive {:stanza, %Presence{}} @@ -74,14 +80,24 @@ defmodule Romeo.ConnectionTest do assert_receive {:stanza, %Presence{}} # Juliet sends Romeo a message - assert :ok = Romeo.Connection.send(juliet, Romeo.Stanza.chat("romeo@localhost/romeo", "Where art thou?")) + assert :ok = + Romeo.Connection.send( + juliet, + Romeo.Stanza.chat("romeo@localhost/romeo", "Where art thou?") + ) + assert_receive {:stanza, %Message{from: from, to: to, body: body}} assert to_string(from) == "juliet@localhost/juliet" assert to_string(to) == "romeo@localhost/romeo" assert body == "Where art thou?" # Romeo responds - assert :ok = Romeo.Connection.send(romeo, Romeo.Stanza.chat("juliet@localhost/juliet", "Hey babe")) + assert :ok = + Romeo.Connection.send( + romeo, + Romeo.Stanza.chat("juliet@localhost/juliet", "Hey babe") + ) + assert_receive {:stanza, %Message{from: from, to: to, body: body}} assert to_string(from) == "romeo@localhost/romeo" assert to_string(to) == "juliet@localhost/juliet" diff --git a/test/romeo/jid_test.exs b/test/romeo/jid_test.exs index 7eff4db..7596e17 100644 --- a/test/romeo/jid_test.exs +++ b/test/romeo/jid_test.exs @@ -28,12 +28,30 @@ defmodule Romeo.JidTest do assert JID.parse(string) == %JID{user: "jdoe", server: "example.com", full: string} string = "jdoe@example.com/library" - assert JID.parse(string) == %JID{user: "jdoe", server: "example.com", resource: "library", full: string} + + assert JID.parse(string) == %JID{ + user: "jdoe", + server: "example.com", + resource: "library", + full: string + } string = "jdoe@example.com/jdoe@example.com/resource" - assert JID.parse(string) == %JID{user: "jdoe", server: "example.com", resource: "jdoe@example.com/resource", full: string} + + assert JID.parse(string) == %JID{ + user: "jdoe", + server: "example.com", + resource: "jdoe@example.com/resource", + full: string + } string = "example.com" - assert JID.parse(string) == %JID{user: "", server: "example.com", resource: "", full: "example.com"} + + assert JID.parse(string) == %JID{ + user: "", + server: "example.com", + resource: "", + full: "example.com" + } end end diff --git a/test/romeo/roster_test.exs b/test/romeo/roster_test.exs index 047a30b..6e6ebd4 100644 --- a/test/romeo/roster_test.exs +++ b/test/romeo/roster_test.exs @@ -4,12 +4,11 @@ defmodule Romeo.RosterTest do use UserHelper use Romeo.XML - import Romeo.Roster - + alias Romeo.Roster alias Romeo.Roster.Item setup do - romeo = build_user("romeo", tls: true) + romeo = build_user("romeo", tls: true) juliet = build_user("juliet", resource: "juliet", tls: true) mercutio = build_user("mercutio", resource: "mercutio", tls: true) benvolio = build_user("benvolio", resource: "benvolio", tls: true) @@ -21,15 +20,29 @@ defmodule Romeo.RosterTest do {:ok, romeo: romeo, juliet: juliet, mercutio: mercutio, benvolio: benvolio, pid: pid} end - test "getting, adding, removing roster items", %{benvolio: benvolio, mercutio: mercutio, pid: pid} do - assert [%Item{name: "juliet"}, %Item{name: "mercutio"}] = items(pid) - - b_jid = benvolio[:jid] - assert :ok = add(pid, b_jid) - assert [%Item{name: "juliet"}, %Item{name: "mercutio"}, %Item{name: "benvolio"}] = items(pid) + test "getting, adding, removing roster items", %{ + benvolio: benvolio, + mercutio: mercutio, + pid: pid + } do + items = Roster.items(pid) + assert item_by_name(items, "juliet") + assert item_by_name(items, "mercutio") + + assert :ok = Roster.add(pid, benvolio[:jid]) + items = Roster.items(pid) + assert item_by_name(items, "juliet") + assert item_by_name(items, "mercutio") + assert item_by_name(items, "benvolio") + + assert :ok = Roster.remove(pid, mercutio[:jid]) + items = Roster.items(pid) + assert item_by_name(items, "juliet") + assert item_by_name(items, "benvolio") + refute item_by_name(items, "mercutio") + end - m_jid = mercutio[:jid] - assert :ok = remove(pid, m_jid) - assert [%Item{name: "juliet"}, %Item{name: "benvolio"}] = items(pid) + defp item_by_name(items, name) do + Enum.find(items, fn item -> item.name == name end) end end diff --git a/test/romeo/stanza/parser_test.exs b/test/romeo/stanza/parser_test.exs index cbb071d..9f7701b 100644 --- a/test/romeo/stanza/parser_test.exs +++ b/test/romeo/stanza/parser_test.exs @@ -5,10 +5,20 @@ defmodule Romeo.Stanza.ParserTest do alias Romeo.Stanza.Parser - @iq {:xmlel, "iq", [{"from", "im.test.dev"}, {"to", "scrogson@im.test.dev/issues"}, {"id", "b0e3"}, {"type", "result"}], [ - {:xmlel, "query", [{"xmlns", "http://jabber.org/protocol/disco#items"}], [ - {:xmlel, "item", [{"jid", "conference.im.test.dev"}], []}, - {:xmlel, "item", [{"jid", "pubsub.im.test.dev"}], []}]}]} + @iq {:xmlel, "iq", + [ + {"from", "im.test.dev"}, + {"to", "scrogson@im.test.dev/issues"}, + {"id", "b0e3"}, + {"type", "result"} + ], + [ + {:xmlel, "query", [{"xmlns", "http://jabber.org/protocol/disco#items"}], + [ + {:xmlel, "item", [{"jid", "conference.im.test.dev"}], []}, + {:xmlel, "item", [{"jid", "pubsub.im.test.dev"}], []} + ]} + ]} test "it parses stanzas" do parsed = Parser.parse(@iq) diff --git a/test/romeo/stanza_test.exs b/test/romeo/stanza_test.exs index 36c3d3b..d2d03b0 100644 --- a/test/romeo/stanza_test.exs +++ b/test/romeo/stanza_test.exs @@ -7,138 +7,148 @@ defmodule Romeo.StanzaTest do doctest Romeo.Stanza test "to_xml for IQ struct" do - assert %IQ{to: "test@localhost", type: "get", id: "123"} |> Stanza.to_xml == - "" + assert %IQ{to: "test@localhost", type: "get", id: "123"} |> Stanza.to_xml() == + "" end test "to_xml for Presence struct" do - assert %Presence{to: "test@localhost", type: "subscribe"} |> Stanza.to_xml == - "" + assert %Presence{to: "test@localhost", type: "subscribe"} |> Stanza.to_xml() == + "" end test "start_stream with default xmlns" do - assert Stanza.start_stream("im.wonderland.lit") |> Stanza.to_xml == - "" + assert Stanza.start_stream("im.wonderland.lit") |> Stanza.to_xml() == + "" end test "start_stream with 'jabber:server' xmlns" do - assert Stanza.start_stream("im.wonderland.lit", ns_jabber_server) |> Stanza.to_xml == - "" + assert Stanza.start_stream("im.wonderland.lit", ns_jabber_server()) |> Stanza.to_xml() == + "" end test "end_stream" do - assert Stanza.end_stream |> Stanza.to_xml == "" + assert Stanza.end_stream() |> Stanza.to_xml() == "" end test "start_tls" do - assert Stanza.start_tls |> Stanza.to_xml == - "" + assert Stanza.start_tls() |> Stanza.to_xml() == + "" end test "get_inband_register" do - assert Stanza.get_inband_register |> Stanza.to_xml =~ - ~r"" + assert Stanza.get_inband_register() |> Stanza.to_xml() =~ + ~r"" end test "set_inband_register" do - assert Stanza.set_inband_register("username", "password") |> Stanza.to_xml =~ - ~r"usernamepassword" + assert Stanza.set_inband_register("username", "password") |> Stanza.to_xml() =~ + ~r"usernamepassword" end test "subscribe" do - assert Stanza.subscribe("pubsub.wonderland.lit", "posts", "alice@wonderland.lit") |> Stanza.to_xml =~ - ~r"" + assert Stanza.subscribe("pubsub.wonderland.lit", "posts", "alice@wonderland.lit") + |> Stanza.to_xml() =~ + ~r"" end test "compress" do - assert Stanza.compress("zlib") |> Stanza.to_xml == - "zlib" + assert Stanza.compress("zlib") |> Stanza.to_xml() == + "zlib" end test "auth" do data = <<0>> <> "username" <> <<0>> <> "password" - assert Stanza.auth("PLAIN", Stanza.base64_cdata(data)) |> Stanza.to_xml == - "AHVzZXJuYW1lAHBhc3N3b3Jk" + + assert Stanza.auth("PLAIN", Stanza.base64_cdata(data)) |> Stanza.to_xml() == + "AHVzZXJuYW1lAHBhc3N3b3Jk" end test "auth anonymous" do - assert Stanza.auth("ANONYMOUS") |> Stanza.to_xml == "" + assert Stanza.auth("ANONYMOUS") |> Stanza.to_xml() == + "" end test "bind" do - assert Stanza.bind("hedwig") |> Stanza.to_xml =~ - ~r"hedwig" + assert Stanza.bind("hedwig") |> Stanza.to_xml() =~ + ~r"hedwig" end test "session" do - assert Stanza.session |> Stanza.to_xml =~ - ~r"" + assert Stanza.session() |> Stanza.to_xml() =~ + ~r"" end test "presence" do - assert Stanza.presence |> Stanza.to_xml == "" + assert Stanza.presence() |> Stanza.to_xml() == "" end test "presence/1" do - assert Stanza.presence("subscribe") |> Stanza.to_xml == "" + assert Stanza.presence("subscribe") |> Stanza.to_xml() == "" end test "presence/2" do - assert Stanza.presence("room@muc.localhost/nick", "unavailable") |> Stanza.to_xml == - "" + assert Stanza.presence("room@muc.localhost/nick", "unavailable") |> Stanza.to_xml() == + "" end test "message" do - assert Stanza.message("test@localhost", "chat", "Hello") |> Stanza.to_xml =~ - ~r"Hello" + assert Stanza.message("test@localhost", "chat", "Hello") |> Stanza.to_xml() =~ + ~r"Hello" end test "message map" do msg = %{"to" => "test@localhost", "type" => "chat", "body" => "Hello"} - assert Stanza.message(msg) |> Stanza.to_xml =~ - ~r"Hello" + + assert Stanza.message(msg) |> Stanza.to_xml() =~ + ~r"Hello" end test "normal chat" do - assert Stanza.normal("test@localhost", "Hello") |> Stanza.to_xml =~ - ~r"Hello" + assert Stanza.normal("test@localhost", "Hello") |> Stanza.to_xml() =~ + ~r"Hello" end test "group chat" do - assert Stanza.groupchat("test@localhost", "Hello") |> Stanza.to_xml =~ - ~r"Hello" + assert Stanza.groupchat("test@localhost", "Hello") |> Stanza.to_xml() =~ + ~r"Hello" end test "get_roster" do - assert Stanza.get_roster |> Stanza.to_xml =~ - ~r"" + assert Stanza.get_roster() |> Stanza.to_xml() =~ + ~r"" end test "set_roster_item" do - assert Stanza.set_roster_item("test@localhost", "none", "test2", "buddies") |> Stanza.to_xml =~ - ~r"buddies" - assert Stanza.set_roster_item("test@localhost") |> Stanza.to_xml =~ - ~r"" + assert Stanza.set_roster_item("test@localhost", "none", "test2", "buddies") |> Stanza.to_xml() =~ + ~r"buddies" + + assert Stanza.set_roster_item("test@localhost") |> Stanza.to_xml() =~ + ~r"" end test "get_vcard" do - assert Stanza.get_vcard("test@localhost") |> Stanza.to_xml =~ - ~r"" + assert Stanza.get_vcard("test@localhost") |> Stanza.to_xml() =~ + ~r"" end test "disco_info" do - assert Stanza.disco_info("test@localhost") |> Stanza.to_xml =~ - ~r"" + assert Stanza.disco_info("test@localhost") |> Stanza.to_xml() =~ + ~r"" end test "disco_items" do - assert Stanza.disco_items("test@localhost") |> Stanza.to_xml =~ - ~r"" + assert Stanza.disco_items("test@localhost") |> Stanza.to_xml() =~ + ~r"" end test "xhtml im" do xhtml_msg = "

Hello

" - assert Stanza.xhtml_im(xhtml_msg) |> Stanza.to_xml == - "#{xhtml_msg}" + + assert Stanza.xhtml_im(xhtml_msg) |> Stanza.to_xml() == + "#{ + xhtml_msg + }" end end diff --git a/test/romeo/xml_test.exs b/test/romeo/xml_test.exs index d42b491..b945053 100644 --- a/test/romeo/xml_test.exs +++ b/test/romeo/xml_test.exs @@ -5,13 +5,21 @@ defmodule Romeo.XMLTest do import Romeo.XML test "encode!" do - xml = xmlel(name: "message", children: [ - xmlel(name: "body", children: [ - xmlcdata(content: "testing") - ]) - ]) + xml = + xmlel( + name: "message", + children: [ + xmlel( + name: "body", + children: [ + xmlcdata(content: "testing") + ] + ) + ] + ) + assert encode!(xml) == - ~s(testing) + ~s(testing) end test "attr" do @@ -22,29 +30,47 @@ defmodule Romeo.XMLTest do end test "subelement" do - xml = xmlel(name: "message", children: [ - xmlel(name: "body", children: [ - xmlcdata(content: "testing") - ]) - ]) + xml = + xmlel( + name: "message", + children: [ + xmlel( + name: "body", + children: [ + xmlcdata(content: "testing") + ] + ) + ] + ) + assert subelement(xml, "body") == - {:xmlel, "body", [], [xmlcdata(content: "testing")]} + {:xmlel, "body", [], [xmlcdata(content: "testing")]} assert subelement(xml, "non-existent") == nil assert subelement(xml, "non-existent", []) == [] end test "cdata" do - body = xmlel(name: "body", children: [ - xmlcdata(content: "testing") - ]) + body = + xmlel( + name: "body", + children: [ + xmlcdata(content: "testing") + ] + ) + assert cdata(body) == "testing" end test "empty cdata" do - body = xmlel(name: "body", children: [ - xmlcdata(content: "testing") - ]) + body = + xmlel( + name: "body", + children: [ + xmlcdata(content: "testing") + ] + ) + assert cdata(body) == "testing" end end diff --git a/test/romeo/xmlns_test.exs b/test/romeo/xmlns_test.exs index ac9154a..4505781 100644 --- a/test/romeo/xmlns_test.exs +++ b/test/romeo/xmlns_test.exs @@ -4,7 +4,7 @@ defmodule Romeo.XMLNSTest do import Romeo.XMLNS test "it provides XML namespaces" do - assert ns_xml == "http://www.w3.org/XML/1998/namespace" - assert ns_xmpp == "http://etherx.jabber.org/streams" + assert ns_xml() == "http://www.w3.org/XML/1998/namespace" + assert ns_xmpp() == "http://etherx.jabber.org/streams" end end diff --git a/test/romeo_test.exs b/test/romeo_test.exs deleted file mode 100644 index d41604a..0000000 --- a/test/romeo_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule RomeoTest do - use ExUnit.Case - doctest Romeo - - test "the truth" do - assert 1 + 1 == 2 - end -end diff --git a/test/test_helper.exs b/test/test_helper.exs index 02466e7..f841a80 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,6 +1,4 @@ +Logger.configure(level: :info) Code.require_file("user_helper.exs", __DIR__) - -Application.ensure_all_started(:ejabberd) +ExUnit.configure(assert_receive_timeout: 2000) ExUnit.start() - -System.at_exit(fn _ -> File.rm_rf("mnesia") end) diff --git a/test/user_helper.exs b/test/user_helper.exs index aac20bc..5d2a291 100644 --- a/test/user_helper.exs +++ b/test/user_helper.exs @@ -1,5 +1,4 @@ defmodule UserHelper do - defmacro __using__(_) do quote do import UserHelper @@ -14,23 +13,65 @@ defmodule UserHelper do register_user(username, password) - [jid: username <> "@localhost", - password: password, - resource: resource, - nickname: username, - port: (if tls, do: 52225, else: 52222)] + [ + jid: username <> "@localhost", + password: password, + resource: resource, + nickname: username, + port: if(tls, do: 5222, else: 5222) + ] end def register_user(username, password \\ "password") do - :ejabberd_admin.register(username, "localhost", password) + System.cmd("docker", [ + "exec", + "ejabberd", + "ejabberdctl", + "register", + username, + "localhost", + password + ]) end def unregister_user(username) do - :ejabberd_admin.unregister(username, "localhost") + System.cmd("docker", [ + "exec", + "ejabberd", + "ejabberdctl", + "unregister", + username, + "localhost" + ]) end def setup_presence_subscriptions(user1, user2) do - :mod_admin_extra.add_rosteritem(user1, "localhost", user2, "localhost", user2, "buddies", "both") - :mod_admin_extra.add_rosteritem(user2, "localhost", user1, "localhost", user1, "buddies", "both") + System.cmd("docker", [ + "exec", + "ejabberd", + "ejabberdctl", + "add_rosteritem", + user1, + "localhost", + user2, + "localhost", + user2, + "buddies", + "both" + ]) + + System.cmd("docker", [ + "exec", + "ejabberd", + "ejabberdctl", + "add_rosteritem", + user2, + "localhost", + user1, + "localhost", + user1, + "buddies", + "both" + ]) end end