]> git.stg.codes - stg.git/blob - projects/stargazer/user_impl.cpp
Use async-radius.
[stg.git] / projects / stargazer / user_impl.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Date: 27.10.2002
19  */
20
21 /*
22  *    Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
23  */
24
25 /*
26  $Revision: 1.101 $
27  $Date: 2010/11/03 10:50:03 $
28  $Author: faust $
29  */
30
31 #ifndef _GNU_SOURCE
32 #define _GNU_SOURCE
33 #endif
34
35 #include "user_impl.h"
36 #include "settings_impl.h"
37 #include "stg_timer.h"
38
39 #include "stg/users.h"
40 #include "stg/common.h"
41 #include "stg/scriptexecuter.h"
42 #include "stg/tariff.h"
43 #include "stg/tariffs.h"
44 #include "stg/services.h"
45 #include "stg/service_conf.h"
46 #include "stg/admin.h"
47
48 #include <algorithm>
49 #include <functional>
50 #include <array>
51
52 #include <cassert>
53 #include <cstdlib>
54 #include <cmath>
55
56 #include <pthread.h>
57 #include <unistd.h> // access
58
59 using STG::UserImpl;
60
61 namespace
62 {
63
64 std::string dirsToString(const bool * dirs)
65 {
66 std::string res;
67 for (size_t i = 0; i < DIR_NUM; i++)
68     res += dirs[i] ? '1' : '0';
69 return res;
70 }
71
72 void dirsFromBits(bool * dirs, uint32_t bits)
73 {
74 for (size_t i = 0; i < DIR_NUM; i++)
75     dirs[i] = bits & (1 << i);
76 }
77
78 }
79
80 UserImpl::UserImpl(const Settings * s,
81            const Store * st,
82            const Tariffs * t,
83            const Admin * a,
84            const Users * u,
85            const Services & svcs)
86     : users(u),
87       properties(*s),
88       WriteServLog(Logger::get()),
89       lastScanMessages(0),
90       id(0),
91       lastIPForDisconnect(0),
92       pingTime(0),
93       sysAdmin(a),
94       store(st),
95       tariffs(t),
96       tariff(NULL),
97       m_services(svcs),
98       settings(s),
99       authorizedModificationTime(0),
100       deleted(false),
101       lastWriteStat(0),
102       lastWriteDetailedStat(0),
103       cash(properties.cash),
104       up(properties.up),
105       down(properties.down),
106       lastCashAdd(properties.lastCashAdd),
107       passiveTime(properties.passiveTime),
108       lastCashAddTime(properties.lastCashAddTime),
109       freeMb(properties.freeMb),
110       lastActivityTime(properties.lastActivityTime),
111       password(properties.password),
112       passive(properties.passive),
113       disabled(properties.disabled),
114       disabledDetailStat(properties.disabledDetailStat),
115       alwaysOnline(properties.alwaysOnline),
116       tariffName(properties.tariffName),
117       nextTariff(properties.nextTariff),
118       address(properties.address),
119       note(properties.note),
120       group(properties.group),
121       email(properties.email),
122       phone(properties.phone),
123       realName(properties.realName),
124       credit(properties.credit),
125       creditExpire(properties.creditExpire),
126       ips(properties.ips),
127       userdata0(properties.userdata0),
128       userdata1(properties.userdata1),
129       userdata2(properties.userdata2),
130       userdata3(properties.userdata3),
131       userdata4(properties.userdata4),
132       userdata5(properties.userdata5),
133       userdata6(properties.userdata6),
134       userdata7(properties.userdata7),
135       userdata8(properties.userdata8),
136       userdata9(properties.userdata9),
137       sessionUploadModTime(stgTime),
138       sessionDownloadModTime(stgTime)
139 {
140     Init();
141 }
142 //-----------------------------------------------------------------------------
143 void UserImpl::Init()
144 {
145 password = "*_EMPTY_PASSWORD_*";
146 tariffName = NO_TARIFF_NAME;
147 tariff = tariffs->FindByName(tariffName);
148 ips = UserIPs::parse("*");
149 lastWriteStat = stgTime + random() % settings->GetStatWritePeriod();
150 lastWriteDetailedStat = stgTime;
151
152 m_beforePassiveConn = properties.passive.beforeChange([this](auto oldVal, auto newVal){ onPassiveChange(oldVal, newVal); });
153 m_afterDisabledConn = properties.disabled.afterChange([this](auto oldVal, auto newVal){ onDisabledChange(oldVal, newVal); });
154 m_beforeTariffConn = properties.tariffName.beforeChange([this](const auto& oldVal, const auto& newVal){ onTariffChange(oldVal, newVal); });
155 m_beforeCashConn = properties.cash.beforeChange([this](auto oldVal, auto newVal){ onCashChange(oldVal, newVal); });
156 m_afterIPConn = ips.afterChange([this](const auto& oldVal, const auto& newVal){ onIPChange(oldVal, newVal); });
157 }
158 //-----------------------------------------------------------------------------
159 UserImpl::UserImpl(const UserImpl & u)
160     : users(u.users),
161       properties(*u.settings),
162       WriteServLog(Logger::get()),
163       lastScanMessages(0),
164       login(u.login),
165       id(u.id),
166       lastIPForDisconnect(0),
167       pingTime(u.pingTime),
168       sysAdmin(u.sysAdmin),
169       store(u.store),
170       tariffs(u.tariffs),
171       tariff(u.tariff),
172       m_services(u.m_services),
173       traffStat(u.traffStat),
174       traffStatSaved(u.traffStatSaved),
175       settings(u.settings),
176       authorizedModificationTime(u.authorizedModificationTime),
177       messages(u.messages),
178       deleted(u.deleted),
179       lastWriteStat(u.lastWriteStat),
180       lastWriteDetailedStat(u.lastWriteDetailedStat),
181       cash(properties.cash),
182       up(properties.up),
183       down(properties.down),
184       lastCashAdd(properties.lastCashAdd),
185       passiveTime(properties.passiveTime),
186       lastCashAddTime(properties.lastCashAddTime),
187       freeMb(properties.freeMb),
188       lastActivityTime(properties.lastActivityTime),
189       password(properties.password),
190       passive(properties.passive),
191       disabled(properties.disabled),
192       disabledDetailStat(properties.disabledDetailStat),
193       alwaysOnline(properties.alwaysOnline),
194       tariffName(properties.tariffName),
195       nextTariff(properties.nextTariff),
196       address(properties.address),
197       note(properties.note),
198       group(properties.group),
199       email(properties.email),
200       phone(properties.phone),
201       realName(properties.realName),
202       credit(properties.credit),
203       creditExpire(properties.creditExpire),
204       ips(properties.ips),
205       userdata0(properties.userdata0),
206       userdata1(properties.userdata1),
207       userdata2(properties.userdata2),
208       userdata3(properties.userdata3),
209       userdata4(properties.userdata4),
210       userdata5(properties.userdata5),
211       userdata6(properties.userdata6),
212       userdata7(properties.userdata7),
213       userdata8(properties.userdata8),
214       userdata9(properties.userdata9),
215       sessionUpload(),
216       sessionDownload(),
217       sessionUploadModTime(stgTime),
218       sessionDownloadModTime(stgTime)
219 {
220     if (&u == this)
221         return;
222
223     m_beforePassiveConn = properties.passive.beforeChange([this](auto oldVal, auto newVal){ onPassiveChange(oldVal, newVal); });
224     m_afterDisabledConn = properties.disabled.afterChange([this](auto oldVal, auto newVal){ onDisabledChange(oldVal, newVal); });
225     m_beforeTariffConn = properties.tariffName.beforeChange([this](const auto& oldVal, const auto& newVal){ onTariffChange(oldVal, newVal); });
226     m_beforeCashConn = properties.cash.beforeChange([this](auto oldVal, auto newVal){ onCashChange(oldVal, newVal); });
227     m_afterIPConn = ips.afterChange([this](const auto& oldVal, const auto& newVal){ onIPChange(oldVal, newVal); });
228
229     properties.SetProperties(u.properties);
230 }
231 //-----------------------------------------------------------------------------
232 void UserImpl::SetLogin(const std::string & l)
233 {
234 std::lock_guard lock(m_mutex);
235 static int idGen = 0;
236 assert(login.empty() && "Login is already set");
237 login = l;
238 id = idGen++;
239 }
240 //-----------------------------------------------------------------------------
241 int UserImpl::ReadConf()
242 {
243 std::lock_guard lock(m_mutex);
244 UserConf conf;
245
246 if (store->RestoreUserConf(&conf, login))
247     {
248     WriteServLog("Cannot read conf for user %s.", login.c_str());
249     WriteServLog("%s", store->GetStrError().c_str());
250     printfd(__FILE__, "Cannot read conf for user %s.\n", login.c_str());
251     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
252     return -1;
253     }
254
255 properties.SetConf(conf);
256
257 tariff = tariffs->FindByName(tariffName);
258 if (tariff == NULL)
259     {
260     WriteServLog("Cannot read user %s. Tariff %s not exist.",
261                  login.c_str(), properties.tariffName.Get().c_str());
262     return -1;
263     }
264
265 std::vector<Message::Header> hdrsList;
266
267 if (store->GetMessageHdrs(&hdrsList, login))
268     {
269     printfd(__FILE__, "Error GetMessageHdrs %s\n", store->GetStrError().c_str());
270     WriteServLog("Cannot read user %s. Error reading message headers: %s.",
271                  login.c_str(),
272                  store->GetStrError().c_str());
273     return -1;
274     }
275
276 std::vector<Message::Header>::const_iterator it;
277 for (it = hdrsList.begin(); it != hdrsList.end(); ++it)
278     {
279     Message msg;
280     if (store->GetMessage(it->id, &msg, login) == 0)
281         {
282         messages.push_back(msg);
283         }
284     }
285
286 return 0;
287 }
288 //-----------------------------------------------------------------------------
289 int UserImpl::ReadStat()
290 {
291 std::lock_guard lock(m_mutex);
292 UserStat stat;
293
294 if (store->RestoreUserStat(&stat, login))
295     {
296     WriteServLog("Cannot read stat for user %s.", login.c_str());
297     WriteServLog("%s", store->GetStrError().c_str());
298     printfd(__FILE__, "Cannot read stat for user %s.\n", login.c_str());
299     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
300     return -1;
301     }
302
303 properties.SetStat(stat);
304
305 return 0;
306 }
307 //-----------------------------------------------------------------------------
308 int UserImpl::WriteConf()
309 {
310 std::lock_guard lock(m_mutex);
311 UserConf conf(properties.GetConf());
312
313 printfd(__FILE__, "UserImpl::WriteConf()\n");
314
315 if (store->SaveUserConf(conf, login))
316     {
317     WriteServLog("Cannot write conf for user %s.", login.c_str());
318     WriteServLog("%s", store->GetStrError().c_str());
319     printfd(__FILE__, "Cannot write conf for user %s.\n", login.c_str());
320     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
321     return -1;
322     }
323
324 return 0;
325 }
326 //-----------------------------------------------------------------------------
327 int UserImpl::WriteStat()
328 {
329 std::lock_guard lock(m_mutex);
330 UserStat stat(properties.GetStat());
331
332 if (store->SaveUserStat(stat, login))
333     {
334     WriteServLog("Cannot write stat for user %s.", login.c_str());
335     WriteServLog("%s", store->GetStrError().c_str());
336     printfd(__FILE__, "Cannot write stat for user %s.\n", login.c_str());
337     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
338     return -1;
339     }
340
341 lastWriteStat = stgTime;
342
343 return 0;
344 }
345 //-----------------------------------------------------------------------------
346 int UserImpl::WriteMonthStat()
347 {
348 std::lock_guard lock(m_mutex);
349 time_t tt = stgTime - 3600;
350 struct tm t1;
351 localtime_r(&tt, &t1);
352
353 UserStat stat(properties.GetStat());
354 if (store->SaveMonthStat(stat, t1.tm_mon, t1.tm_year, login))
355     {
356     WriteServLog("Cannot write month stat for user %s.", login.c_str());
357     WriteServLog("%s", store->GetStrError().c_str());
358     printfd(__FILE__, "Cannot write month stat for user %s.\n", login.c_str());
359     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
360     return -1;
361     }
362
363 return 0;
364 }
365 //-----------------------------------------------------------------------------
366 int UserImpl::Authorize(uint32_t ip, uint32_t dirs, const Auth * auth)
367 {
368 std::lock_guard lock(m_mutex);
369 /*
370  *  Authorize user. It only means that user will be authorized. Nothing more.
371  *  User can be connected or disconnected while authorized.
372  *  Example: user is authorized but disconnected due to 0 money or blocking
373  */
374
375 /*
376  * TODO: in fact "authorization" means allowing access to a service. What we
377  * call "authorization" here, int STG, is "authentication". So this should be
378  * fixed in future.
379  */
380
381 /*
382  * Prevent double authorization by identical authorizers
383  */
384 if (authorizedBy.find(auth) != authorizedBy.end())
385     return 0;
386
387 if (!ip)
388     return -1;
389
390 dirsFromBits(enabledDirs, dirs);
391
392 if (!authorizedBy.empty())
393     {
394     if (m_currIP != ip)
395         {
396         // We are already authorized, but with different IP address
397         errorStr = "User " + login + " already authorized with IP address " + inet_ntostring(ip);
398         return -1;
399         }
400
401     User * u = NULL;
402     if (!users->FindByIPIdx(ip, &u))
403         {
404         // Address presents in IP-index.
405         // If it's not our IP - report it.
406         if (u != this)
407             {
408             errorStr = "IP address " + inet_ntostring(ip) + " is already in use";
409             return -1;
410             }
411         }
412     }
413 else
414     {
415     if (users->IsIPInIndex(ip))
416         {
417         // Address is already present in IP-index.
418         errorStr = "IP address " + inet_ntostring(ip) + " is already in use";
419         return -1;
420         }
421
422     if (ips.ConstData().find(ip))
423         {
424         m_currIP = ip;
425         lastIPForDisconnect = m_currIP;
426         }
427     else
428         {
429         printfd(__FILE__, " user %s: ips = %s\n", login.c_str(), ips.ConstData().toString().c_str());
430         errorStr = "IP address " + inet_ntostring(ip) + " does not belong to user " + login;
431         return -1;
432         }
433     }
434
435 if (authorizedBy.empty())
436     authorizedModificationTime = stgTime;
437 authorizedBy.insert(auth);
438
439 ScanMessage();
440
441 return 0;
442 }
443 //-----------------------------------------------------------------------------
444 void UserImpl::Unauthorize(const Auth * auth, const std::string & reason)
445 {
446 std::lock_guard lock(m_mutex);
447 /*
448  *  Authorizer tries to unauthorize user, that was not authorized by it
449  */
450 if (!authorizedBy.erase(auth))
451     return;
452
453 authorizedModificationTime = stgTime;
454
455 if (authorizedBy.empty())
456     {
457     lastDisconnectReason = reason;
458     lastIPForDisconnect = m_currIP;
459     m_currIP = 0; // DelUser in traffcounter
460     if (m_connected)
461         Disconnect(false, "not authorized");
462     return;
463     }
464 }
465 //-----------------------------------------------------------------------------
466 bool UserImpl::IsAuthorizedBy(const Auth * auth) const
467 {
468 std::lock_guard lock(m_mutex);
469 // Is this user authorized by specified authorizer?
470 return authorizedBy.find(auth) != authorizedBy.end();
471 }
472 //-----------------------------------------------------------------------------
473 std::vector<std::string> UserImpl::GetAuthorizers() const
474 {
475     std::lock_guard lock(m_mutex);
476     std::vector<std::string> list;
477     std::transform(authorizedBy.begin(), authorizedBy.end(), std::back_inserter(list), [](const auto auth){ return auth->GetVersion(); });
478     return list;
479 }
480 //-----------------------------------------------------------------------------
481 void UserImpl::Connect(bool fakeConnect)
482 {
483 /*
484  * Connect user to Internet. This function is differ from Authorize() !!!
485  */
486
487 if (!fakeConnect)
488     {
489     std::string scriptOnConnect = settings->GetScriptsDir() + "/OnConnect";
490
491     if (access(scriptOnConnect.c_str(), X_OK) == 0)
492         {
493         std::string dirs = dirsToString(enabledDirs);
494
495         std::string scriptOnConnectParams;
496         strprintf(&scriptOnConnectParams,
497                   "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
498                   scriptOnConnect.c_str(),
499                   login.c_str(),
500                   inet_ntostring(m_currIP).c_str(),
501                   cash.ConstData(),
502                   id,
503                   dirs.c_str());
504
505         std::vector<std::string>::const_iterator it(settings->GetScriptParams().begin());
506         while (it != settings->GetScriptParams().end())
507             {
508             scriptOnConnectParams += " \"" + GetParamValue(it->c_str()) + "\"";
509             ++it;
510             }
511
512         ScriptExec(scriptOnConnectParams.c_str());
513         }
514     else
515         {
516         WriteServLog("Script %s cannot be executed. File not found.", scriptOnConnect.c_str());
517         }
518
519     m_connected = true;
520     }
521
522 if (!settings->GetDisableSessionLog() && store->WriteUserConnect(login, m_currIP))
523     {
524     WriteServLog("Cannot write connect for user %s.", login.c_str());
525     WriteServLog("%s", store->GetStrError().c_str());
526     }
527
528 if (!fakeConnect)
529     lastIPForDisconnect = m_currIP;
530 }
531 //-----------------------------------------------------------------------------
532 void UserImpl::Disconnect(bool fakeDisconnect, const std::string & reason)
533 {
534 /*
535  *  Disconnect user from Internet. This function is differ from UnAuthorize() !!!
536  */
537
538 if (!lastIPForDisconnect)
539     {
540     printfd(__FILE__, "lastIPForDisconnect\n");
541     return;
542     }
543
544 if (!fakeDisconnect)
545     {
546     lastDisconnectReason = reason;
547     std::string scriptOnDisonnect = settings->GetScriptsDir() + "/OnDisconnect";
548
549     if (access(scriptOnDisonnect.c_str(), X_OK) == 0)
550         {
551         std::string dirs = dirsToString(enabledDirs);
552
553         std::string scriptOnDisonnectParams;
554         strprintf(&scriptOnDisonnectParams,
555                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
556                 scriptOnDisonnect.c_str(),
557                 login.c_str(),
558                 inet_ntostring(lastIPForDisconnect).c_str(),
559                 cash.ConstData(),
560                 id,
561                 dirs.c_str());
562
563         std::vector<std::string>::const_iterator it(settings->GetScriptParams().begin());
564         while (it != settings->GetScriptParams().end())
565             {
566             scriptOnDisonnectParams += " \"" + GetParamValue(it->c_str()) + "\"";
567             ++it;
568             }
569
570         ScriptExec(scriptOnDisonnectParams.c_str());
571         }
572     else
573         {
574         WriteServLog("Script OnDisconnect cannot be executed. File not found.");
575         }
576
577     m_connected = false;
578     }
579
580 std::string reasonMessage(reason);
581 if (!lastDisconnectReason.empty())
582     reasonMessage += ": " + lastDisconnectReason;
583
584 if (!settings->GetDisableSessionLog() && store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload,
585                                                                     cash, freeMb, reasonMessage))
586     {
587     WriteServLog("Cannot write disconnect for user %s.", login.c_str());
588     WriteServLog("%s", store->GetStrError().c_str());
589     }
590
591 if (!fakeDisconnect)
592     lastIPForDisconnect = 0;
593
594 sessionUpload.reset();
595 sessionDownload.reset();
596 sessionUploadModTime = stgTime;
597 sessionDownloadModTime = stgTime;
598 }
599 //-----------------------------------------------------------------------------
600 void UserImpl::Run()
601 {
602 std::lock_guard lock(m_mutex);
603
604 if (stgTime > lastWriteStat + settings->GetStatWritePeriod())
605     {
606     printfd(__FILE__, "UserImpl::WriteStat user=%s\n", GetLogin().c_str());
607     WriteStat();
608     }
609 if (creditExpire.ConstData() && creditExpire.ConstData() < stgTime)
610     {
611     WriteServLog("User: %s. Credit expired.", login.c_str());
612     credit = 0;
613     creditExpire = 0;
614     WriteConf();
615     }
616
617 if (passive.ConstData()
618     && (stgTime % 30 == 0)
619     && (passiveTime.ModificationTime() != stgTime))
620     {
621     passiveTime = passiveTime + (stgTime - passiveTime.ModificationTime());
622     printfd(__FILE__, "===== %s: passiveTime=%d =====\n", login.c_str(), passiveTime.ConstData());
623     }
624
625 if (!authorizedBy.empty())
626     {
627     if (m_connected)
628         properties.Stat().lastActivityTime = stgTime;
629
630     if (!m_connected && IsInetable())
631         Connect();
632
633     if (m_connected && !IsInetable())
634         {
635         if (disabled)
636             Disconnect(false, "disabled");
637         else if (passive)
638             Disconnect(false, "passive");
639         else
640             Disconnect(false, "no cash");
641         }
642
643     if (stgTime - lastScanMessages > 10)
644         {
645         ScanMessage();
646         lastScanMessages = stgTime;
647         }
648     }
649 else
650     {
651     if (m_connected)
652         Disconnect(false, "not authorized");
653     }
654
655 }
656 //-----------------------------------------------------------------------------
657 void UserImpl::UpdatePingTime(time_t t)
658 {
659 std::lock_guard lock(m_mutex);
660 if (t)
661     pingTime = t;
662 else
663     pingTime = stgTime;
664 }
665 //-----------------------------------------------------------------------------
666 bool UserImpl::IsInetable()
667 {
668 if (disabled || passive)
669     return false;
670
671 if (settings->GetFreeMbAllowInet())
672     {
673     if (freeMb >= 0)
674         return true;
675     }
676
677 if (settings->GetShowFeeInCash() || tariff == NULL)
678     return (cash >= -credit);
679
680 return (cash - tariff->GetFee() >= -credit);
681 }
682 //-----------------------------------------------------------------------------
683 std::string UserImpl::GetEnabledDirs() const
684 {
685 return dirsToString(enabledDirs);
686 }
687 //-----------------------------------------------------------------------------
688 #ifdef TRAFF_STAT_WITH_PORTS
689 void UserImpl::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
690 #else
691 void UserImpl::AddTraffStatU(int dir, uint32_t ip, uint32_t len)
692 #endif
693 {
694 std::lock_guard lock(m_mutex);
695
696 if (!m_connected || tariff == NULL)
697     return;
698
699 double cost = 0;
700 DirTraff dt(up);
701
702 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
703 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
704
705 dt[dir] += len;
706
707 int tt = tariff->GetTraffType();
708 if (tt == Tariff::TRAFF_UP ||
709     tt == Tariff::TRAFF_UP_DOWN ||
710     // Check NEW traff data
711     (tt == Tariff::TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
712     {
713     double dc = 0;
714     if (traff < threshold &&
715         traff + len >= threshold)
716         {
717         // cash = partBeforeThreshold * priceBeforeThreshold +
718         //        partAfterThreshold * priceAfterThreshold
719         int64_t before = threshold - traff; // Chunk part before threshold
720         int64_t after = len - before; // Chunk part after threshold
721         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir], // Traff before chunk
722                                            down.ConstData()[dir],
723                                            dir,
724                                            stgTime) * before +
725              tariff->GetPriceWithTraffType(dt[dir], // Traff after chunk
726                                            down.ConstData()[dir],
727                                            dir,
728                                            stgTime) * after;
729         }
730     else
731         {
732         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
733                                            down.ConstData()[dir],
734                                            dir,
735                                            stgTime) * len;
736         }
737
738     if (freeMb.ConstData() <= 0) // FreeMb is exhausted
739         cost = dc;
740     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
741         cost = dc - freeMb.ConstData();
742
743     // Direct access to internal data structures via friend-specifier
744     properties.Stat().freeMb -= dc;
745     properties.Stat().cash -= cost;
746     cash.ModifyTime();
747     freeMb.ModifyTime();
748     }
749
750 up = dt;
751 sessionUpload[dir] += len;
752 sessionUploadModTime = stgTime;
753
754 //Add detailed stat
755
756 if (!settings->GetWriteFreeMbTraffCost() &&
757      freeMb.ConstData() >= 0)
758     cost = 0;
759
760 #ifdef TRAFF_STAT_WITH_PORTS
761 IPDirPair idp(ip, dir, port);
762 #else
763 IPDirPair idp(ip, dir);
764 #endif
765
766 std::map<IPDirPair, StatNode>::iterator lb;
767 lb = traffStat.lower_bound(idp);
768 if (lb == traffStat.end() || lb->first != idp)
769     {
770     traffStat.insert(lb,
771                      std::make_pair(idp,
772                                     StatNode(len, 0, cost)));
773     }
774 else
775     {
776     lb->second.cash += cost;
777     lb->second.up += len;
778     }
779 }
780 //-----------------------------------------------------------------------------
781 #ifdef TRAFF_STAT_WITH_PORTS
782 void UserImpl::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
783 #else
784 void UserImpl::AddTraffStatD(int dir, uint32_t ip, uint32_t len)
785 #endif
786 {
787 std::lock_guard lock(m_mutex);
788
789 if (!m_connected || tariff == NULL)
790     return;
791
792 double cost = 0;
793 DirTraff dt(down);
794
795 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
796 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
797
798 dt[dir] += len;
799
800 int tt = tariff->GetTraffType();
801 if (tt == Tariff::TRAFF_DOWN ||
802     tt == Tariff::TRAFF_UP_DOWN ||
803     // Check NEW traff data
804     (tt == Tariff::TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
805     {
806     double dc = 0;
807     if (traff < threshold &&
808         traff + len >= threshold)
809         {
810         // cash = partBeforeThreshold * priceBeforeThreshold +
811         //        partAfterThreshold * priceAfterThreshold
812         int64_t before = threshold - traff; // Chunk part before threshold
813         int64_t after = len - before; // Chunk part after threshold
814         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
815                                            down.ConstData()[dir], // Traff before chunk
816                                            dir,
817                                            stgTime) * before +
818              tariff->GetPriceWithTraffType(up.ConstData()[dir],
819                                            dt[dir], // Traff after chunk
820                                            dir,
821                                            stgTime) * after;
822         }
823     else
824         {
825         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
826                                            down.ConstData()[dir],
827                                            dir,
828                                            stgTime) * len;
829         }
830
831     if (freeMb.ConstData() <= 0) // FreeMb is exhausted
832         cost = dc;
833     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
834         cost = dc - freeMb.ConstData();
835
836     properties.Stat().freeMb -= dc;
837     properties.Stat().cash -= cost;
838     cash.ModifyTime();
839     freeMb.ModifyTime();
840     }
841
842 down = dt;
843 sessionDownload[dir] += len;
844 sessionDownloadModTime = stgTime;
845
846 //Add detailed stat
847
848 if (!settings->GetWriteFreeMbTraffCost() &&
849      freeMb.ConstData() >= 0)
850     cost = 0;
851
852 #ifdef TRAFF_STAT_WITH_PORTS
853 IPDirPair idp(ip, dir, port);
854 #else
855 IPDirPair idp(ip, dir);
856 #endif
857
858 std::map<IPDirPair, StatNode>::iterator lb;
859 lb = traffStat.lower_bound(idp);
860 if (lb == traffStat.end() || lb->first != idp)
861     {
862     traffStat.insert(lb,
863                      std::make_pair(idp,
864                                     StatNode(0, len, cost)));
865     }
866 else
867     {
868     lb->second.cash += cost;
869     lb->second.down += len;
870     }
871 }
872 //-----------------------------------------------------------------------------
873 void UserImpl::OnAdd()
874 {
875 std::lock_guard lock(m_mutex);
876
877 std::string scriptOnAdd = settings->GetScriptsDir() + "/OnUserAdd";
878
879 if (access(scriptOnAdd.c_str(), X_OK) == 0)
880     {
881     std::string scriptOnAddParams = scriptOnAdd + " \"" + login + "\"";
882
883     ScriptExec(scriptOnAddParams.c_str());
884     }
885 else
886     {
887     WriteServLog("Script %s cannot be executed. File not found.", scriptOnAdd.c_str());
888     }
889 }
890 //-----------------------------------------------------------------------------
891 void UserImpl::OnDelete()
892 {
893 std::lock_guard lock(m_mutex);
894
895 std::string scriptOnDel = settings->GetScriptsDir() + "/OnUserDel";
896
897 if (access(scriptOnDel.c_str(), X_OK) == 0)
898     {
899     std::string scriptOnDelParams = scriptOnDel + " \"" + login + "\"";
900
901     ScriptExec(scriptOnDelParams.c_str());
902     }
903 else
904     {
905     WriteServLog("Script %s cannot be executed. File not found.", scriptOnDel.c_str());
906     }
907
908 Run();
909 }
910 //-----------------------------------------------------------------------------
911 int UserImpl::WriteDetailStat(bool hard)
912 {
913 printfd(__FILE__, "UserImpl::WriteDetailedStat() - saved size = %d\n", traffStatSaved.second.size());
914
915 if (!traffStatSaved.second.empty())
916     {
917     if (store->WriteDetailedStat(traffStatSaved.second, traffStatSaved.first, login))
918         {
919         printfd(__FILE__, "UserImpl::WriteDetailStat() - failed to write detail stat from queue\n");
920         WriteServLog("Cannot write detail stat from queue (of size %d recs) for user %s.", traffStatSaved.second.size(), login.c_str());
921         WriteServLog("%s", store->GetStrError().c_str());
922         return -1;
923         }
924     traffStatSaved.second.erase(traffStatSaved.second.begin(), traffStatSaved.second.end());
925     }
926
927 TraffStat ts;
928
929     {
930     std::lock_guard lock(m_mutex);
931     ts.swap(traffStat);
932     }
933
934 printfd(__FILE__, "UserImpl::WriteDetailedStat() - size = %d\n", ts.size());
935
936 if (ts.size() && !disabledDetailStat)
937     {
938     if (store->WriteDetailedStat(ts, lastWriteDetailedStat, login))
939         {
940         printfd(__FILE__, "UserImpl::WriteDetailStat() - failed to write current detail stat\n");
941         WriteServLog("Cannot write detail stat for user %s.", login.c_str());
942         WriteServLog("%s", store->GetStrError().c_str());
943         if (!hard)
944             {
945             printfd(__FILE__, "UserImpl::WriteDetailStat() - pushing detail stat to queue\n");
946             std::lock_guard lock(m_mutex);
947             traffStatSaved.second.swap(ts);
948             traffStatSaved.first = lastWriteDetailedStat;
949             }
950         return -1;
951         }
952     }
953 lastWriteDetailedStat = stgTime;
954 return 0;
955 }
956 //-----------------------------------------------------------------------------
957 double UserImpl::GetPassiveTimePart() const
958 {
959 std::lock_guard lock(m_mutex);
960 return getPassiveTimePart();
961 }
962
963 double UserImpl::getPassiveTimePart() const
964 {
965 static const std::array<unsigned, 12> daysInMonth{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
966
967 struct tm tms;
968 time_t t = stgTime;
969 localtime_r(&t, &tms);
970
971 time_t secMonth = daysInMonth[(tms.tm_mon + 11) % 12] * 24 * 3600; // Previous month
972
973 if (tms.tm_year % 4 == 0 && tms.tm_mon == 1)
974     {
975     // Leap year
976     secMonth += 24 * 3600;
977     }
978
979 time_t dt = secMonth - passiveTime;
980
981 if (dt < 0)
982     dt = 0;
983
984 return static_cast<double>(dt) / secMonth;
985 }
986 //-----------------------------------------------------------------------------
987 void UserImpl::SetPassiveTimeAsNewUser()
988 {
989 std::lock_guard lock(m_mutex);
990
991 time_t t = stgTime;
992 struct tm tm;
993 localtime_r(&t, &tm);
994 int daysCurrMon = DaysInCurrentMonth();
995 double pt = tm.tm_mday - 1;
996 pt /= daysCurrMon;
997
998 passiveTime = static_cast<time_t>(pt * 24 * 3600 * daysCurrMon);
999 }
1000 //-----------------------------------------------------------------------------
1001 void UserImpl::MidnightResetSessionStat()
1002 {
1003 std::lock_guard lock(m_mutex);
1004
1005 if (m_connected)
1006     {
1007     Disconnect(true, "fake");
1008     Connect(true);
1009     }
1010 }
1011 //-----------------------------------------------------------------------------
1012 void UserImpl::ProcessNewMonth()
1013 {
1014 std::lock_guard lock(m_mutex);
1015 //  Reset traff
1016 if (m_connected)
1017     Disconnect(true, "fake");
1018
1019 WriteMonthStat();
1020
1021 properties.Stat().monthUp.reset();
1022 properties.Stat().monthDown.reset();
1023
1024 if (m_connected)
1025     Connect(true);
1026
1027 //  Set new tariff
1028 if (nextTariff.ConstData() != "")
1029     {
1030     const Tariff * nt = tariffs->FindByName(nextTariff);
1031     if (nt == NULL)
1032         {
1033         WriteServLog("Cannot change tariff for user %s. Tariff %s not exist.",
1034                      login.c_str(), properties.tariffName.Get().c_str());
1035         }
1036     else
1037         {
1038         std::string message = tariff->TariffChangeIsAllowed(*nt, stgTime);
1039         if (message.empty())
1040             {
1041             properties.tariffName.Set(nextTariff, *sysAdmin, login, *store);
1042             }
1043         else
1044             {
1045             WriteServLog("Tariff change is prohibited for user %s. %s",
1046                          login.c_str(),
1047                          message.c_str());
1048             }
1049         }
1050     ResetNextTariff();
1051     WriteConf();
1052     }
1053 }
1054 //-----------------------------------------------------------------------------
1055 void UserImpl::ProcessDayFeeSpread()
1056 {
1057 std::lock_guard lock(m_mutex);
1058
1059 if (passive.ConstData() || tariff == NULL)
1060     return;
1061
1062 if (tariff->GetPeriod() != Tariff::MONTH)
1063     return;
1064
1065 double fee = tariff->GetFee() / DaysInCurrentMonth();
1066
1067 if (std::fabs(fee) < 1.0e-3)
1068     return;
1069
1070 double c = cash;
1071 switch (settings->GetFeeChargeType())
1072     {
1073     case 0:
1074         properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1075         break;
1076     case 1:
1077         if (c + credit >= 0)
1078             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1079         break;
1080     case 2:
1081         if (c + credit >= fee)
1082             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1083         break;
1084     case 3:
1085         if (c >= 0)
1086             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1087         break;
1088     }
1089 ResetPassiveTime();
1090 }
1091 //-----------------------------------------------------------------------------
1092 void UserImpl::ProcessDayFee()
1093 {
1094 std::lock_guard lock(m_mutex);
1095
1096 if (tariff == NULL)
1097     return;
1098
1099 if (tariff->GetPeriod() != Tariff::MONTH)
1100     return;
1101
1102 double passiveTimePart = 1.0;
1103 if (!settings->GetFullFee())
1104     {
1105     passiveTimePart = getPassiveTimePart();
1106     }
1107 else
1108     {
1109     if (passive.ConstData())
1110         {
1111         printfd(__FILE__, "Don't charge fee `cause we are passive\n");
1112         return;
1113         }
1114     }
1115 double fee = tariff->GetFee() * passiveTimePart;
1116
1117 ResetPassiveTime();
1118
1119 if (std::fabs(fee) < 1.0e-3)
1120     {
1121     SetPrepaidTraff();
1122     return;
1123     }
1124
1125 double c = cash;
1126 printfd(__FILE__, "login: %8s Cash=%f Credit=%f  Fee=%f PassiveTimePart=%f fee=%f\n",
1127         login.c_str(),
1128         cash.ConstData(),
1129         credit.ConstData(),
1130         tariff->GetFee(),
1131         passiveTimePart,
1132         fee);
1133 switch (settings->GetFeeChargeType())
1134     {
1135     case 0:
1136         properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1137         SetPrepaidTraff();
1138         break;
1139     case 1:
1140         if (c + credit >= 0)
1141             {
1142             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1143             SetPrepaidTraff();
1144             }
1145         break;
1146     case 2:
1147         if (c + credit >= fee)
1148             {
1149             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1150             SetPrepaidTraff();
1151             }
1152         break;
1153     case 3:
1154         if (c >= 0)
1155             {
1156             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1157             SetPrepaidTraff();
1158             }
1159         break;
1160     }
1161 }
1162 //-----------------------------------------------------------------------------
1163 void UserImpl::ProcessDailyFee()
1164 {
1165 std::lock_guard lock(m_mutex);
1166
1167 if (passive.ConstData() || tariff == NULL)
1168     return;
1169
1170 if (tariff->GetPeriod() != Tariff::DAY)
1171     return;
1172
1173 double fee = tariff->GetFee();
1174
1175 if (fee == 0.0)
1176     return;
1177
1178 double c = cash;
1179 switch (settings->GetFeeChargeType())
1180     {
1181     case 0:
1182         properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1183         break;
1184     case 1:
1185         if (c + credit >= 0)
1186             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1187         break;
1188     case 2:
1189         if (c + credit >= fee)
1190             properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1191         break;
1192     }
1193 ResetPassiveTime();
1194 }
1195 //-----------------------------------------------------------------------------
1196 void UserImpl::ProcessServices()
1197 {
1198 struct tm tms;
1199 time_t t = stgTime;
1200 localtime_r(&t, &tms);
1201
1202 std::lock_guard lock(m_mutex);
1203
1204 double passiveTimePart = 1.0;
1205 if (!settings->GetFullFee())
1206     {
1207     passiveTimePart = getPassiveTimePart();
1208     }
1209 else
1210     {
1211     if (passive.ConstData())
1212         {
1213         printfd(__FILE__, "Don't charge fee `cause we are passive\n");
1214         return;
1215         }
1216     }
1217
1218 for (size_t i = 0; i < properties.Conf().services.size(); ++i)
1219     {
1220     ServiceConf conf;
1221     if (m_services.Find(properties.Conf().services[i], &conf))
1222         continue;
1223     if (conf.payDay == tms.tm_mday ||
1224         (conf.payDay == 0 && tms.tm_mday == DaysInCurrentMonth()))
1225         {
1226         double c = cash;
1227         double fee = conf.cost * passiveTimePart;
1228         printfd(__FILE__, "Service fee. login: %8s Cash=%f Credit=%f  Fee=%f PassiveTimePart=%f fee=%f\n",
1229                 login.c_str(),
1230                 cash.ConstData(),
1231                 credit.ConstData(),
1232                 tariff->GetFee(),
1233                 passiveTimePart,
1234                 fee);
1235         switch (settings->GetFeeChargeType())
1236             {
1237             case 0:
1238                 properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1239                 SetPrepaidTraff();
1240                 break;
1241             case 1:
1242                 if (c + credit >= 0)
1243                     {
1244                     properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1245                     SetPrepaidTraff();
1246                     }
1247                 break;
1248             case 2:
1249                 if (c + credit >= fee)
1250                     {
1251                     properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1252                     SetPrepaidTraff();
1253                     }
1254                 break;
1255             case 3:
1256                 if (c >= 0)
1257                     {
1258                     properties.cash.Set(c - fee, *sysAdmin, login, *store, "Subscriber fee charge");
1259                     SetPrepaidTraff();
1260                     }
1261                 break;
1262             }
1263         }
1264     }
1265 }
1266 //-----------------------------------------------------------------------------
1267 void UserImpl::SetPrepaidTraff()
1268 {
1269 if (tariff != NULL)
1270     properties.freeMb.Set(tariff->GetFree(), *sysAdmin, login, *store, "Prepaid traffic");
1271 }
1272 //-----------------------------------------------------------------------------
1273 int UserImpl::AddMessage(Message * msg)
1274 {
1275 std::lock_guard lock(m_mutex);
1276
1277 if (SendMessage(*msg))
1278     {
1279     if (store->AddMessage(msg, login))
1280         {
1281         errorStr = store->GetStrError();
1282         WriteServLog("Error adding message: '%s'", errorStr.c_str());
1283         printfd(__FILE__, "Error adding message: '%s'\n", errorStr.c_str());
1284         return -1;
1285         }
1286     messages.push_back(*msg);
1287     }
1288 else
1289     {
1290     if (msg->header.repeat > 0)
1291         {
1292         msg->header.repeat--;
1293         #ifndef DEBUG
1294         //TODO: gcc v. 4.x generate ICE on x86_64
1295         msg->header.lastSendTime = static_cast<int>(time(NULL));
1296         #else
1297         msg->header.lastSendTime = static_cast<int>(stgTime);
1298         #endif
1299         if (store->AddMessage(msg, login))
1300             {
1301             errorStr = store->GetStrError();
1302             WriteServLog("Error adding repeatable message: '%s'", errorStr.c_str());
1303             printfd(__FILE__, "Error adding repeatable message: '%s'\n", errorStr.c_str());
1304             return -1;
1305             }
1306         messages.push_back(*msg);
1307         }
1308     }
1309 return 0;
1310 }
1311 //-----------------------------------------------------------------------------
1312 int UserImpl::SendMessage(Message & msg) const
1313 {
1314 // No lock `cause we are already locked from caller
1315 int ret = -1;
1316 std::set<const Auth*>::iterator it(authorizedBy.begin());
1317 while (it != authorizedBy.end())
1318     {
1319     if (!(*it++)->SendMessage(msg, m_currIP))
1320         ret = 0;
1321     }
1322 if (!ret)
1323     {
1324 #ifndef DEBUG
1325     //TODO: gcc v. 4.x generate ICE on x86_64
1326     msg.header.lastSendTime = static_cast<int>(time(NULL));
1327 #else
1328     msg.header.lastSendTime = static_cast<int>(stgTime);
1329 #endif
1330     msg.header.repeat--;
1331     }
1332 return ret;
1333 }
1334 //-----------------------------------------------------------------------------
1335 void UserImpl::ScanMessage()
1336 {
1337 // No lock `cause we are already locked from caller
1338 // We need not check for the authorizedBy `cause it has already checked by caller
1339
1340 auto it = messages.begin();
1341 while (it != messages.end())
1342     {
1343     if (settings->GetMessageTimeout() > 0 &&
1344         difftime(stgTime, it->header.creationTime) > settings->GetMessageTimeout())
1345         {
1346         // Timeout exceeded
1347         if (store->DelMessage(it->header.id, login))
1348             {
1349             WriteServLog("Error deleting message: '%s'", store->GetStrError().c_str());
1350             printfd(__FILE__, "Error deleting message: '%s'\n", store->GetStrError().c_str());
1351             }
1352         messages.erase(it++);
1353         continue;
1354         }
1355     if (it->GetNextSendTime() <= stgTime)
1356         {
1357         if (SendMessage(*it))
1358             {
1359             // We need to check all messages in queue for timeout
1360             ++it;
1361             continue;
1362             }
1363         if (it->header.repeat < 0)
1364             {
1365             if (store->DelMessage(it->header.id, login))
1366                 {
1367                 WriteServLog("Error deleting message: '%s'", store->GetStrError().c_str());
1368                 printfd(__FILE__, "Error deleting message: '%s'\n", store->GetStrError().c_str());
1369                 }
1370             messages.erase(it++);
1371             }
1372         else
1373             {
1374             if (store->EditMessage(*it, login))
1375                 {
1376                 WriteServLog("Error modifying message: '%s'", store->GetStrError().c_str());
1377                 printfd(__FILE__, "Error modifying message: '%s'\n", store->GetStrError().c_str());
1378                 }
1379             ++it;
1380             }
1381         }
1382     else
1383         {
1384         ++it;
1385         }
1386     }
1387 }
1388 //-----------------------------------------------------------------------------
1389 std::string UserImpl::GetParamValue(const std::string & name) const
1390 {
1391     std::string lowerName = ToLower(name);
1392     if (lowerName == "id")
1393         {
1394         std::ostringstream stream;
1395         stream << id;
1396         return stream.str();
1397         }
1398     if (lowerName == "login")       return login;
1399     if (lowerName == "currip")      return m_currIP.ToString();
1400     if (lowerName == "enableddirs") return GetEnabledDirs();
1401     if (lowerName == "tariff")      return properties.tariffName;
1402     if (properties.Exists(lowerName))
1403         return properties.GetPropertyValue(lowerName);
1404     else
1405         {
1406         WriteServLog("User’s parameter '%s' does not exist.", name.c_str());
1407         return "";
1408         }
1409 }
1410 //-----------------------------------------------------------------------------
1411 //-----------------------------------------------------------------------------
1412 //-----------------------------------------------------------------------------
1413 void UserImpl::onPassiveChange(int oldVal, int newVal)
1414 {
1415     std::lock_guard lock(m_mutex);
1416     if (newVal && !oldVal && tariff != NULL)
1417         properties.cash.Set(cash - tariff->GetPassiveCost(),
1418                             *sysAdmin,
1419                             login,
1420                             *store,
1421                             "Freeze");
1422 }
1423 //-----------------------------------------------------------------------------
1424 void UserImpl::onDisabledChange(int oldVal, int newVal)
1425 {
1426     std::lock_guard lock(m_mutex);
1427     if (oldVal && !newVal && GetConnected())
1428         Disconnect(false, "disabled");
1429     else if (!oldVal && newVal && IsInetable())
1430         Connect(false);
1431 }
1432 //-----------------------------------------------------------------------------
1433 void UserImpl::onTariffChange(const std::string& /*oldVal*/, const std::string& newVal)
1434 {
1435     std::lock_guard lock(m_mutex);
1436     if (settings->GetReconnectOnTariffChange() && m_connected)
1437         Disconnect(false, "Change tariff");
1438     tariff = tariffs->FindByName(newVal);
1439     if (settings->GetReconnectOnTariffChange() &&
1440         !authorizedBy.empty() &&
1441         IsInetable())
1442     {
1443         // This notifier gets called *before* changing the tariff, and in Connect we want to see new tariff name.
1444         properties.Conf().tariffName = newVal;
1445         Connect(false);
1446     }
1447 }
1448 //-----------------------------------------------------------------------------
1449 void UserImpl::onCashChange(double oldVal, double newVal)
1450 {
1451     time_t now = stgTime;
1452     lastCashAddTime = now;
1453     lastCashAdd = newVal - oldVal;
1454 }
1455 //-----------------------------------------------------------------------------
1456 void UserImpl::onIPChange(const UserIPs& oldVal, const UserIPs& newVal)
1457 {
1458     std::lock_guard lock(m_mutex);
1459     printfd(__FILE__, "Change IP from '%s' to '%s'\n", oldVal.toString().c_str(), newVal.toString().c_str());
1460     if (m_connected)
1461         Disconnect(false, "Change IP");
1462     if (!authorizedBy.empty() && IsInetable())
1463         Connect(false);
1464 }