diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index 64eb6d2bc..e0fc6daa6 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -18,8 +18,7 @@ def search(query_string, opts \\ []) do
for_user = Keyword.get(opts, :for_user)
- # Strip the beginning @ off if there is a query
- query_string = String.trim_leading(query_string, "@")
+ query_string = format_query(query_string)
maybe_resolve(resolve, for_user, query_string)
@@ -40,6 +39,18 @@ def search(query_string, opts \\ []) do
results
end
+ defp format_query(query_string) do
+ # Strip the beginning @ off if there is a query
+ query_string = String.trim_leading(query_string, "@")
+
+ with [name, domain] <- String.split(query_string, "@"),
+ formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do
+ name <> "@" <> to_string(:idna.encode(formatted_domain))
+ else
+ _ -> query_string
+ end
+ end
+
defp search_query(query_string, for_user, following) do
for_user
|> base_query(following)
@@ -151,7 +162,7 @@ defp boost_search_rank_query(query, for_user) do
defp fts_search_subquery(query, term) do
processed_query =
String.trim_trailing(term, "@" <> local_domain())
- |> String.replace(~r/\W+/, " ")
+ |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
|> String.trim()
|> String.split()
|> Enum.map(&(&1 <> ":*"))
diff --git a/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml b/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml
new file mode 100644
index 000000000..df64d44b0
--- /dev/null
+++ b/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/test/fixtures/lain.xml b/test/fixtures/lain.xml
new file mode 100644
index 000000000..332b3b28d
--- /dev/null
+++ b/test/fixtures/lain.xml
@@ -0,0 +1,12 @@
+
+
+ acct:lain@zetsubou.xn--q9jyb4c
+ https://zetsubou.xn--q9jyb4c/users/lain
+
+
+
+
+
+
+
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index c593a5e4a..ff6bb78f9 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -840,6 +840,45 @@ def get("http://404.site" <> _, _, _, _) do
}}
end
+ def get(
+ "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=lain@zetsubou.xn--q9jyb4c",
+ _,
+ _,
+ Accept: "application/xrd+xml,application/jrd+json"
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/lain.xml")
+ }}
+ end
+
+ def get(
+ "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=https://zetsubou.xn--q9jyb4c/users/lain",
+ _,
+ _,
+ Accept: "application/xrd+xml,application/jrd+json"
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/lain.xml")
+ }}
+ end
+
+ def get(
+ "https://zetsubou.xn--q9jyb4c/.well-known/host-meta",
+ _,
+ _,
+ _
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml")
+ }}
+ end
+
def get(url, query, body, headers) do
{:error,
"Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
diff --git a/test/user_search_test.exs b/test/user_search_test.exs
index 1f0162486..4de6c82a5 100644
--- a/test/user_search_test.exs
+++ b/test/user_search_test.exs
@@ -248,5 +248,57 @@ test "local user search with users" do
[result] = User.search("lain@localhost", resolve: true, for_user: user)
assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user
end
+
+ test "works with idna domains" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results = User.search("lain@zetsubou.みんな", resolve: false, for_user: user)
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "works with idna domains converted input" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results =
+ User.search("lain@zetsubou." <> to_string(:idna.encode("zetsubou.みんな")),
+ resolve: false,
+ for_user: user
+ )
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "works with idna domains and bad chars in domain" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results =
+ User.search("lain@zetsubou!@#$%^&*()+,-/:;<=>?[]'_{}|~`.みんな",
+ resolve: false,
+ for_user: user
+ )
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "works with idna domains and query as link" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results =
+ User.search("https://zetsubou.みんな/users/lain",
+ resolve: false,
+ for_user: user
+ )
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
end
end
diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs
index 335c95b18..0578b4b8e 100644
--- a/test/web/web_finger/web_finger_test.exs
+++ b/test/web/web_finger/web_finger_test.exs
@@ -104,5 +104,16 @@ test "it gets the xrd endpoint for statusnet" do
assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
end
+
+ test "it works with idna domains as nickname" do
+ nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな"))
+
+ {:ok, _data} = WebFinger.finger(nickname)
+ end
+
+ test "it works with idna domains as link" do
+ ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
+ {:ok, _data} = WebFinger.finger(ap_id)
+ end
end
end