rename files on SD card via List Files

This commit is contained in:
scgbckbone 2025-06-19 18:33:19 +02:00 committed by doc-hex
parent 4d2349fef4
commit 3353f3d4a4
3 changed files with 84 additions and 13 deletions

View File

@ -8,6 +8,7 @@ This lists the new changes that have not yet been published in a normal release.
- Enhancement: Add warning for zero value outputs if not `OP_RETURN`
- Enhancement: Show QR codes of output addresses in transaction output explorer. Explorer is
now offered for transactions of all sizes, not just complex ones.
Enhancement: Add ability to rename files on SD card via `Advanced/Tools -> File Management -> List Files`
- Bugfix: If all change outputs have `nValue=0` they were not shown in UX.
- Bugfix: Disallow negative input/output amounts in PSBT.
- Bugfix: Fix filesystem initialization after Wife LFS or Destroy Seed.

View File

@ -1683,30 +1683,39 @@ async def list_files(*A):
from pincodes import pa
digest = chk.digest()
basename = fn.rsplit('/', 1)[-1]
msg_base = 'SHA256(%s)\n\n%s\n\nPress ' % (basename, B2A(digest))
escape = "6"
path, basename = fn.rsplit('/', 1)
msg_base = 'SHA256(%s)\n\n' + B2A(digest) + '\n\nPress (1) to rename file, '
escape = "61"
if pa.has_secrets():
msg_sign = '(4) to sign file digest and export detached signature, '
msg_base += '(4) to sign file digest and export detached signature, '
escape += "4"
else:
msg_sign = ""
msg_delete = '(6) to delete.'
msg = msg_base + msg_sign + msg_delete
msg_base += '(6) to delete.'
while True:
ch = await ux_show_story(msg, escape=escape)
ch = await ux_show_story(msg_base % basename, escape=escape)
if ch == "x": break
if ch in '46':
if ch in '461':
with CardSlot() as card:
if ch == '6':
card.securely_blank_file(fn)
break
elif ch == '1':
new_basename = await ux_input_text(basename, max_len=32, min_len=3)
if new_basename:
try:
# prohibit both slashes and space in filenames
for s in "\/ ":
assert s not in new_basename, "illegal char"
uos.rename(path + "/" + basename, path + "/" + new_basename)
basename = new_basename
except Exception as e:
await ux_show_story("Failed to rename the file. " + str(e),
title="Failure")
else:
from msgsign import write_sig_file
sig_nice = write_sig_file([(digest, fn)])
await ux_show_story("Signature file %s written." % sig_nice)
msg = msg_base + msg_delete
return
async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None,

View File

@ -2,7 +2,7 @@
#
import pytest, time, os, re, hashlib, shutil
from helpers import xfp2str, prandom
from charcodes import KEY_DOWN, KEY_QR, KEY_NFC, KEY_DELETE
from charcodes import KEY_DOWN, KEY_QR, KEY_NFC, KEY_DELETE, KEY_CANCEL
from constants import AF_CLASSIC, simulator_fixed_words, simulator_fixed_xfp
from mnemonic import Mnemonic
from bip32 import BIP32Node
@ -818,7 +818,6 @@ def test_sign_file_from_list_files(f_len, goto_home, cap_story, pick_menu_item,
verify_detached_signature_file([fname], signame, "sd", AF_CLASSIC)
time.sleep(0.1)
_, story = cap_story()
assert "(4) to sign file digest and export detached signature" not in story
assert "(6) to delete" in story
@ -828,6 +827,68 @@ def test_sign_file_from_list_files(f_len, goto_home, cap_story, pick_menu_item,
assert "List Files" in menu
def test_rename_from_list_files(goto_home, cap_story, pick_menu_item, need_keypress, is_q1,
microsd_path, press_select, cap_screen, enter_complex):
def clear(fname):
for i in range(len(fname)):
if not is_q1 and not i:
# Mk4 different menu entry UX
continue
need_keypress(KEY_DELETE if is_q1 else "x")
time.sleep(0.01)
fname = "file_to_rename.pdf"
fpath = microsd_path(fname)
contents = os.urandom(64)
digest = hashlib.sha256(contents).digest().hex()
with open(fpath, "wb") as f:
f.write(contents)
goto_home()
pick_menu_item("Advanced/Tools")
pick_menu_item('File Management')
pick_menu_item('List Files')
time.sleep(0.1)
pick_menu_item(fname)
time.sleep(0.1)
_, story = cap_story()
assert f"SHA256({fname})" in story
assert digest in story
assert "Press (1) to rename file" in story
need_keypress("1")
time.sleep(0.1)
if is_q1:
scr = cap_screen()
assert fname in scr
clear(fname)
bad_fnames = ["renamed file.txt", "/sd/renamed_file.txt", "renamed\\file.txt"]
for bad in bad_fnames:
enter_complex(bad, b39pass=False)
time.sleep(.1)
title, story = cap_story()
assert title == "Failure"
assert "Failed to rename the file" in story
assert "illegal char" in story
press_select()
time.sleep(.1)
need_keypress("1") # rename again
time.sleep(.1)
clear(fname)
if not is_q1:
need_keypress("1") # toggle case back to upper (enter complex expect to start in that state)
new_fname = "renamed_file.txt"
enter_complex(new_fname, b39pass=False)
time.sleep(.1)
_, story = cap_story()
assert f"SHA256({new_fname})" in story
assert digest in story
assert not os.path.exists(fpath)
assert os.path.exists(microsd_path(new_fname))
def test_bip39_pw_signing_xfp_ux(pick_menu_item, press_select, cap_story, enter_complex,
reset_seed_words, cap_menu, go_to_passphrase, microsd_wipe):
microsd_wipe() # need to wipe all PSBT on SD card so we do not proceed to signing