| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088 |
- package eyelib::database;
- #
- # Copyright (C) Roman Dmitriev, rnd@rajven.ru
- #
- # commit example
- # Начинаем транзакцию вручную
- #$db->{AutoCommit} = 0;
- #eval {
- # for my $row (@rows) {
- # insert_record($db, 'user_auth', $row);
- # insert_record($db, 'user_auth_alias', $row2);
- # }
- # $db->commit();
- #};
- #if ($@) {
- # eval { $db->rollback(); };
- # die "Migration failed: $@";
- #}
- #$db->{AutoCommit} = 1;
- use warnings FATAL => 'all';
- use feature ':5.20';
- 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);
- use Text::CSV;
- our @ISA = qw(Exporter);
- our @EXPORT = qw(
- update_records
- get_office_subnet
- get_notify_subnet
- is_hotspot
- get_queue
- get_group
- get_subnet_description
- get_filter_instance_description
- get_vendor_name
- get_ou
- get_device_name
- get_device_model
- get_device_model_name
- get_building
- get_filter
- get_login
- StrToIp
- IpToStr
- prepare_audit_message
- 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
- normalize_value
- get_table_columns
- init_db
- do_sql
- _execute_param
- do_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
- is_system_ou
- Set_Variable
- Get_Variable
- Del_Variable
- clean_variables
- build_db_schema
- $add_rules
- $L_WARNING
- $L_INFO
- $L_DEBUG
- $L_ERROR
- $L_VERBOSE
- %db_schema
- );
- 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',
- );
- our %db_schema;
- #---------------------------------------------------------------------------------------------------------------
- sub get_office_subnet {
- my ($db, $ip) = @_;
- return undef unless $db && defined $ip;
- my @rows = get_records_sql(
- $db,
- "SELECT * FROM subnets WHERE office = 1 AND LENGTH(subnet) > 0"
- );
- return undef unless @rows;
- my $pat = Net::Patricia->new;
- for my $row (@rows) {
- next unless defined $row->{subnet};
- # Защита от некорректных подсетей в БД
- eval { $pat->add_string($row->{subnet}, $row); 1 } or next;
- }
- return $pat->match_string($ip);
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_notify_subnet {
- my $db = shift;
- my $ip = shift;
- my $notify_flag = get_office_subnet($db,$ip);
- if ($notify_flag) { return $notify_flag->{notify}; }
- return 0;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub is_hotspot {
- my ($db, $ip) = @_;
- return 0 unless $db && defined $ip;
- my @subnets = get_records_sql(
- $db,
- "SELECT subnet FROM subnets WHERE hotspot = 1 AND LENGTH(subnet) > 0"
- );
- my $pat = Net::Patricia->new;
- for my $row (@subnets) {
- $pat->add_string($row->{subnet}) if defined $row->{subnet};
- }
- return $pat->match_string($ip) ? 1 : 0;
- }
- #---------------------------------------------------------------------------------------------------------------
- # Вспомогательная функция для проверки "пустого" значения
- sub _is_empty {
- my ($val) = @_;
- return !defined $val || $val eq '';
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_queue {
- my ($dbh, $queue_value) = @_;
- return '' if _is_empty($queue_value);
- my $queue = get_record_sql($dbh, "SELECT queue_name FROM queue_list WHERE id = ?", $queue_value);
- return $queue->{queue_name} // '';
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_group {
- my ($dbh, $group_id) = @_;
- return '' if _is_empty($group_id);
- my $group = get_record_sql($dbh, "SELECT group_name FROM group_list WHERE id = ?", $group_id);
- return $group->{group_name} // '';
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_subnet_description {
- my ($dbh, $subnet_id) = @_;
- return '' if _is_empty($subnet_id);
- my $subnet = get_record_sql($dbh, "SELECT * FROM subnets WHERE id = ?", $subnet_id);
- return '' unless $subnet;
- my $desc = $subnet->{description} // '';
- return "$subnet->{subnet} ($desc)";
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_filter_instance_description {
- my ($dbh, $instance_id) = @_;
- return '' if _is_empty($instance_id);
- my $instance = get_record_sql($dbh, "SELECT * FROM filter_instances WHERE id = ?", $instance_id);
- return '' unless $instance;
- my $desc = $instance->{description} // '';
- return "$instance->{name} ($desc)";
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_vendor_name {
- my ($dbh, $v_id) = @_;
- return '' if _is_empty($v_id);
- my $vendor = get_record_sql($dbh, "SELECT name FROM vendors WHERE id = ?", $v_id);
- return $vendor->{name} // '';
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_ou {
- my ($dbh, $ou_value) = @_;
- return undef if _is_empty($ou_value);
- my $ou_name = get_record_sql($dbh, "SELECT ou_name FROM ou WHERE id = ?", $ou_value);
- return $ou_name ? $ou_name->{ou_name} : undef;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_device_name {
- my ($dbh, $device_id) = @_;
- return undef if _is_empty($device_id);
- my $dev = get_record_sql($dbh, "SELECT device_name FROM devices WHERE id = ?", $device_id);
- return $dev ? $dev->{device_name} : undef;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_device_model {
- my ($dbh, $model_value) = @_;
- return undef if _is_empty($model_value);
- my $model_name = get_record_sql($dbh, "SELECT model_name FROM device_models WHERE id = ?", $model_value);
- return $model_name ? $model_name->{model_name} : undef;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_device_model_name {
- my ($dbh, $model_value) = @_;
- return '' if _is_empty($model_value);
- my $row = get_record_sql($dbh, "SELECT M.id, M.model_name, V.name FROM device_models M, vendors V WHERE M.vendor_id = V.id AND M.id = ?", $model_value);
- return '' unless $row;
- my $vendor = $row->{name} // '';
- my $model = $row->{model_name} // '';
- return "$vendor $model";
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_building {
- my ($dbh, $building_value) = @_;
- return undef if _is_empty($building_value);
- my $building_name = get_record_sql($dbh, "SELECT name FROM building WHERE id = ?", $building_value);
- return $building_name ? $building_name->{name} : undef;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_filter {
- my ($dbh, $filter_value) = @_;
- return '' if _is_empty($filter_value);
- my $filter = get_record_sql($dbh, "SELECT name FROM filter_list WHERE id = ?", $filter_value);
- return $filter->{name} // '';
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_login {
- my ($dbh, $user_id) = @_;
- return '' if _is_empty($user_id);
- my $login = get_record_sql($dbh, "SELECT login FROM user_list WHERE id = ?", $user_id);
- return $login->{login} // '';
- }
- #---------------------------------------------------------------------------------------------------------------
- sub prepare_audit_message {
- my ($dbh, $table, $old_data, $new_data, $record_id, $operation) = @_;
- # === 1. Конфигурация отслеживаемых таблиц ===
- my %audit_config = (
- 'auth_rules' => {
- summary => ['rule'],
- fields => ['user_id', 'ou_id', 'rule_type', 'rule', 'description']
- },
- 'building' => {
- summary => ['name'],
- fields => ['name', 'description']
- },
- 'customers' => {
- summary => ['login'],
- fields => ['login', 'description', 'rights']
- },
- 'devices' => {
- summary => ['device_name'],
- fields => [
- 'device_type', 'device_model_id', 'vendor_id', 'device_name', 'building_id',
- 'ip', 'login', 'protocol', 'control_port', 'port_count', 'sn',
- 'description', 'snmp_version', 'snmp3_auth_proto', 'snmp3_priv_proto',
- 'snmp3_user_rw', 'snmp3_user_ro', 'community', 'rw_community',
- 'discovery', 'netflow_save', 'user_acl', 'dhcp', 'nagios',
- 'active', 'queue_enabled', 'connected_user_only', 'user_id'
- ]
- },
- 'device_filter_instances' => {
- summary => [],
- fields => ['instance_id', 'device_id']
- },
- 'device_l3_interfaces' => {
- summary => ['name'],
- fields => ['device_id', 'snmpin', 'interface_type', 'name']
- },
- 'device_models' => {
- summary => ['model_name'],
- fields => ['model_name', 'vendor_id', 'poe_in', 'poe_out', 'nagios_template']
- },
- 'device_ports' => {
- summary => ['port', 'ifname'],
- fields => [
- 'device_id', 'snmp_index', 'port', 'ifname', 'port_name', 'description',
- 'target_port_id', 'auth_id', 'last_mac_count', 'uplink', 'nagios',
- 'skip', 'vlan', 'tagged_vlan', 'untagged_vlan', 'forbidden_vlan'
- ]
- },
- 'filter_instances' => {
- summary => ['name'],
- fields => ['name', 'description']
- },
- 'filter_list' => {
- summary => ['name'],
- fields => ['name', 'description', 'proto', 'dst', 'dstport', 'srcport', 'filter_type']
- },
- 'gateway_subnets' => {
- summary => [],
- fields => ['device_id', 'subnet_id']
- },
- 'group_filters' => {
- summary => [],
- fields => ['group_id', 'filter_id', 'rule_order', 'action']
- },
- 'group_list' => {
- summary => ['group_name'],
- fields => ['instance_id', 'group_name', 'description']
- },
- 'ou' => {
- summary => ['ou_name'],
- fields => [
- 'ou_name', 'description', 'default_users', 'default_hotspot',
- 'nagios_dir', 'nagios_host_use', 'nagios_ping', 'nagios_default_service',
- 'enabled', 'filter_group_id', 'queue_id', 'dynamic', 'life_duration', 'parent_id'
- ]
- },
- 'queue_list' => {
- summary => ['queue_name'],
- fields => ['queue_name', 'download', 'upload']
- },
- 'subnets' => {
- summary => ['subnet'],
- fields => [
- 'subnet', 'vlan_tag', 'ip_int_start', 'ip_int_stop', 'dhcp_start', 'dhcp_stop',
- 'dhcp_lease_time', 'gateway', 'office', 'hotspot', 'vpn', 'free', 'dhcp',
- 'static', 'dhcp_update_hostname', 'discovery', 'notify', 'description'
- ]
- },
- 'user_auth' => {
- summary => ['ip', 'dns_name'],
- fields => [
- 'user_id', 'ou_id', 'ip', 'save_traf', 'enabled', 'dhcp', 'filter_group_id',
- 'dynamic', 'end_life', 'description', 'dns_name', 'dns_ptr_only', 'wikiname',
- 'dhcp_acl', 'queue_id', 'mac', 'dhcp_option_set', 'blocked', 'day_quota',
- 'month_quota', 'device_model_id', 'firmware', 'client_id', 'nagios',
- 'nagios_handler', 'link_check', 'deleted'
- ]
- },
- 'user_auth_alias' => {
- summary => ['alias'],
- fields => ['auth_id', 'alias', 'description']
- },
- 'user_list' => {
- summary => ['login'],
- fields => [
- 'login', 'description', 'enabled', 'blocked', 'deleted', 'ou_id',
- 'device_id', 'filter_group_id', 'queue_id', 'day_quota', 'month_quota', 'permanent'
- ]
- },
- 'vendors' => {
- summary => ['name'],
- fields => ['name']
- }
- );
- return undef unless exists $audit_config{$table};
- my $summary_fields = $audit_config{$table}{summary};
- my $monitored_fields = $audit_config{$table}{fields};
- # === 2. Нормализация данных и определение изменений ===
- my %changes;
- if ($operation eq 'insert') {
- for my $field (@$monitored_fields) {
- if (exists $new_data->{$field}) {
- $changes{$field} = { old => undef, new => $new_data->{$field} };
- }
- }
- }
- elsif ($operation eq 'delete') {
- for my $field (@$monitored_fields) {
- if (exists $old_data->{$field}) {
- $changes{$field} = { old => $old_data->{$field}, new => undef };
- }
- }
- }
- elsif ($operation eq 'update') {
- $old_data //= {};
- $new_data //= {};
- for my $field (@$monitored_fields) {
- next unless exists $new_data->{$field}; # частичное обновление
- my $old_val = exists $old_data->{$field} ? $old_data->{$field} : undef;
- my $new_val = $new_data->{$field};
- my $old_str = !defined($old_val) ? '' : "$old_val";
- my $new_str = !defined($new_val) ? '' : "$new_val";
- if ($old_str ne $new_str) {
- $changes{$field} = { old => $old_val, new => $new_val };
- }
- }
- }
- return undef unless %changes;
- # === 3. Краткое описание записи ===
- my @summary_parts;
- for my $field (@$summary_fields) {
- my $val = defined($new_data->{$field}) ? $new_data->{$field}
- : (defined($old_data->{$field}) ? $old_data->{$field} : undef);
- push @summary_parts, "$val" if defined $val && $val ne '';
- }
- my $summary_label = @summary_parts
- ? '"' . join(' | ', @summary_parts) . '"'
- : "ID=$record_id";
- # === 4. Расшифровка *_id полей ===
- my %resolved_changes;
- for my $field (keys %changes) {
- my $old_resolved = resolve_reference_value($dbh, $field, $changes{$field}{old});
- my $new_resolved = resolve_reference_value($dbh, $field, $changes{$field}{new});
- $resolved_changes{$field} = { old => $old_resolved, new => $new_resolved };
- }
- # === 5. Формирование сообщения ===
- my $op_label = 'Updated';
- if ($operation eq 'insert') {
- $op_label = 'Created';
- } elsif ($operation eq 'delete') {
- $op_label = 'Deleted';
- } else {
- $op_label = ucfirst($operation);
- }
- my $message = sprintf("[%s] %s (%s) in table `%s` by %s:\n\n",
- $op_label,
- ucfirst($table),
- $summary_label,
- $table,
- $MY_NAME
- );
- for my $field (sort keys %resolved_changes) {
- my $change = $resolved_changes{$field};
- if ($operation eq 'insert') {
- if (defined $change->{new}) {
- $message .= sprintf(" %s: %s\n", $field, $change->{new});
- }
- } elsif ($operation eq 'delete') {
- if (defined $change->{old}) {
- $message .= sprintf(" %s: %s\n", $field, $change->{old});
- }
- } else { # update
- my $old_display = !defined($change->{old}) ? '[NULL]' : $change->{old};
- my $new_display = !defined($change->{new}) ? '[NULL]' : $change->{new};
- $message .= sprintf(" %s: \"%s\" → \"%s\"\n", $field, $old_display, $new_display);
- }
- }
- chomp $message;
- return $message;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub resolve_reference_value {
- my ($dbh, $field, $value) = @_;
- return undef if !defined $value || $value eq '';
- # Проверка на целое число (как в PHP)
- if ($value !~ /^[+-]?\d+$/) {
- return "$value";
- }
- my $as_int = int($value);
- if ("$as_int" ne "$value") {
- return "$value";
- }
- my $id = $as_int;
- if ($field eq 'device_id') {
- return get_device_name($dbh, $id) // "Device#$id";
- }
- elsif ($field eq 'building_id') {
- return get_building($dbh, $id) // "Building#$id";
- }
- elsif ($field eq 'user_id') {
- return get_login($dbh, $id) // "User#$id";
- }
- elsif ($field eq 'ou_id') {
- return get_ou($dbh, $id) // "OU#$id";
- }
- elsif ($field eq 'vendor_id') {
- return get_vendor_name($dbh, $id) // "Vendor#$id";
- }
- elsif ($field eq 'device_model_id') {
- return get_device_model_name($dbh, $id) // "Model#$id";
- }
- elsif ($field eq 'instance_id') {
- return get_filter_instance_description($dbh, $id) // "FilterInstance#$id";
- }
- elsif ($field eq 'subnet_id') {
- return get_subnet_description($dbh, $id) // "Subnet#$id";
- }
- elsif ($field eq 'group_id') {
- return get_group($dbh, $id) // "FilterGroup#$id";
- }
- elsif ($field eq 'filter_id') {
- return get_filter($dbh, $id) // "Filter#$id";
- }
- elsif ($field eq 'filter_group_id') {
- return get_group($dbh, $id) // "FilterGroup#$id";
- }
- elsif ($field eq 'queue_id') {
- return get_queue($dbh, $id) // "Queue#$id";
- }
- elsif ($field eq 'auth_id') {
- return 'None' if $id <= 0;
- my $sql = "
- SELECT
- COALESCE(ul.login, CONCAT('User#', ua.user_id)) AS login,
- ua.ip,
- ua.dns_name
- FROM user_auth ua
- LEFT JOIN user_list ul ON ul.id = ua.user_id
- WHERE ua.id = ?
- ";
- my $row = get_record_sql($dbh, $sql, $id);
- return "Auth#$id" unless $row;
- my @parts;
- push @parts, "login: $row->{login}" if $row->{login} && $row->{login} ne '';
- push @parts, "IP: $row->{ip}" if $row->{ip} && $row->{ip} ne '';
- push @parts, "DNS: $row->{dns_name}" if $row->{dns_name} && $row->{dns_name} ne '';
- return @parts ? join(', ', @parts) : "Auth#$id";
- }
- elsif ($field eq 'target_port_id') {
- return 'None' if $id == 0;
- my $sql = "
- SELECT CONCAT(d.device_name, '[', dp.port, ']')
- FROM device_ports dp
- JOIN devices d ON d.id = dp.device_id
- WHERE dp.id = ?
- ";
- my $name = $dbh->selectrow_array($sql, undef, $id);
- return $name // "Port#$id";
- }
- else {
- return "$value";
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub build_db_schema {
- my ($dbh) = @_;
- # Определяем тип СУБД
- my $db_type = lc($dbh->{Driver}->{Name});
- die "Unsupported database driver: $db_type"
- unless $db_type eq 'mysql' || $db_type eq 'pg';
- # Получаем имя базы данных
- my $db_name;
- if ($db_type eq 'mysql') {
- ($db_name) = $dbh->selectrow_array("SELECT DATABASE()");
- } elsif ($db_type eq 'pg') {
- ($db_name) = $dbh->selectrow_array("SELECT current_database()");
- }
- my $db_info;
- $db_info->{db_type}=$db_type;
- $db_info->{db_name}=$db_name;
- return $db_info if (exists $db_schema{$db_type}{$db_name});
- # Получаем список таблиц
- my @tables;
- if ($db_type eq 'mysql') {
- my $sth = $dbh->prepare("SHOW TABLES");
- $sth->execute();
- @tables = map { $_->[0] } @{$sth->fetchall_arrayref()};
- } elsif ($db_type eq 'pg') {
- my $sql = q{
- SELECT tablename
- FROM pg_tables
- WHERE schemaname = 'public'
- };
- my $sth = $dbh->prepare($sql);
- $sth->execute();
- @tables = map { $_->[0] } @{$sth->fetchall_arrayref()};
- }
- # Собираем схему
- for my $table (@tables) {
- my $sth = $dbh->column_info(undef, undef, $table, '%');
- while (my $col = $sth->fetchrow_hashref) {
- my $col_name = lc($col->{COLUMN_NAME});
- $db_schema{$db_type}{$db_name}{$table}{$col_name} = {
- type => $col->{TYPE_NAME} // '',
- nullable => $col->{NULLABLE} // 1,
- default => $col->{COLUMN_DEF} // undef,
- };
- }
- }
- return $db_info;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub normalize_value {
- my ($value, $col_info) = @_;
- # Если значение пустое — обрабатываем по правилам колонки
- if (!defined $value || $value eq '' || $value =~ /^(?:NULL|\\N)$/i) {
- return $col_info->{nullable} ? undef : _default_for_type($col_info);
- }
- my $type = lc($col_info->{type});
- # --- Числовые типы: приводим к числу, если выглядит как число ---
- if ($type =~ /^(?:tinyint|smallint|mediumint|int|integer|bigint|serial|bigserial)$/i) {
- # Просто конвертируем строку в число (Perl сам обрежет мусор)
- # Например: "123abc" → 123, "abc" → 0
- return 0 + $value;
- }
- # --- Булевы: приводим к 0/1 ---
- if ($type =~ /^(?:bool|boolean|bit)$/i) {
- return $value ? 1 : 0;
- }
- # --- Временные типы: оставляем как есть, но фильтруем "нулевые" даты MySQL ---
- if ($type =~ /^(?:timestamp|datetime|date|time)$/i) {
- # Это частая проблема при миграции — '0000-00-00' ломает PostgreSQL
- return undef if $value =~ /^0000-00-00/;
- return $value;
- }
- # --- Все остальные типы (строки, inet, json и т.д.) — передаём как есть ---
- return $value;
- }
- # Вспомогательная: безопасное значение по умолчанию
- sub _default_for_type {
- my ($col) = @_;
- # Используем DEFAULT, только если он простой литерал (не выражение)
- if (defined $col->{default}) {
- my $def = $col->{default};
- # Пропускаем выражения: nextval(), CURRENT_TIMESTAMP, NOW(), uuid() и т.п.
- if ($def !~ /(nextval|current_timestamp|now|uuid|auto_increment|::)/i) {
- # Убираем одинарные кавычки, если строка: 'value' → value
- if ($def =~ /^'(.*)'$/) {
- return $1;
- }
- # Если похоже на число — вернём как число
- if ($def =~ /^[+-]?\d+$/) {
- return 0 + $def;
- }
- return $def;
- }
- }
- # Фолбэк по типу
- my $type = lc($col->{type});
- if ($type =~ /^(?:tinyint|smallint|int|integer|bigint)/i) { return 0; }
- if ($type =~ /^(?:char|varchar|text)/i) { return ''; }
- if ($type =~ /^(?:timestamp|datetime)/i) { return GetNowTime(); }
- return undef;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_table_columns {
- my ($db, $table) = @_;
- my %columns;
- my $sth = $db->column_info(undef, undef, $table, '%');
- while (my $row = $sth->fetchrow_hashref) {
- my $name = lc($row->{COLUMN_NAME}); # ← приводим к нижнему регистру сразу!
- $columns{$name} = {
- type => $row->{TYPE_NAME} // '',
- nullable => $row->{NULLABLE} // 1,
- default => $row->{COLUMN_DEF} // undef,
- };
- }
- return %columns; # возвращает список: key1, val1, key2, val2...
- }
- #---------------------------------------------------------------------------------------------------------------
- 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_cached {
- my ( $sql, $data) = @_;
- my $db=init_db();
- # Запоминаем исходное состояние AutoCommit
- my $original_autocommit = $db->{AutoCommit};
- eval {
- # Выключаем AutoCommit для транзакции
- $db->{AutoCommit} = 0;
- my $sth = $db->prepare_cached($sql) or die "Unable to prepare SQL: " . $db->errstr;
- for my $params (@$data) {
- next unless @$params;
- $sth->execute(@$params) or die "Unable to execute with params [" . join(',', @$params) . "]: " . $sth->errstr;
- }
- $db->commit();
- 1;
- } or do {
- my $err = $@ || 'Unknown error';
- eval { $db->rollback() };
- warn "batch_sql_cached failed: $err";
- # Восстанавливаем AutoCommit даже при ошибке
- $db->{AutoCommit} = $original_autocommit;
- return 0;
- };
- # Восстанавливаем исходный режим AutoCommit
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 1;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub batch_db_sql_csv {
- my ($table, $data) = @_;
- return 0 unless @$data;
- # Первая строка — заголовки (имена столбцов)
- my $header_row = shift @$data;
- unless ($header_row && ref($header_row) eq 'ARRAY' && @$header_row) {
- log_error("First row must be column names (array reference)");
- return 0;
- }
- my @columns = @$header_row;
- # Теперь @$data содержит только строки данных
- my $data_rows = $data;
- # Если нет данных — только заголовок
- unless (@$data_rows) {
- log_debug("No data rows to insert, only header");
- return 1;
- }
- my $db = init_db();
- my $original_autocommit = $db->{AutoCommit};
- $db->{AutoCommit} = 0;
- if (get_db_type($db) eq 'mysql') {
- # --- MySQL: попытка LOAD DATA, fallback на INSERT ---
- log_debug("Using LOAD DATA LOCAL INFILE for MySQL");
- my $fh = File::Temp->new(UNLINK => 1);
- my $fname = $fh->filename;
- binmode($fh, ':utf8');
- my $csv = Text::CSV->new({
- binary => 1,
- quote_char => '"',
- escape_char => '"',
- sep_char => ',',
- eol => "\r\n",
- always_quote => 1,
- }) or do {
- my $err = "Cannot create Text::CSV: " . Text::CSV->error_diag();
- log_error($err);
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 0;
- };
- # Пишем заголовок
- $csv->print($fh, \@columns);
- # Пишем данные
- for my $row (@$data_rows) {
- next unless $row && ref($row) eq 'ARRAY' && @$row == @columns;
- my @vals = map { defined($_) ? $_ : 'NULL' } @$row;
- $csv->print($fh, \@vals);
- }
- close $fh;
- my $col_list = join(', ', map { $db->quote_identifier($_) } @columns);
- my $query = qq{LOAD DATA LOCAL INFILE '$fname' INTO TABLE $table FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES ($col_list)};
- my $load_ok = eval { $db->do($query); 1 };
- if (!$load_ok) {
- my $err = "MySQL LOAD DATA failed: $@";
- log_error($err);
- log_debug("Falling back to bulk INSERT for MySQL");
- goto FALLBACK_INSERT_MYSQL;
- }
- $db->commit();
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 1;
- # ========================
- # Fallback для MySQL
- # ========================
- FALLBACK_INSERT_MYSQL:
- {
- my $quoted_cols = join(', ', map { $db->quote_identifier($_) } @columns);
- my $placeholders = join(',', ('?') x @columns);
- my $sql = "INSERT INTO $table ($quoted_cols) VALUES ($placeholders)";
- my $sth = $db->prepare($sql);
- my $success = eval {
- for my $row (@$data_rows) {
- next unless $row && ref($row) eq 'ARRAY' && @$row == @columns;
- my @vals = map { defined($_) ? $_ : undef } @$row;
- $sth->execute(@vals);
- }
- 1;
- };
- if ($success) {
- $db->commit();
- } else {
- eval { $db->rollback(); };
- my $err = "MySQL bulk INSERT failed: $@";
- log_error($err);
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 0;
- }
- $db->{AutoCommit} = $original_autocommit;
- }
- } elsif (get_db_type($db) eq 'pg') {
- if (!$db->can('pg_putcopydata') || !$db->can('pg_putcopyend')) {
- log_debug("pg_putcopydata/pg_putcopyend not available — falling back to bulk INSERT");
- goto FALLBACK_INSERT_PG;
- }
- my $col_list = join(', ', map { $db->quote_identifier($_) } @columns);
- my $copy_sql = "COPY $table ($col_list) FROM STDIN WITH (FORMAT CSV, HEADER true)";
- my $use_header_as_data;
- my $start_ok = eval { $db->do($copy_sql); 1 };
- if (!$start_ok) {
- log_debug("COPY with HEADER failed: $@ — trying without HEADER");
- $copy_sql = "COPY $table ($col_list) FROM STDIN WITH (FORMAT CSV)";
- $start_ok = eval { $db->do($copy_sql); 1 };
- if (!$start_ok) {
- log_debug("COPY failed entirely: $@ — falling back to bulk INSERT");
- goto FALLBACK_INSERT_PG;
- }
- $use_header_as_data = 1;
- } else {
- $use_header_as_data = 0;
- }
- log_debug("Using CSV COPY for PostgreSQL");
- my $csv = Text::CSV->new({
- binary => 1,
- quote_char => '"',
- escape_char => '"',
- sep_char => ',',
- eol => "\n",
- always_quote => 1,
- }) or do {
- my $err = "Cannot create Text::CSV: " . Text::CSV->error_diag();
- log_error($err);
- eval { $db->pg_putcopyend(); };
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 0;
- };
- my $success = eval {
- if ($use_header_as_data) {
- $csv->combine(@columns);
- $db->pg_putcopydata($csv->string);
- }
- for my $row (@$data_rows) {
- next unless $row && ref($row) eq 'ARRAY' && @$row == @columns;
- my @vals = map { defined($_) ? $_ : undef } @$row;
- $csv->combine(@vals);
- $db->pg_putcopydata($csv->string);
- }
- $db->pg_putcopyend();
- 1;
- };
- if ($success) {
- $db->commit();
- } else {
- eval { $db->rollback(); };
- my $err = "CSV COPY failed: $@";
- log_error($err);
- eval { $db->pg_putcopyend(); };
- goto FALLBACK_INSERT_PG;
- }
- # ========================
- # Fallback для PostgreSQL
- # ========================
- FALLBACK_INSERT_PG:
- {
- my $quoted_cols = join(', ', map { $db->quote_identifier($_) } @columns);
- my $placeholders = join(',', ('?') x @columns);
- my $sql = "INSERT INTO $table ($quoted_cols) VALUES ($placeholders)";
- my $sth = $db->prepare($sql);
- my $success = eval {
- for my $row (@$data_rows) {
- next unless $row && ref($row) eq 'ARRAY' && @$row == @columns;
- my @vals = map { defined($_) ? $_ : undef } @$row;
- $sth->execute(@vals);
- }
- 1;
- };
- if ($success) {
- $db->commit();
- } else {
- eval { $db->rollback(); };
- my $err = "PostgreSQL bulk INSERT failed: $@";
- log_error($err);
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 0;
- }
- }
- } else {
- my $err = "Unsupported DBTYPE: ". get_db_type($db);
- log_error($err);
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 0;
- }
- $db->{AutoCommit} = $original_autocommit;
- $db->disconnect();
- return 1;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub reconnect_db {
- my $db_ref = shift;
- # Если соединение активно — ничего не делаем
- if ($$db_ref && $$db_ref->ping) {
- return 1;
- }
- # Сохраняем AutoCommit из текущего соединения (если есть)
- my $original_autocommit = 1;
- if ($$db_ref) {
- $original_autocommit = $$db_ref->{AutoCommit};
- eval { $$db_ref->disconnect; };
- $$db_ref = undef;
- }
- # Пытаемся переподключиться
- eval {
- $$db_ref = init_db($original_autocommit);
- unless ($$db_ref && $$db_ref->ping) {
- log_die "Failed to establish database connection";
- }
- 1;
- } or do {
- my $error = $@ || 'Unknown error';
- $$db_ref = undef;
- log_die "Database reconnection failed: $error";
- 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 {
- my $autocommit = shift;
- if (!defined $autocommit) { $autocommit = 1; }
- my $db;
- if ($config_ref{DBTYPE} eq 'mysql') {
- $db = DBI->connect(
- "dbi:mysql:database=$DBNAME;host=$DBHOST;port=3306;mysql_local_infile=1", $DBUSER, $DBPASS,
- { RaiseError => 0, AutoCommit => $autocommit, mysql_enable_utf8 => 1 }
- );
- if (!defined $db) {
- log_die "Cannot connect to MySQL server: $DBI::errstr\n";
- }
- $db->do('SET NAMES utf8mb4');
- } else {
- $db = DBI->connect(
- "dbi:Pg:dbname=$DBNAME;host=$DBHOST;port=5432", $DBUSER, $DBPASS,
- { RaiseError => 0, AutoCommit => $autocommit, pg_enable_utf8 => 1, pg_server_prepare => 0 }
- );
- if (!defined $db) {
- log_die "Cannot connect to PostgreSQL server: $DBI::errstr\n";
- }
- }
- return $db;
- }
- #---------------------------------------------------------------------------------------------------------------
- # Обновленная функция get_option с параметризованными запросами
- sub get_option {
- 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.option_type
- FROM config_options co
- LEFT JOIN config c ON c.option_id = co.id
- WHERE co.id = ?
- };
- my $record = get_record_sql($db, $sql, $option_id);
- unless ($record) {
- log_error("Option ID $option_id not found in config_options table");
- return;
- }
- return $record->{value};
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_records_sql {
- my ($db, $sql, @params) = @_;
- my @result;
- return @result if (!$db);
- return @result if (!$sql);
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return @result;
- }
- my $result_ref = _execute_param($db, $sql, \@params, { mode => 'array' });
- if (ref($result_ref) eq 'ARRAY') {
- @result = @$result_ref;
- }
- return @result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_record_sql {
- my ($db, $sql, @params) = @_;
- my @result;
- return @result if (!$db);
- return @result if (!$sql);
- # Добавляем LIMIT только если его еще нет в запросе
- if ($sql !~ /\bLIMIT\s+\d+/i && $sql !~ /\bFETCH\s+FIRST\s+\d+/i) {
- $sql .= ' LIMIT 1';
- }
- # Переподключение
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- return _execute_param($db, $sql, \@params, { mode => 'single' });
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_count_records {
- my ($db, $table, $filter, @params) = @_;
- 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, @params);
- if ($record->{rec_cnt}) { $result = $record->{rec_cnt}; }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_id_record {
- my ($db, $table, $filter, @params) = @_;
- my $result = 0;
- return $result if (!$db);
- return $result if (!$table);
- my $record = get_record_sql($db,"SELECT id FROM $table WHERE $filter", @params);
- if ($record->{id}) { $result = $record->{id}; }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_diff_rec {
- my ($db, $table, $record, $filter_sql, @filter_params) = @_;
- return unless $db && $table && $filter_sql;
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- my $old_record = get_record_sql($db,"SELECT * FROM $table WHERE $filter_sql",@filter_params);
- return unless $old_record;
- my $result;
- foreach my $field (keys %$record) {
- if (!$record->{$field}) { $record->{$field}=''; }
- if (!$old_record->{$field}) { $old_record->{$field}=''; }
- if ($record->{$field}!~/^$old_record->{$field}$/) { $result->{$field} = "$record->{$field} [ old: " . $old_record->{$field} . "]"; }
- }
- return hash_to_text($result);
- }
- #---------------------------------------------------------------------------------------------------------------
- sub get_db_type {
- my $db = shift;
- return lc($db->{Driver}->{Name});
- #'mysql', 'pg'
- }
- #---------------------------------------------------------------------------------------------------------------
- # Внутренняя функция для выполнения параметризованных запросов
- sub _execute_param {
- my ($db, $sql, $params, $options) = @_;
- return unless $db && $sql;
- my $mode = $options->{mode} || 'execute';
- # --- Автоматическая поддержка RETURNING для PostgreSQL ---
- my $was_modified = 0;
- my $original_sql = $sql;
- if ($mode eq 'id' && $sql =~ /^\s*INSERT\b/i) {
- if (get_db_type($db) eq 'pg') {
- unless ($sql =~ /\bRETURNING\b/i) {
- $sql .= ' RETURNING id';
- $was_modified = 1;
- $mode = 'scalar';
- }
- }
- }
- # Логируем не-SELECT
- unless ($original_sql =~ /^\s*SELECT/i) {
- log_debug($original_sql . ($params ? ' | params: [' . join(', ', map { defined $_ ? $_ : 'undef' } @$params) . ']' : ''));
- }
- # === не переподключаемся внутри транзакции ===
- my $autocommit_enabled = $db->{AutoCommit};
- unless ($autocommit_enabled) {
- # В транзакции: нельзя переподключаться!
- unless ($db->ping) {
- log_error("Database connection lost during transaction");
- return wantarray ? () : undef;
- }
- } else {
- # Вне транзакции: можно переподключиться
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return wantarray ? () : undef;
- }
- }
- my $sth = $db->prepare($sql) or do {
- log_error("Unable to prepare SQL [$original_sql]: " . $db->errstr);
- return wantarray ? () : undef;
- };
- my $rv = $params ? $sth->execute(@$params) : $sth->execute();
- unless ($rv) {
- log_error("Unable to execute SQL [$original_sql]" . ($params ? " with params: [" . join(', ', map { defined $_ ? $_ : 'undef' } @$params) . "]" : "") . ": " . $sth->errstr);
- $sth->finish();
- return wantarray ? () : undef;
- }
- # --- Обработка результатов ---
- if ($was_modified && $mode eq 'scalar') {
- my $row = $sth->fetchrow_arrayref();
- $sth->finish();
- my $id = $row ? $row->[0] : 0;
- return $id;
- }
- elsif ($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 ($original_sql =~ /^\s*INSERT/i) {
- my $id;
- if (get_db_type($db) 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;
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub do_sql {
- my ($db, $sql, @bind_values) = @_;
- return unless $db && $sql; # Возвращаем undef при ошибке входных данных
- my $mode;
- if ($sql =~ /^\s*insert\b/i) {
- $mode = 'id';
- } elsif ($sql =~ /^\s*select\b/i) {
- $mode = 'arrayref';
- } else {
- $mode = 'execute';
- }
- my $result = _execute_param($db, $sql, \@bind_values, { mode => $mode });
- # Если _execute_param вернул undef/ложь — это ошибка
- unless (defined $result) {
- return; # Возвращаем undef (лучше, чем 0)
- }
- if ($mode eq 'id') {
- return $result; # число (возможно 0 — допустимо для ID)
- } elsif ($mode eq 'arrayref') {
- # _execute_param всегда возвращает ARRAYREF при успехе
- return $result;
- } else {
- # Для UPDATE/DELETE: возвращаем количество затронутых строк или 1
- return $result ? $result : 1;
- }
- }
- #---------------------------------------------------------------------------------------------------------------
- sub insert_record {
- my ($db, $table, $record) = @_;
- return unless $db && $table && ref($record) eq 'HASH' && %$record;
- # Переподключаемся ТОЛЬКО если не в транзакции
- if ($db->{AutoCommit}) {
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- } else {
- unless ($db->ping) {
- log_error("Database connection lost during transaction");
- return;
- }
- }
- my $db_info= build_db_schema($db);
- my $dns_changed = 0;
- my $rec_id = 0;
- 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; }
- }
- }
- my @insert_params;
- my $fields = '';
- my $values = '';
- foreach my $field (keys %$record) {
- my $val = normalize_value($record->{$field}, $db_schema{$db_info->{db_type}}{$db_info->{db_name}}{$table}{$field});
- # Экранируем имя поля в зависимости от СУБД
- my $quoted_field = get_db_type($db) eq 'mysql'
- ? '`' . $field . '`'
- : '"' . $field . '"';
- $fields .= "$quoted_field, ";
- $values .= "?, ";
- push @insert_params, $val;
- }
- $fields =~ s/,\s*$//;
- $values =~ s/,\s*$//;
- my $sSQL = "INSERT INTO $table($fields) VALUES($values)";
- my $result = do_sql($db,$sSQL,@insert_params);
- if ($result) {
- $rec_id = $result;
- my $changed_msg = prepare_audit_message($db, $table, undef, $record, $rec_id, 'insert');
- if ($table !~ /session/i) {
- if (defined $changed_msg && $changed_msg ne '') {
- if ($table !~ /user/i) {
- db_log_info($db, $changed_msg);
- } else {
- if ($table eq 'user_auth' && defined $record->{ip} && $record->{ip} ne '') {
- if (is_hotspot($db, $record->{ip})) {
- db_log_info($db, $changed_msg, $rec_id);
- } else {
- db_log_warning($db, $changed_msg, $rec_id);
- my $send_alert_create = isNotifyCreate(get_notify_subnet($db, $record->{ip}));
- sendEmail("WARN! " . get_first_line($changed_msg), $changed_msg, 1) if $send_alert_create;
- }
- } else {
- db_log_warning($db, $changed_msg);
- }
- }
- }
- 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->{'operation_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 !$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->{'operation_type'}='add';
- $add_dns->{'auth_id'}=$result;
- insert_record($db,'dns_queue',$add_dns);
- }
- if ($record->{'dns_name'} and $record->{'ip'} 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->{'operation_type'}='add';
- $add_dns->{'auth_id'}=$result;
- insert_record($db,'dns_queue',$add_dns);
- }
- }
- }
- }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub update_records {
- my ($db, $table, $filter, $newvalue, @filter_params) = @_;
- # Получаем ID всех записей, подходящих под фильтр
- my $uSQL = "SELECT id FROM $table WHERE $filter";
- my @ids = get_records_sql($db, $uSQL, @filter_params);
- # Если ничего не найдено — считаем успехом
- return 1 unless @ids;
- # Обновляем каждую запись по отдельности
- for my $record (@ids) {
- next unless ref $record eq 'HASH' && defined $record->{id};
- update_record($db, $table, $newvalue, "id = ?", $record->{id});
- }
- return 1;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub update_record {
- my ($db, $table, $record, $filter_sql, @filter_params) = @_;
- return unless $db && $table && $filter_sql;
- # Переподключаемся ТОЛЬКО если не в транзакции
- if ($db->{AutoCommit}) {
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- } else {
- unless ($db->ping) {
- log_error("Database connection lost during transaction");
- return;
- }
- }
- my $db_info = build_db_schema($db);
- my $select_sql = "SELECT * FROM $table WHERE $filter_sql";
- my $old_record = get_record_sql($db, $select_sql, @filter_params);
- return unless $old_record;
- my @update_params;
- my $set_clause = '';
- my $dns_changed = 0;
- my $rec_id = $old_record->{id} || 0;
- if ($table eq "user_auth") {
- $rec_id = $old_record->{'id'} if ($old_record->{'id'});
- my $cur_ou_id = $old_record->{'ou_id'} if ($old_record->{'ou_id'});
- if (exists $record->{ou_id}) { $cur_ou_id = $record->{'ou_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} and !is_system_ou($db,$cur_ou_id)) { $record->{dhcp_changed}="1"; }
- if (exists $dns_fields{$field}) { $dns_changed=1; }
- }
- }
- for my $field (keys %$record) {
- next if (!$field);
- next if (!exists $record->{$field});
- next if (!defined $record->{$field});
- my $old_val = defined $old_record->{$field} ? $old_record->{$field} : '';
- my $new_val = normalize_value( $record->{$field}, $db_schema{$db_info->{db_type}}{$db_info->{db_name}}{$table}{$field});
- $new_val = defined $new_val ? $new_val : '';
- if ($new_val ne $old_val) {
- $set_clause .= " $field = ?, ";
- push @update_params, $new_val;
- }
- }
- return unless $set_clause;
- # Добавляем служебные поля
- if ($table eq 'user_auth') {
- if ($record->{changed} || $record->{dhcp_changed} || $dns_changed ) {
- $set_clause .= "changed_time = ?, ";
- push @update_params, GetNowTime();
- }
- }
- $set_clause =~ s/,\s*$//;
- if ($table eq 'user_auth') {
- 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->{'operation_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->{'operation_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->{'operation_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->{'operation_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->{'operation_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->{'operation_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 @all_params = (@update_params, @filter_params);
- my $update_sql = "UPDATE $table SET $set_clause WHERE $filter_sql";
- my $result = do_sql($db, $update_sql, @all_params);
- if ($result) {
- my $changed_msg = prepare_audit_message($db, $table, $old_record, $record , $rec_id, 'update');
- if ($table !~ /session/i) {
- if (defined $changed_msg && $changed_msg ne '') {
- if ($table !~ /user/i) {
- db_log_info($db, $changed_msg);
- } else {
- if (is_hotspot($db, $old_record->{ip})) {
- db_log_info($db, $changed_msg, $rec_id);
- } else {
- db_log_warning($db, $changed_msg, $rec_id);
- if ($table eq 'user_auth' && defined $old_record->{ip} && $old_record->{ip} ne '') {
- my $send_alert_update = isNotifyUpdate(get_notify_subnet($db, $old_record->{ip}));
- sendEmail("WARN! " . get_first_line($changed_msg), $changed_msg, 1) if $send_alert_update;
- }
- }
- }
- }
- }
- }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub delete_record {
- my ($db, $table, $filter_sql, @filter_params) = @_;
- return unless $db && $table && $filter_sql;
- # Переподключаемся ТОЛЬКО если не в транзакции
- if ($db->{AutoCommit}) {
- unless (reconnect_db(\$db)) {
- log_error("No database connection available");
- return;
- }
- } else {
- unless ($db->ping) {
- log_error("Database connection lost during transaction");
- return;
- }
- }
- my $select_sql = "SELECT * FROM $table WHERE $filter_sql";
- my $old_record = get_record_sql($db, $select_sql, @filter_params);
- return unless $old_record;
- my $rec_id = $old_record->{'id'};
- #never delete user ip record!
- if ($table eq 'user_auth') {
- my $sSQL = "UPDATE user_auth SET changed = 1, deleted = 1, changed_time = ? WHERE $filter_sql";
- my $ret = do_sql($db, $sSQL, GetNowTime(), @filter_params);
- 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->{'operation_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->{'operation_type'}='del';
- $del_dns->{'auth_id'}=$old_record->{'id'};
- insert_record($db,'dns_queue',$del_dns);
- }
- my $changed_msg = prepare_audit_message($db, $table, $old_record, undef , $rec_id, 'delete');
- if ($ret) {
- if (defined $changed_msg && $changed_msg ne '') {
- if (defined $old_record->{ip} && $old_record->{ip} ne '') {
- if (is_hotspot($db, $old_record->{ip})) {
- db_log_info($db, $changed_msg, $rec_id);
- } else {
- db_log_warning($db, $changed_msg, $rec_id);
- my $send_alert_delete = isNotifyDelete(get_notify_subnet($db, $old_record->{ip}));
- sendEmail("WARN! " . get_first_line($changed_msg), $changed_msg, 1) if $send_alert_delete;
- }
- }
- }
- }
- 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->{'operation_type'}='del';
- $del_dns->{'auth_id'}=$old_record->{'auth_id'};
- insert_record($db,'dns_queue',$del_dns);
- }
- }
- my $sSQL = "DELETE FROM ".$table." WHERE ".$filter_sql;
- my $result = do_sql($db,$sSQL,@filter_params);
- my $changed_msg = prepare_audit_message($db, $table, $old_record, undef , $rec_id, 'delete');
- if ($result && $table !~ /session/i) {
- if (defined $changed_msg && $changed_msg ne '') {
- if ($table !~ /user/i) {
- db_log_info($db, $changed_msg);
- } else {
- db_log_warning($db, $changed_msg);
- }
- }
- }
- return $result;
- }
- #---------------------------------------------------------------------------------------------------------------
- sub is_system_ou {
- my ($db, $ou_id) = @_;
- return 0 if !defined $ou_id || $ou_id !~ /^\d+$/ || $ou_id <= 0;
- my $sql = "SELECT 1 FROM ou WHERE id = ? AND (default_users = 1 OR default_hotspot = 1)";
- my $record = get_record_sql($db, $sql, $ou_id);
- return $record ? 1 : 0;
- }
- #---------------------------------------------------------------------------------------------------------------
- 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, $name, $value, $timeshift) = @_;
- $name //= $MY_NAME;
- $value //= $$;
- $timeshift //= 60;
- Del_Variable($db, $name);
- my $clean_time = time() + $timeshift;
- my ($sec, $min, $hour, $day, $month, $year) = localtime($clean_time);
- $month++;
- $year += 1900;
- my $clear_time_str = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec;
- my $sql = "INSERT INTO variables (name, value, clear_time) VALUES (?, ?, ?)";
- do_sql($db, $sql, $name, $value, $clear_time_str);
- }
- #---------------------------------------------------------------------------------------------------------------
- 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, $name) = @_;
- $name //= $MY_NAME;
- do_sql($db, "DELETE FROM variables WHERE name = ?", $name);
- }
- #---------------------------------------------------------------------------------------------------------------
- sub clean_variables {
- my ($db) = @_;
- # 1. Clean temporary variables
- my $now = time();
- my ($sec, $min, $hour, $day, $month, $year) = localtime($now);
- $month++;
- $year += 1900;
- my $now_str = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec;
- do_sql($db, "DELETE FROM variables WHERE clear_time <= ?", $now_str);
- # 2. Clean old AD computer cache
- my $yesterday = DateTime->now(time_zone => 'local')->subtract(days => 1);
- my $clean_str = $yesterday->strftime("%Y-%m-%d 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);
- # warn "DBI driver name: ", $dbh->{Driver}->{Name}, "\n" if ($debug);
- # warn "Full dbh class: ", ref($dbh), "\n" if ($debug);
- }
- 1;
- }
|