]> git.stg.codes - stg.git/blobdiff - projects/stargazer/user_impl.cpp
Fix build on Darwin.
[stg.git] / projects / stargazer / user_impl.cpp
index d720ffe50f18b3a623099994b4add849f18675dc..7116a11e0a97cf1e68029c16c19244e095832c71 100644 (file)
 #define _GNU_SOURCE
 #endif
 
 #define _GNU_SOURCE
 #endif
 
-#include <pthread.h>
-#include <unistd.h> // access
+#include "user_impl.h"
+#include "settings_impl.h"
+#include "stg_timer.h"
+
+#include "stg/users.h"
+#include "stg/common.h"
+#include "stg/scriptexecuter.h"
+#include "stg/tariff.h"
+#include "stg/tariffs.h"
+#include "stg/services.h"
+#include "stg/service_conf.h"
+#include "stg/admin.h"
+
+#include <algorithm>
+#include <functional>
+#include <array>
 
 #include <cassert>
 #include <cstdlib>
 
 #include <cassert>
 #include <cstdlib>
+#include <cmath>
 
 
-#include "user_impl.h"
-#include "users.h"
-#include "common.h"
-#include "settings_impl.h"
-#include "script_executer.h"
-#include "tariff.h"
-#include "tariffs.h"
-#include "admin.h"
-
-USER_IMPL::USER_IMPL(const SETTINGS_IMPL * s,
-           const STORE * st,
-           const TARIFFS * t,
-           const ADMIN * a,
-           const USERS * u)
+#include <pthread.h>
+#include <unistd.h> // access
+
+using STG::UserImpl;
+
+namespace
+{
+
+std::string dirsToString(const bool * dirs)
+{
+std::string res;
+for (size_t i = 0; i < DIR_NUM; i++)
+    res += dirs[i] ? '1' : '0';
+return res;
+}
+
+void dirsFromBits(bool * dirs, uint32_t bits)
+{
+for (size_t i = 0; i < DIR_NUM; i++)
+    dirs[i] = bits & (1 << i);
+}
+
+}
+
+UserImpl::UserImpl(const Settings * s,
+           const Store * st,
+           const Tariffs * t,
+           const Admin * a,
+           const Users * u,
+           const Services & svcs)
     : users(u),
     : users(u),
-      property(s->GetScriptsDir()),
-      WriteServLog(GetStgLogger()),
-      login(),
+      properties(*s),
+      WriteServLog(Logger::get()),
+      lastScanMessages(0),
       id(0),
       id(0),
-      __connected(0),
-      connected(__connected),
-      userIDGenerator(),
-      __currIP(0),
-      currIP(__currIP),
       lastIPForDisconnect(0),
       pingTime(0),
       sysAdmin(a),
       store(st),
       tariffs(t),
       lastIPForDisconnect(0),
       pingTime(0),
       sysAdmin(a),
       store(st),
       tariffs(t),
-      tariff(tariffs->GetNoTariff()),
-      cash(property.cash),
-      up(property.up),
-      down(property.down),
-      lastCashAdd(property.lastCashAdd),
-      passiveTime(property.passiveTime),
-      lastCashAddTime(property.lastCashAddTime),
-      freeMb(property.freeMb),
-      lastActivityTime(property.lastActivityTime),
-      password(property.password),
-      passive(property.passive),
-      disabled(property.disabled),
-      disabledDetailStat(property.disabledDetailStat),
-      alwaysOnline(property.alwaysOnline),
-      tariffName(property.tariffName),
-      nextTariff(property.nextTariff),
-      address(property.address),
-      note(property.note),
-      group(property.group),
-      email(property.email),
-      phone(property.phone),
-      realName(property.realName),
-      credit(property.credit),
-      creditExpire(property.creditExpire),
-      ips(property.ips),
-      userdata0(property.userdata0),
-      userdata1(property.userdata1),
-      userdata2(property.userdata2),
-      userdata3(property.userdata3),
-      userdata4(property.userdata4),
-      userdata5(property.userdata5),
-      userdata6(property.userdata6),
-      userdata7(property.userdata7),
-      userdata8(property.userdata8),
-      userdata9(property.userdata9),
-      passiveNotifier(this),
-      tariffNotifier(this),
-      cashNotifier(this),
-      ipNotifier(this)
+      tariff(NULL),
+      m_services(svcs),
+      settings(s),
+      authorizedModificationTime(0),
+      deleted(false),
+      lastWriteStat(0),
+      lastWriteDetailedStat(0),
+      cash(properties.cash),
+      up(properties.up),
+      down(properties.down),
+      lastCashAdd(properties.lastCashAdd),
+      passiveTime(properties.passiveTime),
+      lastCashAddTime(properties.lastCashAddTime),
+      freeMb(properties.freeMb),
+      lastActivityTime(properties.lastActivityTime),
+      password(properties.password),
+      passive(properties.passive),
+      disabled(properties.disabled),
+      disabledDetailStat(properties.disabledDetailStat),
+      alwaysOnline(properties.alwaysOnline),
+      tariffName(properties.tariffName),
+      nextTariff(properties.nextTariff),
+      address(properties.address),
+      note(properties.note),
+      group(properties.group),
+      email(properties.email),
+      phone(properties.phone),
+      realName(properties.realName),
+      credit(properties.credit),
+      creditExpire(properties.creditExpire),
+      ips(properties.ips),
+      userdata0(properties.userdata0),
+      userdata1(properties.userdata1),
+      userdata2(properties.userdata2),
+      userdata3(properties.userdata3),
+      userdata4(properties.userdata4),
+      userdata5(properties.userdata5),
+      userdata6(properties.userdata6),
+      userdata7(properties.userdata7),
+      userdata8(properties.userdata8),
+      userdata9(properties.userdata9),
+      sessionUploadModTime(stgTime),
+      sessionDownloadModTime(stgTime)
+{
+    Init();
+}
+//-----------------------------------------------------------------------------
+void UserImpl::Init()
 {
 {
-settings = s;
-
 password = "*_EMPTY_PASSWORD_*";
 tariffName = NO_TARIFF_NAME;
 password = "*_EMPTY_PASSWORD_*";
 tariffName = NO_TARIFF_NAME;
-connected = 0;
-tariff = tariffs->GetNoTariff();
-ips = StrToIPS("*");
-deleted = false;
+tariff = tariffs->FindByName(tariffName);
+ips = UserIPs::parse("*");
 lastWriteStat = stgTime + random() % settings->GetStatWritePeriod();
 lastWriteDetailedStat = stgTime;
 
 lastWriteStat = stgTime + random() % settings->GetStatWritePeriod();
 lastWriteDetailedStat = stgTime;
 
-property.tariffName.AddBeforeNotifier(&tariffNotifier);
-property.passive.AddBeforeNotifier(&passiveNotifier);
-property.cash.AddBeforeNotifier(&cashNotifier);
-currIP.AddAfterNotifier(&ipNotifier);
-
-lastScanMessages = 0;
+m_beforePassiveConn = properties.passive.beforeChange([this](auto oldVal, auto newVal){ onPassiveChange(oldVal, newVal); });
+m_afterDisabledConn = properties.disabled.afterChange([this](auto oldVal, auto newVal){ onDisabledChange(oldVal, newVal); });
+m_beforeTariffConn = properties.tariffName.beforeChange([this](const auto& oldVal, const auto& newVal){ onTariffChange(oldVal, newVal); });
+m_beforeCashConn = properties.cash.beforeChange([this](auto oldVal, auto newVal){ onCashChange(oldVal, newVal); });
+m_afterIPConn = ips.afterChange([this](const auto& oldVal, const auto& newVal){ onIPChange(oldVal, newVal); });
 
 pthread_mutexattr_t attr;
 pthread_mutexattr_init(&attr);
 
 pthread_mutexattr_t attr;
 pthread_mutexattr_init(&attr);
@@ -131,107 +161,102 @@ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
 pthread_mutex_init(&mutex, &attr);
 }
 //-----------------------------------------------------------------------------
 pthread_mutex_init(&mutex, &attr);
 }
 //-----------------------------------------------------------------------------
-USER_IMPL::USER_IMPL(const USER_IMPL & u)
+UserImpl::UserImpl(const UserImpl & u)
     : users(u.users),
     : users(u.users),
-      property(u.settings->GetScriptsDir()),
-      WriteServLog(GetStgLogger()),
+      properties(*u.settings),
+      WriteServLog(Logger::get()),
+      lastScanMessages(0),
       login(u.login),
       id(u.id),
       login(u.login),
       id(u.id),
-      __connected(u.__connected),
-      connected(__connected),
-      __currIP(u.__currIP),
-      currIP(__currIP),
       lastIPForDisconnect(0),
       pingTime(u.pingTime),
       sysAdmin(u.sysAdmin),
       store(u.store),
       tariffs(u.tariffs),
       tariff(u.tariff),
       lastIPForDisconnect(0),
       pingTime(u.pingTime),
       sysAdmin(u.sysAdmin),
       store(u.store),
       tariffs(u.tariffs),
       tariff(u.tariff),
-      cash(property.cash),
-      up(property.up),
-      down(property.down),
-      lastCashAdd(property.lastCashAdd),
-      passiveTime(property.passiveTime),
-      lastCashAddTime(property.lastCashAddTime),
-      freeMb(property.freeMb),
-      lastActivityTime(property.lastActivityTime),
-      password(property.password),
-      passive(property.passive),
-      disabled(property.disabled),
-      disabledDetailStat(property.disabledDetailStat),
-      alwaysOnline(property.alwaysOnline),
-      tariffName(property.tariffName),
-      nextTariff(property.nextTariff),
-      address(property.address),
-      note(property.note),
-      group(property.group),
-      email(property.email),
-      phone(property.phone),
-      realName(property.realName),
-      credit(property.credit),
-      creditExpire(property.creditExpire),
-      ips(property.ips),
-      userdata0(property.userdata0),
-      userdata1(property.userdata1),
-      userdata2(property.userdata2),
-      userdata3(property.userdata3),
-      userdata4(property.userdata4),
-      userdata5(property.userdata5),
-      userdata6(property.userdata6),
-      userdata7(property.userdata7),
-      userdata8(property.userdata8),
-      userdata9(property.userdata9),
-      passiveNotifier(this),
-      tariffNotifier(this),
-      cashNotifier(this),
-      ipNotifier(this)
+      m_services(u.m_services),
+      traffStat(u.traffStat),
+      traffStatSaved(u.traffStatSaved),
+      settings(u.settings),
+      authorizedModificationTime(u.authorizedModificationTime),
+      messages(u.messages),
+      deleted(u.deleted),
+      lastWriteStat(u.lastWriteStat),
+      lastWriteDetailedStat(u.lastWriteDetailedStat),
+      cash(properties.cash),
+      up(properties.up),
+      down(properties.down),
+      lastCashAdd(properties.lastCashAdd),
+      passiveTime(properties.passiveTime),
+      lastCashAddTime(properties.lastCashAddTime),
+      freeMb(properties.freeMb),
+      lastActivityTime(properties.lastActivityTime),
+      password(properties.password),
+      passive(properties.passive),
+      disabled(properties.disabled),
+      disabledDetailStat(properties.disabledDetailStat),
+      alwaysOnline(properties.alwaysOnline),
+      tariffName(properties.tariffName),
+      nextTariff(properties.nextTariff),
+      address(properties.address),
+      note(properties.note),
+      group(properties.group),
+      email(properties.email),
+      phone(properties.phone),
+      realName(properties.realName),
+      credit(properties.credit),
+      creditExpire(properties.creditExpire),
+      ips(properties.ips),
+      userdata0(properties.userdata0),
+      userdata1(properties.userdata1),
+      userdata2(properties.userdata2),
+      userdata3(properties.userdata3),
+      userdata4(properties.userdata4),
+      userdata5(properties.userdata5),
+      userdata6(properties.userdata6),
+      userdata7(properties.userdata7),
+      userdata8(properties.userdata8),
+      userdata9(properties.userdata9),
+      sessionUpload(),
+      sessionDownload(),
+      sessionUploadModTime(stgTime),
+      sessionDownloadModTime(stgTime)
 {
 {
-if (&u == this)
-    return;
-
-connected = 0;
-
-deleted = u.deleted;
-
-lastWriteStat = u.lastWriteStat;
-lastWriteDetailedStat = u.lastWriteDetailedStat;
-
-settings = u.settings;
-
-property.tariffName.AddBeforeNotifier(&tariffNotifier);
-property.passive.AddBeforeNotifier(&passiveNotifier);
-property.cash.AddBeforeNotifier(&cashNotifier);
-currIP.AddAfterNotifier(&ipNotifier);
+    if (&u == this)
+        return;
 
 
-lastScanMessages = 0;
+    m_beforePassiveConn = properties.passive.beforeChange([this](auto oldVal, auto newVal){ onPassiveChange(oldVal, newVal); });
+    m_afterDisabledConn = properties.disabled.afterChange([this](auto oldVal, auto newVal){ onDisabledChange(oldVal, newVal); });
+    m_beforeTariffConn = properties.tariffName.beforeChange([this](const auto& oldVal, const auto& newVal){ onTariffChange(oldVal, newVal); });
+    m_beforeCashConn = properties.cash.beforeChange([this](auto oldVal, auto newVal){ onCashChange(oldVal, newVal); });
+    m_afterIPConn = ips.afterChange([this](const auto& oldVal, const auto& newVal){ onIPChange(oldVal, newVal); });
 
 
-property.SetProperties(u.property);
+    properties.SetProperties(u.properties);
 
 
-pthread_mutexattr_t attr;
-pthread_mutexattr_init(&attr);
-pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-pthread_mutex_init(&mutex, &attr);
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&mutex, &attr);
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-USER_IMPL::~USER_IMPL()
+UserImpl::~UserImpl()
 {
 {
-property.passive.DelBeforeNotifier(&passiveNotifier);
-property.tariffName.DelBeforeNotifier(&tariffNotifier);
 pthread_mutex_destroy(&mutex);
 }
 //-----------------------------------------------------------------------------
 pthread_mutex_destroy(&mutex);
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::SetLogin(string const & l)
+void UserImpl::SetLogin(const std::string & l)
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
+static int idGen = 0;
 assert(login.empty() && "Login is already set");
 login = l;
 assert(login.empty() && "Login is already set");
 login = l;
-id = userIDGenerator.GetNextID();
+id = idGen++;
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::ReadConf()
+int UserImpl::ReadConf()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-USER_CONF conf;
+STG_LOCKER lock(&mutex);
+UserConf conf;
 
 if (store->RestoreUserConf(&conf, login))
     {
 
 if (store->RestoreUserConf(&conf, login))
     {
@@ -242,28 +267,31 @@ if (store->RestoreUserConf(&conf, login))
     return -1;
     }
 
     return -1;
     }
 
-property.SetConf(conf);
+properties.SetConf(conf);
 
 tariff = tariffs->FindByName(tariffName);
 if (tariff == NULL)
     {
     WriteServLog("Cannot read user %s. Tariff %s not exist.",
 
 tariff = tariffs->FindByName(tariffName);
 if (tariff == NULL)
     {
     WriteServLog("Cannot read user %s. Tariff %s not exist.",
-                 login.c_str(), property.tariffName.Get().c_str());
+                 login.c_str(), properties.tariffName.Get().c_str());
     return -1;
     }
 
     return -1;
     }
 
-std::vector<STG_MSG_HDR> hdrsList;
+std::vector<Message::Header> hdrsList;
 
 if (store->GetMessageHdrs(&hdrsList, login))
     {
     printfd(__FILE__, "Error GetMessageHdrs %s\n", store->GetStrError().c_str());
 
 if (store->GetMessageHdrs(&hdrsList, login))
     {
     printfd(__FILE__, "Error GetMessageHdrs %s\n", store->GetStrError().c_str());
+    WriteServLog("Cannot read user %s. Error reading message headers: %s.",
+                 login.c_str(),
+                 store->GetStrError().c_str());
     return -1;
     }
 
     return -1;
     }
 
-std::vector<STG_MSG_HDR>::const_iterator it;
+std::vector<Message::Header>::const_iterator it;
 for (it = hdrsList.begin(); it != hdrsList.end(); ++it)
     {
 for (it = hdrsList.begin(); it != hdrsList.end(); ++it)
     {
-    STG_MSG msg;
+    Message msg;
     if (store->GetMessage(it->id, &msg, login) == 0)
         {
         messages.push_back(msg);
     if (store->GetMessage(it->id, &msg, login) == 0)
         {
         messages.push_back(msg);
@@ -273,10 +301,10 @@ for (it = hdrsList.begin(); it != hdrsList.end(); ++it)
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::ReadStat()
+int UserImpl::ReadStat()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-USER_STAT stat;
+STG_LOCKER lock(&mutex);
+UserStat stat;
 
 if (store->RestoreUserStat(&stat, login))
     {
 
 if (store->RestoreUserStat(&stat, login))
     {
@@ -287,17 +315,17 @@ if (store->RestoreUserStat(&stat, login))
     return -1;
     }
 
     return -1;
     }
 
-property.SetStat(stat);
+properties.SetStat(stat);
 
 return 0;
 }
 //-----------------------------------------------------------------------------
 
 return 0;
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::WriteConf()
+int UserImpl::WriteConf()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-USER_CONF conf(property.GetConf());
+STG_LOCKER lock(&mutex);
+UserConf conf(properties.GetConf());
 
 
-printfd(__FILE__, "USER::WriteConf()\n");
+printfd(__FILE__, "UserImpl::WriteConf()\n");
 
 if (store->SaveUserConf(conf, login))
     {
 
 if (store->SaveUserConf(conf, login))
     {
@@ -311,12 +339,10 @@ if (store->SaveUserConf(conf, login))
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::WriteStat()
+int UserImpl::WriteStat()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-USER_STAT stat(property.GetStat());
-
-printfd(__FILE__, "USER::WriteStat()\n");
+STG_LOCKER lock(&mutex);
+UserStat stat(properties.GetStat());
 
 if (store->SaveUserStat(stat, login))
     {
 
 if (store->SaveUserStat(stat, login))
     {
@@ -332,14 +358,14 @@ lastWriteStat = stgTime;
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::WriteMonthStat()
+int UserImpl::WriteMonthStat()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 time_t tt = stgTime - 3600;
 struct tm t1;
 localtime_r(&tt, &t1);
 
 time_t tt = stgTime - 3600;
 struct tm t1;
 localtime_r(&tt, &t1);
 
-USER_STAT stat(property.GetStat());
+UserStat stat(properties.GetStat());
 if (store->SaveMonthStat(stat, t1.tm_mon, t1.tm_year, login))
     {
     WriteServLog("Cannot write month stat for user %s.", login.c_str());
 if (store->SaveMonthStat(stat, t1.tm_mon, t1.tm_year, login))
     {
     WriteServLog("Cannot write month stat for user %s.", login.c_str());
@@ -352,15 +378,21 @@ if (store->SaveMonthStat(stat, t1.tm_mon, t1.tm_year, login))
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::Authorize(uint32_t ip, uint32_t dirs, const AUTH * auth)
+int UserImpl::Authorize(uint32_t ip, uint32_t dirs, const Auth * auth)
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 /*
  *  Authorize user. It only means that user will be authorized. Nothing more.
  *  User can be connected or disconnected while authorized.
  *  Example: user is authorized but disconnected due to 0 money or blocking
  */
 
 /*
  *  Authorize user. It only means that user will be authorized. Nothing more.
  *  User can be connected or disconnected while authorized.
  *  Example: user is authorized but disconnected due to 0 money or blocking
  */
 
+/*
+ * TODO: in fact "authorization" means allowing access to a service. What we
+ * call "authorization" here, int STG, is "authentication". So this should be
+ * fixed in future.
+ */
+
 /*
  * Prevent double authorization by identical authorizers
  */
 /*
  * Prevent double authorization by identical authorizers
  */
@@ -370,28 +402,25 @@ if (authorizedBy.find(auth) != authorizedBy.end())
 if (!ip)
     return -1;
 
 if (!ip)
     return -1;
 
-for (int i = 0; i < DIR_NUM; i++)
-    {
-    enabledDirs[i] = dirs & (1 << i);
-    }
+dirsFromBits(enabledDirs, dirs);
 
 
-if (authorizedBy.size())
+if (!authorizedBy.empty())
     {
     {
-    if (currIP != ip)
+    if (m_currIP != ip)
         {
         {
-        //  We are already authorized, but with different IP address
-        errorStr = "User " + login + " alredy authorized with IP address " + inet_ntostring(ip);
+        // We are already authorized, but with different IP address
+        errorStr = "User " + login + " already authorized with IP address " + inet_ntostring(ip);
         return -1;
         }
 
         return -1;
         }
 
-    USER * u = NULL;
+    User * u = NULL;
     if (!users->FindByIPIdx(ip, &u))
         {
     if (!users->FindByIPIdx(ip, &u))
         {
-        //  Address is already present in IP-index
-        //  If it's not our IP - throw an error
+        // Address presents in IP-index.
+        // If it's not our IP - report it.
         if (u != this)
             {
         if (u != this)
             {
-            errorStr = "IP address " + inet_ntostring(ip) + " alredy in use";
+            errorStr = "IP address " + inet_ntostring(ip) + " is already in use";
             return -1;
             }
         }
             return -1;
             }
         }
@@ -400,24 +429,26 @@ else
     {
     if (users->IsIPInIndex(ip))
         {
     {
     if (users->IsIPInIndex(ip))
         {
-        //  Address is already present in IP-index
-        errorStr = "IP address " + inet_ntostring(ip) + " alredy in use";
+        // Address is already present in IP-index.
+        errorStr = "IP address " + inet_ntostring(ip) + " is already in use";
         return -1;
         }
 
         return -1;
         }
 
-    if (ips.ConstData().IsIPInIPS(ip))
+    if (ips.ConstData().find(ip))
         {
         {
-        currIP = ip;
-        lastIPForDisconnect = currIP;
+        m_currIP = ip;
+        lastIPForDisconnect = m_currIP;
         }
     else
         {
         }
     else
         {
-        printfd(__FILE__, " user %s: ips = %s\n", login.c_str(), ips.ConstData().GetIpStr().c_str());
-        errorStr = "IP address " + inet_ntostring(ip) + " not belong user " + login;
+        printfd(__FILE__, " user %s: ips = %s\n", login.c_str(), ips.ConstData().toString().c_str());
+        errorStr = "IP address " + inet_ntostring(ip) + " does not belong to user " + login;
         return -1;
         }
     }
 
         return -1;
         }
     }
 
+if (authorizedBy.empty())
+    authorizedModificationTime = stgTime;
 authorizedBy.insert(auth);
 
 ScanMessage();
 authorizedBy.insert(auth);
 
 ScanMessage();
@@ -425,88 +456,103 @@ ScanMessage();
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::Unauthorize(const AUTH * auth)
+void UserImpl::Unauthorize(const Auth * auth, const std::string & reason)
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 /*
  *  Authorizer tries to unauthorize user, that was not authorized by it
  */
 if (!authorizedBy.erase(auth))
     return;
 
 /*
  *  Authorizer tries to unauthorize user, that was not authorized by it
  */
 if (!authorizedBy.erase(auth))
     return;
 
+authorizedModificationTime = stgTime;
+
 if (authorizedBy.empty())
     {
 if (authorizedBy.empty())
     {
-    lastIPForDisconnect = currIP;
-    currIP = 0; // DelUser in traffcounter
+    lastDisconnectReason = reason;
+    lastIPForDisconnect = m_currIP;
+    m_currIP = 0; // DelUser in traffcounter
+    if (m_connected)
+        Disconnect(false, "not authorized");
     return;
     }
 }
 //-----------------------------------------------------------------------------
     return;
     }
 }
 //-----------------------------------------------------------------------------
-bool USER_IMPL::IsAuthorizedBy(const AUTH * auth) const
+bool UserImpl::IsAuthorizedBy(const Auth * auth) const
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-//  Is this user authorized by specified authorizer?
+STG_LOCKER lock(&mutex);
+// Is this user authorized by specified authorizer?
 return authorizedBy.find(auth) != authorizedBy.end();
 }
 //-----------------------------------------------------------------------------
 return authorizedBy.find(auth) != authorizedBy.end();
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::Connect(bool fakeConnect)
+std::vector<std::string> UserImpl::GetAuthorizers() const
+{
+    STG_LOCKER lock(&mutex);
+    std::vector<std::string> list;
+    std::transform(authorizedBy.begin(), authorizedBy.end(), std::back_inserter(list), [](const auto auth){ return auth->GetVersion(); });
+    return list;
+}
+//-----------------------------------------------------------------------------
+void UserImpl::Connect(bool fakeConnect)
 {
 /*
 {
 /*
- *  Connect user to Internet. This function is differ from Authorize() !!!
+ * Connect user to Internet. This function is differ from Authorize() !!!
  */
 
  */
 
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (!fakeConnect)
     {
 
 if (!fakeConnect)
     {
-    string scriptOnConnect = settings->GetScriptsDir() + "/OnConnect";
+    std::string scriptOnConnect = settings->GetScriptsDir() + "/OnConnect";
 
     if (access(scriptOnConnect.c_str(), X_OK) == 0)
         {
 
     if (access(scriptOnConnect.c_str(), X_OK) == 0)
         {
-        char dirsStr[DIR_NUM + 1];
-        dirsStr[DIR_NUM] = 0;
-        for (int i = 0; i < DIR_NUM; i++)
-            {
-            dirsStr[i] = enabledDirs[i] ? '1' : '0';
-            }
+        std::string dirs = dirsToString(enabledDirs);
 
 
-        string scriptOnConnectParams;
+        std::string scriptOnConnectParams;
         strprintf(&scriptOnConnectParams,
         strprintf(&scriptOnConnectParams,
-                "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
-                scriptOnConnect.c_str(),
-                login.c_str(),
-                inet_ntostring(currIP).c_str(),
-                (double)cash,
-                id,
-                dirsStr);
+                  "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
+                  scriptOnConnect.c_str(),
+                  login.c_str(),
+                  inet_ntostring(m_currIP).c_str(),
+                  cash.ConstData(),
+                  id,
+                  dirs.c_str());
+
+        std::vector<std::string>::const_iterator it(settings->GetScriptParams().begin());
+        while (it != settings->GetScriptParams().end())
+            {
+            scriptOnConnectParams += " \"" + GetParamValue(it->c_str()) + "\"";
+            ++it;
+            }
 
 
-        ScriptExec(scriptOnConnectParams);
+        ScriptExec(scriptOnConnectParams.c_str());
         }
     else
         {
         WriteServLog("Script %s cannot be executed. File not found.", scriptOnConnect.c_str());
         }
 
         }
     else
         {
         WriteServLog("Script %s cannot be executed. File not found.", scriptOnConnect.c_str());
         }
 
-    connected = true;
+    m_connected = true;
     }
 
     }
 
-if (store->WriteUserConnect(login, currIP))
+if (!settings->GetDisableSessionLog() && store->WriteUserConnect(login, m_currIP))
     {
     WriteServLog("Cannot write connect for user %s.", login.c_str());
     WriteServLog("%s", store->GetStrError().c_str());
     }
 
 if (!fakeConnect)
     {
     WriteServLog("Cannot write connect for user %s.", login.c_str());
     WriteServLog("%s", store->GetStrError().c_str());
     }
 
 if (!fakeConnect)
-    lastIPForDisconnect = currIP;
+    lastIPForDisconnect = m_currIP;
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::Disconnect(bool fakeDisconnect, const std::string & reason)
+void UserImpl::Disconnect(bool fakeDisconnect, const std::string & reason)
 {
 /*
  *  Disconnect user from Internet. This function is differ from UnAuthorize() !!!
  */
 
 {
 /*
  *  Disconnect user from Internet. This function is differ from UnAuthorize() !!!
  */
 
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (!lastIPForDisconnect)
     {
 
 if (!lastIPForDisconnect)
     {
@@ -516,38 +562,46 @@ if (!lastIPForDisconnect)
 
 if (!fakeDisconnect)
     {
 
 if (!fakeDisconnect)
     {
-    string scriptOnDisonnect = settings->GetScriptsDir() + "/OnDisconnect";
+    lastDisconnectReason = reason;
+    std::string scriptOnDisonnect = settings->GetScriptsDir() + "/OnDisconnect";
 
     if (access(scriptOnDisonnect.c_str(), X_OK) == 0)
         {
 
     if (access(scriptOnDisonnect.c_str(), X_OK) == 0)
         {
-        char dirsStr[DIR_NUM + 1];
-        dirsStr[DIR_NUM] = 0;
-        for (int i = 0; i < DIR_NUM; i++)
-            {
-            dirsStr[i] = enabledDirs[i] ? '1' : '0';
-            }
+        std::string dirs = dirsToString(enabledDirs);
 
 
-        string scriptOnDisonnectParams;
+        std::string scriptOnDisonnectParams;
         strprintf(&scriptOnDisonnectParams,
                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
                 scriptOnDisonnect.c_str(),
                 login.c_str(),
                 inet_ntostring(lastIPForDisconnect).c_str(),
         strprintf(&scriptOnDisonnectParams,
                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
                 scriptOnDisonnect.c_str(),
                 login.c_str(),
                 inet_ntostring(lastIPForDisconnect).c_str(),
-                (double)cash,
+                cash.ConstData(),
                 id,
                 id,
-                dirsStr);
+                dirs.c_str());
+
+        std::vector<std::string>::const_iterator it(settings->GetScriptParams().begin());
+        while (it != settings->GetScriptParams().end())
+            {
+            scriptOnDisonnectParams += " \"" + GetParamValue(it->c_str()) + "\"";
+            ++it;
+            }
 
 
-        ScriptExec(scriptOnDisonnectParams);
+        ScriptExec(scriptOnDisonnectParams.c_str());
         }
     else
         {
         WriteServLog("Script OnDisconnect cannot be executed. File not found.");
         }
 
         }
     else
         {
         WriteServLog("Script OnDisconnect cannot be executed. File not found.");
         }
 
-    connected = false;
+    m_connected = false;
     }
 
     }
 
-if (store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload, cash, freeMb, reason))
+std::string reasonMessage(reason);
+if (!lastDisconnectReason.empty())
+    reasonMessage += ": " + lastDisconnectReason;
+
+if (!settings->GetDisableSessionLog() && store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload,
+                                                                    cash, freeMb, reasonMessage))
     {
     WriteServLog("Cannot write disconnect for user %s.", login.c_str());
     WriteServLog("%s", store->GetStrError().c_str());
     {
     WriteServLog("Cannot write disconnect for user %s.", login.c_str());
     WriteServLog("%s", store->GetStrError().c_str());
@@ -556,56 +610,19 @@ if (store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload,
 if (!fakeDisconnect)
     lastIPForDisconnect = 0;
 
 if (!fakeDisconnect)
     lastIPForDisconnect = 0;
 
-DIR_TRAFF zeroSesssion;
-
-sessionUpload = zeroSesssion;
-sessionDownload = zeroSesssion;
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::PrintUser() const
-{
-//return;
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-cout << "============================================================" << endl;
-cout << "id=" << id << endl;
-cout << "login=" << login << endl;
-cout << "password=" << password << endl;
-cout << "passive=" << passive << endl;
-cout << "disabled=" << disabled << endl;
-cout << "disabledDetailStat=" << disabledDetailStat << endl;
-cout << "alwaysOnline=" << alwaysOnline << endl;
-cout << "tariffName=" << tariffName << endl;
-cout << "address=" << address << endl;
-cout << "phone=" << phone << endl;
-cout << "email=" << email << endl;
-cout << "note=" << note << endl;
-cout << "realName=" <<realName << endl;
-cout << "group=" << group << endl;
-cout << "credit=" << credit << endl;
-cout << "nextTariff=" << nextTariff << endl;
-cout << "userdata0" << userdata0 << endl;
-cout << "userdata1" << userdata1 << endl;
-cout << "creditExpire=" << creditExpire << endl;
-cout << "ips=" << ips << endl;
-cout << "------------------------" << endl;
-cout << "up=" << up << endl;
-cout << "down=" << down << endl;
-cout << "cash=" << cash << endl;
-cout << "freeMb=" << freeMb << endl;
-cout << "lastCashAdd=" << lastCashAdd << endl;
-cout << "lastCashAddTime=" << lastCashAddTime << endl;
-cout << "passiveTime=" << passiveTime << endl;
-cout << "lastActivityTime=" << lastActivityTime << endl;
-cout << "============================================================" << endl;
+sessionUpload.reset();
+sessionDownload.reset();
+sessionUploadModTime = stgTime;
+sessionDownloadModTime = stgTime;
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::Run()
+void UserImpl::Run()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-if (stgTime > static_cast<time_t>(lastWriteStat + settings->GetStatWritePeriod()))
+if (stgTime > lastWriteStat + settings->GetStatWritePeriod())
     {
     {
-    printfd(__FILE__, "USER::WriteStat user=%s\n", GetLogin().c_str());
+    printfd(__FILE__, "UserImpl::WriteStat user=%s\n", GetLogin().c_str());
     WriteStat();
     }
 if (creditExpire.ConstData() && creditExpire.ConstData() < stgTime)
     WriteStat();
     }
 if (creditExpire.ConstData() && creditExpire.ConstData() < stgTime)
@@ -626,15 +643,13 @@ if (passive.ConstData()
 
 if (!authorizedBy.empty())
     {
 
 if (!authorizedBy.empty())
     {
-    if (connected)
-        {
-        lastActivityTime = *const_cast<time_t *>(&stgTime);
-        }
-    if (!connected && IsInetable())
-        {
+    if (m_connected)
+        properties.Stat().lastActivityTime = stgTime;
+
+    if (!m_connected && IsInetable())
         Connect();
         Connect();
-        }
-    if (connected && !IsInetable())
+
+    if (m_connected && !IsInetable())
         {
         if (disabled)
             Disconnect(false, "disabled");
         {
         if (disabled)
             Disconnect(false, "disabled");
@@ -652,28 +667,23 @@ if (!authorizedBy.empty())
     }
 else
     {
     }
 else
     {
-    if (connected)
-        {
+    if (m_connected)
         Disconnect(false, "not authorized");
         Disconnect(false, "not authorized");
-        }
     }
 
 }
 //-----------------------------------------------------------------------------
     }
 
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::UpdatePingTime(time_t t)
+void UserImpl::UpdatePingTime(time_t t)
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-//printfd(__FILE__, "UpdatePingTime(%d) %s\n", t, login.c_str());
+STG_LOCKER lock(&mutex);
 if (t)
     pingTime = t;
 else
     pingTime = stgTime;
 }
 //-----------------------------------------------------------------------------
 if (t)
     pingTime = t;
 else
     pingTime = stgTime;
 }
 //-----------------------------------------------------------------------------
-bool USER_IMPL::IsInetable()
+bool UserImpl::IsInetable()
 {
 {
-//STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-
 if (disabled || passive)
     return false;
 
 if (disabled || passive)
     return false;
 
@@ -683,37 +693,30 @@ if (settings->GetFreeMbAllowInet())
         return true;
     }
 
         return true;
     }
 
-if (settings->GetShowFeeInCash())
-    {
+if (settings->GetShowFeeInCash() || tariff == NULL)
     return (cash >= -credit);
     return (cash >= -credit);
-    }
 
 return (cash - tariff->GetFee() >= -credit);
 }
 //-----------------------------------------------------------------------------
 
 return (cash - tariff->GetFee() >= -credit);
 }
 //-----------------------------------------------------------------------------
-string USER_IMPL::GetEnabledDirs()
+std::string UserImpl::GetEnabledDirs() const
 {
 {
-//STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-
-string dirs = "";
-for(int i = 0; i < DIR_NUM; i++)
-    dirs += enabledDirs[i] ? "1" : "0";
-return dirs;
+return dirsToString(enabledDirs);
 }
 //-----------------------------------------------------------------------------
 #ifdef TRAFF_STAT_WITH_PORTS
 }
 //-----------------------------------------------------------------------------
 #ifdef TRAFF_STAT_WITH_PORTS
-void USER_IMPL::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
+void UserImpl::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
 #else
 #else
-void USER_IMPL::AddTraffStatU(int dir, uint32_t ip, uint32_t len)
+void UserImpl::AddTraffStatU(int dir, uint32_t ip, uint32_t len)
 #endif
 {
 #endif
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-if (!connected)
+if (!m_connected || tariff == NULL)
     return;
 
 double cost = 0;
     return;
 
 double cost = 0;
-DIR_TRAFF dt(up);
+DirTraff dt(up);
 
 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
 
 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
@@ -721,10 +724,10 @@ int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
 dt[dir] += len;
 
 int tt = tariff->GetTraffType();
 dt[dir] += len;
 
 int tt = tariff->GetTraffType();
-if (tt == TRAFF_UP ||
-    tt == TRAFF_UP_DOWN ||
+if (tt == Tariff::TRAFF_UP ||
+    tt == Tariff::TRAFF_UP_DOWN ||
     // Check NEW traff data
     // Check NEW traff data
-    (tt == TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
+    (tt == Tariff::TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
     {
     double dc = 0;
     if (traff < threshold &&
     {
     double dc = 0;
     if (traff < threshold &&
@@ -757,14 +760,15 @@ if (tt == TRAFF_UP ||
         cost = dc - freeMb.ConstData();
 
     // Direct access to internal data structures via friend-specifier
         cost = dc - freeMb.ConstData();
 
     // Direct access to internal data structures via friend-specifier
-    property.Stat().freeMb -= dc;
-    property.Stat().cash -= cost;
+    properties.Stat().freeMb -= dc;
+    properties.Stat().cash -= cost;
     cash.ModifyTime();
     freeMb.ModifyTime();
     }
 
 up = dt;
 sessionUpload[dir] += len;
     cash.ModifyTime();
     freeMb.ModifyTime();
     }
 
 up = dt;
 sessionUpload[dir] += len;
+sessionUploadModTime = stgTime;
 
 //Add detailed stat
 
 
 //Add detailed stat
 
@@ -773,18 +777,18 @@ if (!settings->GetWriteFreeMbTraffCost() &&
     cost = 0;
 
 #ifdef TRAFF_STAT_WITH_PORTS
     cost = 0;
 
 #ifdef TRAFF_STAT_WITH_PORTS
-IP_DIR_PAIR idp(ip, dir, port);
+IPDirPair idp(ip, dir, port);
 #else
 #else
-IP_DIR_PAIR idp(ip, dir);
+IPDirPair idp(ip, dir);
 #endif
 
 #endif
 
-map<IP_DIR_PAIR, STAT_NODE>::iterator lb;
+std::map<IPDirPair, StatNode>::iterator lb;
 lb = traffStat.lower_bound(idp);
 if (lb == traffStat.end() || lb->first != idp)
     {
     traffStat.insert(lb,
 lb = traffStat.lower_bound(idp);
 if (lb == traffStat.end() || lb->first != idp)
     {
     traffStat.insert(lb,
-                     pair<IP_DIR_PAIR, STAT_NODE>(idp,
-                                                  STAT_NODE(len, 0, cost)));
+                     std::make_pair(idp,
+                                    StatNode(len, 0, cost)));
     }
 else
     {
     }
 else
     {
@@ -794,18 +798,18 @@ else
 }
 //-----------------------------------------------------------------------------
 #ifdef TRAFF_STAT_WITH_PORTS
 }
 //-----------------------------------------------------------------------------
 #ifdef TRAFF_STAT_WITH_PORTS
-void USER_IMPL::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
+void UserImpl::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
 #else
 #else
-void USER_IMPL::AddTraffStatD(int dir, uint32_t ip, uint32_t len)
+void UserImpl::AddTraffStatD(int dir, uint32_t ip, uint32_t len)
 #endif
 {
 #endif
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-if (!connected)
+if (!m_connected || tariff == NULL)
     return;
 
 double cost = 0;
     return;
 
 double cost = 0;
-DIR_TRAFF dt(down);
+DirTraff dt(down);
 
 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
 
 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
@@ -813,10 +817,10 @@ int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
 dt[dir] += len;
 
 int tt = tariff->GetTraffType();
 dt[dir] += len;
 
 int tt = tariff->GetTraffType();
-if (tt == TRAFF_DOWN ||
-    tt == TRAFF_UP_DOWN ||
+if (tt == Tariff::TRAFF_DOWN ||
+    tt == Tariff::TRAFF_UP_DOWN ||
     // Check NEW traff data
     // Check NEW traff data
-    (tt == TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
+    (tt == Tariff::TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
     {
     double dc = 0;
     if (traff < threshold &&
     {
     double dc = 0;
     if (traff < threshold &&
@@ -848,14 +852,15 @@ if (tt == TRAFF_DOWN ||
     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
         cost = dc - freeMb.ConstData();
 
     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
         cost = dc - freeMb.ConstData();
 
-    property.Stat().freeMb -= dc;
-    property.Stat().cash -= cost;
+    properties.Stat().freeMb -= dc;
+    properties.Stat().cash -= cost;
     cash.ModifyTime();
     freeMb.ModifyTime();
     }
 
 down = dt;
 sessionDownload[dir] += len;
     cash.ModifyTime();
     freeMb.ModifyTime();
     }
 
 down = dt;
 sessionDownload[dir] += len;
+sessionDownloadModTime = stgTime;
 
 //Add detailed stat
 
 
 //Add detailed stat
 
@@ -864,18 +869,18 @@ if (!settings->GetWriteFreeMbTraffCost() &&
     cost = 0;
 
 #ifdef TRAFF_STAT_WITH_PORTS
     cost = 0;
 
 #ifdef TRAFF_STAT_WITH_PORTS
-IP_DIR_PAIR idp(ip, dir, port);
+IPDirPair idp(ip, dir, port);
 #else
 #else
-IP_DIR_PAIR idp(ip, dir);
+IPDirPair idp(ip, dir);
 #endif
 
 #endif
 
-map<IP_DIR_PAIR, STAT_NODE>::iterator lb;
+std::map<IPDirPair, StatNode>::iterator lb;
 lb = traffStat.lower_bound(idp);
 if (lb == traffStat.end() || lb->first != idp)
     {
     traffStat.insert(lb,
 lb = traffStat.lower_bound(idp);
 if (lb == traffStat.end() || lb->first != idp)
     {
     traffStat.insert(lb,
-                     pair<IP_DIR_PAIR, STAT_NODE>(idp,
-                                                  STAT_NODE(0, len, cost)));
+                     std::make_pair(idp,
+                                    StatNode(0, len, cost)));
     }
 else
     {
     }
 else
     {
@@ -884,69 +889,17 @@ else
     }
 }
 //-----------------------------------------------------------------------------
     }
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::AddCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-currIP.AddBeforeNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::DelCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-currIP.DelBeforeNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::AddCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-currIP.AddAfterNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::DelCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-currIP.DelAfterNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::AddConnectedBeforeNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
+void UserImpl::OnAdd()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-connected.AddBeforeNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::DelConnectedBeforeNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-connected.DelBeforeNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::AddConnectedAfterNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-connected.AddAfterNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::DelConnectedAfterNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-connected.DelAfterNotifier(n);
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::OnAdd()
-{
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-string scriptOnAdd = settings->GetScriptsDir() + "/OnUserAdd";
+std::string scriptOnAdd = settings->GetScriptsDir() + "/OnUserAdd";
 
 if (access(scriptOnAdd.c_str(), X_OK) == 0)
     {
 
 if (access(scriptOnAdd.c_str(), X_OK) == 0)
     {
-    string scriptOnAddParams;
-    strprintf(&scriptOnAddParams,
-            "%s \"%s\"",
-            scriptOnAdd.c_str(),
-            login.c_str());
+    std::string scriptOnAddParams = scriptOnAdd + " \"" + login + "\"";
 
 
-    ScriptExec(scriptOnAddParams);
+    ScriptExec(scriptOnAddParams.c_str());
     }
 else
     {
     }
 else
     {
@@ -954,21 +907,17 @@ else
     }
 }
 //-----------------------------------------------------------------------------
     }
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::OnDelete()
+void UserImpl::OnDelete()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-string scriptOnDel = settings->GetScriptsDir() + "/OnUserDel";
+std::string scriptOnDel = settings->GetScriptsDir() + "/OnUserDel";
 
 if (access(scriptOnDel.c_str(), X_OK) == 0)
     {
 
 if (access(scriptOnDel.c_str(), X_OK) == 0)
     {
-    string scriptOnDelParams;
-    strprintf(&scriptOnDelParams,
-            "%s \"%s\"",
-            scriptOnDel.c_str(),
-            login.c_str());
+    std::string scriptOnDelParams = scriptOnDel + " \"" + login + "\"";
 
 
-    ScriptExec(scriptOnDelParams);
+    ScriptExec(scriptOnDelParams.c_str());
     }
 else
     {
     }
 else
     {
@@ -978,15 +927,15 @@ else
 Run();
 }
 //-----------------------------------------------------------------------------
 Run();
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::WriteDetailStat(bool hard)
+int UserImpl::WriteDetailStat(bool hard)
 {
 {
-printfd(__FILE__, "USER::WriteDetailedStat() - saved size = %d\n", traffStatSaved.second.size());
+printfd(__FILE__, "UserImpl::WriteDetailedStat() - saved size = %d\n", traffStatSaved.second.size());
 
 if (!traffStatSaved.second.empty())
     {
     if (store->WriteDetailedStat(traffStatSaved.second, traffStatSaved.first, login))
         {
 
 if (!traffStatSaved.second.empty())
     {
     if (store->WriteDetailedStat(traffStatSaved.second, traffStatSaved.first, login))
         {
-        printfd(__FILE__, "USER::WriteDetailStat() - failed to write detail stat from queue\n");
+        printfd(__FILE__, "UserImpl::WriteDetailStat() - failed to write detail stat from queue\n");
         WriteServLog("Cannot write detail stat from queue (of size %d recs) for user %s.", traffStatSaved.second.size(), login.c_str());
         WriteServLog("%s", store->GetStrError().c_str());
         return -1;
         WriteServLog("Cannot write detail stat from queue (of size %d recs) for user %s.", traffStatSaved.second.size(), login.c_str());
         WriteServLog("%s", store->GetStrError().c_str());
         return -1;
@@ -994,26 +943,26 @@ if (!traffStatSaved.second.empty())
     traffStatSaved.second.erase(traffStatSaved.second.begin(), traffStatSaved.second.end());
     }
 
     traffStatSaved.second.erase(traffStatSaved.second.begin(), traffStatSaved.second.end());
     }
 
-TRAFF_STAT ts;
+TraffStat ts;
 
     {
 
     {
-    STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+    STG_LOCKER lock(&mutex);
     ts.swap(traffStat);
     }
 
     ts.swap(traffStat);
     }
 
-printfd(__FILE__, "USER::WriteDetailedStat() - size = %d\n", ts.size());
+printfd(__FILE__, "UserImpl::WriteDetailedStat() - size = %d\n", ts.size());
 
 if (ts.size() && !disabledDetailStat)
     {
     if (store->WriteDetailedStat(ts, lastWriteDetailedStat, login))
         {
 
 if (ts.size() && !disabledDetailStat)
     {
     if (store->WriteDetailedStat(ts, lastWriteDetailedStat, login))
         {
-        printfd(__FILE__, "USER::WriteDetailStat() - failed to write current detail stat\n");
+        printfd(__FILE__, "UserImpl::WriteDetailStat() - failed to write current detail stat\n");
         WriteServLog("Cannot write detail stat for user %s.", login.c_str());
         WriteServLog("%s", store->GetStrError().c_str());
         if (!hard)
             {
         WriteServLog("Cannot write detail stat for user %s.", login.c_str());
         WriteServLog("%s", store->GetStrError().c_str());
         if (!hard)
             {
-            printfd(__FILE__, "USER::WriteDetailStat() - pushing detail stat to queue\n");
-            STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+            printfd(__FILE__, "UserImpl::WriteDetailStat() - pushing detail stat to queue\n");
+            STG_LOCKER lock(&mutex);
             traffStatSaved.second.swap(ts);
             traffStatSaved.first = lastWriteDetailedStat;
             }
             traffStatSaved.second.swap(ts);
             traffStatSaved.first = lastWriteDetailedStat;
             }
@@ -1024,12 +973,11 @@ lastWriteDetailedStat = stgTime;
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-double USER_IMPL::GetPassiveTimePart() const
+double UserImpl::GetPassiveTimePart() const
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-static int daysInMonth[12] =
-{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static const std::array<unsigned, 12> daysInMonth{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
 struct tm tms;
 time_t t = stgTime;
 
 struct tm tms;
 time_t t = stgTime;
@@ -1043,98 +991,128 @@ if (tms.tm_year % 4 == 0 && tms.tm_mon == 1)
     secMonth += 24 * 3600;
     }
 
     secMonth += 24 * 3600;
     }
 
-int dt = secMonth - passiveTime;
+time_t dt = secMonth - passiveTime;
 
 if (dt < 0)
     dt = 0;
 
 
 if (dt < 0)
     dt = 0;
 
-return double(dt) / (secMonth);
+return static_cast<double>(dt) / secMonth;
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::SetPassiveTimeAsNewUser()
+void UserImpl::SetPassiveTimeAsNewUser()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 time_t t = stgTime;
 struct tm tm;
 localtime_r(&t, &tm);
 int daysCurrMon = DaysInCurrentMonth();
 
 time_t t = stgTime;
 struct tm tm;
 localtime_r(&t, &tm);
 int daysCurrMon = DaysInCurrentMonth();
-double pt = (tm.tm_mday - 1) / (double)daysCurrMon;
+double pt = tm.tm_mday - 1;
+pt /= daysCurrMon;
 
 
-passiveTime = (time_t)(pt * 24 * 3600 * daysCurrMon);
+passiveTime = static_cast<time_t>(pt * 24 * 3600 * daysCurrMon);
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::MidnightResetSessionStat()
+void UserImpl::MidnightResetSessionStat()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 
-if (connected)
+if (m_connected)
     {
     Disconnect(true, "fake");
     Connect(true);
     }
 }
 //-----------------------------------------------------------------------------
     {
     Disconnect(true, "fake");
     Connect(true);
     }
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::ProcessNewMonth()
+void UserImpl::ProcessNewMonth()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 //  Reset traff
 //  Reset traff
-if (connected)
-    {
+if (m_connected)
     Disconnect(true, "fake");
     Disconnect(true, "fake");
-    }
-DIR_TRAFF zeroTarff;
 
 WriteMonthStat();
 
 
 WriteMonthStat();
 
-up = zeroTarff;
-down = zeroTarff;
+properties.Stat().monthUp.reset();
+properties.Stat().monthDown.reset();
 
 
-if (connected)
-    {
+if (m_connected)
     Connect(true);
     Connect(true);
-    }
 
 //  Set new tariff
 if (nextTariff.ConstData() != "")
     {
 
 //  Set new tariff
 if (nextTariff.ConstData() != "")
     {
-    const TARIFF * nt;
-    nt = tariffs->FindByName(nextTariff);
+    const Tariff * nt = tariffs->FindByName(nextTariff);
     if (nt == NULL)
         {
         WriteServLog("Cannot change tariff for user %s. Tariff %s not exist.",
     if (nt == NULL)
         {
         WriteServLog("Cannot change tariff for user %s. Tariff %s not exist.",
-                     login.c_str(), property.tariffName.Get().c_str());
+                     login.c_str(), properties.tariffName.Get().c_str());
         }
     else
         {
         }
     else
         {
-        property.tariffName.Set(nextTariff, sysAdmin, login, store);
-        tariff = nt;
+        std::string message = tariff->TariffChangeIsAllowed(*nt, stgTime);
+        if (message.empty())
+            {
+            properties.tariffName.Set(nextTariff, *sysAdmin, login, *store);
+            }
+        else
+            {
+            WriteServLog("Tariff change is prohibited for user %s. %s",
+                         login.c_str(),
+                         message.c_str());
+            }
         }
     ResetNextTariff();
     WriteConf();
     }
 }
 //-----------------------------------------------------------------------------
         }
     ResetNextTariff();
     WriteConf();
     }
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::ProcessDayFeeSpread()
+void UserImpl::ProcessDayFeeSpread()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
+
+if (passive.ConstData() || tariff == NULL)
+    return;
 
 
-if (passive.ConstData())
+if (tariff->GetPeriod() != Tariff::MONTH)
     return;
 
     return;
 
-double f = tariff->GetFee() / DaysInCurrentMonth();
+double fee = tariff->GetFee() / DaysInCurrentMonth();
 
 
-if (f == 0.0)
+if (std::fabs(fee) < 1.0e-3)
     return;
 
 double c = cash;
     return;
 
 double c = cash;
-property.cash.Set(c - f, sysAdmin, login, store, "Subscriber fee charge");
+switch (settings->GetFeeChargeType())
+    {
+    case 0:
+        properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    case 1:
+        if (c + credit >= 0)
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    case 2:
+        if (c + credit >= fee)
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    case 3:
+        if (c >= 0)
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    }
 ResetPassiveTime();
 }
 //-----------------------------------------------------------------------------
 ResetPassiveTime();
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::ProcessDayFee()
+void UserImpl::ProcessDayFee()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
+
+if (tariff == NULL)
+    return;
+
+if (tariff->GetPeriod() != Tariff::MONTH)
+    return;
 
 double passiveTimePart = 1.0;
 if (!settings->GetFullFee())
 
 double passiveTimePart = 1.0;
 if (!settings->GetFullFee())
@@ -1149,32 +1127,165 @@ else
         return;
         }
     }
         return;
         }
     }
-double f = tariff->GetFee() * passiveTimePart;
+double fee = tariff->GetFee() * passiveTimePart;
 
 ResetPassiveTime();
 
 
 ResetPassiveTime();
 
-if (f == 0.0)
+if (std::fabs(fee) < 1.0e-3)
+    {
+    SetPrepaidTraff();
     return;
     return;
+    }
 
 double c = cash;
 
 double c = cash;
-printfd(__FILE__, "login: %8s   Fee=%f PassiveTimePart=%f fee=%f\n",
+printfd(__FILE__, "login: %8s Cash=%f Credit=%f  Fee=%f PassiveTimePart=%f fee=%f\n",
         login.c_str(),
         login.c_str(),
+        cash.ConstData(),
+        credit.ConstData(),
         tariff->GetFee(),
         passiveTimePart,
         tariff->GetFee(),
         passiveTimePart,
-        f);
-property.cash.Set(c - f, sysAdmin, login, store, "Subscriber fee charge");
+        fee);
+switch (settings->GetFeeChargeType())
+    {
+    case 0:
+        properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        SetPrepaidTraff();
+        break;
+    case 1:
+        if (c + credit >= 0)
+            {
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+            SetPrepaidTraff();
+            }
+        break;
+    case 2:
+        if (c + credit >= fee)
+            {
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+            SetPrepaidTraff();
+            }
+        break;
+    case 3:
+        if (c >= 0)
+            {
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+            SetPrepaidTraff();
+            }
+        break;
+    }
+}
+//-----------------------------------------------------------------------------
+void UserImpl::ProcessDailyFee()
+{
+STG_LOCKER lock(&mutex);
+
+if (passive.ConstData() || tariff == NULL)
+    return;
+
+if (tariff->GetPeriod() != Tariff::DAY)
+    return;
+
+double fee = tariff->GetFee();
+
+if (fee == 0.0)
+    return;
+
+double c = cash;
+switch (settings->GetFeeChargeType())
+    {
+    case 0:
+        properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    case 1:
+        if (c + credit >= 0)
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    case 2:
+        if (c + credit >= fee)
+            properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+        break;
+    }
+ResetPassiveTime();
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::SetPrepaidTraff()
+void UserImpl::ProcessServices()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+struct tm tms;
+time_t t = stgTime;
+localtime_r(&t, &tms);
+
+double passiveTimePart = 1.0;
+if (!settings->GetFullFee())
+    {
+    passiveTimePart = GetPassiveTimePart();
+    }
+else
+    {
+    if (passive.ConstData())
+        {
+        printfd(__FILE__, "Don't charge fee `cause we are passive\n");
+        return;
+        }
+    }
 
 
-property.freeMb.Set(tariff->GetFree(), sysAdmin, login, store, "Prepaid traffic");
+for (size_t i = 0; i < properties.Conf().services.size(); ++i)
+    {
+    ServiceConf conf;
+    if (m_services.Find(properties.Conf().services[i], &conf))
+        continue;
+    if (conf.payDay == tms.tm_mday ||
+        (conf.payDay == 0 && tms.tm_mday == DaysInCurrentMonth()))
+        {
+        double c = cash;
+        double fee = conf.cost * passiveTimePart;
+        printfd(__FILE__, "Service fee. login: %8s Cash=%f Credit=%f  Fee=%f PassiveTimePart=%f fee=%f\n",
+                login.c_str(),
+                cash.ConstData(),
+                credit.ConstData(),
+                tariff->GetFee(),
+                passiveTimePart,
+                fee);
+        switch (settings->GetFeeChargeType())
+            {
+            case 0:
+                properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+                SetPrepaidTraff();
+                break;
+            case 1:
+                if (c + credit >= 0)
+                    {
+                    properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+                    SetPrepaidTraff();
+                    }
+                break;
+            case 2:
+                if (c + credit >= fee)
+                    {
+                    properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+                    SetPrepaidTraff();
+                    }
+                break;
+            case 3:
+                if (c >= 0)
+                    {
+                    properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
+                    SetPrepaidTraff();
+                    }
+                break;
+            }
+        }
+    }
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::AddMessage(STG_MSG * msg)
+void UserImpl::SetPrepaidTraff()
 {
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+if (tariff != NULL)
+    properties.freeMb.Set(tariff->GetFree(), *sysAdmin, login, *store, "Prepaid traffic");
+}
+//-----------------------------------------------------------------------------
+int UserImpl::AddMessage(Message * msg)
+{
+STG_LOCKER lock(&mutex);
 
 if (SendMessage(*msg))
     {
 
 if (SendMessage(*msg))
     {
@@ -1194,9 +1305,9 @@ else
         msg->header.repeat--;
         #ifndef DEBUG
         //TODO: gcc v. 4.x generate ICE on x86_64
         msg->header.repeat--;
         #ifndef DEBUG
         //TODO: gcc v. 4.x generate ICE on x86_64
-        msg->header.lastSendTime = time(NULL);
+        msg->header.lastSendTime = static_cast<int>(time(NULL));
         #else
         #else
-        msg->header.lastSendTime = stgTime;
+        msg->header.lastSendTime = static_cast<int>(stgTime);
         #endif
         if (store->AddMessage(msg, login))
             {
         #endif
         if (store->AddMessage(msg, login))
             {
@@ -1211,35 +1322,35 @@ else
 return 0;
 }
 //-----------------------------------------------------------------------------
 return 0;
 }
 //-----------------------------------------------------------------------------
-int USER_IMPL::SendMessage(STG_MSG & msg) const
+int UserImpl::SendMessage(Message & msg) const
 {
 // No lock `cause we are already locked from caller
 int ret = -1;
 {
 // No lock `cause we are already locked from caller
 int ret = -1;
-set<const AUTH*>::iterator it(authorizedBy.begin());
+std::set<const Auth*>::iterator it(authorizedBy.begin());
 while (it != authorizedBy.end())
     {
 while (it != authorizedBy.end())
     {
-    if (!(*it++)->SendMessage(msg, currIP))
+    if (!(*it++)->SendMessage(msg, m_currIP))
         ret = 0;
     }
 if (!ret)
     {
 #ifndef DEBUG
     //TODO: gcc v. 4.x generate ICE on x86_64
         ret = 0;
     }
 if (!ret)
     {
 #ifndef DEBUG
     //TODO: gcc v. 4.x generate ICE on x86_64
-    msg.header.lastSendTime = time(NULL);
+    msg.header.lastSendTime = static_cast<int>(time(NULL));
 #else
 #else
-    msg.header.lastSendTime = stgTime;
+    msg.header.lastSendTime = static_cast<int>(stgTime);
 #endif
     msg.header.repeat--;
     }
 return ret;
 }
 //-----------------------------------------------------------------------------
 #endif
     msg.header.repeat--;
     }
 return ret;
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::ScanMessage()
+void UserImpl::ScanMessage()
 {
 // No lock `cause we are already locked from caller
 // We need not check for the authorizedBy `cause it has already checked by caller
 
 {
 // No lock `cause we are already locked from caller
 // We need not check for the authorizedBy `cause it has already checked by caller
 
-std::list<STG_MSG>::iterator it(messages.begin());
+auto it = messages.begin();
 while (it != messages.end())
     {
     if (settings->GetMessageTimeout() > 0 &&
 while (it != messages.end())
     {
     if (settings->GetMessageTimeout() > 0 &&
@@ -1281,40 +1392,83 @@ while (it != messages.end())
             ++it;
             }
         }
             ++it;
             }
         }
+    else
+        {
+        ++it;
+        }
     }
 }
 //-----------------------------------------------------------------------------
     }
 }
 //-----------------------------------------------------------------------------
+std::string UserImpl::GetParamValue(const std::string & name) const
+{
+    std::string lowerName = ToLower(name);
+    if (lowerName == "id")
+        {
+        std::ostringstream stream;
+        stream << id;
+        return stream.str();
+        }
+    if (lowerName == "login")       return login;
+    if (lowerName == "currip")      return m_currIP.ToString();
+    if (lowerName == "enableddirs") return GetEnabledDirs();
+    if (lowerName == "tariff")      return properties.tariffName;
+    if (properties.Exists(lowerName))
+        return properties.GetPropertyValue(lowerName);
+    else
+        {
+        WriteServLog("User’s parameter '%s' does not exist.", name.c_str());
+        return "";
+        }
+}
+//-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
-void CHG_PASSIVE_NOTIFIER::Notify(const int & oldPassive, const int & newPassive)
+void UserImpl::onPassiveChange(int oldVal, int newVal)
 {
 {
-if (newPassive && !oldPassive)
-    user->property.cash.Set(user->cash - user->tariff->GetPassiveCost(),
-                            user->sysAdmin,
-                            user->login,
-                            user->store,
+    if (newVal && !oldVal && tariff != NULL)
+        properties.cash.Set(cash - tariff->GetPassiveCost(),
+                            *sysAdmin,
+                            login,
+                            *store,
                             "Freeze");
 }
 //-----------------------------------------------------------------------------
                             "Freeze");
 }
 //-----------------------------------------------------------------------------
-void CHG_TARIFF_NOTIFIER::Notify(const string &, const string & newTariff)
+void UserImpl::onDisabledChange(int oldVal, int newVal)
 {
 {
-user->tariff = user->tariffs->FindByName(newTariff);
+    if (oldVal && !newVal && GetConnected())
+        Disconnect(false, "disabled");
+    else if (!oldVal && newVal && IsInetable())
+        Connect(false);
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void CHG_CASH_NOTIFIER::Notify(const double & oldCash, const double & newCash)
+void UserImpl::onTariffChange(const std::string& /*oldVal*/, const std::string& newVal)
 {
 {
-user->lastCashAddTime = *const_cast<time_t *>(&stgTime);
-user->lastCashAdd = newCash - oldCash;
+    STG_LOCKER lock(&mutex);
+    if (settings->GetReconnectOnTariffChange() && m_connected)
+        Disconnect(false, "Change tariff");
+    tariff = tariffs->FindByName(newVal);
+    if (settings->GetReconnectOnTariffChange() &&
+        !authorizedBy.empty() &&
+        IsInetable())
+    {
+        // This notifier gets called *before* changing the tariff, and in Connect we want to see new tariff name.
+        properties.Conf().tariffName = newVal;
+        Connect(false);
+    }
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
-void CHG_IP_NOTIFIER::Notify(const uint32_t & from, const uint32_t & to)
+void UserImpl::onCashChange(double oldVal, double newVal)
 {
 {
-    printfd(__FILE__, "Change IP from %s to %s\n", inet_ntostring(from).c_str(), inet_ntostring(to).c_str());
-    if (from != 0)
-        if (user->connected)
-            user->Disconnect(false, "Change IP");
-    if (to != 0)
-        if (user->IsInetable())
-            user->Connect(false);
+    time_t now = stgTime;
+    lastCashAddTime = now;
+    lastCashAdd = newVal - oldVal;
 }
 //-----------------------------------------------------------------------------
 }
 //-----------------------------------------------------------------------------
+void UserImpl::onIPChange(const UserIPs& oldVal, const UserIPs& newVal)
+{
+    printfd(__FILE__, "Change IP from '%s' to '%s'\n", oldVal.toString().c_str(), newVal.toString().c_str());
+    if (m_connected)
+        Disconnect(false, "Change IP");
+    if (!authorizedBy.empty() && IsInetable())
+        Connect(false);
+}