Added save file

This commit is contained in:
emdee 2022-10-02 08:44:33 +00:00
parent 58e1606451
commit fe7715abb5
2 changed files with 72 additions and 40 deletions

View file

@ -99,5 +99,18 @@ Because it's written in Python it is easy to extend to, for example,
rekeying a profile when copying a profile to a new device: rekeying a profile when copying a profile to a new device:
<https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC> <https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC>
### Editing - save
The code now can generate a saved copy of the profile as it parses the profile.
Use the command ```--command save``` with ```--output``` and a filename,
to process the file with info to stderr, and it will save an copy of the file
to the ```--output``` (unencrypted).
It may be shorter than the original profile by up to 512 bytes, as the
original toxic profile is padded at the end with nulls. So this code
can be extended to edit the profile before saving it.
## Specification
There is a copy of the Tox [spec](https://toktok.ltd/spec.html) There is a copy of the Tox [spec](https://toktok.ltd/spec.html)
in the repo - it is missing any description of the groups section. in the repo - it is missing any description of the groups section.

View file

@ -76,6 +76,7 @@ except ImportError as e:
try: try:
# https://git.plastiras.org/emdee/toxygen_wrapper # https://git.plastiras.org/emdee/toxygen_wrapper
from wrapper.toxencryptsave import ToxEncryptSave from wrapper.toxencryptsave import ToxEncryptSave
from wrapper_tests.support_http import download_url
except ImportError as e: except ImportError as e:
print(f"Import Error {e}") print(f"Import Error {e}")
print("Download toxygen_wrapper to deal with encrypted tox files, from:") print("Download toxygen_wrapper to deal with encrypted tox files, from:")
@ -85,16 +86,7 @@ except ImportError as e:
print("and libtoxencryptsave.so into wrapper/../libs/") print("and libtoxencryptsave.so into wrapper/../libs/")
print("Link all 3 from libtoxcore.so if you have only libtoxcore.so") print("Link all 3 from libtoxcore.so if you have only libtoxcore.so")
ToxEncryptSave = None ToxEncryptSave = None
try: download_url = None
from wrapper_tests.support_http import download_url
except:
try:
from support_http import download_url
except ImportError as e:
print(f"Import Error {e}")
print("Download toxygen_wrapper to deal with encrypted tox files, from:")
print("https://git.plastiras.org/emdee/toxygen_wrapper")
download_url = None
LOG = logging.getLogger('TSF') LOG = logging.getLogger('TSF')
@ -211,7 +203,10 @@ Length Contents
"Pk": pk}] "Pk": pk}]
return lIN return lIN
def lProcessGroups(state, index, length, result): def lProcessGroups(state, index, length, result, label="GROUPS"):
"""
No GROUPS description in spec.html
"""
lIN = [] lIN = []
i = 0 i = 0
if not msgpack: if not msgpack:
@ -219,10 +214,11 @@ def lProcessGroups(state, index, length, result):
return [] return []
try: try:
groups = msgpack.loads(result, raw=True) groups = msgpack.loads(result, raw=True)
LOG.debug(f"process_chunk {label} len={len(groups)}") LOG.info(f"{label} {len(groups)} groups")
for group in groups: for group in groups:
assert len(group) == 7, group assert len(group) == 7, group
i += 1 i += 1
state_values, \ state_values, \
state_bin, \ state_bin, \
topic_info, \ topic_info, \
@ -262,7 +258,7 @@ def lProcessGroups(state, index, length, result):
assert len(mod_list) == 2, mod_list assert len(mod_list) == 2, mod_list
num_moderators = mod_list[0] num_moderators = mod_list[0]
LOG.debug(f"lProcessGroups #{i} num moderators={mod_list[0]}") LOG.info(f"lProcessGroups #{i} num moderators={mod_list[0]}")
#define CRYPTO_SIGN_PUBLIC_KEY_SIZE 32 #define CRYPTO_SIGN_PUBLIC_KEY_SIZE 32
mods = mod_list[1] mods = mod_list[1]
assert len(mods) % 32 == 0, len(mods) assert len(mods) % 32 == 0, len(mods)
@ -272,7 +268,7 @@ def lProcessGroups(state, index, length, result):
mod = mods[j*32:j*32 + 32] mod = mods[j*32:j*32 + 32]
LOG.info(f"lProcessGroups group#{i} mod#{j} sig_pk={bin_to_hex(mod)}") LOG.info(f"lProcessGroups group#{i} mod#{j} sig_pk={bin_to_hex(mod)}")
lMODS += [{"Sig_pk": bin_to_hex(mod)}] lMODS += [{"Sig_pk": bin_to_hex(mod)}]
if lMODS: lIN += [{"Moderators": lMODS}] lIN += [{"Moderators": lMODS}]
assert len(keys) == 4, keys assert len(keys) == 4, keys
LOG.debug(f"lProcessGroups #{i} {repr(list(map(len, keys)))}") LOG.debug(f"lProcessGroups #{i} {repr(list(map(len, keys)))}")
@ -403,20 +399,27 @@ def lProcessDHTnodes(state, index, length, result, label="DHTnode"):
return lIN return lIN
def process_chunk(index, state): def process_chunk(index, state):
global lOUT, bOUT, iTOTAL, aOUT global lOUT, bOUT, aOUT
length = struct.unpack_from("<H", state, index)[0] length = struct.unpack_from("<I", state, index)[0]
data_type = struct.unpack_from("<H", state, index + 4)[0] data_type = struct.unpack_from("<H", state, index + 4)[0]
check = struct.unpack_from("<H", state, index + 6)[0]
assert check == 0x01CE, check
new_index = index + length + 8 new_index = index + length + 8
result = state[index + 8:index + 8 + length] result = state[index + 8:index + 8 + length]
iTOTAL += length + 8
label = dSTATE_TYPE[data_type]
diff = index - len(bOUT)
if diff:
LOG.debug(f"PROCESS_CHUNK {label} index={index} bOUT={len(bOUT)} delta={diff} length={length}")
# plan on repacking as we read - this is just a starting point # plan on repacking as we read - this is just a starting point
# We'll add the results back to bOUT to see if we get what we started with. # We'll add the results back to bOUT to see if we get what we started with.
# Then will will be able to selectively null sections or selectively edit. # Then will will be able to selectively null sections or selectively edit.
bOUT += struct.pack("<H", length) + struct.pack("<H", data_type) + result bOUT += struct.pack("<I", length) + \
struct.pack("<H", data_type) + \
struct.pack("<H", check) + \
result
label = dSTATE_TYPE[data_type]
if data_type == MESSENGER_STATE_TYPE_NOSPAMKEYS: if data_type == MESSENGER_STATE_TYPE_NOSPAMKEYS:
nospam = bin_to_hex(result[0:4]) nospam = bin_to_hex(result[0:4])
public_key = bin_to_hex(result[4:36]) public_key = bin_to_hex(result[4:36])
@ -435,21 +438,21 @@ def process_chunk(index, state):
lOUT += [{label: lIN}]; aOUT.update({label: lIN}) lOUT += [{label: lIN}]; aOUT.update({label: lIN})
elif data_type == MESSENGER_STATE_TYPE_FRIENDS: elif data_type == MESSENGER_STATE_TYPE_FRIENDS:
LOG.debug(f"process_chunk {label} {length // 2216} FRIENDS {length} {length % 2216}") LOG.info(f"{label} {length // 2216} FRIENDS {length % 2216}")
lIN = lProcessFriends(state, index, length, result) lIN = lProcessFriends(state, index, length, result)
lOUT += [{"FRIENDS": lIN}]; aOUT.update({"FRIENDS": lIN}) lOUT += [{label: lIN}]; aOUT.update({label: lIN})
elif data_type == MESSENGER_STATE_TYPE_NAME: elif data_type == MESSENGER_STATE_TYPE_NAME:
name = str(state[index + 8:index + 8 + length], 'utf-8') name = str(state[index + 8:index + 8 + length], 'utf-8')
LOG.info("Nick_name = " +name) LOG.info(f"{label} Nick_name = " +name)
aIN = {"NAME": name} aIN = {"Nick_name": name}
lOUT += [{label: aIN}]; aOUT.update({label: aIN}) lOUT += [{label: aIN}]; aOUT.update({label: aIN})
elif data_type == MESSENGER_STATE_TYPE_STATUSMESSAGE: elif data_type == MESSENGER_STATE_TYPE_STATUSMESSAGE:
mess = str(state[index + 8:index + 8 + length], 'utf-8') mess = str(state[index + 8:index + 8 + length], 'utf-8')
LOG.info(f"StatusMessage = " +mess) LOG.info(f"{label} StatusMessage = " +mess)
aIN = {"Status_message": mess} aIN = {"Status_message": mess}
lOUT += [{"STATUSMESSAGE": aIN}]; aOUT.update({"STATUSMESSAGE": aIN}) lOUT += [{label: aIN}]; aOUT.update({label: aIN})
elif data_type == MESSENGER_STATE_TYPE_STATUS: elif data_type == MESSENGER_STATE_TYPE_STATUS:
# 1 uint8_t status (0 = online, 1 = away, 2 = busy) # 1 uint8_t status (0 = online, 1 = away, 2 = busy)
@ -461,11 +464,19 @@ def process_chunk(index, state):
lOUT += [{"STATUS": aIN}]; aOUT.update({"STATUS": aIN}) lOUT += [{"STATUS": aIN}]; aOUT.update({"STATUS": aIN})
elif data_type == MESSENGER_STATE_TYPE_GROUPS: elif data_type == MESSENGER_STATE_TYPE_GROUPS:
lIN = lProcessGroups(state, index, length, result) if length > 0:
lOUT += [{"GROUPS": lIN}]; aOUT.update({"GROUPS": lIN}) lIN = lProcessGroups(state, index, length, result, label)
else:
lIN = []
LOG.info(f"NO {label}")
lOUT += [{label: lIN}]; aOUT.update({label: lIN})
elif data_type == MESSENGER_STATE_TYPE_TCP_RELAY: elif data_type == MESSENGER_STATE_TYPE_TCP_RELAY:
lIN = lProcessNodeInfo(state, index, length, result, "TCPnode") if length > 0:
lIN = lProcessNodeInfo(state, index, length, result, "TCPnode")
else:
lIN = []
LOG.info(f"NO {label}")
lOUT += [{label: lIN}]; aOUT.update({label: lIN}) lOUT += [{label: lIN}]; aOUT.update({label: lIN})
elif data_type == MESSENGER_STATE_TYPE_PATH_NODE: elif data_type == MESSENGER_STATE_TYPE_PATH_NODE:
@ -487,6 +498,11 @@ def process_chunk(index, state):
LOG.warn("UNRECOGNIZED datatype={datatype}") LOG.warn("UNRECOGNIZED datatype={datatype}")
else: else:
diff = len(bSAVE) - len(bOUT)
if diff:
# if short repacking as we read - tox_profile is padded with nulls
LOG.debug(f"PROCESS_CHUNK bSAVE={len(bSAVE)} bOUT={len(bOUT)} delta={diff}")
LOG.info("END") # That's all folks... LOG.info("END") # That's all folks...
return return
@ -615,7 +631,7 @@ def oMainArgparser(_=None):
parser.add_argument('--output', type=str, default='', parser.add_argument('--output', type=str, default='',
help='Destination for info/decrypt - defaults to stderr') help='Destination for info/decrypt - defaults to stderr')
parser.add_argument('--command', type=str, default='info', parser.add_argument('--command', type=str, default='info',
choices=['info', 'decrypt', 'nodes'], choices=['info', 'decrypt', 'nodes', 'save'],
# required=True, # required=True,
help='Action command - default: info') help='Action command - default: info')
parser.add_argument('--indent', type=int, default=2, parser.add_argument('--indent', type=int, default=2,
@ -641,7 +657,6 @@ def oMainArgparser(_=None):
return parser return parser
if __name__ == '__main__': if __name__ == '__main__':
iTOTAL = 0
lArgv = sys.argv[1:] lArgv = sys.argv[1:]
parser = oMainArgparser() parser = oMainArgparser()
oArgs = parser.parse_args(lArgv) oArgs = parser.parse_args(lArgv)
@ -733,20 +748,29 @@ if __name__ == '__main__':
elif iRet == 0: elif iRet == 0:
LOG.info(f"{oArgs.nodes} iRet={iRet} to {oArgs.output}") LOG.info(f"{oArgs.nodes} iRet={iRet} to {oArgs.output}")
elif oArgs.command == 'info': elif oArgs.command in ['save', 'info']:
bOUT = b'\x00\x00\x00\x00\x1f\x1b\xed\x15' if oArgs.command == 'save':
assert oArgs.output, "--output required for this command"
mark = b'\x00\x00\x00\x00\x1f\x1b\xed\x15'
bOUT = mark
# toxEsave # toxEsave
assert bSAVE[:8] == bOUT, "Not a Tox profile" assert bSAVE[:8] == bOUT, "Not a Tox profile"
iErrs = 0 iErrs = 0
lOUT = []; aOUT = {} lOUT = []; aOUT = {}
process_chunk(len(bOUT), bSAVE) process_chunk(len(bOUT), bSAVE)
if lOUT: if aOUT:
if oArgs.output: if oArgs.output:
oStream = open(oArgs.output, 'wb') oStream = open(oArgs.output, 'wb')
else: else:
oStream = sys.stdout oStream = sys.stdout
if oArgs.info == 'yaml' and yaml:
if oArgs.command == 'save':
oStream.write(bOUT)
elif oArgs.info == 'info':
pass
elif oArgs.info == 'yaml' and yaml:
yaml.dump(aOUT, stream=oStream, indent=oArgs.indent) yaml.dump(aOUT, stream=oStream, indent=oArgs.indent)
oStream.write('\n') oStream.write('\n')
elif oArgs.info == 'json' and json: elif oArgs.info == 'json' and json:
@ -757,8 +781,6 @@ if __name__ == '__main__':
oStream.write('\n') oStream.write('\n')
elif oArgs.info == 'pprint': elif oArgs.info == 'pprint':
pprint(aOUT, stream=oStream, indent=oArgs.indent, width=80) pprint(aOUT, stream=oStream, indent=oArgs.indent, width=80)
elif oArgs.info == 'info':
pass
elif oArgs.info == 'nmap_tcp' and bHAVE_NMAP: elif oArgs.info == 'nmap_tcp' and bHAVE_NMAP:
assert oArgs.output, "--output required for this command" assert oArgs.output, "--output required for this command"
oStream.close() oStream.close()
@ -772,9 +794,6 @@ if __name__ == '__main__':
oStream.close() oStream.close()
vOsSystemNmapUdp(aOUT["PATH_NODE"], oArgs) vOsSystemNmapUdp(aOUT["PATH_NODE"], oArgs)
# were short repacking as we read - 446 bytes missing
LOG.debug(f"len bSAVE={len(bSAVE)} bOUT={len(bOUT)} delta={len(bSAVE) - len(bOUT)} iTOTAL={iTOTAL}")
if oStream and oStream != sys.stdout and oStream != sys.stderr: if oStream and oStream != sys.stdout and oStream != sys.stderr:
oStream.close() oStream.close()