Skip to content

Commit ce7c618

Browse files
committed
Merge branch 'master' into is-unmessageable
2 parents 22b3062 + db21942 commit ce7c618

File tree

5 files changed

+210
-3
lines changed

5 files changed

+210
-3
lines changed

meshtastic/__main__.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,18 @@ def onConnected(interface):
342342
if args.set_owner or args.set_owner_short or args.set_is_unmessageable or args.set_is_unmessagable:
343343
closeNow = True
344344
waitForAckNak = True
345+
346+
# Validate owner names before connecting to device
347+
if args.set_owner is not None:
348+
stripped_long_name = args.set_owner.strip()
349+
if not stripped_long_name:
350+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
351+
352+
if hasattr(args, 'set_owner_short') and args.set_owner_short is not None:
353+
stripped_short_name = args.set_owner_short.strip()
354+
if not stripped_short_name:
355+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
356+
345357
if args.set_owner and args.set_owner_short:
346358
print(f"Setting device owner to {args.set_owner} and short name to {args.set_owner_short}")
347359
elif args.set_owner:
@@ -417,6 +429,8 @@ def onConnected(interface):
417429
print(" ".join(fieldNames))
418430

419431
if args.set_ham:
432+
if not args.set_ham.strip():
433+
meshtastic.util.our_exit("ERROR: Ham radio callsign cannot be empty or contain only whitespace characters")
420434
closeNow = True
421435
print(f"Setting Ham ID to {args.set_ham} and turning off encryption")
422436
interface.getNode(args.dest, **getNode_kwargs).setOwner(args.set_ham, is_licensed=True)
@@ -659,12 +673,20 @@ def onConnected(interface):
659673
interface.getNode(args.dest, False, **getNode_kwargs).beginSettingsTransaction()
660674

661675
if "owner" in configuration:
676+
# Validate owner name before setting
677+
owner_name = str(configuration["owner"]).strip()
678+
if not owner_name:
679+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
662680
print(f"Setting device owner to {configuration['owner']}")
663681
waitForAckNak = True
664682
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(configuration["owner"])
665683
time.sleep(0.5)
666684

667685
if "owner_short" in configuration:
686+
# Validate owner short name before setting
687+
owner_short_name = str(configuration["owner_short"]).strip()
688+
if not owner_short_name:
689+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
668690
print(
669691
f"Setting device owner short to {configuration['owner_short']}"
670692
)
@@ -675,6 +697,10 @@ def onConnected(interface):
675697
time.sleep(0.5)
676698

677699
if "ownerShort" in configuration:
700+
# Validate owner short name before setting
701+
owner_short_name = str(configuration["ownerShort"]).strip()
702+
if not owner_short_name:
703+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
678704
print(
679705
f"Setting device owner short to {configuration['ownerShort']}"
680706
)
@@ -1111,6 +1137,7 @@ def export_config(interface) -> str:
11111137
configObj["location"]["alt"] = alt
11121138

11131139
config = MessageToDict(interface.localNode.localConfig) #checkme - Used as a dictionary here and a string below
1140+
#was used as a string here and a Dictionary above
11141141
if config:
11151142
# Convert inner keys to correct snake/camelCase
11161143
prefs = {}
@@ -1205,6 +1232,22 @@ def common():
12051232
meshtastic.util.support_info()
12061233
meshtastic.util.our_exit("", 0)
12071234

1235+
# Early validation for owner names before attempting device connection
1236+
if hasattr(args, 'set_owner') and args.set_owner is not None:
1237+
stripped_long_name = args.set_owner.strip()
1238+
if not stripped_long_name:
1239+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
1240+
1241+
if hasattr(args, 'set_owner_short') and args.set_owner_short is not None:
1242+
stripped_short_name = args.set_owner_short.strip()
1243+
if not stripped_short_name:
1244+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
1245+
1246+
if hasattr(args, 'set_ham') and args.set_ham is not None:
1247+
stripped_ham_name = args.set_ham.strip()
1248+
if not stripped_ham_name:
1249+
meshtastic.util.our_exit("ERROR: Ham radio callsign cannot be empty or contain only whitespace characters")
1250+
12081251
if have_powermon:
12091252
create_power_meter()
12101253

meshtastic/node.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,16 @@ def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None
307307
nChars = 4
308308
if long_name is not None:
309309
long_name = long_name.strip()
310+
# Validate that long_name is not empty or whitespace-only
311+
if not long_name:
312+
our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
310313
p.set_owner.long_name = long_name
311314
p.set_owner.is_licensed = is_licensed
312315
if short_name is not None:
313316
short_name = short_name.strip()
317+
# Validate that short_name is not empty or whitespace-only
318+
if not short_name:
319+
our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
314320
if len(short_name) > nChars:
315321
short_name = short_name[:nChars]
316322
print(f"Maximum is 4 characters, truncated to {short_name}")

meshtastic/tests/test_main.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,3 +2744,91 @@ def test_remove_ignored_node():
27442744
main()
27452745

27462746
mocked_node.removeIgnored.assert_called_once_with("!12345678")
2747+
@pytest.mark.unit
2748+
@pytest.mark.usefixtures("reset_mt_config")
2749+
def test_main_set_owner_whitespace_only(capsys):
2750+
"""Test --set-owner with whitespace-only name"""
2751+
sys.argv = ["", "--set-owner", " "]
2752+
mt_config.args = sys.argv
2753+
2754+
with pytest.raises(SystemExit) as excinfo:
2755+
main()
2756+
2757+
out, _ = capsys.readouterr()
2758+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
2759+
assert excinfo.value.code == 1
2760+
2761+
2762+
@pytest.mark.unit
2763+
@pytest.mark.usefixtures("reset_mt_config")
2764+
def test_main_set_owner_empty_string(capsys):
2765+
"""Test --set-owner with empty string"""
2766+
sys.argv = ["", "--set-owner", ""]
2767+
mt_config.args = sys.argv
2768+
2769+
with pytest.raises(SystemExit) as excinfo:
2770+
main()
2771+
2772+
out, _ = capsys.readouterr()
2773+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
2774+
assert excinfo.value.code == 1
2775+
2776+
2777+
@pytest.mark.unit
2778+
@pytest.mark.usefixtures("reset_mt_config")
2779+
def test_main_set_owner_short_whitespace_only(capsys):
2780+
"""Test --set-owner-short with whitespace-only name"""
2781+
sys.argv = ["", "--set-owner-short", " "]
2782+
mt_config.args = sys.argv
2783+
2784+
with pytest.raises(SystemExit) as excinfo:
2785+
main()
2786+
2787+
out, _ = capsys.readouterr()
2788+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
2789+
assert excinfo.value.code == 1
2790+
2791+
2792+
@pytest.mark.unit
2793+
@pytest.mark.usefixtures("reset_mt_config")
2794+
def test_main_set_owner_short_empty_string(capsys):
2795+
"""Test --set-owner-short with empty string"""
2796+
sys.argv = ["", "--set-owner-short", ""]
2797+
mt_config.args = sys.argv
2798+
2799+
with pytest.raises(SystemExit) as excinfo:
2800+
main()
2801+
2802+
out, _ = capsys.readouterr()
2803+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
2804+
assert excinfo.value.code == 1
2805+
2806+
2807+
@pytest.mark.unit
2808+
@pytest.mark.usefixtures("reset_mt_config")
2809+
def test_main_set_ham_whitespace_only(capsys):
2810+
"""Test --set-ham with whitespace-only name"""
2811+
sys.argv = ["", "--set-ham", " "]
2812+
mt_config.args = sys.argv
2813+
2814+
with pytest.raises(SystemExit) as excinfo:
2815+
main()
2816+
2817+
out, _ = capsys.readouterr()
2818+
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
2819+
assert excinfo.value.code == 1
2820+
2821+
2822+
@pytest.mark.unit
2823+
@pytest.mark.usefixtures("reset_mt_config")
2824+
def test_main_set_ham_empty_string(capsys):
2825+
"""Test --set-ham with empty string"""
2826+
sys.argv = ["", "--set-ham", ""]
2827+
mt_config.args = sys.argv
2828+
2829+
with pytest.raises(SystemExit) as excinfo:
2830+
main()
2831+
2832+
out, _ = capsys.readouterr()
2833+
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
2834+
assert excinfo.value.code == 1

meshtastic/tests/test_node.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,8 +1254,7 @@ def test_requestChannels_non_localNode_starting_index(caplog):
12541254
# },
12551255
# 'id': 1692918436,
12561256
# 'hopLimit': 3,
1257-
# 'priority':
1258-
# 'RELIABLE',
1257+
# 'priority': 'RELIABLE',
12591258
# 'raw': 'fake',
12601259
# 'fromId': '!9388f81c',
12611260
# 'toId': '!9388f81c'
@@ -1480,6 +1479,77 @@ def test_remove_ignored(ignored):
14801479
iface.sendData.assert_called_once()
14811480

14821481

1482+
@pytest.mark.unit
1483+
def test_setOwner_whitespace_only_long_name(capsys):
1484+
"""Test setOwner with whitespace-only long name"""
1485+
iface = MagicMock(autospec=MeshInterface)
1486+
anode = Node(iface, 123, noProto=True)
1487+
1488+
with pytest.raises(SystemExit) as excinfo:
1489+
anode.setOwner(long_name=" ")
1490+
1491+
out, _ = capsys.readouterr()
1492+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
1493+
assert excinfo.value.code == 1
1494+
1495+
1496+
@pytest.mark.unit
1497+
def test_setOwner_empty_long_name(capsys):
1498+
"""Test setOwner with empty long name"""
1499+
iface = MagicMock(autospec=MeshInterface)
1500+
anode = Node(iface, 123, noProto=True)
1501+
1502+
with pytest.raises(SystemExit) as excinfo:
1503+
anode.setOwner(long_name="")
1504+
1505+
out, _ = capsys.readouterr()
1506+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
1507+
assert excinfo.value.code == 1
1508+
1509+
1510+
@pytest.mark.unit
1511+
def test_setOwner_whitespace_only_short_name(capsys):
1512+
"""Test setOwner with whitespace-only short name"""
1513+
iface = MagicMock(autospec=MeshInterface)
1514+
anode = Node(iface, 123, noProto=True)
1515+
1516+
with pytest.raises(SystemExit) as excinfo:
1517+
anode.setOwner(short_name=" ")
1518+
1519+
out, _ = capsys.readouterr()
1520+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
1521+
assert excinfo.value.code == 1
1522+
1523+
1524+
@pytest.mark.unit
1525+
def test_setOwner_empty_short_name(capsys):
1526+
"""Test setOwner with empty short name"""
1527+
iface = MagicMock(autospec=MeshInterface)
1528+
anode = Node(iface, 123, noProto=True)
1529+
1530+
with pytest.raises(SystemExit) as excinfo:
1531+
anode.setOwner(short_name="")
1532+
1533+
out, _ = capsys.readouterr()
1534+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
1535+
assert excinfo.value.code == 1
1536+
1537+
1538+
@pytest.mark.unit
1539+
def test_setOwner_valid_names(caplog):
1540+
"""Test setOwner with valid names"""
1541+
iface = MagicMock(autospec=MeshInterface)
1542+
anode = Node(iface, 123, noProto=True)
1543+
1544+
with caplog.at_level(logging.DEBUG):
1545+
anode.setOwner(long_name="ValidName", short_name="VN")
1546+
1547+
# Should not raise any exceptions
1548+
# Note: When noProto=True, _sendAdmin is not called as the method returns early
1549+
assert re.search(r'p.set_owner.long_name:ValidName:', caplog.text, re.MULTILINE)
1550+
assert re.search(r'p.set_owner.short_name:VN:', caplog.text, re.MULTILINE)
1551+
1552+
14831553
# TODO
14841554
# @pytest.mark.unitslow
14851555
# def test_waitForConfig():

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "meshtastic"
3-
version = "2.6.3"
3+
version = "2.6.4"
44
description = "Python API & client shell for talking to Meshtastic devices"
55
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
66
license = "GPL-3.0-only"

0 commit comments

Comments
 (0)