| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263 |
- package eyelib::database;
- #
- # Copyright (C) Roman Dmitriev, rnd@rajven.ru
- #
- use utf8;
- use open ":encoding(utf8)";
- use strict;
- use English;
- use FindBin '$Bin';
- use lib "/opt/Eye/scripts";
- use base 'Exporter';
- use vars qw(@EXPORT @ISA);
- use eyelib::config;
- use eyelib::main;
- use Net::Patricia;
- use eyelib::net_utils;
- use Data::Dumper;
- use DateTime;
- use POSIX qw(mktime ctime strftime);
- use File::Temp qw(tempfile);
- use DBI;
- use DBD::Pg qw(:pg_types);
- our @ISA = qw(Exporter);
- our @EXPORT = qw(
- StrToIp
- IpToStr
- batch_db_sql
- batch_db_sql_cached
- batch_db_sql_csv
- reconnect_db
- write_db_log
- db_log_debug
- db_log_error
- db_log_info
- db_log_verbose
- db_log_warning
- init_db
- do_sql
- _execute_param
- do_sql_param
- get_record_sql_param
- get_records_sql_param
- get_scalar_sql_param
- get_count_sql_param
- get_option_safe
- get_count_records
- get_id_record
- get_records_sql
- get_record_sql
- get_diff_rec
- update_record
- insert_record
- delete_record
- get_option
- init_option
- Set_Variable
- Get_Variable
- Del_Variable
- clean_variables
- $add_rules
- $L_WARNING
- $L_INFO
- $L_DEBUG
- $L_ERROR
- $L_VERBOSE
- );
- BEGIN
- {
- #---------------------------------------------------------------------------------------------------------------
- our $add_rules;
- our $L_ERROR = 0;
- our $L_WARNING = 1;
- our $L_INFO = 2;
- our $L_VERBOSE = 3;
- our $L_DEBUG = 255;
- our %acl_fields = (
- 'ip' => '1',
- 'ip_int' => '1',
- 'enabled'=>'1',
- 'dhcp'=>'1',
- 'filter_group_id'=>'1',
- 'deleted'=>'1',
- 'dhcp_acl'=>'1',
- 'queue_id'=>'1',
- 'mac'=>'1',
- 'blocked'=>'1'
- );
- our %dhcp_fields = (
- 'ip' => '1',
- 'dhcp_acl'=>'1',
- 'dhcp_option_set'=>'1',
- 'dhcp'=>'1',
- 'deleted'=>'1',
- 'mac'=>'1',
- );
- our %dns_fields = (
- 'ip' => '1',
- 'dns_name'=>'1',
- 'dns_ptr_only'=>'1',
- 'alias'=>'1',
- );
- #---------------------------------------------------------------------------------------------------------------
- sub StrToIp {
- return unpack('N',pack('C4',split(/\./,$_[0])));
- }
- #---------------------------------------------------------------------------------------------------------------
- sub IpToStr {
- my $nIP = shift;
- my $res = (($nIP>>24) & 255) .".". (($nIP>>16) & 255) .".". (($nIP>>8) & 255) .".". ($nIP & 255);
- return $res;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub batch_db_sql {
- my $db=shift;
- my $batch_sql=shift;
- return if (!$db);
- $db->{AutoCommit} = 0;
- my $apply = 0;
- my $sth;
- my @msg = ();
- if (ref($batch_sql) eq 'ARRAY') { @msg = @$batch_sql; } else { @msg = split("\n",$batch_sql); }
- foreach my $sSQL (@msg) {
- next if (!$sSQL);
- $sth = $db->prepare($sSQL) or die "Unable to prepare $sSQL" . $db->errstr;
- $sth->execute() or die "Unable to prepare $sSQL" . $db->errstr;
- $apply = 1;
- }
- if ($apply) { $sth->finish(); }
- $db->{AutoCommit} = 1;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub batch_db_sql_cached {
- my $db = DBI->connect("dbi:$config_ref{DBTYPE}:dbname=$DBNAME;host=$DBHOST","$DBUSER","$DBPASS", { RaiseError => 0, AutoCommit => 0 });
- if ( !defined $db ) { die "Cannot connect to $config_ref{DBTYPE} server: $DBI::errstr\n"; }
- if ($config_ref{DBTYPE} eq 'mysql') {
- $db->do('SET NAMES utf8mb4');
- $db->{'mysql_enable_utf8'} = 1;
- }
- my $table= shift;
- my $batch_sql=shift;
- return if (!$db);
- my @msg = ();
- if (ref($batch_sql) eq 'ARRAY') { @msg = @$batch_sql; } else { @msg = split("\n",$batch_sql); }
- my $sth = $db->prepare_cached($table) or die "Unable to prepare:" . $db->errstr;
- foreach my $sSQL (@msg) {
- next if (!$sSQL);
- $sth->execute(@$sSQL) or die "Unable to execute:" . $db->errstr;
- }
- $db->commit();
- $db->disconnect();
- }
- #---------------------------------------------------------------------------------------------------------------
- sub batch_db_sql_csv {
- my $db;
- if ($config_ref{DBTYPE} eq 'mysql') {
- $db = DBI->connect("dbi:$config_ref{DBTYPE}:dbname=$DBNAME;host=$DBHOST","$DBUSER","$DBPASS", { RaiseError => 1, mysql_local_infile=> 1 });
- } else {
- $db = DBI->connect("dbi:$config_ref{DBTYPE}:dbname=$DBNAME;host=$DBHOST","$DBUSER","$DBPASS", { RaiseError => 1 });
- }
- if ( !defined $db ) { die "Cannot connect to $config_ref{DBTYPE} server: $DBI::errstr\n"; }
- return if (!$db);
- if ($config_ref{DBTYPE} eq 'mysql') {
- $db->do('SET NAMES utf8mb4');
- $db->{'mysql_enable_utf8'} = 1;
- }
- my $table= shift;
- my $data = shift;
- my $fh = File::Temp->new(UNLINK=>1);
- my $fname = $fh->filename;
- binmode($fh,':utf8');
- foreach my $row (@$data) {
- next if (!$row);
- my @tmp = @$row;
- my $values = 'NULL';
- for (my $i = 0; $i <@tmp ; $i++) {
- $values.=',"'.$tmp[$i].'"';
- }
- $values =~s/,$//;
- print $fh $values."\r\n";
- }
- close $fh;
- if ($config_ref{DBTYPE} eq 'mysql') {
- my $query = qq{ LOAD DATA LOCAL INFILE '$fname' INTO TABLE $table FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\r\n'; };
- $db->do($query);
- } else {
- # PostgreSQL использует COPY
- my $query = qq{ COPY $table FROM STDIN WITH (FORMAT CSV, DELIMITER ',', QUOTE '"', NULL 'NULL'); };
- $db->do($query, undef, $fh);
- }
- $db->disconnect;
- File::Temp::cleanup();
- }
- #---------------------------------------------------------------------------------------------------------------
- sub reconnect_db {
- my $db_ref = shift;
- # Если соединение активно, ничего не делаем
- if ($$db_ref && $$db_ref->ping) {
- return 1;
- }
- # Переподключаемся
- eval {
- # Закрываем старое соединение если есть
- if ($$db_ref) {
- $$db_ref->disconnect;
- $$db_ref = undef;
- }
- # Создаем новое соединение
- $$db_ref = init_db();
- # Проверяем что соединение установлено
- unless ($$db_ref && $$db_ref->ping) {
- die "Failed to establish database connection";
- }
- 1; # возвращаем истину при успехе
- } or do {
- my $error = $@ || 'Unknown error';
- warn "Database reconnection failed: $error";
- $$db_ref = undef;
- return 0;
- };
- return 1;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub write_db_log {
- my $db=shift;
- my $msg=shift;
- my $level = shift || $L_VERBOSE;
- my $auth_id = shift || 0;
- return if (!$db);
- return if (!$msg);
- $msg=~s/[\'\"]//g;
- my $db_log = 0;
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- $db_log = 0;
- }
- if ($level eq $L_ERROR and $log_level >= $L_ERROR) { log_error($msg); $db_log = 1; }
- if ($level eq $L_WARNING and $log_level >= $L_WARNING) { log_warning($msg); $db_log = 1; }
- if ($level eq $L_INFO and $log_level >= $L_INFO) { log_info($msg); $db_log = 1; }
- if ($level eq $L_VERBOSE and $log_level >= $L_VERBOSE) { log_verbose($msg); $db_log = 1; }
- if ($level eq $L_DEBUG and $log_level >= $L_DEBUG) { log_debug($msg); return; }
- if ($db_log) {
- #my $new_id = do_sql($dbh, 'INSERT INTO user_list (login) VALUES (?)', 'Ivan');
- do_sql($db,'INSERT INTO worklog(customer,message,level,auth_id,ip) VALUES( ?, ?, ?, ?, ?)',$MY_NAME,$msg,$level,$auth_id,$config_ref{self_ip});
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub db_log_debug {
- my $db = shift;
- my $msg = shift;
- my $id = shift;
- if ($debug) { log_debug($msg); }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub db_log_error {
- my $db = shift;
- my $msg = shift;
- if ($log_level >= $L_ERROR) {
- sendEmail("ERROR! ".get_first_line($msg),$msg,1);
- write_db_log($db,$msg,$L_ERROR);
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub db_log_info {
- my $db = shift;
- my $msg = shift;
- my $id = shift;
- if ($log_level >= $L_INFO) { write_db_log($db,$msg,$L_INFO,$id); }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub db_log_verbose {
- my $db = shift;
- my $msg = shift;
- my $id = shift;
- if ($log_level >= $L_VERBOSE) { write_db_log($db,$msg,$L_VERBOSE,$id); }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub db_log_warning {
- my $db = shift;
- my $msg = shift;
- my $id = shift;
- if ($log_level >= $L_WARNING) { write_db_log($db,$msg,$L_WARNING,$id); }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub init_db {
- # Create new database handle. If we can't connect, die()
- my $db;
- if ($config_ref{DBTYPE} eq 'mysql') {
- $db = DBI->connect("dbi:mysql:database=$DBNAME;host=$DBHOST","$DBUSER","$DBPASS",
- { RaiseError => 0, AutoCommit => 1, mysql_enable_utf8 => 1 });
- if ( !defined $db ) { die "Cannot connect to MySQL server: $DBI::errstr\n"; }
- $db->do('SET NAMES utf8mb4');
- } else {
- $db = DBI->connect("dbi:Pg:dbname=$DBNAME;host=$DBHOST","$DBUSER","$DBPASS",
- { RaiseError => 0, AutoCommit => 1, pg_enable_utf8 => 1, pg_server_prepare => 0 });
- if ( !defined $db ) { die "Cannot connect to PostgreSQL server: $DBI::errstr\n"; }
- }
- return $db;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub do_sql {
- my ($db, $sql, @bind_values) = @_;
- return 0 unless $db;
- return 0 unless $sql;
- unless (reconnect_db(\$db)) {
- log_error("No database connection available for SQL: $sql");
- return 0;
- }
- # Логируем не-SELECT-запросы
- log_debug( $sql . (@bind_values ? ' | bind: [' . join(', ', map { defined $_ ? $_ : 'undef' } @bind_values) . ']' : '')) unless $sql =~ /^select /i;
- # Подготовка запроса
- my $sth = $db->prepare($sql) or do {
- log_error("Unable to prepare SQL [$sql]: " . $db->errstr);
- return 0;
- };
- # Выполнение запроса
- my $rv;
- if (@bind_values) {
- $rv = $sth->execute(@bind_values) or do {
- log_error("Unable to execute SQL [$sql] with bind: [" . join(', ', map { defined $_ ? $_ : 'undef' } @bind_values) . "]: " . $sth->errstr);
- return 0;
- };
- } else {
- $rv = $sth->execute() or do {
- log_error("Unable to execute SQL [$sql]: " . $sth->errstr);
- return 0;
- };
- }
- # Обработка результатов по типу запроса
- if ($sql =~ /^insert/i) {
- my $id;
- if ($config_ref{DBTYPE} and $config_ref{DBTYPE} eq 'mysql') {
- $id = $sth->{mysql_insertid};
- } else {
- ($id) = $db->selectrow_array("SELECT lastval()");
- }
- $sth->finish();
- return $id || 0; # Возвращаем ID или 0 если ID нет
- }
- elsif ($sql =~ /^select /i) {
- my $data = $sth->fetchall_arrayref({});
- $sth->finish();
- return $data; # возвращаем ссылку на массив
- }
- else {
- # UPDATE, DELETE, CREATE, ALTER и т.д.
- $sth->finish();
- return 1;
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- # Внутренняя функция для выполнения параметризованных запросов
- sub _execute_param {
- my ($db, $sql, $params, $options) = @_;
-
- return unless $db && $sql;
-
- # Логируем не-SELECT-запросы
- unless ($sql =~ /^\s*SELECT/i) {
- log_debug( $sql . ($params ? ' | params: [' . join(', ', map { defined $_ ? $_ : 'undef' } @$params) . ']' : ''));
- }
-
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return wantarray ? () : undef;
- }
-
- my $mode = $options->{mode} || 'execute';
-
- eval {
- my $sth = $db->prepare($sql) or die "Unable to prepare SQL [$sql]: " . $db->errstr;
-
- my $rv = $params ? $sth->execute(@$params) : $sth->execute();
- unless ($rv) {
- die "Unable to execute SQL [$sql]" . ($params ? " with params: [" . join(', ', @$params) . "]" : "") . ": " . $sth->errstr;
- }
-
- if ($mode eq 'single') {
- my $row = $sth->fetchrow_hashref();
- $sth->finish();
- return $row;
- }
- elsif ($mode eq 'array') {
- my @rows;
- while (my $row = $sth->fetchrow_hashref()) {
- push @rows, $row;
- }
- $sth->finish();
- return @rows;
- }
- elsif ($mode eq 'arrayref') {
- my $rows = $sth->fetchall_arrayref({});
- $sth->finish();
- return $rows;
- }
- elsif ($mode eq 'scalar') {
- my $row = $sth->fetchrow_arrayref();
- $sth->finish();
- return $row ? $row->[0] : undef;
- }
- elsif ($mode eq 'id') {
- if ($sql =~ /^\s*INSERT/i) {
- my $id;
- if ($config_ref{DBTYPE} and $config_ref{DBTYPE} eq 'mysql') {
- $id = $sth->{mysql_insertid};
- } else {
- ($id) = $db->selectrow_array("SELECT lastval()");
- }
- $sth->finish();
- return $id || 0;
- }
- $sth->finish();
- return 1;
- }
- else {
- $sth->finish();
- return 1;
- }
- };
-
- if ($@) {
- log_error("Error executing SQL [$sql]: " . $@);
- return wantarray ? () : undef;
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- # Выполнение SQL с параметрами (новая безопасная версия)
- sub do_sql_param {
- my ($db, $sql, @params) = @_;
- return 0 unless $db && $sql;
-
- # Определяем режим по типу запроса
- my $mode = 'execute';
- if ($sql =~ /^\s*SELECT/i) {
- $mode = 'arrayref';
- } elsif ($sql =~ /^\s*INSERT/i) {
- $mode = 'id';
- }
-
- return _execute_param($db, $sql, \@params, { mode => $mode });
- }
- #---------------------------------------------------------------------------------------------------------------
- # Получение одной записи с параметрами
- sub get_record_sql_param {
- my ($db, $sql, @params) = @_;
- return unless $db && $sql;
-
- # Добавляем LIMIT только если его еще нет в запросе
- if ($sql !~ /\bLIMIT\s+\d+/i && $sql !~ /\bFETCH\s+FIRST\s+\d+/i) {
- $sql .= ' LIMIT 1';
- }
-
- return _execute_param($db, $sql, \@params, { mode => 'single' });
- }
- #---------------------------------------------------------------------------------------------------------------
- # Получение нескольких записей с параметрами
- sub get_records_sql_param {
- my ($db, $sql, @params) = @_;
- return unless $db && $sql;
- return _execute_param($db, $sql, \@params, { mode => 'array' });
- }
- #---------------------------------------------------------------------------------------------------------------
- # Получение скалярного значения с параметрами
- sub get_scalar_sql_param {
- my ($db, $sql, @params) = @_;
- return unless $db && $sql;
-
- # Добавляем LIMIT только если его еще нет в запросе
- if ($sql !~ /\bLIMIT\s+\d+/i && $sql !~ /\bFETCH\s+FIRST\s+\d+/i) {
- $sql .= ' LIMIT 1';
- }
-
- return _execute_param($db, $sql, \@params, { mode => 'scalar' });
- }
- #---------------------------------------------------------------------------------------------------------------
- # Получение количества записей с параметрами
- sub get_count_sql_param {
- my ($db, $sql, @params) = @_;
- return 0 unless $db && $sql;
-
- # Если это простой SELECT COUNT(*), используем скаляр
- if ($sql =~ /SELECT\s+COUNT\(/i) {
- return _execute_param($db, $sql, \@params, { mode => 'scalar' });
- }
-
- # Иначе получаем запись и извлекаем count
- my $record = get_record_sql_param($db, $sql, @params);
- return $record ? $record->{count} || $record->{cnt} || $record->{total} || 0 : 0;
- }
- #---------------------------------------------------------------------------------------------------------------
- # Обновленная функция get_option с параметризованными запросами (опционально)
- sub get_option_safe {
- my $db = shift;
- my $option_id = shift;
- return if (!$option_id);
- return if (!$db);
-
- my $sql = q{
- SELECT
- COALESCE(c.value, co.default_value) as value,
- co.type
- FROM config_options co
- LEFT JOIN config c ON c.option_id = co.id AND c.option_id = ?
- WHERE co.id = ?
- LIMIT 1
- };
-
- my $record = get_record_sql_param($db, $sql, $option_id, $option_id);
-
- unless ($record) {
- log_error("Option ID $option_id not found in config_options table");
- return;
- }
-
- my $result = $record->{value};
-
- # Приводим к правильному типу
- if ($record->{type} =~ /^(int|bool)/i) {
- $result = $result * 1;
- }
-
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_count_records {
- my $db = shift;
- my $table = shift;
- my $filter = shift;
- my $result = 0;
- return $result if (!$db);
- return $result if (!$table);
- my $sSQL='SELECT COUNT(*) as rec_cnt FROM '.$table;
- if ($filter) { $sSQL=$sSQL." WHERE ".$filter; }
- my $record = get_record_sql($db,$sSQL);
- if ($record->{rec_cnt}) { $result = $record->{rec_cnt}; }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_id_record {
- my $db = shift;
- my $table = shift;
- my $filter = shift;
- my $result = 0;
- return $result if (!$db);
- return $result if (!$table);
- my $record = get_record_sql($db,"SELECT id FROM $table WHERE $filter");
- if ($record->{id}) { $result = $record->{id}; }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_records_sql {
- my $db = shift;
- my $table = shift;
- my @result;
- return @result if (!$db);
- return @result if (!$table);
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return 0;
- }
- my $list = $db->prepare( $table ) or die "Unable to prepare $table:" . $db->errstr;
- $list->execute() or die "Unable to execute $table: " . $db->errstr;
- while(my $row_ref = $list->fetchrow_hashref()) { push(@result,$row_ref); }
- $list->finish();
- return @result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_record_sql {
- my $db = shift;
- my $tsql = shift;
- my @result;
- return @result if (!$db);
- return @result if (!$tsql);
- $tsql.=' LIMIT 1';
- my $row_ref;
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- eval {
- my $list = $db->prepare($tsql) or die "Unable to prepare $tsql: " . $db->errstr;
- $list->execute() or die "Unable to execute $tsql: " . $db->errstr;
- $row_ref = $list->fetchrow_hashref();
- $list->finish();
- };
- if ($@) {
- log_error("Error apply sql: $tsql err:".$@);
- die "Error apply sql: $tsql";
- }
- return $row_ref;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_diff_rec {
- my $db = shift;
- my $table = shift;
- my $value = shift;
- my $filter = shift;
- return if (!$db);
- return if (!$table);
- return if (!$filter);
- my $old_value = get_record_sql($db,"SELECT * FROM $table WHERE $filter");
- my $result;
- foreach my $field (keys %$value) {
- if (!$value->{$field}) { $value->{$field}=''; }
- if (!$old_value->{$field}) { $old_value->{$field}=''; }
- if ($value->{$field}!~/^$old_value->{$field}$/) { $result->{$field} = "$value->{$field} [ old: " . $old_value->{$field} . "]"; }
- }
- return hast_to_txt($result);
- }
- #---------------------------------------------------------------------------------------------------------------
- sub update_record {
- my $db = shift;
- my $table = shift;
- my $record = shift;
- my $filter = shift;
- return if (!$db);
- return if (!$table);
- return if (!$filter);
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- my $old_record = get_record_sql($db,"SELECT * FROM $table WHERE $filter");
- my $diff='';
- my $change_str='';
- my $found_changed=0;
- my $rec_id = 0;
- my $dns_changed = 0;
- if ($table eq "user_auth") {
- $rec_id = $old_record->{'id'} if ($old_record->{'id'});
- #disable update field 'created_by'
- if ($old_record->{'created_by'} and exists ($record->{'created_by'})) { delete $record->{'created_by'}; }
- foreach my $field (keys %$record) {
- if (exists $acl_fields{$field}) { $record->{changed}="1"; }
- if (exists $dhcp_fields{$field}) { $record->{dhcp_changed}="1"; }
- if (exists $dns_fields{$field}) { $dns_changed=1; }
- }
- }
- foreach my $field (keys %$record) {
- if (!defined $record->{$field}) { $record->{$field}=''; }
- if (!defined $old_record->{$field}) { $old_record->{$field}=''; }
- my $old_value = quotemeta($old_record->{$field});
- my $new_value = $record->{$field};
- $new_value=~s/\'//g;
- $new_value=~s/\"//g;
- if ($new_value!~/^$old_value$/) {
- $diff = $diff." $field => $record->{$field} (old: $old_record->{$field}),";
- # Разные методы экранирования для разных БД
- if ($config_ref{DBTYPE} eq 'mysql') {
- $change_str = $change_str." $field=".$db->quote($record->{$field}).",";
- } else {
- $change_str = $change_str." \"$field\"=".$db->quote($record->{$field}).",";
- }
- $found_changed++;
- }
- }
- if ($found_changed) {
- $change_str=~s/\,$//;
- $diff=~s/\,$//;
- if ($table eq 'user_auth') {
- $change_str .= ", changed_time='".GetNowTime()."'";
- if ($dns_changed) {
- my $del_dns;
- if ($old_record->{'dns_name'} and $old_record->{'ip'} and !$old_record->{'dns_ptr_only'} and $old_record->{'dns_name'}!~/\.$/) {
- $del_dns->{'name_type'}='A';
- $del_dns->{'name'}=$old_record->{'dns_name'};
- $del_dns->{'value'}=$old_record->{'ip'};
- $del_dns->{'type'}='del';
- if ($rec_id) { $del_dns->{'auth_id'}=$rec_id; }
- insert_record($db,'dns_queue',$del_dns);
- }
- if ($old_record->{'dns_name'} and $old_record->{'ip'} and $old_record->{'dns_ptr_only'} and $old_record->{'dns_name'}!~/\.$/) {
- $del_dns->{'name_type'}='PTR';
- $del_dns->{'name'}=$old_record->{'dns_name'};
- $del_dns->{'value'}=$old_record->{'ip'};
- $del_dns->{'type'}='del';
- if ($rec_id) { $del_dns->{'auth_id'}=$rec_id; }
- insert_record($db,'dns_queue',$del_dns);
- }
- my $new_dns;
- my $dns_rec_ip = $old_record->{ip};
- my $dns_rec_name = $old_record->{dns_name};
- if ($record->{'dns_name'}) { $dns_rec_name = $record->{'dns_name'}; }
- if ($record->{'ip'}) { $dns_rec_ip = $record->{'ip'}; }
- if ($dns_rec_name and $dns_rec_ip and !$record->{'dns_ptr_only'} and $record->{'dns_name'}!~/\.$/) {
- $new_dns->{'name_type'}='A';
- $new_dns->{'name'}=$dns_rec_name;
- $new_dns->{'value'}=$dns_rec_ip;
- $new_dns->{'type'}='add';
- if ($rec_id) { $new_dns->{'auth_id'}=$rec_id; }
- insert_record($db,'dns_queue',$new_dns);
- }
- if ($dns_rec_name and $dns_rec_ip and $record->{'dns_ptr_only'} and $record->{'dns_name'}!~/\.$/) {
- $new_dns->{'name_type'}='PTR';
- $new_dns->{'name'}=$dns_rec_name;
- $new_dns->{'value'}=$dns_rec_ip;
- $new_dns->{'type'}='add';
- if ($rec_id) { $new_dns->{'auth_id'}=$rec_id; }
- insert_record($db,'dns_queue',$new_dns);
- }
- }
- }
- if ($table eq 'user_auth_alias') {
- if ($dns_changed) {
- my $del_dns;
- if ($old_record->{'alias'} and $old_record->{'alias'}!~/\.$/) {
- $del_dns->{'name_type'}='CNAME';
- $del_dns->{'name'}=$old_record->{'alias'};
- $del_dns->{'type'}='del';
- $del_dns->{'value'}=get_dns_name($db,$old_record->{auth_id});
- $del_dns->{'auth_id'}=$old_record->{auth_id};
- insert_record($db,'dns_queue',$del_dns);
- }
- my $new_dns;
- my $dns_rec_name = $old_record->{alias};
- if ($record->{'alias'}) { $dns_rec_name = $record->{'alias'}; }
- if ($dns_rec_name and $record->{'alias'}!~/\.$/) {
- $new_dns->{'name_type'}='CNAME';
- $new_dns->{'name'}=$dns_rec_name;
- $new_dns->{'type'}='add';
- $new_dns->{'value'}=get_dns_name($db,$old_record->{auth_id});
- $new_dns->{'auth_id'}=$rec_id;
- insert_record($db,'dns_queue',$new_dns);
- }
- }
- }
- my $sSQL = "UPDATE $table SET $change_str WHERE $filter";
- db_log_debug($db,'Change table '.$table.' for '.$filter.' set: '.$diff, $rec_id);
- do_sql($db,$sSQL);
- }
- return 1;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub insert_record {
- my $db = shift;
- my $table = shift;
- my $record = shift;
- return if (!$db);
- return if (!$table);
- my $change_str='';
- my $fields='';
- my $values='';
- my $new_str='';
- my $rec_id = 0;
- my $dns_changed = 0;
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- if ($table eq "user_auth") {
- foreach my $field (keys %$record) {
- if (exists $acl_fields{$field}) { $record->{changed}="1"; }
- if (exists $dhcp_fields{$field}) { $record->{dhcp_changed}="1"; }
- if (exists $dns_fields{$field}) { $dns_changed=1; }
- }
- }
- foreach my $field (keys %$record) {
- if (!defined $record->{$field}) { $record->{$field}=''; }
- my $new_value = $record->{$field};
- $new_value=~s/\'//g;
- $new_value=~s/\"//g;
- $record->{$field} = $new_value;
- # Разные методы экранирования имен полей для разных БД
- if ($config_ref{DBTYPE} eq 'mysql') {
- $fields = $fields."$field,";
- } else {
- $fields = $fields."\"$field\",";
- }
- $values = $values." ".$db->quote($record->{$field}).",";
- $new_str = $new_str." $field => $record->{$field},";
- }
- $fields=~s/,$//;
- $values=~s/,$//;
- $new_str=~s/,$//;
- my $sSQL = "INSERT INTO $table($fields) VALUES($values)";
- my $result = do_sql($db,$sSQL);
- if ($result) {
- $rec_id = $result if ($table eq "user_auth");
- $new_str='id: '.$result.' '.$new_str;
- if ($table eq 'user_auth_alias' and $dns_changed) {
- if ($record->{'alias'} and $record->{'alias'}!~/\.$/) {
- my $add_dns;
- $add_dns->{'name_type'}='CNAME';
- $add_dns->{'name'}=$record->{'alias'};
- $add_dns->{'value'}=get_dns_name($db,$record->{'auth_id'});
- $add_dns->{'type'}='add';
- $add_dns->{'auth_id'}=$record->{'auth_id'};
- insert_record($db,'dns_queue',$add_dns);
- }
- }
- if ($table eq 'user_auth' and $dns_changed) {
- if ($record->{'dns_name'} and $record->{'ip'} and $dns_changed and !$record->{'dns_ptr_only'} and $record->{'dns_name'}!~/\.$/) {
- my $add_dns;
- $add_dns->{'name_type'}='A';
- $add_dns->{'name'}=$record->{'dns_name'};
- $add_dns->{'value'}=$record->{'ip'};
- $add_dns->{'type'}='add';
- $add_dns->{'auth_id'}=$result;
- insert_record($db,'dns_queue',$add_dns);
- }
- if ($record->{'dns_name'} and $record->{'ip'} and $dns_changed and $record->{'dns_ptr_only'} and $record->{'dns_name'}!~/\.$/) {
- my $add_dns;
- $add_dns->{'name_type'}='PTR';
- $add_dns->{'name'}=$record->{'dns_name'};
- $add_dns->{'value'}=$record->{'ip'};
- $add_dns->{'type'}='add';
- $add_dns->{'auth_id'}=$result;
- insert_record($db,'dns_queue',$add_dns);
- }
- }
- }
- db_log_debug($db,'Add record to table '.$table.' '.$new_str,$rec_id);
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub delete_record {
- my $db = shift;
- my $table = shift;
- my $filter = shift;
- return if (!$db);
- return if (!$table);
- return if (!$filter);
- my $rec_id = 0;
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- my $old_record = get_record_sql($db,"SELECT * FROM $table WHERE $filter");
- my $diff='';
- foreach my $field (keys %$old_record) {
- if (!$old_record->{$field}) { $old_record->{$field}=''; }
- $diff = $diff." $field => $old_record->{$field},";
- }
- $diff=~s/,$//;
- if ($table eq 'user_auth') {
- $rec_id = $old_record->{'id'} if ($old_record->{'id'});
- }
- db_log_debug($db,'Delete record from table '.$table.' value: '.$diff, $rec_id);
- #never delete user ip record!
- if ($table eq 'user_auth') {
- my $sSQL = "UPDATE user_auth SET changed=1, deleted=1, changed_time='".GetNowTime()."' WHERE ".$filter;
- my $ret = do_sql($db,$sSQL);
- if ($old_record->{'dns_name'} and $old_record->{'ip'} and !$old_record->{'dns_ptr_only'} and $old_record->{'dns_name'}!~/\.$/) {
- my $del_dns;
- $del_dns->{'name_type'}='A';
- $del_dns->{'name'}=$old_record->{'dns_name'};
- $del_dns->{'value'}=$old_record->{'ip'};
- $del_dns->{'type'}='del';
- $del_dns->{'auth_id'}=$old_record->{'id'};
- insert_record($db,'dns_queue',$del_dns);
- }
- if ($old_record->{'dns_name'} and $old_record->{'ip'} and $old_record->{'dns_ptr_only'} and $old_record->{'dns_name'}!~/\.$/) {
- my $del_dns;
- $del_dns->{'name_type'}='PTR';
- $del_dns->{'name'}=$old_record->{'dns_name'};
- $del_dns->{'value'}=$old_record->{'ip'};
- $del_dns->{'type'}='del';
- $del_dns->{'auth_id'}=$old_record->{'id'};
- insert_record($db,'dns_queue',$del_dns);
- }
- return $ret;
- }
- if ($table eq 'user_list' and $old_record->{'permanent'}) { return; }
- if ($table eq 'user_auth_alias') {
- if ($old_record->{'alias'} and $old_record->{'auth_id'} and $old_record->{'alias'}!~/\.$/) {
- my $del_dns;
- $del_dns->{'name_type'}='CNAME';
- $del_dns->{'name'}=$old_record->{'alias'};
- $del_dns->{'value'}=get_dns_name($db,$old_record->{'auth_id'});
- $del_dns->{'type'}='del';
- $del_dns->{'auth_id'}=$old_record->{'auth_id'};
- insert_record($db,'dns_queue',$del_dns);
- }
- }
- my $sSQL = "DELETE FROM ".$table." WHERE ".$filter;
- return do_sql($db,$sSQL);
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_option {
- my $db=shift;
- my $option_id=shift;
- return if (!$option_id);
- return if (!$db);
- my $default_option = get_record_sql($db,'SELECT * FROM config_options WHERE id='.$option_id);
- my $config_options = get_record_sql($db,'SELECT * FROM config WHERE option_id='.$option_id);
- my $result;
- if (!$config_options) {
- if ($default_option->{'type'}=~/^(int|bool)/i) { $result = $default_option->{'default_value'}*1; };
- if ($default_option->{'type'}=~/^(string|text)/i) { $result = $default_option->{'default_value'}; }
- if ($default_option->{'type'}=~/^list/i) { $result = $default_option->{'default_value'}; }
- return $result;
- }
- $result = $config_options->{'value'};
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub init_option {
- my $db=shift;
- $last_refresh_config = time();
- $config_ref{version}='';
- my $version_record = get_record_sql($db,"SELECT version FROM version WHERE version is NOT NULL");
- if ($version_record) { $config_ref{version}=$version_record->{version}; }
- $config_ref{self_ip} = '127.0.0.1';
- if ($DBHOST ne '127.0.0.1') {
- my $ip_route = qx(ip r get $DBHOST 2>&1 | head -1);
- if ($? == 0) {
- if ($ip_route =~ /src\s+(\d+\.\d+\.\d+\.\d+)/) { $config_ref{self_ip} = $1; }
- }
- }
- $config_ref{dbh}=$db;
- $config_ref{save_detail}=get_option($db,23);
- $config_ref{add_unknown_user}=get_option($db,22);
- $config_ref{dhcp_server}=get_option($db,5);
- $config_ref{snmp_default_version}=get_option($db,9);
- $config_ref{snmp_default_community}=get_option($db,11);
- $config_ref{KB}=get_option($db,1);
- if ($config_ref{KB} ==0) { $config_ref{KB}=1000; }
- if ($config_ref{KB} ==1) { $config_ref{KB}=1024; }
- $config_ref{admin_email}=get_option($db,21);
- $config_ref{sender_email}=get_option($db,52);
- $config_ref{send_email}=get_option($db,51);
- $config_ref{history}=get_option($db,26);
- $config_ref{history_dhcp}=get_option($db,27);
- $config_ref{router_login}=get_option($db,28);
- $config_ref{router_password}=get_option($db,29);
- $config_ref{router_port}=get_option($db,30);
- $config_ref{org_name}=get_option($db,32);
- $config_ref{domain_name}=get_option($db,33);
- $config_ref{connections_history}=get_option($db,35);
- $config_ref{debug}=get_option($db,34);
- $config_ref{log_level} = get_option($db,53);
- if ($config_ref{debug}) { $config_ref{log_level} = 255; }
- $config_ref{urgent_sync}=get_option($db,50);
- $config_ref{ignore_hotspot_dhcp_log} = get_option($db,44);
- $config_ref{ignore_update_dhcp_event} = get_option($db,45);
- $config_ref{update_hostname_from_dhcp} = get_option($db,46);
- $config_ref{history_log_day}=get_option($db,47);
- $config_ref{history_syslog_day} = get_option($db,48);
- $config_ref{history_trafstat_day} = get_option($db,49);
- $config_ref{enable_quotes} = get_option($db,54);
- $config_ref{netflow_step} = get_option($db,55);
- $config_ref{traffic_ipstat_history} = get_option($db,56);
- $config_ref{nagios_url} = get_option($db,57);
- $config_ref{cacti_url} = get_option($db,58);
- $config_ref{torrus_url} = get_option($db,59);
- $config_ref{wiki_url} = get_option($db,60);
- $config_ref{stat_url} = get_option($db,62);
- $config_ref{wiki_path} = get_option($db,61);
- $config_ref{auto_mac_rule} = get_option($db,64);
- #network configuration mode
- $config_ref{config_mode}=get_option($db,68);
- #auto clean old user record
- $config_ref{clean_empty_user}=get_option($db,69);
- #dns_server_type
- $config_ref{dns_server}=get_option($db,3);
- $config_ref{dns_server_type}=get_option($db,70);
- $config_ref{enable_dns_updates}=get_option($db,71);
- #$save_detail = 1; id=23
- $save_detail=get_option($db,23);
- #$add_unknown_user = 1; id=22
- $add_unknown_user=get_option($db,22);
- #$dns_server='192.168.2.12'; id=3
- $dns_server=get_option($db,3);
- #$dhcp_server='192.168.2.12'; id=5
- $dhcp_server=get_option($db,5);
- #$snmp_default_version='2'; id=9
- $snmp_default_version=get_option($db,9);
- #$snmp_default_community='public'; id=11
- $snmp_default_community=get_option($db,11);
- #$KB=1024; id=1
- $KB=$config_ref{KB};
- #$admin_email; id=21
- $admin_email=get_option($db,21);
- #sender email
- $sender_email=get_option($db,52);
- #send email
- $send_email=get_option($db,51);
- #$history=15; id=26
- $history=get_option($db,26);
- #$history_dhcp=7; id=27
- $history_dhcp=get_option($db,27);
- #$router_login="admin"; id=28
- $router_login=get_option($db,28);
- #$router_password="admin"; id=29
- $router_password=get_option($db,29);
- #$router_port=23; id=30
- $router_port=get_option($db,30);
- #32
- $org_name=get_option($db,32);
- #33
- $domain_name=get_option($db,33);
- #35
- $connections_history=get_option($db,35);
- #debug
- $debug=get_option($db,34);
- #log level
- $log_level = get_option($db,53);
- if ($debug) { $log_level = 255; }
- #urgent sync access
- $urgent_sync=get_option($db,50);
- $ignore_hotspot_dhcp_log = get_option($db,44);
- $ignore_update_dhcp_event = get_option($db,45);
- $update_hostname_from_dhcp = get_option($db,46);
- $history_log_day=get_option($db,47);
- $history_syslog_day = get_option($db,48);
- $history_trafstat_day = get_option($db,49);
- my $ou = get_record_sql($db,"SELECT id FROM OU WHERE default_users = 1");
- if (!$ou) { $default_user_ou_id = 0; } else { $default_user_ou_id = $ou->{'id'}; }
- $ou = get_record_sql($db,"SELECT id FROM OU WHERE default_hotspot = 1 ");
- if (!$ou) { $default_hotspot_ou_id = $default_user_ou_id; } else { $default_hotspot_ou_id = $ou->{'id'}; }
- @subnets=get_records_sql($db,'SELECT * FROM subnets ORDER BY ip_int_start');
- if (defined $office_networks) { undef $office_networks; }
- if (defined $free_networks) { undef $free_networks; }
- if (defined $vpn_networks) { undef $vpn_networks; }
- if (defined $hotspot_networks) { undef $hotspot_networks; }
- if (defined $all_networks) { undef $all_networks; }
- $office_networks = new Net::Patricia;
- $free_networks = new Net::Patricia;
- $vpn_networks = new Net::Patricia;
- $hotspot_networks = new Net::Patricia;
- $all_networks = new Net::Patricia;
- @office_network_list=();
- @free_network_list=();
- @free_network_list=();
- @vpn_network_list=();
- @hotspot_network_list=();
- @all_network_list=();
- foreach my $net (@subnets) {
- next if (!$net->{subnet});
- $subnets_ref{$net->{subnet}}=$net;
- if ($net->{office}) {
- push(@office_network_list,$net->{subnet});
- $office_networks->add_string($net->{subnet},$net);
- }
- if ($net->{free}) {
- push(@free_network_list,$net->{subnet});
- $free_networks->add_string($net->{subnet},$net);
- }
- if ($net->{vpn}) {
- push(@vpn_network_list,$net->{subnet});
- $vpn_networks->add_string($net->{subnet},$net);
- }
- if ($net->{hotspot}) {
- push(@hotspot_network_list,$net->{subnet});
- push(@all_network_list,$net->{subnet});
- $hotspot_networks->add_string($net->{subnet},$net);
- }
- push(@all_network_list,$net->{subnet});
- $all_networks->add_string($net->{subnet},$net);
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub Set_Variable {
- my $db = shift;
- my $name = shift || $MY_NAME;
- my $value = shift || $$;
- my $timeshift = shift || 60;
- Del_Variable($db,$name);
- my $clean_variables = time() + $timeshift;
- my ($sec,$min,$hour,$day,$month,$year,$zone) = localtime($clean_variables);
- $month++;
- $year += 1900;
- my $clean_str=sprintf "%04d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec;
- my $clean_variables_date=$db->quote($clean_str);
- do_sql($db,"INSERT INTO variables(name,value,clear_time) VALUES('".$name."','".$value."',".$clean_variables_date.");");
- }
- #---------------------------------------------------------------------------------------------------------------
- sub Get_Variable {
- my $db = shift;
- my $name = shift || $MY_NAME;
- my $variable=get_record_sql($db,'SELECT value FROM variables WHERE name=\''.$name.'\'');
- if ($variable and $variable->{'value'}) { return $variable->{'value'}; }
- return;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub Del_Variable {
- my $db = shift;
- my $name = shift || $MY_NAME;
- do_sql($db,"DELETE FROM variables WHERE name='".$name."';");
- }
- #---------------------------------------------------------------------------------------------------------------
- sub clean_variables {
- my $db = shift;
- #clean temporary variables
- my $clean_variables = time();
- my ($sec,$min,$hour,$day,$month,$year,$zone) = localtime($clean_variables);
- $month++;
- $year += 1900;
- my $now_str=sprintf "%04d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec;
- my $clean_variables_date=$db->quote($now_str);
- do_sql($db,"DELETE FROM variables WHERE clear_time<=$clean_variables_date");
- #clean old AD computer cache
- my $now = DateTime->now(time_zone=>'local');
- my $day_dur = DateTime::Duration->new( days => 1 );
- my $clean_date = $now - $day_dur;
- my $clean_str = $db->quote($clean_date->ymd("-")." 00:00:00");
- do_sql($db,"DELETE FROM ad_comp_cache WHERE last_found<=$clean_str");
- }
- #---------------------------------------------------------------------------------------------------------------
- #skip init for upgrade
- if ($MY_NAME!~/upgrade.pl/) {
- $dbh=init_db();
- init_option($dbh);
- clean_variables($dbh);
- Set_Variable($dbh);
- }
- 1;
- }
|