]> git.stg.codes - stg.git/blobdiff - projects/stargazer/plugins/store/mysql/mysql_store.cpp
MySQL custom port implemented
[stg.git] / projects / stargazer / plugins / store / mysql / mysql_store.cpp
index b4ac87155a762d5510d4700f624decf959956fd2..48be4aa6b37ad952950c8b6f14d2d022e1d9918e 100644 (file)
@@ -7,11 +7,13 @@
 #include <mysql.h>
 #include <errmsg.h>
 
+#include "stg/common.h"
 #include "stg/user_ips.h"
 #include "stg/user_conf.h"
 #include "stg/user_stat.h"
 #include "stg/blowfish.h"
 #include "stg/plugin_creator.h"
+#include "stg/logger.h"
 #include "mysql_store.h"
 
 #define adm_enc_passwd "cjeifY8m3"
@@ -29,10 +31,10 @@ template <typename T>
 int GetInt(const std::string & str, T * val, T defaultVal = T())
 {
     char *res;
-    
+
     *val = static_cast<T>(strtoll(str.c_str(), &res, 10));
-    
-    if (*res != 0) 
+
+    if (*res != 0)
     {
         *val = defaultVal; //Error!
         return EINVAL;
@@ -44,10 +46,10 @@ int GetInt(const std::string & str, T * val, T defaultVal = T())
 int GetDouble(const std::string & str, double * val, double defaultVal)
 {
     char *res;
-    
+
     *val = strtod(str.c_str(), &res);
-    
-    if (*res != 0) 
+
+    if (*res != 0)
     {
         *val = defaultVal; //Error!
         return EINVAL;
@@ -59,10 +61,10 @@ int GetDouble(const std::string & str, double * val, double defaultVal)
 int GetTime(const std::string & str, time_t * val, time_t defaultVal)
 {
     char *res;
-    
+
     *val = strtol(str.c_str(), &res, 10);
-    
-    if (*res != 0) 
+
+    if (*res != 0)
     {
         *val = defaultVal; //Error!
         return EINVAL;
@@ -72,7 +74,7 @@ int GetTime(const std::string & str, time_t * val, time_t defaultVal)
 }
 
 //-----------------------------------------------------------------------------
-std::string ReplaceStr(std::string source, const std::string symlist, const char chgsym)
+std::string ReplaceStr(std::string source, const std::string symlist, const char chgsym)
 {
     std::string::size_type pos=0;
 
@@ -85,17 +87,17 @@ std::string ReplaceStr(std::string source, const std::string symlist, const char
 int GetULongLongInt(const std::string & str, uint64_t * val, uint64_t defaultVal)
 {
     char *res;
-    
+
     *val = strtoull(str.c_str(), &res, 10);
-    
-    if (*res != 0) 
+
+    if (*res != 0)
     {
         *val = defaultVal; //Error!
         return EINVAL;
     }
 
     return 0;
-} 
+}
 
 PLUGIN_CREATOR<MYSQL_STORE> msc;
 }
@@ -110,28 +112,24 @@ return msc.GetPlugin();
 }
 //-----------------------------------------------------------------------------
 MYSQL_STORE_SETTINGS::MYSQL_STORE_SETTINGS()
-    : settings(NULL),
-      errorStr(),
-      dbUser(),
-      dbPass(),
-      dbName(),
-      dbHost()
+    : settings(NULL)
+    , dbPort(0)
 {
 }
 //-----------------------------------------------------------------------------
-int MYSQL_STORE_SETTINGS::ParseParam(const std::vector<PARAM_VALUE> & moduleParams, 
-                        const std::string & name, std::string & result)
+int MYSQL_STORE_SETTINGS::ParseParam(const std::vector<PARAM_VALUE> & moduleParams,
+                                     const std::string & name, std::string & result)
 {
 PARAM_VALUE pv;
 pv.param = name;
 std::vector<PARAM_VALUE>::const_iterator pvi;
 pvi = find(moduleParams.begin(), moduleParams.end(), pv);
-if (pvi == moduleParams.end())
+if (pvi == moduleParams.end() || pvi->value.empty())
     {
     errorStr = "Parameter \'" + name + "\' not found.";
     return -1;
     }
-    
+
 result = pvi->value[0];
 
 return 0;
@@ -152,16 +150,25 @@ if (ParseParam(s.moduleParams, "server", dbHost) < 0 &&
     ParseParam(s.moduleParams, "dbhost", dbHost) < 0)
     return -1;
 
+std::string dbPortAsString;
+if (ParseParam(s.moduleParams, "port", dbPortAsString) == 0 ||
+    ParseParam(s.moduleParams, "dbport", dbPortAsString) == 0)
+{
+    if (GetInt<unsigned int>(dbPortAsString, &dbPort, 0) != 0)
+    {
+        errorStr = "Can't parse db port from string: \"" + dbPortAsString + "\"\n";
+        return -1;
+    }
+}
+
 return 0;
 }
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 MYSQL_STORE::MYSQL_STORE()
-    : errorStr(),
-      version("mysql_store v.0.67"),
-      storeSettings(),
-      settings(),
+    : version("mysql_store v.0.67"),
+      schemaVersion(0),
       logger(GetPluginLogger(GetStgLogger(), "store_mysql"))
 {
 }
@@ -180,7 +187,7 @@ int    MYSQL_STORE::MysqlQuery(const char* sQuery,MYSQL * sock) const
                 return 0;
         }
     }
-    
+
     return ret;
 }
 //-----------------------------------------------------------------------------
@@ -190,7 +197,6 @@ int MYSQL_STORE::ParseSettings()
 {
 int ret = storeSettings.ParseSettings(settings);
 MYSQL mysql;
-MYSQL * sock;
 mysql_init(&mysql);
 if (ret)
     errorStr = storeSettings.GetStrError();
@@ -201,10 +207,10 @@ else
         errorStr = "Database password must be not empty. Please read Manual.";
         return -1;
     }
-    
+    MYSQL * sock;
     if (!(sock = mysql_real_connect(&mysql,storeSettings.GetDBHost().c_str(),
             storeSettings.GetDBUser().c_str(),storeSettings.GetDBPassword().c_str(),
-            0,0,NULL,0)))
+            0,storeSettings.GetDBPort(),NULL,0)))
         {
             errorStr = "Couldn't connect to mysql engine! With error:\n";
             errorStr += mysql_error(&mysql);
@@ -216,7 +222,7 @@ else
          if(mysql_select_db(sock, storeSettings.GetDBName().c_str()))
          {
              std::string res = "CREATE DATABASE " + storeSettings.GetDBName();
-            
+
             if(MysqlQuery(res.c_str(),sock))
             {
                 errorStr = "Couldn't create database! With error:\n";
@@ -228,19 +234,26 @@ else
             {
                  if(mysql_select_db(sock, storeSettings.GetDBName().c_str()))
                  {
-                    errorStr = "Couldn't select database! With error:\n";
-                    errorStr += mysql_error(sock);
-                    mysql_close(sock);
-                    ret = -1;
+                     errorStr = "Couldn't select database! With error:\n";
+                     errorStr += mysql_error(sock);
+                     mysql_close(sock);
+                     ret = -1;
                  }
-                 ret = CheckAllTables(sock);
+                 else
+                     ret = CheckAllTables(sock);
             }
-         }
-         else
-             ret = CheckAllTables(sock);
-         mysql_close(sock);
+        }
+        else
+        {
+            ret = CheckAllTables(sock);
+        }
+        if (!ret)
+        {
+            logger("MYSQL_STORE: Current DB schema version: %d", schemaVersion);
+            MakeUpdates(sock);
+        }
+        mysql_close(sock);
     }
-    
 }
 return ret;
 }
@@ -267,6 +280,43 @@ return num_rows == 1;
 //-----------------------------------------------------------------------------
 int MYSQL_STORE::CheckAllTables(MYSQL * sock)
 {
+//info-------------------------------------------------------------------------
+if(!IsTablePresent("info",sock))
+{
+    sprintf(qbuf,"CREATE TABLE info (version INTEGER NOT NULL)");
+
+    if(MysqlQuery(qbuf,sock))
+        {
+        errorStr = "Couldn't create info table With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+        }
+
+    sprintf(qbuf,"INSERT INTO info SET version=0");
+
+    if(MysqlQuery(qbuf,sock))
+        {
+        errorStr = "Couldn't write default version. With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+        }
+    schemaVersion = 0;
+}
+else
+{
+    std::vector<std::string> info;
+    if (GetAllParams(&info, "info", "version"))
+        schemaVersion = 0;
+    else
+    {
+        if (info.empty())
+            schemaVersion = 0;
+        else
+            GetInt(info.front(), &schemaVersion, 0);
+    }
+}
 //admins-----------------------------------------------------------------------
 if(!IsTablePresent("admins",sock))
 {
@@ -275,7 +325,7 @@ if(!IsTablePresent("admins",sock))
         "ChgPassword TINYINT DEFAULT 0,ChgStat TINYINT DEFAULT 0,"\
         "ChgCash TINYINT DEFAULT 0,UsrAddDel TINYINT DEFAULT 0,"\
         "ChgTariff TINYINT DEFAULT 0,ChgAdmin TINYINT DEFAULT 0)");
-    
+
     if(MysqlQuery(qbuf,sock))
     {
         errorStr = "Couldn't create admin table list With error:\n";
@@ -287,7 +337,7 @@ if(!IsTablePresent("admins",sock))
     sprintf(qbuf,"INSERT INTO admins SET login='admin',"\
         "password='geahonjehjfofnhammefahbbbfbmpkmkmmefahbbbfbmpkmkmmefahbbbfbmpkmkaa',"\
         "ChgConf=1,ChgPassword=1,ChgStat=1,ChgCash=1,UsrAddDel=1,ChgTariff=1,ChgAdmin=1");
-    
+
     if(MysqlQuery(qbuf,sock))
     {
         errorStr = "Couldn't create default admin. With error:\n";
@@ -302,37 +352,40 @@ std::string param, res;
 if(!IsTablePresent("tariffs",sock))
 {
     res = "CREATE TABLE tariffs (name VARCHAR(40) DEFAULT '' PRIMARY KEY,";
-        
+
     for (int i = 0; i < DIR_NUM; i++)
         {
-        strprintf(&param, " PriceDayA%d DOUBLE DEFAULT 0.0,", i); 
+        strprintf(&param, " PriceDayA%d DOUBLE DEFAULT 0.0,", i);
         res += param;
-    
+
         strprintf(&param, " PriceDayB%d DOUBLE DEFAULT 0.0,", i);
         res += param;
-            
+
         strprintf(&param, " PriceNightA%d DOUBLE DEFAULT 0.0,", i);
         res += param;
-    
+
         strprintf(&param, " PriceNightB%d DOUBLE DEFAULT 0.0,", i);
         res += param;
-            
+
         strprintf(&param, " Threshold%d INT DEFAULT 0,", i);
         res += param;
-    
+
         strprintf(&param, " Time%d VARCHAR(15) DEFAULT '0:0-0:0',", i);
         res += param;
-    
+
         strprintf(&param, " NoDiscount%d INT DEFAULT 0,", i);
         res += param;
-    
+
         strprintf(&param, " SinglePrice%d INT DEFAULT 0,", i);
         res += param;
         }
-    
-    res += "PassiveCost DOUBLE DEFAULT 0.0, Fee DOUBLE DEFAULT 0.0,"\
-        "Free DOUBLE DEFAULT 0.0, TraffType VARCHAR(10) DEFAULT '')";
-    
+
+    res += "PassiveCost DOUBLE DEFAULT 0.0, Fee DOUBLE DEFAULT 0.0,"
+        "Free DOUBLE DEFAULT 0.0, TraffType VARCHAR(10) DEFAULT '',"
+        "period VARCHAR(32) NOT NULL DEFAULT 'month',"
+        "change_policy VARCHAR(32) NOT NULL DEFAULT 'allow',"
+        "change_policy_timeout TIMESTAMP NOT NULL DEFAULT 0)";
+
     if(MysqlQuery(res.c_str(),sock))
     {
         errorStr = "Couldn't create tariffs table list With error:\n";
@@ -342,51 +395,52 @@ if(!IsTablePresent("tariffs",sock))
     }
 
     res = "INSERT INTO tariffs SET name='tariff',";
-        
+
     for (int i = 0; i < DIR_NUM; i++)
         {
         strprintf(&param, " NoDiscount%d=1,", i);
         res += param;
-    
+
         strprintf(&param, " Threshold%d=0,", i);
         res += param;
-    
+
         strprintf(&param, " Time%d='0:0-0:0',", i);
         res += param;
-    
+
         if(i != 0 && i != 1)
         {
             strprintf(&param, " SinglePrice%d=0,", i);
-            res += param;        
+            res += param;
         }
-    
+
         if(i != 1)
         {
-            strprintf(&param, " PriceDayA%d=0.0,", i); 
-            res += param;        
+            strprintf(&param, " PriceDayA%d=0.0,", i);
+            res += param;
         }
         if(i != 1)
         {
-            strprintf(&param, " PriceDayB%d=0.0,", i);        
-            res += param;    
+            strprintf(&param, " PriceDayB%d=0.0,", i);
+            res += param;
         }
-    
+
         if(i != 0)
         {
-            strprintf(&param, " PriceNightA%d=0.0,", i); 
-            res += param;        
+            strprintf(&param, " PriceNightA%d=0.0,", i);
+            res += param;
         }
         if(i != 0)
         {
-            strprintf(&param, " PriceNightB%d=0.0,", i);        
-            res += param;    
+            strprintf(&param, " PriceNightB%d=0.0,", i);
+            res += param;
         }
         }
-    
+
     res += "PassiveCost=0.0, Fee=10.0, Free=0,"\
         "SinglePrice0=1, SinglePrice1=1,PriceDayA1=0.75,PriceDayB1=0.75,"\
-        "PriceNightA0=1.0,PriceNightB0=1.0,TraffType='up+down'";
-    
+        "PriceNightA0=1.0,PriceNightB0=1.0,TraffType='up+down',period='month',"\
+        "change_policy='allow', change_policy_timeout=0";
+
     if(MysqlQuery(res.c_str(),sock))
     {
         errorStr = "Couldn't create default tariff. With error:\n";
@@ -394,6 +448,17 @@ if(!IsTablePresent("tariffs",sock))
         mysql_close(sock);
         return -1;
     }
+
+    sprintf(qbuf,"UPDATE info SET version=1");
+
+    if(MysqlQuery(qbuf,sock))
+    {
+        errorStr = "Couldn't write default version. With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+    }
+    schemaVersion = 2;
 }
 
 //users-----------------------------------------------------------------------
@@ -404,34 +469,34 @@ if(!IsTablePresent("users",sock))
         "Address VARCHAR(254) NOT NULL DEFAULT '',Phone VARCHAR(128) NOT NULL DEFAULT '',Email VARCHAR(50) NOT NULL DEFAULT '',"\
         "Note TEXT NOT NULL,RealName VARCHAR(254) NOT NULL DEFAULT '',StgGroup VARCHAR(40) NOT NULL DEFAULT '',"\
         "Credit DOUBLE DEFAULT 0, TariffChange VARCHAR(40) NOT NULL DEFAULT '',";
-    
+
     for (int i = 0; i < USERDATA_NUM; i++)
         {
         strprintf(&param, " Userdata%d VARCHAR(254) NOT NULL,", i);
         res += param;
         }
-    
+
     param = " CreditExpire INT(11) DEFAULT 0,";
     res += param;
-    
+
     strprintf(&param, " IP VARCHAR(254) DEFAULT '*',");
     res += param;
-    
+
     for (int i = 0; i < DIR_NUM; i++)
         {
         strprintf(&param, " D%d BIGINT(30) DEFAULT 0,", i);
         res += param;
-    
+
         strprintf(&param, " U%d BIGINT(30) DEFAULT 0,", i);
         res += param;
         }
-    
+
     strprintf(&param, "Cash DOUBLE DEFAULT 0,FreeMb DOUBLE DEFAULT 0,LastCashAdd DOUBLE DEFAULT 0,"\
         "LastCashAddTime INT(11) DEFAULT 0,PassiveTime INT(11) DEFAULT 0,LastActivityTime INT(11) DEFAULT 0,"\
         "NAS VARCHAR(17) NOT NULL, INDEX (AlwaysOnline), INDEX (IP), INDEX (Address),"\
         " INDEX (Tariff),INDEX (Phone),INDEX (Email),INDEX (RealName))");
     res += param;
-        
+
     if(MysqlQuery(res.c_str(),sock))
     {
         errorStr = "Couldn't create users table list With error:\n";
@@ -444,21 +509,26 @@ if(!IsTablePresent("users",sock))
     res = "INSERT INTO users SET login='test',Address='',AlwaysOnline=0,"\
         "Credit=0.0,CreditExpire=0,Down=0,Email='',DisabledDetailStat=0,"\
         "StgGroup='',IP='192.168.1.1',Note='',Passive=0,Password='123456',"\
-        "Phone='', RealName='',Tariff='tariff',TariffChange='',Userdata0='',"\
-        "Userdata1='',";
-    
+        "Phone='', RealName='',Tariff='tariff',TariffChange='',NAS='',";
+
+    for (int i = 0; i < USERDATA_NUM; i++)
+        {
+        strprintf(&param, " Userdata%d='',", i);
+        res += param;
+        }
+
     for (int i = 0; i < DIR_NUM; i++)
         {
         strprintf(&param, " D%d=0,", i);
         res += param;
-    
+
         strprintf(&param, " U%d=0,", i);
         res += param;
         }
-    
+
     res += "Cash=10.0,FreeMb=0.0,LastActivityTime=0,LastCashAdd=0,"\
         "LastCashAddTime=0, PassiveTime=0";
-        
+
     if(MysqlQuery(res.c_str(),sock))
     {
         errorStr = "Couldn't create default user. With error:\n";
@@ -472,7 +542,7 @@ if(!IsTablePresent("users",sock))
 if(!IsTablePresent("logs"))
 {
     sprintf(qbuf,"CREATE TABLE logs (unid INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, login VARCHAR(40),text TEXT)");
-    
+
     if(MysqlQuery(qbuf))
     {
         errorStr = "Couldn't create admin table list With error:\n";
@@ -487,7 +557,7 @@ if(!IsTablePresent("messages",sock))
     sprintf(qbuf,"CREATE TABLE messages (login VARCHAR(40) DEFAULT '', id BIGINT, "\
             "type INT, lastSendTime INT, creationTime INT, showTime INT,"\
             "stgRepeat INT, repeatPeriod INT, text TEXT)");
-    
+
     if(MysqlQuery(qbuf,sock))
     {
         errorStr = "Couldn't create messages table. With error:\n";
@@ -501,18 +571,18 @@ if(!IsTablePresent("messages",sock))
 if(!IsTablePresent("stat",sock))
 {
     res = "CREATE TABLE stat (login VARCHAR(50), month TINYINT, year SMALLINT,";
-    
+
     for (int i = 0; i < DIR_NUM; i++)
         {
-        strprintf(&param, " U%d BIGINT,", i); 
+        strprintf(&param, " U%d BIGINT,", i);
         res += param;
-            
-        strprintf(&param, " D%d BIGINT,", i); 
+
+        strprintf(&param, " D%d BIGINT,", i);
         res += param;
         }
-        
+
     res += " cash DOUBLE, INDEX (login))";
-    
+
     if(MysqlQuery(res.c_str(),sock))
     {
         errorStr = "Couldn't create stat table. With error:\n";
@@ -525,20 +595,64 @@ if(!IsTablePresent("stat",sock))
 return 0;
 }
 //-----------------------------------------------------------------------------
+int MYSQL_STORE::MakeUpdates(MYSQL * sock)
+{
+if (schemaVersion  < 1)
+    {
+    if (MysqlQuery("ALTER TABLE tariffs ADD period VARCHAR(32) NOT NULL DEFAULT 'month'", sock))
+        {
+        errorStr = "Couldn't update tariffs table to version 1. With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+        }
+    if (MysqlQuery("UPDATE info SET version = 1", sock))
+        {
+        errorStr = "Couldn't update DB schema version to 1. With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+        }
+    schemaVersion = 1;
+    logger("MYSQL_STORE: Updated DB schema to version %d", schemaVersion);
+    }
+
+if (schemaVersion  < 2)
+    {
+    if (MysqlQuery("ALTER TABLE tariffs ADD change_policy VARCHAR(32) NOT NULL DEFAULT 'allow'", sock) ||
+        MysqlQuery("ALTER TABLE tariffs ADD change_policy_timeout TIMESTAMP NOT NULL DEFAULT 0", sock))
+        {
+        errorStr = "Couldn't update tariffs table to version 2. With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+        }
+    if (MysqlQuery("UPDATE info SET version = 2", sock))
+        {
+        errorStr = "Couldn't update DB schema version to 2. With error:\n";
+        errorStr += mysql_error(sock);
+        mysql_close(sock);
+        return -1;
+        }
+    schemaVersion = 2;
+    logger("MYSQL_STORE: Updated DB schema to version %d", schemaVersion);
+    }
+return 0;
+}
 //-----------------------------------------------------------------------------
 
-int MYSQL_STORE::GetAllParams(std::vector<std::string> * ParamList, 
+int MYSQL_STORE::GetAllParams(std::vector<std::string> * ParamList,
                             const std::string & table, const std::string & name) const
 {
 MYSQL_RES *res;
 MYSQL_ROW row;
 MYSQL * sock=NULL;
 my_ulonglong num, i;
-    
+
 ParamList->clear();
-    
+
 sprintf(qbuf,"SELECT %s FROM %s", name.c_str(), table.c_str());
-    
+
 if(MysqlGetQuery(qbuf,sock))
 {
     errorStr = "Couldn't GetAllParams Query for: ";
@@ -560,7 +674,7 @@ num = mysql_num_rows(res);
 
 for(i = 0; i < num; i++)
 {
-    row = mysql_fetch_row(res);    
+    row = mysql_fetch_row(res);
     ParamList->push_back(row[0]);
 }
 
@@ -597,9 +711,12 @@ return 0;
 //-----------------------------------------------------------------------------
 int MYSQL_STORE::AddUser(const std::string & login) const
 {
-sprintf(qbuf,"INSERT INTO users SET login='%s'", login.c_str());
-    
-if(MysqlSetQuery(qbuf))
+std::string query = "INSERT INTO users SET login='" + login + "',Note='',NAS=''";
+
+for (int i = 0; i < USERDATA_NUM; i++)
+    query += ",Userdata" + x2str(i) + "=''";
+
+if(MysqlSetQuery(query.c_str()))
 {
     errorStr = "Couldn't add user:\n";
     //errorStr += mysql_error(sock);
@@ -612,7 +729,7 @@ return 0;
 int MYSQL_STORE::DelUser(const std::string & login) const
 {
 sprintf(qbuf,"DELETE FROM users WHERE login='%s' LIMIT 1", login.c_str());
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't delete user:\n";
@@ -644,7 +761,7 @@ query += "CreditExpire, IP FROM users WHERE login='";
 query += login + "' LIMIT 1";
 
 //sprintf(qbuf,"SELECT * FROM users WHERE login='%s' LIMIT 1", login.c_str());
-    
+
 if(MysqlGetQuery(query.c_str(),sock))
 {
     errorStr = "Couldn't restore Tariff(on query):\n";
@@ -670,8 +787,6 @@ if (mysql_num_rows(res) != 1)
 
 row = mysql_fetch_row(res);
 
-std::string param;
-
 conf->password = row[1];
 
 if (conf->password.empty())
@@ -716,7 +831,7 @@ if (GetInt(row[5], &conf->alwaysOnline) != 0)
 
 conf->tariffName = row[6];
 
-if (conf->tariffName.empty()) 
+if (conf->tariffName.empty())
     {
     mysql_free_result(res);
     errorStr = "User \'" + login + "\' tariff is blank.";
@@ -747,7 +862,7 @@ for (int i = 0; i < USERDATA_NUM; i++)
     }
 
 GetTime(row[15+USERDATA_NUM], &conf->creditExpire, 0);
-    
+
 std::string ipStr = row[16+USERDATA_NUM];
 USER_IPS i;
 try
@@ -790,7 +905,7 @@ query += "Cash, FreeMb, LastCashAdd, LastCashAddTime, PassiveTime, LastActivityT
 query += login + "'";
 
 //sprintf(qbuf,"SELECT * FROM users WHERE login='%s' LIMIT 1", login.c_str());
-    
+
 if(MysqlGetQuery(query.c_str() ,sock))
 {
     errorStr = "Couldn't restore UserStat(on query):\n";
@@ -824,7 +939,7 @@ for (int i = 0; i < DIR_NUM; i++)
         mysql_close(sock);
         return -1;
         }
-    stat->down[i] = traff;
+    stat->monthDown[i] = traff;
 
     sprintf(s, "U%d", i);
     if (GetULongLongInt(row[startPos+i*2+1], &traff, 0) != 0)
@@ -834,7 +949,7 @@ for (int i = 0; i < DIR_NUM; i++)
         mysql_close(sock);
         return -1;
         }
-    stat->up[i] = traff;
+    stat->monthUp[i] = traff;
     }//for
 
 startPos += (2*DIR_NUM);
@@ -899,7 +1014,7 @@ std::string res;
 
 strprintf(&res,"UPDATE users SET Password='%s', Passive=%d, Down=%d, DisabledDetailStat = %d, "\
     "AlwaysOnline=%d, Tariff='%s', Address='%s', Phone='%s', Email='%s', "\
-    "Note='%s', RealName='%s', StgGroup='%s', Credit=%f, TariffChange='%s', ", 
+    "Note='%s', RealName='%s', StgGroup='%s', Credit=%f, TariffChange='%s', ",
     conf.password.c_str(),
     conf.passive,
     conf.disabled,
@@ -918,11 +1033,11 @@ strprintf(&res,"UPDATE users SET Password='%s', Passive=%d, Down=%d, DisabledDet
 
 for (int i = 0; i < USERDATA_NUM; i++)
     {
-    strprintf(&param, " Userdata%d='%s',", i, 
+    strprintf(&param, " Userdata%d='%s',", i,
         (ReplaceStr(conf.userdata[i],badSyms,repSym)).c_str());
     res += param;
     }
-    
+
 strprintf(&param, " CreditExpire=%d,", conf.creditExpire);
 res += param;
 
@@ -954,15 +1069,15 @@ res = "UPDATE users SET";
 
 for (int i = 0; i < DIR_NUM; i++)
     {
-    strprintf(&param, " D%d=%lld,", i, stat.down[i]);
+    strprintf(&param, " D%d=%lld,", i, stat.monthDown[i]);
     res += param;
 
-    strprintf(&param, " U%d=%lld,", i, stat.up[i]);
+    strprintf(&param, " U%d=%lld,", i, stat.monthUp[i]);
     res += param;
     }
 
 strprintf(&param, " Cash=%f, FreeMb=%f, LastCashAdd=%f, LastCashAddTime=%d,"\
-    " PassiveTime=%d, LastActivityTime=%d", 
+    " PassiveTime=%d, LastActivityTime=%d",
     stat.cash,
     stat.freeMb,
     stat.lastCashAdd,
@@ -1017,7 +1132,7 @@ if (num_rows < 1)
 {
     sprintf(qbuf,"CREATE TABLE logs_%02d_%4d (unid INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, login VARCHAR(40),text TEXT)",
     lt->tm_mon+1, lt->tm_year+1900);
-    
+
     if(MysqlQuery(qbuf,sock))
     {
         errorStr = "Couldn't create WriteDetailedStat table:\n";
@@ -1107,24 +1222,24 @@ logStr += "\'";
 return WriteLogString(logStr, login);
 }
 //-----------------------------------------------------------------------------
-int MYSQL_STORE::SaveMonthStat(const USER_STAT & stat, int month, int year, 
+int MYSQL_STORE::SaveMonthStat(const USER_STAT & stat, int month, int year,
                                 const std::string & login) const
 {
 std::string param, res;
 
-strprintf(&res, "INSERT INTO stat SET login='%s', month=%d, year=%d,", 
+strprintf(&res, "INSERT INTO stat SET login='%s', month=%d, year=%d,",
     login.c_str(), month+1, year+1900);
-    
+
 for (int i = 0; i < DIR_NUM; i++)
     {
-    strprintf(&param, " U%d=%lld,", i, stat.up[i]); 
+    strprintf(&param, " U%d=%lld,", i, stat.monthUp[i]);
     res += param;
 
-    strprintf(&param, " D%d=%lld,", i, stat.down[i]);        
+    strprintf(&param, " D%d=%lld,", i, stat.monthDown[i]);
     res += param;
     }
-    
-strprintf(&param, " cash=%f", stat.cash);        
+
+strprintf(&param, " cash=%f", stat.cash);
 res += param;
 
 if(MysqlSetQuery(res.c_str()))
@@ -1140,7 +1255,7 @@ return 0;
 int MYSQL_STORE::AddAdmin(const std::string & login) const
 {
 sprintf(qbuf,"INSERT INTO admins SET login='%s'", login.c_str());
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't add admin:\n";
@@ -1154,7 +1269,7 @@ return 0;
 int MYSQL_STORE::DelAdmin(const std::string & login) const
 {
 sprintf(qbuf,"DELETE FROM admins where login='%s' LIMIT 1", login.c_str());
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't delete admin:\n";
@@ -1175,14 +1290,14 @@ memset(pass, 0, sizeof(pass));
 memset(adminPass, 0, sizeof(adminPass));
 
 BLOWFISH_CTX ctx;
-EnDecodeInit(adm_enc_passwd, strlen(adm_enc_passwd), &ctx);
+InitContext(adm_enc_passwd, strlen(adm_enc_passwd), &ctx);
 
 strncpy(adminPass, ac.password.c_str(), ADM_PASSWD_LEN);
 adminPass[ADM_PASSWD_LEN - 1] = 0;
 
 for (int i = 0; i < ADM_PASSWD_LEN/8; i++)
     {
-    EncodeString(pass + 8*i, adminPass + 8*i, &ctx);
+    EncryptBlock(pass + 8*i, adminPass + 8*i, &ctx);
     }
 
 pass[ADM_PASSWD_LEN - 1] = 0;
@@ -1190,7 +1305,7 @@ Encode12(passwordE, pass, ADM_PASSWD_LEN);
 
 sprintf(qbuf,"UPDATE admins SET password='%s', ChgConf=%d, ChgPassword=%d, "\
     "ChgStat=%d, ChgCash=%d, UsrAddDel=%d, ChgTariff=%d, ChgAdmin=%d "\
-    "WHERE login='%s' LIMIT 1", 
+    "WHERE login='%s' LIMIT 1",
     passwordE,
     ac.priv.userConf,
     ac.priv.userPasswd,
@@ -1219,16 +1334,14 @@ char password[ADM_PASSWD_LEN + 1];
 char passwordE[2*ADM_PASSWD_LEN + 2];
 BLOWFISH_CTX ctx;
 
-memset(pass, 0, sizeof(pass));
 memset(password, 0, sizeof(password));
-memset(passwordE, 0, sizeof(passwordE));
 
 std::string p;
 MYSQL_RES *res;
 MYSQL_ROW row;
 MYSQL * sock;
 sprintf(qbuf,"SELECT * FROM admins WHERE login='%s' LIMIT 1", login.c_str());
-    
+
 if(MysqlGetQuery(qbuf,sock))
 {
     errorStr = "Couldn't restore admin:\n";
@@ -1252,7 +1365,7 @@ if ( mysql_num_rows(res) == 0)
     mysql_close(sock);
     return -1;
 }
-  
+
 row = mysql_fetch_row(res);
 
 p = row[1];
@@ -1273,11 +1386,11 @@ memset(pass, 0, sizeof(pass));
 if (passwordE[0] != 0)
     {
     Decode21(pass, passwordE);
-    EnDecodeInit(adm_enc_passwd, strlen(adm_enc_passwd), &ctx);
+    InitContext(adm_enc_passwd, strlen(adm_enc_passwd), &ctx);
 
     for (int i = 0; i < ADM_PASSWD_LEN/8; i++)
         {
-        DecodeString(password + 8*i, pass + 8*i, &ctx);
+        DecryptBlock(password + 8*i, pass + 8*i, &ctx);
         }
     }
 else
@@ -1289,7 +1402,7 @@ ac->password = password;
 
 uint16_t a;
 
-if (GetInt(row[2], &a) == 0) 
+if (GetInt(row[2], &a) == 0)
     ac->priv.userConf = a;
 else
     {
@@ -1299,7 +1412,7 @@ else
     return -1;
     }
 
-if (GetInt(row[3], &a) == 0) 
+if (GetInt(row[3], &a) == 0)
     ac->priv.userPasswd = a;
 else
     {
@@ -1309,7 +1422,7 @@ else
     return -1;
     }
 
-if (GetInt(row[4], &a) == 0) 
+if (GetInt(row[4], &a) == 0)
     ac->priv.userStat = a;
 else
     {
@@ -1319,7 +1432,7 @@ else
     return -1;
     }
 
-if (GetInt(row[5], &a) == 0) 
+if (GetInt(row[5], &a) == 0)
     ac->priv.userCash = a;
 else
     {
@@ -1329,7 +1442,7 @@ else
     return -1;
     }
 
-if (GetInt(row[6], &a) == 0) 
+if (GetInt(row[6], &a) == 0)
     ac->priv.userAddDel = a;
 else
     {
@@ -1339,7 +1452,7 @@ else
     return -1;
     }
 
-if (GetInt(row[7], &a) == 0) 
+if (GetInt(row[7], &a) == 0)
     ac->priv.tariffChg = a;
 else
     {
@@ -1349,7 +1462,7 @@ else
     return -1;
     }
 
-if (GetInt(row[8], &a) == 0) 
+if (GetInt(row[8], &a) == 0)
     ac->priv.adminChg = a;
 else
     {
@@ -1367,7 +1480,7 @@ return 0;
 int MYSQL_STORE::AddTariff(const std::string & name) const
 {
 sprintf(qbuf,"INSERT INTO tariffs SET name='%s'", name.c_str());
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't add tariff:\n";
@@ -1381,7 +1494,7 @@ return 0;
 int MYSQL_STORE::DelTariff(const std::string & name) const
 {
 sprintf(qbuf,"DELETE FROM tariffs WHERE name='%s' LIMIT 1", name.c_str());
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't delete tariff: ";
@@ -1398,7 +1511,7 @@ MYSQL_RES *res;
 MYSQL_ROW row;
 MYSQL * sock;
 sprintf(qbuf,"SELECT * FROM tariffs WHERE name='%s' LIMIT 1", tariffName.c_str());
-    
+
 if(MysqlGetQuery(qbuf,sock))
 {
     errorStr = "Couldn't restore Tariff:\n";
@@ -1433,10 +1546,10 @@ for (int i = 0; i<DIR_NUM; i++)
         return -1;
         }
 
-    ParseTariffTimeStr(str.c_str(), 
-                       td->dirPrice[i].hDay, 
-                       td->dirPrice[i].mDay, 
-                       td->dirPrice[i].hNight, 
+    ParseTariffTimeStr(str.c_str(),
+                       td->dirPrice[i].hDay,
+                       td->dirPrice[i].mDay,
+                       td->dirPrice[i].hNight,
                        td->dirPrice[i].mNight);
 
     strprintf(&param, "PriceDayA%d", i);
@@ -1533,7 +1646,22 @@ if (GetDouble(row[1+8*DIR_NUM], &td->tariffConf.passiveCost, 0.0) < 0)
 
     str = row[4+8*DIR_NUM];
     param = "TraffType";
-    
+
+    if (str.length() == 0)
+        {
+        mysql_free_result(res);
+        errorStr = "Cannot read tariff " + tariffName + ". Parameter " + param;
+        mysql_close(sock);
+        return -1;
+        }
+
+td->tariffConf.traffType = TARIFF::StringToTraffType(str);
+
+if (schemaVersion > 0)
+{
+    str = row[5+8*DIR_NUM];
+    param = "Period";
+
     if (str.length() == 0)
         {
         mysql_free_result(res);
@@ -1542,24 +1670,46 @@ if (GetDouble(row[1+8*DIR_NUM], &td->tariffConf.passiveCost, 0.0) < 0)
         return -1;
         }
 
-if (!strcasecmp(str.c_str(), "up"))
-    td->tariffConf.traffType = TRAFF_UP;
+    td->tariffConf.period = TARIFF::StringToPeriod(str);
+    }
 else
-    if (!strcasecmp(str.c_str(), "down"))
-        td->tariffConf.traffType = TRAFF_DOWN;
-    else
-        if (!strcasecmp(str.c_str(), "up+down"))
-            td->tariffConf.traffType = TRAFF_UP_DOWN;
-        else
-            if (!strcasecmp(str.c_str(), "max"))
-                td->tariffConf.traffType = TRAFF_MAX;
-            else
-                {
-                mysql_free_result(res);
-                errorStr = "Cannot read tariff " + tariffName + ". Parameter TraffType incorrect";
-                mysql_close(sock);
-                return -1;
-                }
+    {
+    td->tariffConf.period = TARIFF::MONTH;
+    }
+
+if (schemaVersion > 1)
+    {
+    str = row[6+8*DIR_NUM];
+    param = "ChangePolicy";
+
+    if (str.length() == 0)
+        {
+        mysql_free_result(res);
+        errorStr = "Cannot read tariff " + tariffName + ". Parameter " + param;
+        mysql_close(sock);
+        return -1;
+        }
+
+    td->tariffConf.changePolicy = TARIFF::StringToChangePolicy(str);
+
+    str = row[7+8*DIR_NUM];
+    param = "ChangePolicyTimeout";
+
+    if (str.length() == 0)
+        {
+        mysql_free_result(res);
+        errorStr = "Cannot read tariff " + tariffName + ". Parameter " + param;
+        mysql_close(sock);
+        return -1;
+        }
+
+    td->tariffConf.changePolicyTimeout = readTime(str);
+    }
+else
+    {
+    td->tariffConf.changePolicy = TARIFF::ALLOW;
+    td->tariffConf.changePolicyTimeout = 0;
+    }
 
 mysql_free_result(res);
 mysql_close(sock);
@@ -1574,30 +1724,30 @@ std::string res="UPDATE tariffs SET";
 
 for (int i = 0; i < DIR_NUM; i++)
     {
-    strprintf(&param, " PriceDayA%d=%f,", i, 
+    strprintf(&param, " PriceDayA%d=%f,", i,
         td.dirPrice[i].priceDayA * pt_mega);
     res += param;
 
-    strprintf(&param, " PriceDayB%d=%f,", i, 
-        td.dirPrice[i].priceDayB * pt_mega);        
+    strprintf(&param, " PriceDayB%d=%f,", i,
+        td.dirPrice[i].priceDayB * pt_mega);
     res += param;
-        
+
     strprintf(&param, " PriceNightA%d=%f,", i,
         td.dirPrice[i].priceNightA * pt_mega);
     res += param;
 
-    strprintf(&param, " PriceNightB%d=%f,", i, 
+    strprintf(&param, " PriceNightB%d=%f,", i,
         td.dirPrice[i].priceNightB * pt_mega);
     res += param;
-        
-    strprintf(&param, " Threshold%d=%d,", i, 
+
+    strprintf(&param, " Threshold%d=%d,", i,
         td.dirPrice[i].threshold);
     res += param;
 
     std::string s;
     strprintf(&param, " Time%d", i);
 
-    strprintf(&s, "%0d:%0d-%0d:%0d", 
+    strprintf(&s, "%0d:%0d-%0d:%0d",
             td.dirPrice[i].hDay,
             td.dirPrice[i].mDay,
             td.dirPrice[i].hNight,
@@ -1605,11 +1755,11 @@ for (int i = 0; i < DIR_NUM; i++)
 
     res += (param + "='" + s + "',");
 
-    strprintf(&param, " NoDiscount%d=%d,", i, 
+    strprintf(&param, " NoDiscount%d=%d,", i,
         td.dirPrice[i].noDiscount);
     res += param;
 
-    strprintf(&param, " SinglePrice%d=%d,", i, 
+    strprintf(&param, " SinglePrice%d=%d,", i,
         td.dirPrice[i].singlePrice);
     res += param;
     }
@@ -1623,27 +1773,21 @@ res += param;
 strprintf(&param, " Free=%f,", td.tariffConf.free);
 res += param;
 
-switch (td.tariffConf.traffType)
-    {
-    case TRAFF_UP:
-        res += " TraffType='up'";
-        break;
-    case TRAFF_DOWN:
-        res += " TraffType='down'";
-        break;
-    case TRAFF_UP_DOWN:
-        res += " TraffType='up+down'";
-        break;
-    case TRAFF_MAX:
-        res += " TraffType='max'";
-        break;
-    }
+res += " TraffType='" + TARIFF::TraffTypeToString(td.tariffConf.traffType) + "'";
+
+if (schemaVersion > 0)
+    res += ", Period='" + TARIFF::PeriodToString(td.tariffConf.period) + "'";
+
+if (schemaVersion > 1)
+    res += ", change_policy='" + TARIFF::ChangePolicyToString(td.tariffConf.changePolicy) + "'"\
+           ", change_policy_timeout='" + formatTime(td.tariffConf.changePolicy) + "'";
+
 strprintf(&param, " WHERE name='%s' LIMIT 1", tariffName.c_str());
 res += param;
 
 if(MysqlSetQuery(res.c_str()))
 {
-    errorStr = "Couldn't save admin:\n";
+    errorStr = "Couldn't save tariff:\n";
     //errorStr += mysql_error(sock);
     return -1;
 }
@@ -1651,8 +1795,8 @@ if(MysqlSetQuery(res.c_str()))
 return 0;
 }
 //-----------------------------------------------------------------------------
-int MYSQL_STORE::WriteDetailedStat(const std::map<IP_DIR_PAIR, STAT_NODE> & statTree, 
-                                   time_t lastStat, 
+int MYSQL_STORE::WriteDetailedStat(const std::map<IP_DIR_PAIR, STAT_NODE> & statTree,
+                                   time_t lastStat,
                                    const std::string & login) const
 {
 std::string res, stTime, endTime, tempStr;
@@ -1696,7 +1840,7 @@ if (num_rows < 1)
         "IP VARCHAR(17) DEFAULT '',dir INT DEFAULT 0,"\
         "down BIGINT DEFAULT 0,up BIGINT DEFAULT 0, cash DOUBLE DEFAULT 0.0, INDEX (login), INDEX(dir), INDEX(day), INDEX(IP))",
     lt->tm_mon+1, lt->tm_year+1900);
-    
+
     if(MysqlQuery(qbuf,sock))
     {
         errorStr = "Couldn't create WriteDetailedStat table:\n";
@@ -1723,12 +1867,12 @@ lt2 = localtime(&t);
 h2 = lt2->tm_hour;
 m2 = lt2->tm_min;
 s2 = lt2->tm_sec;
-    
+
 strprintf(&stTime, "%02d:%02d:%02d", h1, m1, s1);
 strprintf(&endTime, "%02d:%02d:%02d", h2, m2, s2);
 
 strprintf(&res,"INSERT INTO detailstat_%02d_%4d SET login='%s',"\
-    "day=%d,startTime='%s',endTime='%s',", 
+    "day=%d,startTime='%s',endTime='%s',",
     lt->tm_mon+1, lt->tm_year+1900,
     login.c_str(),
     lt->tm_mday,
@@ -1741,14 +1885,14 @@ stIter = statTree.begin();
 
 while (stIter != statTree.end())
     {
-        strprintf(&tempStr,"IP='%s', dir=%d, down=%lld, up=%lld, cash=%f", 
+        strprintf(&tempStr,"IP='%s', dir=%d, down=%lld, up=%lld, cash=%f",
                 inet_ntostring(stIter->first.ip).c_str(),
-                stIter->first.dir, 
-                stIter->second.down, 
-                stIter->second.up, 
+                stIter->first.dir,
+                stIter->second.down,
+                stIter->second.up,
                 stIter->second.cash
             );
-    
+
         if( MysqlQuery((res+tempStr).c_str(),sock) )
         {
             errorStr = "Couldn't insert data in WriteDetailedStat:\n";
@@ -1775,11 +1919,11 @@ gettimeofday(&tv, NULL);
 
 msg->header.id = static_cast<uint64_t>(tv.tv_sec) * 1000000 + static_cast<uint64_t>(tv.tv_usec);
 
-sprintf(qbuf,"INSERT INTO messages SET login='%s', id=%lld", 
+sprintf(qbuf,"INSERT INTO messages SET login='%s', id=%lld",
     login.c_str(),
     static_cast<long long>(msg->header.id)
     );
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't add message:\n";
@@ -1796,7 +1940,7 @@ std::string res;
 
 strprintf(&res,"UPDATE messages SET type=%d, lastSendTime=%u, creationTime=%u, "\
     "showTime=%u, stgRepeat=%d, repeatPeriod=%u, text='%s' "\
-    "WHERE login='%s' AND id=%lld LIMIT 1", 
+    "WHERE login='%s' AND id=%lld LIMIT 1",
     msg.header.type,
     msg.header.lastSendTime,
     msg.header.creationTime,
@@ -1826,7 +1970,7 @@ MYSQL * sock;
 
 sprintf(qbuf,"SELECT * FROM messages WHERE login='%s' AND id=%llu LIMIT 1",
         login.c_str(), static_cast<unsigned long long>(id));
-    
+
 if(MysqlGetQuery(qbuf,sock))
 {
     errorStr = "Couldn't GetMessage:\n";
@@ -1903,9 +2047,9 @@ return 0;
 //-----------------------------------------------------------------------------
 int MYSQL_STORE::DelMessage(uint64_t id, const std::string & login) const
 {
-sprintf(qbuf,"DELETE FROM messages WHERE login='%s' AND id=%lld LIMIT 1", 
+sprintf(qbuf,"DELETE FROM messages WHERE login='%s' AND id=%lld LIMIT 1",
         login.c_str(), static_cast<long long>(id));
-    
+
 if(MysqlSetQuery(qbuf))
 {
     errorStr = "Couldn't delete Message:\n";
@@ -1922,7 +2066,7 @@ MYSQL_RES *res;
 MYSQL_ROW row;
 MYSQL * sock;
 sprintf(qbuf,"SELECT * FROM messages WHERE login='%s'", login.c_str());
-    
+
 if(MysqlGetQuery(qbuf,sock))
 {
     errorStr = "Couldn't GetMessageHdrs:\n";
@@ -1948,9 +2092,9 @@ for (i = 0; i < num_rows; i++)
     row = mysql_fetch_row(res);
     if (str2x(row[1], id))
         continue;
-    
+
     STG_MSG_HDR hdr;
-    if (row[2]) 
+    if (row[2])
         if(str2x(row[2], hdr.type))
             continue;
 
@@ -2007,7 +2151,7 @@ MYSQL *  MYSQL_STORE::MysqlConnect() const {
     }
     if (!(sock = mysql_real_connect(sock,storeSettings.GetDBHost().c_str(),
             storeSettings.GetDBUser().c_str(),storeSettings.GetDBPassword().c_str(),
-            0,0,NULL,0)))
+            0,storeSettings.GetDBPort(),NULL,0)))
         {
             errorStr = "Couldn't connect to mysql engine! With error:\n";
             errorStr += mysql_error(sock);