1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
# Copyright 2020 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.Client.Endpoint.GetMediaDownload do
@moduledoc """
Download from the media repository.
https://matrix.org/docs/spec/client_server/latest#get-matrix-media-r0-download-servername-mediaid
https://matrix.org/docs/spec/client_server/latest#get-matrix-media-r0-download-servername-mediaid-filename
"""
@type t :: %__MODULE__{
url: String.t() | URI.t() | {String.t(), String.t()},
allow_remote: boolean,
filename: String.t() | nil
}
@enforce_keys [:url]
defstruct [
:url,
:filename,
allow_remote: true
]
defimpl Polyjuice.Client.Endpoint.Proto do
def http_spec(%{
url: mxc_url,
allow_remote: allow_remote,
filename: filename
}) do
e = &URI.encode_www_form/1
{server_name, media_id} =
case mxc_url do
_ when is_binary(mxc_url) ->
%{
scheme: "mxc",
host: host,
path: path
} = URI.parse(mxc_url)
{host, String.trim_leading(path, "/")}
%URI{
scheme: "mxc",
host: host,
path: path
} ->
{host, String.trim_leading(path, "/")}
{_, _} ->
mxc_url
end
filename_part = if is_binary(filename), do: "/" <> e.(filename), else: ""
Polyjuice.Client.Endpoint.HttpSpec.get(
:media_r0,
"download/#{e.(server_name)}/#{e.(media_id)}#{filename_part}",
query: if(!allow_remote, do: [allow_remote: "false"]),
headers: [
{"Accept", "*/*"}
],
stream_response: true
)
end
def transform_http_result(req, status_code, resp_headers, body) do
if status_code == 200 do
content_type =
Polyjuice.Client.Endpoint.get_header(
resp_headers,
"content-type",
"application/octet-stream"
)
filename =
Enum.find_value(
resp_headers,
fn {name, value} ->
if String.downcase(name, :ascii) == "content-disposition" do
Regex.split(~r";\s*", value)
|> Enum.find_value(fn directive ->
# FIXME: also handle "filename*"
with [d_name, quoted_name] <- String.split(directive, "=", parts: 2),
"filename" <- String.downcase(d_name) do
case Regex.run(~r/"(.*)"/, quoted_name) do
[_, name] -> Regex.replace(~r/\\(.)/, name, "\\1")
nil -> quoted_name
end
# do some basic sanitizing
|> (&Regex.replace(~r/\0/, &1, "")).()
|> Path.basename()
|> (&if(&1 == "", do: nil, else: &1)).()
else
_ ->
nil
end
end)
end
end
)
{:ok, filename, content_type, body}
else
Polyjuice.Client.Endpoint.parse_response(req, status_code, resp_headers, Enum.join(body))
end
end
end
end
|