From fbb00b1778c9a58180e8fa960f2a57a79b5ea760 Mon Sep 17 00:00:00 2001 From: Janik Kleinhoff Date: Sun, 13 Mar 2016 16:27:49 +0000 Subject: BOOM GOES THE XML This likely introduces twenty new bugs, but on the other hand it avoids twenty thousand other issues (most of them relating to XML::Simple doing the utterly wrong things all the time and configuration merely mitigating the issues, rather than fixing the core problem) so chances are it's more than worth it. We check if there's shiny new JSON-based config in place already; if not, we try to load XML config and convert it to the new format. The old config files are left untouched in case we fucked up somehow. --- lib/ASM/Commander.pm | 40 +++++++++---------- lib/ASM/Config.pm | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ASM/Event.pm | 2 +- lib/ASM/XML.pm | 70 -------------------------------- meta.pl | 6 +-- 5 files changed, 134 insertions(+), 94 deletions(-) create mode 100644 lib/ASM/Config.pm delete mode 100644 lib/ASM/XML.pm diff --git a/lib/ASM/Commander.pm b/lib/ASM/Commander.pm index 1fd92d1..7037755 100644 --- a/lib/ASM/Commander.pm +++ b/lib/ASM/Commander.pm @@ -377,7 +377,7 @@ sub cmd_monitor2 { my $chan = lc $+{chan}; my $switch = lc $+{switch}; $::channels->{channel}->{$chan}->{monitor} = $switch; - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); $conn->privmsg($event->replyto, "Monitor flag for $chan set to $switch"); } @@ -397,10 +397,10 @@ sub cmd_suppress { $conn->schedule($duration, sub { if (($::channels->{channel}{$chan}{suppress} // 0) - 10 <= time) { # we needn't actually delete this here, but doing so - # avoids cluttering the XML + # avoids cluttering the config delete $::channels->{channel}{$chan}{suppress}; $conn->privmsg($event->replyto, "Unsuppressed $chan"); - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); } }); $conn->privmsg($event->replyto, "Suppressing alerts from $chan for $minutes minutes."); @@ -433,7 +433,7 @@ sub cmd_silence2 { my $chan = lc $+{chan}; my $switch = lc $+{switch}; $::channels->{channel}->{$chan}->{silence} = $switch; - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); $conn->privmsg($event->replyto, "Silence flag for $chan set to $switch"); } @@ -592,7 +592,7 @@ sub cmd_user_add { return; } $::users->{person}->{$nick} = { 'flags' => $flags }; - ASM::XML->writeUsers(); + ASM::Config->writeUsers(); $conn->privmsg($event->replyto, "Flags for NickServ account $nick set to $flags"); } @@ -651,7 +651,7 @@ sub cmd_user_flags2 { $o_Htgroup->save(); } $::users->{person}->{$nick}->{flags} = $flags; - ASM::XML->writeUsers(); + ASM::Config->writeUsers(); $conn->privmsg($event->replyto, "Flags for $nick set to $flags"); } @@ -665,7 +665,7 @@ sub cmd_user_del { return $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the config file manually."); } delete($::users->{person}->{$nick}); - ASM::XML->writeUsers(); + ASM::Config->writeUsers(); use Apache::Htpasswd; use Apache::Htgroup; my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1}); my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile}); @@ -697,7 +697,7 @@ sub cmd_target { my @tmphl = @{$::channels->{channel}->{$chan}->{msgs}->{$level}}; push(@tmphl, $nick); $::channels->{channel}->{$chan}->{msgs}->{$level} = \@tmphl; - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); $conn->privmsg($event->replyto, "$nick added to $level risk messages for $chan"); } @@ -717,7 +717,7 @@ sub cmd_detarget { @ppl = grep { lc $_ ne $nick } @ppl; $::channels->{channel}->{$chan}->{msgs}->{$risk} = \@ppl; } - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); $conn->privmsg($event->replyto, "$nick removed from targets for $chan"); } @@ -778,7 +778,7 @@ sub cmd_hilight { push(@tmphl, $nick); } $::channels->{channel}->{$chan}->{hilights}->{$level} = \@tmphl; - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); $conn->privmsg($event->replyto, ASM::Util->commaAndify(@nicks) . " added to $level risk hilights for $chan"); } @@ -798,7 +798,7 @@ sub cmd_dehilight { @ppl = grep { !(lc $_ ~~ @nicks) } @ppl; $::channels->{channel}->{$chan}->{hilights}->{$risk} = \@ppl; } - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); $conn->privmsg($event->replyto, "Removing hilights for " . ASM::Util->commaAndify(@nicks) . " in $chan"); } @@ -808,14 +808,14 @@ sub cmd_join { my $chan = lc $+{chan}; unless (defined($::channels->{channel}->{$chan})) { $::channels->{channel}->{$chan} = { monitor => "yes", silence => "no" }; - ASM::XML->writeChannels(); + ASM::Config->writeChannels(); } $conn->join($chan); my @autojoins = @{$::settings->{autojoins}}; if (!grep { $chan eq lc $_ } @autojoins) { @autojoins = (@autojoins, $chan); $::settings->{autojoins} = \@autojoins; - ASM::XML->writeSettings(); + ASM::Config->writeSettings(); } } @@ -827,7 +827,7 @@ sub cmd_part { my @autojoins = @{$::settings->{autojoins}}; @autojoins = grep { lc $_ ne $chan } @autojoins; $::settings->{autojoins} = \@autojoins; - ASM::XML->writeSettings(); + ASM::Config->writeSettings(); } sub cmd_sl { @@ -852,7 +852,7 @@ sub cmd_ev { sub cmd_rehash { my ($conn, $event) = @_; - ASM::XML->readXML(); + ASM::Config->readConfig(); $conn->privmsg($event->replyto, 'config files were re-read'); } @@ -871,7 +871,7 @@ sub cmd_restrict { $::restrictions->{$+{type} . 's'}->{$+{type}}->{$who}->{$+{restriction}} = $+{restriction}; $conn->privmsg($event->replyto, "Added $+{restriction} restriction for $+{type} $who"); } - ASM::XML->writeRestrictions(); + ASM::Config->writeRestrictions(); } sub cmd_ops { @@ -950,7 +950,7 @@ sub cmd_blacklist { use String::CRC32; my $id = sprintf("%08x", crc32($string)); $::blacklist->{string}->{$id} = { "content" => $string, "type" => "string", "setby" => $event->nick, "settime" => strftime('%F', gmtime) }; - ASM::XML->writeBlacklist(); + ASM::Config->writeBlacklist(); $conn->privmsg($event->replyto, "$string blacklisted with id $id, please use ;blreason $id reasonGoesHere to set a reason"); } @@ -960,7 +960,7 @@ sub cmd_blacklistpcre { use String::CRC32; my $id = sprintf("%08x", crc32($+{string})); $::blacklist->{string}->{$id} = { "content" => $+{string}, "type" => "pcre", "setby" => $event->nick, "settime" => strftime('%F', gmtime) }; - ASM::XML->writeBlacklist(); + ASM::Config->writeBlacklist(); $conn->privmsg($event->replyto, "$+{string} blacklisted with id $id, please use ;blreason $id reasonGoesHere to set a reason"); } @@ -970,7 +970,7 @@ sub cmd_unblacklist { if (defined($::blacklist->{string}->{$+{id}})) { delete $::blacklist->{string}->{$+{id}}; $conn->privmsg($event->replyto, "blacklist id $+{id} removed"); - ASM::XML->writeBlacklist(); + ASM::Config->writeBlacklist(); } else { $conn->privmsg($event->replyto, "invalid id"); } @@ -1014,7 +1014,7 @@ sub cmd_blreason { if (defined($::blacklist->{string}->{$+{id}})) { $::blacklist->{string}->{$+{id}}->{reason} = $+{reason}; $conn->privmsg($event->replyto, "Reason set"); - ASM::XML->writeBlacklist(); + ASM::Config->writeBlacklist(); } else { $conn->privmsg($event->replyto, "ID is invalid"); } diff --git a/lib/ASM/Config.pm b/lib/ASM/Config.pm new file mode 100644 index 0000000..de0bb21 --- /dev/null +++ b/lib/ASM/Config.pm @@ -0,0 +1,110 @@ +package ASM::Config; +no autovivification; +use warnings; +use strict; +use feature 'state'; + +use XML::Simple qw(:strict); +use JSON; +use IO::All; +no if $] >= 5.017011, warnings => 'experimental::smartmatch'; + +our $json = JSON->new->utf8->pretty->canonical; + +sub serialize { + return $json->encode(@_); +} +sub deserialize { + return $json->decode(@_); +} + +sub readXML { + my ( $p ) = $::cset; + my @fchan = ( 'event', keys %::RISKS ); + my $xs1 = XML::Simple->new( KeyAttr => ['id'], Cache => [ qw/memcopy/ ]); + $::settings = $xs1->XMLin( "$p/settings.xml", ForceArray => ['host'], + 'GroupTags' => { altnicks => 'altnick', server => 'host', + autojoins => 'autojoin' }); + $::channels = $xs1->XMLin( "$p/channels.xml", ForceArray => \@fchan ); + $::users = $xs1->XMLin( "$p/users.xml", ForceArray => 'person'); + $::mysql = $xs1->XMLin( "$p/mysql.xml", ForceArray => ['ident', 'geco'], + 'GroupTags' => { ignoredidents => 'ident', ignoredgecos => 'geco' }); + $::dnsbl = $xs1->XMLin( "$p/dnsbl.xml", ForceArray => []); + $::rules = $xs1->XMLin( "$p/rules.xml", ForceArray => []); + $::restrictions = $xs1->XMLin( "$p/restrictions.xml", ForceArray => ['host', 'nick', 'account']); + $::blacklist = $xs1->XMLin( "$p/blacklist.xml", ForceArray => 'string'); +} + +sub readConfig { + if (!-e "$::cset/settings.json") { + state $in_readconfig = 0; + die "Unexpected readConfig recursion" if $in_readconfig++; + readXML(); + writeConfig(); + readConfig(); + } + else { + $::settings = deserialize(io->file("$::cset/settings.json")->all); + $::channels = deserialize(io->file("$::cset/channels.json")->all); + $::users = deserialize(io->file("$::cset/users.json")->all); + $::mysql = deserialize(io->file("$::cset/mysql.json")->all); + $::dnsbl = deserialize(io->file("$::cset/dnsbl.json")->all); + $::rules = deserialize(io->file("$::cset/rules.json")->all); + $::restrictions = deserialize(io->file("$::cset/restrictions.json")->all); + $::blacklist = deserialize(io->file("$::cset/blacklist.json")->all); + } +} + +sub writeConfig { + writeMysql(); + writeChannels(); + writeUsers(); + writeSettings(); + writeRestrictions(); + writeBlacklist(); + writeDnsbl(); + writeRules(); +} + +sub writeMysql { + $::settingschanged=1; + serialize($::mysql) > io->file("$::cset/mysql.json"); +} + +sub writeRules { + $::settingschanged=1; + serialize($::rules) > io->file("$::cset/rules.json"); +} + +sub writeDnsbl { + $::settingschanged=1; + serialize($::dnsbl) > io->file("$::cset/dnsbl.json"); +} + +sub writeChannels { + $::settingschanged=1; + serialize($::channels) > io("$::cset/channels.json"); +} + +sub writeUsers { + $::settingschanged=1; + serialize($::users) > io("$::cset/users.json"); +} + +sub writeSettings { + $::settingschanged=1; + serialize($::settings) > io("$::cset/settings.json"); +} + +sub writeRestrictions { + $::settingschanged=1; + serialize($::restrictions) > io("$::cset/restrictions.json"); +} + +sub writeBlacklist { + $::settingschanged=1; + serialize($::blacklist) > io("$::cset/blacklist.json"); +} + +return 1; +# vim: ts=2:sts=2:sw=2:expandtab diff --git a/lib/ASM/Event.pm b/lib/ASM/Event.pm index 005a9d3..1b2a4a2 100644 --- a/lib/ASM/Event.pm +++ b/lib/ASM/Event.pm @@ -87,7 +87,7 @@ sub on_pong $::settingschanged = 0; } else { $conn->privmsg($::settings->{masterchan}, "Config files changed, auto rehash triggered. Check console for possible errors."); - ASM::XML->readXML(); + ASM::Config->readConfig(); } } if ($lag > 1) { diff --git a/lib/ASM/XML.pm b/lib/ASM/XML.pm deleted file mode 100644 index 7588699..0000000 --- a/lib/ASM/XML.pm +++ /dev/null @@ -1,70 +0,0 @@ -package ASM::XML; -no autovivification; -use warnings; -use strict; - -use XML::Simple qw(:strict); -use IO::All; -no if $] >= 5.017011, warnings => 'experimental::smartmatch'; - -$::xs1 = XML::Simple->new( KeyAttr => ['id'], Cache => [ qw/memcopy/ ]); - -sub readXML { - my ( $p ) = $::cset; - my @fchan = ( 'event', keys %::RISKS ); - $::settings = $::xs1->XMLin( "$p/settings.xml", ForceArray => ['host'], - 'GroupTags' => { altnicks => 'altnick', server => 'host', - autojoins => 'autojoin' }); - $::channels = $::xs1->XMLin( "$p/channels.xml", ForceArray => \@fchan ); - $::users = $::xs1->XMLin( "$p/users.xml", ForceArray => 'person'); - $::mysql = $::xs1->XMLin( "$p/mysql.xml", ForceArray => ['ident', 'geco'], - 'GroupTags' => { ignoredidents => 'ident', ignoredgecos => 'geco' }); - $::dnsbl = $::xs1->XMLin( "$p/dnsbl.xml", ForceArray => []); - $::rules = $::xs1->XMLin( "$p/rules.xml", ForceArray => []); - $::restrictions = $::xs1->XMLin( "$p/restrictions.xml", ForceArray => ['host', 'nick', 'account']); - $::blacklist = $::xs1->XMLin( "$p/blacklist.xml", ForceArray => 'string'); -} - -sub writeXML { - writeSettings(); - writeChannels(); - writeUsers(); - writeRestrictions(); - writeBlacklist(); - writeMysql(); -} - -sub writeMysql { - $::settingschanged=1; - $::xs1->XMLout($::mysql, RootName => 'mysql', KeyAttr => ['id']) > io("$::cset/mysql.xml"); -} - -sub writeChannels { - $::settingschanged=1; - $::xs1->XMLout($::channels, RootName => 'channels', KeyAttr => ['id'], NumericEscape => 2) > io("$::cset/channels.xml"); -} - -sub writeUsers { - $::settingschanged=1; - $::xs1->XMLout($::users, RootName => 'people', KeyAttr => ['id']) > io("$::cset/users.xml"); -} - -sub writeSettings { - $::settingschanged=1; - $::xs1->XMLout($::settings, RootName => 'settings', - GroupTags => { altnicks => 'altnick', server => 'host', autojoins => 'autojoin' }, NoAttr => 1) > io("$::cset/settings.xml"); -} - -sub writeRestrictions { - $::settingschanged=1; - $::xs1->XMLout($::restrictions, RootName => 'restrictions', KeyAttr => ['id'], - GroupTags => { hosts => "host", nicks => "nick", accounts => "account"}) > io("$::cset/restrictions.xml"); -} - -sub writeBlacklist { - $::settingschanged=1; - $::xs1->XMLout($::blacklist, RootName => 'blacklist', KeyAttr => ['id'], NumericEscape => 2) > io("$::cset/blacklist.xml"); -} - -return 1; -# vim: ts=2:sts=2:sw=2:expandtab diff --git a/meta.pl b/meta.pl index 9a4cf2f..1730942 100755 --- a/meta.pl +++ b/meta.pl @@ -20,7 +20,7 @@ use Tie::CPHash; use Net::DNS::Async; use ASM::Util; -use ASM::XML; +use ASM::Config; use ASM::Inspect; use ASM::Event; use ASM::Services; @@ -107,7 +107,7 @@ sub init { } if ($::cset eq '') { $::cset = 'config-default'; } else { $::cset = "config-$::cset"; } - ASM::XML->readXML(); + ASM::Config->readConfig(); $::pass = $::settings->{pass} if $::pass eq ''; $::async = HTTP::Async->new(); $::dns = Net::DNS::Async->new(QueueSize => 5000, Retries => 3); @@ -161,7 +161,7 @@ sub init { } $::fm = File::Monitor->new(); foreach my $file ("channels", "dnsbl", "mysql", "restrictions", "rules", "settings", "users", "blacklist") { - $::fm->watch("./" . $::cset . '/' . $file . ".xml"); + $::fm->watch("./" . $::cset . '/' . $file . ".json"); } $::fm->scan(); $irc->start(); -- cgit v1.2.3