Packages

Utility functions for the Matrix.org communications protocol

Current section

Files

Jump to
polyjuice_util lib polyjuice util.ex
Raw

lib/polyjuice/util.ex

# Copyright 2019 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
@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
end