APP_NAME = "lxmf" ########################################################## # The following core fields are provided to facilitate # # interoperability in data exchange between various LXMF # # clients and systems. # ########################################################## FIELD_EMBEDDED_LXMS = 0x01 FIELD_TELEMETRY = 0x02 FIELD_TELEMETRY_STREAM = 0x03 FIELD_ICON_APPEARANCE = 0x04 FIELD_FILE_ATTACHMENTS = 0x05 FIELD_IMAGE = 0x06 FIELD_AUDIO = 0x07 FIELD_THREAD = 0x08 FIELD_COMMANDS = 0x09 FIELD_RESULTS = 0x0A FIELD_GROUP = 0x0B FIELD_TICKET = 0x0C FIELD_EVENT = 0x0D FIELD_RNR_REFS = 0x0E FIELD_RENDERER = 0x0F # For usecases such as including custom data structures, # embedding or encapsulating other data types or protocols # that are not native to LXMF, or bridging/tunneling # external protocols or services over LXMF, the following # fields are available. A format/type/protocol (or other) # identifier can be included in the CUSTOM_TYPE field, and # the embedded payload can be included in the CUSTOM_DATA # field. It is up to the client application to correctly # discern and potentially utilise any data embedded using # this mechanism. FIELD_CUSTOM_TYPE = 0xFB FIELD_CUSTOM_DATA = 0xFC FIELD_CUSTOM_META = 0xFD # The non-specific and debug fields are intended for # development, testing and debugging use. FIELD_NON_SPECIFIC = 0xFE FIELD_DEBUG = 0xFF ########################################################## # The following section lists field-specific specifiers, # # modes and identifiers that are native to LXMF. It is # # optional for any client or system to support any of # # these, and they are provided as template for easing # # interoperability without sacrificing expandability # # and flexibility of the format. # ########################################################## # Audio modes for the data structure in FIELD_AUDIO # Codec2 Audio Modes AM_CODEC2_450PWB = 0x01 AM_CODEC2_450 = 0x02 AM_CODEC2_700C = 0x03 AM_CODEC2_1200 = 0x04 AM_CODEC2_1300 = 0x05 AM_CODEC2_1400 = 0x06 AM_CODEC2_1600 = 0x07 AM_CODEC2_2400 = 0x08 AM_CODEC2_3200 = 0x09 # Opus Audio Modes AM_OPUS_OGG = 0x10 AM_OPUS_LBW = 0x11 AM_OPUS_MBW = 0x12 AM_OPUS_PTT = 0x13 AM_OPUS_RT_HDX = 0x14 AM_OPUS_RT_FDX = 0x15 AM_OPUS_STANDARD = 0x16 AM_OPUS_HQ = 0x17 AM_OPUS_BROADCAST = 0x18 AM_OPUS_LOSSLESS = 0x19 # Custom, unspecified audio mode, the client must # determine it itself based on the included data. AM_CUSTOM = 0xFF # Message renderer specifications for FIELD_RENDERER. # The renderer specification is completely optional, # and only serves as an indication to the receiving # client on how to render the message contents. It is # not mandatory to implement, either on sending or # receiving sides, but is the recommended way to # signal how to render a message, if non-plaintext # formatting is used. RENDERER_PLAIN = 0x00 RENDERER_MICRON = 0x01 RENDERER_MARKDOWN = 0x02 RENDERER_BBCODE = 0x03 # Optional propagation node metadata fields. These # fields may be highly unstable in allocation and # availability until the version 1.0.0 release, so use # at your own risk until then, and expect changes! PN_META_VERSION = 0x00 PN_META_NAME = 0x01 PN_META_SYNC_STRATUM = 0x02 PN_META_SYNC_THROTTLE = 0x03 PN_META_AUTH_BAND = 0x04 PN_META_UTIL_PRESSURE = 0x05 PN_META_CUSTOM = 0xFF # Supported functionality codes for signalling # feature and capability support. SF_COMPRESSION = 0x00 ########################################################## # The following helper functions makes it easier to # # handle and operate on LXMF data in client programs # ########################################################## import RNS import RNS.vendor.umsgpack as msgpack def display_name_from_app_data(app_data=None): if app_data == None: return None elif len(app_data) == 0: return None else: # Version 0.5.0+ announce format if (app_data[0] >= 0x90 and app_data[0] <= 0x9f) or app_data[0] == 0xdc: peer_data = msgpack.unpackb(app_data) if type(peer_data) == list: if len(peer_data) < 1: return None else: dn = peer_data[0] if dn == None: return None else: try: decoded = dn.decode("utf-8") return decoded except Exception as e: RNS.log(f"Could not decode display name in included announce data. The contained exception was: {e}", RNS.LOG_ERROR) return None # Original announce format else: return app_data.decode("utf-8") def stamp_cost_from_app_data(app_data=None): if app_data == None or app_data == b"": return None else: # Version 0.5.0+ announce format if (app_data[0] >= 0x90 and app_data[0] <= 0x9f) or app_data[0] == 0xdc: peer_data = msgpack.unpackb(app_data) if type(peer_data) == list: if len(peer_data) < 2: return None else: return peer_data[1] # Original announce format else: return None def compression_support_from_app_data(app_data=None): if app_data == None or app_data == b"": return None else: # Version 0.5.0+ announce format if (app_data[0] >= 0x90 and app_data[0] <= 0x9f) or app_data[0] == 0xdc: peer_data = msgpack.unpackb(app_data) if type(peer_data) == list: if len(peer_data) < 3: return True else: if not type(peer_data[2]) == list: return True else: return SF_COMPRESSION in peer_data[2] # Original announce format else: return True def pn_name_from_app_data(app_data=None): if app_data == None: return None else: if pn_announce_data_is_valid(app_data): data = msgpack.unpackb(app_data) metadata = data[6] if not PN_META_NAME in metadata: return None else: try: return metadata[PN_META_NAME].decode("utf-8") except: return None return None def pn_stamp_cost_from_app_data(app_data=None): if app_data == None: return None else: if pn_announce_data_is_valid(app_data): data = msgpack.unpackb(app_data) return data[5][0] else: return None def pn_announce_data_is_valid(data): try: if type(data) != bytes: return False else: data = msgpack.unpackb(data) if len(data) < 7: raise ValueError("Invalid announce data: Insufficient peer data, likely from deprecated LXMF version") else: try: int(data[1]) except: raise ValueError("Invalid announce data: Could not decode timebase") if data[2] != True and data[2] != False: raise ValueError("Invalid announce data: Indeterminate propagation node status") try: int(data[3]) except: raise ValueError("Invalid announce data: Could not decode propagation transfer limit") try: int(data[4]) except: raise ValueError("Invalid announce data: Could not decode propagation sync limit") if type(data[5]) != list: raise ValueError("Invalid announce data: Could not decode stamp costs") try: int(data[5][0]) except: raise ValueError("Invalid announce data: Could not decode target stamp cost") try: int(data[5][1]) except: raise ValueError("Invalid announce data: Could not decode stamp cost flexibility") try: int(data[5][2]) except: raise ValueError("Invalid announce data: Could not decode peering cost") if type(data[6]) != dict: raise ValueError("Invalid announce data: Could not decode metadata") except Exception as e: RNS.log(f"Could not validate propagation node announce data: {e}", RNS.LOG_DEBUG) return False return True