| Line 30... |
Line 30... |
| 30 |
# It allows adding, editing, and removing router configurations stored in a JSON file.
|
30 |
# It allows adding, editing, and removing router configurations stored in a JSON file.
|
| 31 |
#
|
31 |
#
|
| 32 |
# Change History:
|
32 |
# Change History:
|
| 33 |
# v1.0.0 2025-10-01 RWR
|
33 |
# v1.0.0 2025-10-01 RWR
|
| 34 |
# Initial version
|
34 |
# Initial version
|
| - |
|
35 |
# v1.1.0 2026-01-04 RWR
|
| - |
|
36 |
# Added formats key to router configuration
|
| - |
|
37 |
# Added editFormats subroutine to manage formats hash structure
|
| - |
|
38 |
# Formats contain arrays of hashes with filename and additionalStrings keys
|
| 35 |
|
39 |
|
| 36 |
use strict;
|
40 |
use strict;
|
| 37 |
use warnings;
|
41 |
use warnings;
|
| 38 |
|
42 |
|
| 39 |
# use libraries from the directory this script is in
|
43 |
# use libraries from the directory this script is in
|
| Line 48... |
Line 52... |
| 48 |
|
52 |
|
| 49 |
use JSON qw( decode_json encode_json );
|
53 |
use JSON qw( decode_json encode_json );
|
| 50 |
use Data::Dumper;
|
54 |
use Data::Dumper;
|
| 51 |
use opnsense;
|
55 |
use opnsense;
|
| 52 |
|
56 |
|
| 53 |
our $VERSION = "1.0.0";
|
57 |
our $VERSION = "1.1.0";
|
| 54 |
|
58 |
|
| 55 |
# Default config file name if not specified on command line. Defaults to 'routers.json' in the script directory
|
59 |
# Default config file name if not specified on command line. Defaults to 'routers.json' in the script directory
|
| 56 |
my $configFileName = $FindBin::RealBin . '/routers.json';
|
60 |
my $configFileName = $FindBin::RealBin . '/routers.json';
|
| 57 |
# fields used in the router configuration
|
61 |
# fields used in the router configuration
|
| 58 |
my @fields = ( 'url', 'apiKey', 'apiSecret', 'ovpnIndex', 'template', 'hostname', 'localPort', 'downloadToken' );
|
62 |
my @fields = ( 'url', 'apiKey', 'apiSecret', 'ovpnIndex', 'template', 'hostname', 'localPort', 'downloadToken', 'formats' );
|
| 59 |
|
63 |
|
| 60 |
# Check if running as root
|
64 |
# Check if running as root
|
| 61 |
if ($> != 0) {
|
65 |
if ($> != 0) {
|
| 62 |
die "Error: This script must be run as root.\n";
|
66 |
die "Error: This script must be run as root.\n";
|
| 63 |
}
|
67 |
}
|
| Line 168... |
Line 172... |
| 168 |
}
|
172 |
}
|
| 169 |
return undef;
|
173 |
return undef;
|
| 170 |
}
|
174 |
}
|
| 171 |
|
175 |
|
| 172 |
#-----------------------------
|
176 |
#-----------------------------
|
| - |
|
177 |
# editFormats: Edit the formats hash for a router
|
| - |
|
178 |
# formats - reference to the router's formats hash
|
| - |
|
179 |
# Each format is a hash key pointing to a hashref with 'filename' and 'additionalStrings' keys
|
| - |
|
180 |
# returns the updated formats hash reference
|
| - |
|
181 |
#-----------------------------
|
| - |
|
182 |
sub editFormats {
|
| - |
|
183 |
my ( $formats ) = @_;
|
| - |
|
184 |
$formats = {} unless ref($formats) eq 'HASH';
|
| - |
|
185 |
my $option = '';
|
| - |
|
186 |
while ( $option ne 'Done' ) {
|
| - |
|
187 |
my @formatNames = sort keys %$formats;
|
| - |
|
188 |
print "\nCurrent formats:\n";
|
| - |
|
189 |
if ( @formatNames ) {
|
| - |
|
190 |
for my $name ( @formatNames ) {
|
| - |
|
191 |
my $format = $formats->{$name};
|
| - |
|
192 |
my $fname = ref($format) eq 'HASH' ? ($format->{'filename'} || '(no filename)') : '(invalid)';
|
| - |
|
193 |
my $addStr = ref($format) eq 'HASH' ? ($format->{'additionalStrings'} || '') : '';
|
| - |
|
194 |
print " $name: $fname" . ($addStr ? " [strings: $addStr]" : "") . "\n";
|
| - |
|
195 |
}
|
| - |
|
196 |
} else {
|
| - |
|
197 |
print " (no formats defined)\n";
|
| - |
|
198 |
}
|
| - |
|
199 |
$option = &menuSelect(
|
| - |
|
200 |
"Manage formats",
|
| - |
|
201 |
[ @formatNames, 'Add new format', 'Done' ]
|
| - |
|
202 |
);
|
| - |
|
203 |
if ( $option eq 'Add new format' ) {
|
| - |
|
204 |
print "Enter format name: ";
|
| - |
|
205 |
my $formatName = <STDIN>;
|
| - |
|
206 |
chomp $formatName;
|
| - |
|
207 |
$formatName = trim($formatName);
|
| - |
|
208 |
if ( $formatName ne '' ) {
|
| - |
|
209 |
$formats->{$formatName} = {} unless exists $formats->{$formatName};
|
| - |
|
210 |
print "Format '$formatName' created\n";
|
| - |
|
211 |
}
|
| - |
|
212 |
} elsif ( $option ne 'Done' ) {
|
| - |
|
213 |
# Edit existing format
|
| - |
|
214 |
my $formatName = $option;
|
| - |
|
215 |
my $format = $formats->{$formatName};
|
| - |
|
216 |
$format = {} unless ref($format) eq 'HASH';
|
| - |
|
217 |
my $action = '';
|
| - |
|
218 |
while ( $action ne 'Back' ) {
|
| - |
|
219 |
print "\nFormat '$formatName':\n";
|
| - |
|
220 |
print " filename: " . ($format->{'filename'} || '(not set)') . "\n";
|
| - |
|
221 |
print " additionalStrings: " . ($format->{'additionalStrings'} || '(not set)') . "\n";
|
| - |
|
222 |
$action = &menuSelect( "Edit format '$formatName'", [ 'Change filename', 'Change additionalStrings', 'Delete format', 'Back' ] );
|
| - |
|
223 |
if ( $action eq 'Change filename' ) {
|
| - |
|
224 |
print "Enter new filename (ROUTER and USER are replaced with actual values): ";
|
| - |
|
225 |
my $filename = <STDIN>;
|
| - |
|
226 |
chomp $filename;
|
| - |
|
227 |
$format->{'filename'} = trim($filename);
|
| - |
|
228 |
} elsif ( $action eq 'Change additionalStrings' ) {
|
| - |
|
229 |
print "Enter new additionalStrings (\\n separated): ";
|
| - |
|
230 |
my $addStr = <STDIN>;
|
| - |
|
231 |
chomp $addStr;
|
| - |
|
232 |
$format->{'additionalStrings'} = trim($addStr);
|
| - |
|
233 |
} elsif ( $action eq 'Delete format' ) {
|
| - |
|
234 |
my $confirm = &menuSelect( "Are you sure you want to delete format '$formatName'?", [ 'No', 'Yes' ] );
|
| - |
|
235 |
if ( $confirm eq 'Yes' ) {
|
| - |
|
236 |
delete $formats->{$formatName};
|
| - |
|
237 |
print "Format '$formatName' deleted\n";
|
| - |
|
238 |
last;
|
| - |
|
239 |
}
|
| - |
|
240 |
}
|
| - |
|
241 |
}
|
| - |
|
242 |
$formats->{$formatName} = $format unless $action eq 'Delete format';
|
| - |
|
243 |
}
|
| - |
|
244 |
}
|
| - |
|
245 |
return $formats;
|
| - |
|
246 |
}
|
| - |
|
247 |
|
| - |
|
248 |
#-----------------------------
|
| 173 |
# editRouter: Edit the configuration for a router
|
249 |
# editRouter: Edit the configuration for a router
|
| 174 |
# config - reference to the router's configuration hash
|
250 |
# config - reference to the router's configuration hash
|
| 175 |
# routerName - name of the router being edited
|
251 |
# routerName - name of the router being edited
|
| 176 |
# keys - reference to an array of keys (fields) that can be edited
|
252 |
# keys - reference to an array of keys (fields) that can be edited
|
| 177 |
# works on one router at a time, allows setting fields for that router
|
253 |
# works on one router at a time, allows setting fields for that router
|
| Line 192... |
Line 268... |
| 192 |
[ map { defined $currentRouter->{$_} ? " (current: $currentRouter->{$_})" : " (not set)" } @$keys, '' ]
|
268 |
[ map { defined $currentRouter->{$_} ? " (current: $currentRouter->{$_})" : " (not set)" } @$keys, '' ]
|
| 193 |
);
|
269 |
);
|
| 194 |
if ( $option eq 'ovpnIndex' ) {
|
270 |
if ( $option eq 'ovpnIndex' ) {
|
| 195 |
$currentRouter->{'ovpnIndex'} = &selectOvpnIndex( $currentRouter );
|
271 |
$currentRouter->{'ovpnIndex'} = &selectOvpnIndex( $currentRouter );
|
| 196 |
$saved = 0; # mark as unsaved
|
272 |
$saved = 0; # mark as unsaved
|
| - |
|
273 |
} elsif ( $option eq 'formats' ) {
|
| - |
|
274 |
$currentRouter->{'formats'} = &editFormats( $currentRouter->{'formats'} );
|
| - |
|
275 |
$saved = 0; # mark as unsaved
|
| 197 |
} elsif ( $option eq 'Save Config' ) {
|
276 |
} elsif ( $option eq 'Save Config' ) {
|
| 198 |
writeConfig( $configFileName, $config );
|
277 |
writeConfig( $configFileName, $config );
|
| 199 |
$saved = 1; # mark as saved
|
278 |
$saved = 1; # mark as saved
|
| 200 |
print "Configuration saved to $configFileName\n";
|
279 |
print "Configuration saved to $configFileName\n";
|
| 201 |
} elsif ($option eq 'Done') {
|
280 |
} elsif ($option eq 'Done') {
|