commit e16eab02753afd1b01c8ded5191d1c49ef576b53
parent 3dea64f0aa027c2c354906a8d57b6a624bfea987
Author: kyle <kyle@0x30.net>
Date: Mon, 11 Jan 2016 21:44:01 -0700
sl: create logger and database objects
- logger object allows log printing, same as before
- except now called via $log->print() instead of log_print()
- also create a database object
- put db initialization code in constructor
- make create_tables() and prepare_stmt_handles() member functions
Diffstat:
M | server/sl | | | 238 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
1 file changed, 133 insertions(+), 105 deletions(-)
diff --git a/server/sl b/server/sl
@@ -8,12 +8,10 @@ use File::Temp;
use Digest::SHA qw(sha256_base64);
use Getopt::Std;
use IO::Socket::SSL;
-use POSIX;
use Scalar::Util qw(looks_like_number);
require "msgs.pl";
our (%msg_num, @msg_str, @msg_func, $protocol_ver);
-my $log_msg_type = '';
my %args;
getopts("p:t", \%args);
@@ -25,8 +23,11 @@ my $db_file = "db";
# EXLOCK needs to be 0 because SQLite expects it to be
$db_file = File::Temp->new(SUFFIX => '.db', EXLOCK => 0) if ($args{t});
-log_print_bare("creating new database '$db_file'\n") unless (-e $db_file);
-create_tables();
+my $log = logger->new();
+$log->print_bare("using database '$db_file'\n");
+
+my $db = database->new();
+$db->create_tables();
my $listen_sock = new IO::Socket::INET (
LocalHost => '0.0.0.0',
@@ -38,7 +39,7 @@ my $listen_sock = new IO::Socket::INET (
die "Could not create socket: $!\n" unless $listen_sock;
my ($addr, $port) = ($listen_sock->sockhost(), $listen_sock->sockport());
-log_print_bare("accepting connections on $addr:$port (pid = '$$')\n");
+$log->print_bare("accepting connections on $addr:$port (pid = '$$')\n");
# every time accept() returns we have a new client trying to connect
while (my $client_sock = $listen_sock->accept()) {
@@ -56,8 +57,8 @@ while (my $client_sock = $listen_sock->accept()) {
# in child: on linux we must stir the random pool after fork()'s
close $listen_sock;
arc4random_stir();
- log_set_peer_host_port($client_sock);
- log_print("new connection (pid = '$$')\n");
+ $log->set_peer_host_port($client_sock);
+ $log->print("new connection (pid = '$$')\n");
# unconditionally upgrade connection to SSL
my $ret = IO::Socket::SSL->start_SSL($client_sock,
@@ -66,29 +67,21 @@ while (my $client_sock = $listen_sock->accept()) {
SSL_key_file => 'ssl/privkey.pem'
);
unless ($ret) {
- log_print("error: $SSL_ERROR\n");
+ $log->print("error: $SSL_ERROR\n");
exit 0;
}
my $ssl_ver = $client_sock->get_sslversion();
my $ssl_cipher = $client_sock->get_cipher();
- log_print("ssl ok, ver = '$ssl_ver' cipher = '$ssl_cipher'\n");
-
- # open a new database connection
- my $dbh = DBI->connect(
- "dbi:SQLite:dbname=$db_file",
- "", "",
- { RaiseError => 1 }
- ) or die $DBI::errstr;
+ $log->print("ssl ok, ver = '$ssl_ver' cipher = '$ssl_cipher'\n");
- # foreign keys are off by default, autocommit is needed for transactions
- $dbh->do("PRAGMA foreign_keys = ON");
- $dbh->{AutoCommit} = 1;
- my $sths = prepare_stmt_handles($dbh);
+ my $db = database->new();
+ my $sths = $db->prepare_stmt_handles();
+ my $dbh = $db->{dbh};
while (1) {
my ($ver, $msg_type, $msg) = recv_msg($client_sock);
- log_set_msg($msg_str[$msg_type]);
+ $log->set_msg($msg_str[$msg_type]);
$dbh->begin_work;
my $reply = $msg_func[$msg_type]->($sths, $msg);
@@ -99,12 +92,12 @@ while (my $client_sock = $listen_sock->accept()) {
# but do it in an eval{} as it may also fail
eval { $dbh->rollback };
- log_print("discarding reply '$reply'\n");
- log_print("db transaction aborted: $@\n");
+ $log->print("discarding reply '$reply'\n");
+ $log->print("db transaction aborted: $@\n");
$reply = "err\0database transaction aborted";
}
- log_set_msg('');
+ $log->set_msg('');
send_msg($client_sock, $ver, $msg_type, $reply);
}
}
@@ -118,17 +111,15 @@ sub recv_msg {
my ($version, $msg_type, $msg_size) = unpack("nnn", $header);
if ($version != 0) {
- log_print("error: unsupported protocol version $version\n");
+ $log->print("error: unsupported protocol version $version\n");
exit 0;
}
-
- if ($msg_type >= @msg_str) {
- log_print("error: unknown message type $msg_type\n");
+ elsif ($msg_type >= @msg_str) {
+ $log->print("error: unknown message type $msg_type\n");
exit 0;
}
-
- if ($msg_size > 4096) {
- log_print("error: $msg_size byte message too large\n");
+ elsif ($msg_size > 4096) {
+ $log->print("error: $msg_size byte message too large\n");
exit 0;
}
elsif ($msg_size == 0) {
@@ -148,10 +139,10 @@ sub read_all {
my $bytes_read = $sock->sysread(my $tmp, $bytes_left);
if (!defined $bytes_read) {
- log_print("error: read failed: $!\n");
+ $log->print("error: read failed: $!\n");
exit 0;
} elsif ($bytes_read == 0) {
- log_print("disconnected!\n");
+ $log->print("disconnected!\n");
exit 0;
}
@@ -180,10 +171,10 @@ sub send_all {
my $bytes_written = $socket->syswrite($bytes);
if (!defined $bytes_written) {
- log_print("error: write failed: $!\n");
+ $log->print("error: write failed: $!\n");
exit 0;
} elsif ($bytes_written != $bytes_total) {
- log_print("error: wrote $bytes_written instead of $bytes_total bytes\n");
+ $log->print("error: wrote $bytes_written instead of $bytes_total bytes\n");
exit 0;
}
@@ -197,17 +188,17 @@ sub msg_device_add {
return "err\0$err" if ($err);
unless (looks_like_number($ph_num)) {
- log_print("phone number '$ph_num' invalid\n");
+ $log->print("phone number '$ph_num' invalid\n");
return "err\0the sent phone number is not a number";
}
$$sth{ph_num_exists}->execute($ph_num);
if ($$sth{ph_num_exists}->fetchrow_array()) {
- log_print("phone number '$ph_num' already exists\n");
+ $log->print("phone number '$ph_num' already exists\n");
return "err\0the sent phone number already exists";
}
if ($os ne 'unix' && $os ne 'android' && $os ne 'ios') {
- log_print("unknown operating system '$os'\n");
+ $log->print("unknown operating system '$os'\n");
return "err\0operating system not supported";
}
@@ -218,7 +209,7 @@ sub msg_device_add {
$$sth{new_device}->execute($token, $ph_num, $os, time);
my $fp = fingerprint($token);
- log_print("success, '$ph_num':'$fp' os '$os'\n");
+ $log->print("success, '$ph_num':'$fp' os '$os'\n");
return "ok\0$token";
}
@@ -233,12 +224,12 @@ sub msg_list_add {
return "err\0$err" if ($err);
my $devid_fp = fingerprint($device_id);
- log_print("'$list_name'\n");
- log_print("adding first member devid = '$devid_fp'\n");
+ $log->print("'$list_name'\n");
+ $log->print("adding first member devid = '$devid_fp'\n");
my $time = time;
my $list_id = sha256_base64(arc4random_bytes(32));
- log_print("fingerprint = '" .fingerprint($list_id). "'\n");
+ $log->print("fingerprint = '" .fingerprint($list_id). "'\n");
# add new list with single list member
$$sth{new_list}->execute($list_id, $list_name, $time, $time);
@@ -287,17 +278,17 @@ sub msg_list_join {
$err = list_id_valid($sth, $list_id);
return "err\0$err" if ($err);
- log_print("device '$device_id'\n");
- log_print("list '$list_id'\n");
+ $log->print("device '$device_id'\n");
+ $log->print("list '$list_id'\n");
my $time = time;
$$sth{check_list_member}->execute($list_id, $device_id);
if (!$$sth{check_list_member}->fetchrow_array()) {
$$sth{new_list_member}->execute($list_id, $device_id, $time);
- log_print("device '$device_id' has been added to list '$list_id'\n");
+ $log->print("device '$device_id' has been added to list '$list_id'\n");
} else {
- log_print("tried to create a duplicate list member entry for device '$device_id' and list '$list_id'\n");
+ $log->print("tried to create a duplicate list member entry for device '$device_id' and list '$list_id'\n");
return "err\0the device is already part of this list";
}
@@ -316,16 +307,16 @@ sub msg_list_leave {
$err = list_id_valid($sth, $list_id);
return "err\0$err" if ($err);
- log_print("device '$device_id'\n");
- log_print("list '$list_id'\n");
+ $log->print("device '$device_id'\n");
+ $log->print("list '$list_id'\n");
$$sth{check_list_member}->execute($list_id, $device_id);
if ($$sth{check_list_member}->fetchrow_array()) {
$$sth{remove_list_member}->execute($list_id, $device_id);
- log_print("device '$device_id' has been removed from list '$list_id'\n");
+ $log->print("device '$device_id' has been removed from list '$list_id'\n");
} else {
- log_print("warn: tried to leave a list the user was not in for device '$device_id' and list '$list_id'\n");
+ $log->print("tried to leave a list the user was not in for device '$device_id' and list '$list_id'\n");
}
$$sth{check_list_member}->finish();
@@ -334,7 +325,7 @@ sub msg_list_leave {
my $alive = 1;
if (!$$sth{get_list_members}->fetchrow_array()) {
- log_print("list '$list_id' is empty... deleting\n");
+ $log->print("list '$list_id' is empty... deleting\n");
$$sth{delete_list}->execute($list_id);
$$sth{delete_list_data}->execute($list_id);
$alive = 0;
@@ -354,10 +345,10 @@ sub msg_friend_add {
return "err\0$err" if ($err);
my $devid_fp = fingerprint($device_id);
- log_print("'$devid_fp' adding '$friend'\n");
+ $log->print("'$devid_fp' adding '$friend'\n");
unless (looks_like_number($friend)) {
- log_print("bad friends number '$friend'\n");
+ $log->print("bad friends number '$friend'\n");
return "err\0friends phone number is not a valid phone number";
}
@@ -367,18 +358,18 @@ sub msg_friend_add {
# check if the device is trying to add itself
if ($fr_devid eq $device_id) {
- log_print("device '$devid_fp' tried adding itself\n");
+ $log->print("device '$devid_fp' tried adding itself\n");
return "err\0device cannot add itself as a friend";
}
my $friends_fp = fingerprint($fr_devid);
- log_print("added friend is a member\n");
- log_print("friends device id is '$friends_fp'\n");
+ $log->print("added friend is a member\n");
+ $log->print("friends device id is '$friends_fp'\n");
# check if my phone number is in their friends list
$$sth{friends_select}->execute($fr_devid, $my_phnum);
if ($$sth{friends_select}->fetchrow_array()) {
- log_print("found mutual friendship\n");
+ $log->print("found mutual friendship\n");
$$sth{mutual_friend_insert}->execute($device_id, $fr_devid);
$$sth{mutual_friend_insert}->execute($fr_devid, $device_id);
}
@@ -399,17 +390,17 @@ sub msg_friend_delete {
return "err\0$err" if ($err);
unless (looks_like_number($friend)) {
- log_print("bad friends number '$friend'\n");
+ $log->print("bad friends number '$friend'\n");
return "err\0friends phone number is not a valid phone number";
}
$$sth{friends_select}->execute($device_id, $friend);
if ($$sth{friends_select}->fetchrow_array()) {
- log_print("removing '$friend' from friends list\n");
+ $log->print("removing '$friend' from friends list\n");
$$sth{friends_delete}->execute($device_id, $friend);
}
else {
- log_print("tried deleting friend '$friend' but they weren't a friend\n");
+ $log->print("tried deleting friend '$friend' but they weren't a friend\n");
return "err\0friend sent for deletion was not a friend";
}
@@ -417,7 +408,7 @@ sub msg_friend_delete {
$$sth{ph_num_exists}->execute($friend);
if (my ($friend_id) = $$sth{ph_num_exists}->fetchrow_array()) {
my $friend_fp = fingerprint($friend_id);
- log_print("friend '$friend' was registered as '$friend_fp'\n");
+ $log->print("friend '$friend' was registered as '$friend_fp'\n");
$$sth{mutual_friends_delete}->execute($device_id, $friend_id);
$$sth{mutual_friends_delete}->execute($friend_id, $device_id);
}
@@ -435,14 +426,14 @@ sub msg_lists_get {
return "err\0$err" if ($err);
my $devid_fp = fingerprint($device_id);
- log_print("gathering lists for '$devid_fp'\n");
+ $log->print("gathering lists for '$devid_fp'\n");
my @lists;
$$sth{get_lists}->execute($device_id);
while (my ($list_id, $list_name) = $$sth{get_lists}->fetchrow_array()) {
my $list_fp = fingerprint($list_id);
- log_print("found list '$list_name' '$list_fp'\n");
+ $log->print("found list '$list_name' '$list_fp'\n");
# find all members of this list
my @members;
@@ -451,7 +442,7 @@ sub msg_lists_get {
push @members, devid_to_phnum($sth, $devid);
}
my $members = join("\0", @members);
- log_print("list has ". @members ." members\n");
+ $log->print("list has ". @members ." members\n");
# find how many items are complete in this list
my $num_items = 0;
@@ -461,7 +452,7 @@ sub msg_lists_get {
# XXX: actually check the item status
$num_items++;
}
- log_print("list has $num_items items\n");
+ $log->print("list has $num_items items\n");
push @lists, "$list_id\0$list_name\0$num_items\0$members";
}
@@ -479,7 +470,7 @@ sub msg_lists_get_other {
return "err\0$err" if ($err);
my $devid_fp = fingerprint($device_id);
- log_print("gathering lists for '$devid_fp'\n");
+ $log->print("gathering lists for '$devid_fp'\n");
my @list_ids;
$$sth{get_lists}->execute($device_id);
@@ -493,7 +484,7 @@ sub msg_lists_get_other {
while (my ($friend_id) = $$sth{mutual_friend_select}->fetchrow_array()) {
my $friend_fp = fingerprint($friend_id);
- log_print("found mutual friend '$friend_fp'\n");
+ $log->print("found mutual friend '$friend_fp'\n");
# we can't send device id's back to the client
my $friend_phnum = devid_to_phnum($sth, $friend_id);
@@ -507,7 +498,7 @@ sub msg_lists_get_other {
push(@{ $members{$id} }, $friend_phnum);
$names{$id} = $name;
- log_print("found list '$name'\n");
+ $log->print("found list '$name'\n");
}
}
@@ -529,22 +520,22 @@ sub msg_list_items_get {
return "err\0$err" if ($err);
if (!$list_id) {
- log_print("received null list id");
+ $log->print("received null list id");
return "err\0the sent list id was empty";
}
# unless ($dbh->selectrow_array($sth{check_list_member}, undef, $list_id, $device_id)) {
# # XXX: table list_members list_id's should always exist in table lists
- # log_print("list_items: $device_id not a member of $list_id\n");
+ # $log->print("list_items: $device_id not a member of $list_id\n");
# return "err\0the sent device id is not a member of the list";
# }
- log_print("$device_id request items for $list_id\n");
+ $log->print("$device_id request items for $list_id\n");
$$sth{get_list_items}->execute($list_id);
my @items;
while (my ($list_id, $pos, $name, $status, $owner, undef) =
$$sth{get_list_items}->fetchrow_array()) {
- log_print("list item #$pos $name\n");
+ $log->print("list item #$pos $name\n");
push @items, "$pos:$name:$owner:$status";
}
@@ -564,8 +555,8 @@ sub split_fields {
my @fields = split("\0", $msg, $total_fields);
if (@fields != $total_fields) {
my $fields = @fields;
- log_print("got $fields fields, expected $total_fields\n");
- return ("wrong number of arguments");
+ $log->print("got $fields fields, expected $total_fields\n");
+ return ("the wrong number of message arguments were sent");
}
return (undef, @fields);
@@ -584,7 +575,7 @@ sub device_id_valid
my ($sth, $device_id) = @_;
unless ($device_id =~ m/^[a-zA-Z0-9+\/=]*$/) {
- log_print("'$device_id' not base64\n");
+ $log->print("'$device_id' not base64\n");
return ('the client sent a device id that was not base64');
}
@@ -593,7 +584,7 @@ sub device_id_valid
return (undef, $phnum);
}
- log_print("unknown device '$device_id'\n");
+ $log->print("unknown device '$device_id'\n");
return ('the client sent an unknown device id');
}
@@ -601,27 +592,45 @@ sub list_id_valid {
my ($sth, $list_id) = @_;
unless ($list_id =~ m/^[a-zA-Z0-9+\/=]*$/) {
- log_print("'$list_id' not base64\n");
+ $log->print("'$list_id' not base64\n");
return "the client sent a list id that was not base64";
}
$$sth{list_select}->execute($list_id);
unless ($$sth{list_select}->fetchrow_array()) {
- log_print("unknown list '$list_id'\n");
+ $log->print("unknown list '$list_id'\n");
return "the client sent an unknown list id";
}
return;
}
-sub create_tables {
- my $db_handle = DBI->connect(
+package database;
+
+sub new {
+ my $class = shift;
+
+ my $self = {};
+ bless ($self, $class);
+
+ $self->{dbh} = DBI->connect(
"dbi:SQLite:dbname=$db_file",
"", "",
{ RaiseError => 1 }
) or die $DBI::errstr;
- $db_handle->do("PRAGMA foreign_keys = ON");
+
+ $self->{dbh}->do("PRAGMA foreign_keys = ON");
+ $self->{dbh}->{AutoCommit} = 1;
+
+ return $self;
+}
+
+sub create_tables {
+ my ($self) = @_;
+
+ my $db_handle = $self->{dbh};
+ $db_handle->begin_work;
$db_handle->do(qq{create table if not exists lists(
list_id int not null primary key,
@@ -629,7 +638,7 @@ sub create_tables {
date int,
first_created int not null,
last_updated int not null)
- }) or die $DBI::errstr;
+ });
$db_handle->do(qq{create table if not exists devices(
device_id text not null primary key,
@@ -637,14 +646,14 @@ sub create_tables {
os text,
push_token text,
first_seen int not null)
- }) or die $DBI::errstr;
+ });
$db_handle->do(qq{create table if not exists friends(
device_id text not null,
friend int not null,
primary key(device_id, friend),
foreign key(device_id) references devices(device_id))
- }) or die $DBI::errstr;
+ });
$db_handle->do(qq{create table if not exists mutual_friends(
device_id text not null,
@@ -652,7 +661,7 @@ sub create_tables {
primary key(device_id, mutual_friend),
foreign key(device_id) references devices(device_id),
foreign key(mutual_friend) references devices(device_id))
- }) or die $DBI::errstr;
+ });
$db_handle->do(qq{create table if not exists list_members(
list_id int not null,
@@ -661,7 +670,7 @@ sub create_tables {
primary key(list_id, device_id),
foreign key(list_id) references lists(list_id),
foreign key(device_id) references devices(device_id))
- }) or die $DBI::errstr;
+ });
$db_handle->do(qq{create table if not exists list_data(
list_id int not null,
@@ -673,16 +682,18 @@ sub create_tables {
primary key(list_id, name),
foreign key(list_id) references lists(list_id),
foreign key(owner) references devices(device_id))
- }) or die $DBI::errstr;
+ });
- $db_handle->disconnect();
- $db_handle = undef;
+ $db_handle->commit;
+ $self->{dbh}->disconnect();
+ $self->{dbh} = undef;
}
sub prepare_stmt_handles {
- my $dbh = shift;
+ my ($self) = @_;
my %stmt_handles;
+ my $dbh = $self->{dbh};
my $sql;
# list table queries
@@ -756,32 +767,49 @@ sub prepare_stmt_handles {
return \%stmt_handles;
}
-my ($log_addr, $log_port) = ('', '');
-sub log_set_peer_host_port {
- my ($sock) = @_;
- ($log_addr, $log_port) = ($sock->peerhost(), $sock->peerport());
+package logger;
+use POSIX;
+
+sub new {
+ my $class = shift;
+
+ my $self = {};
+ bless ($self, $class);
+
+ $self->{addr} = '';
+ $self->{port} = '';
+ $self->{msg_type} = '';
+ return $self;
+}
+
+sub set_peer_host_port {
+ my ($self, $sock) = @_;
+ ($self->{addr}, $self->{port}) = ($sock->peerhost(), $sock->peerport());
}
-sub log_set_msg {
- my ($msg_type) = @_;
+sub set_msg {
+ my ($self, $msg_type) = @_;
if ($msg_type ne '') {
- $log_msg_type = "$msg_type: ";
- }
- else {
- $log_msg_type = '';
+ $self->{msg_type} = "$msg_type: ";
+ } else {
+ $self->{msg_type} = '';
}
}
-sub log_print {
+sub print {
+ my ($self, @args) = @_;
+
my $ftime = strftime("%F %T", localtime);
- printf "%s %-15s %-5s> %s", $ftime, $log_addr, $log_port, $log_msg_type;
+ printf "%s %-15s %-5s> %s", $ftime, $self->{addr}, $self->{port}, $self->{msg_type};
# we print potentially unsafe strings here, don't use printf
- print @_;
+ print @args;
}
-sub log_print_bare {
+sub print_bare {
+ my ($self, @args) = @_;
+
my $ftime = strftime("%F %T", localtime);
printf "%s> ", $ftime;
- printf @_;
+ printf @args;
}