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