<commit_before>#!/usr/bin/env perl

# This file is part of the VROOM project
# released under the MIT licence
# Copyright 2014 Firewall Services
# <NAME> <<EMAIL>>

use lib 'lib';
use Mojolicious::Lite;
use Mojolicious::Plugin::Mail;
use Mojolicious::Plugin::Database;
use Vroom::Constants;
use Crypt::SaltedHash;
use MIME::Base64;
use File::stat;
use File::Basename;
use Etherpad::API;
use Session::Token;
use Config::Simple;

app->log->level('info');
# Read conf file, and set default values
my $cfg = new Config::Simple();
$cfg->read('conf/settings.ini');
our $config = $cfg->vars();

$config->{'database.dsn'}                      ||= 'DBI:mysql:database=vroom;host=localhost';
$config->{'database.user'}                     ||= 'vroom';
$config->{'database.password'}                 ||= '<PASSWORD>';
$config->{'signaling.uri'}                     ||= 'https://vroom.example.com/';
$config->{'turn.stun_server'}                  ||= 'stun.l.google.com:19302';
$config->{'turn.turn_server'}                  ||= '';
$config->{'turn.realm'}                        ||= 'vroom';
$config->{'email.from '}                       ||= '<EMAIL>';
$config->{'email.contact'}                     ||= '<EMAIL>';
$config->{'email.sendmail'}                    ||= '/sbin/sendmail';
$config->{'interface.powered_by'}              ||= '<a href="http://www.firewall-services.com" target="_blank">Firewall Services</a>';
$config->{'interface.template'}                ||= 'default';
$config->{'interface.chrome_extension_id'}     ||= 'ecicdpoejfllflombfanbhfpgcimjddn';
$config->{'cookie.secret'}                     ||= 'secret';
$config->{'cookie.name'}                       ||= 'vroom';
$config->{'rooms.inactivity_timeout'}          ||= 3600;
$config->{'rooms.reserved_inactivity_timeout'} ||= 5184000;
$config->{'rooms.common_names'}                ||= '';
$config->{'log.level'}                         ||= 'info';
$config->{'etherpad.uri'}                      ||= '';
$config->{'etherpad.api_key'}                  ||= '';
$config->{'etherpad.base_domain'}              ||= '';
$config->{'daemon.listen_ip'}                  ||= '127.0.0.1';
$config->{'daemon.listen_port'}                ||= '8090';
$config->{'daemon.backend'}                    ||= 'hypnotoad';

# Set log level
app->log->level($config->{'log.level'});

# Create etherpad api client if required
our $ec = undef;
if ($config->{'etherpad.uri'} =~ m/https?:\/\/.*/ && $config->{'etherpad.api_key'} ne ''){
  $ec = Etherpad::API->new({
    url => $config->{'etherpad.uri'},
    apikey => $config->{'etherpad.api_key'}
  });
}

# Load I18N, and declare supported languages
plugin I18N => {
  namespace => 'Vroom::I18N',
};

# Connect to the database
plugin database => {
  dsn      => $config->{'database.dsn'},
  username => $config->{'database.user'},
  password => $config->{'database.password'},
  options  => { mysql_enable_utf8 => 1 }
};

# Load mail plugin with its default values
plugin mail => {
  from => $config->{'email.from'},
  type => 'text/html',
};

##########################
#  Validation helpers    #
##########################

# take a string as argument and check if it's a valid room name
helper valid_room_name => sub {
  my $self = shift;
  my ($name) = @_;
  my $ret = {};
  # A few names are reserved
  my @reserved = qw(about help feedback feedback_thanks goodbye admin create localize action
                    missing dies password kicked invitation js css img fonts snd);
  if ($name !~ m/^[\w\-]{1,49}$/){
    return {msg => 'ERROR_NAME_INVALID'};
  }
  elsif (grep { $name eq $_ } @reserved){
    return {msg => 'ERROR_NAME_RESERVED'};
  }
  return {ok => 1};
};

# Check id arg is a valid ID number
helper valid_id => sub {
  my $self = shift;
  my ($id) = @_;
  if ($id !~ m/^\d+$/){
    return {msg => 'INVALID_ID'};
  }
  return {ok => 1};
};

##########################
#   Various helpers      #
##########################

# Create a cookie based session
helper login => sub {
  my $self = shift;
  my $ret = {};
  if ($self->session('name')){
    return {ok => 1};
  }
  my $login = $ENV{'REMOTE_USER'} || lc $self->get_random(256);
  $self->session(
      name => $login,
      ip   => $self->tx->remote_address
  );
  $self->app->log->info($self->session('name') . " logged in from " . $self->tx->remote_address);
  return {ok => 1};
};

# Expire the cookie
helper logout => sub {
  my $self = shift;
  my ($room) = @_;
  my $ret = {};
  # Logout from etherpad
  if ($ec && $self->session($room) && $self->session($room)->{etherpadSessionId}){
    $ec->delete_session($self->session($room)->{etherpadSessionId});
  }
  $self->session( expires => 1 );
  $self->app->log->info($self->session('name') . " logged out");
  return {ok => 1};
};

# Create a new room in the DB
# Requires two args: the name of the room and the session name of the creator
helper create_room => sub {
  my $self = shift;
  my ($name,$owner) = @_;
  my $res = {};
  # Convert room names to lowercase
  if ($name ne lc $name){
    $name = lc $name;
  }
  # Check if the name is valid
  $res = $self->valid_room_name($name);
  if (!$res->{ok}){
    return $res;
  }
  $res = $self->get_room_by_name($name);
  if ($res->{data}){
    return $res;
  }
  my $sth = eval {
    $self->db->prepare('INSERT INTO `rooms`
                          (`name`,
                           `create_date`,
                           `last_activity`,
                           `owner`,
                           `token`,
                           `realm`)
                          VALUES (?,
                                  CONVERT_TZ(NOW(), @@session.time_zone, \'+00:00\'),
                                  CONVERT_TZ(NOW(), @@session.time_zone, \'+00:00\'),
                                  ?,
                                  ?,
                                  ?)');
  };
  if ($@){
    return {msg => $@};
  }
  $sth->execute(
    $name,
    $owner,
    $self->get_random(256),
    $config->{'turn.realm'}
  );
  if ($sth->err){
    return {msg => "DB Error: " . $sth->errstr . " (code " . $sth->err . ")"};
  }
  $self->app->log->info("Room $name created by " . $self->session('name'));
  # Etherpad integration ? If so, create the corresponding pad
  if ($ec){
    $self->create_pad($name);
  }
  return {ok => 1};
};

# Take a string as argument
# Return corresponding room data in ->{data}
helper get_room_by_name => sub {
  my $self = shift;
  my ($name) = @_;
  my $res = $self->valid_room_name($name);
  if (!$res->{ok}){
    return $res;
  }
  my $sth = eval {
    $self->db->prepare('SELECT *
                          FROM `rooms`
                          WHERE `name`=?');
  };
  if ($@){
    return {msg => $@};
  }
  $sth->execute($name);
  if ($sth->err){
    return {msg => "DB Error: " . $sth->errstr . " (code " . $sth->err . ")"};
  }
  return {
    data  => $sth->fetchall_hashref('name')->{$name}
  };
};

# Same as before, but take a room ID as argument
helper get_room_by_id => sub {
  my $self = shift;
  my ($id) = @_;
  my $res = $self->valid_id($id);
  if (!$res->{ok}){
    return $res;
  }
  my $sth = eval {
    $self->db->prepare('SELECT *
                          FROM `rooms`
                          WHERE `id`=?');
  };
  if ($@){
    return {msg => $@};
  }
  $sth->execute($id);
  if ($sth->err){
    return {msg => "DB Error: " . $sth->errstr . " (code " . $sth->err . ")"};
  }
  return {
    data   => $sth->fetchall_hashref('id')->{$id}
  };
};

# Update a room, take a room object as a hashref
helper modify_room => sub {
  my $self = shift;
  my ($room) = @_;
  my $res = {};
  my $sth = eval {
    $self->db->prepare('UPDATE `rooms`
                          SET `owner`=?,
                              `last_activity`=CONVERT_TZ(NOW(), @@session.time_zone, \'+00:00\'),
                              `locked`=?,
                              `ask_for_name`=?,
                              `join_password`=?,
                              `owner_password`=?,
                              `persistent`=?');
  } || return undef;
  $sth->execute(
    $room->{owner},
    $room->{locked},
    $room->{ask_for_name},
    $room->{join_password},
    $room->{owner_password},
    $room->{persistent}
  ) || return undef;
  $self->app->log->info("Room " . $room->{name} ." modified by " . $self->session('name'));
  return 1;<commit_msg>Return OK when everything is fine in get_room_by_*<commit_after>#!/usr/bin/env perl

# This file is part of the VROOM project
# released under the MIT licence
# Copyright 2014 Firewall Services
# <NAME> <<EMAIL>>

use lib 'lib';
use Mojolicious::Lite;
use Mojolicious::Plugin::Mail;
use Mojolicious::Plugin::Database;
use Vroom::Constants;
use Crypt::SaltedHash;
use MIME::Base64;
use File::stat;
use File::Basename;
use Etherpad::API;
use Session::Token;
use Config::Simple;

app->log->level('info');
# Read conf file, and set default values
my $cfg = new Config::Simple();
$cfg->read('conf/settings.ini');
our $config = $cfg->vars();

$config->{'database.dsn'}                      ||= 'DBI:mysql:database=vroom;host=localhost';
$config->{'database.user'}                     ||= 'vroom';
$config->{'database.password'}                 ||= '<PASSWORD>';
$config->{'signaling.uri'}                     ||= 'https://vroom.example.com/';
$config->{'turn.stun_server'}                  ||= 'stun.l.google.com:19302';
$config->{'turn.turn_server'}                  ||= '';
$config->{'turn.realm'}                        ||= 'vroom';
$config->{'email.from '}                       ||= '<EMAIL>';
$config->{'email.contact'}                     ||= '<EMAIL>';
$config->{'email.sendmail'}                    ||= '/sbin/sendmail';
$config->{'interface.powered_by'}              ||= '<a href="http://www.firewall-services.com" target="_blank">Firewall Services</a>';
$config->{'interface.template'}                ||= 'default';
$config->{'interface.chrome_extension_id'}     ||= 'ecicdpoejfllflombfanbhfpgcimjddn';
$config->{'cookie.secret'}                     ||= 'secret';
$config->{'cookie.name'}                       ||= 'vroom';
$config->{'rooms.inactivity_timeout'}          ||= 3600;
$config->{'rooms.reserved_inactivity_timeout'} ||= 5184000;
$config->{'rooms.common_names'}                ||= '';
$config->{'log.level'}                         ||= 'info';
$config->{'etherpad.uri'}                      ||= '';
$config->{'etherpad.api_key'}                  ||= '';
$config->{'etherpad.base_domain'}              ||= '';
$config->{'daemon.listen_ip'}                  ||= '127.0.0.1';
$config->{'daemon.listen_port'}                ||= '8090';
$config->{'daemon.backend'}                    ||= 'hypnotoad';

# Set log level
app->log->level($config->{'log.level'});

# Create etherpad api client if required
our $ec = undef;
if ($config->{'etherpad.uri'} =~ m/https?:\/\/.*/ && $config->{'etherpad.api_key'} ne ''){
  $ec = Etherpad::API->new({
    url => $config->{'etherpad.uri'},
    apikey => $config->{'etherpad.api_key'}
  });
}

# Load I18N, and declare supported languages
plugin I18N => {
  namespace => 'Vroom::I18N',
};

# Connect to the database
plugin database => {
  dsn      => $config->{'database.dsn'},
  username => $config->{'database.user'},
  password => $config->{'<PASSWORD>'},
  options  => { mysql_enable_utf8 => 1 }
};

# Load mail plugin with its default values
plugin mail => {
  from => $config->{'email.from'},
  type => 'text/html',
};

##########################
#  Validation helpers    #
##########################

# take a string as argument and check if it's a valid room name
helper valid_room_name => sub {
  my $self = shift;
  my ($name) = @_;
  my $ret = {};
  # A few names are reserved
  my @reserved = qw(about help feedback feedback_thanks goodbye admin create localize action
                    missing dies password kicked invitation js css img fonts snd);
  if ($name !~ m/^[\w\-]{1,49}$/){
    return {msg => 'ERROR_NAME_INVALID'};
  }
  elsif (grep { $name eq $_ } @reserved){
    return {msg => 'ERROR_NAME_RESERVED'};
  }
  return {ok => 1};
};

# Check id arg is a valid ID number
helper valid_id => sub {
  my $self = shift;
  my ($id) = @_;
  if ($id !~ m/^\d+$/){
    return {msg => 'INVALID_ID'};
  }
  return {ok => 1};
};

##########################
#   Various helpers      #
##########################

# Create a cookie based session
helper login => sub {
  my $self = shift;
  my $ret = {};
  if ($self->session('name')){
    return {ok => 1};
  }
  my $login = $ENV{'REMOTE_USER'} || lc $self->get_random(256);
  $self->session(
      name => $login,
      ip   => $self->tx->remote_address
  );
  $self->app->log->info($self->session('name') . " logged in from " . $self->tx->remote_address);
  return {ok => 1};
};

# Expire the cookie
helper logout => sub {
  my $self = shift;
  my ($room) = @_;
  my $ret = {};
  # Logout from etherpad
  if ($ec && $self->session($room) && $self->session($room)->{etherpadSessionId}){
    $ec->delete_session($self->session($room)->{etherpadSessionId});
  }
  $self->session( expires => 1 );
  $self->app->log->info($self->session('name') . " logged out");
  return {ok => 1};
};

# Create a new room in the DB
# Requires two args: the name of the room and the session name of the creator
helper create_room => sub {
  my $self = shift;
  my ($name,$owner) = @_;
  my $res = {};
  # Convert room names to lowercase
  if ($name ne lc $name){
    $name = lc $name;
  }
  # Check if the name is valid
  $res = $self->valid_room_name($name);
  if (!$res->{ok}){
    return $res;
  }
  $res = $self->get_room_by_name($name);
  if ($res->{data}){
    return $res;
  }
  my $sth = eval {
    $self->db->prepare('INSERT INTO `rooms`
                          (`name`,
                           `create_date`,
                           `last_activity`,
                           `owner`,
                           `token`,
                           `realm`)
                          VALUES (?,
                                  CONVERT_TZ(NOW(), @@session.time_zone, \'+00:00\'),
                                  CONVERT_TZ(NOW(), @@session.time_zone, \'+00:00\'),
                                  ?,
                                  ?,
                                  ?)');
  };
  if ($@){
    return {msg => $@};
  }
  $sth->execute(
    $name,
    $owner,
    $self->get_random(256),
    $config->{'turn.realm'}
  );
  if ($sth->err){
    return {msg => "DB Error: " . $sth->errstr . " (code " . $sth->err . ")"};
  }
  $self->app->log->info("Room $name created by " . $self->session('name'));
  # Etherpad integration ? If so, create the corresponding pad
  if ($ec){
    $self->create_pad($name);
  }
  return {ok => 1};
};

# Take a string as argument
# Return corresponding room data in ->{data}
helper get_room_by_name => sub {
  my $self = shift;
  my ($name) = @_;
  my $res = $self->valid_room_name($name);
  if (!$res->{ok}){
    return $res;
  }
  my $sth = eval {
    $self->db->prepare('SELECT *
                          FROM `rooms`
                          WHERE `name`=?');
  };
  if ($@){
    return {msg => $@};
  }
  $sth->execute($name);
  if ($sth->err){
    return {msg => "DB Error: " . $sth->errstr . " (code " . $sth->err . ")"};
  }
  return {
    ok    => 1,
    data  => $sth->fetchall_hashref('name')->{$name}
  };
};

# Same as before, but take a room ID as argument
helper get_room_by_id => sub {
  my $self = shift;
  my ($id) = @_;
  my $res = $self->valid_id($id);
  if (!$res->{ok}){
    return $res;
  }
  my $sth = eval {
    $self->db->prepare('SELECT *
                          FROM `rooms`
                          WHERE `id`=?');
  };
  if ($@){
    return {msg => $@};
  }
  $sth->execute($id);
  if ($sth->err){
    return {msg => "DB Error: " . $sth->errstr . " (code " . $sth->err . ")"};
  }
  return {
    ok     => 1,
    data   => $sth->fetchall_hashref('id')->{$id}
  };
};

# Update a room, take a room object as a hashref
helper modify_room => sub {
  my $self = shift;
  my ($room) = @_;
  my $res = {};
  my $sth = eval {
    $self->db->prepare('UPDATE `rooms`
                          SET `owner`=?,
                              `last_activity`=CONVERT_TZ(NOW(), @@session.time_zone, \'+00:00\'),
                              `locked`=?,
                              `ask_for_name`=?,
                              `join_password`=?,
                              `owner_password`=?,
                              `persistent`=?');
  } || return undef;
  $sth->execute(
    $room->{owner},
    $room->{locked},
    $room->{ask_for_name},
    $room->{join_password},
    $room->{owner_password},
    $room->{persistent}
  ) || return undef;
  $self->app->log->info("Room " . $room->{name} ." modified by " . $self->session('name'));
  return 1;
{'fraction_non_alphanumeric': 0.2083109759732904, 'fraction_numerical': 0.008346747749359089, 'mean_word_length': 2.206039755351682, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 334, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4678305', 'n_tokens_mistral': 5712, 'n_tokens_neox': 5490, 'n_words': 1216}