[GH-ISSUE #171] htmlmail option for Proxmox Mail Gateway #94

Open
opened 2026-02-27 15:46:22 +03:00 by kerem · 9 comments
Owner

Originally created by @MrDiba on GitHub (Aug 26, 2024).
Original GitHub issue: https://github.com/proxmoxer/proxmoxer/issues/171

Originally assigned to: @jhollowe on GitHub.

Python version: 3.12.3
Proxmoxer version: 2.1.0 using https backend
PMG version: 7.3-11

I'm creating an custom application around the Proxmox Mail Gateway API and I ran into an issue where I can not receive the htmlmail version from an quarantine content.

For example: proxmox.quarantine.content.get(id=mail_id) gets the data from /api2/json/quarantine/content

In the API documentation there is the following description: Get email data. There is a special formatter called 'htmlmail' to get sanitized html view of the mail content (use the '/api2/htmlmail/quarantine/content' url).

But when I try to do proxmox.htmlmail.quarantine.content.get(id=mail_id) it errors with:

  File "/home/user/git/spamfilter/venv/lib/python3.12/site-packages/proxmoxer/core.py", line 167, in get
    return self(args)._request("GET", params=params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/git/spamfilter/venv/lib/python3.12/site-packages/proxmoxer/core.py", line 147, in _request
    raise ResourceException(
proxmoxer.core.ResourceException: 501 Not Implemented: Method 'GET /htmlmail/quarantine/content' not implemented

Is there a way to get the htmlmail using Proxmoxer or can someone guide me to the place where to impement it so I can open a pull request?

Originally created by @MrDiba on GitHub (Aug 26, 2024). Original GitHub issue: https://github.com/proxmoxer/proxmoxer/issues/171 Originally assigned to: @jhollowe on GitHub. Python version: 3.12.3 Proxmoxer version: 2.1.0 using https backend PMG version: 7.3-11 I'm creating an custom application around the Proxmox Mail Gateway API and I ran into an issue where I can not receive the `htmlmail` version from an quarantine content. For example: `proxmox.quarantine.content.get(id=mail_id)` gets the data from `/api2/json/quarantine/content` In the API documentation there is the following description: `Get email data. There is a special formatter called 'htmlmail' to get sanitized html view of the mail content (use the '/api2/htmlmail/quarantine/content' url).` But when I try to do `proxmox.htmlmail.quarantine.content.get(id=mail_id)` it errors with: ``` File "/home/user/git/spamfilter/venv/lib/python3.12/site-packages/proxmoxer/core.py", line 167, in get return self(args)._request("GET", params=params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/git/spamfilter/venv/lib/python3.12/site-packages/proxmoxer/core.py", line 147, in _request raise ResourceException( proxmoxer.core.ResourceException: 501 Not Implemented: Method 'GET /htmlmail/quarantine/content' not implemented ``` Is there a way to get the `htmlmail` using Proxmoxer or can someone guide me to the place where to impement it so I can open a pull request?
Author
Owner

@MrDiba commented on GitHub (Aug 26, 2024):

@JONTY650413 could you please elaborate what the fix is you are proposing? The speed in which you answered with this supposing 'fix' looks like a scam.

<!-- gh-comment-id:2310418066 --> @MrDiba commented on GitHub (Aug 26, 2024): @JONTY650413 could you please elaborate what the fix is you are proposing? The speed in which you answered with this supposing 'fix' looks like a scam.
Author
Owner

@jhollowe commented on GitHub (Aug 26, 2024):

sorry about that. I have reported them to GitHub for malicious content, blocked the user, and deleted the comment. Do not click the link that was sent by that user.

<!-- gh-comment-id:2310476239 --> @jhollowe commented on GitHub (Aug 26, 2024): sorry about that. I have reported them to GitHub for malicious content, blocked the user, and deleted the comment. Do not click the link that was sent by that user.
Author
Owner

@jhollowe commented on GitHub (Aug 26, 2024):

Can you try using proxmoxer 2.0.1? #170 seems slightly similar to this and I'm wondering if there is a bug in 2.1.0.

<!-- gh-comment-id:2310519794 --> @jhollowe commented on GitHub (Aug 26, 2024): Can you try using proxmoxer 2.0.1? #170 seems slightly similar to this and I'm wondering if there is a bug in 2.1.0.
Author
Owner

@MrDiba commented on GitHub (Aug 27, 2024):

Hi @jhollowe, thank you for your quick action on the spam and your reply.

I have tried downgrading to version 2.0.1, but it gives me the same results.

Edit:

Taking a look at the code: github.com/proxmoxer/proxmoxer@f72466614a/proxmoxer/backends/https.py (L251-L265)

It looks like the json part of the url is set over here, so i tried to change my code to proxmox_html = ProxmoxAPI( "pmg.example.com", port=443, user=Config.PMG_API_USERNAME, password=Config.PMG_API_PASSWORD, service='pmg', mode="htmlmail" )

But that results in errors in the authentication.

My assumption is that proxmox.htmlmail.quarantine.content.get(id=mail_id) results in an url like this: /api2/json/htmlmail/quarantine/content

<!-- gh-comment-id:2311672624 --> @MrDiba commented on GitHub (Aug 27, 2024): Hi @jhollowe, thank you for your quick action on the spam and your reply. I have tried downgrading to version 2.0.1, but it gives me the same results. Edit: Taking a look at the code: https://github.com/proxmoxer/proxmoxer/blob/f72466614a05b0c4f7505d5805a5eb232505cdea/proxmoxer/backends/https.py#L251-L265 It looks like the `json` part of the url is set over here, so i tried to change my code to `proxmox_html = ProxmoxAPI( "pmg.example.com", port=443, user=Config.PMG_API_USERNAME, password=Config.PMG_API_PASSWORD, service='pmg', mode="htmlmail" )` But that results in errors in the authentication. My assumption is that `proxmox.htmlmail.quarantine.content.get(id=mail_id)` results in an url like this: `/api2/json/htmlmail/quarantine/content`
Author
Owner

@MrDiba commented on GitHub (Aug 27, 2024):

Edit, this breaks more stuff than it fixes...

I have created an small but (in my opinion) ugly patch:

github.com/proxmoxer/proxmoxer@f72466614a/proxmoxer/core.py (L91-L96)

That piece of code handles the parameters, by adding an if statement that when the parameter is htmlmail it will remove the json from the base url:

  def __getattr__(self, item):
      if item.startswith("_"):
          raise AttributeError(item)

      kwargs = self._store.copy()

      if item == "htmlmail":
          self._store["base_url"] = self._store["base_url"].rstrip("json")

      kwargs["base_url"] = self.url_join(self._store["base_url"], item)

I would not advice to use this, but it works until there is an more permanent solution.

The output of proxmox.htmlmail.quarantine.content.get(id=mail_id) is an dict containing {'errors': b'<email in html>'}

<!-- gh-comment-id:2311813247 --> @MrDiba commented on GitHub (Aug 27, 2024): Edit, this breaks more stuff than it fixes... I have created an small but (in my opinion) ugly patch: https://github.com/proxmoxer/proxmoxer/blob/f72466614a05b0c4f7505d5805a5eb232505cdea/proxmoxer/core.py#L91-L96 That piece of code handles the parameters, by adding an if statement that when the parameter is `htmlmail` it will remove the json from the base url: ```python def __getattr__(self, item): if item.startswith("_"): raise AttributeError(item) kwargs = self._store.copy() if item == "htmlmail": self._store["base_url"] = self._store["base_url"].rstrip("json") kwargs["base_url"] = self.url_join(self._store["base_url"], item) ``` I would not advice to use this, but it works until there is an more permanent solution. The output of `proxmox.htmlmail.quarantine.content.get(id=mail_id)` is an dict containing `{'errors': b'<email in html>'}`
Author
Owner

@jhollowe commented on GitHub (Aug 27, 2024):

I don't have a PMG setup to test against. Do I need actual emails flowing through it to test this or can I just spin up a fresh PMG and still be able to test this?

<!-- gh-comment-id:2313162163 --> @jhollowe commented on GitHub (Aug 27, 2024): I don't have a PMG setup to test against. Do I need actual emails flowing through it to test this or can I just spin up a fresh PMG and still be able to test this?
Author
Owner

@MrDiba commented on GitHub (Aug 28, 2024):

I just tried to call spam_html = proxmox.htmlmail.quarantine.content.get() without an ID parameter and that gives the same 501 Not Implemented error so I assume it will work without having mail flowing through.

The documentation can be found here: https://pmg.proxmox.com/pmg-docs/api-viewer/index.html#/quarantine/content I can test patches for you if needed.

<!-- gh-comment-id:2314772761 --> @MrDiba commented on GitHub (Aug 28, 2024): I just tried to call `spam_html = proxmox.htmlmail.quarantine.content.get()` without an ID parameter and that gives the same `501 Not Implemented` error so I assume it will work without having mail flowing through. The documentation can be found here: <https://pmg.proxmox.com/pmg-docs/api-viewer/index.html#/quarantine/content> I can test patches for you if needed.
Author
Owner

@MrDiba commented on GitHub (Sep 4, 2024):

I have implemented an workaround in my code like this:

proxmox = ProxmoxAPI(
    Config.PMG_API_URL,
    port=Config.PMG_API_PORT,
    user=Config.PMG_API_USERNAME,
    password=Config.PMG_API_PASSWORD,
    service="pmg",
)

# ... do stuff in between ...

proxmox._store["base_url"] = "https://pmg.example.com:443/api2/htmlmail"

spam_html = proxmox.quarantine.content.get(id=mail_id)
decoded_spam_html = spam_html["errors"].decode("utf-8")

proxmox._store["base_url"] = "https://pmg.example.com:443/api2/json"

This way it only injects the new url when calling the htmlmail endpoint.

<!-- gh-comment-id:2328452362 --> @MrDiba commented on GitHub (Sep 4, 2024): I have implemented an workaround in my code like this: ```python proxmox = ProxmoxAPI( Config.PMG_API_URL, port=Config.PMG_API_PORT, user=Config.PMG_API_USERNAME, password=Config.PMG_API_PASSWORD, service="pmg", ) # ... do stuff in between ... proxmox._store["base_url"] = "https://pmg.example.com:443/api2/htmlmail" spam_html = proxmox.quarantine.content.get(id=mail_id) decoded_spam_html = spam_html["errors"].decode("utf-8") proxmox._store["base_url"] = "https://pmg.example.com:443/api2/json" ``` This way it only injects the new url when calling the `htmlmail` endpoint.
Author
Owner

@morph027 commented on GitHub (Sep 7, 2024):

You could create a context manager for this, which you can re-use in other calls too.

proxmox = ProxmoxAPI(
    Config.PMG_API_URL,
    port=Config.PMG_API_PORT,
    user=Config.PMG_API_USERNAME,
    password=Config.PMG_API_PASSWORD,
    service="pmg",
)

class PMGHtmlMailApi:
    def __init__(self, proxmox: ProxmoxAPI):
        self.proxmox = proxmox
    def __enter__(self):
        self.base_url = self.proxmox._store["base_url"]
        self.proxmox._store["base_url"] = self.base_url.replace("/api2/json", "/api2/htmlmail")
        return self.proxmox
    def __exit__(self, exc_type, exc_value, exc_tb):
        self.proxmox._store["base_url"] = self.base_url

with PMGHtmlMailApi(proxmox) as pmghtmlmail:
    spam_html = pmghtmlmail.quarantine.content.get(id=mail_id)
    decoded_spam_html = spam_html["errors"].decode("utf-8")
<!-- gh-comment-id:2336407808 --> @morph027 commented on GitHub (Sep 7, 2024): You could create a context manager for this, which you can re-use in other calls too. ```python proxmox = ProxmoxAPI( Config.PMG_API_URL, port=Config.PMG_API_PORT, user=Config.PMG_API_USERNAME, password=Config.PMG_API_PASSWORD, service="pmg", ) class PMGHtmlMailApi: def __init__(self, proxmox: ProxmoxAPI): self.proxmox = proxmox def __enter__(self): self.base_url = self.proxmox._store["base_url"] self.proxmox._store["base_url"] = self.base_url.replace("/api2/json", "/api2/htmlmail") return self.proxmox def __exit__(self, exc_type, exc_value, exc_tb): self.proxmox._store["base_url"] = self.base_url with PMGHtmlMailApi(proxmox) as pmghtmlmail: spam_html = pmghtmlmail.quarantine.content.get(id=mail_id) decoded_spam_html = spam_html["errors"].decode("utf-8") ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/proxmoxer#94
No description provided.