diff --git a/bin/auto b/bin/auto index f2f8fcf..7e343eb 100755 --- a/bin/auto +++ b/bin/auto @@ -361,6 +361,7 @@ API::Std::cmd_add('MODUNLOAD', 1, 'cfunc.modules', \%Core::Cmd::HELP_MODUNLOAD, API::Std::cmd_add('MODRELOAD', 1, 'cfunc.modules', \%Core::Cmd::HELP_MODRELOAD, \&Core::Cmd::cmd_modreload); API::Std::cmd_add('SHUTDOWN', 2, 'cmd.shutdown', \%Core::Cmd::HELP_SHUTDOWN, \&Core::Cmd::cmd_shutdown); API::Std::cmd_add('RESTART', 2, 'cmd.restart', \%Core::Cmd::HELP_RESTART, \&Core::Cmd::cmd_restart); +API::Std::cmd_add('REHASH', 2, 'cmd.rehash', \%Core::Cmd::HELP_REHASH, \&Core::Cmd::cmd_rehash); API::Std::cmd_add('HELP', 2, 0, \%Core::Cmd::HELP_HELP, \&Core::Cmd::cmd_help); # Infinite while loop. diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 84135dc..373bceb 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -3,6 +3,7 @@ Auto IRC Bot 3.0: Change Log 3.0 Indev =============================================================================== + * Added core command REHASH. * Moved signal handlers to Lib::Auto. * Renamed src/ to lib/. * Switched from Auto-Flatfile to SQLite for database backend. diff --git a/lib/Core/Cmd.pm b/lib/Core/Cmd.pm index db7e8ef..b0b1240 100644 --- a/lib/Core/Cmd.pm +++ b/lib/Core/Cmd.pm @@ -182,6 +182,27 @@ sub cmd_restart return 1; } +# Help hash for REHASH. Spanish, French and German needed. +our %HELP_REHASH = ( + 'en' => 'This will reload the configuration file, update logs, load new modules and connect to new servers.', +); +# REHASH callback. +sub cmd_rehash +{ + my (%data) = @_; + + # Send out notifications. + notice($data{svr}, $data{nick}, "Rehashing."); + dbug "Got REHASH from ".$data{nick}."!".$data{user}."@".$data{host}."/".$data{svr}."! Rehashing. . ."; + alog "Got REHASH from ".$data{nick}."!".$data{user}."@".$data{host}."/".$data{svr}."! Rehashing. . ."; + + # Rehash. + Lib::Auto::rehash(); + notice($data{svr}, $data{nick}, "Done."); + + return 1; +} + # Help hash for HELP. Spanish, French and German needed. our %HELP_HELP = ( 'en' => 'Displays help for commands.', diff --git a/lib/Lib/Auto.pm b/lib/Lib/Auto.pm index cfaa09d..53a77e7 100644 --- a/lib/Lib/Auto.pm +++ b/lib/Lib/Auto.pm @@ -5,6 +5,9 @@ package Lib::Auto; use strict; use warnings; use English qw(-no_match_vars); +use Sys::Hostname; +use feature qw(switch); +use API::Std qw(conf_get err); use API::Log qw(println dbug alog); our $VERSION = 3.000000; @@ -41,6 +44,165 @@ sub checkver } } +sub rehash +{ + # Parse configuration file. + my %newsettings = $Auto::CONF->parse or err(2, 'Failed to parse configuration file!', 0) and return; + + # Check for required configuration values. + my @REQCVALS = qw(locale expire_logs server fantasy_pf); + foreach my $REQCVAL (@REQCVALS) { + if (!defined $newsettings{$REQCVAL}) { + err(2, "Missing required configuration value: $REQCVAL", 0) and return; + } + } + undef @REQCVALS; + + # Set new configuration. + %Auto::SETTINGS = %newsettings; + + # Expire old logs. + API::Log::expire_logs(); + + # Parse privileges. + my %PRIVILEGES; + # If there are any privsets. + if (conf_get('privset')) { + # Get them. + my %tcprivs = conf_get('privset'); + + foreach my $tckpriv (keys %tcprivs) { + # For each privset, get the inner values. + my %mcprivs = conf_get("privset:$tckpriv"); + + # Iterate through them. + foreach my $mckpriv (keys %mcprivs) { + # Switch statement for the values. + given ($mckpriv) { + # If it's 'priv', save it as a privilege. + when ('priv') { + if (defined $PRIVILEGES{$tckpriv}) { + # If this privset exists, push to it. + push @{ $PRIVILEGES{$tckpriv} }, ($mcprivs{$mckpriv})[0][0]; + } + else { + # Otherwise, create it. + @{ $PRIVILEGES{$tckpriv} } = (($mcprivs{$mckpriv})[0][0]); + } + } + # If it's 'inherit', inherit the privileges of another privset. + when ('inherit') { + # If the privset we're inheriting exists, continue. + if (defined $PRIVILEGES{($mcprivs{$mckpriv})[0][0]}) { + # Iterate through each privilege. + foreach (@{ $PRIVILEGES{($mcprivs{$mckpriv})[0][0]} }) { + # And save them to the privset inheriting them + if (defined $PRIVILEGES{$tckpriv}) { + # If this privset exists, push to it. + push @{ $PRIVILEGES{$tckpriv} }, $_; + } + else { + # Otherwise, create it. + @{ $PRIVILEGES{$tckpriv} } = ($_); + } + } + } + } + } + } + } + } + %Auto::PRIVILEGES = %PRIVILEGES; + + # Load modules. + if (conf_get('module')) { + alog '* Loading modules...'; + foreach (@{ (conf_get('module'))[0] }) { + if (!API::Std::mod_exists($_)) { Auto::mod_load($_); } + } + } + + ## Create sockets. + alog '* Connecting to servers...'; + # Get servers from config. + my %cservers = conf_get('server'); + # Iterate through each configured server. + foreach my $cskey (keys %cservers) { + if (!defined $Auto::SOCKET{$cskey}) { + # Prepare socket data. + my %conndata = ( + Proto => 'tcp', + LocalAddr => $cservers{$cskey}{'bind'}[0], + PeerAddr => $cservers{$cskey}{'host'}[0], + PeerPort => $cservers{$cskey}{'port'}[0], + Timeout => 20, + ); + # Set IPv6/SSL data. + my $use6 = 0; + my $usessl = 0; + if (defined $cservers{$cskey}{'ipv6'}[0]) { $use6 = $cservers{$cskey}{'ipv6'}[0]; } + if (defined $cservers{$cskey}{'ssl'}[0]) { $usessl = $cservers{$cskey}{'ssl'}[0]; } + + # CertFP. + if ($usessl) { + if (defined $cservers{$cskey}{'certfp'}[0]) { + if ($cservers{$cskey}{'certfp'}[0] eq 1) { + $conndata{'SSL_use_cert'} = 1; + if (defined $cservers{$cskey}{'certfp_cert'}[0]) { + $conndata{'SSL_cert_file'} = "$Auto::Bin/../etc/certs/".$cservers{$cskey}{'certfp_cert'}[0]; + } + if (defined $cservers{$cskey}{'certfp_key'}[0]) { + $conndata{'SSL_key_file'} = "$Auto::Bin/../etc/certs/".$cservers{$cskey}{'certfp_key'}[0]; + } + if (defined $cservers{$cskey}{'certfp_pass'}[0]) { + $conndata{'SSL_passwd_cb'} = sub { return $cservers{$cskey}{'certfp_pass'}[0]; }; + } + } + } + } + + # Create the socket. + if ($use6) { + $Auto::SOCKET{$cskey} = IO::Socket::INET6->new(%conndata) or # Or error. + err(2, 'Failed to connect to server ('.$ERRNO.'): '.$cskey.' ['.$cservers{$cskey}{'host'}[0].q{:}.$cservers{$cskey}{'port'}[0].']', 0) + and delete $Auto::SOCKET{$cskey} and next; + } + else { + if ($usessl) { + $Auto::SOCKET{$cskey} = IO::Socket::SSL->new(%conndata) or # Or error. + err(2, 'Failed to connect to server ('.$ERRNO.'): '.$cskey.' ['.$cservers{$cskey}{'host'}[0].q{:}.$cservers{$cskey}{'port'}[0].']', 0) + and delete $Auto::SOCKET{$cskey} and next; + } + else { + $Auto::SOCKET{$cskey} = IO::Socket::INET->new(%conndata) or # Or error. + err(2, 'Failed to connect to server ('.$ERRNO.'): '.$cskey.' ['.$cservers{$cskey}{'host'}[0].q{:}.$cservers{$cskey}{'port'}[0].']', 0) + and delete $Auto::SOCKET{$cskey} and next; + } + } + + # Send PASS if we have one. + if (defined $cservers{$cskey}{'pass'}[0]) { + Auto::socksnd($cskey, 'PASS :'.$cservers{$cskey}{'pass'}[0]) or + err(2, 'Failed to connect to server: '.$cskey.' ['.$cservers{$cskey}{'host'}[0].q{:}.$cservers{$cskey}{'port'}[0].']', 0) + and next; + } + API::Std::event_run('on_preconnect', $cskey); + # Send NICK/USER. + API::IRC::nick($cskey, $cservers{$cskey}{'nick'}[0]); + Auto::socksnd($cskey, 'USER '.$cservers{$cskey}{'ident'}[0].q{ }.hostname.q{ }.$cservers{$cskey}{'host'}[0].' :'.$cservers{$cskey}{'realname'}[0]) or + err(2, 'Failed to connect to server: '.$cskey.' ['.$cservers{$cskey}{'host'}[0].q{:}.$cservers{$cskey}{'port'}[0].']', 0) + and next; + # Add to select. + $Auto::SELECT->add($Auto::SOCKET{$cskey}); + # Success! + alog '** Successfully connected to server: '.$cskey; + dbug '** Successfully connected to server: '.$cskey; + } + } + + return 1; +} + ################### # Signal handlers # ###################