From 7ce22da3eaf41a4a96b7a38154ead93b9d9ab70e Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Thu, 21 May 2026 20:48:37 +0200 Subject: [PATCH] Userlist --- CMakeLists.txt | 22 + apply.pl | 181 +++++++- cupsutils.cmake | 64 +++ settings.pl | 950 +++++++++++++++++++++++++++++++++++---- smart.conf.cmake | 4 + smart_userlist.c | 171 +++++++ static/icon-configh.svg | 17 + static/icon-devices.svg | 19 + static/icon-dirs.svg | 18 + static/icon-general.svg | 18 + static/icon-groups.svg | 18 + static/icon-logging.svg | 19 + static/icon-queues.svg | 20 + static/icon-security.svg | 17 + static/icon-service.svg | 23 + static/icon-smart.svg | 18 + static/icon-start.svg | 22 + static/icon-stations.svg | 19 + static/icon-susers.svg | 18 + static/icon-users.svg | 19 + static/icon-volumes.svg | 19 + static/menu.html | 441 ++++++++++++++---- 22 files changed, 1924 insertions(+), 193 deletions(-) create mode 100644 cupsutils.cmake create mode 100644 smart_userlist.c create mode 100644 static/icon-configh.svg create mode 100644 static/icon-devices.svg create mode 100644 static/icon-dirs.svg create mode 100644 static/icon-general.svg create mode 100644 static/icon-groups.svg create mode 100644 static/icon-logging.svg create mode 100644 static/icon-queues.svg create mode 100644 static/icon-security.svg create mode 100644 static/icon-service.svg create mode 100644 static/icon-smart.svg create mode 100644 static/icon-start.svg create mode 100644 static/icon-stations.svg create mode 100644 static/icon-susers.svg create mode 100644 static/icon-users.svg create mode 100644 static/icon-volumes.svg diff --git a/CMakeLists.txt b/CMakeLists.txt index c6ef08e..07f3106 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ INCLUDE_DIRECTORIES( add_executable(nwwebui nwwebui.c) add_executable(check_login check_login.c) +add_executable(smart_userlist smart_userlist.c) ################################# # Linking @@ -86,6 +87,11 @@ target_link_libraries(check_login ${DL_LIBRARY} ) +target_link_libraries(smart_userlist + ${PAM_LIBRARY} + ${DL_LIBRARY} +) + ################################# # Install Files ############## @@ -108,12 +114,28 @@ install(FILES static/favicon.ico DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR} install(FILES static/favicon-32x32.png DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) install(FILES static/favicon-16x16.png DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) install(FILES static/apple-touch-icon.png DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-start.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-service.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-general.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-dirs.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-configh.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-security.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-susers.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-volumes.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-devices.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-logging.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-smart.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-stations.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-users.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-groups.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) +install(FILES static/icon-queues.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static) if(WITH_SYSTEMD) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mars-nwe-webui.service DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR}) endif() install(TARGETS check_login DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}) +install(TARGETS smart_userlist DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}) install(TARGETS nwwebui DESTINATION ${CMAKE_INSTALL_SBINDIR}) diff --git a/apply.pl b/apply.pl index ab32dc5..8590a55 100644 --- a/apply.pl +++ b/apply.pl @@ -22,12 +22,54 @@ # # +sub read_smart_bindery_values() +{ + my( $bind_server, $bind_user, $bind_pass ) = ( '', '', '' ); + + if( open( my $fh, '<' . $smart_nwclient_path ) ) + { + my $line = <$fh>; + close( $fh ); + + chomp( $line ) if defined( $line ); + ( $bind_server, $bind_user, $bind_pass ) = split( '[/ ]', $line ) if defined( $line ); + } + + $bind_server = '' unless defined( $bind_server ); + $bind_user = '' unless defined( $bind_user ); + $bind_pass = '' unless defined( $bind_pass ); + + return( $bind_server, $bind_user, $bind_pass ); +} + +sub write_smart_bindery_values( $$$ ) +{ + my( $bind_server, $bind_user, $bind_pass ) = @_; + + $bind_server = '' unless defined( $bind_server ); + $bind_user = '' unless defined( $bind_user ); + $bind_pass = '' unless defined( $bind_pass ); + + open( my $fh, '>' . $smart_nwclient_path ) or die "Could not open $smart_nwclient_path: $!"; + print( $fh $bind_server . '/' . $bind_user . ' ' . $bind_pass . "\n" ); + close( $fh ); + + chown( scalar( getpwnam( $nonroot_user ) ), 0, $smart_nwclient_path ); + chmod( 0600, $smart_nwclient_path ); +} + + sub handle_request() { if( $c[1] eq 'general' ) { setc( 2, server_name ); setc( 3, internal_net ); + if( defined( $p{sync_smart_bind_server} ) && $p{sync_smart_bind_server} eq 'on' && defined( $p{server_name} ) && $p{server_name} ne '' ) + { + my( $old_bind_server, $old_bind_user, $old_bind_pass ) = read_smart_bindery_values(); + write_smart_bindery_values( $p{server_name}, $old_bind_user, $old_bind_pass ); + } setc( 16, test ); setc( 210, timing_down ); setc( 211, timing_warn ); @@ -109,10 +151,6 @@ sub handle_request() { delconfigline( '4 ' . $c[2] ); } - if( defined( $p{interface_manual} ) && $p{interface_manual} ne '' ) - { - $p{interface} = $p{interface_manual}; - } $p{interface} =~ s/[^-_\.A-Za-z0-9:\*]//g; if( $p{number} ne '' ) { @@ -148,12 +186,13 @@ sub handle_request() close( FILE ); } - open( FILE, '>' . $smart_nwclient_path ) or die "Could not open $smart_nwclient_path: $!"; - print( FILE $p{bind_server} . '/' . $p{bind_user} . ' ' . $p{bind_pass } . "\n" ); - close( FILE ); - - chown( scalar( getpwnam( $nonroot_user ) ), 0, $smart_nwclient_path ); - chmod( 0600, $smart_nwclient_path ); + write_smart_bindery_values( $p{bind_server}, $p{bind_user}, $p{bind_pass} ); + + if( defined( $p{sync_general_server_name} ) && $p{sync_general_server_name} eq 'on' && defined( $p{bind_server} ) && $p{bind_server} ne '' ) + { + $p{server_name} = $p{bind_server}; + setc( 2, server_name ); + } } elsif( $c[1] eq 'users' ) { @@ -355,6 +394,92 @@ EOF redirect( '/settings/queues' ); } + + elsif( $c[1] eq 'advanced' ) + { + my $advanced_category = $p{advanced_category}; + $advanced_category = 'all' if ! defined( $advanced_category ) || $advanced_category eq ''; + + my $save_devices = ( $advanced_category eq 'all' || $advanced_category eq 'devices' ); + my $save_security = ( $advanced_category eq 'all' || $advanced_category eq 'security' ); + my $save_users = ( $advanced_category eq 'all' || $advanced_category eq 'users' ); + my $save_queues = ( $advanced_category eq 'all' || $advanced_category eq 'queues' ); + my $save_configh = ( $advanced_category eq 'all' || $advanced_category eq 'configh' ); + my $save_stations = ( $advanced_category eq 'all' || $advanced_category eq 'stations' ); + my $save_network = ( $advanced_category eq 'all' || $advanced_category eq 'network' ); + + if( $save_devices ) + { + my $device_flags = 0; + $device_flags += 1 if $p{dev_keep} eq 'on'; + $device_flags += 2 if $p{dev_auto} eq 'on'; + $device_flags += 4 if $p{dev_remove_all} eq 'on'; + + delconfigline( 5 ); + addconfigline( '5 ' . $device_flags ) if $device_flags != 0; + } + + if( $save_security ) + { + my $security_flags = 0; + $security_flags += 0x8 if $p{sec_supervisor_ignore} eq 'on'; + $security_flags += 0x40 if $p{sec_2gb_free} eq 'on'; + $security_flags += 0x200 if $p{sec_int17} eq 'on'; + $security_flags += 0x2 if $p{sec_delete_open} eq 'on'; + $security_flags += 0x4 if $p{sec_rename_open} eq 'on'; + + delconfigline( 8 ); + addconfigline( '8 ' . $security_flags ) if $security_flags != 0; + } + + if( $save_users ) + { + my $bindery_flags = 0; + $bindery_flags += 1 if $p{bindery_empty_scripts} eq 'on'; + + delconfigline( 17 ); + addconfigline( '17 ' . $bindery_flags ) if $bindery_flags != 0; + } + + if( $save_queues ) + { + my $queue_flags = 0; + $queue_flags += 1 if $p{queue_no_banner} eq 'on'; + + delconfigline( 18 ); + addconfigline( '18 ' . $queue_flags ) if $queue_flags != 0; + + delconfigline( 22 ); + foreach my $line ( split( /\r?\n/, $p{print_servers} ) ) + { + $line =~ s/^\s+//; + $line =~ s/\s+$//; + next if $line eq ''; + $line =~ s/#.*//; + next if $line eq ''; + addconfigline( '22 ' . $line ); + } + } + + if( $save_configh ) + { + setc( 50, conversion_tables ); + setc( 80, max_dir_search_handles ); + } + + if( $save_network ) + { + setc( 310, watchdogs ); + } + + if( $save_stations ) + { + setc( 400, station_file ); + setc( 401, nearest_replies ); + setc( 402, connect_replies ); + } + } + else { putp(); @@ -365,6 +490,42 @@ EOF writeconfig(); } + +sub add_mask_flag( $$$ ) +{ + my( $value, $param, $mask ) = @_; + if( defined( $p{$param} ) && $p{$param} ne '' ) + { + $value |= $mask; + } + return $value; +} + +sub addconfig_hex( $$ ) +{ + delconfigline( $_[0] ); + addconfigline( $_[0] . ' 0x' . sprintf( '%x', $_[1] ) ); +} + +sub sanitize_simple_value( $ ) +{ + my $v = $_[0]; + $v = '' unless defined $v; + $v =~ s/[\r\n]//g; + $v =~ s/^\s+//; + $v =~ s/\s+$//; + $v =~ s/[^-_\.\/\:\~A-Za-z0-9]//g; + return $v; +} + +sub sanitize_number_value( $ ) +{ + my $v = $_[0]; + $v = '' unless defined $v; + $v =~ s/[^0-9xXa-fA-F\-]//g; + return $v; +} + sub putp() { $cc =~ s/.*\?//; diff --git a/cupsutils.cmake b/cupsutils.cmake new file mode 100644 index 0000000..a3d6ac3 --- /dev/null +++ b/cupsutils.cmake @@ -0,0 +1,64 @@ +# Optional CUPS command line utility support for SMArT WebUI. +# +# This module intentionally searches for the CUPS command line utilities +# used by the WebUI, not for libcups development headers/libraries. +# +# Variables exported: +# +# WITH_CUPSUTILS +# User option to enable/disable CUPS utility discovery. +# +# HAVE_CUPSUTILS +# ON when the required CUPS utilities were found. +# +# SMART_CUPS_ENABLE +# 1 when HAVE_CUPSUTILS is ON, otherwise 0. Intended for configure_file() +# into Perl config files. +# +# CUPS_LPSTAT_EXECUTABLE +# Path to lpstat. +# +# CUPS_LP_EXECUTABLE +# Path to lp. +# +# CUPS_LPR_EXECUTABLE +# Optional path to lpr, if available. + +option(WITH_CUPSUTILS "Enable CUPS utility support for SMArT WebUI" ON) + +set(HAVE_CUPSUTILS OFF) +set(SMART_CUPS_ENABLE 0) + +if(WITH_CUPSUTILS) + find_program(CUPS_LPSTAT_EXECUTABLE NAMES lpstat) + find_program(CUPS_LP_EXECUTABLE NAMES lp) + find_program(CUPS_LPR_EXECUTABLE NAMES lpr) + + if(CUPS_LPSTAT_EXECUTABLE AND CUPS_LP_EXECUTABLE) + set(HAVE_CUPSUTILS ON) + set(SMART_CUPS_ENABLE 1) + message(STATUS "Found CUPS utilities: lpstat=${CUPS_LPSTAT_EXECUTABLE}, lp=${CUPS_LP_EXECUTABLE}") + if(CUPS_LPR_EXECUTABLE) + message(STATUS "Found optional CUPS lpr utility: ${CUPS_LPR_EXECUTABLE}") + endif() + else() + message(STATUS "CUPS utilities not found; SMArT WebUI CUPS printer discovery disabled") + endif() +else() + message(STATUS "CUPS utility support disabled") +endif() + +# Provide stable fallback strings for configure_file() consumers. These are +# only used when SMART_CUPS_ENABLE is 0 or when the admin later overrides the +# paths in smart.conf. +if(NOT CUPS_LPSTAT_EXECUTABLE) + set(CUPS_LPSTAT_EXECUTABLE "/usr/bin/lpstat") +endif() + +if(NOT CUPS_LP_EXECUTABLE) + set(CUPS_LP_EXECUTABLE "/usr/bin/lp") +endif() + +if(NOT CUPS_LPR_EXECUTABLE) + set(CUPS_LPR_EXECUTABLE "/usr/bin/lpr") +endif() diff --git a/settings.pl b/settings.pl index c12d79a..2648749 100644 --- a/settings.pl +++ b/settings.pl @@ -51,6 +51,112 @@ sub delete_confirm_attr( $ ) return ' onclick="return confirm(\'Delete ' . $what . '?\')"'; } +sub advanced_link_row( $$ ) +{ + my( $href, $label ) = @_; + + return qq|\t\n\t\t$label\n\t\n|; +} + +sub advanced_category() +{ + my $cat = $c[2]; + + $cat = 'all' if ! defined( $cat ) || $cat eq ''; + + if( $cat =~ /^(devices|security|users|queues|configh|stations|network|all)$/ ) + { + return $cat; + } + + return 'all'; +} + +sub advanced_section_visible( $$ ) +{ + my( $cat, $section ) = @_; + + return 1 if $cat eq 'all'; + + return 1 if $cat eq 'devices' && $section =~ /^(5)$/; + return 1 if $cat eq 'security' && $section =~ /^(8)$/; + return 1 if $cat eq 'users' && $section =~ /^(17)$/; + return 1 if $cat eq 'queues' && $section =~ /^(18|22)$/; + return 1 if $cat eq 'configh' && $section =~ /^(50|80)$/; + return 1 if $cat eq 'stations' && $section =~ /^(400|401|402)$/; + return 1 if $cat eq 'network' && $section =~ /^(310)$/; + + return 0; +} + +sub advanced_title( $ ) +{ + my $cat = $_[0]; + + return 'Advanced device settings' if $cat eq 'devices'; + return 'Advanced security settings' if $cat eq 'security'; + return 'Advanced user / bindery settings' if $cat eq 'users'; + return 'Advanced print queue settings' if $cat eq 'queues'; + return 'Advanced precompiled / path settings' if $cat eq 'configh'; + return 'Advanced station access settings' if $cat eq 'stations'; + return 'Advanced network watchdog settings' if $cat eq 'network'; + + return 'Advanced settings'; +} + +sub advanced_hidden_category_input( $ ) +{ + my $cat = $_[0]; + + return '' if ! defined( $cat ) || $cat eq '' || $cat eq 'all'; + + return '' . "\n"; +} + + + +sub cfg_int( $ ) +{ + my $v = $_[0]; + $v = '' unless defined $v; + $v =~ s/^\s+//; + $v =~ s/\s+$//; + return 0 if $v eq ''; + return oct( $v ) if $v =~ /^0x[0-9a-f]+$/i; + return int( $v ); +} + +sub cfg_checked( $$ ) +{ + my( $value, $mask ) = @_; + return ( cfg_int( $value ) & $mask ) ? ' CHECKED' : ''; +} + +sub html_selected( $$ ) +{ + return ( defined $_[0] && defined $_[1] && $_[0] eq $_[1] ) ? ' SELECTED' : ''; +} + +sub option_012( $ ) +{ + my $v = $_[0]; + $v = '0' unless defined $v && $v ne ''; + return + '' . "\n" . + '' . "\n" . + '' . "\n"; +} + +sub getconfiglines_without_section( $ ) +{ + my @lines = getconfig( $_[0] ); + foreach my $line ( @lines ) + { + $line =~ s/^\s*$_[0]\s+//; + } + return @lines; +} + $settings_nav_bar = settings_nav_bar(); sub kernel_network_interfaces() @@ -241,89 +347,105 @@ sub cups_print_command( $ ) return $template; } +sub cups_add_printer( $$$ ) +{ + my( $printers, $seen, $name ) = @_; + + return if ! defined( $name ); + $name =~ s/^\s+//; + $name =~ s/\s+$//; + return if $name eq ''; + return if $name =~ /[^-_\.A-Za-z0-9]/; + + my $queue = cups_queue_name( $name ); + + my $name_key = lc( $name ); + my $queue_key = lc( $queue ); + + # Deduplicate by both the original CUPS name and the generated MARS_NWE + # queue name. This avoids duplicates when the same printer is discovered + # through lpstat -e and a localized lpstat -p fallback. + return if $seen->{'name:' . $name_key}; + return if $seen->{'queue:' . $queue_key}; + + push( @$printers, { + name => $name, + queue => $queue, + command => cups_print_command( $name ), + } ); + + $seen->{'name:' . $name_key} = 1; + $seen->{'queue:' . $queue_key} = 1; +} + sub cups_printers() { - return () unless defined( $smart_cups_enable ) && $smart_cups_enable; + my @printers = (); + my %seen = (); - my %printers = (); - my $lpstat = smart_find_executable( - $smart_cups_lpstat_path, - '/usr/bin/lpstat', - '/bin/lpstat' - ); + return @printers if ! defined( $smart_cups_enable ) || !$smart_cups_enable; + return @printers if ! defined( $smart_cups_lpstat_path ) || $smart_cups_lpstat_path eq ''; + return @printers if ! -x $smart_cups_lpstat_path; - return () if $lpstat eq ''; - - # Prefer lpstat -e: it prints one destination name per line and is - # independent of translated words like "printer" / "Drucker". - if( open( my $fh, '-|', $lpstat, '-e' ) ) + # Prefer "lpstat -e": it prints only destination names and is language + # independent. If it returns at least one printer, do not run fallback + # parsers, because some CUPS versions/locales can expose aliases there. + if( open( my $fh, '-|', $smart_cups_lpstat_path, '-e' ) ) { while( my $line = <$fh> ) { chomp( $line ); $line =~ s/^\s+//; $line =~ s/\s+$//; - next if $line eq ''; - my $printer = sanitize_cups_printer_name( ( split( /\s+/, $line ) )[0] ); - $printers{$printer} = 1 if $printer ne ''; + next if $line eq ''; + next if $line =~ /[\r\n]/; + + cups_add_printer( \@printers, \%seen, $line ); } + close( $fh ); } - # Fallback: English "device for NAME:" and German "Gerät für NAME:". - if( scalar( keys( %printers ) ) == 0 ) + return @printers if scalar( @printers ) > 0; + + # Fallback: "lpstat -v" is also mostly language independent because the + # destination name follows "for :" on many systems. + if( open( my $fh, '-|', $smart_cups_lpstat_path, '-v' ) ) { - if( open( my $fh, '-|', $lpstat, '-v' ) ) + while( my $line = <$fh> ) { - while( my $line = <$fh> ) + chomp( $line ); + + if( $line =~ /\bfor\s+([^:\s]+)\s*:/i ) { - chomp( $line ); - - my $printer = ''; - if( $line =~ /^device\s+for\s+([^:\s]+):/i ) - { - $printer = $1; - } - elsif( $line =~ /^ger\S*\s+f\S*r\s+([^:\s]+):/i ) - { - $printer = $1; - } - - $printer = sanitize_cups_printer_name( $printer ); - $printers{$printer} = 1 if $printer ne ''; + cups_add_printer( \@printers, \%seen, $1 ); } - close( $fh ); } + + close( $fh ); } - # Fallback: English "printer NAME ..." and German "Drucker NAME ...". - if( scalar( keys( %printers ) ) == 0 ) + return @printers if scalar( @printers ) > 0; + + # Last fallback: parse localized "lpstat -p" output. English starts with + # "printer ", German starts with "Drucker ". + if( open( my $fh, '-|', $smart_cups_lpstat_path, '-p' ) ) { - if( open( my $fh, '-|', $lpstat, '-p' ) ) + while( my $line = <$fh> ) { - while( my $line = <$fh> ) + chomp( $line ); + + if( $line =~ /^(?:printer|Drucker)\s+(\S+)/i ) { - chomp( $line ); - - my $printer = ''; - if( $line =~ /^printer\s+(\S+)/i ) - { - $printer = $1; - } - elsif( $line =~ /^drucker\s+(\S+)/i ) - { - $printer = $1; - } - - $printer = sanitize_cups_printer_name( $printer ); - $printers{$printer} = 1 if $printer ne ''; + cups_add_printer( \@printers, \%seen, $1 ); } - close( $fh ); } + + close( $fh ); } - return sort( keys( %printers ) ); + return @printers; } sub cups_import_rows() @@ -340,12 +462,13 @@ sub cups_import_rows() foreach my $printer ( @printers ) { - my $queue = cups_queue_name( $printer ); - my $cmd = cups_print_command( $printer ); - my $href = '/settings/queues/add_new?cups_printer=' . url_escape( $printer ); + my $name = ref( $printer ) eq 'HASH' ? $printer->{name} : $printer; + my $queue = ref( $printer ) eq 'HASH' ? $printer->{queue} : cups_queue_name( $printer ); + my $cmd = ref( $printer ) eq 'HASH' ? $printer->{command} : cups_print_command( $printer ); + my $href = '/settings/queues/add_new?cups_printer=' . url_escape( $name ); $html .= "\t\n"; - $html .= "\t\t" . html_escape( $printer ) . "
Queue: " . html_escape( $queue ) . "   Command: " . html_escape( $cmd ) . "\n"; + $html .= "\t\t" . html_escape( $name ) . "
Queue: " . html_escape( $queue ) . "   Command: " . html_escape( $cmd ) . "\n"; $html .= "\t\tAdd as print queue\n"; $html .= "\t\n"; } @@ -367,10 +490,14 @@ sub cups_select_html( $ ) foreach my $printer ( @printers ) { - my $sel = ( $printer eq $current ) ? ' SELECTED' : ''; - $html .= qq|\t\t\t\t\n|; - $js .= "smartCupsCommands['" . js_escape( $printer ) . "'] = '" . js_escape( cups_print_command( $printer ) ) . "';\n"; - $js .= "smartCupsQueues['" . js_escape( $printer ) . "'] = '" . js_escape( cups_queue_name( $printer ) ) . "';\n"; + my $name = ref( $printer ) eq 'HASH' ? $printer->{name} : $printer; + my $queue = ref( $printer ) eq 'HASH' ? $printer->{queue} : cups_queue_name( $printer ); + my $cmd = ref( $printer ) eq 'HASH' ? $printer->{command} : cups_print_command( $printer ); + my $sel = ( $name eq $current ) ? ' SELECTED' : ''; + + $html .= qq|\t\t\t\t\n|; + $js .= "smartCupsCommands['" . js_escape( $name ) . "'] = '" . js_escape( $cmd ) . "';\n"; + $js .= "smartCupsQueues['" . js_escape( $name ) . "'] = '" . js_escape( $queue ) . "';\n"; } $html .= qq|\t\t\t|; @@ -378,12 +505,359 @@ sub cups_select_html( $ ) return ( $html, $js ); } +sub ipx_interface_info( $ ) +{ + my $want = $_[0]; + my %info = (); + + return %info if ! defined( $want ) || $want eq ''; + + if( open( my $fh, '<', '/proc/net/ipx/interface' ) ) + { + while( my $line = <$fh> ) + { + chomp( $line ); + $line =~ s/^\s+//; + $line =~ s/\s+$//; + + next if $line eq ''; + next if $line =~ /^Network\s+Node_Address\s+Primary\s+Device\s+Frame_Type/i; + + my @fields = split( /\s+/, $line ); + next if scalar( @fields ) < 5; + + my $network = $fields[0]; + my $dev = $fields[3]; + my $frame = $fields[4]; + + next if ! defined( $dev ) || $dev ne $want; + next if lc( $dev ) eq 'internal'; + + $info{network} = $network; + $info{frame} = $frame; + last; + } + + close( $fh ); + } + + return %info; +} + +sub ipx_device_import_rows() +{ + my $html = ''; + my @ifaces = ipx_enabled_interfaces(); + + if( scalar( @ifaces ) == 0 ) + { + return qq|\t\n\t\tIPX interfaces detected on host\n\t\n\t\n\t\tNo active IPX interfaces were found in /proc/net/ipx/interface.\n\t\n|; + } + + $html .= qq|\t\n\t\tIPX interfaces detected on host\n\t\n|; + + foreach my $iface ( @ifaces ) + { + my %info = ipx_interface_info( $iface ); + my $net = $info{network}; + my $frame = $info{frame}; + my $delay = 1; + + $net = '0x0' if ! defined( $net ) || $net eq ''; + $frame = '802.2' if ! defined( $frame ) || $frame eq '' || lc( $frame ) eq 'none'; + + my $href = '/settings/devices/add_new?ipx_iface=' . url_escape( $iface ); + + $html .= "\t\n"; + $html .= "\t\t" . html_escape( $iface ) . "
Config line: 4 " . html_escape( $net ) . " " . html_escape( $iface ) . " " . html_escape( $frame ) . " " . html_escape( $delay ) . "\n"; + $html .= "\t\tAdd as device\n"; + $html .= "\t\n"; + } + + return $html; +} + +sub ipx_device_defaults_from_query() +{ + my %defaults = (); + + $defaults{number} = ''; + $defaults{interface} = ''; + $defaults{frametype} = '802.2'; + $defaults{delay} = 1; + + if( defined( $p{ipx_iface} ) && $p{ipx_iface} ne '' ) + { + my $iface = $p{ipx_iface}; + $iface =~ s/[^-_\.A-Za-z0-9]//g; + + my %info = ipx_interface_info( $iface ); + + $defaults{interface} = $iface; + $defaults{number} = defined( $info{network} ) && $info{network} ne '' ? $info{network} : '0x0'; + $defaults{frametype} = defined( $info{frame} ) && $info{frame} ne '' && lc( $info{frame} ) ne 'none' ? $info{frame} : '802.2'; + $defaults{delay} = 1; + } + + return %defaults; +} + + +sub smart_bindery_values() +{ + my( $bind_server, $bind_user, $bind_pass ) = ( '', '', '' ); + + if( open( my $fh, '<' . $smart_nwclient_path ) ) + { + my $line = <$fh>; + close( $fh ); + + chomp( $line ) if defined( $line ); + ( $bind_server, $bind_user, $bind_pass ) = split( '[/ ]', $line ) if defined( $line ); + } + + $bind_server = '' unless defined( $bind_server ); + $bind_user = '' unless defined( $bind_user ); + $bind_pass = '' unless defined( $bind_pass ); + + return( $bind_server, $bind_user, $bind_pass ); +} + +sub sync_server_hint_row( $$$$ ) +{ + my( $checkbox_name, $current_name, $other_name, $direction ) = @_; + + $current_name = '' unless defined( $current_name ); + $other_name = '' unless defined( $other_name ); + + my $msg = ''; + + if( $direction eq 'general_to_smart' ) + { + $msg = 'Also update the SMArT bindery server when saving. Current SMArT bindery server: '; + $msg .= $other_name ne '' ? '' . html_escape( $other_name ) . '' : 'not set'; + } + else + { + $msg = 'Also update the MARS_NWE server name when saving. Current MARS_NWE server name: '; + $msg .= $other_name ne '' ? '' . html_escape( $other_name ) . '' : 'not set'; + } + + return qq|\t\n\t\tKeep server names in sync:
$msg\n\t\tYes \n\t\n|; +} + + +sub unix_user_import_rows() +{ + my $html = ''; + my @users = unix_userlist_effective(); + my %existing = (); + my @candidates = (); + + foreach my $line ( getconfig( 13 ) ) + { + my @f = split( ' ', $line ); + $existing{lc( $f[1] )} = 1 if defined( $f[1] ) && $f[1] ne ''; + } + + foreach my $u ( @users ) + { + next if ! defined( $u->{name} ) || $u->{name} eq ''; + next if defined( $u->{uid} ) && $u->{uid} < 1000; + next if lc( $u->{name} ) eq 'nobody'; + next if lc( $u->{name} ) eq 'root'; + next if $existing{lc( $u->{name} )}; + + push( @candidates, $u ); + } + + if( scalar( @candidates ) == 0 ) + { + return qq|\t\n\t\tUnix users detected on host\n\t\n\t\n\t\tNo additional local Unix users were found for import.\n\t\n|; + } + + $html .= qq|\t\n\t\tUnix users detected on host\n\t\n|; + + foreach my $u ( @candidates ) + { + my $name = $u->{name}; + my $uid = $u->{uid}; + my $gecos = defined( $u->{gecos} ) ? $u->{gecos} : ''; + $gecos =~ s/,.*$//; + + my $href = '/settings/users_import/' . url_escape( $name ); + + $html .= "\t\n"; + $html .= "\t\t" . html_escape( $name ) . "
UID: " . html_escape( $uid ) . ""; + $html .= "   " . html_escape( $gecos ) if $gecos ne ''; + $html .= "\n"; + $html .= "\t\tAdd as MARS_NWE user\n"; + $html .= "\t\n"; + } + + return $html; +} + +sub unix_user_defaults_from_query() +{ + my %defaults = (); + + $defaults{name} = ''; + $defaults{unix_user} = ''; + $defaults{fullname} = ''; + + my $want = ''; + + if( defined( $p{unix_user} ) && $p{unix_user} ne '' ) + { + $want = $p{unix_user}; + } + elsif( defined( $c[1] ) && $c[1] eq 'users_import' && defined( $c[2] ) && $c[2] ne '' ) + { + $want = $c[2]; + } + elsif( defined( $c[3] ) && $c[3] ne '' ) + { + $want = $c[3]; + } + + if( $want ne '' ) + { + $want =~ s/[^-_\.A-Za-z0-9]//g; + + foreach my $u ( unix_userlist_effective() ) + { + next if ! defined( $u->{name} ) || $u->{name} ne $want; + + my $gecos = defined( $u->{gecos} ) ? $u->{gecos} : ''; + $gecos =~ s/,.*$//; + + $defaults{name} = uc( $u->{name} ); + $defaults{name} =~ s/[^-_\.A-Za-z0-9]//g; + $defaults{unix_user} = $u->{name}; + $defaults{fullname} = $gecos; + last; + } + } + + return %defaults; +} + + +sub selected_attr( $$ ) +{ + my( $a, $b ) = @_; + + $a = '' unless defined( $a ); + $b = '' unless defined( $b ); + + return $a eq $b ? ' SELECTED' : ''; +} + + +sub smart_group_checkbox_rows( $ ) +{ + my $server = $_[0]; + my $html = ''; + + my @groups = (); + my $cmd = 'nwbols -t 2 -S ' . $server; + + @groups = sort( split( "\n", `$cmd` ) ); + + foreach my $g ( @groups ) + { + my @f = split( ' ', $g ); + my $group = $f[0]; + + next if ! defined( $group ) || $group eq ''; + next if $group =~ /[^-_\.A-Za-z0-9]/; + + $html .= html_escape( $group ) . '
' . "\n"; + } + + if( $html eq '' ) + { + $html = 'No bindery groups found. Save the user first, then add groups later.
' . "\n"; + } + + return $html; +} + + +sub smart_debug_user_import_row() +{ + my $p_unix = defined( $p{unix_user} ) ? $p{unix_user} : ''; + my $line = ''; + + $line .= 'c0=' . html_escape( defined( $c[0] ) ? $c[0] : '' ) . ' '; + $line .= 'c1=' . html_escape( defined( $c[1] ) ? $c[1] : '' ) . ' '; + $line .= 'c2=' . html_escape( defined( $c[2] ) ? $c[2] : '' ) . ' '; + $line .= 'c3=' . html_escape( defined( $c[3] ) ? $c[3] : '' ) . ' '; + $line .= 'c4=' . html_escape( defined( $c[4] ) ? $c[4] : '' ) . ' '; + $line .= 'p_unix_user=' . html_escape( $p_unix ); + + return qq|\t\n\t\tDebug user import:\n\t\t$line\n\t\n|; +} + + +sub unix_userlist_effective() +{ + my @users = (); + + if( defined( $smart_userlist_path ) && $smart_userlist_path ne '' && -x $smart_userlist_path ) + { + my @cmd = ( $smart_userlist_path ); + + if( defined( $smart_userlist_pam_check ) && $smart_userlist_pam_check ) + { + push( @cmd, '--pam-check' ); + push( @cmd, '--pam-service', 'smart' ); + } + + if( open( my $fh, '-|', @cmd ) ) + { + while( my $line = <$fh> ) + { + chomp( $line ); + + my( $name, $uid, $gid, $gecos, $home, $shell ) = split( /\t/, $line, 6 ); + + next if ! defined( $name ) || $name eq ''; + next if $name =~ /[^-_\.A-Za-z0-9]/; + + push( @users, { + name => $name, + uid => $uid, + gid => $gid, + gecos => defined( $gecos ) ? $gecos : '', + home => defined( $home ) ? $home : '', + shell => defined( $shell ) ? $shell : '', + } ); + } + + close( $fh ); + } + } + + # Last-resort compatibility fallback: old SMArT unix_userlist() implementation. + if( scalar( @users ) == 0 ) + { + @users = unix_userlist(); + } + + return @users; +} + + sub handle_request() { if( $c[1] eq 'general' ) { $server_name = getconfigline( 2 ); $internal_net = getconfigline( 3 ); + my( $smart_bind_server_for_general, $smart_bind_user_for_general, $smart_bind_pass_for_general ) = smart_bindery_values(); + my $general_sync_server_row = sync_server_hint_row( 'sync_smart_bind_server', $server_name, $smart_bind_server_for_general, 'general_to_smart' ); ( $burst_read, $burst_write ) = split( ' ', getconfigline( 30 ) ); $timing_down = getconfigline( 210 ); $timing_warn = getconfigline( 211 ); @@ -532,6 +1006,7 @@ $settings_nav_bar
+$general_sync_server_row @@ -795,6 +1270,9 @@ $settings_nav_bar
+ + Advanced precompiled / path settings + @@ -892,6 +1370,9 @@ $settings_nav_bar Disallow encryption
+ + Advanced security settings + @@ -913,7 +1394,7 @@ EOF ( $root_name, $root, $root_password ) = split( ' ', getconfigline( 12 ) ); $guest_user_list = ''; $root_list = ''; - foreach $u ( unix_userlist() ) + foreach $u ( unix_userlist_effective() ) { if( $u->{uid} eq $guest ) { $guest_user_list .= "\t\t\t" . '' . "\n"; } @@ -1063,6 +1544,9 @@ $root_list
+ + Advanced user / bindery settings + @@ -1320,6 +1804,9 @@ $settings_nav_bar Write to separate files (.1, .2, etc)
+ + Advanced network watchdog settings + @@ -1634,6 +2121,8 @@ EOF { if( $c[2] eq '' ) { + my $ipx_device_import_rows = ipx_device_import_rows(); + print < - Add new device + Add new device manually

+ Advanced device settings @@ -1712,13 +2203,42 @@ EOF } else { - $c = getconfigline( '4 ' . $c[2] ); - @c = split( ' ', $c ); - $interface_options = network_interface_options( $c[1] ); - - $c[2] =~ s/\.//g; - eval( '$frametype_' . $c[2] . ' = " SELECTED";' ); - + my $title = $c[2]; + my $number = ''; + my $interface = ''; + my $frametype = '802.2'; + my $delay = 1; + + if( $c[2] eq 'add_new' ) + { + my %defaults = ipx_device_defaults_from_query(); + $number = $defaults{number}; + $interface = $defaults{interface}; + $frametype = $defaults{frametype}; + $delay = $defaults{delay}; + } + else + { + my $line = getconfigline( '4 ' . $c[2] ); + my @dev = split( ' ', $line ); + + $number = $dev[0]; + $interface = $dev[1]; + $frametype = $dev[2]; + $delay = $dev[3]; + + $title = $number; + } + + $number = '' unless defined $number; + $interface = '' unless defined $interface; + $frametype = '802.2' unless defined $frametype && $frametype ne ''; + $delay = 1 unless defined $delay && $delay ne ''; + + my $frametype_key = $frametype; + $frametype_key =~ s/\.//g; + eval( '$frametype_' . $frametype_key . ' = " SELECTED";' ); + print < $settings_nav_bar -
+ @@ -1780,10 +2300,8 @@ $settings_nav_bar Network interface: @@ -1806,7 +2324,7 @@ $interface_options
Interface delay: @@ -1825,14 +2343,12 @@ $interface_options
EOF } } + elsif( $c[1] eq 'smart' ) { - open( FILE, '<' . $smart_nwclient_path ) or die "Could not open $smart_nwclient_path: $!"; - $x = ; - close( FILE ); - - chomp( $x ); - ( $bind_server, $bind_user, $bind_pass ) = split( '[/ ]', $x ); + ( $bind_server, $bind_user, $bind_pass ) = smart_bindery_values(); + my $smart_general_server_name = getconfigline( 2 ); + my $smart_sync_server_row = sync_server_hint_row( 'sync_general_server_name', $bind_server, $smart_general_server_name, 'smart_to_general' ); print <
+$smart_sync_server_row @@ -2018,6 +2543,15 @@ EOF } else { + my $debug_user_import_row = smart_debug_user_import_row(); + my $group_rows = smart_group_checkbox_rows( $server ); + my %unix_defaults = unix_user_defaults_from_query(); + my $default_name = $unix_defaults{name}; + my $default_unix_user = $unix_defaults{unix_user}; + my $default_fullname = $unix_defaults{fullname}; + $default_name = '' unless defined( $default_name ); + $default_unix_user = '' unless defined( $default_unix_user ); + $default_fullname = '' unless defined( $default_fullname ); $unix_user_list = ''; $group_list = ''; @allgroups = sort( split( "\n", `nwbols -S $server -t 2` ) ); @@ -2025,7 +2559,7 @@ EOF { $fullname = read_property_string( $c[2], 1, 'IDENTIFICATION' ); $unix_user = read_property_string( $c[2], 1, 'UNIX_USER' ); - foreach $u ( unix_userlist() ) + foreach $u ( unix_userlist_effective() ) { if( $u->{name} eq $unix_user ) { $unix_user_list .= "\t\t\t" . '' . "\n"; } @@ -2044,15 +2578,36 @@ EOF } else { - foreach $u ( unix_userlist() ) + $fullname = $default_fullname; + + foreach $u ( unix_userlist_effective() ) { - $unix_user_list .= "\t\t\t" . '' . "\n"; + if( defined( $default_unix_user ) && $default_unix_user ne '' && $u->{name} eq $default_unix_user ) + { + $unix_user_list .= "\t\t\t" . '' . "\n"; + } + else + { + $unix_user_list .= "\t\t\t" . '' . "\n"; + } } + + if( defined( $default_unix_user ) && $default_unix_user ne '' && $unix_user_list !~ /VALUE="\Q$default_unix_user\E"/ ) + { + $unix_user_list .= "\t\t\t" . '' . "\n"; + } + foreach $g ( @allgroups ) { $g =~ s/ .*//; + next if $g eq ''; $group_list .= "\t\t\t" . $g . '
' . "\n"; } + + if( $group_list eq '' ) + { + $group_list = "\t\t\t" . 'EVERYONE
' . "\n"; + } } print < +
- Device: $c[0] + Device: $title
@@ -1772,7 +2292,7 @@ $settings_nav_bar Network number:
-
+
-
- Manual override:
-
+
+ Use the IPX interfaces list on the Devices overview to prefill this value.
-
+
@@ -1934,8 +2451,15 @@ $settings_nav_bar EOF } - elsif( $c[1] eq 'users' ) + elsif( $c[1] eq 'users' || $c[1] eq 'users_import' ) { + if( $c[1] eq 'users_import' ) + { + $p{unix_user} = $c[2]; + $c[1] = 'users'; + $c[2] = 'add_new'; + } + my $unix_user_import_rows = unix_user_import_rows(); $server = get_server(); if( $c[2] eq '' ) @@ -2006,6 +2530,7 @@ EOF print < + $unix_user_import_rows Add new user
+$debug_user_import_row + + + + + EOF if( $c[2] eq 'add_new' ) @@ -2113,7 +2675,7 @@ EOF Account name: EOF @@ -2163,6 +2725,22 @@ $group_list
@@ -2103,6 +2659,12 @@ $settings_nav_bar
Debug defaults:default_name=$default_name default_unix_user=$default_unix_user default_fullname=$default_fullname fullname=$fullname
-
+
+ + + EOF @@ -2340,7 +2918,7 @@ EOF Group name: -
+
EOF @@ -2614,6 +3192,196 @@ EOF EOF } } + + elsif( $c[1] eq 'advanced' ) + { + my $advanced_category = advanced_category(); + my $advanced_title = advanced_title( $advanced_category ); + my $advanced_hidden = advanced_hidden_category_input( $advanced_category ); + + my $device_flags = getconfigline( 5 ); + my $security_flags = getconfigline( 8 ); + my $bindery_flags = getconfigline( 17 ); + my $queue_flags = getconfigline( 18 ); + my $max_dir_search_handles = getconfigline( 80 ); + my $conversion_tables = getconfigline( 50 ); + my $watchdogs = getconfigline( 310 ); + my $station_file = getconfigline( 400 ); + my $nearest_replies = getconfigline( 401 ); + my $connect_replies = getconfigline( 402 ); + + $device_flags = 0 if ! defined( $device_flags ) || $device_flags eq ''; + $security_flags = 0 if ! defined( $security_flags ) || $security_flags eq ''; + $bindery_flags = 0 if ! defined( $bindery_flags ) || $bindery_flags eq ''; + $queue_flags = 0 if ! defined( $queue_flags ) || $queue_flags eq ''; + + my $dev_keep = ( $device_flags & 1 ) ? ' CHECKED' : ''; + my $dev_auto = ( $device_flags & 2 ) ? ' CHECKED' : ''; + my $dev_remove_all = ( $device_flags & 4 ) ? ' CHECKED' : ''; + + my $sec_supervisor_ignore = ( $security_flags & 0x8 ) ? ' CHECKED' : ''; + my $sec_2gb_free = ( $security_flags & 0x40 ) ? ' CHECKED' : ''; + my $sec_int17 = ( $security_flags & 0x200 ) ? ' CHECKED' : ''; + my $sec_delete_open = ( $security_flags & 0x2 ) ? ' CHECKED' : ''; + my $sec_rename_open = ( $security_flags & 0x4 ) ? ' CHECKED' : ''; + + my $bindery_empty_scripts = ( $bindery_flags & 1 ) ? ' CHECKED' : ''; + my $queue_no_banner = ( $queue_flags & 1 ) ? ' CHECKED' : ''; + + my $nearest_0 = $nearest_replies eq '0' ? ' SELECTED' : ''; + my $nearest_1 = $nearest_replies eq '1' ? ' SELECTED' : ''; + my $nearest_2 = $nearest_replies eq '2' ? ' SELECTED' : ''; + + my $connect_0 = $connect_replies eq '0' ? ' SELECTED' : ''; + my $connect_1 = $connect_replies eq '1' ? ' SELECTED' : ''; + my $connect_2 = $connect_replies eq '2' ? ' SELECTED' : ''; + + my $print_server_rows = ''; + foreach my $ps ( getconfig( 22 ) ) + { + my $line = $ps; + $line =~ s/^22\s+//; + $print_server_rows .= html_escape( $line ) . "\n"; + } + + print < + +SMArT Advanced Settings + + + + +$settings_nav_bar + +
+$advanced_hidden + + + + + +EOF + + if( advanced_section_visible( $advanced_category, '5' ) ) + { + print < + + + +EOF + } + + if( advanced_section_visible( $advanced_category, '8' ) ) + { + print < + + + + + +EOF + } + + if( advanced_section_visible( $advanced_category, '17' ) ) + { + print < + +EOF + } + + if( advanced_section_visible( $advanced_category, '18' ) ) + { + print < + +EOF + } + + if( advanced_section_visible( $advanced_category, '22' ) ) + { + print < + +EOF + } + + if( advanced_section_visible( $advanced_category, '50' ) || advanced_section_visible( $advanced_category, '80' ) ) + { + print < +EOF + if( advanced_section_visible( $advanced_category, '50' ) ) + { + print qq|\t\n|; + } + if( advanced_section_visible( $advanced_category, '80' ) ) + { + print qq|\t\n|; + } + } + + if( advanced_section_visible( $advanced_category, '310' ) ) + { + print qq|\t\n|; + print qq|\t\n|; + } + + if( advanced_section_visible( $advanced_category, '400' ) || advanced_section_visible( $advanced_category, '401' ) || advanced_section_visible( $advanced_category, '402' ) ) + { + print < +EOF + if( advanced_section_visible( $advanced_category, '400' ) ) + { + print qq|\t\n|; + } + if( advanced_section_visible( $advanced_category, '401' ) ) + { + print < +EOF + } + if( advanced_section_visible( $advanced_category, '402' ) ) + { + print < +EOF + } + } + + print < + + + +
$advanced_title
IPX device flags
Keep IPX devices/routes on stop
Allow automatic kernel IPX interface creation
Remove all IPX routes/devices on stop
Security / compatibility flags
Ignore station/time restrictions for supervisor
Limit volume free space information to 2 GB
Enable direct INT17 printing for NETX clients
Allow deleting open files (advanced)
Allow renaming open files (advanced)
User / bindery flags
Create empty login scripts when missing
Print queue flags
Always disable print banner
Print server entries

One entry per line, without leading section number 22.
Precompiled / path settings
Conversion table file
Maximum directory search handles
Watchdog behavior
Watchdog ticks
0 = always, -1 = never, positive value = only below ticks.
Station access control
Station file
Nearest server replies
Connection replies

+
+ + + +EOF + } + else { error( 404 ); diff --git a/smart.conf.cmake b/smart.conf.cmake index b0d5371..ae0e369 100644 --- a/smart.conf.cmake +++ b/smart.conf.cmake @@ -146,3 +146,7 @@ $nw_cert_file = '@MARS_NWE_INSTALL_FULL_CONFDIR@/server.crt'; # TLS private key file in PEM format. # Required only when HTTPS is enabled. $nw_key_file = '@MARS_NWE_INSTALL_FULL_CONFDIR@/server.key'; + +# Helper used by SMArT to list Unix/NSS users. +$smart_userlist_path = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart_userlist'; +$smart_userlist_pam_check = 0; diff --git a/smart_userlist.c b/smart_userlist.c new file mode 100644 index 0000000..133d7d3 --- /dev/null +++ b/smart_userlist.c @@ -0,0 +1,171 @@ +/* + SMArT + + List local/NSS users for the WebUI. + + PAM itself cannot enumerate users. User enumeration is done through NSS + getpwent(), so /etc/nsswitch.conf is honored (files, sss, ldap, nis, ...). + Optionally, each user can be checked with pam_acct_mgmt() against the "smart" + PAM service. + + Output format: + usernameuidgidgecoshomeshell +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int empty_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + struct pam_response *reply; + + (void) msg; + (void) appdata_ptr; + + if (num_msg <= 0) { + *resp = NULL; + return PAM_SUCCESS; + } + + reply = calloc((size_t) num_msg, sizeof(struct pam_response)); + if (reply == NULL) { + return PAM_BUF_ERR; + } + + *resp = reply; + return PAM_SUCCESS; +} + +static int pam_account_ok(const char *service, const char *user) +{ + struct pam_conv conv = { empty_conv, NULL }; + pam_handle_t *pamh = NULL; + int rc; + + rc = pam_start(service, user, &conv, &pamh); + if (rc == PAM_SUCCESS) { + rc = pam_acct_mgmt(pamh, PAM_SILENT); + } + + if (pamh != NULL) { + pam_end(pamh, rc); + } + + return rc == PAM_SUCCESS; +} + +static void print_sanitized(const char *s) +{ + const unsigned char *p = (const unsigned char *) (s != NULL ? s : ""); + + while (*p != '\0') { + if (*p == '\t' || *p == '\n' || *p == '\r') { + putchar(' '); + } else { + putchar((int) *p); + } + p++; + } +} + +static int is_safe_name(const char *s) +{ + const unsigned char *p = (const unsigned char *) s; + + if (s == NULL || *s == '\0') { + return 0; + } + + while (*p != '\0') { + if (!( (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || + *p == '_' || *p == '-' || *p == '.' )) { + return 0; + } + p++; + } + + return 1; +} + +int main(int argc, char **argv) +{ + struct passwd *pw; + uid_t min_uid = 1000; + int include_system = 0; + int pam_check = 0; + const char *pam_service = "smart"; + int i; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--all") == 0) { + include_system = 1; + min_uid = 0; + } else if (strcmp(argv[i], "--min-uid") == 0 && i + 1 < argc) { + char *end = NULL; + unsigned long v = strtoul(argv[++i], &end, 10); + if (end == NULL || *end != '\0') { + fprintf(stderr, "Invalid --min-uid value\n"); + return 2; + } + min_uid = (uid_t) v; + } else if (strcmp(argv[i], "--pam-check") == 0) { + pam_check = 1; + } else if (strcmp(argv[i], "--pam-service") == 0 && i + 1 < argc) { + pam_service = argv[++i]; + } else { + fprintf(stderr, + "Usage: %s [--all] [--min-uid UID] [--pam-check] [--pam-service SERVICE]\n", + argv[0]); + return 2; + } + } + + errno = 0; + setpwent(); + + while ((pw = getpwent()) != NULL) { + if (!is_safe_name(pw->pw_name)) { + continue; + } + + if (!include_system && pw->pw_uid < min_uid) { + continue; + } + + if (!include_system && + (strcmp(pw->pw_name, "root") == 0 || strcmp(pw->pw_name, "nobody") == 0)) { + continue; + } + + if (pam_check && !pam_account_ok(pam_service, pw->pw_name)) { + continue; + } + + print_sanitized(pw->pw_name); + printf("\t%lu\t%lu\t", (unsigned long) pw->pw_uid, (unsigned long) pw->pw_gid); + print_sanitized(pw->pw_gecos); + putchar('\t'); + print_sanitized(pw->pw_dir); + putchar('\t'); + print_sanitized(pw->pw_shell); + putchar('\n'); + } + + endpwent(); + + if (errno != 0) { + perror("getpwent"); + return 1; + } + + return 0; +} diff --git a/static/icon-configh.svg b/static/icon-configh.svg new file mode 100644 index 0000000..9195096 --- /dev/null +++ b/static/icon-configh.svg @@ -0,0 +1,17 @@ + diff --git a/static/icon-devices.svg b/static/icon-devices.svg new file mode 100644 index 0000000..20dd28a --- /dev/null +++ b/static/icon-devices.svg @@ -0,0 +1,19 @@ + diff --git a/static/icon-dirs.svg b/static/icon-dirs.svg new file mode 100644 index 0000000..f73e46a --- /dev/null +++ b/static/icon-dirs.svg @@ -0,0 +1,18 @@ + diff --git a/static/icon-general.svg b/static/icon-general.svg new file mode 100644 index 0000000..2bc0b09 --- /dev/null +++ b/static/icon-general.svg @@ -0,0 +1,18 @@ + diff --git a/static/icon-groups.svg b/static/icon-groups.svg new file mode 100644 index 0000000..4086c88 --- /dev/null +++ b/static/icon-groups.svg @@ -0,0 +1,18 @@ + diff --git a/static/icon-logging.svg b/static/icon-logging.svg new file mode 100644 index 0000000..cb7ab98 --- /dev/null +++ b/static/icon-logging.svg @@ -0,0 +1,19 @@ + diff --git a/static/icon-queues.svg b/static/icon-queues.svg new file mode 100644 index 0000000..6389200 --- /dev/null +++ b/static/icon-queues.svg @@ -0,0 +1,20 @@ + diff --git a/static/icon-security.svg b/static/icon-security.svg new file mode 100644 index 0000000..3657465 --- /dev/null +++ b/static/icon-security.svg @@ -0,0 +1,17 @@ + diff --git a/static/icon-service.svg b/static/icon-service.svg new file mode 100644 index 0000000..253acba --- /dev/null +++ b/static/icon-service.svg @@ -0,0 +1,23 @@ + diff --git a/static/icon-smart.svg b/static/icon-smart.svg new file mode 100644 index 0000000..4bfb5d9 --- /dev/null +++ b/static/icon-smart.svg @@ -0,0 +1,18 @@ + diff --git a/static/icon-start.svg b/static/icon-start.svg new file mode 100644 index 0000000..6210a19 --- /dev/null +++ b/static/icon-start.svg @@ -0,0 +1,22 @@ + diff --git a/static/icon-stations.svg b/static/icon-stations.svg new file mode 100644 index 0000000..f905af0 --- /dev/null +++ b/static/icon-stations.svg @@ -0,0 +1,19 @@ + diff --git a/static/icon-susers.svg b/static/icon-susers.svg new file mode 100644 index 0000000..48c44e3 --- /dev/null +++ b/static/icon-susers.svg @@ -0,0 +1,18 @@ + diff --git a/static/icon-users.svg b/static/icon-users.svg new file mode 100644 index 0000000..f40d566 --- /dev/null +++ b/static/icon-users.svg @@ -0,0 +1,19 @@ + diff --git a/static/icon-volumes.svg b/static/icon-volumes.svg new file mode 100644 index 0000000..aadd76f --- /dev/null +++ b/static/icon-volumes.svg @@ -0,0 +1,19 @@ + diff --git a/static/menu.html b/static/menu.html index b56a6be..0736bbc 100644 --- a/static/menu.html +++ b/static/menu.html @@ -9,47 +9,219 @@
-
-
-

Main menu

-

SMArT configuration navigation with the original explanations preserved.

+
+
+

Main menu

+

Choose a section from the icon list. The explanation opens here on the left, and the editor opens on the right.

+
+ SMArT logo
- SMArT logo -
-
- -
-

General settings

-
Open settings
+ +
+ + +
+
+
+

Setup first

+

Explanation on the left, editor opens in the right pane.

+
+
+
+
+ -
+
+
+

Fill SMArT settings first

+
Before editing users, groups, bindery objects, trustees or print queues, configure the SMArT settings first.
+
+
+

Bindery / Supervisor access

+
SMArT needs the NetWare bindery server name, the supervisor login and the supervisor password. Use the supervisor password that is configured for your MARS_NWE server in nwserv.conf. Without this, SMArT can still show configuration pages, but operations which talk to the running server can fail.
+
+
+

Why this matters

+
User, group and trustee actions use the MARS_NWE tools against the bindery. If the bindery credentials are missing or wrong, SMArT cannot reliably create objects, update memberships, set passwords or modify trustees.
+
+
+

Recommended order

+
+
    +
  1. Open SMArT settings.
  2. +
  3. Set the nwserv.conf path if needed.
  4. +
  5. Enter bindery server, supervisor user and supervisor password.
  6. +
  7. Save, then continue with users, groups, volumes, trustees and queues.
  8. +
+
+
+
+
+
+
+
+
Service control
+

MARS_NWE service

+
+ Open start page +
+
+
+

Start, stop, restart and status

+
Use this page to control the systemd service for the running MARS_NWE server.
+
+
+

Available actions

+
+
    +
  • status shows the current service state.
  • +
  • start starts the MARS_NWE service.
  • +
  • stop waits until the service is really stopped.
  • +
  • restart restarts the service and then shows the final status.
  • +
+
+
+
+

When to use it

+
After changing nwserv.conf, restart the service so the server reloads the changed configuration. Use the start page buttons on the right to run start, stop, restart or status.
+
+
+
+
+
+
+
Section
+

General settings

+
+ Open settings +
+

Servername

The servername is the name under which this server will show up when using @@ -119,39 +291,45 @@ Don't ask me what they mean, but they're hexadecimal, so don't forget to prepend 0x.
- - -
-

Directories

-
Open settings
+
+ -
+
Some directories for MARS_NWE files. The path cache directory is needed for Client-32 and the namespace calls, the spool directory is used for internal print queue handling.
- - -
-

Precompiled settings

-
Open settings
+
+ -
+
When you just leave these fields empty, the values in your config.h file will be used. If you want to change those settings without recompiling MARS_NWE, you can change them here.
- - -
-

Security

-
Open settings
+
+ -
+

Modes

Here you can change the standard modes for new files and directories. @@ -170,13 +348,15 @@ not-encrypted sending of passwords over the net.
On the Linux-side, passwords will only be stored in encrypted format.
- - -
-

User configuration

-
Open settings
+
+ -
+

Guest user

Here you can set the UID a user will get before logging in.
@@ -217,13 +397,15 @@ Only those Linux-logins will handled automatically that don't have a x or asterisk as their encrypted password.
- - -
-

Volumes

-
Open settings
+
+ -
+
The OS/2 additional namespace can be used by Windows 9x too. The 'no fixed i-nodes' option is necessary when exporting DOS or CD-ROM @@ -233,13 +415,15 @@ For more information about pipe filesystems you can take a look at MARS_NWE's documentation directory.
- - -
-

Devices

-
Open settings
+
+ -
+
This section contains information for the ipx-router built into mars_nwe and/or the external program nwrouted.
@@ -289,13 +473,15 @@ in internal device/routing table at runtime.
You don't have to set this in FreeBSD!
- - -
-

Logging

-
Open settings
+
+ -
+
MARS_NWE can keep a log file with error messages, click here to set what kind of messages must be logged and where.
@@ -305,48 +491,69 @@ According to nwserv.conf, the NWCLIENT tag must always be set to No debugging.
- - -
-

SMArT settings

-
Open settings
+
+ -
+
Some things have to be configured here before you can use SMArT.
- - -
-

Users

-
Open settings
+
+ -
+
+
+
Configure station file based nearest-server and connection replies.
+
+
+
+
+
+
Section
+

Users

+
+ Open settings +
+
You can edit the userlist from the bindery files here. This option will not change anything to the nwserv.conf configuration file.
- - -
-

Groups

-
Open settings
+
+ -
+
You can edit the group list from the bindery files here. This option will not change anything to the nwserv.conf configuration file.
- - -
-

Print queues

-
Open settings
+
+ -
+
Here you can edit the list of print queues. The things you have to fill in are:
    @@ -366,8 +573,58 @@ default directory.
- -
+
+
+
Tip: click an icon on the left to switch topics. The corresponding settings page is opened automatically in the editor pane on the right.
+
+
+ +