#!/usr/bin/perl -w # # Copyright (c) 2005, Jilles Tjoelker # All rights reserved. # # Redistribution and use in source and binary forms, with # or without modification, are permitted provided that the # following conditions are met: # # 1. Redistributions of source code must retain the above # copyright notice, this list of conditions and the # following disclaimer. # 2. Redistributions in binary form must reproduce the # above copyright notice, this list of conditions and # the following disclaimer in the documentation and/or # other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY # OF SUCH DAMAGE. # # HybServ2/dancer-services to ratbox-services database converter # - Reads nick.db and chan.db from the current directory. # - Writes SQL to stdout, to be executed after the schema. # - Certain errors abort the script and give a message on stderr. # - Several other problematic cases are noted in the SQL output as comments # ("--"). # # Some problems/caveats: # - Flag and level constants are hard-coded, see # hybserv/include/{nick,chan}serv.h, rserv/include/s_{user,chan}serv.h # and rserv/src/s_chanserv.c. # - Access levels cannot be fully converted, but the code here could be # improved a bit. # - rserv does not store much additional contact information for users/channels. # - Master nicks are converted to usernames with that nick registered; any # linked nicks are registered to that username as well. This means users # should use SET MASTER an appropriate nick in HybServ2; this cannot be # easily changed later. # - Available flags are different; there is information loss here. # - rserv does not support locking modes off. # - Channels which had -n and/or -t modelocked may need to be set AUTOJOIN # in order that they are created that way. # - If you want to convert to not having nick registration, delete everything # from the nicks table before using the resulting database. # - The script is probably easy to adapt to other HybServ2 derivatives, # please send me the patch if you do. # # Jilles Tjoelker # # http://www.stack.nl/~jilles/irc/ print "-- hybservtorserv.pl Copyright 2005 Jilles Tjoelker\n"; print "BEGIN TRANSACTION;\n"; $fakets = time() || 2147483647; print "-- Converting nick.db\n"; open(NICKDB, ") { if (/^->([a-zA-Z]+) (.*)$/) { $word = $1; $args = $2; if ($word eq 'PASS') { $password = $args; } elsif ($word eq 'EMAIL') { $email = $args; } elsif ($word eq 'LINK') { # Store master nick, might be wrong in channel # access list :( $masternick{$word} = $args; print "INSERT INTO nicks VALUES (\"$nick\", \"$args\", $regtime, $lastseentime, 1);\n"; $nick = ''; } } elsif (/^([^ ]+) ([0-9]+) ([0-9]+) ([0-9]+)$/) { if ($nick ne '') { print "INSERT INTO users VALUES (\"$nick\", \"$password\", \"$email\", \"\", $regtime, $lastseentime, $rsflags);\n"; $masternick{$nick} = $nick; # flags = 1 (NS_FLAGS_WARN) print "INSERT INTO nicks VALUES (\"$nick\", \"$nick\", $regtime, $lastseentime, 1);\n"; } $nick = $1; $hsflags = $2; $regtime = $3; $lastseentime = $4; $password = ''; $email = ''; $rsflags = 0; $rsflags |= 2 if ($hsflags & 16); # private # rserv doesn't have noexpire flag #$rsflags |= 0 if ($hsflags & 4); # operator/noexpire # transform either of noregister and nochanops to suspend # hope rserv doesn't mind we don't know the suspender... $rsflags |= 1 if ($hsflags & 0x300000); # many other flags unused, see hybserv/include/nickserv.h if ($hsflags & 0x80) { # "forbidden" nick, hybserv will kill # sort of a friendlier network-wide nick resv # no use creating a userserv entry for it print "-- Ignoring forbidden nick $nick\n"; $nick = ''; } } elsif (/^;( ?)(.*)$/) { print "-- $2\n"; } else { print STDERR "Unrecognized line $. in nick.db:\n"; print STDERR $_; exit(1); } } if ($nick ne '') { print "INSERT INTO users VALUES (\"$nick\", \"$password\", \"$email\", \"\", $regtime, $lastseentime, $rsflags);\n"; $masternick{$nick} = $nick; # flags = 1 (NS_FLAGS_WARN) print "INSERT INTO nicks VALUES (\"$nick\", \"$nick\", $regtime, $lastseentime, 1);\n"; } close(NICKDB); print "-- Converting chan.db\n"; open(CHANDB, ") { if (/^->([a-zA-Z]+) (.*)$/) { $word = $1; $args = $2; if ($word eq 'FNDR') { $founder = $args; # strip off dancer-services last seen time $founder =~ s/ .*//; } elsif ($word eq 'SUCCESSOR') { $successor = $args; # strip off dancer-services last seen time $successor =~ s/ .*//; } elsif ($word eq 'PASS') { $password = $args; } elsif ($word eq 'TOPIC') { $topic = $args; $topic =~ s/^://; } elsif ($word eq 'LIMIT') { $lockedlimit = $args; } elsif ($word eq 'KEY') { $lockedkey = $args; } elsif ($word eq 'MON') { $lockedon .= 'l' if $args & 0x10; $lockedon .= 'k' if $args & 0x20; $lockedon .= 's' if $args & 0x40; $lockedon .= 'p' if $args & 0x80; $lockedon .= 'n' if $args & 0x100; $lockedon .= 't' if $args & 0x200; $lockedon .= 'm' if $args & 0x400; $lockedon .= 'i' if $args & 0x800; # regonly, specific to dancer-services $lockedon .= 'r' if $args & 0x80000; # anonops, hybrid7.0 only and often disabled #$lockedon .= 'a' if $args & 0x4000; } elsif ($word eq 'MOFF') { $lockedoff .= 'l' if $args & 0x10; $lockedoff .= 'k' if $args & 0x20; $lockedoff .= 's' if $args & 0x40; $lockedoff .= 'p' if $args & 0x80; $lockedoff .= 'n' if $args & 0x100; $lockedoff .= 't' if $args & 0x200; $lockedoff .= 'm' if $args & 0x400; $lockedoff .= 'i' if $args & 0x800; # regonly, specific to dancer-services $lockedoff .= 'r' if $args & 0x80000; # anonops, hybrid7.0 only and often disabled #$lockedoff .= 'a' if $args & 0x4000; } elsif ($word eq 'ENTRYMSG') { $entrymsg = $args; } elsif ($word eq 'URL') { $url = $args; } elsif ($word eq 'ALVL') { # ->ALVL always appears, and we have all required # data for the channels table now $enforcemodes = $lockedon; $enforcemodes .= ' '.$lockedkey if ($lockedon =~ /k/); $enforcemodes .= ' '.$lockedlimit if ($lockedon =~ /l/); $createmodes = $enforcemodes; # If you patched your ircd to change default modes # for new channels, change here: $createmodes = 'n'.$createmodes if (($lockedon.$lockedoff) !~ /n/); $createmodes = 't'.$createmodes if (($lockedon.$lockedoff) !~ /t/); $lockedoff =~ s/[nt]//g; $enforcemodes = '+'.$enforcemodes if ($enforcemodes ne ''); $createmodes = '+'.$createmodes if ($createmodes ne ''); if ($lockedoff ne '') { print "-- Skipping locked off modes '$lockedoff' on $chan\n"; } print "INSERT INTO channels VALUES (\"$chan\", \"$topic\", \"$url\", \"$createmodes\", \"$enforcemodes\", $fakets, $regtime, $lastusedtime, $rsflags, \"\");\n"; if ($args =~ /^(-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+)$/) { # w/ halfops $nooplvl = $1; $autovoicelvl = $2; $voicelvl = $3; $accessmodlvl = $4; $invitelvl = $5; $autohalfoplvl = $6; $halfoplvl = $7; $autooplvl = $8; $oplvl = $9; $unbanselflvl = $10; $akicklvl = $11; $clearlvl = $12; $setlvl = $13; $superoplvl = $14; $founderlvl = $15; } elsif ($args =~ /^(-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+)$/) { # no halfops $nooplvl = $1; $autovoicelvl = $2; $voicelvl = $3; $accessmodlvl = $4; $invitelvl = $5; $autooplvl = $6; $oplvl = $7; $unbanselflvl = $8; $akicklvl = $9; $clearlvl = $10; $setlvl = $11; $superoplvl = $12; $founderlvl = $13; $autohalfoplvl = $autooplvl; $halfoplvl = $oplvl; } else { print STDERR "Unrecognized access level line $. in chan.db channel $chan\n"; exit(1); } } elsif ($word eq 'ACCESS') { $args =~ /^([^ ]+) (-?[0-9]+)/; $nick = $1; $hslevel = $2; $rslevel = 0; $rslevel = 1 if ($hslevel >= $invitelvl); $rslevel = 1 if ($hslevel >= $voicelvl); $rslevel = 1 if ($hslevel >= $autovoicelvl); # it is unlikely that someone has been trusted with # access to the crude AKICK facility but not with ops, # but anyway: $rslevel = 10 if ($hslevel >= $akicklvl); # also allow adding bans if they had halfop rights $rslevel = 10 if ($hslevel >= $autohalfoplvl); $rslevel = 10 if ($hslevel >= $halfoplvl); $rslevel = 50 if ($hslevel >= $oplvl); $rslevel = 50 if ($hslevel >= $autooplvl); $rslevel = 52 if ($hslevel > $oplvl && $hslevel > $autooplvl); $rslevel = 140 if ($hslevel >= $clearlvl); $rslevel = 150 if ($hslevel >= $accessmodlvl && $hslevel >= $clearlvl); $rslevel = 190 if ($hslevel >= $setlvl); $rslevel = 190 if ($hslevel >= $superoplvl); # give the successor more chance to become founder # if the founder is dropped; however, this may also # enable the successor to use higher levels for bans # and adduser. $rslevel += 5 if ($nick eq $successor); $rslevel = 200 if ($nick eq $founder); $membflags = 0; if ($hslevel >= $autooplvl) { $membflags |= 1; } elsif ($hslevel >= $autovoicelvl) { $membflags |= 2; } if ($nick =~ /@/) { # rserv doesn't allow hostmasks in access lists, # only usernames! print "-- Skipping access entry $chan $nick @ level $rslevel flags $membflags\n"; } elsif ($rslevel == 0) { print "-- Skipping access entry $chan $nick @ level zero\n"; } else { $master = $masternick{$nick}; if (defined($master)) { print "INSERT INTO members VALUES (\"$chan\", \"$master\", \"\", $rslevel, $membflags, 0);\n"; } else { print "-- Skipping access entry $chan $nick @ level $rslevel flags $membflags because of invalid nick\n"; } } } elsif ($word eq 'AKICK') { $args =~ /^([^ ]+) :(.*)$/; $banmask = $1; $reason = $2; $reason =~ s/"/\\"/; print "INSERT INTO bans VALUES (\"$chan\", \"$banmask\", \"$reason\", \"\", 10, 0);\n"; } } elsif (/^(\#[^ ]+) ([0-9]+) ([0-9]+) ([0-9]+)$/) { $chan = $1; $hsflags = $2; $regtime = $3; $lastusedtime = $4; $password = ''; $topic = ''; $successor = ''; $lockedlimit = ''; $lockedkey = ''; $lockedon = ''; $lockedoff = ''; $url = ''; $entrymsg = ''; $createmodes = ''; $enforcemodes = ''; $rsflags = 0; $rsflags |= 4 if ($hsflags & 0x400); # changuard/autojoin # the following is not a real equivalent $rsflags |= 8 if ($hsflags & 0x1000); # verbose/warnoverride $rsflags |= 16 if ($hsflags & 8); # secureops/restrictops } elsif (/^;( ?)(.*)$/) { print "-- $2\n"; } else { print STDERR "Unrecognized line $. in chan.db:\n"; print STDERR $_; exit(1); } } close(CHANDB); print "COMMIT TRANSACTION;\n"; print "-- End of hybservtorserv.pl output\n";