#!/usr/bin/perl 
# Скрипт осуществляет выдачу данных для клиентских агентов системы UserSide
# http://userside.ua/manual/pg_132.htm - информация о модуле
# Настройка
$zrevision = "9";
$zver = "us_agent_server v.3.5." . $zrevision; #версия
#========================================================================
use Socket;
use DBI;
use Carp;
use Time::Local;
use FindBin;

$zpath = $FindBin::RealBin . "/";
require($zpath . "us_config.tmp");

#Настройки конкретного модуля
$zmodule_param = 'M_AGENT';

if ($zport < 1) {
    $zport = 2345;
}

#Загружаем параметры
load_first();

#Проверяем версию
version_verify();

#Начальные системные операции
load_second();

#Фиксируем в логе специфические параметры
printlog("port                  = " . $zport);

#================================================================================================================

socket(Server, PF_INET, SOCK_STREAM, getprotobyname('tcp'))        || die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))   || die "setsockopt: $!";
bind(Server, sockaddr_in($zport, INADDR_ANY))        || die "bind: $!";
listen(Server, 1000)                            || die "listen: $!"; #1000 - максимальное к-во подключенных

@pmas_param = ();
#Получаем значения конфига
$ps_constr = "SELECT PARAM, VALUEINT, VALUESTR, VALUEDATE FROM tbl_conf";
printlog_sql_u($ps_constr);
    while (@ln = $rs->fetchrow_array()) {
        if ($ln[0] eq 'AGENT_LASTVER') {
            $pmas_param[1] = $ln[2];
        }
        if ($ln[0] eq 'AGENT_UPDATEONLY') {
            $pmas_param[2] = $ln[1];
        }
        if ($ln[0] eq 'AGENT_PATHDOWN') {
            $pmas_param[3] = $ln[2];
        }
        if ($ln[0] eq 'AGENT_NAZV') {
            $pmas_param[4] = $ln[2];
        }
        if ($ln[0] eq 'AGENT_INTDATA') {
            $pmas_param[5] = $ln[1];
        }
        if ($ln[0] eq 'AGENT_BILDATAPATH') {
            $pmas_param[6] = $ln[2];
        }
        if ($ln[0] eq 'AGENT_UPDATETIME') {
            $pmas_param[7] = $ln[2];
        }
        if ($ln[0] eq 'AGENT_SHOWTRAF') {
            $pmas_param[8] = $ln[1];
        }
        if ($ln[0] eq 'AGENT_USLINKS') {
            $pmas_param[9] = $ln[1];
        }
        if ($ln[0] eq 'AGENT_MINUSVOTE') {
            $pmas_param[10] = $ln[2];
        }
        if ($ln[0] eq 'AGENT_WRITEIN') {
            $pmas_param[11] = $ln[1];
        }
    };
$rs->finish();

#Сброс буферов
bufer_clear();

#================================================================================================================
while(1) {
    $paddr = accept(Client,Server);

    my($zport, $iaddr) = sockaddr_in($paddr);
    my $ps_ip = inet_ntoa($iaddr);

    $ps_str = "Client connection. IP: " . $ps_ip . " (port: " . $zport . ")";
    printlog($ps_str);

    chomp($ps_param=<Client>);
    chomp($ps_param2=<Client>);
    chomp($ps_param3=<Client>);
    chomp($ps_param4=<Client>);

    if ($ps_param ne '') {
        $ps_param = substr($ps_param, -length($ps_param), length($ps_param) - 1);
    }
    if ($ps_param2 ne '') {
        $ps_param2 = substr($ps_param2, -length($ps_param2), length($ps_param2) - 1);
    }
    if ($ps_param3 ne '') {
        $ps_param3 = substr($ps_param3, -length($ps_param3), length($ps_param3) - 1);
    }
    if ($ps_param4 ne '') {
        $ps_param4 = substr($ps_param4, -length($ps_param4), length($ps_param4) - 1);
    }

    print formatdatetime("DD.MM.YYYY hh:mm:ss", time) . ' ' . $ps_str . ' (' . $ps_param . ")\n";

    #Общие параметры
    #==================================================================
    if ($ps_param eq 'main') {

        #Сброс буферов
        bufer_clear();

        #Прописываемся в базу
        $ps_info = "CONCAT('" . $zver . "|" . $zpath . "|', NOW())";
        $ps_constr = "UPDATE tbl_conf SET VALUEDATE = NOW() WHERE PARAM = '" . $zmodule_param . "'";
        sql_do($ps_constr);

        print Client 'us_agent\n';
        #Выводим ответ
        print Client 'LASTVER             |' . $pmas_param[1] . '\n';
        print Client 'UPDATEONLY          |' . $pmas_param[2] . '\n';
        print Client 'PATHDOWN            |' . $pmas_param[3] . '\n';
        print Client 'NAZV                |' . $pmas_param[4] . '\n';
        print Client 'INTDATA             |' . $pmas_param[5] . '\n';
        print Client 'BILDATAPATH         |' . $pmas_param[6] . '\n';
        print Client 'UPDATETIME          |' . $pmas_param[7] . '\n';
        print Client 'SHOWTRAF            |' . $pmas_param[8] . '\n';
        print Client 'USLINKS             |' . $pmas_param[9] . '\n';
        print Client 'MINUSVOTE           |' . $pmas_param[10] . '\n';
        #Фиксируем данные о клиенте
        if ($pmas_param[11] eq 1) {
            #Ищем самого клиента
            $ps_ip2 = ip2long($ps_ip);
            $ps_constr = "SELECT USERCODE FROM tbl_ip WHERE TYPER = 1 AND USERIP = " . $ps_ip2;
            printlog_sql_u($ps_constr);
            $pi_usercode = $rs->fetchrow_array();            
            if ($pi_usercode > 0) {
                $ps_constr = "UPDATE tbl_base SET LASTPING = NOW(), AGENT_VER = '" . $ps_param2 . "', AGENT_LASTVISIT = NOW(), AGENT_DATA = '" . $ps_param4 . "' WHERE CODE = " . $pi_usercode;
                sql_do($ps_constr);
            }
        }
    }

    #Ссылки
    #==================================================================
    if ($ps_param eq 'links') {
        print Client 'us_agent\n';

        $ps_constr ="SELECT NAZV, LINKS FROM tbl_links WHERE LINKS <> '' ORDER BY NAZV";
        printlog_sql_u($ps_constr);
            while (@ln = $rs->fetchrow_array()) {
                # 3 - рекомендуемые сайты
                print Client '3|' . substr($ln[0] . "                                        ", 0, 20) . '|' . $ln[1] . '\n';
            }
        $rs->finish();
    }

    #Статистика пользователя
    #==================================================================
    if ($ps_param eq 'stats') {
        print Client 'us_agent\n';

        #Ищем самого клиента
        $ps_ip2 = ip2long($ps_ip);
        $ps_constr = "SELECT b.CODE, g.GROUPNAME, b.BALANS, b.RXTRAF, b.TXTRAF FROM tbl_base AS b LEFT JOIN tbl_group AS g ON g.CODE = b.GROUPN INNER JOIN tbl_ip AS i ON i.TYPER = 1 AND i.USERCODE = b.CODE AND i.USERIP = " . $ps_ip2;
        printlog_sql_u($ps_constr);
        @ln = $rs->fetchrow_array();
        $ps_usercode = $ln[0];
        if ($pi_usercode > 0) {
            $ps_groupname = $ln[1];
            $ps_balans = $ln[2];
            $ps_rxtraf = $ln[3];
            $ps_txtraf = $ln[4];
            print Client $ps_groupname . '\n';
            print Client $ps_balans . '\n';
            print Client $ps_rxtraf . '\n';
            print Client $ps_txtraf . '\n';
        } else {
            print Client 'notuser\n';
            print Client 'notuser\n';
            print Client 'notuser\n';
            print Client 'notuser\n';
        }
    }

    #Номер последнего сообщения
    #==================================================================
    if ($ps_param eq 'lastmsg') {
        print Client 'us_agent\n';

        $ps_constr = "SELECT MAX(CODE) FROM tbl_trouble_all";
        printlog_sql_u($ps_constr);
        $pi_lastmsg = $rs->fetchrow_array();
        if ($pi_lastmsg < 1) {
            $pi_lastmsg = '0';
        }
        print Client $pi_lastmsg . '\n';

        #Сброс буферов
        bufer_clear();
    }

    #Сообщение
    #==================================================================
    if ($ps_param eq 'textmsg') {
        print Client 'us_agent\n';

        $ps_constr = "SELECT DATE_FORMAT(DATEADD, '%d.%m.%Y %k:%i'), THEME, OPIS FROM tbl_trouble_all WHERE CODE = " . $ps_param3;
        printlog_sql_u($ps_constr);
        @ln = $rs->fetchrow_array();
        $pi_dateadd = $ln[0];
        $ps_theme = $ln[1];
        $ps_opis = $ln[2];
        print Client $pi_dateadd . '\n';
        print Client $ps_theme . '\n';
        print Client $ps_opis . '\n';
    }

    #Тест
    #==================================================================
    if ($ps_param eq 'hello') {
        print Client 'Hello, Client\n';
    }
    close (Client);
}
close Server;                 
unlink $pidfile;
exit(0);

#================================================================================================================
#Standart Module Sync function (for not billing) v.2 - 13/02/2013 ####
#================================================================================================================

sub trim {
    my($string) = @_;
    for ($string) {
        s/^\s+//;
        s/\s+$//;
    }
    return $string;
}
#========================================================

sub formatdatetime {
    my $format = shift @_ || "YYYY-MM-DD hh:mm:ss";
    my $datetime = shift @_ || time;
    my %strings = (
        '500'=>"AM", '501'=>"PM",
        '400'=>"January", '401'=>"February", '402'=>"March", '403'=>"April", '404'=>"May", '405'=>"June", '406'=>"July", '407'=>"August", '408'=>"September", '409'=>"October", '410'=>"November", '411'=>"December",
        '300'=>"Jan", '301'=>"Feb", '302'=>"Mar", '303'=>"Apr", '304'=>"May", '305'=>"Jun", '306'=>"Jul", '307'=>"Aug", '308'=>"Sep", '309'=>"Oct", '310'=>"Nov", '311'=>"Dec",
        '200'=>"Sunday", '201'=>"Monday", '202'=>"Tuesday", '203'=>"Wednesday", '204'=>"Thursday", '205'=>"Friday", '206'=>"Saturday",
        '100'=>"Sun", '101'=>"Mon", '102'=>"Tue", '103'=>"Wed", '104'=>"Thu", '105'=>"Fri", '106'=>"Sat"
    );
    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($datetime);
    $year += 1900;
    if ($format=~m/AM/) {
        my $ampmhour=($hour % 12) || 12;
        $format=~s/hh/sprintf("%02u",$ampmhour)/eg;
        $format=~s/h/$ampmhour/eg;
        $format=~s/AM/($hour>11?"^501":"^500")/eg;
    } else {
        $format=~s/hh/sprintf("%02u",$hour)/eg;
        $format=~s/h/$hour/eg;
    };
    $format=~s/mm/sprintf("%02u",$min)/eg;
    $format=~s/m/$min/eg;
    $format=~s/ss/sprintf("%02u",$sec)/eg;
    $format=~s/s/$sec/eg;

    $format=~s/YYYY/$year/eg;
    $format=~s/YY/sprintf("%02u",$year % 100)/eg;
    $format=~s/MMMM/sprintf("^4%02u",$mon)/eg;
    $format=~s/MMM/sprintf("^3%02u",$mon)/eg;
    $format=~s/MM/sprintf("%02u",$mon+1)/eg;
    $format=~s/M/($mon+1)/eg;
    $format=~s/DDDD/sprintf("^2%02u",$wday)/eg;
    $format=~s/DDD/sprintf("^1%02u",$wday)/eg;
    $format=~s/DD/sprintf("%02u",$mday)/eg;
    $format=~s/D/$mday/eg;
    $format=~s/\^(\d\d\d)/$strings{$1}/g;
    return $format;
}
#========================================================

sub ip2long {
    return unpack("N",inet_aton(shift));
}
#========================================================

sub mysqldatetounix {
    my($string)=@_;
    $ps_year = substr($string, -length($string), 4);
    $ps_month = substr($string, -length($string) + 5, 2);
    $ps_day = substr($string, -length($string) + 8, 2);
    $ps_hour = substr($string, -length($string) + 11, 2);
    $ps_minute = substr($string, -length($string) + 14, 2);
    $ps_second = substr($string, -length($string) + 17, 2);
    if ($ps_month < 1) {
        $ps_month = '1';
    }
    if (!$ps_day) {
        $ps_day = '1';
    }
    $string = timelocal($ps_second, $ps_minute, $ps_hour, $ps_day, $ps_month-1, $ps_year);
    return $string;
}

#========================================================
sub printlog{
    my($string) = @_;
    print $ps_file101 formatdatetime("DD.MM.YYYY hh:mm:ss", time) . " - " . $string . "\n";
    return $string;
}

#========================================================
sub sql_do{
    my($string) = @_;
    $zl_sqlc_u++;
    #print 'U: '.$zl_sqlc_u."\n";
    print $ps_file102 "U:" . $zl_sqlc_u . "| " . $string . ";\n";
    $conn2->do($string);
    return $string;
}

#========================================================
sub printlog_sql_u{
    my($string) =@_;
    $zl_sqlc_u++ ;
    print $ps_file102 "U:" . $zl_sqlc_u . '| ' . $string . ";\n";
    $rs = $conn2->prepare($string);
    $rs->execute();
    return $string;
}

#========================================================
sub printlog_sql_u2{
    my($string)=@_;
    $zl_sqlc_u++;
    print $ps_file102 "U:" . $zl_sqlc_u . "| " . $string . ";\n";
    $rs2 = $conn2->prepare($string);
    $rs2->execute();
    return $string;
}

#========================================================
sub AddSlashes{
    $text = shift;
    if (($zncp ne 'cp1252') && ($zncp ne 'latin1')){
        $text =~s/»/&#034;/g;
        $text =~s/«/&#034;/g;
    }
    $text =~s/\\/\\\\/g;
    $text =~s/'/&#039;/g;
    $text =~s/"/&#034;/g;
    $text =~s/\\0/\\\\0/g;
    return $text;
}

#========================================================
sub load_first {
    $ps_repalllog = $ps_logpath . "rep_alllog.txt";
    $ps_rep_logupd = $ps_logpath . "rep_logupd.txt";
    $ps_rep_sql = $ps_logpath . "rep_mysql.txt";
    open ($ps_file101, ">>".$ps_repalllog) || die;
        print $ps_file101 $zver . " START at " . formatdatetime("DD.MM.YYYY hh:mm:ss", time) . "\n";
    close $ps_file101;
    $pidfile = $ps_logpath . "pid.txt";
    if (-e $pidfile) {
        unless (open(PIDFILE, $pidfile)) {
            #Если найден - то выходим
            open ($ps_file101, ">>".$ps_repalllog) || die;
                print $ps_file101 $zver . " Find previous PID: " . $oldpid . " - exit\n";
                print $ps_file101 $zver . " FINISH at " . formatdatetime("DD.MM.YYYY hh:mm:ss", time) . "\n";
                print $ps_file101 "==============================================================\n";
            close $ps_file101;
            exit(1);
        }
        $oldpid=<PIDFILE>;
        close PIDFILE;
        if ($oldpid > 1 && kill(0,$oldpid)) {
            kill(1,$oldpid);
            #Если найден - то убиваем старый процесс
            open ($ps_file101,">>" . $ps_logpath . "rep_alllog.txt") || die;
                print $ps_file101 $zver . " Find previous PID: " . $oldpid . " - kill it\n";
            close $ps_file101;
        }
    }
    open(PID, ">$pidfile") or die;
        print PID $$;
    close(PID);
    #Выясняем UNIX-дату последних 5 суток
    $pd_nowdate_5 = time - 86400 * 5;
    # Выведем заголовок типа данных
    print $zver . "\n";
    print "===================================\n";
    print "start module at " . formatdatetime("DD.MM.YYYY hh:mm:ss", time) . "\n";
    open ($ps_file101, ">" . $ps_rep_logupd) || die;
    print $ps_file101 "===========================================\n";
    printlog($zver . " - начало работы");
    printlog($zpath);
    open ($ps_file102, ">" . $ps_rep_sql) || die;
    print $ps_file102 formatdatetime("DD.MM.YYYY hh:mm:ss", time) . "\n";
    $zl_sqlc_u = 0;
    $zl_sqlc_b = 0;
    if ($zucp eq '') {
        $zucp = 'cp1251';
    }

    #Открываем базы
    $conn2 = DBI->connect("DBI:mysql:database=" . $zubase . ";host=" . $zuserver, $zuuser, $zupass) || die $DBI::errstr;
    $conn2->do('set character set ' . $zucp);
    $conn2->do('set names ' . $zucp);

    #Получаем значения конфига
    $ps_constr = "SELECT PARAM, VALUEINT, VALUESTR, VALUEDATE FROM tbl_conf";
    printlog_sql_u($ps_constr);
        while (@ln = $rs->fetchrow_array()) {
            #M_
            if ($ln[0] eq $zmodule_param) {
                $ps_needversion = $ln[2];
            }
        };
    $rs->finish();
    return;
}

#========================================================
sub version_verify{
    if (($zrevision < $ps_needversion) || ($ps_needversion < 1)) {
        open ($ps_file101, ">>".$ps_repalllog) || die;
            print $ps_file101 $zver . " Version failure. Need version: " . $ps_needversion . " - exit\n";
            print $ps_file101 $zver . " FINISH at " . formatdatetime("DD.MM.YYYY hh:mm:ss", time) . "\n";
            print $ps_file101 "==============================================================\n";
        close $ps_file101;
        close $ps_file102;
        print "Version failure. Need version: " . $ps_needversion . " - exit\n";
        print "===================================\n";
        $rs->finish();
        $conn2->disconnect();
        unlink $pidfile;
        exit(1);
    }
    return;
}

#========================================================
sub load_second {
    #Фиксируем время по базам
    $ps_constr = "SELECT NOW()";
    printlog_sql_u($ps_constr);
    @ln = $rs->fetchrow_array();
    $ps_time_us = $ln[0];
    printlog("Время в БД UserSide: " . $ps_time_us);

    #Прописываемся в базу
    $ps_info = $zver . '|' . $zpath . '|' . $ps_time_us . '|';
    $ps_constr = "UPDATE tbl_conf SET VALUEMEMO = '" . $ps_info . "' WHERE PARAM = '" . $zmodule_param . "'";
    sql_do($ps_constr);

    printlog("==================================================================");
    printlog("zucp          = " . $zucp);
    printlog("==================================================================");

    return;
}

#========================================================
sub bufer_clear{
    #Сброс буферов в файлы
    close $ps_file101;
    open ($ps_file101, ">>" . $ps_rep_logupd) || die;
    close $ps_file102;
    open ($ps_file102, ">>" . $ps_rep_sql) || die;
    return;
}

#========================================================
