Commit Diff


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 =~ /<User (.*)>/) {
+		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 <username> <email>");
+			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 <text>");
+		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 <username> <email> 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 <username> <email>. For example, !bnc john john\@example.com.
+To request a free shell account, type !shell <username> <email>. For example, !shell john john\@example.com.
+EOF
+#To request a free email account, type !email <username> <email>. For example, !email john john\@example.com.
+#To request a free VPN account, type !vpn <username> <email>. For example, !vpn john john\@example.com.
+	if (main::isstaff($bot, $nick)) {
+		$msg .= <<"EOF";
+To delete a bouncer, type !bnc delete <username>
+To verify a captcha, type !bnc captcha <username>
+To recreate cloneuser, type !bnc cloneuser
+To get a list of usernames that match IPs, type !regex ips <ips>
+To get a list of IPs that match usernames, type !regex users <usernames>
+To regex search znc.log and output to the terminal, type !regex <regex>
+To delete a shell account, type !shell delete <username>
+To verify a captcha, type !shell captcha <username>
+EOF
+#To get a list of usernames that match IPs, type !shell regex ips <ips>
+#To get a list of IPs that match usernames, type !shell regex users <usernames>
+#To regex search znc.log and output to the terminal, type !shell regex <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 <jrmu@ircnow.org>
+
+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 \<support\@ircnow.org\>
+#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 <table> <keys> <vals>
+# 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 <table> <idkey> <idval> <key> <val>
+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 <table> <key> <val>
+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 <table> <key> <val>
+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 <table> <idkey> <idval> <key>
+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 <table> <idkey> <idval> <key> <val>
+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 <username> <email>
+# !shell captcha <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 <username> <email>");
+			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 <text>");
+		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 <username> <email> 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 <username>:<password>, or /quote PASS <username>/<network>:<password> 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 @@
+<?php 
+
+$hash = explode("/", $_SERVER["REQUEST_URI"])[1];
+$fpr{"hash"} = $hash;
+$fpr{"remoteaddr"} = $_SERVER['REMOTE_ADDR'];
+$fpr{"httpxforwarded"} = $_SERVER['HTTP_X_FORWARDED_FOR'];
+$fpr{"time"} = date("Y-m-d H:i:s");
+foreach (getallheaders() as $key => $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 =<<<EOF
+INSERT INTO www ($keystr)
+VALUES ($valstr);
+EOF;
+	
+	if (!$wwwdb->exec($sql)) {
+		echo $db->lastErrorMsg();
+		return;
+	}
+
+	$sql =<<<EOF
+SELECT * from bnc;
+EOF;
+	$ret = $wwwdb->query($sql);
+	while($row = $ret->fetchArray(SQLITE3_ASSOC)) {
+//		echo "hash: $hash, row['hashid']: ".$row['hashid']."<br>\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 @@
+<?php
+
+$hashirc = isset($_REQUEST['hashirc']) ? $_REQUEST['hashirc'] : NULL;
+$hashbnc = isset($_REQUEST['hashbnc']) ? $_REQUEST['hashbnc'] : NULL;
+$hashshell = isset($_REQUEST['hashshell']) ? $_REQUEST['hashshell'] : NULL;
+$hashsmtp = isset($_REQUEST['hashsmtp']) ? $_REQUEST['hashsmtp'] : NULL;
+$lines = file('botnow.conf');
+
+foreach ($lines as $i => $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 =<<<EOF
+INSERT INTO www ($keystr)
+VALUES ($valstr);
+EOF;
+	if (!$wwwdb->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 =<<<EOF
+SELECT * from irc where hashid = "$hashirc";
+EOF;
+	$ret = $wwwdb->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 =<<<EOF
+SELECT * from bnc where ircid = "$ircid";
+EOF;
+	$ret = $wwwdb->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"};
+?>
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<meta name="robots" content="index, follow"/>
+	<title>IRCNow -- Verify Account</title>  
+<style>
+body {
+	width: 80%;
+	margin: 5rem auto;
+	text-align: center;
+	font-family: "Tahoma", "Verdana", "Arial";
+}
+</style>
+<script>
+console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);
+var offset = new Date().getTimezoneOffset();
+console.log(offset);
+console.log(new Date().toString());
+console.log(window.screen.width);
+console.log(window.screen.height);
+//console.log(screen.availWidth);
+//console.log(screen.availHeight);
+console.log(screen.colorDepth);
+</script>
+</head>
+
+<body>
+<p>Please email <a href="mailto:<?php echo $conf{"mailfrom"}."?subject=".myURLEncode("IRCNow Verify: $passphrase")."&body=".myURLEncode("IRCNow Verify: $passphrase") ?>"><?php echo $conf{"mailfrom"} ?></a> with the following passphrase:</p>
+
+<p>IRCNow Verify: <?php echo $passphrase ?></p>
+
+<p><a href="https://wiki.ircnow.org/index.php?n=Bouncer.<?php echo $client ?>">Click here for <?php echo $client ?> connection instructions.</a></p>
+
+<p>To change your password, login to <a href="<?php echo "https://bnc.$hostname/" ?>"><?php echo "https://bnc.$hostname/" ?></a>, fill in the password field, then click on the "Save and Return" button.</p>
+
+<p>Or, you can change the password from your IRC client. Once you have logged in, type:</p>
+
+<code>/msg *controlpanel set password <?php echo $username ?> newpassword</code>
+
+</body>
+</html>
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