From dfe933cbb4071ae517e971c5630e29690bd3a00f Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sat, 15 Nov 2025 12:41:29 -0500 Subject: [PATCH] fixed handling of non-standard matrix:// ports (#1450) --- apprise/plugins/matrix.py | 33 +++++---------- tests/test_plugin_matrix.py | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 22 deletions(-) diff --git a/apprise/plugins/matrix.py b/apprise/plugins/matrix.py index b6d27b60..6e57fbfc 100644 --- a/apprise/plugins/matrix.py +++ b/apprise/plugins/matrix.py @@ -462,17 +462,11 @@ class NotifyMatrix(NotifyBase): # Acquire our access token from our URL access_token = self.password if self.password else self.user - default_port = 443 if self.secure else 80 - # Prepare our URL - url = "{schema}://{hostname}:{port}{webhook_path}/{token}".format( + url = "{schema}://{hostname}{port}{webhook_path}/{token}".format( schema="https" if self.secure else "http", hostname=self.host, - port=( - "" - if self.port is None or self.port == default_port - else self.port - ), + port=("" if not self.port else f":{self.port}"), webhook_path=MATRIX_V1_WEBHOOK_PATH, token=access_token, ) @@ -1568,7 +1562,6 @@ class NotifyMatrix(NotifyBase): ), ) - default_port = 443 if self.secure else 80 return "{schema}://{auth}{hostname}{port}/{rooms}?{params}".format( schema=self.secure_protocol if self.secure else self.protocol, auth=auth, @@ -1577,11 +1570,7 @@ class NotifyMatrix(NotifyBase): if self.mode != MatrixWebhookMode.T2BOT else self.pprint(self.access_token, privacy, safe="") ), - port=( - "" - if self.port is None or self.port == default_port - else f":{self.port}" - ), + port=("" if not self.port else f":{self.port}"), rooms=NotifyMatrix.quote("/".join(self.rooms)), params=NotifyMatrix.urlencode(params), ) @@ -1724,9 +1713,14 @@ class NotifyMatrix(NotifyBase): # We can use our cached value and return early return base_url - # 1. Extract the server name from the user's Matrix ID by splitting # the Matrix ID at the first colon. - verify_url = f"https://{self.host}/.well-known/matrix/client" + verify_url = \ + "{schema}://{hostname}{port}/.well-known/matrix/client".format( + schema="https" if self.secure else "http", + hostname=self.host, + port=("" if not self.port else f":{self.port}"), + ) + code, wk_response = self._fetch( None, method="GET", url_override=verify_url ) @@ -1910,15 +1904,10 @@ class NotifyMatrix(NotifyBase): # If we get hear, we need to build our URL dynamically based on what # was provided to us during the plugins initialization - default_port = 443 if self.secure else 80 return "{schema}://{hostname}{port}".format( schema="https" if self.secure else "http", hostname=self.host, - port=( - "" - if self.port is None or self.port == default_port - else f":{self.port}" - ), + port=("" if not self.port else f":{self.port}"), ) @property diff --git a/tests/test_plugin_matrix.py b/tests/test_plugin_matrix.py index e8630a94..ca05a311 100644 --- a/tests/test_plugin_matrix.py +++ b/tests/test_plugin_matrix.py @@ -1759,3 +1759,85 @@ def test_plugin_matrix_transaction_ids_api_v3_w_cache( assert mock_get.call_count == 0 assert mock_post.call_count == 0 assert mock_put.call_count == 0 + +@mock.patch("requests.put") +@mock.patch("requests.get") +@mock.patch("requests.post") +def test_plugin_matrix_v3_url_with_port_assembly( + mock_post, mock_get, mock_put, tmpdir +): + """NotifyMatrix() URL with Port Assembly Checks (v3)""" + + # Prepare a good response + response = mock.Mock() + response.status_code = requests.codes.ok + response.content = MATRIX_GOOD_RESPONSE.encode("utf-8") + + # Prepare Mock return object + mock_post.return_value = response + mock_get.return_value = response + mock_put.return_value = response + + asset = AppriseAsset( + storage_mode=PersistentStoreMode.FLUSH, + storage_path=str(tmpdir), + ) + + # Instantiate our object + obj = Apprise.instantiate( + "matrixs://user1:pass123@example.ca:8080/#general?v=3", asset=asset + ) + # Performs a login + assert ( + obj.notify(body="body", title="title", notify_type=NotifyType.INFO) + is True + ) + + # Secure Connections have a bit of additional overhead to verify + # the authenticity of the server through discovery + assert mock_get.call_count == 3 + assert ( + mock_get.call_args_list[0][0][0] + == "https://example.ca:8080/.well-known/matrix/client" + ) + assert ( + mock_get.call_args_list[1][0][0] + == "https://matrix.example.com/_matrix/client/versions" + ) + assert ( + mock_get.call_args_list[2][0][0] + == "https://vector.im/_matrix/identity/v2" + ) + + assert mock_post.call_count == 2 + # matrix.example.com comes from our MATRIX_GOOD_RESPONSE + # response which defines wht our .well-known returned to us + assert ( + mock_post.call_args_list[0][0][0] + == "https://matrix.example.com/_matrix/client/v3/login" + ) + assert ( + mock_post.call_args_list[1][0][0] + == "https://matrix.example.com/_matrix/client/v3/" + "join/%23general%3Alocalhost" + ) + assert mock_put.call_count == 1 + assert ( + mock_put.call_args_list[0][0][0] + == "https://matrix.example.com/_matrix/client/v3/rooms/" + + "%21abc123%3Alocalhost/send/m.room.message/0" + ) + + mock_post.reset_mock() + mock_get.reset_mock() + mock_put.reset_mock() + + assert obj.base_url == "https://matrix.example.com" + + # Cache is used under the hood; no second discover is performed + assert mock_put.call_count == 0 + assert mock_get.call_count == 0 + assert mock_post.call_count == 0 + + # Cleanup + del obj