Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions bambulabs_api/bambu.cert
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZTCCAk2gAwIBAgIUV1FckwXElyek1onFnQ9kL7Bk4N8wDQYJKoZIhvcNAQEL
BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yMjA0MDQwMzQyMTFaFw0zMjA0MDEw
MzQyMTFaMEIxCzAJBgNVBAYTAkNOMSIwIAYDVQQKDBlCQkwgVGVjaG5vbG9naWVz
IENvLiwgTHRkMQ8wDQYDVQQDDAZCQkwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDL3pnDdxGOk5Z6vugiT4dpM0ju+3Xatxz09UY7mbj4tkIdby4H
oeEdiYSZjc5LJngJuCHwtEbBJt1BriRdSVrF6M9D2UaBDyamEo0dxwSaVxZiDVWC
eeCPdELpFZdEhSNTaT4O7zgvcnFsfHMa/0vMAkvE7i0qp3mjEzYLfz60axcDoJLk
p7n6xKXI+cJbA4IlToFjpSldPmC+ynOo7YAOsXt7AYKY6Glz0BwUVzSJxU+/+VFy
/QrmYGNwlrQtdREHeRi0SNK32x1+bOndfJP0sojuIrDjKsdCLye5CSZIvqnbowwW
1jRwZgTBR29Zp2nzCoxJYcU9TSQp/4KZuWNVAgMBAAGjUzBRMB0GA1UdDgQWBBSP
NEJo3GdOj8QinsV8SeWr3US+HjAfBgNVHSMEGDAWgBSPNEJo3GdOj8QinsV8SeWr
3US+HjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQABlBIT5ZeG
fgcK1LOh1CN9sTzxMCLbtTPFF1NGGA13mApu6j1h5YELbSKcUqfXzMnVeAb06Htu
3CoCoe+wj7LONTFO++vBm2/if6Jt/DUw1CAEcNyqeh6ES0NX8LJRVSe0qdTxPJuA
BdOoo96iX89rRPoxeed1cpq5hZwbeka3+CJGV76itWp35Up5rmmUqrlyQOr/Wax6
itosIzG0MfhgUzU51A2P/hSnD3NDMXv+wUY/AvqgIL7u7fbDKnku1GzEKIkfH8hm
Rs6d8SCU89xyrwzQ0PR853irHas3WrHVqab3P+qNwR0YirL0Qk7Xt/q3O1griNg2
Blbjg3obpHo9
-----END CERTIFICATE-----
35 changes: 33 additions & 2 deletions bambulabs_api/ftp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@


import ftplib
import functools
from io import BytesIO
import os
from socket import socket
import ssl
from PIL import Image

Expand Down Expand Up @@ -31,6 +34,16 @@ def sock(self, value): # type: ignore
value = self.context.wrap_socket(value)
self._sock = value

def ntransfercmd(self, cmd: str, rest: int | str | None = None) -> tuple[socket, int | None]:
conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)

if self._prot_p: # type: ignore
assert self.sock is not None
conn = self.context.wrap_socket(
conn, server_hostname=self.host, session=self.sock.session
) # this is the fix
return conn, size

def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
self.voidcmd('TYPE I')
conn = self.transfercmd(cmd, rest)
Expand All @@ -51,13 +64,31 @@ def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
return self.voidresp()


@functools.lru_cache(maxsize=1)
def create_local_ssl_context():
"""
This context validates the certificate for TLS connections to local printers.
"""
script_path = os.path.abspath(__file__)
directory_path = os.path.dirname(script_path)
certfile = directory_path + "/bambu.cert"
context = ssl.create_default_context(cafile=certfile)
# Ignore "CA cert does not include key usage extension" error since python 3.13
# See note in https://docs.python.org/3/library/ssl.html#ssl.create_default_context
context.verify_flags &= ~ssl.VERIFY_X509_STRICT
# Workaround because some users get this error despite SNI: "certificate verify failed: IP address mismatch"
context.check_hostname = False
return context


class PrinterFTPClient:
def __init__(self,
server_ip: str,
access_code: str,
user: str = 'bblp',
port: int = 990) -> None:
self.ftps = ImplicitFTP_TLS()
self._context = create_local_ssl_context()
self.ftps = ImplicitFTP_TLS(self._context)

self.server_ip = server_ip
self.port = port
Expand All @@ -73,7 +104,7 @@ def connect_and_run(func):
func (function): the function to be decorated
""" # noqa

def wrapper(self, *args, **kwargs) -> Any:
def wrapper(self: 'PrinterFTPClient', *args, **kwargs) -> Any:
logging.info("Connecting to FTP server...")
self.ftps.connect(host=self.server_ip, port=self.port)
self.ftps.login(self.user, self.access_code)
Expand Down
Loading