From 9484814c3fdb573fbd08cedd289049a67ba63fdf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Jul 2024 21:14:29 +0530 Subject: [PATCH] Specify that unknown payload types should be ignored --- docs/desktop-notifications.rst | 4 +++- kitty/notify.py | 36 +++++++++++++++++++--------------- kitty_tests/parser.py | 8 ++++++-- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/docs/desktop-notifications.rst b/docs/desktop-notifications.rst index d948db6e5..8277e4722 100644 --- a/docs/desktop-notifications.rst +++ b/docs/desktop-notifications.rst @@ -156,7 +156,9 @@ Key Value Default Description direct responses to the correct window. ``p`` One of ``title``, ``title`` Whether the payload is the notification title or body or query. If a - ``body`` or ``?`` notification has no title, the body will be used as title. + ``body`` or ``?`` notification has no title, the body will be used as title. Terminal + emulators should ignore payloads of unknown type to allow for future + expansion of this protocol. ``o`` One of ``always``, ``always`` When to honor the notification request. ``unfocused`` means when the window ``unfocused`` or the notification is sent on does not have keyboard focus. ``invisible`` diff --git a/kitty/notify.py b/kitty/notify.py index 7d2ecff42..40e1fffe4 100644 --- a/kitty/notify.py +++ b/kitty/notify.py @@ -137,7 +137,7 @@ class QueryResponse(Exception): self.response_string = response_string -def parse_osc_99(raw: str) -> NotificationCommand: +def parse_osc_99(raw: str, log_warnings: bool = True) -> NotificationCommand: cmd = NotificationCommand() metadata, payload = raw.partition(';')[::2] payload_is_encoded = False @@ -147,7 +147,8 @@ def parse_osc_99(raw: str) -> NotificationCommand: try: k, v = part.split('=', 1) except Exception: - log_error('Malformed OSC 99: metadata is not key=value pairs') + if log_warnings: + log_error('Malformed OSC 99: metadata is not key=value pairs') return cmd if k == 'p': payload_type = v @@ -183,19 +184,21 @@ def parse_osc_99(raw: str) -> NotificationCommand: i = f'i={sanitize_id(cmd.identifier or "0")}:' raise QueryResponse(f'99;{i}p=?;a={actions}:o={when}:u={urgency}') - if payload_type not in ('body', 'title'): - log_error(f'Malformed OSC 99: unknown payload type: {payload_type}') - return NotificationCommand() - if payload_is_encoded: - try: - payload = standard_b64decode(payload).decode('utf-8') - except Exception: - log_error('Malformed OSC 99: payload is not base64 encoded UTF-8 text') - return NotificationCommand() - if payload_type == 'title': - cmd.title = payload + if payload_type in ('body', 'title'): + if payload_is_encoded: + try: + payload = standard_b64decode(payload).decode('utf-8') + except Exception: + if log_warnings: + log_error('Malformed OSC 99: payload is not base64 encoded UTF-8 text') + return NotificationCommand() + if payload_type == 'title': + cmd.title = payload + else: + cmd.body = payload else: - cmd.body = payload + if log_warnings: + log_error(f'OSC 99: unknown payload type: {payload_type}, ignoring payload') return cmd @@ -293,10 +296,11 @@ def handle_notification_cmd( raw_data: str, window_id: int, prev_cmd: NotificationCommand, - notify_implementation: NotifyImplementation = notify_implementation + notify_implementation: NotifyImplementation = notify_implementation, + log_warnings: bool = True, ) -> Optional[NotificationCommand]: if osc_code == 99: - cmd = merge_osc_99(prev_cmd, parse_osc_99(raw_data)) + cmd = merge_osc_99(prev_cmd, parse_osc_99(raw_data, log_warnings=log_warnings)) if cmd.done: notify_with_command(cmd, window_id, notify_implementation) cmd = NotificationCommand() diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index faf24e857..a6c16a853 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -542,7 +542,7 @@ class TestParser(BaseTest): def h(raw_data, osc_code=99, window_id=1): nonlocal prev_cmd try: - x = handle_notification_cmd(osc_code, raw_data, window_id, prev_cmd, notify) + x = handle_notification_cmd(osc_code, raw_data, window_id, prev_cmd, notify, log_warnings=False) if x is not None and osc_code == 99: prev_cmd = x except QueryResponse as err: @@ -582,7 +582,6 @@ class TestParser(BaseTest): self.ae(activations, [('0', 1, True, False)]) reset() - h('d=0:i=x:a=-report;title') h('d=1:i=x:a=report;body') self.ae(notifications, [('titlebody', '', 'i0', Urgency.Normal)]) @@ -590,6 +589,11 @@ class TestParser(BaseTest): self.ae(activations, [('x', 1, True, True)]) reset() + h('d=0:i=y;title') + h('d=1:i=y:p=xxx;title') + self.ae(notifications, [('title', '', 'i0', Urgency.Normal)]) + reset() + h(';title') self.ae(notifications, [('title', '', 'i0', Urgency.Normal)]) notification_activated(notifications[-1][-2], activated)