1"""
2  Copyright (c) 2024, The OpenThread Authors.
3  All rights reserved.
4
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7  1. Redistributions of source code must retain the above copyright
8     notice, this list of conditions and the following disclaimer.
9  2. Redistributions in binary form must reproduce the above copyright
10     notice, this list of conditions and the following disclaimer in the
11     documentation and/or other materials provided with the distribution.
12  3. Neither the name of the copyright holder nor the
13     names of its contributors may be used to endorse or promote products
14     derived from this software without specific prior written permission.
15
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  POSSIBILITY OF SUCH DAMAGE.
27"""
28
29from cli.command import Command, CommandResultNone
30from dataset.dataset import ThreadDataset, initial_dataset
31from tlv.dataset_tlv import MeshcopTlvType
32
33
34def handle_dataset_entry_command(type: MeshcopTlvType, args, context):
35    ds: ThreadDataset = context['dataset']
36    if len(args) == 0:
37        ds.get_entry(type).print_content()
38        return CommandResultNone()
39
40    ds.set_entry(type, args)
41    print('Done.')
42    return CommandResultNone()
43
44
45class DatasetHelpCommand(Command):
46
47    def get_help_string(self) -> str:
48        return 'Display help message and return.'
49
50    async def execute_default(self, args, context):
51        indent_width = 4
52        indentation = ' ' * indent_width
53        commands: ThreadDataset = context['commands']
54        ds_command: Command = commands['dataset']
55        print(ds_command.get_help_string())
56        print('Subcommands:')
57        for name, subcommand in ds_command._subcommands.items():
58            print(f'{indentation}{name}')
59            print(f'{indentation}{" " * indent_width}{subcommand.get_help_string()}')
60        return CommandResultNone()
61
62
63class PrintDatasetHexCommand(Command):
64
65    def get_help_string(self) -> str:
66        return 'Print current dataset as a hexadecimal string.'
67
68    async def execute_default(self, args, context):
69        ds: ThreadDataset = context['dataset']
70        print(ds.to_bytes().hex())
71        return CommandResultNone()
72
73
74class ReloadDatasetCommand(Command):
75
76    def get_help_string(self) -> str:
77        return 'Reset dataset to the initial value.'
78
79    async def execute_default(self, args, context):
80        context['dataset'].set_from_bytes(initial_dataset)
81        return CommandResultNone()
82
83
84class ActiveTimestampCommand(Command):
85
86    def get_help_string(self) -> str:
87        return 'View and set ActiveTimestamp seconds. Arguments: [seconds (int)]'
88
89    async def execute_default(self, args, context):
90        return handle_dataset_entry_command(MeshcopTlvType.ACTIVETIMESTAMP, args, context)
91
92
93class PendingTimestampCommand(Command):
94
95    def get_help_string(self) -> str:
96        return 'View and set PendingTimestamp seconds. Arguments: [seconds (int)]'
97
98    async def execute_default(self, args, context):
99        return handle_dataset_entry_command(MeshcopTlvType.PENDINGTIMESTAMP, args, context)
100
101
102class NetworkKeyCommand(Command):
103
104    def get_help_string(self) -> str:
105        return 'View and set NetworkKey. Arguments: [nk (hexstring, len=32)]'
106
107    async def execute_default(self, args, context):
108        return handle_dataset_entry_command(MeshcopTlvType.NETWORKKEY, args, context)
109
110
111class NetworkNameCommand(Command):
112
113    def get_help_string(self) -> str:
114        return 'View and set NetworkName. Arguments: [nn (string, maxlen=16)]'
115
116    async def execute_default(self, args, context):
117        return handle_dataset_entry_command(MeshcopTlvType.NETWORKNAME, args, context)
118
119
120class ExtPanIDCommand(Command):
121
122    def get_help_string(self) -> str:
123        return 'View and set ExtPanID. Arguments: [extpanid (hexstring, len=16)]'
124
125    async def execute_default(self, args, context):
126        return handle_dataset_entry_command(MeshcopTlvType.EXTPANID, args, context)
127
128
129class MeshLocalPrefixCommand(Command):
130
131    def get_help_string(self) -> str:
132        return 'View and set MeshLocalPrefix. Arguments: [mlp (hexstring, len=16)]'
133
134    async def execute_default(self, args, context):
135        return handle_dataset_entry_command(MeshcopTlvType.MESHLOCALPREFIX, args, context)
136
137
138class DelayTimerCommand(Command):
139
140    def get_help_string(self) -> str:
141        return 'View and set DelayTimer delay. Arguments: [delay (int)]'
142
143    async def execute_default(self, args, context):
144        return handle_dataset_entry_command(MeshcopTlvType.DELAYTIMER, args, context)
145
146
147class PanIDCommand(Command):
148
149    def get_help_string(self) -> str:
150        return 'View and set PanID. Arguments: [panid (hexstring, len=4)]'
151
152    async def execute_default(self, args, context):
153        return handle_dataset_entry_command(MeshcopTlvType.PANID, args, context)
154
155
156class ChannelCommand(Command):
157
158    def get_help_string(self) -> str:
159        return 'View and set Channel. Arguments: [channel (int)]'
160
161    async def execute_default(self, args, context):
162        return handle_dataset_entry_command(MeshcopTlvType.CHANNEL, args, context)
163
164
165class ChannelMaskCommand(Command):
166
167    def get_help_string(self) -> str:
168        return 'View and set ChannelMask. Arguments: [mask (hexstring)]'
169
170    async def execute_default(self, args, context):
171        return handle_dataset_entry_command(MeshcopTlvType.CHANNELMASK, args, context)
172
173
174class PskcCommand(Command):
175
176    def get_help_string(self) -> str:
177        return 'View and set Pskc. Arguments: [pskc (hexstring, maxlen=32)]'
178
179    async def execute_default(self, args, context):
180        return handle_dataset_entry_command(MeshcopTlvType.PSKC, args, context)
181
182
183class SecurityPolicyCommand(Command):
184
185    def get_help_string(self) -> str:
186        return 'View and set SecurityPolicy. Arguments: '\
187               '[<rotation_time (int)> [flags (string)] [version_threshold (int)]]'
188
189    async def execute_default(self, args, context):
190        return handle_dataset_entry_command(MeshcopTlvType.SECURITYPOLICY, args, context)
191
192
193class DatasetCommand(Command):
194
195    def __init__(self):
196        self._subcommands = {
197            'help': DatasetHelpCommand(),
198            'hex': PrintDatasetHexCommand(),
199            'reload': ReloadDatasetCommand(),
200            'activetimestamp': ActiveTimestampCommand(),
201            'pendingtimestamp': PendingTimestampCommand(),
202            'networkkey': NetworkKeyCommand(),
203            'networkname': NetworkNameCommand(),
204            'extpanid': ExtPanIDCommand(),
205            'meshlocalprefix': MeshLocalPrefixCommand(),
206            'delay': DelayTimerCommand(),
207            'panid': PanIDCommand(),
208            'channel': ChannelCommand(),
209            'channelmask': ChannelMaskCommand(),
210            'pskc': PskcCommand(),
211            'securitypolicy': SecurityPolicyCommand()
212        }
213
214    def get_help_string(self) -> str:
215        return 'View and manipulate current dataset. ' \
216            'Call without parameters to show current dataset.'
217
218    async def execute_default(self, args, context):
219        ds: ThreadDataset = context['dataset']
220        ds.print_content()
221        return CommandResultNone()
222