]> git.stg.codes - stg.git/blobdiff - projects/stargazer/user_impl.cpp
Merge branch 'stg-2.409'
[stg.git] / projects / stargazer / user_impl.cpp
index 55b5ffe7a194eaeeda8ac8c8446039d6ec20c834..09e71a35bc2051a006f755eff573c967a3097335 100644 (file)
 #define _GNU_SOURCE
 #endif
 
-#include <pthread.h>
-#include <unistd.h> // access
-
-#include <cassert>
-#include <cstdlib>
-#include <cmath>
+#include "user_impl.h"
+#include "settings_impl.h"
+#include "stg_timer.h"
 
 #include "stg/users.h"
 #include "stg/common.h"
 #include "stg/tariff.h"
 #include "stg/tariffs.h"
 #include "stg/admin.h"
-#include "user_impl.h"
-#include "settings_impl.h"
-#include "stg_timer.h"
+
+#include <algorithm>
+#include <functional>
+
+#include <cassert>
+#include <cstdlib>
+#include <cmath>
+
+#include <pthread.h>
+#include <unistd.h> // access
+
+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);
+}
+
+}
 
 #ifdef USE_ABSTRACT_SETTINGS
 USER_IMPL::USER_IMPL(const SETTINGS * s,
            const STORE * st,
            const TARIFFS * t,
            const ADMIN * a,
-           const USERS * u)
-    : USER(),
-      users(u),
-      property(s->GetScriptsDir()),
+           const USERS * u,
+           const SERVICES & svcs)
+    : users(u),
+      property(*s),
       WriteServLog(GetStgLogger()),
       lastScanMessages(0),
-      login(),
       id(0),
       __connected(0),
       connected(__connected),
-      enabledDirs(),
-      userIDGenerator(),
       __currIP(0),
       currIP(__currIP),
       lastIPForDisconnect(0),
@@ -74,11 +94,9 @@ USER_IMPL::USER_IMPL(const SETTINGS * s,
       store(st),
       tariffs(t),
       tariff(NULL),
-      traffStat(),
-      traffStatSaved(),
+      m_services(svcs),
       settings(s),
-      authorizedBy(),
-      messages(),
+      authorizedModificationTime(0),
       deleted(false),
       lastWriteStat(0),
       lastWriteDetailedStat(0),
@@ -116,49 +134,30 @@ USER_IMPL::USER_IMPL(const SETTINGS * s,
       userdata7(property.userdata7),
       userdata8(property.userdata8),
       userdata9(property.userdata9),
-      sessionUpload(),
-      sessionDownload(),
+      sessionUploadModTime(stgTime),
+      sessionDownloadModTime(stgTime),
       passiveNotifier(this),
+      disabledNotifier(this),
       tariffNotifier(this),
       cashNotifier(this),
-      ipNotifier(this),
-      mutex(),
-      errorStr()
+      ipNotifier(this)
 {
-password = "*_EMPTY_PASSWORD_*";
-tariffName = NO_TARIFF_NAME;
-ips = StrToIPS("*");
-lastWriteStat = stgTime + random() % settings->GetStatWritePeriod();
-lastWriteDetailedStat = stgTime;
-
-property.tariffName.AddBeforeNotifier(&tariffNotifier);
-property.passive.AddBeforeNotifier(&passiveNotifier);
-property.disabled.AddAfterNotifier(&disabledNotifier);
-property.cash.AddBeforeNotifier(&cashNotifier);
-ips.AddAfterNotifier(&ipNotifier);
-
-pthread_mutexattr_t attr;
-pthread_mutexattr_init(&attr);
-pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-pthread_mutex_init(&mutex, &attr);
+Init();
 }
 #else
 USER_IMPL::USER_IMPL(const SETTINGS_IMPL * s,
                      const STORE * st,
                      const TARIFFS * t,
                      const ADMIN * a,
-                     const USERS * u)
-    : USER(),
-      users(u),
-      property(s->GetScriptsDir()),
+                     const USERS * u,
+                     const SERVICES & svcs)
+    : users(u),
+      property(*s),
       WriteServLog(GetStgLogger()),
       lastScanMessages(0),
-      login(),
       id(0),
       __connected(0),
       connected(__connected),
-      enabledDirs(),
-      userIDGenerator(),
       __currIP(0),
       currIP(__currIP),
       lastIPForDisconnect(0),
@@ -167,11 +166,9 @@ USER_IMPL::USER_IMPL(const SETTINGS_IMPL * s,
       store(st),
       tariffs(t),
       tariff(NULL),
-      traffStat(),
-      traffStatSaved(),
+      m_services(svcs),
       settings(s),
-      authorizedBy(),
-      messages(),
+      authorizedModificationTime(0),
       deleted(false),
       lastWriteStat(0),
       lastWriteDetailedStat(0),
@@ -209,18 +206,23 @@ USER_IMPL::USER_IMPL(const SETTINGS_IMPL * s,
       userdata7(property.userdata7),
       userdata8(property.userdata8),
       userdata9(property.userdata9),
-      sessionUpload(),
-      sessionDownload(),
+      sessionUploadModTime(stgTime),
+      sessionDownloadModTime(stgTime),
       passiveNotifier(this),
       disabledNotifier(this),
       tariffNotifier(this),
       cashNotifier(this),
-      ipNotifier(this),
-      mutex(),
-      errorStr()
+      ipNotifier(this)
+{
+Init();
+}
+#endif
+//-----------------------------------------------------------------------------
+void USER_IMPL::Init()
 {
 password = "*_EMPTY_PASSWORD_*";
 tariffName = NO_TARIFF_NAME;
+tariff = tariffs->FindByName(tariffName);
 ips = StrToIPS("*");
 lastWriteStat = stgTime + random() % settings->GetStatWritePeriod();
 lastWriteDetailedStat = stgTime;
@@ -236,19 +238,17 @@ pthread_mutexattr_init(&attr);
 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
 pthread_mutex_init(&mutex, &attr);
 }
-#endif
 //-----------------------------------------------------------------------------
 USER_IMPL::USER_IMPL(const USER_IMPL & u)
     : USER(),
       users(u.users),
-      property(u.settings->GetScriptsDir()),
+      property(*u.settings),
       WriteServLog(GetStgLogger()),
       lastScanMessages(0),
       login(u.login),
       id(u.id),
       __connected(0),
       connected(__connected),
-      enabledDirs(),
       userIDGenerator(u.userIDGenerator),
       __currIP(u.__currIP),
       currIP(__currIP),
@@ -258,10 +258,11 @@ USER_IMPL::USER_IMPL(const USER_IMPL & u)
       store(u.store),
       tariffs(u.tariffs),
       tariff(u.tariff),
+      m_services(u.m_services),
       traffStat(u.traffStat),
       traffStatSaved(u.traffStatSaved),
       settings(u.settings),
-      authorizedBy(),
+      authorizedModificationTime(u.authorizedModificationTime),
       messages(u.messages),
       deleted(u.deleted),
       lastWriteStat(u.lastWriteStat),
@@ -302,13 +303,13 @@ USER_IMPL::USER_IMPL(const USER_IMPL & u)
       userdata9(property.userdata9),
       sessionUpload(),
       sessionDownload(),
+      sessionUploadModTime(stgTime),
+      sessionDownloadModTime(stgTime),
       passiveNotifier(this),
       disabledNotifier(this),
       tariffNotifier(this),
       cashNotifier(this),
-      ipNotifier(this),
-      mutex(),
-      errorStr()
+      ipNotifier(this)
 {
 if (&u == this)
     return;
@@ -338,7 +339,7 @@ pthread_mutex_destroy(&mutex);
 //-----------------------------------------------------------------------------
 void USER_IMPL::SetLogin(const std::string & l)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 assert(login.empty() && "Login is already set");
 login = l;
 id = userIDGenerator.GetNextID();
@@ -346,7 +347,7 @@ id = userIDGenerator.GetNextID();
 //-----------------------------------------------------------------------------
 int USER_IMPL::ReadConf()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 USER_CONF conf;
 
 if (store->RestoreUserConf(&conf, login))
@@ -394,7 +395,7 @@ return 0;
 //-----------------------------------------------------------------------------
 int USER_IMPL::ReadStat()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 USER_STAT stat;
 
 if (store->RestoreUserStat(&stat, login))
@@ -413,7 +414,7 @@ return 0;
 //-----------------------------------------------------------------------------
 int USER_IMPL::WriteConf()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 USER_CONF conf(property.GetConf());
 
 printfd(__FILE__, "USER::WriteConf()\n");
@@ -432,7 +433,7 @@ return 0;
 //-----------------------------------------------------------------------------
 int USER_IMPL::WriteStat()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 USER_STAT stat(property.GetStat());
 
 if (store->SaveUserStat(stat, login))
@@ -451,7 +452,7 @@ return 0;
 //-----------------------------------------------------------------------------
 int USER_IMPL::WriteMonthStat()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 time_t tt = stgTime - 3600;
 struct tm t1;
 localtime_r(&tt, &t1);
@@ -471,13 +472,19 @@ return 0;
 //-----------------------------------------------------------------------------
 int USER_IMPL::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
  */
 
+/*
+ * 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
  */
@@ -487,16 +494,13 @@ if (authorizedBy.find(auth) != authorizedBy.end())
 if (!ip)
     return -1;
 
-for (int i = 0; i < DIR_NUM; i++)
-    {
-    enabledDirs[i] = dirs & (1 << i);
-    }
+dirsFromBits(enabledDirs, dirs);
 
 if (!authorizedBy.empty())
     {
     if (currIP != ip)
         {
-        //  We are already authorized, but with different IP address
+        // We are already authorized, but with different IP address
         errorStr = "User " + login + " already authorized with IP address " + inet_ntostring(ip);
         return -1;
         }
@@ -504,11 +508,11 @@ if (!authorizedBy.empty())
     USER * u = NULL;
     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)
             {
-            errorStr = "IP address " + inet_ntostring(ip) + " already in use";
+            errorStr = "IP address " + inet_ntostring(ip) + " is already in use";
             return -1;
             }
         }
@@ -517,8 +521,8 @@ else
     {
     if (users->IsIPInIndex(ip))
         {
-        //  Address is already present in IP-index
-        errorStr = "IP address " + inet_ntostring(ip) + " already in use";
+        // Address is already present in IP-index.
+        errorStr = "IP address " + inet_ntostring(ip) + " is already in use";
         return -1;
         }
 
@@ -530,11 +534,13 @@ 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;
+        errorStr = "IP address " + inet_ntostring(ip) + " does not belong to user " + login;
         return -1;
         }
     }
 
+if (authorizedBy.empty())
+    authorizedModificationTime = stgTime;
 authorizedBy.insert(auth);
 
 ScanMessage();
@@ -542,37 +548,50 @@ ScanMessage();
 return 0;
 }
 //-----------------------------------------------------------------------------
-void USER_IMPL::Unauthorize(const AUTH * auth)
+void USER_IMPL::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;
 
+authorizedModificationTime = stgTime;
+
 if (authorizedBy.empty())
     {
+    lastDisconnectReason = reason;
     lastIPForDisconnect = currIP;
     currIP = 0; // DelUser in traffcounter
+    if (connected)
+        Disconnect(false, "not authorized");
     return;
     }
 }
 //-----------------------------------------------------------------------------
 bool USER_IMPL::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();
 }
 //-----------------------------------------------------------------------------
+std::vector<std::string> USER_IMPL::GetAuthorizers() const
+{
+    STG_LOCKER lock(&mutex);
+    std::vector<std::string> list;
+    std::transform(authorizedBy.begin(), authorizedBy.end(), std::back_inserter(list), std::mem_fun(&AUTH::GetVersion));
+    return list;
+}
+//-----------------------------------------------------------------------------
 void USER_IMPL::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)
     {
@@ -580,30 +599,25 @@ if (!fakeConnect)
 
     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);
 
         std::string scriptOnConnectParams;
+        strprintf(&scriptOnConnectParams,
+                  "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
+                  scriptOnConnect.c_str(),
+                  login.c_str(),
+                  inet_ntostring(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());
+            scriptOnConnectParams += " \"" + GetParamValue(it->c_str()) + "\"";
+            ++it;
             }
 
-//        strprintf(&scriptOnConnectParams,
-//                "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
-//                scriptOnConnect.c_str(),
-//                login.c_str(),
-//                inet_ntostring(currIP).c_str(),
-//                cash.ConstData(),
-//                id,
-//                dirsStr);
-
         ScriptExec(scriptOnConnectParams.c_str());
         }
     else
@@ -614,7 +628,7 @@ if (!fakeConnect)
     connected = true;
     }
 
-if (store->WriteUserConnect(login, currIP))
+if (!settings->GetDisableSessionLog() && store->WriteUserConnect(login, currIP))
     {
     WriteServLog("Cannot write connect for user %s.", login.c_str());
     WriteServLog("%s", store->GetStrError().c_str());
@@ -630,7 +644,7 @@ void USER_IMPL::Disconnect(bool fakeDisconnect, const std::string & reason)
  *  Disconnect user from Internet. This function is differ from UnAuthorize() !!!
  */
 
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (!lastIPForDisconnect)
     {
@@ -640,16 +654,12 @@ if (!lastIPForDisconnect)
 
 if (!fakeDisconnect)
     {
+    lastDisconnectReason = reason;
     std::string scriptOnDisonnect = settings->GetScriptsDir() + "/OnDisconnect";
 
     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);
 
         std::string scriptOnDisonnectParams;
         strprintf(&scriptOnDisonnectParams,
@@ -659,7 +669,14 @@ if (!fakeDisconnect)
                 inet_ntostring(lastIPForDisconnect).c_str(),
                 cash.ConstData(),
                 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.c_str());
         }
@@ -671,7 +688,12 @@ if (!fakeDisconnect)
     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());
@@ -680,52 +702,15 @@ if (store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload,
 if (!fakeDisconnect)
     lastIPForDisconnect = 0;
 
-DIR_TRAFF zeroSesssion;
-
-sessionUpload = zeroSesssion;
-sessionDownload = zeroSesssion;
-}
-//-----------------------------------------------------------------------------
-void USER_IMPL::PrintUser() const
-{
-//return;
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-std::cout << "============================================================" << std::endl;
-std::cout << "id=" << id << std::endl;
-std::cout << "login=" << login << std::endl;
-std::cout << "password=" << password << std::endl;
-std::cout << "passive=" << passive << std::endl;
-std::cout << "disabled=" << disabled << std::endl;
-std::cout << "disabledDetailStat=" << disabledDetailStat << std::endl;
-std::cout << "alwaysOnline=" << alwaysOnline << std::endl;
-std::cout << "tariffName=" << tariffName << std::endl;
-std::cout << "address=" << address << std::endl;
-std::cout << "phone=" << phone << std::endl;
-std::cout << "email=" << email << std::endl;
-std::cout << "note=" << note << std::endl;
-std::cout << "realName=" <<realName << std::endl;
-std::cout << "group=" << group << std::endl;
-std::cout << "credit=" << credit << std::endl;
-std::cout << "nextTariff=" << nextTariff << std::endl;
-std::cout << "userdata0" << userdata0 << std::endl;
-std::cout << "userdata1" << userdata1 << std::endl;
-std::cout << "creditExpire=" << creditExpire << std::endl;
-std::cout << "ips=" << ips << std::endl;
-std::cout << "------------------------" << std::endl;
-std::cout << "up=" << up << std::endl;
-std::cout << "down=" << down << std::endl;
-std::cout << "cash=" << cash << std::endl;
-std::cout << "freeMb=" << freeMb << std::endl;
-std::cout << "lastCashAdd=" << lastCashAdd << std::endl;
-std::cout << "lastCashAddTime=" << lastCashAddTime << std::endl;
-std::cout << "passiveTime=" << passiveTime << std::endl;
-std::cout << "lastActivityTime=" << lastActivityTime << std::endl;
-std::cout << "============================================================" << std::endl;
+sessionUpload.Reset();
+sessionDownload.Reset();
+sessionUploadModTime = stgTime;
+sessionDownloadModTime = stgTime;
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::Run()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (stgTime > static_cast<time_t>(lastWriteStat + settings->GetStatWritePeriod()))
     {
@@ -751,13 +736,11 @@ if (passive.ConstData()
 if (!authorizedBy.empty())
     {
     if (connected)
-        {
         property.Stat().lastActivityTime = stgTime;
-        }
+
     if (!connected && IsInetable())
-        {
         Connect();
-        }
+
     if (connected && !IsInetable())
         {
         if (disabled)
@@ -777,17 +760,14 @@ if (!authorizedBy.empty())
 else
     {
     if (connected)
-        {
         Disconnect(false, "not authorized");
-        }
     }
 
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::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
@@ -796,8 +776,6 @@ else
 //-----------------------------------------------------------------------------
 bool USER_IMPL::IsInetable()
 {
-//STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-
 if (disabled || passive)
     return false;
 
@@ -808,21 +786,14 @@ if (settings->GetFreeMbAllowInet())
     }
 
 if (settings->GetShowFeeInCash() || tariff == NULL)
-    {
     return (cash >= -credit);
-    }
 
 return (cash - tariff->GetFee() >= -credit);
 }
 //-----------------------------------------------------------------------------
-std::string USER_IMPL::GetEnabledDirs()
+std::string USER_IMPL::GetEnabledDirs() const
 {
-//STG_LOCKER lock(&mutex, __FILE__, __LINE__);
-
-std::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
@@ -831,7 +802,7 @@ void USER_IMPL::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
 void USER_IMPL::AddTraffStatU(int dir, uint32_t ip, uint32_t len)
 #endif
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (!connected || tariff == NULL)
     return;
@@ -845,10 +816,10 @@ int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
 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
-    (tt == TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
+    (tt == TARIFF::TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
     {
     double dc = 0;
     if (traff < threshold &&
@@ -889,6 +860,7 @@ if (tt == TRAFF_UP ||
 
 up = dt;
 sessionUpload[dir] += len;
+sessionUploadModTime = stgTime;
 
 //Add detailed stat
 
@@ -923,7 +895,7 @@ void USER_IMPL::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
 void USER_IMPL::AddTraffStatD(int dir, uint32_t ip, uint32_t len)
 #endif
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (!connected || tariff == NULL)
     return;
@@ -937,10 +909,10 @@ int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
 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
-    (tt == TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
+    (tt == TARIFF::TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
     {
     double dc = 0;
     if (traff < threshold &&
@@ -980,6 +952,7 @@ if (tt == TRAFF_DOWN ||
 
 down = dt;
 sessionDownload[dir] += len;
+sessionDownloadModTime = stgTime;
 
 //Add detailed stat
 
@@ -1010,65 +983,61 @@ else
 //-----------------------------------------------------------------------------
 void USER_IMPL::AddCurrIPBeforeNotifier(CURR_IP_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 currIP.AddBeforeNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::DelCurrIPBeforeNotifier(const CURR_IP_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 currIP.DelBeforeNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::AddCurrIPAfterNotifier(CURR_IP_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 currIP.AddAfterNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::DelCurrIPAfterNotifier(const CURR_IP_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 currIP.DelAfterNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::AddConnectedBeforeNotifier(CONNECTED_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 connected.AddBeforeNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::DelConnectedBeforeNotifier(const CONNECTED_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 connected.DelBeforeNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::AddConnectedAfterNotifier(CONNECTED_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 connected.AddAfterNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::DelConnectedAfterNotifier(const CONNECTED_NOTIFIER * notifier)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 connected.DelAfterNotifier(notifier);
 }
 //-----------------------------------------------------------------------------
 void USER_IMPL::OnAdd()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 std::string scriptOnAdd = settings->GetScriptsDir() + "/OnUserAdd";
 
 if (access(scriptOnAdd.c_str(), X_OK) == 0)
     {
-    std::string scriptOnAddParams;
-    strprintf(&scriptOnAddParams,
-            "%s \"%s\"",
-            scriptOnAdd.c_str(),
-            login.c_str());
+    std::string scriptOnAddParams = scriptOnAdd + " \"" + login + "\"";
 
     ScriptExec(scriptOnAddParams.c_str());
     }
@@ -1080,17 +1049,13 @@ else
 //-----------------------------------------------------------------------------
 void USER_IMPL::OnDelete()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 std::string scriptOnDel = settings->GetScriptsDir() + "/OnUserDel";
 
 if (access(scriptOnDel.c_str(), X_OK) == 0)
     {
-    std::string scriptOnDelParams;
-    strprintf(&scriptOnDelParams,
-            "%s \"%s\"",
-            scriptOnDel.c_str(),
-            login.c_str());
+    std::string scriptOnDelParams = scriptOnDel + " \"" + login + "\"";
 
     ScriptExec(scriptOnDelParams.c_str());
     }
@@ -1121,7 +1086,7 @@ if (!traffStatSaved.second.empty())
 TRAFF_STAT ts;
 
     {
-    STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+    STG_LOCKER lock(&mutex);
     ts.swap(traffStat);
     }
 
@@ -1137,7 +1102,7 @@ if (ts.size() && !disabledDetailStat)
         if (!hard)
             {
             printfd(__FILE__, "USER::WriteDetailStat() - pushing detail stat to queue\n");
-            STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+            STG_LOCKER lock(&mutex);
             traffStatSaved.second.swap(ts);
             traffStatSaved.first = lastWriteDetailedStat;
             }
@@ -1150,7 +1115,7 @@ return 0;
 //-----------------------------------------------------------------------------
 double USER_IMPL::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};
@@ -1177,7 +1142,7 @@ return static_cast<double>(dt) / secMonth;
 //-----------------------------------------------------------------------------
 void USER_IMPL::SetPassiveTimeAsNewUser()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 time_t t = stgTime;
 struct tm tm;
@@ -1191,7 +1156,7 @@ passiveTime = static_cast<time_t>(pt * 24 * 3600 * daysCurrMon);
 //-----------------------------------------------------------------------------
 void USER_IMPL::MidnightResetSessionStat()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (connected)
     {
@@ -1202,29 +1167,23 @@ if (connected)
 //-----------------------------------------------------------------------------
 void USER_IMPL::ProcessNewMonth()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 //  Reset traff
 if (connected)
-    {
     Disconnect(true, "fake");
-    }
-DIR_TRAFF zeroTarff;
 
 WriteMonthStat();
 
-up = zeroTarff;
-down = zeroTarff;
+property.Stat().monthUp.Reset();
+property.Stat().monthDown.Reset();
 
 if (connected)
-    {
     Connect(true);
-    }
 
 //  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.",
@@ -1232,8 +1191,17 @@ if (nextTariff.ConstData() != "")
         }
     else
         {
-        property.tariffName.Set(nextTariff, sysAdmin, login, store);
-        //tariff = nt;
+        std::string message = tariff->TariffChangeIsAllowed(*nt, stgTime);
+        if (message.empty())
+            {
+            property.tariffName.Set(nextTariff, sysAdmin, login, store);
+            }
+        else
+            {
+            WriteServLog("Tariff change is prohibited for user %s. %s",
+                         login.c_str(),
+                         message.c_str());
+            }
         }
     ResetNextTariff();
     WriteConf();
@@ -1242,11 +1210,14 @@ if (nextTariff.ConstData() != "")
 //-----------------------------------------------------------------------------
 void USER_IMPL::ProcessDayFeeSpread()
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (passive.ConstData() || tariff == NULL)
     return;
 
+if (tariff->GetPeriod() != TARIFF::MONTH)
+    return;
+
 double fee = tariff->GetFee() / DaysInCurrentMonth();
 
 if (std::fabs(fee) < 1.0e-3)
@@ -1276,11 +1247,14 @@ ResetPassiveTime();
 //-----------------------------------------------------------------------------
 void USER_IMPL::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())
     {
@@ -1342,6 +1316,108 @@ switch (settings->GetFeeChargeType())
     }
 }
 //-----------------------------------------------------------------------------
+void USER_IMPL::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:
+        property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+        break;
+    case 1:
+        if (c + credit >= 0)
+            property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+        break;
+    case 2:
+        if (c + credit >= fee)
+            property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+        break;
+    }
+ResetPassiveTime();
+}
+//-----------------------------------------------------------------------------
+void USER_IMPL::ProcessServices()
+{
+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;
+        }
+    }
+
+for (size_t i = 0; i < property.Conf().services.size(); ++i)
+    {
+    SERVICE_CONF conf;
+    if (m_services.Find(property.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:
+                property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+                SetPrepaidTraff();
+                break;
+            case 1:
+                if (c + credit >= 0)
+                    {
+                    property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+                    SetPrepaidTraff();
+                    }
+                break;
+            case 2:
+                if (c + credit >= fee)
+                    {
+                    property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+                    SetPrepaidTraff();
+                    }
+                break;
+            case 3:
+                if (c >= 0)
+                    {
+                    property.cash.Set(c - fee, sysAdmin, login, store, "Subscriber fee charge");
+                    SetPrepaidTraff();
+                    }
+                break;
+            }
+        }
+    }
+}
+//-----------------------------------------------------------------------------
 void USER_IMPL::SetPrepaidTraff()
 {
 if (tariff != NULL)
@@ -1350,7 +1426,7 @@ if (tariff != NULL)
 //-----------------------------------------------------------------------------
 int USER_IMPL::AddMessage(STG_MSG * msg)
 {
-STG_LOCKER lock(&mutex, __FILE__, __LINE__);
+STG_LOCKER lock(&mutex);
 
 if (SendMessage(*msg))
     {
@@ -1465,116 +1541,26 @@ while (it != messages.end())
 }
 //-----------------------------------------------------------------------------
 std::string USER_IMPL::GetParamValue(const std::string & name) const
-    {
-    if (name == "cash")
-        {
-        return static_cast<std::string>(property.cash);
-        }
-    if (name == "freeMb")
-        {
-        return static_cast<std::string>(property.freeMb);
-        }
-    if (name == "passive")
-        {
-        return static_cast<std::string>(property.passive);
-        }
-    if (name == "disabled")
-        {
-        return static_cast<std::string>(property.disabled);
-        }
-    if (name == "alwaysOnline")
-        {
-        return static_cast<std::string>(property.alwaysOnline);
-        }
-    if (name == "tariffName")
-        {
-        return property.tariffName;
-        }
-    if (name == "nextTariff")
-        {
-        return property.nextTariff;
-        }
-    if (name == "address")
-        {
-        return property.address;
-        }
-    if (name == "note")
-        {
-        return property.note;
-        }
-    if (name == "group")
-        {
-        return property.group;
-        }
-    if (name == "email")
-        {
-        return property.email;
-        }
-    if (name == "phone")
-        {
-        return property.phone;
-        }
-    if (name == "realName")
-        {
-        return property.realName;
-        }
-    if (name == "credit")
-        {
-        return static_cast<std::string>(property.credit);
-        }
-    if (name == "userdata0")
-        {
-        return property.userdata0;
-        }
-    if (name == "userdata1")
-        {
-        return property.userdata1;
-        }
-    if (name == "userdata2")
-        {
-        return property.userdata2;
-        }
-    if (name == "userdata3")
-        {
-        return property.userdata3;
-        }
-    if (name == "userdata4")
-        {
-        return property.userdata4;
-        }
-    if (name == "userdata5")
-        {
-        return property.userdata5;
-        }
-    if (name == "userdata6")
-        {
-        return property.userdata6;
-        }
-    if (name == "userdata7")
-        {
-        return property.userdata7;
-        }
-    if (name == "userdata8")
-        {
-        return property.userdata8;
-        }
-    if (name == "userdata9")
-        {
-        return property.userdata9;
-        }
-    if (name == "id")
-        {
-        return static_cast<std::string>(id);
-        }
-    if (name == "login")
+{
+    std::string lowerName = ToLower(name);
+    if (lowerName == "id")
         {
-        return login;
+        std::ostringstream stream;
+        stream << id;
+        return stream.str();
         }
-    if (name == "ip")
+    if (lowerName == "login")       return login;
+    if (lowerName == "currip")      return currIP.ToString();
+    if (lowerName == "enableddirs") return GetEnabledDirs();
+    if (lowerName == "tariff")      return property.tariffName;
+    if (property.Exists(lowerName))
+        return property.GetPropertyValue(lowerName);
+    else
         {
-        return static_cast<std::string>(currIP);
+        WriteServLog("User’s parameter '%s' does not exist.", name.c_str());
+        return "";
         }
-    }
+}
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
@@ -1591,25 +1577,25 @@ if (newPassive && !oldPassive && user->tariff != NULL)
 void CHG_DISABLED_NOTIFIER::Notify(const int & oldValue, const int & newValue)
 {
 if (oldValue && !newValue && user->GetConnected())
-    {
     user->Disconnect(false, "disabled");
-    }
 else if (!oldValue && newValue && user->IsInetable())
-    {
     user->Connect(false);
-    }
-
 }
 //-----------------------------------------------------------------------------
 void CHG_TARIFF_NOTIFIER::Notify(const std::string &, const std::string & newTariff)
 {
+STG_LOCKER lock(&user->mutex);
 if (user->settings->GetReconnectOnTariffChange() && user->connected)
     user->Disconnect(false, "Change tariff");
 user->tariff = user->tariffs->FindByName(newTariff);
 if (user->settings->GetReconnectOnTariffChange() &&
     !user->authorizedBy.empty() &&
     user->IsInetable())
+    {
+    // This notifier gets called *before* changing the tariff, and in Connect we want to see new tariff name.
+    user->property.Conf().tariffName = newTariff;
     user->Connect(false);
+    }
 }
 //-----------------------------------------------------------------------------
 void CHG_CASH_NOTIFIER::Notify(const double & oldCash, const double & newCash)
@@ -1620,10 +1606,9 @@ user->lastCashAdd = newCash - oldCash;
 //-----------------------------------------------------------------------------
 void CHG_IPS_NOTIFIER::Notify(const USER_IPS & from, const USER_IPS & to)
 {
-    printfd(__FILE__, "Change IP from '%s' to '%s'\n", from.GetIpStr().c_str(), to.GetIpStr().c_str());
-    if (user->connected)
-        user->Disconnect(false, "Change IP");
-    if (!user->authorizedBy.empty() && user->IsInetable())
-        user->Connect(false);
+printfd(__FILE__, "Change IP from '%s' to '%s'\n", from.GetIpStr().c_str(), to.GetIpStr().c_str());
+if (user->connected)
+    user->Disconnect(false, "Change IP");
+if (!user->authorizedBy.empty() && user->IsInetable())
+    user->Connect(false);
 }
-//-----------------------------------------------------------------------------