Blob


1 #!/usr/bin/perl
3 package SQLite;
5 use strict;
6 use warnings;
7 use OpenBSD::Pledge;
8 use OpenBSD::Unveil;
9 use Data::Dumper;
10 use DBI;
11 use DBD::SQLite;
13 use constant {
14 NONE => 0,
15 ERRORS => 1,
16 WARNINGS => 2,
17 ALL => 3,
18 };
19 my %conf = %main::conf;
20 my $staff = $conf{staff};
21 our $dbh;
22 my $verbose = $conf{verbose};
23 my $dbpath = "/var/www/botnow/botnow.db";
24 my $database = "/var/www/botnow/"; # database path
25 main::cbind("msg", "-", "get", \&mget);
26 main::cbind("msg", "-", "set", \&mset);
27 main::cbind("msg", "-", "connectdb", \&mconnectdb);
28 main::cbind("msg", "-", "insert", \&minsert);
29 main::cbind("msg", "-", "update", \&mupdate);
30 main::cbind("msg", "-", "delete", \&mdelete);
31 main::cbind("msg", "-", "select", \&mselect);
33 sub init {
34 unveil("$dbpath", "rwc") or die "Unable to unveil $!";
35 unveil("$dbpath-journal", "rwc") or die "Unable to unveil $!";
36 unveil("$database", "rwxc") or die "Unable to unveil $!";
37 }
39 # !connectdb
40 sub mconnectdb {
41 my ($bot, $nick, $host, $hand, $text) = @_;
42 if (! (main::isstaff($bot, $nick))) { return; }
43 if (connectdb()) {
44 main::putserv($bot, "PRIVMSG $nick :connectdb succeeded");
45 } else {
46 main::putserv($bot, "PRIVMSG $nick :ERROR: connectdb failed");
47 }
48 }
50 # !insert <table> <keys> <vals>
51 # Insert comma-separated keys and vals into table
52 sub minsert {
53 my ($bot, $nick, $host, $hand, $text) = @_;
54 if (! (main::isstaff($bot, $nick))) { return; }
55 if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+([[:ascii:]]+)/) {
56 my ($table, $keys, $vals) = ($1, $2, $3);
57 # strings in the values must be quoted
58 if ($vals =~ s{,}{","}g) { $vals = '"'.$vals.'"'; }
59 if (insertrow($table, $keys, $vals)) {
60 main::putserv($bot, "PRIVMSG $nick :$table ($keys) => ($vals)");
61 } else {
62 main::putserv($bot, "PRIVMSG $nick :$table insert failed");
63 }
64 } else {
65 main::putserv($bot, "PRIVMSG $nick :invalid insert");
66 }
67 }
69 # Set key = val where idkey = idval in table
70 # !update <table> <idkey> <idval> <key> <val>
71 sub mupdate {
72 my ($bot, $nick, $host, $hand, $text) = @_;
73 if (! (main::isstaff($bot, $nick))) { return; }
74 if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+(\S+)\s+([-_[:alnum:]]+)\s+(\S+)/) {
75 my ($table, $idkey, $idval, $key, $val) = ($1, $2, $3, $4, $5);
76 if (updaterow($table, $idkey, $idval, $key, $val)) {
77 main::putserv($bot, "PRIVMSG $nick :$table $key => $val where $idkey = $idval");
78 } else {
79 main::putserv($bot, "PRIVMSG $nick :update failed");
80 }
81 } else {
82 main::putserv($bot, "PRIVMSG $nick :invalid update");
83 }
84 }
86 # Delete rows where key = val in table
87 # !delete <table> <key> <val>
88 sub mdelete {
89 my ($bot, $nick, $host, $hand, $text) = @_;
90 if (! (main::isstaff($bot, $nick))) { return; }
91 if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_[:alnum:]]+)\s+(\S+)/) {
92 my ($table, $key, $val) = ($1, $2, $3);
93 if (deleterows($table, $key, $val)) {
94 main::putserv($bot, "PRIVMSG $nick :$table $key = $val deleted");
95 } else {
96 main::putserv($bot, "PRIVMSG $nick :delete failed");
97 }
98 } else {
99 main::putserv($bot, "PRIVMSG $nick :invalid delete");
103 # Output rows where key = val in table
104 # !select <table> <key> <val>
105 sub mselect {
106 my ($bot, $nick, $host, $hand, $text) = @_;
107 if (! (main::isstaff($bot, $nick))) { return; }
108 if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_[:alnum:]]+)\s+(\S+)/) {
109 my ($table, $key, $val) = ($1, $2, $3);
110 my @rows = selectrows($table, $key, $val);
111 if (@rows) {
112 foreach my $row (@rows) {
113 my @pairs;
114 foreach $key (keys %$row) {
115 my $val = $row->{$key} || "";
116 push(@pairs, "$key => $val");
118 main::putserv($bot, "PRIVMSG $nick :$table ".join(',', @pairs));
120 } else {
121 main::putserv($bot, "PRIVMSG $nick :no results");
123 } else {
124 main::putserv($bot, "PRIVMSG $nick :select invalid");
128 # Get value of key where idkey = idval in table
129 # !get <table> <idkey> <idval> <key>
130 sub mget {
131 my ($bot, $nick, $host, $hand, $text) = @_;
132 if (! (main::isstaff($bot, $nick))) { return; }
133 if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+(\S+)\s+([-_[:alnum:]]+)/) {
134 my ($table, $idkey, $idval, $key) = ($1, $2, $3, $4);
135 my $val = get($table, $idkey, $idval, $key);
136 if (defined($val)) {
137 main::putserv($bot, "PRIVMSG $nick :$table $key => $val where $idkey = $idval");
138 } else {
139 main::putserv($bot, "PRIVMSG $nick :undefined");
141 } else {
142 main::putserv($bot, "PRIVMSG $nick :invalid get");
145 # !set <table> <idkey> <idval> <key> <val>
146 sub mset {
147 my ($bot, $nick, $host, $hand, $text) = @_;
148 if (! (main::isstaff($bot, $nick))) { return; }
149 if ($text =~ /^([-_~@!,\.[:alnum:]]+)\s+([-_~@!,\.[:alnum:]]+)\s+(\S+)\s+([-_[:alnum:]]+)\s+(\S+)/) {
150 my ($table, $idkey, $idval, $key, $val) = ($1, $2, $3, $4, $5);
151 if (set($table, $idkey, $idval, $key, $val)) {
152 main::putserv($bot, "PRIVMSG $nick :$table $key => $val where $idkey = $idval");
153 } else {
154 main::putserv($bot, "PRIVMSG $nick :failed set");
156 } else {
157 main::putserv($bot, "PRIVMSG $nick :invalid set");
161 # Connect to database, creating table if necessary
162 # Returns true on success, false on failure
163 sub connectdb {
164 my $dsn = "dbi:SQLite:dbname=$dbpath";
165 my $user = "";
166 my $password = "";
167 $dbh = DBI->connect($dsn, $user, $password, {
168 PrintError => 1,
169 RaiseError => 1,
170 AutoCommit => 1,
171 FetchHashKeyName => 'NAME_lc',
172 }) or die "Couldn't connect to database: " . $DBI::errstr;
173 if (!(-s "$dbpath")) {
174 my $sql = main::readstr('table.sql');
175 my @sql = split /;/m, $sql;
176 foreach my $s (@sql) {
177 $dbh->do($s);
180 main::debug(ALL, "connected to $dbpath");
181 return defined($dbh);
184 # Inserts comma-separated keys and vals into table
185 # Returns number of rows successfully inserted
186 sub insertrow {
187 my ($table, $keys, $vals) = @_;
188 if (!defined($dbh)) { connectdb(); }
189 my $rows = $dbh->do("INSERT INTO $table ($keys) values ($vals)");
190 if ($rows) {
191 main::debug(ALL, "INSERT INTO $table ($keys) values ($vals)");
192 } else {
193 main::debug(ERRORS, "ERRORS: Failed INSERT INTO $table ($keys) values ($vals)");
195 return $rows;
198 # Update key, value pair for record where idkey equals idval in table
199 # Returns number of rows successfully updated
200 sub updaterow {
201 my ($table, $idkey, $idval, $key, $val) = @_;
202 if (!defined($dbh)) { connectdb(); }
203 my $rows = $dbh->do("UPDATE $table SET $key = ? where $idkey = ?", undef, $val, $idval);
204 if ($rows) {
205 main::debug(ALL, "UPDATE $table SET $key = $val where $idkey = $idval");
206 } else {
207 main::debug(ERRORS, "ERRORS: Failed UPDATE $table SET $key = $val where $idkey = $idval");
209 return $rows;
212 # Delete records from $table where $key = $val
213 # Returns number of rows deleted
214 sub deleterows {
215 my ($table, $key, $val) = @_;
216 if (!defined($dbh)) { connectdb(); }
217 my $rows = $dbh->do("DELETE FROM $table WHERE $key = ?", undef, $val);
218 if ($rows) {
219 main::debug(ALL, "DELETE FROM $table WHERE $key = $val");
220 } else {
221 main::debug(ERRORS, "ERRORS: Failed DELETE FROM $table WHERE $key = $val");
223 return $rows;
226 # Returns all records in the database
227 sub selectall {
228 my ($table) = @_;
229 if (!defined($dbh)) { connectdb(); }
230 my $sth = $dbh->prepare("SELECT * FROM $table");
231 $sth->execute();
232 my @results;
233 while (my $row = $sth->fetchrow_hashref) {
234 push(@results, $row);
236 return @results;
239 # Returns all records from table where key equals value
240 sub selectrows {
241 my ($table, $key, $val) = @_;
242 if (!defined($dbh)) { connectdb(); }
243 my $sth = $dbh->prepare("SELECT * FROM $table WHERE $key = ?");
244 $sth->execute($val);
245 my @results;
246 while (my $row = $sth->fetchrow_hashref) {
247 push(@results, $row);
249 return @results;
252 # Returns list of tables
253 sub tables {
254 # if (!defined($dbh)) { connectdb(); }
255 # my $sth = $dbh->prepare(".tables");
256 # $sth->execute($val);
257 # my @results;
258 # while (my $row = $sth->fetchrow_hashref) {
259 # push(@results, $row);
260 # }
261 # return @results;
262 return qw(bnc shell www irc smtp);
265 # Returns value of key in record in table where idkey = idval
266 sub get {
267 my ($table, $idkey, $idval, $key) = @_;
268 if (!defined($dbh)) { connectdb(); }
269 my $sth = $dbh->prepare("SELECT * FROM $table WHERE $idkey = ?");
270 $sth->execute($idval);
271 if (my $row = $sth->fetchrow_hashref) {
272 my $val = $row->{$key};
273 if (!defined($val)) { $val = "undefined"; }
274 main::debug(ALL, "get: $table $key => $val where $idkey = $idval");
275 return $row->{$key};
276 } else {
277 main::debug(ERRORS, "ERRORS: $table $key undefined where $idkey = $idval");
278 return;
282 # Sets value of key in the record in table where idkey = idval
283 # Returns true on success; false on failure
284 sub set {
285 my ($table, $idkey, $idval, $key, $val) = @_;
286 if (defined(get($table, $idkey, $idval, $idkey))) {
287 main::debug(ALL, "set: update");
288 return updaterow($table, $idkey, $idval, $key, $val) > 0;
289 } else {
290 main::debug(ALL, "set: insert");
291 return insertrow($table, "$idkey,$key", "\"$idval\",\"$val\"") > 0;
295 # given a key, val pair in table, return the id that falls within expires seconds
296 sub id {
297 my ($table, $key, $val, $expires) = @_;
298 my @rows = selectrows($table, $key, $val);
299 if (scalar(@rows) == 0) {
300 print "table => $table, key => $key, val => $val\n\n";
302 my $maxrow;
303 foreach my $row (@rows) {
304 if (!defined($maxrow)) { $maxrow = $row; }
305 if ($row->{localtime} > $maxrow->{localtime}) {
306 $maxrow = $row;
309 if (abs(time() - $maxrow->{localtime}) <= $expires) {
310 main::debug(ALL, "id: $maxrow->{id} where $key = $val at $expires");
311 return $maxrow->{id};
312 } else {
313 main::debug(ERRORS, "no id found");
314 return;
318 1; # MUST BE LAST STATEMENT IN FILE