Module mudproto.charset
¶
Charset protocol.
Class CharsetMixIn(TelnetInterface)
¶
A charset mix in class for the Telnet protocol.
Source code in mudproto/charset.py
class CharsetMixIn(TelnetInterface):
"""A charset mix in class for the Telnet protocol."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
Defines the constructor.
Args:
*args: Positional arguments to be passed to the parent constructor.
**kwargs: Key-word only arguments to be passed to the parent constructor.
"""
super().__init__(*args, **kwargs)
self.subnegotiation_map[CHARSET] = self.on_charset
self._charsets: tuple[bytes, ...] = (b"US-ASCII",)
self._charset: bytes = self._charsets[0]
@property
def charset(self) -> str:
"""The currently used character set."""
return str(self._charset, "us-ascii")
def negotiate_charset(self, name: bytes | str) -> None:
"""
Negotiates changing the character set.
Args:
name: The name of the character set to use.
"""
separator: bytes = b";"
if not isinstance(name, str):
name = str(name, "us-ascii")
try:
target = codecs.lookup(name).name
except LookupError:
logger.warning(f"'{name}' not a valid codec")
return
for item in self._charsets:
if target == codecs.lookup(str(item, "us-ascii")).name:
logger.debug(f"Tell peer we would like to use the {item!r} charset.")
self.request_negotiation(CHARSET, CHARSET_REQUEST + separator + item)
return
logger.warning(f"Could not find any charsets which target '{target}'")
@staticmethod
def parse_supported_charsets(response: bytes) -> tuple[bytes, ...]:
"""
Parses the supported character sets from peer.
Args:
response: The response from peer, containing the supported character sets.
Returns:
The character sets supported by peer, with duplicate aliases removed.
"""
charsets: list[bytes] = []
names: set[str] = set()
separator, response = response[:1], response[1:]
for item in response.split(separator):
with suppress(LookupError):
name = codecs.lookup(str(item, "us-ascii")).name
if name not in names:
charsets.append(item)
names.add(name)
return tuple(charsets)
def on_charset(self, data: bytes) -> None:
"""
Called when a charset subnegotiation is received.
Args:
data: The payload.
"""
status, response = data[:1], data[1:]
if status == CHARSET_REQUEST:
self._charsets = self.parse_supported_charsets(response)
logger.debug(f"Peer responds: Supported charsets: {self._charsets!r}.")
self.negotiate_charset(self._charset)
elif status == CHARSET_ACCEPTED:
logger.debug(f"Peer responds: Charset {response!r} accepted.")
self._charset = response
elif status == CHARSET_REJECTED:
logger.warning("Peer responds: Charset rejected.")
else:
logger.warning(f"Unknown charset negotiation response from peer: {data!r}")
self.wont(CHARSET)
def on_enable_local(self, option: bytes) -> bool: # NOQA: D102
if option == CHARSET:
logger.debug("Charset negotiation enabled.")
return True
return bool(super().on_enable_local(option)) # pragma: no cover
def on_disable_local(self, option: bytes) -> None: # NOQA: D102
if option == CHARSET:
logger.debug("Charset negotiation disabled.")
return
super().on_disable_local(option) # type: ignore[safe-super] # pragma: no cover
Attribute charset: str
property
readonly
¶
The currently used character set.
Attribute is_client: bool
inherited
property
readonly
¶
True if acting as a client, False otherwise.
Attribute is_server: bool
inherited
property
readonly
¶
True if acting as a server, False otherwise.
Method __init__(self, *args, **kwargs)
special
¶
Defines the constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args |
Any |
Positional arguments to be passed to the parent constructor. |
() |
**kwargs |
Any |
Key-word only arguments to be passed to the parent constructor. |
{} |
Source code in mudproto/charset.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
Defines the constructor.
Args:
*args: Positional arguments to be passed to the parent constructor.
**kwargs: Key-word only arguments to be passed to the parent constructor.
"""
super().__init__(*args, **kwargs)
self.subnegotiation_map[CHARSET] = self.on_charset
self._charsets: tuple[bytes, ...] = (b"US-ASCII",)
self._charset: bytes = self._charsets[0]
Method do(self, option)
inherited
¶
Requests that the peer enable an option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option to enable. |
required |
Source code in mudproto/charset.py
@abstractmethod
def do(self, option: bytes) -> None:
"""
Requests that the peer enable an option.
Args:
option: The option to enable.
"""
Method dont(self, option)
inherited
¶
Requests that the peer disable an option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option to disable. |
required |
Source code in mudproto/charset.py
@abstractmethod
def dont(self, option: bytes) -> None:
"""
Requests that the peer disable an option.
Args:
option: The option to disable.
"""
Method get_option_state(self, option)
inherited
¶
Gets the state of a Telnet option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option to get state. |
required |
Returns:
| Type | Description |
|---|---|
_OptionState |
An object containing the option state. |
Source code in mudproto/charset.py
@abstractmethod
def get_option_state(self, option: bytes) -> _OptionState:
"""
Gets the state of a Telnet option.
Args:
option: The option to get state.
Returns:
An object containing the option state.
"""
Method negotiate_charset(self, name)
¶
Negotiates changing the character set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
bytes | str |
The name of the character set to use. |
required |
Source code in mudproto/charset.py
def negotiate_charset(self, name: bytes | str) -> None:
"""
Negotiates changing the character set.
Args:
name: The name of the character set to use.
"""
separator: bytes = b";"
if not isinstance(name, str):
name = str(name, "us-ascii")
try:
target = codecs.lookup(name).name
except LookupError:
logger.warning(f"'{name}' not a valid codec")
return
for item in self._charsets:
if target == codecs.lookup(str(item, "us-ascii")).name:
logger.debug(f"Tell peer we would like to use the {item!r} charset.")
self.request_negotiation(CHARSET, CHARSET_REQUEST + separator + item)
return
logger.warning(f"Could not find any charsets which target '{target}'")
Method on_charset(self, data)
¶
Called when a charset subnegotiation is received.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
bytes |
The payload. |
required |
Source code in mudproto/charset.py
def on_charset(self, data: bytes) -> None:
"""
Called when a charset subnegotiation is received.
Args:
data: The payload.
"""
status, response = data[:1], data[1:]
if status == CHARSET_REQUEST:
self._charsets = self.parse_supported_charsets(response)
logger.debug(f"Peer responds: Supported charsets: {self._charsets!r}.")
self.negotiate_charset(self._charset)
elif status == CHARSET_ACCEPTED:
logger.debug(f"Peer responds: Charset {response!r} accepted.")
self._charset = response
elif status == CHARSET_REJECTED:
logger.warning("Peer responds: Charset rejected.")
else:
logger.warning(f"Unknown charset negotiation response from peer: {data!r}")
self.wont(CHARSET)
Method on_command(self, command, option)
inherited
¶
Called when a 1 or 2 byte command is received.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
command |
bytes |
The first byte in a 1 or 2 byte negotiation sequence. |
required |
option |
bytes | None |
The second byte in a 2 byte negotiation sequence or None. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_command(self, command: bytes, option: bytes | None) -> None:
"""
Called when a 1 or 2 byte command is received.
Args:
command: The first byte in a 1 or 2 byte negotiation sequence.
option: The second byte in a 2 byte negotiation sequence or None.
"""
Method on_connection_lost(self)
inherited
¶
Called by disconnect when a connection to peer has been lost.
Source code in mudproto/charset.py
@abstractmethod
def on_connection_lost(self) -> None:
"""Called by `disconnect` when a connection to peer has been lost."""
Method on_connection_made(self)
inherited
¶
Called by connect when a connection to peer has been established.
Source code in mudproto/charset.py
@abstractmethod
def on_connection_made(self) -> None:
"""Called by `connect` when a connection to peer has been established."""
Method on_data_received(self, data)
inherited
¶
Called by parse when data is received.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
bytes |
The received data. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_data_received(self, data: bytes) -> None:
"""
Called by `parse` when data is received.
Args:
data: The received data.
"""
self._receiver(data)
Method on_disable_local(self, option)
¶
Disables a locally managed option.
This method is called before we disable a locally enabled option, in order to perform any necessary cleanup.
Note
If on_enable_local is overridden, this method must be overridden as well.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option being disabled. |
required |
Source code in mudproto/charset.py
def on_disable_local(self, option: bytes) -> None: # NOQA: D102
if option == CHARSET:
logger.debug("Charset negotiation disabled.")
return
super().on_disable_local(option) # type: ignore[safe-super] # pragma: no cover
Method on_disable_remote(self, option)
inherited
¶
Disables a remotely managed option.
This method is called when peer disables a remotely enabled option, in order to perform any necessary cleanup on our end.
Note
If on_enable_remote is overridden, this method must be overridden as well.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option being disabled. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_disable_remote(self, option: bytes) -> None:
"""
Disables a remotely managed option.
This method is called when peer disables a remotely enabled option,
in order to perform any necessary cleanup on our end.
Note:
If on_enable_remote is overridden, this method must be overridden as well.
Args:
option: The option being disabled.
"""
raise NotImplementedError(f"Don't know how to disable remote Telnet option {option!r}")
Method on_enable_local(self, option)
¶
Called to accept or reject the request for us to manage the option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option that peer requests us to handle. |
required |
Returns:
| Type | Description |
|---|---|
bool |
True if we will handle the option, False otherwise. |
Source code in mudproto/charset.py
def on_enable_local(self, option: bytes) -> bool: # NOQA: D102
if option == CHARSET:
logger.debug("Charset negotiation enabled.")
return True
return bool(super().on_enable_local(option)) # pragma: no cover
Method on_enable_remote(self, option)
inherited
¶
Called to accept or reject the request for peer to manage the option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option that peer wants to handle. |
required |
Returns:
| Type | Description |
|---|---|
bool |
True if we will allow peer to handle the option, False otherwise. |
Source code in mudproto/charset.py
@abstractmethod
def on_enable_remote(self, option: bytes) -> bool:
"""
Called to accept or reject the request for peer to manage the option.
Args:
option: The option that peer wants to handle.
Returns:
True if we will allow peer to handle the option, False otherwise.
"""
return False # Reject all options by default.
Method on_option_enabled(self, option)
inherited
¶
Called after an option has been fully enabled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option that has been enabled. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_option_enabled(self, option: bytes) -> None:
"""
Called after an option has been fully enabled.
Args:
option: The option that has been enabled.
"""
Method on_subnegotiation(self, option, data)
inherited
¶
Called when a subnegotiation is received.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The subnegotiation option. |
required |
data |
bytes |
The payload. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_subnegotiation(self, option: bytes, data: bytes) -> None:
"""
Called when a subnegotiation is received.
Args:
option: The subnegotiation option.
data: The payload.
"""
Method on_unhandled_command(self, command, option)
inherited
¶
Called for commands for which no handler is installed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
command |
bytes |
The first byte in a 1 or 2 byte negotiation sequence. |
required |
option |
bytes | None |
The second byte in a 2 byte negotiation sequence or None. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_unhandled_command(self, command: bytes, option: bytes | None) -> None:
"""
Called for commands for which no handler is installed.
Args:
command: The first byte in a 1 or 2 byte negotiation sequence.
option: The second byte in a 2 byte negotiation sequence or None.
"""
Method on_unhandled_subnegotiation(self, option, data)
inherited
¶
Called for subnegotiations for which no handler is installed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The subnegotiation option. |
required |
data |
bytes |
The payload. |
required |
Source code in mudproto/charset.py
@abstractmethod
def on_unhandled_subnegotiation(self, option: bytes, data: bytes) -> None:
"""
Called for subnegotiations for which no handler is installed.
Args:
option: The subnegotiation option.
data: The payload.
"""
Method parse_supported_charsets(response)
staticmethod
¶
Parses the supported character sets from peer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
response |
bytes |
The response from peer, containing the supported character sets. |
required |
Returns:
| Type | Description |
|---|---|
tuple[bytes, ...] |
The character sets supported by peer, with duplicate aliases removed. |
Source code in mudproto/charset.py
@staticmethod
def parse_supported_charsets(response: bytes) -> tuple[bytes, ...]:
"""
Parses the supported character sets from peer.
Args:
response: The response from peer, containing the supported character sets.
Returns:
The character sets supported by peer, with duplicate aliases removed.
"""
charsets: list[bytes] = []
names: set[str] = set()
separator, response = response[:1], response[1:]
for item in response.split(separator):
with suppress(LookupError):
name = codecs.lookup(str(item, "us-ascii")).name
if name not in names:
charsets.append(item)
names.add(name)
return tuple(charsets)
Method request_negotiation(self, option, data)
inherited
¶
Sends a subnegotiation message to the peer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The subnegotiation option. |
required |
data |
bytes |
The payload. |
required |
Source code in mudproto/charset.py
@abstractmethod
def request_negotiation(self, option: bytes, data: bytes) -> None:
"""
Sends a subnegotiation message to the peer.
Args:
option: The subnegotiation option.
data: The payload.
"""
Method will(self, option)
inherited
¶
Indicates our willingness to enable an option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option to accept. |
required |
Source code in mudproto/charset.py
@abstractmethod
def will(self, option: bytes) -> None:
"""
Indicates our willingness to enable an option.
Args:
option: The option to accept.
"""
Method wont(self, option)
inherited
¶
Indicates we are not willing to enable an option.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
option |
bytes |
The option to reject. |
required |
Source code in mudproto/charset.py
@abstractmethod
def wont(self, option: bytes) -> None:
"""
Indicates we are not willing to enable an option.
Args:
option: The option to reject.
"""
Method write(self, data)
inherited
¶
Writes data to peer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
bytes |
The bytes to be written. |
required |
Source code in mudproto/charset.py
def write(self, data: bytes) -> None:
"""
Writes data to peer.
Args:
data: The bytes to be written.
"""
self._writer(data)