]> git.stg.codes - stg.git/blob - projects/stargazer/user_impl.cpp
ae77da4fb454f5e94616082eeee7922114a6b1be
[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 <pthread.h>
36 #include <unistd.h> // access
37
38 #include <cassert>
39 #include <cstdlib>
40
41 #include "user_impl.h"
42 #include "users.h"
43 #include "common.h"
44 #include "settings_impl.h"
45 #include "script_executer.h"
46 #include "tariff.h"
47 #include "tariffs.h"
48 #include "admin.h"
49
50 USER_IMPL::USER_IMPL(const SETTINGS_IMPL * s,
51            const STORE * st,
52            const TARIFFS * t,
53            const ADMIN * a,
54            const USERS * u)
55     : users(u),
56       property(s->GetScriptsDir()),
57       WriteServLog(GetStgLogger()),
58       login(),
59       id(0),
60       __connected(0),
61       connected(__connected),
62       userIDGenerator(),
63       __currIP(0),
64       currIP(__currIP),
65       lastIPForDisconnect(0),
66       pingTime(0),
67       sysAdmin(a),
68       store(st),
69       tariffs(t),
70       tariff(tariffs->GetNoTariff()),
71       cash(property.cash),
72       up(property.up),
73       down(property.down),
74       lastCashAdd(property.lastCashAdd),
75       passiveTime(property.passiveTime),
76       lastCashAddTime(property.lastCashAddTime),
77       freeMb(property.freeMb),
78       lastActivityTime(property.lastActivityTime),
79       password(property.password),
80       passive(property.passive),
81       disabled(property.disabled),
82       disabledDetailStat(property.disabledDetailStat),
83       alwaysOnline(property.alwaysOnline),
84       tariffName(property.tariffName),
85       nextTariff(property.nextTariff),
86       address(property.address),
87       note(property.note),
88       group(property.group),
89       email(property.email),
90       phone(property.phone),
91       realName(property.realName),
92       credit(property.credit),
93       creditExpire(property.creditExpire),
94       ips(property.ips),
95       userdata0(property.userdata0),
96       userdata1(property.userdata1),
97       userdata2(property.userdata2),
98       userdata3(property.userdata3),
99       userdata4(property.userdata4),
100       userdata5(property.userdata5),
101       userdata6(property.userdata6),
102       userdata7(property.userdata7),
103       userdata8(property.userdata8),
104       userdata9(property.userdata9),
105       passiveNotifier(this),
106       tariffNotifier(this),
107       cashNotifier(this),
108       ipNotifier(this)
109 {
110 settings = s;
111
112 password = "*_EMPTY_PASSWORD_*";
113 tariffName = NO_TARIFF_NAME;
114 connected = 0;
115 tariff = tariffs->GetNoTariff();
116 ips = StrToIPS("*");
117 deleted = false;
118 lastWriteStat = stgTime + random() % settings->GetStatWritePeriod();
119 lastWriteDeatiledStat = stgTime;
120
121 property.tariffName.AddBeforeNotifier(&tariffNotifier);
122 property.passive.AddBeforeNotifier(&passiveNotifier);
123 property.cash.AddBeforeNotifier(&cashNotifier);
124 currIP.AddAfterNotifier(&ipNotifier);
125
126 lastScanMessages = 0;
127
128 pthread_mutexattr_t attr;
129 pthread_mutexattr_init(&attr);
130 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
131 pthread_mutex_init(&mutex, &attr);
132 }
133 //-----------------------------------------------------------------------------
134 USER_IMPL::USER_IMPL(const USER_IMPL & u)
135     : users(u.users),
136       property(u.settings->GetScriptsDir()),
137       WriteServLog(GetStgLogger()),
138       login(u.login),
139       id(u.id),
140       __connected(u.__connected),
141       connected(__connected),
142       __currIP(u.__currIP),
143       currIP(__currIP),
144       lastIPForDisconnect(0),
145       pingTime(u.pingTime),
146       sysAdmin(u.sysAdmin),
147       store(u.store),
148       tariffs(u.tariffs),
149       tariff(u.tariff),
150       cash(property.cash),
151       up(property.up),
152       down(property.down),
153       lastCashAdd(property.lastCashAdd),
154       passiveTime(property.passiveTime),
155       lastCashAddTime(property.lastCashAddTime),
156       freeMb(property.freeMb),
157       lastActivityTime(property.lastActivityTime),
158       password(property.password),
159       passive(property.passive),
160       disabled(property.disabled),
161       disabledDetailStat(property.disabledDetailStat),
162       alwaysOnline(property.alwaysOnline),
163       tariffName(property.tariffName),
164       nextTariff(property.nextTariff),
165       address(property.address),
166       note(property.note),
167       group(property.group),
168       email(property.email),
169       phone(property.phone),
170       realName(property.realName),
171       credit(property.credit),
172       creditExpire(property.creditExpire),
173       ips(property.ips),
174       userdata0(property.userdata0),
175       userdata1(property.userdata1),
176       userdata2(property.userdata2),
177       userdata3(property.userdata3),
178       userdata4(property.userdata4),
179       userdata5(property.userdata5),
180       userdata6(property.userdata6),
181       userdata7(property.userdata7),
182       userdata8(property.userdata8),
183       userdata9(property.userdata9),
184       passiveNotifier(this),
185       tariffNotifier(this),
186       cashNotifier(this),
187       ipNotifier(this)
188 {
189 if (&u == this)
190     return;
191
192 connected = 0;
193
194 deleted = u.deleted;
195
196 lastWriteStat = u.lastWriteStat;
197 lastWriteDeatiledStat = u.lastWriteDeatiledStat;
198
199 settings = u.settings;
200
201 property.tariffName.AddBeforeNotifier(&tariffNotifier);
202 property.passive.AddBeforeNotifier(&passiveNotifier);
203 property.cash.AddBeforeNotifier(&cashNotifier);
204 currIP.AddAfterNotifier(&ipNotifier);
205
206 lastScanMessages = 0;
207
208 property.SetProperties(u.property);
209
210 pthread_mutexattr_t attr;
211 pthread_mutexattr_init(&attr);
212 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
213 pthread_mutex_init(&mutex, &attr);
214 }
215 //-----------------------------------------------------------------------------
216 USER_IMPL::~USER_IMPL()
217 {
218 property.passive.DelBeforeNotifier(&passiveNotifier);
219 property.tariffName.DelBeforeNotifier(&tariffNotifier);
220 pthread_mutex_destroy(&mutex);
221 }
222 //-----------------------------------------------------------------------------
223 void USER_IMPL::SetLogin(string const & l)
224 {
225 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
226 assert(login.empty() && "Login is already set");
227 login = l;
228 id = userIDGenerator.GetNextID();
229 }
230 //-----------------------------------------------------------------------------
231 int USER_IMPL::ReadConf()
232 {
233 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
234 USER_CONF conf;
235
236 if (store->RestoreUserConf(&conf, login))
237     {
238     WriteServLog("Cannot read conf for user %s.", login.c_str());
239     WriteServLog("%s", store->GetStrError().c_str());
240     printfd(__FILE__, "Cannot read conf for user %s.\n", login.c_str());
241     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
242     return -1;
243     }
244
245 property.SetConf(conf);
246
247 tariff = tariffs->FindByName(tariffName);
248 if (tariff == NULL)
249     {
250     WriteServLog("Cannot read user %s. Tariff %s not exist.",
251                  login.c_str(), property.tariffName.Get().c_str());
252     return -1;
253     }
254
255 std::vector<STG_MSG_HDR> hdrsList;
256
257 if (store->GetMessageHdrs(&hdrsList, login))
258     {
259     printfd(__FILE__, "Error GetMessageHdrs %s\n", store->GetStrError().c_str());
260     return -1;
261     }
262
263 std::vector<STG_MSG_HDR>::const_iterator it;
264 for (it = hdrsList.begin(); it != hdrsList.end(); ++it)
265     {
266     STG_MSG msg;
267     if (store->GetMessage(it->id, &msg, login) == 0)
268         {
269         messages.push_back(msg);
270         }
271     }
272
273 return 0;
274 }
275 //-----------------------------------------------------------------------------
276 int USER_IMPL::ReadStat()
277 {
278 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
279 USER_STAT stat;
280
281 if (store->RestoreUserStat(&stat, login))
282     {
283     WriteServLog("Cannot read stat for user %s.", login.c_str());
284     WriteServLog("%s", store->GetStrError().c_str());
285     printfd(__FILE__, "Cannot read stat for user %s.\n", login.c_str());
286     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
287     return -1;
288     }
289
290 property.SetStat(stat);
291
292 return 0;
293 }
294 //-----------------------------------------------------------------------------
295 int USER_IMPL::WriteConf()
296 {
297 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
298 USER_CONF conf(property.GetConf());
299
300 printfd(__FILE__, "USER::WriteConf()\n");
301
302 if (store->SaveUserConf(conf, login))
303     {
304     WriteServLog("Cannot write conf for user %s.", login.c_str());
305     WriteServLog("%s", store->GetStrError().c_str());
306     printfd(__FILE__, "Cannot write conf for user %s.\n", login.c_str());
307     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
308     return -1;
309     }
310
311 return 0;
312 }
313 //-----------------------------------------------------------------------------
314 int USER_IMPL::WriteStat()
315 {
316 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
317 USER_STAT stat(property.GetStat());
318
319 printfd(__FILE__, "USER::WriteStat()\n");
320
321 if (store->SaveUserStat(stat, login))
322     {
323     WriteServLog("Cannot write stat for user %s.", login.c_str());
324     WriteServLog("%s", store->GetStrError().c_str());
325     printfd(__FILE__, "Cannot write stat for user %s.\n", login.c_str());
326     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
327     return -1;
328     }
329
330 lastWriteStat = stgTime;
331
332 return 0;
333 }
334 //-----------------------------------------------------------------------------
335 int USER_IMPL::WriteMonthStat()
336 {
337 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
338 time_t tt = stgTime - 3600;
339 struct tm t1;
340 localtime_r(&tt, &t1);
341
342 USER_STAT stat(property.GetStat());
343 if (store->SaveMonthStat(stat, t1.tm_mon, t1.tm_year, login))
344     {
345     WriteServLog("Cannot write month stat for user %s.", login.c_str());
346     WriteServLog("%s", store->GetStrError().c_str());
347     printfd(__FILE__, "Cannot write month stat for user %s.\n", login.c_str());
348     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
349     return -1;
350     }
351
352 return 0;
353 }
354 //-----------------------------------------------------------------------------
355 int USER_IMPL::Authorize(uint32_t ip, uint32_t dirs, const AUTH * auth)
356 {
357 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
358 /*
359  *  Authorize user. It only means that user will be authorized. Nothing more.
360  *  User can be connected or disconnected while authorized.
361  *  Example: user is authorized but disconnected due to 0 money or blocking
362  */
363
364 /*
365  * Prevent double authorization by identical authorizers
366  */
367 if (authorizedBy.find(auth) != authorizedBy.end())
368     return 0;
369
370 if (!ip)
371     return -1;
372
373 for (int i = 0; i < DIR_NUM; i++)
374     {
375     enabledDirs[i] = dirs & (1 << i);
376     }
377
378 if (authorizedBy.size())
379     {
380     if (currIP != ip)
381         {
382         //  We are already authorized, but with different IP address
383         errorStr = "User " + login + " alredy authorized with IP address " + inet_ntostring(ip);
384         return -1;
385         }
386
387     USER * u = NULL;
388     if (!users->FindByIPIdx(ip, &u))
389         {
390         //  Address is already present in IP-index
391         //  If it's not our IP - throw an error
392         if (u != this)
393             {
394             errorStr = "IP address " + inet_ntostring(ip) + " alredy in use";
395             return -1;
396             }
397         }
398     }
399 else
400     {
401     if (users->IsIPInIndex(ip))
402         {
403         //  Address is already present in IP-index
404         errorStr = "IP address " + inet_ntostring(ip) + " alredy in use";
405         return -1;
406         }
407
408     if (ips.ConstData().IsIPInIPS(ip))
409         {
410         currIP = ip;
411         lastIPForDisconnect = currIP;
412         }
413     else
414         {
415         printfd(__FILE__, " user %s: ips = %s\n", login.c_str(), ips.ConstData().GetIpStr().c_str());
416         errorStr = "IP address " + inet_ntostring(ip) + " not belong user " + login;
417         return -1;
418         }
419     }
420
421 authorizedBy.insert(auth);
422
423 ScanMessage();
424
425 return 0;
426 }
427 //-----------------------------------------------------------------------------
428 void USER_IMPL::Unauthorize(const AUTH * auth)
429 {
430 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
431 /*
432  *  Authorizer tries to unauthorize user, that was not authorized by it
433  */
434 if (!authorizedBy.erase(auth))
435     return;
436
437 if (authorizedBy.empty())
438     {
439     lastIPForDisconnect = currIP;
440     currIP = 0; // DelUser in traffcounter
441     return;
442     }
443 }
444 //-----------------------------------------------------------------------------
445 bool USER_IMPL::IsAuthorizedBy(const AUTH * auth) const
446 {
447 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
448 //  Is this user authorized by specified authorizer?
449 return authorizedBy.find(auth) != authorizedBy.end();
450 }
451 //-----------------------------------------------------------------------------
452 void USER_IMPL::Connect(bool fakeConnect)
453 {
454 /*
455  *  Connect user to Internet. This function is differ from Authorize() !!!
456  */
457
458 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
459
460 if (!fakeConnect)
461     {
462     string scriptOnConnect = settings->GetScriptsDir() + "/OnConnect";
463
464     if (access(scriptOnConnect.c_str(), X_OK) == 0)
465         {
466         char dirsStr[DIR_NUM + 1];
467         dirsStr[DIR_NUM] = 0;
468         for (int i = 0; i < DIR_NUM; i++)
469             {
470             dirsStr[i] = enabledDirs[i] ? '1' : '0';
471             }
472
473         string scriptOnConnectParams;
474         strprintf(&scriptOnConnectParams,
475                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
476                 scriptOnConnect.c_str(),
477                 login.c_str(),
478                 inet_ntostring(currIP).c_str(),
479                 (double)cash,
480                 id,
481                 dirsStr);
482
483         ScriptExec(scriptOnConnectParams);
484         }
485     else
486         {
487         WriteServLog("Script %s cannot be executed. File not found.", scriptOnConnect.c_str());
488         }
489
490     connected = true;
491     }
492
493 if (store->WriteUserConnect(login, currIP))
494     {
495     WriteServLog("Cannot write connect for user %s.", login.c_str());
496     WriteServLog("%s", store->GetStrError().c_str());
497     }
498
499 if (!fakeConnect)
500     lastIPForDisconnect = currIP;
501 }
502 //-----------------------------------------------------------------------------
503 void USER_IMPL::Disconnect(bool fakeDisconnect, const std::string & reason)
504 {
505 /*
506  *  Disconnect user from Internet. This function is differ from UnAuthorize() !!!
507  */
508
509 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
510
511 if (!lastIPForDisconnect)
512     {
513     printfd(__FILE__, "lastIPForDisconnect\n");
514     return;
515     }
516
517 if (!fakeDisconnect)
518     {
519     string scriptOnDisonnect = settings->GetScriptsDir() + "/OnDisconnect";
520
521     if (access(scriptOnDisonnect.c_str(), X_OK) == 0)
522         {
523         char dirsStr[DIR_NUM + 1];
524         dirsStr[DIR_NUM] = 0;
525         for (int i = 0; i < DIR_NUM; i++)
526             {
527             dirsStr[i] = enabledDirs[i] ? '1' : '0';
528             }
529
530         string scriptOnDisonnectParams;
531         strprintf(&scriptOnDisonnectParams,
532                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
533                 scriptOnDisonnect.c_str(),
534                 login.c_str(),
535                 inet_ntostring(lastIPForDisconnect).c_str(),
536                 (double)cash,
537                 id,
538                 dirsStr);
539
540         ScriptExec(scriptOnDisonnectParams);
541         }
542     else
543         {
544         WriteServLog("Script OnDisconnect cannot be executed. File not found.");
545         }
546
547     connected = false;
548     }
549
550 if (store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload, cash, freeMb, reason))
551     {
552     WriteServLog("Cannot write disconnect for user %s.", login.c_str());
553     WriteServLog("%s", store->GetStrError().c_str());
554     }
555
556 if (!fakeDisconnect)
557     lastIPForDisconnect = 0;
558
559 DIR_TRAFF zeroSesssion;
560
561 sessionUpload = zeroSesssion;
562 sessionDownload = zeroSesssion;
563 }
564 //-----------------------------------------------------------------------------
565 void USER_IMPL::PrintUser() const
566 {
567 //return;
568 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
569 cout << "============================================================" << endl;
570 cout << "id=" << id << endl;
571 cout << "login=" << login << endl;
572 cout << "password=" << password << endl;
573 cout << "passive=" << passive << endl;
574 cout << "disabled=" << disabled << endl;
575 cout << "disabledDetailStat=" << disabledDetailStat << endl;
576 cout << "alwaysOnline=" << alwaysOnline << endl;
577 cout << "tariffName=" << tariffName << endl;
578 cout << "address=" << address << endl;
579 cout << "phone=" << phone << endl;
580 cout << "email=" << email << endl;
581 cout << "note=" << note << endl;
582 cout << "realName=" <<realName << endl;
583 cout << "group=" << group << endl;
584 cout << "credit=" << credit << endl;
585 cout << "nextTariff=" << nextTariff << endl;
586 cout << "userdata0" << userdata0 << endl;
587 cout << "userdata1" << userdata1 << endl;
588 cout << "creditExpire=" << creditExpire << endl;
589 cout << "ips=" << ips << endl;
590 cout << "------------------------" << endl;
591 cout << "up=" << up << endl;
592 cout << "down=" << down << endl;
593 cout << "cash=" << cash << endl;
594 cout << "freeMb=" << freeMb << endl;
595 cout << "lastCashAdd=" << lastCashAdd << endl;
596 cout << "lastCashAddTime=" << lastCashAddTime << endl;
597 cout << "passiveTime=" << passiveTime << endl;
598 cout << "lastActivityTime=" << lastActivityTime << endl;
599 cout << "============================================================" << endl;
600 }
601 //-----------------------------------------------------------------------------
602 void USER_IMPL::Run()
603 {
604 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
605
606 if (stgTime > static_cast<time_t>(lastWriteStat + settings->GetStatWritePeriod()))
607     {
608     printfd(__FILE__, "USER::WriteStat user=%s\n", GetLogin().c_str());
609     WriteStat();
610     }
611 if (creditExpire.ConstData() && creditExpire.ConstData() < stgTime)
612     {
613     WriteServLog("User: %s. Credit expired.", login.c_str());
614     credit = 0;
615     creditExpire = 0;
616     WriteConf();
617     }
618
619 if (passive.ConstData()
620     && (stgTime % 30 == 0)
621     && (passiveTime.ModificationTime() != stgTime))
622     {
623     passiveTime = passiveTime + (stgTime - passiveTime.ModificationTime());
624     printfd(__FILE__, "===== %s: passiveTime=%d =====\n", login.c_str(), passiveTime.ConstData());
625     }
626
627 if (!authorizedBy.empty())
628     {
629     if (connected)
630         {
631         lastActivityTime = *const_cast<time_t *>(&stgTime);
632         }
633     if (!connected && IsInetable())
634         {
635         Connect();
636         }
637     if (connected && !IsInetable())
638         {
639         if (disabled)
640             Disconnect(false, "disabled");
641         else if (passive)
642             Disconnect(false, "passive");
643         else
644             Disconnect(false, "no cash");
645         }
646
647     if (stgTime - lastScanMessages > 10)
648         {
649         ScanMessage();
650         lastScanMessages = stgTime;
651         }
652     }
653 else
654     {
655     if (connected)
656         {
657         Disconnect(false, "not authorized");
658         }
659     }
660
661 }
662 //-----------------------------------------------------------------------------
663 void USER_IMPL::UpdatePingTime(time_t t)
664 {
665 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
666 //printfd(__FILE__, "UpdatePingTime(%d) %s\n", t, login.c_str());
667 if (t)
668     pingTime = t;
669 else
670     pingTime = stgTime;
671 }
672 //-----------------------------------------------------------------------------
673 bool USER_IMPL::IsInetable()
674 {
675 //STG_LOCKER lock(&mutex, __FILE__, __LINE__);
676
677 if (disabled || passive)
678     return false;
679
680 if (settings->GetFreeMbAllowInet())
681     {
682     if (freeMb >= 0)
683         return true;
684     }
685
686 if (settings->GetShowFeeInCash())
687     {
688     return (cash >= -credit);
689     }
690
691 return (cash - tariff->GetFee() >= -credit);
692 }
693 //-----------------------------------------------------------------------------
694 string USER_IMPL::GetEnabledDirs()
695 {
696 //STG_LOCKER lock(&mutex, __FILE__, __LINE__);
697
698 string dirs = "";
699 for(int i = 0; i < DIR_NUM; i++)
700     dirs += enabledDirs[i] ? "1" : "0";
701 return dirs;
702 }
703 //-----------------------------------------------------------------------------
704 #ifdef TRAFF_STAT_WITH_PORTS
705 void USER_IMPL::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
706 #else
707 void USER_IMPL::AddTraffStatU(int dir, uint32_t ip, uint32_t len)
708 #endif
709 {
710 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
711
712 if (!connected)
713     return;
714
715 double cost = 0;
716 DIR_TRAFF dt(up);
717
718 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
719 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
720
721 dt[dir] += len;
722
723 int tt = tariff->GetTraffType();
724 if (tt == TRAFF_UP ||
725     tt == TRAFF_UP_DOWN ||
726     // Check NEW traff data
727     (tt == TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
728     {
729     double dc = 0;
730     if (traff < threshold &&
731         traff + len >= threshold)
732         {
733         // cash = partBeforeThreshold * priceBeforeThreshold +
734         //        partAfterThreshold * priceAfterThreshold
735         int64_t before = threshold - traff; // Chunk part before threshold
736         int64_t after = len - before; // Chunk part after threshold
737         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir], // Traff before chunk
738                                            down.ConstData()[dir],
739                                            dir,
740                                            stgTime) * before +
741              tariff->GetPriceWithTraffType(dt[dir], // Traff after chunk
742                                            down.ConstData()[dir],
743                                            dir,
744                                            stgTime) * after;
745         }
746     else
747         {
748         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
749                                            down.ConstData()[dir],
750                                            dir,
751                                            stgTime) * len;
752         }
753
754     if (freeMb.ConstData() <= 0) // FreeMb is exhausted
755         cost = dc;
756     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
757         cost = dc - freeMb.ConstData();
758
759     // Direct access to internal data structures via friend-specifier
760     property.Stat().freeMb -= dc;
761     property.Stat().cash -= cost;
762     cash.ModifyTime();
763     freeMb.ModifyTime();
764     }
765
766 up = dt;
767 sessionUpload[dir] += len;
768
769 //Add detailed stat
770
771 if (!settings->GetWriteFreeMbTraffCost() &&
772      freeMb.ConstData() >= 0)
773     cost = 0;
774
775 #ifdef TRAFF_STAT_WITH_PORTS
776 IP_DIR_PAIR idp(ip, dir, port);
777 #else
778 IP_DIR_PAIR idp(ip, dir);
779 #endif
780
781 map<IP_DIR_PAIR, STAT_NODE>::iterator lb;
782 lb = traffStat.lower_bound(idp);
783 if (lb == traffStat.end() || lb->first != idp)
784     {
785     traffStat.insert(lb,
786                      pair<IP_DIR_PAIR, STAT_NODE>(idp,
787                                                   STAT_NODE(len, 0, cost)));
788     }
789 else
790     {
791     lb->second.cash += cost;
792     lb->second.up += len;
793     }
794 }
795 //-----------------------------------------------------------------------------
796 #ifdef TRAFF_STAT_WITH_PORTS
797 void USER_IMPL::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
798 #else
799 void USER_IMPL::AddTraffStatD(int dir, uint32_t ip, uint32_t len)
800 #endif
801 {
802 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
803
804 if (!connected)
805     return;
806
807 double cost = 0;
808 DIR_TRAFF dt(down);
809
810 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
811 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
812
813 dt[dir] += len;
814
815 int tt = tariff->GetTraffType();
816 if (tt == TRAFF_DOWN ||
817     tt == TRAFF_UP_DOWN ||
818     // Check NEW traff data
819     (tt == TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
820     {
821     double dc = 0;
822     if (traff < threshold &&
823         traff + len >= threshold)
824         {
825         // cash = partBeforeThreshold * priceBeforeThreshold +
826         //        partAfterThreshold * priceAfterThreshold
827         int64_t before = threshold - traff; // Chunk part before threshold
828         int64_t after = len - before; // Chunk part after threshold
829         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
830                                            down.ConstData()[dir], // Traff before chunk
831                                            dir,
832                                            stgTime) * before +
833              tariff->GetPriceWithTraffType(up.ConstData()[dir],
834                                            dt[dir], // Traff after chunk
835                                            dir,
836                                            stgTime) * after;
837         }
838     else
839         {
840         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
841                                            down.ConstData()[dir],
842                                            dir,
843                                            stgTime) * len;
844         }
845
846     if (freeMb.ConstData() <= 0) // FreeMb is exhausted
847         cost = dc;
848     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
849         cost = dc - freeMb.ConstData();
850
851     property.Stat().freeMb -= dc;
852     property.Stat().cash -= cost;
853     cash.ModifyTime();
854     freeMb.ModifyTime();
855     }
856
857 down = dt;
858 sessionDownload[dir] += len;
859
860 //Add detailed stat
861
862 if (!settings->GetWriteFreeMbTraffCost() &&
863      freeMb.ConstData() >= 0)
864     cost = 0;
865
866 #ifdef TRAFF_STAT_WITH_PORTS
867 IP_DIR_PAIR idp(ip, dir, port);
868 #else
869 IP_DIR_PAIR idp(ip, dir);
870 #endif
871
872 map<IP_DIR_PAIR, STAT_NODE>::iterator lb;
873 lb = traffStat.lower_bound(idp);
874 if (lb == traffStat.end() || lb->first != idp)
875     {
876     traffStat.insert(lb,
877                      pair<IP_DIR_PAIR, STAT_NODE>(idp,
878                                                   STAT_NODE(0, len, cost)));
879     }
880 else
881     {
882     lb->second.cash += cost;
883     lb->second.down += len;
884     }
885 }
886 //-----------------------------------------------------------------------------
887 void USER_IMPL::AddCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
888 {
889 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
890 currIP.AddBeforeNotifier(n);
891 }
892 //-----------------------------------------------------------------------------
893 void USER_IMPL::DelCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
894 {
895 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
896 currIP.DelBeforeNotifier(n);
897 }
898 //-----------------------------------------------------------------------------
899 void USER_IMPL::AddCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
900 {
901 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
902 currIP.AddAfterNotifier(n);
903 }
904 //-----------------------------------------------------------------------------
905 void USER_IMPL::DelCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
906 {
907 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
908 currIP.DelAfterNotifier(n);
909 }
910 //-----------------------------------------------------------------------------
911 void USER_IMPL::OnAdd()
912 {
913 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
914
915 string scriptOnAdd = settings->GetScriptsDir() + "/OnUserAdd";
916
917 if (access(scriptOnAdd.c_str(), X_OK) == 0)
918     {
919     string scriptOnAddParams;
920     strprintf(&scriptOnAddParams,
921             "%s \"%s\"",
922             scriptOnAdd.c_str(),
923             login.c_str());
924
925     ScriptExec(scriptOnAddParams);
926     }
927 else
928     {
929     WriteServLog("Script %s cannot be executed. File not found.", scriptOnAdd.c_str());
930     }
931 }
932 //-----------------------------------------------------------------------------
933 void USER_IMPL::OnDelete()
934 {
935 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
936
937 string scriptOnDel = settings->GetScriptsDir() + "/OnUserDel";
938
939 if (access(scriptOnDel.c_str(), X_OK) == 0)
940     {
941     string scriptOnDelParams;
942     strprintf(&scriptOnDelParams,
943             "%s \"%s\"",
944             scriptOnDel.c_str(),
945             login.c_str());
946
947     ScriptExec(scriptOnDelParams);
948     }
949 else
950     {
951     WriteServLog("Script %s cannot be executed. File not found.", scriptOnDel.c_str());
952     }
953
954 Run();
955 }
956 //-----------------------------------------------------------------------------
957 int USER_IMPL::WriteDetailStat(bool hard)
958 {
959 printfd(__FILE__, "USER::WriteDetailedStat() - saved size = %d\n", traffStatSaved.second.size());
960
961 if (!traffStatSaved.second.empty())
962     {
963     if (store->WriteDetailedStat(traffStatSaved.second, traffStatSaved.first, login))
964         {
965         printfd(__FILE__, "USER::WriteDetailStat() - failed to write detail stat from queue\n");
966         WriteServLog("Cannot write detail stat from queue (of size %d recs) for user %s.", traffStatSaved.second.size(), login.c_str());
967         WriteServLog("%s", store->GetStrError().c_str());
968         return -1;
969         }
970     traffStatSaved.second.erase(traffStatSaved.second.begin(), traffStatSaved.second.end());
971     }
972
973 TRAFF_STAT ts;
974
975     {
976     STG_LOCKER lock(&mutex, __FILE__, __LINE__);
977     ts.swap(traffStat);
978     }
979
980 printfd(__FILE__, "USER::WriteDetailedStat() - size = %d\n", ts.size());
981
982 if (ts.size() && !disabledDetailStat)
983     {
984     if (store->WriteDetailedStat(ts, lastWriteDeatiledStat, login))
985         {
986         printfd(__FILE__, "USER::WriteDetailStat() - failed to write current detail stat\n");
987         WriteServLog("Cannot write detail stat for user %s.", login.c_str());
988         WriteServLog("%s", store->GetStrError().c_str());
989         if (!hard)
990             {
991             printfd(__FILE__, "USER::WriteDetailStat() - pushing detail stat to queue\n");
992             STG_LOCKER lock(&mutex, __FILE__, __LINE__);
993             traffStatSaved.second.swap(ts);
994             traffStatSaved.first = lastWriteDeatiledStat;
995             }
996         return -1;
997         }
998     }
999 lastWriteDeatiledStat = stgTime;
1000 return 0;
1001 }
1002 //-----------------------------------------------------------------------------
1003 double USER_IMPL::GetPassiveTimePart() const
1004 {
1005 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1006
1007 static int daysInMonth[12] =
1008 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1009
1010 struct tm tms;
1011 time_t t = stgTime;
1012 localtime_r(&t, &tms);
1013
1014 time_t secMonth = daysInMonth[(tms.tm_mon + 11) % 12] * 24 * 3600; // Previous month
1015
1016 if (tms.tm_year % 4 == 0 && tms.tm_mon == 1)
1017     {
1018     // Leap year
1019     secMonth += 24 * 3600;
1020     }
1021
1022 int dt = secMonth - passiveTime;
1023
1024 if (dt < 0)
1025     dt = 0;
1026
1027 return double(dt) / (secMonth);
1028 }
1029 //-----------------------------------------------------------------------------
1030 void USER_IMPL::SetPassiveTimeAsNewUser()
1031 {
1032 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1033
1034 time_t t = stgTime;
1035 struct tm tm;
1036 localtime_r(&t, &tm);
1037 int daysCurrMon = DaysInCurrentMonth();
1038 double pt = (tm.tm_mday - 1) / (double)daysCurrMon;
1039
1040 passiveTime = (time_t)(pt * 24 * 3600 * daysCurrMon);
1041 }
1042 //-----------------------------------------------------------------------------
1043 void USER_IMPL::MidnightResetSessionStat()
1044 {
1045 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1046
1047 if (connected)
1048     {
1049     Disconnect(true, "fake");
1050     Connect(true);
1051     }
1052 }
1053 //-----------------------------------------------------------------------------
1054 void USER_IMPL::ProcessNewMonth()
1055 {
1056 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1057 //  Reset traff
1058 if (connected)
1059     {
1060     Disconnect(true, "fake");
1061     }
1062 DIR_TRAFF zeroTarff;
1063
1064 WriteMonthStat();
1065
1066 up = zeroTarff;
1067 down = zeroTarff;
1068
1069 if (connected)
1070     {
1071     Connect(true);
1072     }
1073
1074 //  Set new tariff
1075 if (nextTariff.ConstData() != "")
1076     {
1077     const TARIFF * nt;
1078     nt = tariffs->FindByName(nextTariff);
1079     if (nt == NULL)
1080         {
1081         WriteServLog("Cannot change tariff for user %s. Tariff %s not exist.",
1082                      login.c_str(), property.tariffName.Get().c_str());
1083         }
1084     else
1085         {
1086         property.tariffName.Set(nextTariff, sysAdmin, login, store);
1087         tariff = nt;
1088         }
1089     ResetNextTariff();
1090     WriteConf();
1091     }
1092 }
1093 //-----------------------------------------------------------------------------
1094 void USER_IMPL::ProcessDayFeeSpread()
1095 {
1096 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1097
1098 if (passive.ConstData())
1099     return;
1100
1101 double f = tariff->GetFee() / DaysInCurrentMonth();
1102
1103 if (f == 0.0)
1104     return;
1105
1106 double c = cash;
1107 property.cash.Set(c - f, sysAdmin, login, store, "Subscriber fee charge");
1108 ResetPassiveTime();
1109 }
1110 //-----------------------------------------------------------------------------
1111 void USER_IMPL::ProcessDayFee()
1112 {
1113 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1114
1115 double passiveTimePart = 1.0;
1116 if (!settings->GetFullFee())
1117     {
1118     passiveTimePart = GetPassiveTimePart();
1119     }
1120 else
1121     {
1122     if (passive.ConstData())
1123         {
1124         printfd(__FILE__, "Don't charge fee `cause we are passive\n");
1125         return;
1126         }
1127     }
1128 double f = tariff->GetFee() * passiveTimePart;
1129
1130 ResetPassiveTime();
1131
1132 if (f == 0.0)
1133     return;
1134
1135 double c = cash;
1136 printfd(__FILE__, "login: %8s   Fee=%f PassiveTimePart=%f fee=%f\n",
1137         login.c_str(),
1138         tariff->GetFee(),
1139         passiveTimePart,
1140         f);
1141 property.cash.Set(c - f, sysAdmin, login, store, "Subscriber fee charge");
1142 }
1143 //-----------------------------------------------------------------------------
1144 void USER_IMPL::SetPrepaidTraff()
1145 {
1146 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1147
1148 property.freeMb.Set(tariff->GetFree(), sysAdmin, login, store, "Prepaid traffic");
1149 }
1150 //-----------------------------------------------------------------------------
1151 int USER_IMPL::AddMessage(STG_MSG * msg)
1152 {
1153 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1154
1155 if (SendMessage(*msg))
1156     {
1157     if (store->AddMessage(msg, login))
1158         {
1159         errorStr = store->GetStrError();
1160         WriteServLog("Error adding message: '%s'", errorStr.c_str());
1161         printfd(__FILE__, "Error adding message: '%s'\n", errorStr.c_str());
1162         return -1;
1163         }
1164     messages.push_back(*msg);
1165     }
1166 else
1167     {
1168     if (msg->header.repeat > 0)
1169         {
1170         msg->header.repeat--;
1171         #ifndef DEBUG
1172         //TODO: gcc v. 4.x generate ICE on x86_64
1173         msg->header.lastSendTime = time(NULL);
1174         #else
1175         msg->header.lastSendTime = stgTime;
1176         #endif
1177         if (store->AddMessage(msg, login))
1178             {
1179             errorStr = store->GetStrError();
1180             WriteServLog("Error adding repeatable message: '%s'", errorStr.c_str());
1181             printfd(__FILE__, "Error adding repeatable message: '%s'\n", errorStr.c_str());
1182             return -1;
1183             }
1184         messages.push_back(*msg);
1185         }
1186     }
1187 return 0;
1188 }
1189 //-----------------------------------------------------------------------------
1190 int USER_IMPL::SendMessage(STG_MSG & msg) const
1191 {
1192 // No lock `cause we are already locked from caller
1193 int ret = -1;
1194 set<const AUTH*>::iterator it(authorizedBy.begin());
1195 while (it != authorizedBy.end())
1196     {
1197     if (!(*it++)->SendMessage(msg, currIP))
1198         ret = 0;
1199     }
1200 if (!ret)
1201     {
1202 #ifndef DEBUG
1203     //TODO: gcc v. 4.x generate ICE on x86_64
1204     msg.header.lastSendTime = time(NULL);
1205 #else
1206     msg.header.lastSendTime = stgTime;
1207 #endif
1208     msg.header.repeat--;
1209     }
1210 return ret;
1211 }
1212 //-----------------------------------------------------------------------------
1213 void USER_IMPL::ScanMessage()
1214 {
1215 // No lock `cause we are already locked from caller
1216 // We need not check for the authorizedBy `cause it has already checked by caller
1217
1218 std::list<STG_MSG>::iterator it(messages.begin());
1219 while (it != messages.end())
1220     {
1221     if (settings->GetMessageTimeout() > 0 &&
1222         difftime(stgTime, it->header.creationTime) > settings->GetMessageTimeout())
1223         {
1224         // Timeout exceeded
1225         if (store->DelMessage(it->header.id, login))
1226             {
1227             WriteServLog("Error deleting message: '%s'", store->GetStrError().c_str());
1228             printfd(__FILE__, "Error deleting message: '%s'\n", store->GetStrError().c_str());
1229             }
1230         messages.erase(it++);
1231         continue;
1232         }
1233     if (it->GetNextSendTime() <= stgTime)
1234         {
1235         if (SendMessage(*it))
1236             {
1237             // We need to check all messages in queue for timeout
1238             ++it;
1239             continue;
1240             }
1241         if (it->header.repeat < 0)
1242             {
1243             if (store->DelMessage(it->header.id, login))
1244                 {
1245                 WriteServLog("Error deleting message: '%s'", store->GetStrError().c_str());
1246                 printfd(__FILE__, "Error deleting message: '%s'\n", store->GetStrError().c_str());
1247                 }
1248             messages.erase(it++);
1249             }
1250         else
1251             {
1252             if (store->EditMessage(*it, login))
1253                 {
1254                 WriteServLog("Error modifying message: '%s'", store->GetStrError().c_str());
1255                 printfd(__FILE__, "Error modifying message: '%s'\n", store->GetStrError().c_str());
1256                 }
1257             ++it;
1258             }
1259         }
1260     }
1261 }
1262 //-----------------------------------------------------------------------------
1263 //-----------------------------------------------------------------------------
1264 //-----------------------------------------------------------------------------
1265 void CHG_PASSIVE_NOTIFIER::Notify(const int & oldPassive, const int & newPassive)
1266 {
1267 if (newPassive && !oldPassive)
1268     user->property.cash.Set(user->cash - user->tariff->GetPassiveCost(),
1269                             user->sysAdmin,
1270                             user->login,
1271                             user->store,
1272                             "Freeze");
1273 }
1274 //-----------------------------------------------------------------------------
1275 void CHG_TARIFF_NOTIFIER::Notify(const string &, const string & newTariff)
1276 {
1277 user->tariff = user->tariffs->FindByName(newTariff);
1278 }
1279 //-----------------------------------------------------------------------------
1280 void CHG_CASH_NOTIFIER::Notify(const double & oldCash, const double & newCash)
1281 {
1282 user->lastCashAddTime = *const_cast<time_t *>(&stgTime);
1283 user->lastCashAdd = newCash - oldCash;
1284 }
1285 //-----------------------------------------------------------------------------
1286 void CHG_IP_NOTIFIER::Notify(const uint32_t & from, const uint32_t & to)
1287 {
1288     printfd(__FILE__, "Change IP from %s to %s\n", inet_ntostring(from).c_str(), inet_ntostring(to).c_str());
1289     if (from != 0)
1290         if (user->connected)
1291             user->Disconnect(false, "Change IP");
1292     if (to != 0)
1293         if (user->IsInetable())
1294             user->Connect(false);
1295 }
1296 //-----------------------------------------------------------------------------