commit 84c190b6e2d9060286ff1bed2e00bfbae0853f65 from: jrmu date: Fri Dec 17 13:35:57 2021 UTC Imported sources commit - /dev/null commit + 84c190b6e2d9060286ff1bed2e00bfbae0853f65 blob - /dev/null blob + 8dadce0b495d9854d8ce62a6b983da6cd557e77b (mode 644) --- /dev/null +++ BNC.pm @@ -0,0 +1,519 @@ +#!/usr/bin/perl + +package BNC; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use MIME::Base64; +use Digest::SHA qw(sha256_hex); +use lib './'; +require "SQLite.pm"; +require "Hash.pm"; +require "DNS.pm"; +require "Mail.pm"; + +my %conf = %main::conf; +my $chans = $conf{chans}; +my $teamchans = $conf{teamchans}; +my @teamchans = split /[,\s]+/m, $teamchans; +my $staff = $conf{staff}; +my $zncdir = $conf{zncdir}; +my $znclog = $conf{znclog} || "$zncdir/.znc/moddata/adminlog/znc.log"; +my $hostname = $conf{hostname}; +my $terms = $conf{terms}; +my @logs; +my $expires = $conf{expires}; +my $sslport = $conf{sslport}; +my $plainport = $conf{plainport}; +my $mailfrom = $conf{mailfrom}; +my $mailname = $conf{mailname}; +my $zncconfpath = $conf{zncconfpath} || "$zncdir/.znc/configs/znc.conf"; +my $znctree = { Node => "root" }; + +use constant { + NONE => 0, + ERRORS => 1, + WARNINGS => 2, + ALL => 3, +}; + +`doas chown znc:daemon /home/znc/home/znc/.znc/configs/znc.conf`; +`doas chmod g+r /home/znc/home/znc/.znc/`; +my @zncconf = main::readarray($zncconfpath); +$znctree; +my @users; +foreach my $line (@zncconf) { + if ($line =~ //) { + push(@users, $1); + } +} +#$znctree = parseml($znctree, @zncconf); +main::cbind("pub", "-", "bnc", \&mbnc); +main::cbind("msg", "-", "bnc", \&mbnc); +main::cbind("msg", "-", "regex", \&mregex); +main::cbind("msg", "-", "foreach", \&mforeach); +main::cbind("msgm", "-", "*", \&mcontrolpanel); +main::cbind("msg", "-", "taillog", \&mtaillog); +main::cbind("msg", "-", "lastseen", \&mlastseen); + +sub init { + #znc.conf file + unveil("$zncconfpath", "r") or die "Unable to unveil $!"; + #dependencies for figlet + unveil("/usr/local/bin/figlet", "rx") or die "Unable to unveil $!"; + unveil("/usr/lib/libc.so.95.1", "r") or die "Unable to unveil $!"; + unveil("/usr/libexec/ld.so", "r") or die "Unable to unveil $!"; + unveil("/usr/bin/tail", "rx") or die "Unable to unveil $!"; + #znc.log file + unveil("$znclog", "r") or die "Unable to unveil $!"; + #print treeget($znctree, "AnonIPLimit")."\n"; + #print treeget($znctree, "ServerThrottle")."\n"; + #print treeget($znctree, "ConnectDelay")."\n"; + #print "treeget\n"; + #print Dumper \treeget($znctree, "User", "Node"); + #print Dumper \treeget($znctree, "User", "Network", "Node"); +} + +# parseml($tree, @lines) +# tree is a reference to a hash +# returns hash ref of tree +sub parseml { + my ($tree, @lines) = @_; + #if (scalar(@lines) == 0) { return $tree; } + while (scalar(@lines) > 0) { + my $line = shift(@lines); + if ($line =~ /^\s*([^=<>\s]+)\s*=\s*([^=<>]+)\s*$/) { + my ($tag, $val) = ($1, $2); + $tree->{$tag} = $val; + } elsif ($line =~ /^\/\//) { # skip comments + } elsif ($line =~ /^\s*$/) { # skip blank lines + } elsif ($line =~ /^\s*<([^>\s\/]+)\s*([^>\/]*)>\s*$/) { + my ($tag, $val) = ($1, $2); + if (!defined($tree->{$tag})) { $tree->{$tag} = []; } + my @newlines; + while (scalar(@lines) > 0) { + my $line = shift(@lines); + if ($line =~ /^\s*<\/$tag>\s*$/) { + my $subtree = parseml({ Node => $val }, @newlines); + push(@{$tree->{$tag}}, $subtree); + return parseml($tree, @lines); + } + push(@newlines, $line); + } + } else { print "ERROR: $line\n"; } + #TODO ERRORS not defined?? +# } else { main::debug(ERRORS, "ERROR: $line"); } + } + return $tree; +} + +#Returns array of all values +#treeget($tree, "User"); +#treeget($tree, "MaFFia Network"); +sub treeget { + my ($tree, @keys) = @_; + my $subtree; + my @rest = @keys; + my $key = shift(@rest); + $subtree = $tree->{$key}; + if (!defined($subtree)) { + return ("Undefined"); + } elsif (ref($subtree) eq 'HASH') { + return treeget($subtree, @rest); + } elsif (ref($subtree) eq 'ARRAY') { + my @array = @{$subtree}; + my @ret; + foreach my $hashref (@array) { + push(@ret, treeget($hashref, @rest)); + } + return @ret; + #my @array = @{$subtree}; + #print Dumper treeget($hashref, @rest); + #print Dumper treeget({$key => $subtree}, @rest); + #return (treeget($hashref, @rest), treeget({$key => $subtree}, @rest)); + } else { + return ($subtree); + } +} + +sub mbnc { + my ($bot, $nick, $host, $hand, @args) = @_; + my ($chan, $text); + if (@args == 2) { + ($chan, $text) = ($args[0], $args[1]); + } else { $text = $args[0]; } + my $hostmask = "$nick!$host"; + if (defined($chan) && $chans =~ /$chan/) { + main::putserv($bot, "PRIVMSG $chan :$nick: Please check private message"); + } + if ($text =~ /^$/) { + main::putserv($bot, "PRIVMSG $nick :Type !help for new instructions"); + foreach my $chan (@teamchans) { + main::putservlocalnet($bot, "PRIVMSG $chan :Help *$nick* on ".$bot->{name}); + } + return; + } elsif (main::isstaff($bot, $nick) && $text =~ /^delete\s+([[:ascii:]]+)/) { + my $username = $1; + if (SQLite::deleterows("bnc", "username", $username)) { + main::putserv($bot, "PRIVMSG *controlpanel :deluser $username"); + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :$username deleted"); + } + } + return; + } elsif ($staff =~ /$nick/ && $text =~ /^cloneuser$/i) { + main::putserv($bot, "PRIVMSG *controlpanel :deluser cloneuser"); + sleep 3; + main::putserv($bot, "PRIVMSG *controlpanel :get Nick cloneuser"); + } + ### TODO: Check duplicate emails ### + my @rows = SQLite::selectrows("irc", "hostmask", $hostmask); + foreach my $row (@rows) { + my $password = SQLite::get("bnc", "ircid", $row->{id}, "password"); + if (defined($password)) { + main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help."); + return; + } + } + if ($text =~ /^captcha\s+([[:alnum:]]+)/) { + my $text = $1; + # TODO avoid using host mask because cloaking can cause problems + my $ircid = SQLite::id("irc", "nick", $nick, $expires); + my $captcha = SQLite::get("bnc", "ircid", $ircid, "captcha"); + if ($text ne $captcha) { + main::putserv($bot, "PRIVMSG $nick :Wrong captcha. To get a new captcha, type !bnc "); + return; + } + my $pass = Hash::newpass(); + chomp(my $encrypted = `encrypt $pass`); + my $username = SQLite::get("bnc", "ircid", $ircid, "username"); + my $email = SQLite::get("bnc", "ircid", $ircid, "email"); + my $hashirc = SQLite::get("irc", "id", $ircid, "hashid"); + my $bindhost = "$username.$hostname"; + SQLite::set("bnc", "ircid", $ircid, "password", $encrypted); + if (DNS::nextdns($username)) { + sleep(2); + createbnc($bot, $username, $pass, $bindhost); + main::putserv($bot, "PRIVMSG $nick :Check your email!"); + mailbnc($username, $email, $pass, "bouncer", $hashirc); + #www($newnick, $reply, $password, "bouncer"); + } else { + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :Assigning bindhost $bindhost failed"); + } + } + return; + } elsif ($text =~ /^([[:alnum:]]+)\s+([[:ascii:]]+)/) { + my ($username, $email) = ($1, $2); +# my @users = treeget($znctree, "User", "Node"); + foreach my $user (@users) { + if ($user eq $username) { + main::putserv($bot, "PRIVMSG $nick :Sorry, username taken. Please contact staff if you need help."); + return; + } + } + #my $captcha = join'', map +(0..9,'a'..'z','A'..'Z')[rand(10+26*2)], 1..4; + my $captcha = int(rand(999)); + my $ircid = int(rand(9223372036854775807)); + my $hashid = sha256_hex("$ircid"); + SQLite::set("irc", "id", $ircid, "localtime", time()); + SQLite::set("irc", "id", $ircid, "hashid", sha256_hex($ircid)); + SQLite::set("irc", "id", $ircid, "date", main::date()); + SQLite::set("irc", "id", $ircid, "hostmask", $hostmask); + SQLite::set("irc", "id", $ircid, "nick", $nick); + SQLite::set("bnc", "ircid", $ircid, "username", $username); + SQLite::set("bnc", "ircid", $ircid, "email", $email); + SQLite::set("bnc", "ircid", $ircid, "captcha", $captcha); + SQLite::set("bnc", "ircid", $ircid, "hashid", $hashid); + main::whois($bot->{sock}, $nick); + main::ctcp($bot->{sock}, $nick); + main::putserv($bot, "PRIVMSG $nick :".`figlet $captcha`); + main::putserv($bot, "PRIVMSG $nick :https://$hostname/$hashid/captcha.png"); + main::putserv($bot, "PRIVMSG $nick :https://$hostname/register.php?hashirc=$hashid"); + main::putserv($bot, "PRIVMSG $nick :Type !bnc captcha "); + foreach my $chan (@teamchans) { + main::putservlocalnet($bot, "PRIVMSG $chan :$nick\'s on $bot->{name} bnc captcha is $captcha"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :Invalid username or email. Type !bnc to try again."); + foreach my $chan (@teamchans) { + main::putservlocalnet($bot, "PRIVMSG $chan :Help *$nick* on ".$bot->{name}); + } + } +} + +sub mregex { + my ($bot, $nick, $host, $hand, $text) = @_; + if (!main::isstaff($bot, $nick)) { return; } + if ($text =~ /^ips?\s+([-_()|0-9A-Za-z:\.?*\s]{3,})$/) { + my $ips = $1; # space-separated list of IPs + main::putserv($bot, "PRIVMSG $nick :".regexlist($ips)); + } elsif ($text =~ /^users?\s+([-_()|0-9A-Za-z:\.?*\s]{3,})$/) { + my $users = $1; # space-separated list of usernames + main::putserv($bot, "PRIVMSG $nick :".regexlist($users)); + } elsif ($text =~ /^[-_()|0-9A-Za-z:,\.?*\s]{3,}$/) { + my @lines = regex($text); + foreach my $l (@lines) { print "$l\n"; } + } +} +sub mforeach { + my ($bot, $nick, $host, $hand, $text) = @_; + if ($staff !~ /$nick/) { return; } + if ($text =~ /^network\s+del\s+([[:graph:]]+)\s+(#[[:graph:]]+)$/) { + my ($user, $chan) = ($1, $2); + foreach my $n (@main::networks) { + main::putserv($bot, "PRIVMSG *controlpanel :delchan $user $n->{name} $chan"); + } + } +} + +sub mcontrolpanel { + my ($bot, $nick, $host, $hand, @args) = @_; + my ($chan, $text); + if (@args == 2) { + ($chan, $text) = ($args[0], $args[1]); + } else { $text = $args[0]; } + my $hostmask = "$nick!$host"; + if($hostmask eq '*controlpanel!znc@znc.in') { + if ($text =~ /^Error: User \[cloneuser\] does not exist/) { + createclone($bot); + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :Cloneuser created"); + } + } elsif ($text =~ /^User (.*) added!$/) { + main::debug(ALL, "User $1 created"); + } elsif ($text =~ /^Password has been changed!$/) { + main::debug(ALL, "Password changed"); + } elsif ($text =~ /^Queued network (.*) of user (.*) for a reconnect.$/) { + main::debug(ALL, "$2 now connecting to $1..."); + } elsif ($text =~ /^Admin = false/) { + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :ERROR: $nick is not admin"); + } + die "ERROR: $nick is not admin"; + } elsif ($text =~ /^Admin = true/) { + main::debug(ALL, "$nick is ZNC admin"); + } elsif ($text =~ /(.*) = (.*)/) { + my ($key, $val) = ($1, $2); + main::debug(ALL, "ZNC: $key => $val"); + } else { + main::debug(ERRORS, "Unexpected 290 BNC.pm: $hostmask $text"); + } + } +} +sub loadlog { + open(my $fh, '<', "$znclog") or die "Could not read file 'znc.log' $!"; + chomp(@logs = <$fh>); + close $fh; +} + +# return all lines matching a pattern +sub regex { + my ($pattern) = @_; + if (!@logs) { loadlog(); } + return grep(/$pattern/, @logs); +} + +# given a list of IPs, return matching users +# or given a list of users, return matching IPs +sub regexlist { + my ($items) = @_; + my @items = split /[,\s]+/m, $items; + my $pattern = "(".join('|', @items).")"; + if (!@logs) { loadlog(); } + my @matches = grep(/$pattern/, @logs); + my @results; + foreach my $match (@matches) { + if ($match =~ /^\[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\] \[([^]\/]+)(\/[^]]+)?\] connected to ZNC from (.*)/) { + my ($user, $ip) = ($1, $3); + if ($items =~ /[.:]/) { # items are IP addresses + push(@results, $user); + } else { # items are users + push(@results, $ip); + } + } + } + my @sorted = sort @results; + @results = do { my %seen; grep { !$seen{$_}++ } @sorted }; # uniq + return join(' ', @results); +} + +sub createclone { + my ($bot) = @_; + my $socket = $bot->{sock}; + my $password = Hash::newpass(); + my $msg = <<"EOF"; +adduser cloneuser $password +set Nick cloneuser cloneuser +set Altnick cloneuser cloneuser_ +set Ident cloneuser cloneuser +set RealName cloneuser cloneuser +set MaxNetworks cloneuser 1000 +set ChanBufferSize cloneuser 1000 +set MaxQueryBuffers cloneuser 1000 +set QueryBufferSize cloneuser 1000 +set NoTrafficTimeout cloneuser 600 +set QuitMsg cloneuser IRCNow and Forever! +set RealName cloneuser cloneuser +set DenySetBindHost cloneuser true +set Timezone cloneuser US/Pacific +LoadModule cloneuser controlpanel +LoadModule cloneuser chansaver +EOF +#LoadModule cloneuser buffextras + main::putserv($bot, "PRIVMSG *controlpanel :$msg"); + foreach my $n (@main::networks) { + my $net = $n->{name}; + my $server = $n->{server}; + my $port = $n->{port}; + my $trustcerts = $n->{trustcerts}; + $msg = <<"EOF"; +addnetwork cloneuser $net +addserver cloneuser $net $server $port +disconnect cloneuser $net +EOF + if ($trustcerts) { + $msg .= "SetNetwork TrustAllCerts cloneuser $net True\r\n"; + } + my @chans = split /[,\s]+/m, $chans; + foreach my $chan (@chans) { + $msg .= "addchan cloneuser $net $chan\r\n"; + } + main::putserv($bot, "PRIVMSG *controlpanel :$msg"); + } +} + +sub createbnc { + my ($bot, $username, $password, $bindhost) = @_; + my $netname = $bot->{name}; + my $msg = <<"EOF"; +cloneuser cloneuser $username +set Nick $username $username +set Altnick $username ${username}_ +set Ident $username $username +set RealName $username $username +set Password $username $password +set MaxNetworks $username 1000 +set ChanBufferSize $username 1000 +set MaxQueryBuffers $username 1000 +set QueryBufferSize $username 1000 +set NoTrafficTimeout $username 600 +set QuitMsg $username IRCNow and Forever! +set BindHost $username $bindhost +set DCCBindHost $username $bindhost +set DenySetBindHost $username true +reconnect $username $netname +EOF +#set Language $username en-US + main::putserv($bot, "PRIVMSG *controlpanel :$msg"); + return 1; +} +sub mailbnc { + my( $username, $email, $password, $service, $hashirc )=@_; + my $passhash = sha256_hex("$username"); + +my $body = <<"EOF"; +You created a bouncer! + +Username: $username +Password: $password +Server: $hostname +Port: $sslport for SSL (secure connection) +Port: $plainport for plaintext + +*IMPORTANT*: Verify your email address: + +https://$hostname/register.php?hashirc=$hashirc + +You *MUST* click on the link or your account will be deleted. + +IRCNow +EOF + Mail::mail($mailfrom, $email, $mailname, "Verify IRCNow Account", $body); +} + +sub mtaillog { + my ($bot, $nick, $host, $hand, @args) = @_; + my ($chan, $text); + if (@args == 2) { + ($chan, $text) = ($args[0], $args[1]); + } else { $text = $args[0]; } + my $hostmask = "$nick!$host"; + open(my $fh, "-|", "/usr/bin/tail", "-f", $znclog) or die "could not start tail: $!"; + while (my $line = <$fh>) { + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :$line"); + } + } +} + +sub mlastseen { + my ($bot, $nick, $host, $hand, @args) = @_; + my ($chan, $text); + if (@args == 2) { + ($chan, $text) = ($args[0], $args[1]); + } else { $text = $args[0]; } + my $hostmask = "$nick!$host"; + if (!@logs) { loadlog(); } + my @users = treeget($znctree, "User", "Node"); + foreach my $user (@users) { + my @lines = grep(/^\[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\] \[$user\] connected to ZNC from [.0-9a-fA-F:]+/, @logs); + if (scalar(@lines) == 0) { + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :$user never logged in"); + } + next; + } + my $recent = pop(@lines); + if ($recent =~ /^\[(\d{4}-\d\d-\d\d) \d\d:\d\d:\d\d\] \[$user\] connected to ZNC from [.0-9a-fA-F:]+/) { + my $date = $1; + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :$user $date"); + } + } + } +} +#sub resend { +# my ($bot, $newnick, $email) = @_; +# my $password = newpass(); +# sendmsg($bot, "*controlpanel", "set Password $newnick $password"); +# mailverify($newnick, $email, $password, "bouncer"); +# sendmsg($bot, "$newnick", "Email sent"); +#} + +# if ($reply =~ /^!resend ([-_0-9a-zA-Z]+) ([-_0-9a-zA-Z]+@[-_0-9a-zA-Z]+\.[-_0-9a-zA-Z]+)$/i) { +# my ($newnick, $email) = ($1, $2); +# my $password = newpass(); +# resend($bot, $newnick, $email); +# } + +#sub resetznc { +# +#AnonIPLimit 10000 +#AuthOnlyViaModule false +#ConnectDelay 0 +#HideVersion true +#LoadModule +#ServerThrottle +#1337 209.141.38.137 +#31337 209.141.38.137 +#1337 2605:6400:20:5cc:: +#31337 2605:6400:20:5cc:: +#1337 127.0.0.1 +#1338 127.0.0.1 +#} +# +#alias Provides bouncer-side command alias support. +#autoreply Reply to queries when you are away +#block_motd Block the MOTD from IRC so it's not sent to your client(s). +#bouncedcc Bounces DCC transfers through ZNC instead of sending them directly to the user. +#clientnotify Notifies you when another IRC client logs into or out of your account. Configurable. +#ctcpflood Don't forward CTCP floods to clients +#dcc This module allows you to transfer files to and from ZNC +#perform Keeps a list of commands to be executed when ZNC connects to IRC. +#webadmin Web based administration module. + + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + e660d9b341956b454835944c333fd87d803893d6 (mode 644) --- /dev/null +++ Commands @@ -0,0 +1,107 @@ +Output +======== + +putserv BOT TEXT + +Sends text to the IRC server. Returns nothing. + +Bind +======== + +bind TYPE FLAGS MASK PROC + +Binds perl procedures to events. Currently flags and mask are ignored. Returns the name of the command that was added. + +1. MSG + +bind "MSG" FLAGS COMMAND PROC +procname BOT NICK USERHOST HANDLE TEXT + +Called on /msg commands. The first word of the msg is the command, and the rest +the text. + +bind("msg", "", "admin", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $text) = @_; + ... +} + +2. PUB + +bind "PUB" FLAGS COMMAND PROC +procname BOT NICK USERHOST HANDLE CHANNEL TEXT + +bind("pub", "", "help", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $chan, $text) = @_; + ... +} + +Called on commands in a channel. The first word of the msg is the command, and the rest +the text. + +3. MSGM (stackable) + +bind "MSGM" FLAGS MASK PROC +procname BOT NICK USERHOST HANDLE TEXT + +bind("msgm", "", "", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $text) = @_; + ... +} + +Match all text from a /msg. MSGM binds are processed before MSG binds. + +4. PUBM (stackable) + +bind "PUBM" FLAGS MASK PROC +procname BOT NICK USERHOST HANDLE CHAN TEXT + +bind("pubm", "", "", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $chan, $text) = @_; + ... +} + +Match all text from a message on a channel. PUBM binds are processed before PUB binds. + +5. NOTC (stackable) + +bind "NOTC" FLAGS MASK PROC +procname BOT NICK USERHOST HANDLE TEXT DEST + +bind("notc", "", "", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $text, $dest) = @_; + ... +} + +Called when a notice is sent. $dest is either the bot's nickname or channel. +You should not respond to a /notice, so this is useful for logging and analytics. + +6. JOIN (stackable) + +bind "JOIN" FLAGS MASK PROC +procname BOT NICK USERHOST HANDLE CHANNEL + +bind("join", "", "", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $chan) = @_; + ... +} + +Called when someone joins a channel. + +7. PART (stackable) + +bind "PART" FLAGS MASK PROC +procname BOT NICK USERHOST HANDLE CHANNEL TEXT + +bind("part", "", "", $proc); +sub proc { + my ($bot, $nick, $userhost, $hand, $chan, $text) = @_; + ... +} + +Called when someone parts a channel. blob - /dev/null blob + ba1da2271e5ace5b0c47eeb900723f05eab2ddb0 (mode 644) --- /dev/null +++ DNS.pm @@ -0,0 +1,269 @@ +#!/usr/bin/perl + +package DNS; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use Data::Dumper; +use File::Copy qw(copy); + +my %conf = %main::conf; +my $chans = $conf{chans}; +my $staff = $conf{staff}; +my $key = $conf{key}; +my $hash = $conf{hash}; +my $hostname = $conf{hostname}; +my $verbose = $conf{verbose}; +my $ip4 = $conf{ip4}; +my $ip6 = $conf{ip6}; +my $ip6subnet = $conf{ip6subnet}; +my $zonedir = $conf{zonedir}; +my $hostnameif = $conf{hostnameif}; +if (host($hostname) =~ /(\d+\.){3,}\d+/) { + $ip4 = $&; +} +main::cbind("msg", "-", "setrdns", \&msetrdns); +main::cbind("msg", "-", "delrdns", \&mdelrdns); +main::cbind("msg", "-", "setdns", \&msetdns); +main::cbind("msg", "-", "deldns", \&mdeldns); +main::cbind("msg", "-", "host", \&mhost); +main::cbind("msg", "-", "nextdns", \&mnextdns); +main::cbind("msg", "-", "readip6s", \&mreadip6s); + +sub init { + unveil("$zonedir", "rwc") or die "Unable to unveil $!"; + unveil("/usr/bin/doas", "rx") or die "Unable to unveil $!"; + unveil("/usr/bin/host", "rx") or die "Unable to unveil $!"; + unveil("$hostnameif", "rwc") or die "Unable to unveil $!"; +} + +# !setrdns 2001:bd8:: username.example.com +sub msetrdns { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([0-9A-Fa-f:\.]{3,})\s+([-0-9A-Za-z\.]+)$/) { + my ($ip, $hostname) = ($1, $2); + if (setrdns($ip, $ip6subnet, $hostname)) { + main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip"); + } else { + main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS"); + } + } +} + +# !delrdns 2001:bd8:: +sub mdelrdns { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([0-9A-Fa-f:\.]{3,})$/) { + my ($ip) = ($1); + if (delrdns($ip, $ip6subnet)) { + main::putserv($bot, "PRIVMSG $nick :$ip rDNS deleted"); + } else { + main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS"); + } + } +} +# !setdns username 1.2.3.4 +sub msetdns { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-0-9A-Za-z\.]+)\s+([0-9A-Fa-f:\.]+)/) { + my ($name, $value) = ($1, $2); + if ($value =~ /:/ and setdns($name, $hostname, "AAAA", $value)) { + main::putserv($bot, "PRIVMSG $nick :$name.$hostname AAAA set to $value"); + } elsif (setdns($name, $hostname, "A", $value)) { + main::putserv($bot, "PRIVMSG $nick :$name.$hostname A set to $value"); + } else { + main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set DNS"); + } + } +} + +# !deldns username +sub mdeldns { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-0-9A-Za-z\.]+)$/) { + my ($name) = ($1); + if (setdns($name, $hostname)) { + main::putserv($bot, "PRIVMSG $nick :$text deleted"); + } else { + main::putserv($bot, "PRIVMSG $nick :ERROR: failed to delete DNS records"); + } + } +} + +# !host username +sub mhost { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-0-9A-Za-z:\.]{3,})/) { + my ($hostname) = ($1); + main::putserv($bot, "PRIVMSG $nick :".host($hostname)); + } +} + +# !nextdns username +sub mnextdns { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-0-9a-zA-Z]+)/) { + main::putserv($bot, "PRIVMSG $nick :$text set to ".nextdns($text)); + } +} + +# !readip6s +sub mreadip6s { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + foreach my $line (readip6s($hostnameif)) { + print "$line\n" + } +} + +# Return list of ipv6 addresses from filename +sub readip6s { + my ($filename) = @_; + my @lines = main::readarray($filename); + my @ipv6s; + foreach my $line (@lines) { + if ($line =~ /^\s*inet6\s+(alias\s+)?([0-9a-f:]{4,})\s+[0-9]+\s*$/i) { + push(@ipv6s, $2); + } elsif ($line =~ /^\s*([0-9a-f:]{4,})\s*$/i) { + push(@ipv6s, $1); + } + } + return @ipv6s; +} + +# set rdns of $ip6 to $hostname given $subnet +# return true on success; false on failure +sub setrdns { + my ($ip6, $subnet, $hostname) = @_; + my $digits = ip6full($ip6); + $digits =~ tr/://d; + my $reversed = reverse($digits); + my $origin = substr($reversed, 32-$subnet/4); + $origin = join('.', split(//, $origin)).".ip6.arpa"; + my $name = substr($reversed, 0, 32-$subnet/4); + $name = join('.', split(//, $name)); + # delete old PTR records, then set new one + return setdns($name, $origin) && setdns($name, $origin, "PTR", $hostname); +} +# delete rdns of $ip6 given $subnet +# return true on success; false on failure +sub delrdns { + my ($ip6, $subnet) = @_; + return setrdns($ip6, $subnet); +} + +# given $origin. create $name RR of $type and set to $value if provided; +# if $value is missing, delete $domain +# returns true upon success, false upon failure +sub setdns { + my ($name, $origin, $type, $value) = @_; + my $filename = "$zonedir/$origin"; + my @lines = main::readarray($filename); + foreach my $line (@lines) { + # increment the zone's serial number + if ($line =~ /(\d{8})(\d{2})((\s+\d+){4}\s*\))/) { + my $date = main::date(); + my $serial = 0; + if ($date <= $1) { $serial = $2+1; } + $line = $`.$date.sprintf("%02d",$serial).$3.$'; + } + } + if (!defined($value)) { # delete records + @lines = grep !/\b$name\s*3600\s*IN/, @lines; + } else { + push(@lines, "$name 3600 IN $type $value"); + } + # trailing newline necessary + main::writefile("$filename.bak", join("\n", @lines)."\n"); + copy "$filename.bak", $filename; + if (system("doas -u _nsd nsd-control reload")) { + return 0; + } else { + return 1; + } +} + +# given hostname, return IP addresses; or given IP address, return hostname +sub host { + my ($name) = @_; + my @matches; + my @lines = split /\n/m, `host $name`; + if ($name =~ /^[0-9\.]+$/ or $name =~ /:/) { # IP address + foreach my $line (@lines) { + if ($line =~ /([\d\.]+).(in-addr|ip6).arpa domain name pointer (.*)/) { + push(@matches, $3); + } + } + } else { # hostname + foreach my $line (@lines) { + if ($line =~ /$name has (IPv6 )?address ([0-9a-fA-F\.:]+)/) { + push(@matches, $2); + } + } + } + return join(' ', @matches); +} + +# Return an ipv6 address with all zeroes filled in +sub ip6full { + my ($ip6) = @_; + my $left = substr($ip6, 0, index($ip6, "::")); + my $leftcolons = ($left =~ tr/://); + $ip6 =~ s{::}{:}; + my @quartets = split(':', $ip6); + my $length = scalar(@quartets); + for (my $n = 1; $n <= 8 - $length; $n++) { + splice(@quartets, $leftcolons+1, 0, "0000"); + } + my @newquartets = map(sprintf('%04s', $_), @quartets); + my $full = join(':',@newquartets); + return $full; +} +# Returns the network part of the first IPv6 address (indicated by subnet) +# with the host part of the second IPv6 address +sub ip6mask { + my ($ip6net, $subnet, $ip6host) = @_; + my $netdigits = ip6full($ip6net); + $netdigits =~ tr/://d; + my $hostdigits = ip6full($ip6host); + $hostdigits =~ tr/://d; + my $digits = substr($netdigits,0,($subnet/4)).substr($hostdigits,($subnet/4)); + my $ip6; + for (my $n = 0; $n < 32; $n++) { + if ($n > 0 && $n % 4 == 0) { + $ip6 .= ":"; + } + $ip6 .= substr($digits,$n,1); + } + return $ip6; +} +sub randip6 { + return join ':', map { sprintf '%04x', rand 0x10000 } (1 .. 8); +} + +# create A and AAAA records for subdomain, set the rDNS, +# and return the new ipv6 address +sub nextdns { + my ($subdomain) = @_; + my $newip6 = $ip6; + my @allip6s = readip6s($hostnameif); + while (grep(/$newip6/, @allip6s)) { + $newip6 = ip6mask($ip6, $ip6subnet,randip6()); + } + main::appendfile($hostnameif, "inet6 alias $newip6 48\n"); + `doas ifconfig vio0 inet6 $newip6/48`; + if (setdns($subdomain, $hostname, "A", $ip4) && setdns($subdomain, $hostname, "AAAA", $newip6) && setrdns($newip6, $ip6subnet, "$subdomain.$hostname")) { + return "$newip6"; + } + return "false"; +} + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + b0f8dc9f896801ed80cf43e99367f31ffcf14a67 (mode 644) --- /dev/null +++ Hash.pm @@ -0,0 +1,44 @@ +#!/usr/bin/perl + +package Hash; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use Data::Dumper; + +my %conf = %main::conf; +my @words; +my $wordspath = "words"; +my $passlength = $conf{passlength}; +# dictionary words for passwords +@words = main::readarray("words"); + +sub init { + unveil($wordspath, "r") or die "Unable to unveil $!"; +} + +sub newpass { + my $len = scalar @words; + my $pass; + for (my $i=0; $i < $passlength; $i++) { + my $word = $words[int(rand($len))]; + $word =~ s/(\w+)/\u$1/g; + $pass .= $word; + } + return $pass; +} +#dependencies for blowfish +#unveil("./blowfish.o", "rx") or die "Unable to unveil $!"; +# } elsif ($reply =~ /^!identify\s*(.*)?\s+(.*)$/i) { +# my $hash = getkeyval($hostmask, "password"); +# #print "result = ".`./blowfish.o $2 '$hash'`; +# if(system("./blowfish.o $2 '$hash' > /dev/null")) { +# print "login failed\r\n"; +# } else { +# print "logged in\r\n"; +# } + + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + 6f51bbd251c5e01b86dd3b6b666dfba65cc531ef (mode 644) --- /dev/null +++ Help.pm @@ -0,0 +1,63 @@ +#!/usr/bin/perl + +package Help; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; + +my %conf = %main::conf; +my $chans = $conf{chans}; +my $teamchans = $conf{teamchans}; +my @teamchans = split /[,\s]+/m, $teamchans; +my $staff = $conf{staff}; +my $terms = $conf{terms}; +my $time = "600"; +main::cbind("pub", "-", "help", \&help); +main::cbind("msg", "-", "help", \&help); +main::cbind("pub", "-", "request", \&help); + +sub init { +} + +sub help { + my ($bot, $nick, $host, $hand, @args) = @_; + my ($chan, $text); + my $msg = <<"EOF"; +$terms +To request a free bouncer, type !bnc . For example, !bnc john john\@example.com. +To request a free shell account, type !shell . For example, !shell john john\@example.com. +EOF +#To request a free email account, type !email . For example, !email john john\@example.com. +#To request a free VPN account, type !vpn . For example, !vpn john john\@example.com. + if (main::isstaff($bot, $nick)) { + $msg .= <<"EOF"; +To delete a bouncer, type !bnc delete +To verify a captcha, type !bnc captcha +To recreate cloneuser, type !bnc cloneuser +To get a list of usernames that match IPs, type !regex ips +To get a list of IPs that match usernames, type !regex users +To regex search znc.log and output to the terminal, type !regex +To delete a shell account, type !shell delete +To verify a captcha, type !shell captcha +EOF +#To get a list of usernames that match IPs, type !shell regex ips +#To get a list of IPs that match usernames, type !shell regex users +#To regex search znc.log and output to the terminal, type !shell regex + } + if (@args == 2) { + ($chan, $text) = ($args[0], $args[1]); + if ($chans =~ $chan) { + main::putserv($bot, "PRIVMSG $chan :$nick: Please see private message."); + } + } else { + $text = $args[0]; + } + main::putserv($bot, "PRIVMSG $nick :$msg"); + foreach my $chan (@teamchans) { + main::putservlocalnet($bot, "PRIVMSG $chan :Help *$nick* on ".$bot->{name}); + } +} + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + 294ed0bb4de2e8349d901cdf80cb2691b30aa282 (mode 644) --- /dev/null +++ LICENSE @@ -0,0 +1,10 @@ +Copyright (C) 2020 by jrmu + +Permission is granted to use, copy, modify, and/or distribute +this work for any purpose with or without fee. This work is offered +as-is, with absolutely no warranty whatsoever. The author is not +responsible for any damages that result from using this work. + +The file Words comes from: + +https://github.com/first20hours/google-10000-english/raw/master/google-10000-english-usa-no-swears-medium.txt blob - /dev/null blob + 783bd370cc80eaccb3c1fbdaa6748c3e8abcf3a1 (mode 644) --- /dev/null +++ Mail.pm @@ -0,0 +1,103 @@ +#!/usr/bin/perl + +package Mail; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use MIME::Base64; +use Digest::SHA qw(sha256_hex); + +my %conf = %main::conf; +my $staff = $conf{staff}; +my $hostname = $conf{hostname}; +my $mailfrom = $conf{mailfrom}; +my $mailname = $conf{mailname}; +main::cbind("msg", "-", "mail", \&mmail); + +sub init { + #dependencies for encrypt + unveil("/usr/bin/encrypt", "rx") or die "Unable to unveil $!"; + #dependencies for mail + unveil("/usr/sbin/sendmail", "rx") or die "Unable to unveil $!"; + unveil("/usr/lib/libutil.so.13.1", "r") or die "Unable to unveil $!"; + unveil("/bin/sh", "rx") or die "Unable to unveil $!"; +} +sub mmail { + my ($bot, $nick, $host, $hand, $text) = @_; + if ($staff !~ /$nick/) { return; } + if ($text =~ /^([-_0-9a-zA-Z~@!\.]{3,})\s+([-_0-9a-zA-Z~@!\.]{3,})/) { + my ($from, $to) = ($1, $2); + if (mail($from, $to, "support", "alpha bravo", "charlie delta echo foxtrot")) { + main::putserv($bot, "PRIVMSG $nick :mail sent from $from to $to"); + } else { + main::putserv($bot, "PRIVMSG $nick :ERROR: failed to send mail"); + } + } +} + +sub mail { + my( $from, $to, $fromname, $subject, $body )=@_; +my $msg = <<"EOF"; +From: $from +To: $to +Subject: $subject +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline + +$body +EOF +open(my $fh, "| /usr/sbin/sendmail -tv -F '$fromname' -f $from") or die "Could not send mail $!"; +print $fh $msg; +close $fh; +return "true"; +} + + +#sub mailfinish { +# my( $username, $password, $email, $service )=@_; +#my $msg = <<"EOF"; +#From: support \ +#To: $email +#Subject: Welcome to IRCNow! +#MIME-Version: 1.0 +#Content-Type: text/plain; charset=us-ascii +#Content-Disposition: inline +# +#Welcome to IRCNow! +# +#Your account $username with password $password is now activated. +# +#For instructions on how to connect, please visit: https://ircnow.org +# +#For help, please visit our support channel on irc.ircnow.org #ircnow. +# +#IRCNow +#EOF +#open(my $fh, '| /usr/sbin/sendmail -tv -F support -f support@ircnow.org') or die "Could not send mail $!"; +#print $fh $msg; +#close $fh; +#open($fh, '>>', "$database") or die "Could not open file '$database' $!"; +#print $fh $msg; +#close $fh; +#} +# + +#sub createmail { +# my ($password, $username) = @_; +# my $encrypted = `encrypt $password`; +# chomp($encrypted); +# my $line = "${username}\@ircnow.org:${encrypted}::::::userdb_quota_rule=*:storage=1G"; +# $line =~ s{\$}{\\\$}g; +# my $line2 = "${username}\@ircnow.org vmail"; +# my $line3 = "${username}: ${username}\@ircnow.org"; +# `doas sh -c 'echo $line >> /etc/mail/passwd'`; +# `doas sh -c 'echo $line2 >> /etc/mail/virtuals'`; +# `doas sh -c 'echo $line3 >> /etc/mail/aliases'`; +# `doas rcctl restart smtpd`; +# `doas rcctl reload dovecot`; +#} + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + f83d9cb0476bb9ad8fdb7b90791255b2cb158f1b (mode 644) --- /dev/null +++ README @@ -0,0 +1,61 @@ +botnow: the versatile IRC bot + +botnow has only been tested on openbsd 6.9 + +### System requirements ### + +In order to install botnow, you will need to have the following installed and +configured: + +1) sendmail (https://wiki.ircnow.org/index.php?n=Opensmtpd.Configure) +2) nsd (https://wiki.ircnow.org/index.php?n=Nsd.Configure) +3) openhttpd (https://wiki.ircnow.org/index.php?n=Openhttpd.Configure) +4) php (https://wiki.ircnow.org/index.php?n=Openbsd.Php) +5) znc (https://wiki.ircnow.org/index.php?n=Znc.Chroot) +6) IPv6 (https://wiki.ircnow.org/index.php?n=Hostnameif.Static) + +### Install instructions ### + +$ tar xvzf botnow.tgz +$ cd botnow +$ doas make +$ doas su botnow +$ cd /home/botnow/ +$ cp botnow.conf.example botnow.conf + +Fill the file ipv6s with all the unused IPv6 addresses available on your server. + +$ vi botnow.conf +$ ./botnow.pl + +### Configuration of httpd.conf ### + +server "www.$hostname" { + alias "$hostname" + listen on $ext_ip port 80 + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } + location "*.php" { + fastcgi socket "/run/php-fpm.sock" + } + root "/htdocs/ircnow/" +} + +### Reinstall ### + +If you need to reinstall, run + +$ doas make -i + +### Changelog ### + +Version 0.07: Added support for requesting shell accounts +Version 0.06: Refactored code to be modular +Version 0.05: Refactored conf file out of the script and supplied sensible defaults +Version 0.04: Switched from flatfiles to sqlite for user metadata +Version 0.03: Added new DNS commands +Version 0.02: Updated wiki pages, added warnings to common errors, added support +for trustallcerts +Version 0.01: First public version of botnow blob - /dev/null blob + 463e5263fa5b53ebabf0ad940cbae3e69e534db4 (mode 644) --- /dev/null +++ SQLite.pm @@ -0,0 +1,318 @@ +#!/usr/bin/perl + +package SQLite; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use Data::Dumper; +use DBI; +use DBD::SQLite; + +use constant { + NONE => 0, + ERRORS => 1, + WARNINGS => 2, + ALL => 3, +}; +my %conf = %main::conf; +my $staff = $conf{staff}; +my $dbh; +my $verbose = $conf{verbose}; +my $dbpath = "/var/www/botnow/botnow.db"; +my $database = "/var/www/botnow/"; # database path +main::cbind("msg", "-", "get", \&mget); +main::cbind("msg", "-", "set", \&mset); +main::cbind("msg", "-", "connectdb", \&mconnectdb); +main::cbind("msg", "-", "insert", \&minsert); +main::cbind("msg", "-", "update", \&mupdate); +main::cbind("msg", "-", "delete", \&mdelete); +main::cbind("msg", "-", "select", \&mselect); + +sub init { + unveil("$dbpath", "rwc") or die "Unable to unveil $!"; + unveil("$dbpath-journal", "rwc") or die "Unable to unveil $!"; + unveil("$database", "rwxc") or die "Unable to unveil $!"; +} + +# !connectdb +sub mconnectdb { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if (connectdb()) { + main::putserv($bot, "PRIVMSG $nick :connectdb succeeded"); + } else { + main::putserv($bot, "PRIVMSG $nick :ERROR: connectdb failed"); + } +} + +# !insert +# Insert comma-separated keys and vals into table +sub minsert { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+([[:ascii:]]+)/) { + my ($table, $keys, $vals) = ($1, $2, $3); + # strings in the values must be quoted + if ($vals =~ s{,}{","}g) { $vals = '"'.$vals.'"'; } + if (insertrow($table, $keys, $vals)) { + main::putserv($bot, "PRIVMSG $nick :$table ($keys) => ($vals)"); + } else { + main::putserv($bot, "PRIVMSG $nick :$table insert failed"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :invalid insert"); + } +} + +# Set key = val where idkey = idval in table +# !update
+sub mupdate { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+(\S+)\s+([-_[:alnum:]]+)\s+(\S+)/) { + my ($table, $idkey, $idval, $key, $val) = ($1, $2, $3, $4, $5); + if (updaterow($table, $idkey, $idval, $key, $val)) { + main::putserv($bot, "PRIVMSG $nick :$table $key => $val where $idkey = $idval"); + } else { + main::putserv($bot, "PRIVMSG $nick :update failed"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :invalid update"); + } +} + +# Delete rows where key = val in table +# !delete
+sub mdelete { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_[:alnum:]]+)\s+(\S+)/) { + my ($table, $key, $val) = ($1, $2, $3); + if (deleterows($table, $key, $val)) { + main::putserv($bot, "PRIVMSG $nick :$table $key = $val deleted"); + } else { + main::putserv($bot, "PRIVMSG $nick :delete failed"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :invalid delete"); + } +} + +# Output rows where key = val in table +# !select
+sub mselect { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_[:alnum:]]+)\s+(\S+)/) { + my ($table, $key, $val) = ($1, $2, $3); + my @rows = selectrows($table, $key, $val); + if (@rows) { + foreach my $row (@rows) { + my @pairs; + foreach $key (keys %$row) { + my $val = $row->{$key} || ""; + push(@pairs, "$key => $val"); + } + main::putserv($bot, "PRIVMSG $nick :$table ".join(',', @pairs)); + } + } else { + main::putserv($bot, "PRIVMSG $nick :no results"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :select invalid"); + } +} + +# Get value of key where idkey = idval in table +# !get
+sub mget { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+(\S+)\s+([-_[:alnum:]]+)/) { + my ($table, $idkey, $idval, $key) = ($1, $2, $3, $4); + my $val = get($table, $idkey, $idval, $key); + if (defined($val)) { + main::putserv($bot, "PRIVMSG $nick :$table $key => $val where $idkey = $idval"); + } else { + main::putserv($bot, "PRIVMSG $nick :undefined"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :invalid get"); + } +} +# !set
+sub mset { + my ($bot, $nick, $host, $hand, $text) = @_; + if (! (main::isstaff($bot, $nick))) { return; } + if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+(\S+)\s+([-_[:alnum:]]+)\s+(\S+)/) { + my ($table, $idkey, $idval, $key, $val) = ($1, $2, $3, $4, $5); + if (set($table, $idkey, $idval, $key, $val)) { + main::putserv($bot, "PRIVMSG $nick :$table $key => $val where $idkey = $idval"); + } else { + main::putserv($bot, "PRIVMSG $nick :failed set"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :invalid set"); + } +} + +# Connect to database, creating table if necessary +# Returns true on success, false on failure +sub connectdb { + my $dsn = "dbi:SQLite:dbname=$dbpath"; + my $user = ""; + my $password = ""; + $dbh = DBI->connect($dsn, $user, $password, { + PrintError => 1, + RaiseError => 1, + AutoCommit => 1, + FetchHashKeyName => 'NAME_lc', + }) or die "Couldn't connect to database: " . $DBI::errstr; + if (!(-s "$dbpath")) { + my $sql = main::readstr('table.sql'); + my @sql = split /;/m, $sql; + foreach my $s (@sql) { + $dbh->do($s); + } + } + main::debug(ALL, "connected to $dbpath"); + return defined($dbh); +} + +# Inserts comma-separated keys and vals into table +# Returns number of rows successfully inserted +sub insertrow { + my ($table, $keys, $vals) = @_; + if (!defined($dbh)) { connectdb(); } + my $rows = $dbh->do("INSERT INTO $table ($keys) values ($vals)"); + if ($rows) { + main::debug(ALL, "INSERT INTO $table ($keys) values ($vals)"); + } else { + main::debug(ERRORS, "ERRORS: Failed INSERT INTO $table ($keys) values ($vals)"); + } + return $rows; +} + +# Update key, value pair for record where idkey equals idval in table +# Returns number of rows successfully updated +sub updaterow { + my ($table, $idkey, $idval, $key, $val) = @_; + if (!defined($dbh)) { connectdb(); } + my $rows = $dbh->do("UPDATE $table SET $key = ? where $idkey = ?", undef, $val, $idval); + if ($rows) { + main::debug(ALL, "UPDATE $table SET $key = $val where $idkey = $idval"); + } else { + main::debug(ERRORS, "ERRORS: Failed UPDATE $table SET $key = $val where $idkey = $idval"); + } + return $rows; +} + +# Delete records from $table where $key = $val +# Returns number of rows deleted +sub deleterows { + my ($table, $key, $val) = @_; + if (!defined($dbh)) { connectdb(); } + my $rows = $dbh->do("DELETE FROM $table WHERE $key = ?", undef, $val); + if ($rows) { + main::debug(ALL, "DELETE FROM $table WHERE $key = $val"); + } else { + main::debug(ERRORS, "ERRORS: Failed DELETE FROM $table WHERE $key = $val"); + } + return $rows; +} + +# Returns all records in the database +sub selectall { + my ($table) = @_; + if (!defined($dbh)) { connectdb(); } + my $sth = $dbh->prepare("SELECT * FROM $table"); + $sth->execute(); + my @results; + while (my $row = $sth->fetchrow_hashref) { + push(@results, $row); + } + return @results; +} + +# Returns all records from table where key equals value +sub selectrows { + my ($table, $key, $val) = @_; + if (!defined($dbh)) { connectdb(); } + my $sth = $dbh->prepare("SELECT * FROM $table WHERE $key = ?"); + $sth->execute($val); + my @results; + while (my $row = $sth->fetchrow_hashref) { + push(@results, $row); + } + return @results; +} + +# Returns list of tables +sub tables { + # if (!defined($dbh)) { connectdb(); } + # my $sth = $dbh->prepare(".tables"); + # $sth->execute($val); + # my @results; + # while (my $row = $sth->fetchrow_hashref) { + # push(@results, $row); + # } + # return @results; + return qw(bnc shell www irc smtp); +} + +# Returns value of key in record in table where idkey = idval +sub get { + my ($table, $idkey, $idval, $key) = @_; + if (!defined($dbh)) { connectdb(); } + my $sth = $dbh->prepare("SELECT * FROM $table WHERE $idkey = ?"); + $sth->execute($idval); + if (my $row = $sth->fetchrow_hashref) { + my $val = $row->{$key}; + if (!defined($val)) { $val = "undefined"; } + main::debug(ALL, "get: $table $key => $val where $idkey = $idval"); + return $row->{$key}; + } else { + main::debug(ERRORS, "ERRORS: $table $key undefined where $idkey = $idval"); + return; + } +} + +# Sets value of key in the record in table where idkey = idval +# Returns true on success; false on failure +sub set { + my ($table, $idkey, $idval, $key, $val) = @_; + if (defined(get($table, $idkey, $idval, $idkey))) { + main::debug(ALL, "set: update"); + return updaterow($table, $idkey, $idval, $key, $val) > 0; + } else { + main::debug(ALL, "set: insert"); + return insertrow($table, "$idkey,$key", "\"$idval\",\"$val\"") > 0; + } +} + +# given a key, val pair in table, return the id that falls within expires seconds +sub id { + my ($table, $key, $val, $expires) = @_; + my @rows = selectrows($table, $key, $val); + if (scalar(@rows) == 0) { + print "table => $table, key => $key, val => $val\n\n"; + } + my $maxrow; + foreach my $row (@rows) { + if (!defined($maxrow)) { $maxrow = $row; } + if ($row->{localtime} > $maxrow->{localtime}) { + $maxrow = $row; + } + } + if (abs(time() - $maxrow->{localtime}) <= $expires) { + main::debug(ALL, "id: $maxrow->{id} where $key = $val at $expires"); + return $maxrow->{id}; + } else { + main::debug(ERRORS, "no id found"); + return; + } +} + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + 5f27c60c151c0dfefff4dbc5ac70e4099004e6b9 (mode 644) --- /dev/null +++ Sh.pm @@ -0,0 +1,48 @@ +#!/usr/bin/perl + +package Shell; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use Data::Dumper; + +my $authlog = "/var/log/authlog"; +my $etcpasswd = "/etc/master.passwd"; +my @etcpasswd = readarray($etcpasswd); +my @users; +foreach my $line (@etcpasswd) { + if ($line =~ /^([^:]+):[^:]+:([^:]+)/) { + my ($username, $uid) = ($1, $2); + if ($uid > 1000) { + push(@users, $username); + } + } +} +my @files = ("/var/log/authlog"); +push(@files, glob q("/var/log/authlog.?")); +push(@files, glob q("/var/log/authlog.1?")); +foreach my $user (@users) { + my $lastseen; + foreach my $file (@files) { + my @logs = readarray($file); + my @seen = grep(/$user/, @logs); + if (scalar(@seen) && $seen[0] =~ /^(\w+ \d+ \d\d:\d\d:\d\d)/) { + $lastseen = $1; + print "$user => $lastseen\n"; + last; + } + } + if (!defined($lastseen)) { + print "$user => Never logged in\n"; + } +} +#warn Dumper \$loglines[1]; +sub readarray { + my ($filename) = @_; + open(my $fh, '<', $filename) or die "Could not read file '$filename' $!"; + chomp(my @lines = <$fh>); + close $fh; + return @lines; +} blob - /dev/null blob + e91a445581f60c7b3b280167ab0180a57c4048c8 (mode 644) --- /dev/null +++ Shell.pm @@ -0,0 +1,399 @@ +#!/usr/bin/perl + +package Shell; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; +use MIME::Base64; +use Data::Dumper; +use Digest::SHA qw(sha256_hex); +use lib './'; +require "SQLite.pm"; +require "Hash.pm"; + +my %conf = %main::conf; +my $chans = $conf{chans}; +my $teamchans = $conf{teamchans}; +my @teamchans = split /[,\s]+/m, $teamchans; +my $staff = $conf{staff}; +my $captchaURL = "https://example.com/captcha.php?vhost="; +my $hostname = $conf{hostname}; +my $terms = $conf{terms}; +my $expires = $conf{expires}; +my $mailfrom = $conf{mailfrom}; +my $mailname = $conf{mailname}; +my $passpath = "/etc/passwd"; +my $httpdconfpath = "/etc/httpd.conf"; +my $acmeconfpath = "/etc/acme-client.conf"; +my $pfconfpath = "/etc/pf.conf"; +my $relaydconfpath = "/etc/relayd.conf"; +my $startPort; +my $endPort; +main::cbind("pub", "-", "shell", \&mshell); +main::cbind("msg", "-", "shell", \&mshell); + +sub init { + #dependencies for figlet + unveil("/usr/local/bin/figlet", "rx") or die "Unable to unveil $!"; + unveil("/usr/lib/libc.so.95.1", "r") or die "Unable to unveil $!"; + unveil("/usr/libexec/ld.so", "r") or die "Unable to unveil $!"; + #dependencies for shell account + unveil($passpath, "r") or die "Unable to unveil $!"; + unveil($httpdconfpath, "rwxc") or die "Unable to unveil $!"; + unveil($acmeconfpath, "rwxc") or die "Unable to unveil $!"; + unveil($pfconfpath, "rwxc") or die "Unable to unveil $!"; + unveil($relaydconfpath, "rwxc") or die "Unable to unveil $!"; + unveil("/usr/sbin/chown", "rx") or die "Unable to unveil $!"; + unveil("/bin/chmod", "rx") or die "Unable to unveil $!"; + unveil("/usr/sbin/groupadd", "rx") or die "Unable to unveil $!"; + unveil("/usr/sbin/useradd", "rx") or die "Unable to unveil $!"; + unveil("/usr/sbin/groupdel", "rx") or die "Unable to unveil $!"; + unveil("/usr/sbin/userdel", "rx") or die "Unable to unveil $!"; + unveil("/bin/mkdir", "rx") or die "Unable to unveil $!"; + unveil("/bin/ln", "rx") or die "Unable to unveil $!"; + unveil("/usr/sbin/acme-client", "rx") or die "Unable to unveil $!"; + unveil("/bin/rm", "rx") or die "Unable to unveil $!"; + unveil("/bin/mv", "rx") or die "Unable to unveil $!"; + unveil("/home/", "rwxc") or die "Unable to unveil $!"; +} + +# !shell +# !shell captcha +sub mshell { + my ($bot, $nick, $host, $hand, @args) = @_; + my ($chan, $text); + if (@args == 2) { + ($chan, $text) = ($args[0], $args[1]); + } else { $text = $args[0]; } + my $hostmask = "$nick!$host"; + if (defined($chan) && $chans =~ /$chan/) { + main::putserv($bot, "PRIVMSG $chan :$nick: Please check private message"); + } + if ($text =~ /^$/) { + main::putserv($bot, "PRIVMSG $nick :Type !help for new instructions"); + foreach my $chan (@teamchans) { + main::putservlocalnet($bot, "PRIVMSG $chan :Help shell *$nick* on ".$bot->{name}); + } + return; + } elsif (main::isstaff($bot, $nick) && $text =~ /^delete\s+([[:ascii:]]+)/) { + my $username = $1; + if (SQLite::deleterows("shell", "username", $username)) { + # TODO delete shell + deleteshell($username); + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :$username deleted"); + } + } + return; + } + ### TODO: Check duplicate emails ### + my @rows = SQLite::selectrows("irc", "nick", $nick); + foreach my $row (@rows) { + my $password = SQLite::get("shell", "ircid", $row->{id}, "password"); + if (defined($password)) { + main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help."); + return; + } + } + if ($text =~ /^lastseen\s+([[:alnum:]]+)/) { + } + if ($text =~ /^captcha\s+([[:alnum:]]+)/) { + my $text = $1; + my $ircid = SQLite::id("irc", "nick", $nick, $expires); + if (!defined($ircid)) { die "undefined ircid"; } + my $captcha = SQLite::get("shell", "ircid", $ircid, "captcha"); + if ($text ne $captcha) { + main::putserv($bot, "PRIVMSG $nick :Wrong captcha. To get a new captcha, type !shell "); + return; + } + my $pass = Hash::newpass(); + chomp(my $encrypted = `encrypt $pass`); + my $username = SQLite::get("shell", "ircid", $ircid, "username"); + my $email = SQLite::get("shell", "ircid", $ircid, "email"); + my $version = SQLite::get("shell", "ircid", $ircid, "version"); + my $bindhost = "$username.$hostname"; + SQLite::set("shell", "ircid", $ircid, "password", $encrypted); + if (DNS::nextdns($username)) { + sleep(2); + createshell($username, $pass, $bindhost); + mailshell($username, $email, $pass, "shell", $version); + main::putserv($bot, "PRIVMSG $nick :Check your email!"); + + #www($newnick, $reply, $password, "bouncer"); + } else { + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :Assigning bindhost $bindhost failed"); + } + } + return; + } elsif ($text =~ /^([[:alnum:]]+)\s+([[:ascii:]]+)/) { + my ($username, $email) = ($1, $2); + my @users = col($passpath, 1, ":"); + my @matches = grep(/^$username$/i, @users); + if (scalar(@matches) > 0) { + main::putserv($bot, "PRIVMSG $nick :Sorry, username taken. Please choose another username, or contact staff for help."); + return; + } + # my $captcha = join'', map +(0..9,'a'..'z','A'..'Z')[rand(10+26*2)], 1..4; + my $captcha = int(rand(999)); + my $ircid = int(rand(2147483647)); + SQLite::set("irc", "id", $ircid, "localtime", time()); + SQLite::set("irc", "id", $ircid, "date", main::date()); + SQLite::set("irc", "id", $ircid, "hostmask", $hostmask); + SQLite::set("irc", "id", $ircid, "nick", $nick); + SQLite::set("shell", "ircid", $ircid, "username", $username); + SQLite::set("shell", "ircid", $ircid, "email", $email); + SQLite::set("shell", "ircid", $ircid, "captcha", $captcha); + main::whois($bot->{sock}, $nick); + main::ctcp($bot->{sock}, $nick); + main::putserv($bot, "PRIVMSG $nick :".`figlet $captcha`); + main::putserv($bot, "PRIVMSG $nick :$captchaURL".encode_base64($captcha)); + main::putserv($bot, "PRIVMSG $nick :Type !shell captcha "); + foreach my $chan (@teamchans) { + main::putservlocalnet($bot, "PRIVMSG $chan :$nick\'s captcha on $bot->{name} is $captcha"); + } + } else { + main::putserv($bot, "PRIVMSG $nick :Invalid username or email. Type !shell to try again."); + foreach my $chan (@teamchans) { + main::putserv($bot, "PRIVMSG $chan :Help *$nick* on ".$bot->{name}); + } + } +} +sub mailshell { + my( $username, $email, $password, $service, $version )=@_; + my $passhash = sha256_hex("$username"); + my $versionhash = encode_base64($version); + my $body = <<"EOF"; +You created a shell account! + +Username: $username +Password: $password +Server: $hostname +SSH Port: 22 +Your Ports: $startPort to $endPort + +To customize your vhost, connect to ask in #ircnow + +*IMPORTANT*: Verify your email address: + +https://www.$hostname/register.php?id=$passhash&version=$versionhash + +You *MUST* click on the link within 24 hours or your account will be deleted. + +IRCNow +EOF + Mail::mail($mailfrom, $email, $mailname, "Verify IRCNow Account", $body); +} + + +#sub mregex { +# my ($bot, $nick, $host, $hand, $text) = @_; +# if ($staff !~ /$nick/) { return; } +# if ($text =~ /^ips?\s+([-_()|0-9A-Za-z:\.?*\s]{3,})$/) { +# my $ips = $1; # space-separated list of IPs +# main::putserv($bot, "PRIVMSG $nick :".regexlist($ips)); +# } elsif ($text =~ /^users?\s+([-_()|0-9A-Za-z:\.?*\s]{3,})$/) { +# my $users = $1; # space-separated list of usernames +# main::putserv($bot, "PRIVMSG $nick :".regexlist($users)); +# } elsif ($text =~ /^[-_()|0-9A-Za-z:,\.?*\s]{3,}$/) { +# my @lines = regex($text); +# foreach my $l (@lines) { print "$l\n"; } +# } +#} +#sub mforeach { +# my ($bot, $nick, $host, $hand, $text) = @_; +# if ($staff !~ /$nick/) { return; } +# if ($text =~ /^network\s+del\s+([[:graph:]]+)\s+(#[[:graph:]]+)$/) { +# my ($user, $chan) = ($1, $2); +# foreach my $n (@main::networks) { +# main::putserv($bot, "PRIVMSG *controlpanel :delchan $user $n->{name} $chan"); +# } +# } +#} + +#sub loadlog { +# open(my $fh, '<', "$authlog") or die "Could not read file 'authlog' $!"; +# chomp(@logs = <$fh>); +# close $fh; +#} + +# return all lines matching a pattern +#sub regex { +# my ($pattern) = @_; +# if (!@logs) { loadlog(); } +# return grep(/$pattern/, @logs); +#} + +# given a list of IPs, return matching users +# or given a list of users, return matching IPs +#sub regexlist { +# my ($items) = @_; +# my @items = split /[,\s]+/m, $items; +# my $pattern = "(".join('|', @items).")"; +# if (!@logs) { loadlog(); } +# my @matches = grep(/$pattern/, @logs); +# my @results; +# foreach my $match (@matches) { +# if ($match =~ /^\[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\] \[([^]\/]+)(\/[^]]+)?\] connected to ZNC from (.*)/) { +# my ($user, $ip) = ($1, $3); +# if ($items =~ /[.:]/) { # items are IP addresses +# push(@results, $user); +# } else { # items are users +# push(@results, $ip); +# } +# } +# } +# my @sorted = sort @results; +# @results = do { my %seen; grep { !$seen{$_}++ } @sorted }; # uniq +# return join(' ', @results); +#} + +sub createshell { + my ($username, $password, $bindhost) = @_; + system "doas groupadd $username"; + system "doas adduser -batch $username $username $username `encrypt $password`"; + system "doas chmod 700 /home/$username /home/$username/.ssh"; + system "doas chmod 600 /home/$username/{.Xdefaults,.cshrc,.cvsrc,.login,.mailrc,.profile}"; + system "doas mkdir /var/www/htdocs/$username"; + system "doas ln -s /var/www/htdocs/$username /home/$username/htdocs"; + system "doas chown -R $username:www /var/www/htdocs/$username /home/$username/htdocs"; + system "doas chmod -R o-rx /var/www/htdocs/$username /home/$username/htdocs"; + system "doas chmod -R g+rwx /var/www/htdocs/$username /home/$username/htdocs"; + my $lusername = lc $username; + my $block = <<"EOF"; +server "$lusername.$hostname" { + listen on * port 80 + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } + location "*.php" { + fastcgi socket "/run/php-fpm.sock" + } + root "/htdocs/$username" +} +EOF + main::appendfile($httpdconfpath, $block); + $block = <<"EOF"; +domain "$lusername.$hostname" { + domain key "/etc/ssl/private/$lusername.$hostname.key" + domain full chain certificate "/etc/ssl/$lusername.$hostname.crt" + sign with letsencrypt +} +EOF + main::appendfile($acmeconfpath, $block); + configurepf($username); + system "doas rcctl reload httpd"; + system "doas acme-client -F $lusername.$hostname"; + system "doas ln -s /etc/ssl/$lusername.$hostname.crt /etc/ssl/$lusername.$hostname.fullchain.pem"; + system "doas pfctl -f /etc/pf.conf"; + configurerelayd($username); + $block = <<"EOF"; +~ * * * * acme-client $lusername.$hostname && rcctl reload relayd +EOF + system "echo $block | doas crontab -"; +#edquota $username + return 1; +} + +sub deleteshell { + my ($username, $bindhost) = @_; + my $lusername = lc $username; + system "doas groupdel $username"; + system "doas userdel $username"; + system "doas rm -f /etc/ssl/$lusername.$hostname.crt /etc/ssl/$lusername.$hostname.fullchain.pem /etc/ssl/private/$lusername.$hostname.key"; + my $httpdconf = main::readstr($httpdconfpath); + my $block = <<"EOF"; +server "$lusername.$hostname" { + listen on * port 80 + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } + location "*.php" { + fastcgi socket "/run/php-fpm.sock" + } + root "/htdocs/$username" +} +EOF + $block =~ s/{/\\{/gm; + $block =~ s/}/\\}/gm; + $block =~ s/\./\\./gm; + $block =~ s/\*/\\*/gm; + $httpdconf =~ s{$block}{}gm; + print $httpdconf; + main::writefile($httpdconfpath, $httpdconf); + + my $acmeconf = main::readstr($acmeconfpath); + $block = <<"EOF"; +domain "$lusername.$hostname" { + domain key "/etc/ssl/private/$lusername.$hostname.key" + domain full chain certificate "/etc/ssl/$lusername.$hostname.fullchain.pem" + sign with letsencrypt +} +EOF + $block =~ s/{/\\{/gm; + $block =~ s/}/\\}/gm; + $block =~ s/\./\\./gm; + $block =~ s/\*/\\*/gm; + $acmeconf =~ s{$block}{}gm; + main::writefile($acmeconfpath, $acmeconf); + return 1; +} + +#TODO Fix for $i +# Return column $i from $filename as an array with file separator $FS +sub col { + my ($filename, $i, $FS) = @_; + my @rows = main::readarray($filename); + my @results; + foreach my $row (@rows) { + if ($row =~ /^(.*?)$FS/) { + push(@results, $1); + } + } + return @results; +} + +sub configurepf { + my $username = shift; + my @read = split('\n', main::readstr($pfconfpath) ); + + my $previousline = ""; + my @pfcontent; + foreach my $line(@read) + { + my $currline = $line; + if( $currline ne "# end user ports") { + $previousline = $currline; + } else { + #pass in proto {tcp udp} to port {31361:31370} user {JL} + if( $previousline =~ /(\d*):(\d*)/ ) { + my $startport = ( $1 + 10 ); + my $endport = ( $2 + 10 ); + my $insert = "pass in proto {tcp udp} to port {$startport:$endport} user {$username}"; + push(@pfcontent, $insert); + $startPort = $startport; + $endPort = $endport; + } + } + push(@pfcontent, $currline) + } + main::writefile("$pfconfpath", join("\n",@pfcontent)) +} + +sub configurerelayd { + my ($username) = @_; + my $block = "tls { keypair $username.$hostname }"; + my $relaydconf = main::readstr($relaydconfpath); + my $newconf; + if ($relaydconf =~ /^.*tls\s+{\s+keypair\s+[.0-9a-zA-Z]+\s*}/m) { + $newconf = "$`$&\n\t$block$'"; + } + main::writefile($relaydconfpath, $newconf); +} + +#unveil("./newacct", "rx") or die "Unable to unveil $!"; +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + 1d5af6d5879fc9494712adb399e02c47fe2dcd9c (mode 644) --- /dev/null +++ VPN.pm @@ -0,0 +1,33 @@ +#!/usr/bin/perl + +package VPN; + +use strict; +use warnings; +use OpenBSD::Pledge; +use OpenBSD::Unveil; + +sub init { +} +# if ($reply =~ /^!vpn (.*) ([-_0-9a-zA-Z]+)$/i) { +# my $ircnick = $1; +# my $newnick = $2; +# if ($staff !~ /$sender/) { +# return; +# } +# my $password = newpass(); +# createvpn($password, $newnick); +# sendmsg($bot, $sender, "vpn created for $newnick"); +#my $msg = <<"EOF"; +#Your vpn account has been created! Username: $newnick with password: $password +#Our official support channel is #vpn. To connect, please follow these instructions: https://ircnow.org/kb/doku.php?id=vpn:vpn . +#EOF +# sendmsg($bot, $ircnick, $msg); +# } +#sub createvpn { +# my ($password, $username) = @_; +# `doas sh -c 'echo "user '$username' '$password'" >> /etc/iked.conf'`; +# `doas rcctl reload iked`; +#} + +1; # MUST BE LAST STATEMENT IN FILE blob - /dev/null blob + 7707f181f81712a7ce97a23609ad07ca400ea794 (mode 644) --- /dev/null +++ botnow.conf.example @@ -0,0 +1,69 @@ +#Name of local network; default ircnow +#localnet = ircnow + +#Internal IPv4 address and plaintext port; default 127.0.0.1@1337 +#host = 127.0.0.1 +#port = 1337 + +#Bouncer hostname +hostname = example.ircnow.org + +#External IP addresses, plaintext and ssl port +ip4 = 192.168.0.1 +ip6 = 2001:db8:: +ip6subnet = 64 +ip6prefix = 48 +#plainport = 1337 +#sslport = 31337 + +#Nick and password of bot -- make sure to add to oper block +nick = botnow +pass = password + +#List of channels for requesting bouncers +chans = #ircnow #testing + +#List of staff nicks; comment out to avoid highlights +staff = jrmu test + +#List of team channels on localnet; comment out to disable +teamchans = #ircnow-team + +#Mail from address +mailfrom = support@ircnow.org +#mailname = support + +#Modules to load +modules = BNC DNS Mail Shell SQLite Hash Help + +#Comment out the line below +die = You did not configure botnow.conf! + +##################################### +# Advanced Options Below # +# Do not edit unless needed # +##################################### + +#Join chans on localnet? +#localchans = False + +#Number of words in password +#passlength = 3 + +#Time in seconds before captcha expires +#expires = 1800 + +#DNS zone directory +#zonedir = /var/nsd/zones/master/ + +#ZNC install directory +#zncdir = /home/znc/home/znc/ + +#Network Interface Config File +#hostnameif = /etc/hostname.vio0 + +#Verbosity: NONE, ERRORS, WARNINGS, ALL +#verbose = ERRORS + +#Terms of Service +#terms = IRCNow: Of the User, By the User, For the User. Rules: no porn, no illegal drugs, no gambling, no slander, no warez, no promoting violence, no spam, illegal cracking, or DDoS. Only one account per person. Don't share passwords. Full terms: https://ircnow.org/terms.php blob - /dev/null blob + cdbebe8d78a43a813448a2f619801a58732d8dbe (mode 644) --- /dev/null +++ botnow.pl @@ -0,0 +1,613 @@ +#!/usr/bin/perl + +use strict; +no strict 'refs'; +use warnings; +use IO::Socket; +use IO::Select; +use OpenBSD::Pledge; +use OpenBSD::Unveil; + +my $confpath = "botnow.conf"; +our %conf; +foreach my $line (readarray($confpath)) { + if ($line =~ /^#/ or $line =~ /^\s*$/) { # skip comments and whitespace + next; + } elsif ($line =~ /^([-_a-zA-Z0-9]+)\s*=\s*([[:print:]]+)$/) { + $conf{$1} = $2; + } else { + die "ERROR: botnow.conf format invalid: $line"; + } +} + +# Name of local network +$conf{localnet} = $conf{localnet} || "ircnow"; + +# Internal IPv4 address and plaintext port +$conf{host} = $conf{host} || "127.0.0.1"; +$conf{port} = $conf{port} || 1337; + +# Bouncer hostname +chomp($conf{hostname} = $conf{hostname} || `hostname`); + +# External IPv4 address, plaintext and ssl port +$conf{ip4} = $conf{ip4} or die "ERROR: botnow.conf: ip4"; +$conf{ip6} = $conf{ip6} or die "ERROR: botnow.conf: ip6"; +$conf{ip6subnet} = $conf{ip6subnet} or die "ERROR: botnow.conf: ip6subnet"; +$conf{plainport} = $conf{plainport} || 1337; +$conf{sslport} = $conf{sslport} || 31337; + +# Nick and password of bot -- Make sure to add to oper block +$conf{nick} = $conf{nick} || "botnow"; +$conf{pass} = $conf{pass} or die "ERROR: botnow.conf: pass"; + +# Comma-separated list of channels for requesting bouncers +$conf{chans} = $conf{chans} || "#ircnow"; + +#Join chans on localnet? +$conf{localchans} = defined($conf{localchans}) && ($conf{localchans} =~ /^true/i); + +# Number of words in password +$conf{passlength} = $conf{passlength} || 3; + +# Mail from address +if (!defined($conf{mailname})) { + if ($conf{mailfrom} =~ /^([^@]+)@/) { + $conf{mailname} = $1 or die "ERROR: botnow.conf mailname"; + } +} + +# ZNC install directory +$conf{zncdir} = $conf{zncdir} || "/home/znc/home/znc"; + +# NSD zone dir +$conf{zonedir} = $conf{zonedir} || "/var/nsd/zones/master/"; + +# Network Interface Config File +$conf{hostnameif} = $conf{hostnameif} || "/etc/hostname.vio0"; + +# Verbosity: 0 (no errors), 1 (errors), 2 (warnings), 3 (diagnostic) +use constant { + NONE => 0, + ERRORS => 1, + WARNINGS => 2, + ALL => 3, +}; +$conf{verbose} = $conf{verbose} || ERRORS; + +# Terms of Service; don't edit lines with the word EOF +$conf{terms} = $conf{terms} || "IRCNow: Of the User, By the User, For the User. Rules: no porn, no illegal drugs, no gambling, no slander, no warez, no promoting violence, no spam, illegal cracking, or DDoS. Only one account per person. Don't share passwords. Full terms: https://ircnow.org/terms.php"; + +$conf{ipv6path} = "ipv6s"; # ipv6 file path +$conf{netpath} = "networks"; # networks file path +$conf{expires} = $conf{expires} || 1800; # time before captcha expires + +if(defined($conf{die})) { die $conf{die}; } + +my @modules; +if (defined($conf{modules})) { + @modules = split(/\s+/, $conf{modules}); +} +use lib './'; +foreach my $mod (@modules) { + require "$mod.pm"; +} +foreach my $mod (@modules) { + my $init = "${mod}::init"; + $init->(); +} + +our @networks; +my @bots; +my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); +my @days = qw(Sun Mon Tue Wed Thu Fri Sat Sun); +my @chans = split /[,\s]+/m, $conf{chans}; +my @teamchans; +if (defined($conf{teamchans})) { @teamchans = split /[,\s]+/m, $conf{teamchans}; } +my $call; +my $botnick = $conf{nick}; +my $host = $conf{host}; +my $port = $conf{port}; +my $pass = $conf{pass}; +my $localnet = $conf{localnet}; +my $staff = $conf{staff}; +my @stafflist = split(/ /,$staff); +my $verbose = $conf{verbose}; +my $ipv6path = $conf{ipv6path}; +my $netpath = $conf{netpath}; +my $expires = $conf{expires}; +my $localchans = $conf{localchans}; + +unveil("./", "r") or die "Unable to unveil $!"; +unveil("$confpath", "r") or die "Unable to unveil $!"; +unveil("$netpath", "r") or die "Unable to unveil $!"; +unveil("$ipv6path", "rwc") or die "Unable to unveil $!"; +unveil() or die "Unable to lock unveil $!"; + +#dns and inet for sockets, proc and exec for figlet +#rpath for reading file, wpath for writing file, cpath for creating path +#flock, fattr for sqlite +pledge( qw(stdio rpath wpath cpath inet dns proc exec flock fattr) ) or die "Unable to pledge: $!"; + +# Read from filename and return array of lines without trailing newlines +sub readarray { + my ($filename) = @_; + open(my $fh, '<', $filename) or die "Could not read file '$filename' $!"; + chomp(my @lines = <$fh>); + close $fh; + return @lines; +} + +# Read from filename and return as string +sub readstr { + my ($filename) = @_; + open my $fh, '<', $filename or die "Could not read file '$filename' $!"; + my $str = do { local $/; <$fh> }; + close $fh; + return $str; +} + +# Write str to filename +sub writefile { + my ($filename, $str) = @_; + open(my $fh, '>', "$filename") or die "Could not write to $filename"; + print $fh $str; + close $fh; +} + +# Append str to filename +sub appendfile { + my ($filename, $str) = @_; + open(my $fh, '>>', "$filename") or die "Could not append to $filename"; + print $fh $str; + close $fh; +} + +# Return list of networks from filename +# To add multiple servers for a single network, simply create a new entry with +# the same net name; znc ignores addnetwork commands when a network already exists +sub readnetworks { + my ($filename) = @_; + my @lines = readarray($filename); + my @networks; + foreach my $line (@lines) { + if ($line =~ /^#/ or $line =~ /^\s*$/) { # skip comments and whitespace + next; + } elsif ($line =~ /^\s*([-a-zA-Z0-9]+)\s*([-_.:a-zA-Z0-9]+)\s*(~|\+)?([0-9]+)\s*$/) { + my ($name, $server, $port) = ($1, $2, $4); + my $trustcerts; + if (!defined($3)) { + $trustcerts = 0; + } elsif ($3 eq "~") { # Use SSL but trust all certs + $port = "+".$port; + $trustcerts = 1; + } else { # Use SSL and verify certs + $port = "+".$port; + $trustcerts = 0; + } + push(@networks, {"name" => $name, "server" => $server, "port" => $port, "trustcerts" => $trustcerts }); + } else { + die "network format invalid: $line\n"; + } + } + return @networks; +} + +@networks = readnetworks($netpath); + +# networks must be sorted to avoid multiple connections +@networks = sort @networks; + +# create sockets +my $sel = IO::Select->new( ); +my $lastnet = ""; +foreach my $network (@networks) { + # avoid duplicate connections + if ($lastnet eq $network->{name}) { next; } + $lastnet = $network->{name}; + my $socket = IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port, Proto=>'tcp', Timeout=>'300') || print "Failed to establish connection\n"; + $sel->add($socket); + my $bot = {("sock" => $socket), %$network}; + push(@bots, $bot); + putserv($bot, "NICK $botnick"); + putserv($bot, "USER $botnick * * :$botnick"); +} + +while(my @ready = $sel->can_read) { + my ($bot, $response); + my ($sender, $val); + foreach my $socket (@ready) { + foreach my $b (@bots) { + if($socket == $b->{sock}) { + $bot = $b; + last; + } + } + if (!defined($response = <$socket>)) { + debug(ERRORS, "ERROR ".$bot->{name}." has no response:"); + next; + } + if ($response =~ /^PING :(.*)\r\n$/i) { + putserv($bot, "PONG :$1"); + } elsif ($response =~ /^:irc.znc.in (.*) (.*) :(.*)\r\n$/) { + my ($type, $target, $text) = ($1, $2, $3); + if ($type eq "001" && $target =~ /^$botnick.?$/ && $text eq "Welcome to ZNC") { + } elsif ($type eq "NOTICE" && $target =~ /^$botnick.?$/ && $text eq "*** To connect now, you can use /quote PASS :, or /quote PASS /: to connect to a specific network.") { + } elsif ($type eq "NOTICE" && $target =~ /^$botnick.?$/ && $text eq "*** You need to send your password. Configure your client to send a server password.") { + } elsif ($type eq "464" && $target =~ /^$botnick.?$/ && $text eq "Password required") { + putserv($bot, "PASS $botnick/$bot->{name}:$pass"); + if ($bot->{name} =~ /^$localnet$/i) { + putserv($bot, "OPER $botnick $pass"); + putserv($bot, "PRIVMSG *status :LoadMod --type=user controlpanel"); + putserv($bot, "PRIVMSG *controlpanel :get Admin $botnick"); + putserv($bot, "PRIVMSG *controlpanel :get Nick cloneuser"); + foreach my $chan (@teamchans) { + putserv($bot, "JOIN $chan"); + } + } + if ($bot->{name} !~ /^$localnet$/i or $localchans) { + foreach my $chan (@chans) { + putserv($bot, "JOIN $chan"); + } + } + } elsif ($type eq "464" && $target =~ /^$botnick.?$/ && $text eq "Invalid Password") { + die "ERROR: Wrong Username/Password: $bot->{name}"; + } else { + debug(ERRORS, "Unexpected bncnow.pl 257: type: $type, target: $target, text: $text"); + } + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PRIVMSG ([^ ]+) :(.*)\r\n$/i) { + my ($hostmask, $nick, $host, $target, $text) = ($1, $2, $3, $4, $5); + if ($hostmask eq '*status!znc@znc.in' && $target =~ /^$botnick.?$/) { + if ($text =~ /Network ([[:ascii:]]+) doesn't exist./) { + debug(ERRORS, "nonexistent: $1"); + } elsif ($text eq "You are currently disconnected from IRC. Use 'connect' to reconnect.") { + debug(ERRORS, "disconnected: $bot->{name}"); + } elsif ($text =~ /Unable to load module (.*): Module (.*) already loaded./) { + debug(ALL, "Module $1 already loaded\n"); + } elsif ($text =~ /^Disconnected from IRC.*$/) { + debug(ERRORS, "$bot->{name}: $text"); + } elsif ($text =~ /^|/) { + debug(ERRORS, "$bot->{name}: $text"); + } else { + debug(ERRORS, "Unexpected bncnow.pl 273: $response"); + } + } elsif ($text =~ /^!([[:graph:]]+)\s*(.*)/) { + my ($cmd, $text) = ($1, $2); + my $hand = $staff; # TODO fix later + if ($target =~ /^#/) { + foreach my $c (@{$call->{pub}}) { + if ($cmd eq $c->{cmd}) { + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $target, $text); + } + } + } else { + foreach my $c (@{$call->{msg}}) { + if ($cmd eq $c->{cmd}) { + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $text); + } + } + } + } else { + my $hand = $staff; # TODO fix later + if ($target =~ /^#/) { + foreach my $c (@{$call->{pubm}}) { + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $target, $text); + } + } else { + foreach my $c (@{$call->{msgm}}) { + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $text); + } + } + } + debug(ALL, "$hostmask $target $text"); + } elsif($response =~ /^:([^ ]+) NOTICE ([^ ]+) :(.*)\r\n$/i) { + my ($hostmask, $target, $text) = ($1, $2, $3); + if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { + my ($nick, $host) = ($1, $2); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{notc}}) { + # if ($text eq $c->{mask}) { # TODO fix later + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $text, $target); + # } + } + # TODO use CTCR + # CTCP replies + if ($hostmask ne '*status!znc@znc.in') { + if ($text =~ /^(PING|VERSION|TIME|USERINFO) (.*)$/i) { + my ($key, $val) = ($1, $2); + my $id = SQLite::id("irc", "nick", $nick, $expires); + SQLite::set("irc", "id", $id, "ctcp".lc($key), $val); + SQLite::set("irc", "id", $id, "localtime", time()); + } + } + } + debug(ALL, "$hostmask NOTICE $target $text"); +#:portlane.se.quakenet.org NOTICE guava :Highest connection count: 1541 (1540 clients) +#:portlane.se.quakenet.org NOTICE guava :on 2 ca 2(4) ft 20(20) tr + } elsif($response =~ /^:([^ ]+) MODE ([^ ]+) ([^ ]+)\s*(.*)\r\n$/i) { + my ($hostmask, $chan, $change, $targets) = ($1, $2, $3, $4); + if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { + my ($nick, $host) = ($1, $2); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{mode}}) { + # TODO filter by mask + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $chan, $change, $targets); + } + } + debug(ALL, "$hostmask MODE $chan $change $targets"); +#:guava!guava@guava.guava.ircnow.org MODE guava :+Ci +#:ChanServ!services@services.irc.ircnow.org MODE #testing +q jrmu +#:jrmu!jrmu@jrmu.staff.ircnow.org MODE #testing +o jrmu +#Unexpected bncnow.pl 460: :irc.guava.ircnow.org MODE guava :+o + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) JOIN :?(.*)\r\n$/i) { + my ($hostmask, $nick, $host, $chan) = ($1, $2, $3, $4); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{join}}) { + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $chan); + } + debug(ALL, "$hostmask JOIN $chan"); +#:jrmu!jrmu@jrmu.staff.ircnow.org JOIN :#testing + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PART ([^ ]+) :(.*)\r\n$/i) { + my ($hostmask, $nick, $host, $chan, $text) = ($1, $2, $3, $4, $5); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{part}}) { + # if ($text eq $c->{mask}) { # TODO fix later + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $chan, $text); + # } + } + debug(ALL, "$hostmask PART $chan :$text"); +#:jrmu!jrmu@jrmu.staff.ircnow.org PART #testing : + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) KICK (#[^ ]+) ([^ ]+) :(.*)\r\n$/i) { + my ($hostmask, $nick, $host, $chan, $kicked, $text) = ($1, $2, $3, $4, $5, $6); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{kick}}) { + # if ($text eq $c->{mask}) { # TODO fix later + my $proc = $c->{proc}; + $proc->($bot, $nick, $host, $hand, $chan, $text); + # } + } + debug(ALL, "$hostmask KICK $chan $kicked :$text"); +#jrmu!jrmu@jrmu.users.undernet.org KICK #ircnow guava :this is a test + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) NICK :?(.*)\r\n$/i) { + my ($hostmask, $nick, $host, $text) = ($1, $2, $3, $4); + debug(ALL, "$hostmask NICK $text"); +#:Fly0nDaWaLL|dal!psybnc@do.not.h4ck.me NICK :nec|dal + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) QUIT :(.*)\r\n$/i) { + my ($hostmask, $nick, $host, $text) = ($1, $2, $3, $4); + debug(ALL, "$hostmask QUIT :$text"); +#:Testah!~sid268081@aa38a510 QUIT :Client closed connection + } elsif($response =~ /^NOTICE AUTH :(.*)\r\n$/i) { + my ($text) = ($1); + debug(ALL, "NOTICE AUTH: $text"); +#NOTICE AUTH :*** Looking up your hostname +#NOTICE AUTH: *** Looking up your hostname +#NOTICE AUTH: *** Checking Ident +#NOTICE AUTH: *** Got ident response +#NOTICE AUTH: *** Found your hostname + } elsif ($response =~ /^:([[:graph:]]+) (\d\d\d) $botnick.? :?(.*)\r?\n?\r$/i) { + my ($server, $code, $text) = ($1, $2, $3); + if ($code =~ /^001$/) { # Server Info + debug(ERRORS, "connected: $bot->{name}"); + } elsif ($code =~ /^0\d\d$/) { # Server Info + debug(ALL, "$server $code $text"); + } elsif ($code =~ /^2\d\d$/) { # Server Stats + debug(ALL, "$server $code $text"); + } elsif ($code == 301 && $text =~ /^([-_\|`a-zA-Z0-9]+) :([[:graph:]]+)/) { + debug(ALL, "$text"); + } elsif ($code == 307 && $text =~ /^([-_\|`a-zA-Z0-9]+) (.*)/) { + my ($sender, $key) = ($1, "registered"); + $val = $2 eq ":is a registered nick" ? "True" : "$2"; + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, "identified", $val); + debug(ALL, "$key: $val"); + } elsif ($code == 311 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([^:]+)\s+([^:]+) \* :([^:]*)/) { + my ($sender, $key, $val) = ($1, "hostmask", "$1\!$2\@$3"); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code == 312 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([^:]+) :([^:]+)/) { + my ($sender, $key, $val) = ($1, "server", $2); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code == 313 && $text =~ /^([-_\|`a-zA-Z0-9]+) :?(.*)/) { + my ($sender, $key, $val) = ($1, "oper", ($2 eq "is an IRC operator" ? "True" : "$2")); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code == 315 && $text =~ /^([-_\|`a-zA-Z0-9]+) :End of \/?WHO(IS)? list/) { + debug(ALL, "End of WHOIS"); + } elsif ($code == 317 && $text =~ /^([-_\|`a-zA-Z0-9]+) (\d+) (\d+) :?(.*)/) { + ($sender, my $idle, my $epochtime) = ($1, $2, $3); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, "idle", $idle); +# SQLite::set("irc", "id", $id, "epochtime", time()); + debug(ALL, "idle: $idle, epochtime: $epochtime"); + } elsif ($code == 318 && $text =~ /^([-_\|`a-zA-Z0-9]+) :End of \/?WHOIS list/) { + debug(ALL, "End of WHOIS"); + } elsif ($code == 319 && $text =~ /^([-_\|`a-zA-Z0-9]+) :(.*)/) { + my ($sender, $key, $val) = ($1, "chans", $2); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code == 330 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([-_\|`a-zA-Z0-9]+) :?(.*)/) { + my ($sender, $key, $val) = ($1, "identified", ($3 eq "is logged in as" ? "True" : $2)); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code == 338 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([0-9a-fA-F:.]+) :actually using host/) { + my ($sender, $key, $val) = ($1, "ip", $2); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + #Unexpected: efnet.port80.se 338 jrmu 206.253.167.44 :actually using host + } elsif ($code == 378 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is connecting from ([^ ]+)\s*([0-9a-fA-F:.]+)?/) { + my ($sender, $key, $val) = ($1, "ip", $3); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code == 671 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is using a secure connection/) { + my ($sender, $key, $val) = ($1, "ssl", "True"); + my $id = SQLite::id("irc", "nick", $sender, $expires); + SQLite::set("irc", "id", $id, $key, $val); + debug(ALL, "$key: $val"); + } elsif ($code =~ /^332$/) { # Topic + # print "$text\r\n"; + } elsif ($code =~ /^333$/) { # + # print "$server $text\r\n"; + #karatkievich.freenode.net 333 #ircnow jrmu!znc@206.253.167.44 1579277253 + } elsif ($code =~ /^352$/) { # Hostmask +#:datapacket.hk.quakenet.org 352 * znc guava.guava.ircnow.org *.quakenet.org guava H :0 guava + # print "$server $code $text\r\n"; + } elsif ($code =~ /^353$/) { # Names + # print "$server $code $text\r\n"; + } elsif ($code =~ /^366$/) { # End of names + # print "$server $code $text\r\n"; + } elsif ($code =~ /^37\d$/) { # MOTD + # print "$server $code $text\r\n"; + } elsif ($code =~ /^381$/) { # IRC Operator Verified + # print "IRC Oper Verified\r\n"; + } elsif ($code =~ /^401$/) { # IRC Operator Verified + # print "IRC Oper Verified\r\n"; + } elsif ($code =~ /^403$/) { # No such channel + # debug(ERRORS, "$text"); + } elsif ($code =~ /^422$/) { # MOTD missing + # print "$server $code $text\r\n"; + } elsif ($code =~ /^396$/) { # Display hostname + # print "$server $code $text\r\n"; +#Unexpected bncnow.pl 454: irc.guava.ircnow.org 396 guava.guava.ircnow.org :is your displayed hostname now + } elsif ($code =~ /^464$/) { # Invalid password for oper + foreach my $chan (@teamchans) { + putserv($bot, "PRIVMSG $chan :$botnick oper password failed; the bot will be unable to view uncloaked IP addresses"); + } + } elsif ($code =~ /^477$/) { # Can't join channel + foreach my $chan (@teamchans) { + putserv($bot, "PRIVMSG $chan :ERROR: $botnick on $server: $text"); + } + } elsif ($code == 716 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is in \+g mode \(server-side ignore.\)/) { + debug(ALL, "$text"); + } else { + debug(ERRORS, "Unexpected bncnow.pl 454: $server $code $text"); + } + } else { + debug(ERRORS, "Unexpected bncnow.pl 460: $response"); + } + } +} + +sub putserv { + my( $bot, $text )=@_; + my $socket = $bot->{sock}; + if ($text =~ /^([^:]+):([[:ascii:]]*)$/m) { + my ($cmd, $line) = ($1, $2); + my @lines = split /\r?\n/m, $line; + foreach my $l (@lines) { + print $socket "$cmd:$l\r\n"; + } + } else { + print $socket "$text\r\n"; + } +} + +sub putserv { + my( $bot, $text )=@_; + my $socket = $bot->{sock}; + if ($text =~ /^([^:]+):([[:ascii:]]*)$/m) { + my ($cmd, $line) = ($1, $2); + my @lines = split /\r?\n/m, $line; + foreach my $l (@lines) { + print $socket "$cmd:$l\r\n"; + } + } else { + print $socket "$text\r\n"; + } +} + +sub putservlocalnet { + my( $bot, $text )=@_; + my $botlocalnet; + foreach my $b (@bots) { + if($b->{name} =~ /^$localnet$/i) { + $botlocalnet = $b; + last; + } + } + putserv($botlocalnet, $text); +} + + +sub date { + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); + my $localtime = sprintf("%04d%02d%02d", $year+1900, $mon+1, $mday); + return $localtime; +} +sub gettime { + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); + my $localtime = sprintf("%s %s %d %02d:%02d:%02d", $days[$wday], $months[$mon], $mday, $hour, $min, $sec); + return $localtime; +} + +sub whois { + my( $socket, $target )=@_; + print $socket "WHOIS $target $target\r\n"; +} + +sub ctcp { + my( $socket, $target )=@_; +# print $socket "PRIVMSG $target :".chr(01)."CLIENTINFO".chr(01)."\r\n"; +# print $socket "PRIVMSG $target :".chr(01)."FINGER".chr(01)."\r\n"; +# print $socket "PRIVMSG $target :".chr(01)."SOURCE".chr(01)."\r\n"; + print $socket "PRIVMSG $target :".chr(01)."TIME".chr(01)."\r\n"; +# print $socket "PRIVMSG $target :".chr(01)."USERINFO".chr(01)."\r\n"; + print $socket "PRIVMSG $target :".chr(01)."VERSION".chr(01)."\r\n"; +# print $socket "PRIVMSG $target :".chr(01)."PING".chr(01)."\r\n"; +} + +sub cbind { + my ($type, $flags, $cmd, $proc) = @_; + if ($type eq "pub") { + push(@{$call->{pub}}, {cmd => $cmd, proc => $proc}); + } elsif ($type eq "msg") { + push(@{$call->{msg}}, {cmd => $cmd, proc => $proc}); + } elsif ($type eq "notc") { + push(@{$call->{notc}}, {mask => $cmd, proc => $proc}); + } elsif ($type eq "mode") { + push(@{$call->{mode}}, {mask => $cmd, proc => $proc}); + } elsif ($type eq "join") { + push(@{$call->{join}}, {mask => $cmd, proc => $proc}); + } elsif ($type eq "partcall") { + push(@{$call->{part}}, {mask => $cmd, proc => $proc}); + } elsif ($type eq "pubm") { + push(@{$call->{pubm}}, {mask => $cmd, proc => $proc}); + } elsif ($type eq "msgm") { + push(@{$call->{msgm}}, {mask => $cmd, proc => $proc}); + } +} + +sub debug { + my ($level, $msg) = @_; + if ($verbose >= $level) { print "$msg\n"; } +} + +sub isstaff { + my( $bot, $nick ) = @_; + if( !( $bot->{name} =~ /^$localnet$/i ) ) + { + return 0; + } + my $lnick = lc $nick; + foreach( @stafflist ) { + if( $lnick eq $_ ) { + return 1; + } + } + return 0; +} blob - /dev/null blob + cd0e738527e87e9f4da51957837ecb74b2d712e1 (mode 644) --- /dev/null +++ captcha.png @@ -0,0 +1,106 @@ + $value) { + if ($key == "User-Agent") { + $fpr{"useragent"} = $value; + } elseif ($key == "Upgrade-Insecure-Requests") { + $fpr{"upgradeinsecure"} = $value; + } elseif ($key == "Host") { + $fpr{"host"} = $value; + } elseif ($key == "Dnt") { + $fpr{"dnt"} = $value; + } elseif ($key == "Connection") { + $fpr{"connection"} = $value; + } elseif ($key == "Cache-Control") { + $fpr{"cachecontrol"} = $value; + } elseif ($key == "Accept-Language") { + $fpr{"acceptlanguage"} = $value; + } elseif ($key == "Accept-Encoding") { + $fpr{"acceptencoding"} = $value; + } elseif ($key == "Accept") { + $fpr{"accept"} = $value; + } +} + +class wwwdb extends SQLite3 { + function __construct() { + $this->open('/botnow/botnow.db'); + } +} +$wwwdb = new wwwdb(); +if(!$wwwdb) { + echo $wwwdb->lastErrorMsg(); +} else { + foreach ($fpr as $key => $value) { + $keys[] = $key; + $values[] = $value; + } + $keystr = '"'.implode('","', $keys).'"'; + $valstr = '"'.implode('","', $values).'"'; + $sql =<<exec($sql)) { + echo $db->lastErrorMsg(); + return; + } + + $sql =<<query($sql); + while($row = $ret->fetchArray(SQLITE3_ASSOC)) { +// echo "hash: $hash, row['hashid']: ".$row['hashid']."
\n"; + if ($hash == $row['hashid']) { + $captcha = $row['captcha']; +// echo $captcha; + } + } + $wwwdb->close(); +// echo "Records created successfully\n"; +} +session_start(); + +// The capcha will be stored +// for the session +$_SESSION["captcha"] = $captcha; + +// Generate a 50x24 standard captcha image +$im = imagecreatetruecolor(250, 120); + +// Blue color +$bg = imagecolorallocate($im, 22, 86, 165); + +// White color +$fg = imagecolorallocate($im, 255, 255, 255); + +// Give the image a blue background +imagefill($im, 0, 0, $bg); + +// Print the captcha text in the image +// with random position & size +//imagestring($im, 5, rand(1, 40), rand(1, 40), $captcha, $fg); +imagettftext($im , 96, 0, rand(0,130), 120-rand(0,60), $fg , 'intuitive.ttf', $captcha); +//imagettftext($im , 96, 0, rand(20, 50), rand(20, 50), $fg , 'eczar.ttf', $captcha); + +// VERY IMPORTANT: Prevent any Browser Cache!! +header("Cache-Control: no-store, + no-cache, must-revalidate"); + +// The PHP-file will be rendered as image +header('Content-type: image/png'); + +// Finally output the captcha as +// PNG image the browser +imagepng($im); + +// Free memory +imagedestroy($im); +?> blob - /dev/null blob + 9039d7b3e5e9bd7830b19102b1ba5d1c37599d0b (mode 644) Binary files /dev/null and intuitive.ttf differ blob - /dev/null blob + 2ef14a3342a3b4a4231a1384b1a147740d16e291 (mode 644) --- /dev/null +++ makefile @@ -0,0 +1,55 @@ +USERNAME="botnow" +HOMEDIR="/home/botnow" +HTDOCS="/var/www/htdocs/botnow" +DATABASE="/var/www/botnow/" +ZONES="/var/nsd/zones/master/" +ZNCUSER="znc" +ZNCDIR="/home/znc/home/znc/" +HTTPDCONF="/etc/httpd.conf" +ACMECONF="/etc/acme-client.conf" + +#botnow: figlet php sqlite +botnow: + useradd -m -g =uid -c ${USERNAME} -d ${HOMEDIR} -s /bin/ksh ${USERNAME} + chmod go-rx ${HOMEDIR} + mkdir ${DATABASE} + chmod o-rx ${DATABASE} + touch ${DATABASE}/www + chown -R www:${USERNAME} ${DATABASE} + chmod -R ug+rw ${DATABASE} + mkdir ${HTDOCS} + cp words ${HTDOCS}/ + cp register.php ${HTDOCS}/ + chown -R www:daemon ${HTDOCS} + chmod o-rwx ${HTDOCS} + usermod -G daemon ${USERNAME} + usermod -G wheel ${USERNAME} + chown -R _nsd:daemon ${ZONES} + chmod ug+rwx ${ZONES} + chmod ug+rw ${ZONES}/* + chmod g+rw ${HTTPDCONF} ${ACMECONF} + echo "permit nopass ${USERNAME} as _nsd cmd nsd-control" >> /etc/doas.conf + cp captcha.png register.php ${HTDOCS}/ + cp LICENSE README botnow.pl botnow.conf.example BNC.pm DNS.pm Mail.pm SQLite.pm Shell.pm table.sql Hash.pm Help.pm makefile networks captcha.png register.php words ${HOMEDIR}/ + chown -R ${USERNAME}:${USERNAME} ${HOMEDIR} + chmod u+x ${HOMEDIR}/botnow.pl + chown -R ${ZNCUSER}:daemon ${ZNCDIR} + chmod -R ug+r ${ZNCDIR} + find ${ZNCDIR} -type d -exec chmod ug+rx {} + + echo "Installation complete. To run botnow, type $ ./botnow.pl" +figlet: + pkg_add figlet-2.2.5 + +php: + pkg_add php-8.0.8 + pkg_add php-sqlite + +sqlite: + pkg_add p5-DBI + pkg_add p5-DBD-SQLite + pkg_add sqlite3 + pkg_add p5-Class-DBI-SQLite + +blowfish: + cc -o blowfish.o blowfish.c + cp blowfish.o ${HOMEDIR}/ blob - /dev/null blob + e537c43e85c59b854d576b6dbe754bef099329aa (mode 644) --- /dev/null +++ networks @@ -0,0 +1,320 @@ +# Network entries follow this format: +# +# Name Address Port +# +# For ports, + indicates SSL, ~ indicates SSL with no PKI checking +# +# Repeating the same network name will add multiple servers for a single network +# +# Please keep network names lowercase +# Please edit 0000net and ircnow to connect to the local server's IPv6 address +# +0000net Your-IPv6-address 6667 +ircnow Your-IPv6-address 6667 +2600net irc.2600.net 6667 +42net sol.42net.org 6667 +#abandoned-irc irc.abandoned-irc.net ~6697 +#abjects irc.abjects.net +6697 +absoluty irc.absoluty-irc.fr 6667 +aerospacechat irc.aerospacechat.com +6697 +afterall irc.after-all.org 6667 +afternet irc.afternet.org +6697 +afterx irc.afterx.net +6697 +allnetwork irc6.allnetwork.org ~6697 +#allnetwork irc.allnetwork.org ~6697 +allnightcafe irc.allnightcafe.com ~6697 +allrednc irc.allrednc.org ~6697 +alphachat irc.alphachat.net +6697 +amicachat irc.amicachat.net ~6697 +amorlatino irc.amorlatino.org 6667 +#anonops irc.anonops.com +6697 +anope irc.anope.org 6667 +austnet irc.austnet.org +6697 +atrum irc.atrum.org ~6697 +axon irc.axon.pw +6697 +ayochat irc.ayochat.or.id 6667 +azzurra irc.azzurra.org +6697 +banglacafe irc.banglacafe.com 6667 +barton irc.barton.de 6667 +bgirc irc.bgirc.com ~6697 +bitlbee 127.0.0.1 6668 +blitzed irc.blitzed.org +6697 +bolchat irc.bolchat.com 6667 +brasirc irc.brasirc.com.br ~6697 +bsdnet bsdnet-eu.in-addr.com 6667 +bsdunix irc.bsdunix.us +6697 +canternet irc.canternet.org +6697 +cavenet ipv6.us.cavenet.noxt.cf +6697 +#cavenet ipv4.us.cavenet.noxt.cf +6697 +chaat irc.chaat.fr 6667 +chat4all irc.chat4all.org ~6697 +chatasia irc.chatasia.net ~6697 +chatgentr chat.gen.tr 6667 +chathispano irc.chathispano.com +6697 +chathub irc.chathub.org +6697 +chating irc.chating.id 6667 +chatjunkies irc.chatjunkies.org +6697 +chatlatino irc.chatlatinos.cl 9090 +chatlounge irc.chatlounge.net +6697 +chatopia irc.chatopia.net +6697 +chatplezier irc.chatplezier.com ~6697 +chatspike irc.chatspike.net ~6697 +chatsystems irc.chatsystems.net +6697 +chattogratis irc.ChattoGratis.it +6697 +chatzona irc.chatzona.org 6667 +#coolsmile irc.coolsmile.net +6697 +dalnet irc6.dal.net +6697 +#dalnet bifrost.ca.us.dal.net ~6697 +#dalnet bitcoin.uk.eu.dal.net ~6697 +#dalnet choopa.nj.us.dal.net ~6697 +#dalnet diamond.hub.dal.net ~6697 +#dalnet foxtrot.hub.dal.net ~6697 +#dalnet halcyon.il.us.dal.net ~6697 +#dalnet hash.ix.nl.dal.net ~6697 +#dalnet hoon.ix.au.dal.net ~6697 +#dalnet istana.ix.sg.dal.net ~6697 +#dalnet jingo.ix.us.dal.net ~6697 +#dalnet karsk.ix.eu.dal.net ~6697 +#dalnet kiwi.ix.nz.dal.net ~6697 +#dalnet koala.vc.au.dal.net ~6697 +#dalnet lair.nl.eu.dal.net ~6697 +#dalnet laud.ix.us.dal.net ~6697 +#dalnet lion.tx.us.dal.net ~6697 +#dalnet loyalty.ix.us.dal.net ~6697 +#dalnet merlin.hub.dal.net ~6697 +#dalnet midnight.hub.dal.net ~6697 +#dalnet ninja.hub.dal.net ~6697 +#dalnet nonstop.ix.me.dal.net ~6697 +#dalnet oi.ix.au.dal.net ~6697 +#dalnet oper-e.dal.net ~6697 +#dalnet paradigm.hub.dal.net ~6697 +#dalnet powertech.no.eu.dal.net ~6697 +#dalnet punch.wa.us.dal.net ~6697 +#dalnet rangers.ix.us.dal.net ~6697 +#dalnet redemption.ix.us.dal.net ~6697 +#dalnet renew.hub.dal.net ~6697 +#dalnet smurf.hub.dal.net ~6697 +#dalnet underworld.se.eu.dal.net ~6697 +darkfasel irc.darkfasel.net +6697 +darkirc irc.darkirc.eu +6697 +darkirc irc.darklinux.uk +6697 +darkmyst irc2.darkmyst.org ~6697 +darkscience irc.darkscience.net +6697 +dejatoons irc.dejatoons.net ~6697 +desirenet irc.desirenet.org 6667 +digitalirc irc.digitalirc.org +6697 +efnet irc.efnet.nl 6667 +efnet efnet.deic.eu 6667 +efnet efnet.port80.se ~6697 +efnet efnet.portlane.se 6667 +efnet irc.Prison.NET 6667 +efnet irc.choopa.net ~6697 +efnet irc.colosolutions.net 6667 +efnet irc.du.se 6667 +efnet irc.efnet.fr 6667 +efnet irc.efnet.nl 6667 +efnet irc.homelien.no 6667 +efnet irc.mzima.net 6667 +efnet irc.nordunet.se 6667 +efnet irc.servercentral.net ~6697 +efnet irc.underworld.no 6667 +ekolay irc.e-kolay.org 6667 +enjoychat irc.enjoychat.net 6667 +epiknet irc.epiknet.org +6697 +esper irc.esper.net +6697 +euirc irc.euirc.net 6667 +europnet ipv6.europnet.org +6697 +evilnet irc.evilnet.org ~6697 +evolu irc.evolu.net ~6697 +ewnix irc.ewnix.net ~6697 +exchat irc.exchat.net ~6697 +exolia irc.exolia.net 6667 +explosionirc irc.explosionirc.net 6667 +forumcerdas irc.forumcerdas.net ~6697 +frantech irc.frantech.ca +6697 +friendsirc irc.friendsirc.com ~6697 +freenode ipv6.chat.freenode.net +6697 +#freenode chat.ipv6.freenode.net +6697 +freeunibg irc.freeunibg.eu 6667 +freshchat irc.freshchat.org +6697 +#furnet irc.furnet.org +6697 +gamesurge irc.gamesurge.net 6667 +geeknode irc.geeknode.org +6697 +geekshed irc.geekshed.net +6697 +germanelite irc.german-elite.net ~6697 +geveze irc.geveze.org 6667 +geyiktr irc.geyiktr.net 6667 +gigairc irc.gigairc.net ~6697 +gimp irc.gimp.org +6697 +globalirc irc.global-irc.org 6667 +globalirc-it irc.globalirc.it ~6697 +greekirc irc.greekirc.net 6667 +hackint irc.hackint.org +6697 +hackthissite irc.hackthissite.org +7000 +hazinem irc.hazinem.net 6667 +hybridirc ipv6.hybridirc.com ~6697 +icqchat irc.icq-chat.com +6697 +insomnia irc.insomnia247.nl 6667 +irc4fun irc.irc4fun.net ~6697 +irccloud irc.irccloud.com +6697 +ircd-hybrid irc.ircd-hybrid.org ~6697 +ircfreakz irc.ircfreakz.net ~6697 +ircgate irc.ircgate.it 6667 +irchaven irc.irchaven.org 6667 +irchighway irc.irchighway.net ~6697 +ircnet open.ircnet.net 6667 +ircnet ircnet.hostsailor.com ~6697 +ircnet ssl.irc.atw-inter.net ~6697 +ircnet irc.atw-inter.net ~6697 +ircnet irc.us.ircnet.net ~6697 +ircnet irc.psychz.net ~6697 +ircnet openirc.snt.utwente.nl 6667 +ircnet irc.portlane.se ~6697 +ircnet irc.nlnog.net 6667 +ircsource ircsource.quoservers.net +6697 +irctl chat.irc.tl ~6697 +irczone irc6.irczone.net ~6697 +italian-amici irc.italian-amici.org 6667 +juggler irc.juggler.jp 6667 +kalbim irc.kalbim.net 6667 +kampungchat etnies6.ircd.link 6667 +librairc irc.librairc.net ~6697 +linuxconsole linuxconsole.net ~6697 +luatic irc.luatic.net 6667 +magnet irc.shadowcat.co.uk 6667 +malikania malikania.fr ~6697 +mibbit irc.mibbit.com ~6697 +mindforge irc.mindforge.org 6667 +myirc chat.myirc.net +6697 +nationchat irc.nationchat.org 6667 +netchat irc.netchat.cl 6667 +nfnet irc.nfnet.org 6667 +oftc irc6.oftc.net +6697 +#oftc irc.oftc.net +6697 +oltreirc irc.oltreirc.net +6697 +onlinegamesnet irc.onlinegamesnet.net 6667 +openjoke irc.openjoke.org ~6697 +overthewire ircs.overthewire.org +6697 +#optilan irc.optilan.eu 6667 +#p2p-net irc.p2p-network.net +6697 +perl irc.perl.org 6667 +pirc-pl irc.pirc.pl +6697 +ptnet irc.ptnet.org +6697 +puntochat irc.puntochat.net ~6697 +quake irc6.quakenet.org 6667 +quake irc.ipv6.quakenet.org 6667 +#quake adrift.sg.quakenet.org 6667 +#quake cymru.us.quakenet.org 6667 +#quake dreamhack.se.quakenet.org 6667 +#quake euroserv.fr.quakenet.org 6667 +#quake hostsailor.ro.quakenet.org 6667 +#quake port80a.se.quakenet.org 6667 +#quake port80b.se.quakenet.org 6667 +#quake port80c.se.quakenet.org 6667 +#quake portlane.se.quakenet.org 6667 +#quake underworld1.no.quakenet.org 6667 +#quake underworld2.no.quakenet.org 6667 +quartznet irc.quartznet.org 6667 +recycled-irc irc.recycled-irc.net 6667 +redhispano irc.redhispano.org ~6697 +rezosup irc.rezosup.org ~6697 +rizon irc6.rizon.net ~6697 +#rizon irc.rizon.net ~6697 +#rizon irc.criten.net ~6697 +#rizon magnet.rizon.net ~6697 +#rizon irc.sxci.net ~6697 +#rizon irc.rizon.io ~6697 +#rizon irc2.rizon.io ~6697 +#rizon irc.x2x.cc ~6697 +#rizon solenoid.rizon.net ~6697 +#rizon irc.sacredland.world ~6697 +#rizon irc.rizon.no ~6697 +#rizon irc.uworld.se ~6697 +#rizon irc.rizon.club ~6697 +#rizon irc.rizon.fun ~6697 +#rizon irc.hostsailor.com ~6697 +#rizon irc.losslessone.com ~6697 +robothive irc.robothive.org ~6697 +#rootworld irc.rootworld.net 6667 +rusnet irc.rusnet.org.ru 6660 +rusnet irc.lucky.net 6660 +#rusnet irc.run.net 6660 +#rusnet irc.ventelo.de 6660 +#rusnet irc.perm.ru 6660 +#rusnet irc.nnov.net 6660 +#rusnet irc.tomsk.net 6660 +#rusnet irc.intertax.ru 6660 +#rusnet irc.sibptus.ru 6660 +#rusnet irc.tom.ru 6660 +#rusnet irc.anarxi.st 6660 +#rusnet irc.fairytale.lo 6660 +#rusnet irc.seb.org.ua 6660 +#rusnet starkiller.lo 6660 +#rusnet irc.seb.org.ua 6660 +#rusnet irc.meganet.ru 6660 +#rusnet irc.tambov.ru 6660 +#rusnet irc.meganet.ru 6660 +#rusnet irc.odessa.ua 6660 +#scenep2p irc.scenep2p.net ~6697 +shadowcat irc.shadowcat.co.uk 6667 +shadowfire irc.shadowfire.org +7000 +shadowfire us.shadowfire.org +7000 +shadowfire eu.shadowfire.org +7000 +simosnap irc.simosnap.com +6697 +skychatz irc.skychatz.org ~6697 +slashnet irc.slashnet.org +6697 +smurfnet irc.smurfnet.ch ~6697 +snoonet irc.snoonet.org +6697 +socialhispana irc.socialhispana.org 6667 +sohbet irc.sohbet.net ~6697 +sorcery irc.sorcery.net ~6697 +spigotmc irc.spi.gt +6697 +spiderchat master.spiderchat.org +6697 +spotchat irc.spotchat.org +6697 +sturtz irc.sturtz.cf +6697 +superhosts irc.superhosts.net 6667 +svipchat irc.svipchat.org ~6697 +swiftirc irc.swiftirc.net 6667 +swissirc irc.swissirc.net ~6697 +swisschat irc.swisschat.tk 6667 +synirc irc.synirc.net ~6697 +technet irc.technet.xi.ht +6697 +thaiirc irc.thaiirc.com 6667 +tilde-chat irc.tilde.chat +6697 +trendsohbet irc.trendsohbet.com 6667 +trsohbet irc.trsohbet.com 6667 +tweakers irc.tweakers.net 6667 +twistednet irc.twistednet.org ~6697 +twitch irc.twitch.tv 6667 +umbrellanet irc.umbrellanet.org ~6697 +undernet irc6.undernet.org 6667 +undernet eu6.undernet.org 6667 +undernet us6.undernet.org 6667 +#undernet irc.undernet.org 6667 +#undernet chicago.il.us.undernet.org 6667 +#undernet bucharest.ro.eu.undernet.org 6667 +#undernet amsterdam.nl.eu.undernet.org 6667 +#undernet pipera.ro.eu.undernet.org 6667 +#undernet budapest.hu.eu.undernet.org 6667 +#undernet melbourne.vic.au.undernet.org 6667 +#undernet dallas.tx.us.undernet.org 6667 +#undernet losangeles.ca.us.undernet.org 6667 +#undernet miami.fl.us.undernet.org 6667 +#undernet capelle.nl.eu.undernet.org 6667 +uugrn irc.uugrn.org +6670 +virtualife irc.virtualife.org ~6697 +w3 irc.w3.org 6667 +wenet irc.wenet.ru 6667 +#whatnet irc.whatnet.org ~6697 +wnet irc.wnet.tk ~6697 +#wnet bantam.wnet.tk ~6697 +xertion irc.xertion.org +6697 +zairc irc.zairc.net ~6697 +zandronum irc.zandronum.com ~6697 +zemra irc.zemra.org +6697 +zenet irc.zenet.org +6697 +zeronode irc.zeronode.net +6697 +zoite irc.zoite.net +6697 +zonebg irc.zonebg.eu ~6697 +zurna irc.zurna.net 6667 +zwergenirc irc.zwergenirc.de ~6697 blob - /dev/null blob + 7b41db879e133958b015c4a89008ff02bec1b82f (mode 644) --- /dev/null +++ register.php @@ -0,0 +1,194 @@ + $line) { + if (preg_match('/^\#/', $line, $matches)) { + } elseif (preg_match('/^\s*$/', $line, $matches)) { + } elseif (preg_match('/^([^=\s]+)\s*=\s*(.*)$/', $line, $matches)) { + $key = $matches[1]; + $val = $matches[2]; + $conf{$key} = $val; + } +} + +$wordslist = file('words'); +$wordslen = sizeof($wordslist); +for ($n = 0; $n < 5; $n++) { + $i = rand(0, $wordslen-1); + $words[] = substr($wordslist[$i],0,-1); // remove newline +} +$passphrase = implode(" ", $words); +$passphrase = ucwords($passphrase); +$fpr{"passphrase"} = $passphrase; +$fpr{"remoteaddr"} = $_SERVER['REMOTE_ADDR']; +$fpr{"httpxforwarded"} = $_SERVER['HTTP_X_FORWARDED_FOR']; +$fpr{"time"} = date("Y-m-d H:i:s"); +if (isset($hashirc)) { + $fpr{"hashirc"} = $hashirc; +} elseif (isset($hashbnc)) { + $fpr{"hashbnc"} = $hashbnc; +} elseif (isset($hashshell)) { + $fpr{"hashshell"} = $hashshell; +} elseif (isset($hashsmtp)) { + $fpr{"hashsmtp"} = $hashsmtp; +} + +foreach (getallheaders() as $key => $value) { + if ($key == "User-Agent") { + $key = "useragent"; + } elseif ($key == "Upgrade-Insecure-Requests") { + $key = "upgradeinsecure"; + } elseif ($key == "Host") { + $key = "host"; + } elseif ($key == "Dnt") { + $key = "dnt"; + } elseif ($key == "Connection") { + $key = "connection"; + } elseif ($key == "Cache-Control") { + $key = "cachecontrol"; + } elseif ($key == "Accept-Language") { + $key = "acceptlanguage"; + } elseif ($key == "Accept-Encoding") { + $key = "acceptencoding"; + } elseif ($key == "Accept") { + $key = "accept"; + } elseif ($key == "Cookie") { + $key = "cookie"; + } + $fpr{$key} = $value; +} + +setcookie("ircnow[hashirc]", $hashirc, time()+86400*30, '/', 'ircnow.org', true); +setcookie("ircnow[hashbnc]", $hashbnc, time()+86400*30, '/', 'ircnow.org', true); +setcookie("ircnow[hashshell]", $hashshell, time()+86400*30, '/', 'ircnow.org', true); +setcookie("ircnow[hashsmtp]", $hashsmtp, time()+86400*30, '/', 'ircnow.org', true); + +class wwwdb extends SQLite3 { + function __construct() { + $this->open('/botnow/botnow.db'); + } +} +$wwwdb = new wwwdb(); +if(!$wwwdb) { + echo $wwwdb->lastErrorMsg(); +} else { + foreach ($fpr as $key => $value) { + $keys[] = $key; + $values[] = $value; + } + $keystr = '"'.implode('","', $keys).'"'; + $valstr = '"'.implode('","', $values).'"'; + $sql =<<exec($sql)) { + echo $db->lastErrorMsg(); + } +$clients = array("AdiIRC", "Adium", "AndroIRC", "Atomic", "Colloquy", "Hexchat", "HydraIRC", "IRCForAndroid", "IRCCloud", "IceChat", "ircEX", "irssi", "Igloo", "KVIrc", "KiwiIRC", "Limechat", "Mibbit", "mIRC", "nettalk", "Pidgin", "qwebirc", "Quassel", "RevolutionIRC", "SimpleIRC", "Smuxi", "Textual", "Thunderbird", "TurboIRC", "Weechat", "Yaaic", "XChat"); +$sql =<<query($sql); + while($row = $ret->fetchArray(SQLITE3_ASSOC)) { + echo "Don't worry I got called"; + $ircid = $row['id']; + $ctcpversion = $row['ctcpversion']; + $hostmask = $row['hostmask']; + foreach ($clients as $i => $value) { + if (preg_match("/$value/i", $ctcpversion, $matches)) { + $client = $value; + } + } + if (!isset($client)) { + if (preg_match('/!~?uid\d+/i', $hostmask, $matches)) { + $client = "IRCCloud"; + } elseif(preg_match('/!~?quassel/i', $hostmask, $matches)) { + $client = "Quassel"; + } elseif(preg_match('/!~?IceChat/i', $hostmask, $matches)) { + $client = "IceChat"; + } elseif(preg_match('/Purple\s+IRC/i', $ctcpversion, $matches)) { + $client = "Pidgin"; + } elseif(preg_match('/X-Chat/i', $ctcpversion, $matches)) { + $client = "XChat"; + } elseif(preg_match('/Kiwi\s+IRC/i', $ctcpversion, $matches)) { + $client = "KiwiIRC"; + } elseif(preg_match('/Android\s+IRC/i', $ctcpversion, $matches)) { + $client = "AndroIRC"; + } elseif(preg_match('/IRC\s+for\s+Android/i', $ctcpversion, $matches)) { + $client = "IRCForAndroid"; + } elseif(preg_match('/Revolution\s+IRC/i', $ctcpversion, $matches)) { + $client = "RevolutionIRC"; + } elseif(preg_match('/Yet\s+another\s+Android\s+IRC\s+client/i', $ctcpversion, $matches)) { + $client = "Yaaic"; + } else { + $client = "Bouncer"; + } + } + + } +$sql =<<query($sql); + while($row = $ret->fetchArray(SQLITE3_ASSOC)) { + $username = $row['username']; + } + $wwwdb->close(); +} +function myURLEncode($string) { + $replace = array('%20', '%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%2B', '%24', '%2C', '%2F', '%3F', '%23', '%5B', '%5D'); + $search = array(' ', '!', '*', "'", "(", ")", ";", ":", "@", "&", "=", "+", "$", ",", "/", "?", "#", "[", "]"); + return str_replace($search, $replace, $string); +} +$hostname = $conf{"hostname"}; +?> + + + + + + + + IRCNow -- Verify Account + + + + + +

Please email "> with the following passphrase:

+ +

IRCNow Verify:

+ +

Click here for connection instructions.

+ +

To change your password, login to ">, fill in the password field, then click on the "Save and Return" button.

+ +

Or, you can change the password from your IRC client. Once you have logged in, type:

+ +/msg *controlpanel set password newpassword + + + blob - /dev/null blob + c182e29fcc0bb7ec8e313bc89fbc5e7c0f7b447a (mode 644) --- /dev/null +++ shell @@ -0,0 +1,35 @@ +if [ -n "$1" ]; then + +groupadd $1 +adduser -batch $1 $1 $1 `encrypt $2` + +chmod 700 /home/$1 /home/$1/.ssh +chmod 600 /home/$1/{.Xdefaults,.cshrc,.cvsrc,.login,.mailrc,.profile} +mkdir /var/www/htdocs/$1 +ln -s /var/www/htdocs/$1 /home/$1/htdocs +chown -R $1:www /var/www/htdocs/$1 /home/$1/htdocs +chmod -R o-rx /var/www/htdocs/$1 /home/$1/htdocs +chmod -R g+rwx /var/www/htdocs/$1 /home/$1/htdocs +echo "server \"$1.us10.ircnow.org\" { + listen on * tls port 443 + tls { + certificate \"/etc/ssl/$1.us10.ircnow.org.fullchain.pem\" + key \"/etc/ssl/private/$1.us10.ircnow.org.key\" + } + location \"/.well-known/acme-challenge/*\" { + root \"/acme\" + } + location \"*.php\" { + fastcgi socket \"/run/php-fpm.sock\" + } + root \"/htdocs/$1\" +} +" >> /etc/httpd.conf +echo "domain \"$1.us10.ircnow.org\" { + domain key \"/etc/ssl/private/$1.us10.ircnow.org.key\" + domain full chain certificate \"/etc/ssl/$1.us10.ircnow.org.fullchain.pem\" + sign with letsencrypt +} +" >> /etc/acme-client.conf +#edquota $1 +fi blob - /dev/null blob + 67031f5c862fcf9f75c9d3e95e7a7785386156f0 (mode 644) --- /dev/null +++ shelllastseen @@ -0,0 +1,229 @@ +birkoff => Nov 13 04:33:44 +gry => Nov 17 21:05:56 +fear => Aug 29 04:17:38 +caesar => Sep 11 15:20:43 +jrmu => Nov 26 19:56:08 +joshua => Nov 26 18:10:42 +micah => Never logged in +nahum => Never logged in +habakkuk => Never logged in +zephaniah => Never logged in +haggai => Never logged in +zechariah => Never logged in +malachi => Never logged in +elijah => Nov 18 03:30:49 +elisha => Nov 17 21:02:18 +hub => Nov 12 18:14:29 +eggdrop => Nov 17 19:15:01 +koragg => Oct 23 14:10:21 +jason => Nov 13 01:04:24 +venin => Jul 30 10:41:28 +comptech => Oct 18 14:49:28 +petualang => Nov 19 06:12:35 +abra => Nov 27 00:33:10 +Agent => Aug 16 15:01:09 +xiwang => Nov 26 18:52:18 +bongi => Jul 25 19:41:27 +felicia2006 => Sep 11 02:30:56 +pahhlavvii => Jul 26 21:47:30 +openbsdtai123 => Nov 26 22:35:36 +znc => Nov 13 04:16:23 +bnc => Nov 17 19:37:34 +aidan => Oct 16 09:18:46 +twentyfox => Nov 13 19:14:32 +jimmy_will => Oct 11 23:14:37 +psybnc => Nov 17 19:37:34 +mo => Nov 26 18:03:17 +superman => Nov 26 21:58:26 +rashk0 => Sep 12 10:47:31 +alfa => Nov 13 03:43:13 +gwapo => Nov 28 02:05:10 +depeche => Sep 17 03:06:25 +hery => Nov 17 19:07:03 +ExClam => Jul 29 17:36:02 +ruel => Oct 20 19:50:21 +AhmaR => Nov 13 19:31:59 +shaman => Nov 27 06:04:23 +BaDoO => Jul 22 23:46:30 +rvn => Oct 18 12:48:24 +Jingo => Nov 13 07:15:25 +Robys => Nov 13 13:15:15 +braian => Nov 13 13:16:20 +kasikas => Sep 24 08:07:23 +Psycho => Nov 28 05:57:06 +dennis => Nov 27 04:18:47 +lukas => Nov 12 18:53:07 +search_social => Nov 13 06:17:55 +hoshi => Nov 17 18:36:39 +mars => Nov 17 18:18:28 +zoro => Oct 17 02:06:08 +firz => Aug 20 22:51:47 +Freuddy => Nov 10 07:06:48 +enemy => Sep 12 10:08:35 +uno => Nov 27 14:08:01 +ComputerTech => Nov 18 14:28:19 +rahab => Sep 19 22:42:38 +Rocking => Nov 27 20:51:39 +rufiogod => Sep 14 20:37:41 +MaTeUs => Nov 18 11:00:14 +clauvius => Never logged in +ciclope => Nov 10 09:26:56 +kassandra => Nov 18 06:58:12 +shutdown => Nov 26 21:17:23 +dex => Nov 12 23:44:26 +XploiD => Never logged in +Geniux => Nov 13 13:18:29 +xiuniu => Nov 13 12:53:01 +PU => Nov 15 15:46:32 +client => Nov 26 21:14:28 +apin => Nov 17 15:33:44 +zas => Nov 15 15:10:22 +CW => Nov 15 17:26:01 +daniel => Nov 26 21:55:54 +yazdan => Nov 27 06:53:03 +nobody => Nov 26 18:35:57 +usrbin => Oct 19 00:10:57 +india => Nov 17 12:34:23 +rss => Aug 28 18:50:39 +miniontoby => Never logged in +josh => Nov 26 18:10:42 +nepal => Sep 12 18:13:32 +Freak => Oct 25 01:58:37 +jonas => Nov 13 00:34:23 +ronnie => Jul 17 18:55:31 +raimondraj => Never logged in +tunisie => Oct 31 08:15:32 +clort => Nov 26 20:16:46 +skg123 => Jul 22 11:25:23 +Shadow => Nov 15 16:41:21 +eiei => Nov 10 19:32:20 +holder => Nov 27 06:56:24 +ciprian => Oct 11 11:02:11 +kisanak => Nov 14 01:35:27 +lion => Nov 17 19:50:00 +soysoy => Oct 27 07:21:36 +skin => Nov 14 01:36:36 +DaWolf => Aug 19 04:45:22 +dd => Nov 27 06:20:06 +renato => Nov 26 18:49:16 +bogdan => Oct 17 00:50:41 +menzen => Nov 14 01:36:03 +xaoc => Oct 18 16:26:39 +lotos => Nov 27 06:59:20 +Achilleas1 => Aug 24 11:29:48 +ayam => Nov 19 14:22:59 +trashman => Nov 27 19:59:15 +beef => Aug 24 02:09:16 +jonathanschlink => Nov 26 23:23:29 +crash => Jul 24 11:38:01 +keset => Nov 27 06:57:53 +pi => Nov 26 18:00:06 +BraT => Never logged in +sushi => Oct 17 02:07:05 +Puknat => Jul 20 21:35:46 +jabz => Nov 14 21:14:13 +Oz => Nov 26 21:24:28 +AAghori => Jul 17 00:06:28 +Darkness => Aug 14 04:58:14 +masoom => Never logged in +nix => Nov 27 14:20:36 +Tuidor => Nov 13 05:37:17 +error => Nov 26 18:03:17 +rifwan => Never logged in +kuliglig => Never logged in +berton => Nov 13 13:15:54 +terox => Nov 28 04:19:40 +Poul => Never logged in +malic => Sep 23 07:46:30 +Baba => Sep 14 22:57:09 +firewall => Nov 14 08:34:42 +maz => Nov 28 10:50:25 +uzi => Oct 15 21:45:30 +Alcahest => Never logged in +rafy => Oct 20 06:48:49 +AdrameleK => Never logged in +NeiL => Sep 27 01:37:03 +cyberdine => Nov 13 14:09:24 +patrick => Nov 27 01:26:23 +skolast => Aug 18 21:55:00 +quofan => Aug 28 21:51:21 +baytuch => Aug 27 05:20:16 +fizi => Nov 13 05:38:14 +tabmode => Never logged in +kghm => Nov 11 09:12:08 +morgana => Never logged in +bastusai => Nov 12 18:41:43 +merdeka => Jul 27 06:14:35 +jhong => Aug 28 04:42:14 +cypher => Sep 24 08:08:35 +baby => Nov 17 16:03:51 +azygous => Nov 28 17:39:13 +mno => Nov 13 19:46:05 +x81 => Never logged in +undersys => Jul 22 21:55:40 +topples => Jul 24 07:28:08 +brb => Jul 17 10:42:48 +misterx => Nov 15 02:12:50 +dwizzt => Aug 13 11:32:57 +joker => Sep 17 14:28:46 +game => Nov 26 18:35:26 +hal9000 => Never logged in +planetofnix => Oct 24 17:33:29 +Azan => Nov 12 18:30:55 +bmorebelieve => Never logged in +berceni => Sep 24 12:19:03 +iced => Sep 19 12:17:39 +jinx => Nov 13 14:04:53 +david => Nov 26 21:59:06 +jessie => Nov 28 06:52:40 +flOP => Jul 26 16:59:20 +midas => Jul 25 22:23:46 +fiubot => Nov 13 07:07:14 +hippie => Sep 17 10:21:11 +gusteru => Never logged in +ita => Nov 26 18:42:26 +leonid => Nov 18 03:57:15 +quintuplicate => Never logged in +deliz => Sep 28 01:21:01 +Carenderia => Sep 29 19:16:16 +dragon => Nov 27 05:07:12 +nyaties => Sep 25 08:42:09 +skipandrun => Aug 17 11:48:24 +yeoj => Oct 24 06:20:41 +neonshell => Aug 21 21:22:47 +RyDeRz => Aug 29 08:20:40 +sen => Nov 26 21:14:28 +Mikael => Never logged in +Dea => Nov 13 05:29:04 +Eve => Oct 16 14:36:07 +Thor => Nov 14 00:07:10 +Noxturnix => Oct 25 12:24:09 +neuro => Sep 26 02:55:55 +itsme => Nov 26 19:26:24 +ghost => Nov 26 18:52:21 +ahbaht => Nov 26 18:58:55 +batibot => Oct 14 17:22:36 +johncarl => Sep 28 06:14:55 +jhilke => Never logged in +ruan => Oct 11 00:33:06 +fossil => Nov 28 06:30:26 +spaceduck => Never logged in +Steve => Nov 14 04:21:00 +mahesh => Nov 13 10:20:28 +botnow => Never logged in +hungryjimmy => Oct 28 23:17:09 +ircnetcom => Oct 16 00:25:42 +slaserx => Oct 14 05:41:45 +Fraternity => Never logged in +prodigy => Nov 10 02:30:51 +alien => Nov 28 08:22:11 +brax => Oct 24 06:27:55 +doni => Nov 27 05:35:01 +inflikted => Nov 10 23:56:58 +Beric => Oct 23 04:14:31 +BGS => Oct 24 03:26:11 +sup => Nov 26 18:06:03 +bad => Nov 26 19:58:12 +r3d => Nov 19 17:53:00 +st13g => Nov 16 08:59:11 +slash => Nov 18 15:41:41 blob - /dev/null blob + b57bfab705b26c3dc55ecb6a56980ccb275437c0 (mode 644) --- /dev/null +++ table.sql @@ -0,0 +1,85 @@ +CREATE TABLE bnc ( + id INTEGER PRIMARY KEY, + hashid VARCHAR(100), + ircid INTEGER, + wwwid INTEGER, + smtpid INTEGER, + username VARCHAR(32), + email VARCHAR(100), + password VARCHAR(100), + localtime VARCHAR(100), + captcha INTEGER +); +CREATE TABLE shell ( + id INTEGER PRIMARY KEY, + hashid VARCHAR(100), + ircid INTEGER, + wwwid INTEGER, + smtpid INTEGER, + username VARCHAR(32), + email VARCHAR(100), + password VARCHAR(100), + localtime VARCHAR(100), + captcha INTEGER +); +CREATE TABLE www ( + id INTEGER PRIMARY KEY, + hashid VARCHAR(100), + hashirc VARCHAR(100), + hashbnc VARCHAR(100), + hashshell VARCHAR(100), + hashsmtp VARCHAR(100), + cookie VARCHAR(1024), + passphrase VARCHAR(100), + remoteaddr VARCHAR(50), + httpxforwarded VARCHAR(100), + time VARCHAR(100), + useragent VARCHAR(200), + upgradeinsecure INTEGER, + host VARCHAR(100), + dnt INTEGER, + connection VARCHAR(100), + cachecontrol VARCHAR(100), + acceptlanguage VARCHAR(100), + acceptencoding VARCHAR(100), + accept VARCHAR(200), + jstime VARCHAR(100), + timezoneoffset VARCHAR(100), + timezone VARCHAR(100), + screenwidth INTEGER, + screenheight INTEGER, + colordepth INTEGER +); +CREATE TABLE irc ( + id INTEGER PRIMARY KEY, + hashid VARCHAR(100), + hostmask VARCHAR(100), + nick VARCHAR(32), + realname VARCHAR(100), + ip VARCHAR(50), + server VARCHAR(100), + identified INTEGER, + ctcpversion VARCHAR(100), + ctcptime VARCHAR(100), + localtime VARCHAR(100), + oper INTEGER, + idle INTEGER, + ssl INTEGER, + epochtime INTEGER, + chans VARCHAR(200), + date VARCHAR(100) +); +CREATE TABLE smtp ( + id INTEGER PRIMARY KEY, + hashid VARCHAR(100), + sentfrom VARCHAR(100), + returnpath VARCHAR(100), + deliveredto VARCHAR(100), + received VARCHAR(1000), + dkim VARCHAR(1000), + date VARCHAR(100), + other VARCHAR(5000), + content VARCHAR(100), + mime VARCHAR(100), + subject VARCHAR(100) +); blob - /dev/null blob + 9030a1d373c2fcf02b3ad8615338d78f36e05553 (mode 644) --- /dev/null +++ todo @@ -0,0 +1,39 @@ +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Channel #plum for user cloneuser added to network virtualife. +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Channel #testing for user cloneuser added to network virtualife. +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Network ircnow added to user cloneuser. +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Added IRC Server 2605:6400:30:fc15:: 6667 to network ircnow for user cloneuser. +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Closed IRC connection for network ircnow of user cloneuser. +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Channel #plum for user cloneuser added to network ircnow. +Unexpected 290 BNC.pm: *controlpanel!znc@znc.in Channel #testing for user cloneuser added to network ircnow. + +check admin status + +server "www.guava.ircnow.org" { + alias "guava.ircnow.org" + listen on * tls port 443 + tls { + certificate "/etc/ssl/guava.ircnow.org.fullchain.pem" + key "/etc/ssl/private/guava.ircnow.org.key" + } + location "/.well-known/acme-challenge/*" { + root "/acme" + } + location "*.php" { + fastcgi socket "/run/php-fpm.sock" + } + location "*/captcha.png" { + request strip 1 + fastcgi socket "/run/php-fpm.sock" + } + root "/htdocs/botnow" +} + +php-gd-7.4.12 image manipulation extensions for php +php-sqlite3-7.4.12 sqlite3 database access extension for php + +/etc/php-7.4.ini +extension=sqlite3 +extension=gd + +/etc/php-fpm.conf +security.limit_extensions = .php .php3 .php4 .php5 .php7 .png blob - /dev/null blob + 0795531c811821c7bd42911c5851498a32964b24 (mode 755) --- /dev/null +++ user_manager.sh @@ -0,0 +1,202 @@ +#!/bin/sh + +htdocs_chroot_path="/home/www/htdocs" +log_path="/var/log/user_manager.log" +lock_list="jrmu,fizi,baytuch,vasya,gry,brk,mailman" + +user_exists="NO" +group_exists="NO" +target_user_id="" +target_group_id="" +error_trig="NO" + + +check_exists() { + user_exists=NO + group_exists=NO + if [ ! -z "$(grep "^$1:.*$" /etc/passwd)" ]; then + user_exists="YES" + fi + if [ ! -z "$(grep "^$1:.*$" /etc/group)" ]; then + group_exists="YES" + fi +} + +create_user() { + groupadd -ov -g $3 $1 + useradd -v -m -d /home/$1 -g $3 -s /bin/ksh -u $2 $1 + passwd $1 +} + +delete_user() { + userdel -rv $1 + groupdel -v $1 +} + +logger() { + if [ ! -f $log_path ]; then + touch $log_path + fi + mess=[$(date '+%Y-%m-%d %H:%M:%S')]" "$1 + echo $mess >> $log_path + echo $mess +} + +get_target_ids() { + if [ $user_exists == "YES" ]; then + target_user_id=$(grep "^$1" /etc/passwd | sed -r -e 's|^.*:([0-9]+):[0-9]+:.*$|\1|g') + target_group_id=$(grep "^$1" /etc/passwd | sed -r -e 's|^.*:[0-9]+:([0-9]+):.*$|\1|g') + else + users_ids=$(cat /etc/passwd | sed -r -e 's|^.*:([0-9]+:[0-9]+):.*$|\1|g') + tmp_last_user_id=0 + for line in $users_ids; do + tmp_user_id=${line%:*} + tmp_group_id=${line#*:} + if [ ${#tmp_user_id} -eq 4 ] && [ ${#tmp_group_id} -eq 4 ]; then + if [ $tmp_user_id -eq $tmp_group_id ]; then + if [ $tmp_user_id -gt $tmp_last_user_id ]; then + tmp_last_user_id=$tmp_user_id + fi + fi + fi + done + target_user_id=$tmp_last_user_id + target_group_id=$tmp_last_user_id + fi + target_user_id=$(echo $target_user_id | sed 's/[^0-9]//g') + target_group_id=$(echo $target_group_id | sed 's/[^0-9]//g') + if [ $user_exists == "NO" ] && [ ! -z $target_user_id ] && [ ! -z $target_group_id ]; then + target_user_id=$((target_user_id + 1)) + target_group_id=$((target_group_id + 1)) + fi +} + +user_tree_config() { + if [ -d /home/$1 ]; then + chmod 700 /home/$1 + chmod 700 /home/$1/.ssh + chmod 600 /home/$1/{.Xdefaults,.cshrc,.cvsrc,.login,.mailrc,.profile} + logger "TREE CONFIG: was configured home dir" + if [ -d $htdocs_chroot_path ]; then + mkdir $htdocs_chroot_path/$1 + chown $1:$1 $htdocs_chroot_path/$1 + ln -s $htdocs_chroot_path/$1 /home/$1/htdocs + chown $1:$1 /home/$1/htdocs + logger "TREE CONFIG: was configured htdocs dir" + else + logger "TREE CONFIG: htdocs chroot not found!" + fi + else + logger "TREE CONFIG: home folder not found!" + fi +} + +user_tree_delete() { + if [ -d /home/$1 ]; then + if [ -d /home/$1/htdocs ]; then + rm -rf /home/$1/htdocs + fi + if [ -d $htdocs_chroot_path/$1 ]; then + rm -rf $htdocs_chroot_path/$1 + fi + if [ ! -d /home/$1/htdocs ] && [ ! -d $htdocs_chroot_path/$1 ]; then + logger "TREE DELETE: was deteled htdocs dir" + else + error_trig="YES" + logger "TREE DELETE: failed to delete folder tree!" + fi + else + error_trig="YES" + logger "TREE DELETE: home folder not found!" + fi +} + +do_create_user() { + logger "WORKER: create a new user..." + if [ -z $1 ]; then + logger "WORKER: you must provide username!" + else + check_exists $1 + if [ $user_exists == "YES" ]; then + logger "WORKER: user $1 already exists" + else + get_target_ids $1 + if [ ${#target_user_id} -eq 4 ] && [ ${#target_group_id} -eq 4 ]; then + logger "WORKER: process of creating a new user has started" + logger "WORKER: -> login - $1" + logger "WORKER: -> user_id - $target_user_id" + logger "WORKER: -> group_id - $target_group_id" + create_user $1 $target_user_id $target_group_id + check_exists $1 + if [ $user_exists == "YES" ]; then + logger "WORKER: user created successfully" + logger "WORKER: user folder tree configuration..." + user_tree_config $1 + else + logger "WORKER: user creation failure!" + fi + else + logger "WORKER: error generating identifiers!" + fi + fi + fi +} + +do_delete_user() { + logger "WORKER: removing a user account..." + if [ -z $1 ]; then + logger "WORKER: you must provide username!" + else + check_exists $1 + if [ $user_exists == "NO" ]; then + logger "WORKER: user $1 not found!" + else + get_target_ids $1 + if [ ${#target_user_id} -ne 4 ]; then + logger "WORKER: you cannot delete the service user!" + else + deny_del="NO" + for tmp_user_name in $(echo $lock_list | tr "," "\n"); do + if [ $1 == $tmp_user_name ]; then + deny_del="YES" + break + fi + done + if [ $deny_del == "NO" ]; then + logger "WORKER: deleting a user's folder tree..." + user_tree_delete $1 + if [ $error_trig == "NO" ]; then + logger "WORKER: deleting a user account..." + delete_user $1 + check_exists $1 + if [ $user_exists == "NO" ]; then + logger "WORKER: user account of $1 deleted" + else + logger "WORKER: failed to delete account!" + fi + fi + else + logger "WORKER: the deletion of this user is blocked!" + fi + fi + fi + fi +} + +if [ $(whoami) == "root" ]; then + case "$1" in + create) + do_create_user $2 + ;; + delete) + do_delete_user $2 + ;; + *) + logger "SELECTOR: unknown command" + ;; + esac + exit 0 +else + echo "must be run as root!" + exit 1 +fi blob - /dev/null blob + 9a08b418928b428b36f54f5ded4132a59696ec8f (mode 644) --- /dev/null +++ words @@ -0,0 +1,5459 @@ +about +search +other +which +their +there +contact +business +online +first +would +services +these +click +service +price +people +state +email +health +world +products +music +should +product +system +policy +number +please +support +message +after +software +video +where +rights +public +books +school +through +links +review +years +order +privacy +items +company +group +under +general +research +january +reviews +program +games +could +great +united +hotel +center +store +travel +comments +report +member +details +terms +before +hotels +right +because +local +those +using +results +office +national +design +posted +internet +address +within +states +phone +shipping +reserved +subject +between +forum +family +based +black +check +special +prices +website +index +being +women +today +south +project +pages +version +section +found +sports +house +related +security +county +american +photo +members +power +while +network +computer +systems +three +total +place +download +without +access +think +north +current +posts +media +control +water +history +pictures +personal +since +guide +board +location +change +white +small +rating +children +during +return +students +shopping +account +times +sites +level +digital +profile +previous +events +hours +image +title +another +shall +property +class +still +money +quality +every +listing +content +country +private +little +visit +tools +reply +customer +december +compare +movies +include +college +value +article +provide +source +author +press +learn +around +print +course +canada +process +stock +training +credit +point +science +advanced +sales +english +estate +select +windows +photos +thread +category +large +gallery +table +register +however +october +november +market +library +really +action +start +series +model +features +industry +human +provided +required +second +movie +forums +march +better +yahoo +going +medical +friend +server +study +staff +articles +feedback +again +looking +issues +april +never +users +complete +street +topic +comment +things +working +against +standard +person +below +mobile +party +payment +login +student +programs +offers +legal +above +recent +stores +problem +memory +social +august +quote +language +story +options +rates +create +young +america +field +paper +single +example +girls +password +latest +question +changes +night +texas +poker +status +browse +issue +range +building +seller +court +february +always +result +audio +light +write +offer +groups +given +files +event +release +analysis +request +china +making +picture +needs +possible +might +month +major +areas +future +space +cards +problems +london +meeting +become +interest +child +enter +share +similar +garden +schools +million +added +listed +learning +energy +delivery +popular +stories +journal +reports +welcome +central +images +notice +original +radio +until +color +council +includes +track +archive +others +format +least +society +months +safety +friends +trade +edition +messages +further +updated +having +provides +david +already +green +studies +close +common +drive +specific +several +living +called +short +display +limited +powered +means +director +daily +beach +natural +whether +period +planning +database +official +weather +average +window +france +region +island +record +direct +records +district +calendar +costs +style +front +update +parts +early +miles +sound +resource +present +either +document +works +material +written +federal +hosting +rules +final +adult +tickets +thing +centre +cheap +finance +minutes +third +gifts +europe +reading +topics +cover +usually +together +videos +percent +function +getting +global +economic +player +projects +lyrics +often +submit +germany +amount +watch +included +though +thanks +deals +various +words +linux +james +weight +heart +received +choose +archives +points +magazine +error +camera +clear +receive +domain +methods +chapter +makes +policies +beauty +manager +india +position +taken +listings +models +michael +known +cases +florida +simple +quick +wireless +license +friday +whole +annual +later +basic +shows +google +church +method +purchase +active +response +practice +hardware +figure +holiday +enough +designed +along +among +death +writing +speed +brand +discount +higher +effects +created +remember +yellow +increase +kingdom +thought +stuff +french +storage +japan +doing +loans +shoes +entry +nature +orders +africa +summary +growth +notes +agency +monday +european +activity +although +western +income +force +overall +river +package +contents +players +engine +album +regional +supplies +started +views +plans +double +build +screen +exchange +types +lines +continue +across +benefits +needed +season +apply +someone +anything +printer +believe +effect +asked +sunday +casino +volume +cross +anyone +mortgage +silver +inside +solution +mature +rather +weeks +addition +supply +nothing +certain +running +lower +union +jewelry +clothing +names +robert +homepage +skills +islands +advice +career +military +rental +decision +leave +british +teens +woman +sellers +middle +cable +taking +values +division +coming +tuesday +object +lesbian +machine +length +actually +score +client +returns +capital +follow +sample +shown +saturday +england +culture +flash +george +choice +starting +thursday +courses +consumer +airport +foreign +artist +outside +levels +channel +letter +phones +ideas +summer +allow +degree +contract +button +releases +homes +super +matter +custom +virginia +almost +located +multiple +asian +editor +cause +focus +featured +rooms +female +thomas +primary +cancer +numbers +reason +browser +spring +answer +voice +friendly +schedule +purpose +feature +comes +police +everyone +approach +cameras +brown +physical +medicine +ratings +chicago +forms +glass +happy +smith +wanted +thank +unique +survey +prior +sport +ready +animal +sources +mexico +regular +secure +simply +evidence +station +round +paypal +favorite +option +master +valley +recently +probably +rentals +built +blood +improve +larger +networks +earth +parents +nokia +impact +transfer +kitchen +strong +carolina +wedding +hospital +ground +overview +owners +disease +italy +perfect +classic +basis +command +cities +william +express +award +distance +peter +ensure +involved +extra +partners +budget +rated +guides +success +maximum +existing +quite +selected +amazon +patients +warning +horse +forward +flowers +stars +lists +owner +retail +animals +useful +directly +housing +takes +bring +catalog +searches +trying +mother +traffic +joined +input +strategy +agent +valid +modern +senior +ireland +teaching +grand +testing +trial +charge +units +instead +canadian +normal +wrote +ships +entire +leading +metal +positive +fitness +chinese +opinion +football +abstract +output +funds +greater +likely +develop +artists +guest +seems +trust +contains +session +multi +republic +vacation +century +academic +graphics +indian +expected +grade +dating +pacific +mountain +filter +mailing +vehicle +longer +consider +northern +behind +panel +floor +german +buying +match +proposed +default +require +outdoor +morning +allows +protein +plant +reported +politics +partner +authors +boards +faculty +parties +mission +string +sense +modified +released +stage +internal +goods +unless +richard +detailed +japanese +approved +target +except +ability +maybe +moving +brands +places +pretty +spain +southern +yourself +winter +battery +youth +pressure +boston +keywords +medium +break +purposes +dance +itself +defined +papers +playing +awards +studio +reader +virtual +device +answers +remote +external +apple +offered +theory +enjoy +remove +surface +minimum +visual +variety +teachers +martin +manual +block +subjects +agents +repair +civil +steel +songs +fixed +wrong +hands +finally +updates +desktop +classes +paris +sector +capacity +requires +jersey +fully +father +electric +quotes +officer +driver +respect +unknown +worth +teacher +workers +georgia +peace +campus +showing +creative +coast +benefit +progress +funding +devices +grant +agree +fiction +watches +careers +beyond +families +museum +blogs +accepted +former +complex +agencies +parent +spanish +michigan +columbia +setting +scale +stand +economy +highest +helpful +monthly +critical +frame +musical +angeles +employee +chief +gives +bottom +packages +detail +changed +heard +begin +colorado +royal +clean +switch +russian +largest +african +titles +relevant +justice +connect +bible +basket +applied +weekly +demand +suite +vegas +square +chris +advance +auction +allowed +correct +charles +nation +selling +piece +sheet +seven +older +illinois +elements +species +cells +module +resort +facility +random +pricing +minister +motion +looks +fashion +visitors +monitor +trading +forest +calls +whose +coverage +couple +giving +chance +vision +ending +clients +actions +listen +discuss +accept +naked +clinical +sciences +markets +lowest +highly +appear +lives +currency +leather +patient +actual +stone +commerce +perhaps +persons +tests +village +accounts +amateur +factors +coffee +settings +buyer +cultural +steve +easily +poster +closed +holidays +zealand +balance +graduate +replies +initial +label +thinking +scott +canon +league +waste +minute +provider +optional +sections +chair +fishing +effort +phase +fields +fantasy +letters +motor +context +install +shirt +apparel +crime +count +breast +johnson +quickly +dollars +websites +religion +claim +driving +surgery +patch +measures +kansas +chemical +doctor +reduce +brought +himself +enable +exercise +santa +leader +diamond +israel +servers +alone +meetings +seconds +jones +arizona +keyword +flight +congress +username +produced +italian +pocket +saint +freedom +argument +creating +drugs +joint +premium +fresh +attorney +upgrade +factor +growing +stream +hearing +eastern +auctions +therapy +entries +dates +signed +upper +serious +prime +samsung +limit +began +louis +steps +errors +shops +efforts +informed +thoughts +creek +worked +quantity +urban +sorted +myself +tours +platform +labor +admin +nursing +defense +machines +heavy +covered +recovery +merchant +expert +protect +solid +became +orange +vehicles +prevent +theme +campaign +marine +guitar +finding +examples +saying +spirit +claims +motorola +affairs +touch +intended +towards +goals +election +suggest +branch +charges +serve +reasons +magic +mount +smart +talking +latin +avoid +manage +corner +oregon +element +birth +virus +abuse +requests +separate +quarter +tables +define +racing +facts +column +plants +faith +chain +identify +avenue +missing +domestic +sitemap +moved +houston +reach +mental +viewed +moment +extended +sequence +attack +sorry +centers +opening +damage +reserve +recipes +gamma +plastic +produce +placed +truth +counter +failure +follows +weekend +dollar +ontario +films +bridge +native +williams +movement +printing +baseball +owned +approval +draft +chart +played +contacts +jesus +readers +clubs +jackson +equal +matching +offering +shirts +profit +leaders +posters +variable +expect +parking +compared +workshop +russia +codes +kinds +seattle +golden +teams +lighting +senate +forces +funny +brother +turned +portable +tried +returned +pattern +named +theatre +laser +earlier +sponsor +warranty +indiana +harry +objects +delete +evening +assembly +nuclear +taxes +mouse +signal +criminal +issued +brain +sexual +powerful +dream +obtained +false +flower +passed +supplied +falls +opinions +promote +stated +stats +hawaii +appears +carry +decided +covers +hello +designs +maintain +tourism +priority +adults +clips +savings +graphic +payments +binding +brief +ended +winning +eight +straight +script +served +wants +prepared +dining +alert +atlanta +dakota +queen +credits +clearly +handle +sweet +criteria +pubmed +diego +truck +behavior +enlarge +revenue +measure +changing +votes +looked +festival +ocean +flights +experts +signs +depth +whatever +logged +laptop +vintage +train +exactly +explore +maryland +concept +nearly +eligible +checkout +reality +forgot +handling +origin +gaming +feeds +billion +scotland +faster +dallas +bought +nations +route +followed +broken +frank +alaska +battle +anime +speak +protocol +query +equity +speech +rural +shared +sounds +judge +bytes +forced +fight +height +speaker +filed +obtain +offices +designer +remain +managed +failed +marriage +korea +banks +secret +kelly +leads +negative +austin +toronto +theater +springs +missouri +andrew +perform +healthy +assets +injury +joseph +ministry +drivers +lawyer +figures +married +proposal +sharing +portal +waiting +birthday +gratis +banking +brian +toward +slightly +assist +conduct +lingerie +calling +serving +profiles +miami +comics +matters +houses +postal +controls +breaking +combined +ultimate +wales +minor +finish +noted +reduced +physics +spent +extreme +samples +davis +daniel +reviewed +forecast +removed +helps +singles +cycle +amounts +contain +accuracy +sleep +pharmacy +brazil +creation +static +scene +hunter +crystal +famous +writer +chairman +violence +oklahoma +speakers +drink +academy +dynamic +gender +cleaning +concerns +vendor +intel +officers +referred +supports +regions +junior +rings +meaning +ladies +henry +ticket +guess +agreed +soccer +import +posting +presence +instant +viewing +majority +christ +aspects +austria +ahead +scheme +utility +preview +manner +matrix +devel +despite +strength +turkey +proper +degrees +delta +seeking +inches +phoenix +shares +daughter +standing +comfort +colors +cisco +ordering +alpha +appeal +cruise +bonus +bookmark +specials +disney +adobe +smoking +becomes +drives +alabama +improved +trees +achieve +dress +dealer +nearby +carried +happen +exposure +gambling +refer +miller +outdoors +clothes +caused +luxury +babes +frames +indeed +circuit +layer +printed +removal +easier +printers +adding +kentucky +mostly +taylor +prints +spend +factory +interior +revised +optical +relative +amazing +clock +identity +suites +feeling +hidden +victoria +serial +relief +revision +ratio +planet +copies +recipe +permit +seeing +proof +tennis +bedroom +empty +instance +licensed +orlando +bureau +maine +ideal +specs +recorded +pieces +finished +parks +dinner +lawyers +sydney +stress +cream +trends +discover +patterns +boxes +hills +fourth +advisor +aware +wilson +shape +irish +stations +remains +greatest +firms +operator +generic +usage +charts +mixed +census +exist +wheel +transit +compact +poetry +lights +tracking +angel +keeping +attempt +matches +width +noise +engines +forget +array +accurate +stephen +climate +alcohol +greek +managing +sister +walking +explain +smaller +newest +happened +extent +sharp +lesbians +export +managers +aircraft +modules +sweden +conflict +versions +employer +occur +knows +describe +concern +backup +citizens +heritage +holding +trouble +spread +coach +kevin +expand +audience +assigned +jordan +affect +virgin +raised +directed +dealers +sporting +helping +affected +totally +plate +expenses +indicate +blonde +anderson +organic +albums +cheats +guests +hosted +diseases +nevada +thailand +agenda +anyway +tracks +advisory +logic +template +prince +circle +grants +anywhere +atlantic +edward +investor +leaving +wildlife +cooking +speaking +sponsors +respond +sizes +plain +entered +launch +checking +costa +belgium +guidance +trail +symbol +crafts +highway +buddy +observed +setup +booking +glossary +fiscal +styles +denver +filled +channels +ericsson +appendix +notify +blues +portion +scope +supplier +cables +cotton +biology +dental +killed +border +ancient +debate +starts +causes +arkansas +leisure +learned +notebook +explorer +historic +attached +opened +husband +disabled +crazy +upcoming +britain +concert +scores +comedy +adopted +weblog +linear +bears +carrier +edited +constant +mouth +jewish +meter +linked +portland +concepts +reflect +deliver +wonder +lessons +fruit +begins +reform +alerts +treated +mysql +relating +assume +alliance +confirm +neither +lewis +howard +offline +leaves +engineer +replace +checks +reached +becoming +safari +sugar +stick +allen +relation +enabled +genre +slide +montana +tested +enhance +exact +bound +adapter +formal +hockey +storm +micro +colleges +laptops +showed +editors +threads +supreme +brothers +presents +dolls +estimate +cancel +limits +weapons +paint +delay +pilot +outlet +czech +novel +ultra +winner +idaho +episode +potter +plays +bulletin +modify +oxford +truly +epinions +painting +universe +patent +eating +planned +watching +lodge +mirror +sterling +sessions +kernel +stocks +buyers +journals +jennifer +antonio +charged +broad +taiwan +chosen +greece +swiss +sarah +clark +terminal +nights +behalf +liquid +nebraska +salary +foods +gourmet +guard +properly +orleans +saving +empire +resume +twenty +newly +raise +prepare +avatar +illegal +hundreds +lincoln +helped +premier +tomorrow +decide +consent +drama +visiting +downtown +keyboard +contest +bands +suitable +millions +lunch +audit +chamber +guinea +findings +muscle +clicking +polls +typical +tower +yours +chicken +attend +shower +sending +jason +tonight +holdem +shell +province +catholic +governor +seemed +swimming +spyware +formula +solar +catch +pakistan +reliable +doubt +finder +unable +periods +tasks +attacks +const +doors +symptoms +resorts +biggest +memorial +visitor +forth +insert +gateway +alumni +drawing +ordered +fighting +happens +romance +bruce +split +themes +powers +heaven +pregnant +twice +focused +egypt +bargain +cellular +norway +vermont +asking +blocks +normally +hunting +diabetes +shift +bodies +cutting +simon +writers +marks +flexible +loved +mapping +numerous +birds +indexed +superior +saved +paying +cartoon +shots +moore +granted +choices +carbon +spending +magnetic +registry +crisis +outlook +massive +denmark +employed +bright +treat +header +poverty +formed +piano +sheets +patrick +puerto +displays +plasma +allowing +earnings +mystery +journey +delaware +bidding +risks +banner +charter +barbara +counties +ports +dreams +blogger +stands +teach +occurred +rapid +hairy +reverse +deposit +seminar +latina +wheels +specify +dutch +formats +depends +boots +holds +router +concrete +editing +poland +folder +womens +upload +pulse +voting +courts +notices +detroit +metro +toshiba +strip +pearl +accident +resident +possibly +airline +regard +exists +smooth +strike +flashing +narrow +threat +surveys +sitting +putting +vietnam +trailer +castle +gardens +missed +malaysia +antique +labels +willing +acting +heads +stored +logos +antiques +density +hundred +strange +mention +parallel +honda +amended +operate +bills +bathroom +stable +opera +doctors +lesson +cinema +asset +drinking +reaction +blank +enhanced +entitled +severe +generate +deluxe +humor +monitors +lived +duration +pursuant +fabric +visits +tight +domains +contrast +flying +berlin +siemens +adoption +meant +capture +pounds +buffalo +plane +desire +camping +meets +welfare +caught +marked +driven +measured +medline +bottle +marshall +massage +rubber +closing +tampa +thousand +legend +grace +susan +adams +python +monster +villa +columns +hamilton +cookies +inner +tutorial +entity +cruises +holder +portugal +lawrence +roman +duties +valuable +ethics +forever +dragon +captain +imagine +brings +heating +scripts +stereo +taste +dealing +commit +airlines +liberal +livecam +trips +sides +turns +cache +jacket +oracle +matthew +lease +aviation +hobbies +proud +excess +disaster +console +commands +giant +achieved +injuries +shipped +seats +alarm +voltage +anthony +nintendo +usual +loading +stamps +appeared +franklin +angle +vinyl +mining +ongoing +worst +imaging +betting +liberty +wyoming +convert +analyst +garage +exciting +thongs +ringtone +finland +morgan +derived +pleasure +honor +oriented +eagle +desktops +pants +columbus +nurse +prayer +quiet +postage +producer +cheese +comic +crown +maker +crack +picks +semester +fetish +applies +casinos +smoke +apache +filters +craft +apart +fellow +blind +lounge +coins +gross +strongly +hilton +proteins +horror +familiar +capable +douglas +debian +epson +elected +carrying +victory +madison +editions +mainly +ethnic +actor +finds +fifth +citizen +vertical +prize +occurs +absolute +consists +anytime +soldiers +guardian +lecture +layout +classics +horses +dirty +wayne +donate +taught +worker +alive +temple +prove +wings +breaks +genetic +waters +promise +prefer +ridge +cabinet +modem +harris +bringing +evaluate +tiffany +tropical +collect +toyota +streets +vector +shaved +turning +buffer +purple +larry +mutual +pipeline +syntax +prison +skill +chairs +everyday +moves +inquiry +ethernet +checked +exhibit +throw +trend +sierra +visible +desert +oldest +rhode +mercury +steven +handbook +navigate +worse +summit +victims +spaces +burning +escape +coupons +somewhat +receiver +cialis +boats +glance +scottish +arcade +richmond +russell +tells +obvious +fiber +graph +covering +platinum +judgment +bedrooms +talks +filing +foster +modeling +passing +awarded +trials +tissue +clinton +masters +bonds +alberta +commons +fraud +spectrum +arrival +pottery +emphasis +roger +aspect +awesome +mexican +counts +priced +crash +desired +inter +closer +assumes +heights +shadow +riding +firefox +expense +grove +venture +clinic +korean +healing +princess +entering +packet +spray +studios +buttons +funded +thompson +winners +extend +roads +dublin +rolling +memories +nelson +arrived +creates +faces +tourist +mayor +murder +adequate +senator +yield +grades +cartoons +digest +lodging +hence +entirely +replaced +radar +rescue +losses +combat +reducing +stopped +lakes +closely +diary +kings +shooting +flags +baker +launched +shock +walls +abroad +ebony +drawn +arthur +visited +walker +suggests +beast +operated +targets +overseas +dodge +counsel +pizza +invited +yards +gordon +farmers +queries +ukraine +absence +nearest +cluster +vendors +whereas +serves +woods +surprise +partial +shoppers +couples +ranking +jokes +simpson +twiki +sublime +palace +verify +globe +trusted +copper +dicke +kerry +receipt +supposed +ordinary +nobody +ghost +applying +pride +knowing +reporter +keith +champion +cloudy +linda +chile +plenty +sentence +throat +ignore +maria +uniform +wealth +vacuum +dancing +brass +writes +plaza +outcomes +survival +quest +publish +trans +jonathan +whenever +lifetime +pioneer +booty +acrobat +plates +acres +venue +athletic +thermal +essays +vital +telling +fairly +coastal +config +charity +excel +modes +campbell +stupid +harbor +hungary +traveler +segment +realize +enemy +puzzle +rising +aluminum +wells +wishlist +opens +insight +secrets +lucky +latter +thick +trailers +repeat +syndrome +philips +penalty +glasses +enables +iraqi +builder +vista +jessica +chips +terry +flood +arena +pupils +stewart +outcome +expanded +casual +grown +polish +lovely +extras +centres +jerry +clause +smile +lands +troops +indoor +bulgaria +armed +broker +charger +believed +cooling +trucks +divorce +laura +shopper +tokyo +partly +nikon +candy +pills +tiger +donald +folks +sensor +exposed +telecom +angels +deputy +sealed +loaded +scenes +boost +spanking +founded +chronic +icons +moral +catering +finger +keeps +pound +locate +trained +roses +bread +tobacco +wooden +motors +tough +roberts +incident +gonna +dynamics +decrease +chest +pension +billy +revenues +emerging +worship +craig +herself +churches +damages +reserves +solve +shorts +minority +diverse +johnny +recorder +facing +nancy +tones +passion +sight +defence +patches +refund +towns +trembl +divided +emails +cyprus +insider +seminars +makers +hearts +worry +carter +legacy +pleased +danger +vitamin +widely +phrase +genuine +raising +paradise +hybrid +reads +roles +glory +bigger +billing +diesel +versus +combine +exceed +saudi +fault +babies +karen +compiled +romantic +revealed +albert +examine +jimmy +graham +bristol +margaret +compaq +slowly +rugby +portions +infant +sectors +samuel +fluid +grounds +regards +unlike +equation +baskets +wright +barry +proven +cached +warren +studied +reviewer +involves +profits +devil +grass +comply +marie +florist +cherry +deutsch +kenya +webcam +funeral +nutten +earrings +enjoyed +chapters +charlie +quebec +dennis +francis +sized +manga +noticed +socket +silent +literary +signals +theft +swing +symbols +humans +analog +facial +choosing +talent +dated +seeker +wisdom +shoot +boundary +packard +offset +payday +philip +elite +holders +believes +swedish +poems +deadline +robot +witness +collins +equipped +stages +winds +powder +broadway +acquired +assess +stones +entrance +gnome +roots +losing +attempts +gadgets +noble +glasgow +impacts +gospel +shore +loves +induced +knight +loose +linking +appeals +earned +illness +islamic +pending +parker +lebanon +kennedy +teenage +triple +cooper +vincent +secured +unusual +answered +slots +disorder +routine +toolbar +rocks +titans +wearing +sought +genes +mounted +habitat +firewall +median +scanner +herein +animated +judicial +integer +bachelor +attitude +engaged +falling +basics +montreal +carpet +struct +lenses +binary +genetics +attended +dropped +walter +besides +hosts +moments +atlas +strings +feels +torture +deleted +mitchell +ralph +warner +embedded +inkjet +wizard +corps +actors +liver +liable +brochure +morris +petition +eminem +recall +antenna +picked +assumed +belief +killing +bikini +memphis +shoulder +decor +lookup +texts +harvard +brokers +diameter +ottawa +podcast +seasons +refine +bidder +singer +evans +herald +literacy +fails +aging +plugin +diving +invite +alice +latinas +suppose +involve +moderate +terror +younger +thirty +opposite +rapidly +dealtime +intro +mercedes +clerk +mills +outline +tramadol +holland +receives +jeans +fonts +refers +favor +veterans +sigma +xhtml +occasion +victim +demands +sleeping +careful +arrive +sunset +tracked +moreover +minimal +lottery +framed +aside +licence +michelle +essay +dialogue +camps +declared +aaron +handheld +trace +disposal +florists +packs +switches +romania +consult +greatly +blogging +cycling +midnight +commonly +inform +turkish +pentium +quantum +murray +intent +largely +pleasant +announce +spoke +arrow +sampling +rough +weird +inspired +holes +weddings +blade +suddenly +oxygen +cookie +meals +canyon +meters +merely +passes +pointer +stretch +durham +permits +muslim +sleeve +netscape +cleaner +cricket +feeding +stroke +township +rankings +robin +robinson +strap +sharon +crowd +olympic +remained +entities +customs +rainbow +roulette +decline +gloves +israeli +medicare +skiing +cloud +valve +hewlett +explains +proceed +flickr +feelings +knife +jamaica +shelf +timing +liked +adopt +denied +fotos +britney +freeware +donation +outer +deaths +rivers +tales +katrina +islam +nodes +thumbs +seeds +cited +targeted +skype +realized +twelve +founder +decade +gamecube +dispute +tired +titten +adverse +excerpt +steam +drinks +voices +acute +climbing +stood +perfume +carol +honest +albany +restore +stack +somebody +curve +creator +amber +museums +coding +tracker +passage +trunk +hiking +pierre +jelsoft +headset +oakland +colombia +waves +camel +lamps +suicide +archived +arabia +juice +chase +logical +sauce +extract +panama +payable +courtesy +athens +judges +retired +remarks +detected +decades +walked +arising +nissan +bracelet +juvenile +afraid +acoustic +railway +cassette +pointed +causing +mistake +norton +locked +fusion +mineral +steering +beads +fortune +canvas +parish +claimed +screens +cemetery +planner +croatia +flows +stadium +fewer +coupon +nurses +proxy +lanka +edwards +contests +costume +tagged +berkeley +voted +killer +bikes +gates +adjusted +bishop +pulled +shaped +seasonal +farmer +counters +slave +cultures +norfolk +coaching +examined +encoding +heroes +painted +lycos +zdnet +artwork +cosmetic +resulted +portrait +ethical +carriers +mobility +floral +builders +struggle +schemes +neutral +fisher +spears +bedding +joining +heading +equally +bearing +combo +seniors +worlds +guilty +haven +tablet +charm +violent +basin +ranch +crossing +cottage +drunk +crimes +resolved +mozilla +toner +latex +branches +anymore +delhi +holdings +alien +locator +broke +nepal +zimbabwe +browsing +resolve +melissa +moscow +thesis +nylon +discs +rocky +bargains +frequent +nigeria +ceiling +pixels +ensuring +hispanic +anybody +diamonds +fleet +untitled +bunch +totals +marriott +singing +afford +starring +referral +optimal +distinct +turner +sucking +cents +reuters +spoken +omega +stayed +civic +manuals +watched +saver +thereof +grill +redeem +rogers +grain +regime +wanna +wishes +depend +differ +ranging +monica +repairs +breath +candle +hanging +colored +verified +formerly +situated +seeks +herbal +loving +strictly +routing +stanley +retailer +vitamins +elegant +gains +renewal +opposed +deemed +scoring +brooklyn +sisters +critics +spots +hacker +madrid +margin +solely +salon +norman +turbo +headed +voters +madonna +murphy +thinks +thats +soldier +phillips +aimed +justin +interval +mirrors +tricks +reset +brush +expansys +panels +repeated +assault +spare +kodak +tongue +bowling +danish +monkey +filename +skirt +florence +invest +honey +analyzes +drawings +scenario +lovers +atomic +approx +arabic +gauge +junction +faced +rachel +solving +weekends +produces +chains +kingston +sixth +engage +deviant +quoted +adapters +farms +imports +cheat +bronze +sandy +suspect +macro +sender +crucial +adjacent +tuition +spouse +exotic +viewer +signup +threats +puzzles +reaching +damaged +receptor +laugh +surgical +destroy +citation +pitch +autos +premises +perry +proved +imperial +dozen +benjamin +teeth +cloth +studying +stamp +lotus +salmon +olympus +cargo +salem +starter +upgrades +likes +butter +pepper +weapon +luggage +burden +tapes +zones +races +stylish +maple +grocery +offshore +depot +kenneth +blend +harrison +julie +emission +finest +realty +janet +apparent +phpbb +autumn +probe +toilet +ranked +jackets +routes +packed +excited +outreach +helen +mounting +recover +lopez +balanced +timely +talked +debug +delayed +chuck +explicit +villas +ebook +exclude +peeing +brooks +newton +anxiety +bingo +whilst +spatial +ceramic +prompt +precious +minds +annually +scanners +xanax +fingers +sunny +ebooks +delivers +necklace +leeds +cedar +arranged +theaters +advocacy +raleigh +threaded +qualify +blair +hopes +mason +diagram +burns +pumps +footwear +beijing +peoples +victor +mario +attach +licenses +utils +removing +advised +spider +ranges +pairs +trails +hudson +isolated +calgary +interim +assisted +divine +approve +chose +compound +abortion +dialog +venues +blast +wellness +calcium +newport +indians +shield +harvest +membrane +prague +previews +locally +pickup +mothers +nascar +iceland +candles +sailing +sacred +morocco +chrome +tommy +refused +brake +exterior +greeting +ecology +oliver +congo +botswana +delays +olive +cyber +verizon +scored +clone +velocity +lambda +relay +composed +tears +oasis +baseline +angry +silicon +compete +lover +belong +honolulu +beatles +rolls +thomson +barnes +malta +daddy +ferry +rabbit +seating +exports +omaha +electron +loads +heather +passport +motel +unions +treasury +warrant +solaris +frozen +occupied +royalty +scales +rally +observer +sunshine +strain +ceremony +somehow +arrested +yamaha +hebrew +gained +dying +laundry +stuck +solomon +placing +stops +homework +adjust +assessed +enabling +filling +imposed +silence +focuses +soviet +treaty +vocal +trainer +organ +stronger +volumes +advances +lemon +toxic +darkness +bizrate +vienna +implied +stanford +packing +statute +rejected +satisfy +shelter +chapel +gamespot +layers +guided +bahamas +powell +mixture +bench +rider +radius +logging +hampton +borders +butts +bobby +sheep +railroad +lectures +wines +nursery +harder +cheapest +travesti +stuart +salvador +salad +monroe +tender +paste +clouds +tanzania +preserve +unsigned +staying +easter +theories +praise +jeremy +venice +estonia +veteran +streams +landing +signing +executed +katie +showcase +integral +relax +namibia +synopsis +hardly +prairie +reunion +composer +sword +absent +sells +ecuador +hoping +accessed +spirits +coral +pixel +float +colin +imported +paths +bubble +acquire +contrary +tribune +vessel +acids +focusing +viruses +cheaper +admitted +dairy +admit +fancy +equality +samoa +stickers +leasing +lauren +beliefs +squad +analyze +ashley +scroll +relate +wages +suffer +forests +invalid +concerts +martial +males +retain +execute +tunnel +genres +cambodia +patents +chaos +wheat +beaver +updating +readings +kijiji +confused +compiler +eagles +bases +accused +unity +bride +defines +airports +begun +brunette +packets +anchor +socks +parade +trigger +gathered +essex +slovenia +notified +beaches +folders +dramatic +surfaces +terrible +routers +pendant +dresses +baptist +hiring +clocks +females +wallace +reflects +taxation +fever +cuisine +surely +myspace +theorem +stylus +drums +arnold +chicks +cattle +radical +rover +treasure +reload +flame +levitra +tanks +assuming +monetary +elderly +floating +bolivia +spell +hottest +stevens +kuwait +emily +alleged +compile +webster +struck +plymouth +warnings +bridal +annex +tribal +curious +freight +rebate +meetup +eclipse +sudan +shuttle +stunning +cycles +affects +detect +actively +ampland +fastest +butler +injured +payroll +cookbook +courier +uploaded +hints +collapse +americas +unlikely +techno +beverage +tribute +wired +elvis +immune +latvia +forestry +barriers +rarely +infected +martha +genesis +barrier +argue +trains +metals +bicycle +letting +arise +celtic +thereby +jamie +particle +minerals +advise +humidity +bottles +boxing +bangkok +hughes +jeffrey +chess +operates +brisbane +survive +oscar +menus +reveal +canal +amino +herbs +clinics +manitoba +missions +watson +lying +costumes +strict +saddam +drill +offense +bryan +protest +hobby +tries +nickname +inline +washing +staffing +trick +enquiry +closure +timber +intense +playlist +showers +ruling +steady +statutes +myers +drops +wider +plugins +enrolled +sensors +screw +publicly +hourly +blame +geneva +freebsd +reseller +handed +suffered +intake +informal +tucson +heavily +swingers +fifty +headers +mistakes +uncle +defining +counting +assure +devoted +jacob +sodium +randy +hormone +timothy +brick +naval +medieval +bridges +captured +thehun +decent +casting +dayton +shortly +cameron +carlos +donna +andreas +warrior +diploma +cabin +innocent +scanning +valium +copying +cordless +patricia +eddie +uganda +fired +trivia +adidas +perth +grammar +syria +disagree +klein +harvey +tires +hazard +retro +gregory +episodes +boolean +circular +anger +mainland +suits +chances +interact +bizarre +glenn +auckland +olympics +fruits +ribbon +startup +suzuki +trinidad +kissing +handy +exempt +crops +reduces +geometry +slovakia +guild +gorgeous +capitol +dishes +barbados +chrysler +nervous +refuse +extends +mcdonald +replica +plumbing +brussels +tribe +trades +superb +trinity +handled +legends +floors +exhaust +shanghai +speaks +burton +davidson +copied +scotia +farming +gibson +roller +batch +organize +alter +nicole +latino +ghana +edges +mixing +handles +skilled +fitted +harmony +asthma +twins +triangle +amend +oriental +reward +windsor +zambia +hydrogen +webshots +sprint +chick +advocate +inputs +genome +escorts +thong +medal +coaches +vessels +walks +knives +arrange +artistic +honors +booth +indie +unified +bones +breed +detector +ignored +polar +fallen +precise +sussex +msgid +invoice +gather +backed +alfred +colonial +carey +motels +forming +embassy +danny +rebecca +slight +proceeds +indirect +amongst +msgstr +arrest +adipex +horizon +deeply +toolbox +marina +prizes +bosnia +browsers +patio +surfing +lloyd +optics +pursue +overcome +attract +brighton +beans +ellis +disable +snake +succeed +leonard +lending +reminder +searched +plains +raymond +insights +sullivan +midwest +karaoke +lonely +hereby +observe +julia +berry +collar +racial +bermuda +amanda +mobiles +kelkoo +exhibits +terrace +bacteria +replied +seafood +novels +ought +safely +finite +kidney +fixes +sends +durable +mazda +allied +throws +moisture +roster +symantec +spencer +wichita +nasdaq +uruguay +timer +tablets +tuning +gotten +tyler +futures +verse +highs +wanting +custody +scratch +launches +ellen +rocket +bullet +towers +racks +nasty +latitude +tumor +deposits +beverly +mistress +trustees +watts +duncan +reprints +bernard +forty +tubes +midlands +priest +floyd +ronald +analysts +queue +trance +locale +nicholas +bundle +hammer +invasion +runner +notion +skins +mailed +fujitsu +spelling +arctic +exams +rewards +beneath +defend +medicaid +infrared +seventh +welsh +belly +quarters +stolen +soonest +haiti +naturals +lenders +fitting +fixtures +bloggers +agrees +surplus +elder +sonic +cheers +belarus +zoning +gravity +thumb +guitars +essence +flooring +ethiopia +mighty +athletes +humanity +holmes +scholars +galaxy +chester +snapshot +caring +segments +dominant +twist +itunes +stomach +buried +newbie +minimize +darwin +ranks +debut +bradley +anatomy +fraction +defects +milton +marker +clarity +sandra +adelaide +monaco +settled +folding +emirates +airfare +vaccine +belize +promised +volvo +penny +robust +bookings +minolta +porter +jungle +ivory +alpine +andale +fabulous +remix +alias +newer +spice +implies +cooler +maritime +periodic +overhead +ascii +prospect +shipment +breeding +donor +tension +trash +shapes +manor +envelope +diane +homeland +excluded +andrea +breeds +rapids +disco +bailey +endif +emotions +incoming +lexmark +cleaners +eternal +cashiers +rotation +eugene +metric +minus +bennett +hotmail +joshua +armenia +varied +grande +closest +actress +assign +tigers +aurora +slides +milan +premiere +lender +villages +shade +chorus +rhythm +digit +argued +dietary +symphony +clarke +sudden +marilyn +lions +findlaw +pools +lyric +claire +speeds +matched +carroll +rational +fighters +chambers +warming +vocals +fountain +chubby +grave +burner +finnish +gentle +deeper +muslims +footage +howto +worthy +reveals +saints +carries +devon +helena +saves +regarded +marion +lobby +egyptian +tunisia +outlined +headline +treating +punch +gotta +cowboy +bahrain +enormous +karma +consist +betty +queens +lucas +tribes +defeat +clicks +honduras +naughty +hazards +insured +harper +mardi +tenant +cabinets +tattoo +shake +algebra +shadows +holly +silly +mercy +hartford +freely +marcus +sunrise +wrapping +weblogs +timeline +belongs +readily +fence +nudist +infinite +diana +ensures +lindsay +legally +shame +civilian +fatal +remedy +realtors +briefly +genius +fighter +flesh +retreat +adapted +barely +wherever +estates +democrat +borough +failing +retained +pamela +andrews +marble +jesse +logitech +surrey +briefing +belkin +highland +modular +brandon +giants +balloon +winston +solved +hawaiian +gratuit +consoles +qatar +magnet +porsche +cayman +jaguar +sheer +posing +hopkins +urgent +infants +gothic +cylinder +witch +cohen +puppy +kathy +graphs +surround +revenge +expires +enemies +finances +accepts +enjoying +patrol +smell +italiano +carnival +roughly +sticker +promises +divide +cornell +satin +deserve +mailto +promo +worried +tunes +garbage +combines +bradford +phrases +chelsea +boring +reynolds +speeches +reaches +schema +catalogs +quizzes +prefix +lucia +savannah +barrel +typing +nerve +planets +deficit +boulder +pointing +renew +coupled +myanmar +metadata +harold +circuits +floppy +texture +handbags +somerset +incurred +antigua +thunder +caution +locks +namely +euros +pirates +aerial +rebel +origins +hired +makeup +textile +nathan +tobago +indexes +hindu +licking +markers +weights +albania +lasting +wicked +kills +roommate +webcams +pushed +slope +reggae +failures +surname +theology +nails +evident +whats +rides +rehab +saturn +allergy +twisted +merit +enzyme +zshops +planes +edmonton +tackle +disks +condo +pokemon +ambien +retrieve +vernon +worldcat +titanium +fairy +builds +shaft +leslie +casio +deutsche +postings +kitty +drain +monte +fires +algeria +blessed +cardiff +cornwall +favors +potato +panic +sticks +leone +excuse +reforms +basement +onion +strand +sandwich +lawsuit +cheque +banners +reject +circles +italic +beats +merry +scuba +passive +valued +courage +verde +gazette +hitachi +batman +hearings +coleman +anaheim +textbook +dried +luther +frontier +settle +stopping +refugees +knights +palmer +derby +peaceful +altered +pontiac +doctrine +scenic +trainers +sewing +conclude +munich +celebs +propose +lighter +advisors +pavilion +tactics +trusts +talented +annie +pillow +derek +shorter +harley +relying +finals +paraguay +steal +parcel +refined +fifteen +fears +predict +boutique +acrylic +rolled +tuner +peterson +shannon +toddler +flavor +alike +homeless +horrible +hungry +metallic +blocked +warriors +cadillac +malawi +sagem +curtis +parental +strikes +lesser +marathon +pressing +gasoline +dressed +scout +belfast +dealt +niagara +warcraft +charms +catalyst +trader +bucks +denial +thrown +prepaid +raises +electro +badge +wrist +analyzed +heath +ballot +lexus +varying +remedies +validity +trustee +weighted +angola +performs +plastics +realm +jenny +helmet +salaries +postcard +elephant +yemen +tsunami +scholar +nickel +buses +expedia +geology +coating +wallet +cleared +smilies +boating +drainage +shakira +corners +broader +rouge +yeast +clearing +coated +intend +louise +kenny +routines +hitting +yukon +beings +aquatic +reliance +habits +striking +podcasts +singh +gilbert +ferrari +brook +outputs +ensemble +insulin +assured +biblical +accent +mysimon +eleven +wives +ambient +utilize +mileage +prostate +adaptor +auburn +unlock +hyundai +pledge +vampire +angela +relates +nitrogen +xerox +merger +softball +firewire +nextel +framing +musician +blocking +rwanda +sorts +vsnet +limiting +dispatch +papua +restored +armor +riders +chargers +remark +dozens +varies +rendered +picking +guards +openings +councils +kruger +pockets +granny +viral +inquire +pipes +laden +aruba +cottages +realtor +merge +edgar +develops +chassis +dubai +pushing +fleece +pierce +allan +dressing +sperm +filme +craps +frost +sally +yacht +tracy +prefers +drilling +breach +whale +tomatoes +bedford +mustang +clusters +antibody +momentum +wiring +pastor +calvin +shark +phases +grateful +emerald +laughing +grows +cliff +tract +ballet +abraham +bumper +webpage +garlic +hostels +shine +senegal +banned +wendy +briefs +diffs +mumbai +ozone +radios +tariff +nvidia +opponent +pasta +muscles +serum +wrapped +swift +runtime +inbox +focal +distant +decimal +propecia +samba +hostel +employ +mongolia +penguin +magical +miracle +manually +reprint +centered +yearly +wound +belle +writings +hamburg +cindy +fathers +charging +marvel +lined +petite +terrain +strips +gossip +rangers +rotary +discrete +beginner +boxed +cubic +sapphire +kinase +skirts +crawford +labeled +marking +serbia +sheriff +griffin +declined +guyana +spies +neighbor +elect +highways +thinkpad +intimate +preston +deadly +bunny +chevy +rounds +longest +tions +dentists +flyer +dosage +variance +cameroon +baking +adaptive +computed +needle +baths +brakes +nirvana +invision +sticky +destiny +generous +madness +emacs +climb +blowing +heated +jackie +sparc +cardiac +dover +adrian +vatican +brutal +learners +token +seekers +yields +suited +numeric +skating +kinda +aberdeen +emperor +dylan +belts +blacks +educated +rebates +burke +proudly +inserted +pulling +basename +obesity +curves +suburban +touring +clara +vertex +tomato +andorra +expired +travels +flush +waiver +hayes +delight +survivor +garcia +cingular +moses +counted +declare +johns +valves +impaired +donors +jewel +teddy +teaches +ventures +bufing +stranger +tragedy +julian +dryer +painful +velvet +tribunal +ruled +pensions +prayers +funky +nowhere +joins +wesley +lately +scary +mattress +mpegs +brunei +likewise +banana +slovak +cakes +mixer +remind +sbjct +charming +tooth +annoying +stays +disclose +affair +drove +washer +upset +restrict +springer +beside +mines +rebound +logan +mentor +fought +baghdad +metres +pencil +freeze +titled +sphere +ratios +concord +endorsed +walnut +lance +ladder +italia +liberia +sherman +maximize +hansen +senators +workout +bleeding +colon +lanes +purse +optimize +stating +caroline +align +bless +engaging +crest +triumph +welding +deferred +alloy +condos +plots +polished +gently +tulsa +locking +casey +draws +fridge +blanket +bloom +simpsons +elliott +fraser +justify +blades +loops +surge +trauma +tahoe +advert +possess +flashers +subaru +vanilla +picnic +souls +arrivals +spank +hollow +vault +securely +fioricet +groove +pursuit +wires +mails +backing +sleeps +blake +travis +endless +figured +orbit +niger +bacon +heater +colony +cannon +circus +promoted +forbes +moldova +paxil +spine +trout +enclosed +cooked +thriller +transmit +apnic +fatty +gerald +pressed +scanned +hunger +mariah +joyce +surgeon +cement +planners +disputes +textiles +missile +intranet +closes +deborah +marco +assists +gabriel +auditor +aquarium +violin +prophet +bracket +isaac +oxide +naples +promptly +modems +harmful +prozac +sexually +dividend +newark +glucose +phantom +playback +turtle +warned +neural +fossil +hometown +badly +apollo +persian +handmade +greene +robots +grenada +scoop +earning +mailman +sanyo +nested +somalia +movers +verbal +blink +carlo +workflow +novelty +bryant +tiles +voyuer +switched +tamil +garmin +fuzzy +grams +richards +budgets +toolkit +render +carmen +hardwood +erotica +temporal +forge +dense +brave +awful +airplane +istanbul +impose +viewers +asbestos +meyer +enters +savage +willow +resumes +throwing +existed +wagon +barbie +knock +potatoes +thorough +peers +roland +optimum +quilt +creature +mounts +syracuse +refresh +webcast +michel +subtle +notre +maldives +stripes +firmware +shepherd +canberra +cradle +mambo +flour +sympathy +choir +avoiding +blond +expects +jumping +fabrics +polymer +hygiene +poultry +virtue +burst +surgeons +bouquet +promotes +mandate +wiley +corpus +johnston +fibre +shades +indices +adware +zoloft +prisoner +daisy +halifax +ultram +cursor +earliest +donated +stuffed +insects +crude +morrison +maiden +examines +viking +myrtle +bored +cleanup +bother +budapest +knitting +attacked +bhutan +mating +compute +redhead +arrives +tractor +allah +unwrap +fares +resist +hoped +safer +wagner +touched +cologne +wishing +ranger +smallest +newman +marsh +ricky +scared +theta +monsters +asylum +lightbox +robbie +stake +cocktail +outlets +arbor +poison