Current section
Files
Jump to
Current section
Files
lib/polyjuice/util.ex
# Copyright 2019-2021 Hubert Chathi <hubert@uhoreg.ca>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
defmodule Polyjuice.Util do
@typedoc ~S"""
Represents a full event, including the event content, sender, event type, etc.
Will have (at least) the following fields: `"type"`, `"content"`, `"sender"`,
`"event_id"` (if a room event), and `"room_id"` (if a room event).
"""
@type event() :: %{String.t() => any}
@typedoc ~S"""
Represents the `content` field of an event.
Its expected fields depend on the type of event. For example, a
`m.room.message` event should have `"msgtype"` and `"body"` fields (though
implementations should be prepared to deal with malformed events).
"""
@type event_content() :: %{String.t() => any}
@doc ~S"""
Escape a user ID localpart so that it only uses the allowable character set.
The method used is defined in
https://matrix.org/docs/spec/appendices#mapping-from-other-character-sets
By default, it will escape upper-case letters. If you want upper-case
letters to simply be lower-cased, use the `fold_case: true` option.
Examples:
iex> Polyjuice.Util.escape_localpart("abc\x01 !\",-./09:;<=>?@AZ[\\]^_`az{|}~á")
"abc=01=20=21=22=2c-./09=3a=3b=3c=3d=3e=3f=40_a_z=5b=5c=5d=5e__=60az=7b=7c=7d=7e=c3=a1"
iex> Polyjuice.Util.escape_localpart("abc\x01 !\",-./09:;<=>?@AZ[\\]^_`az{|}~á", fold_case: true)
"abc=01=20=21=22=2c-./09=3a=3b=3c=3d=3e=3f=40az=5b=5c=5d=5e_=60az=7b=7c=7d=7e=c3=a1"
"""
def escape_localpart(localpart, opts \\ []) when is_binary(localpart) and is_list(opts) do
fold_case = Keyword.get(opts, :fold_case, false)
do_escape(localpart, fold_case) |> IO.iodata_to_binary()
end
defp do_escape("", _), do: ""
defp do_escape(<<c>> <> rest, fold_case)
when c < ?- or (c > ?9 and c < ?A) or (c > ?Z and c != ?_ and c < ?a) or c > ?z do
if c < 0xF do
["=0", Integer.to_string(c, 16) |> String.downcase() | do_escape(rest, fold_case)]
else
[?=, Integer.to_string(c, 16) |> String.downcase() | do_escape(rest, fold_case)]
end
end
defp do_escape(<<c>> <> rest, fold_case) when c >= ?A and c <= ?Z do
if fold_case do
[c - ?A + ?a | do_escape(rest, fold_case)]
else
[?_, c - ?A + ?a | do_escape(rest, fold_case)]
end
end
defp do_escape(<<?_>> <> rest, false) do
["__" | do_escape(rest, false)]
end
defp do_escape(string, fold_case) do
size = chunk_size(string, fold_case, 0)
<<chunk::binary-size(size), rest::binary>> = string
[chunk | do_escape(rest, fold_case)]
end
defp chunk_size("", _, acc), do: acc
defp chunk_size(<<c>> <> _, _, acc)
when c < ?- or (c > ?9 and c != ?= and c < ?A) or (c > ?Z and c != ?_ and c < ?a) or c > ?z do
acc
end
defp chunk_size(<<c>> <> _, false, acc) when (c >= ?A and c <= ?Z) or c == ?_ do
acc
end
defp chunk_size(<<_char>> <> rest, fold_case, acc) do
chunk_size(rest, fold_case, acc + 1)
end
@doc ~S"""
Make a verify key of the type given by the key ID.
Examples:
iex> Polyjuice.Util.make_verify_key("a4qcdbuJoRwy1ykc7Xhj1wTQroEXHdr04AY2Ds6SPb0", "ed25519:1")
%Polyjuice.Util.Ed25519.VerifyKey{
key: <<107, 138, 156, 117, 187, 137, 161, 28, 50, 215, 41, 28, 237, 120, 99, 215,
4, 208, 174, 129, 23, 29, 218, 244, 224, 6, 54, 14, 206, 146, 61, 189>>,
id: "1"
}
"""
@spec make_verify_key(key :: String.t(), id :: String.t()) :: Polyjuice.Util.VerifyKey.t() | nil
def make_verify_key(key, <<"ed25519:", id::binary>>) do
Polyjuice.Util.Ed25519.VerifyKey.from_base64(key, id)
end
def make_verify_key(_, _) do
nil
end
end