]> 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 = localtime(&tt);
339
340 USER_STAT stat(property.GetStat());
341 if (store->SaveMonthStat(stat, t1->tm_mon, t1->tm_year, login))
342     {
343     WriteServLog("Cannot write month stat for user %s.", login.c_str());
344     WriteServLog("%s", store->GetStrError().c_str());
345     printfd(__FILE__, "Cannot write month stat for user %s.\n", login.c_str());
346     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
347     return -1;
348     }
349
350 return 0;
351 }
352 //-----------------------------------------------------------------------------
353 int USER::Authorize(uint32_t ip, const string &, uint32_t dirs, const BASE_AUTH * auth)
354 {
355 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
356 /*
357  *  Authorize user. It only means that user will be authorized. Nothing more.
358  *  User can be connected or disconnected while authorized.
359  *  Example: user is authorized but disconnected due to 0 money or blocking
360  */
361
362 /*
363  * Prevent double authorization by identical authorizers
364  */
365 if (authorizedBy.find(auth) != authorizedBy.end())
366     return 0;
367
368 if (!ip)
369     return -1;
370
371 for (int i = 0; i < DIR_NUM; i++)
372     {
373     enabledDirs[i] = dirs & (1 << i);
374     }
375
376 if (authorizedBy.size())
377     {
378     if (currIP != ip)
379         {
380         //  We are already authorized, but with different IP address
381         errorStr = "User " + login + " alredy authorized with IP address " + inet_ntostring(ip);
382         return -1;
383         }
384
385     map<uint32_t, user_iter>::const_iterator ci = ipIndex->find(ip);
386     if (ci != ipIndex->end())
387         {
388         //  Address is already present in IP-index
389         //  If it's not our IP - throw an error
390         if (&(*ci->second) != this)
391             {
392             errorStr = "IP address " + inet_ntostring(ip) + " alredy in use";
393             return -1;
394             }
395         }
396     }
397 else
398     {
399     if (ipIndex->find(ip) != ipIndex->end())
400         {
401         //  Address is already present in IP-index
402         errorStr = "IP address " + inet_ntostring(ip) + " alredy in use";
403         return -1;
404         }
405
406     if (ips.ConstData().IsIPInIPS(ip))
407         {
408         currIP = ip;
409         lastIPForDisconnect = currIP;
410         }
411     else
412         {
413         printfd(__FILE__, " user %s: ips = %s\n", login.c_str(), ips.ConstData().GetIpStr().c_str());
414         errorStr = "IP address " + inet_ntostring(ip) + " not belong user " + login;
415         return -1;
416         }
417     }
418
419 authorizedBy.insert(auth);
420
421 ScanMessage();
422
423 return 0;
424 }
425 //-----------------------------------------------------------------------------
426 void USER::Unauthorize(const BASE_AUTH * auth)
427 {
428 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
429 /*
430  *  Authorizer tries to unauthorize user, that was not authorized by it
431  */
432 if (!authorizedBy.erase(auth))
433     return;
434
435 if (authorizedBy.empty())
436     {
437     lastIPForDisconnect = currIP;
438     currIP = 0; // DelUser in traffcounter
439     return;
440     }
441 }
442 //-----------------------------------------------------------------------------
443 bool USER::IsAuthorizedBy(const BASE_AUTH * auth) const
444 {
445 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
446 //  Is this user authorized by specified authorizer?
447 return authorizedBy.find(auth) != authorizedBy.end();
448 }
449 //-----------------------------------------------------------------------------
450 void USER::Connect(bool fakeConnect)
451 {
452 /*
453  *  Connect user to Internet. This function is differ from Authorize() !!!
454  */
455
456 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
457
458 if (!fakeConnect)
459     {
460     string scriptOnConnect = settings->GetScriptDir() + "/OnConnect";
461
462     if (access(scriptOnConnect.c_str(), X_OK) == 0)
463         {
464         char dirsStr[DIR_NUM + 1];
465         dirsStr[DIR_NUM] = 0;
466         for (int i = 0; i < DIR_NUM; i++)
467             {
468             dirsStr[i] = enabledDirs[i] ? '1' : '0';
469             }
470
471         string scriptOnConnectParams;
472         strprintf(&scriptOnConnectParams,
473                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
474                 scriptOnConnect.c_str(),
475                 login.c_str(),
476                 inet_ntostring(currIP).c_str(),
477                 (double)cash,
478                 id,
479                 dirsStr);
480
481         ScriptExec(scriptOnConnectParams);
482         }
483     else
484         {
485         WriteServLog("Script %s cannot be executed. File not found.", scriptOnConnect.c_str());
486         }
487
488     connected = true;
489     }
490
491 if (store->WriteUserConnect(login, currIP))
492     {
493     WriteServLog("Cannot write connect for user %s.", login.c_str());
494     WriteServLog("%s", store->GetStrError().c_str());
495     }
496
497 if (!fakeConnect)
498     lastIPForDisconnect = currIP;
499 }
500 //-----------------------------------------------------------------------------
501 void USER::Disconnect(bool fakeDisconnect, const std::string & reason)
502 {
503 /*
504  *  Disconnect user from Internet. This function is differ from UnAuthorize() !!!
505  */
506
507 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
508
509 if (!lastIPForDisconnect)
510     {
511     printfd(__FILE__, "lastIPForDisconnect\n");
512     return;
513     }
514
515 if (!fakeDisconnect)
516     {
517     string scriptOnDisonnect = settings->GetScriptDir() + "/OnDisconnect";
518
519     if (access(scriptOnDisonnect.c_str(), X_OK) == 0)
520         {
521         char dirsStr[DIR_NUM + 1];
522         dirsStr[DIR_NUM] = 0;
523         for (int i = 0; i < DIR_NUM; i++)
524             {
525             dirsStr[i] = enabledDirs[i] ? '1' : '0';
526             }
527
528         string scriptOnDisonnectParams;
529         strprintf(&scriptOnDisonnectParams,
530                 "%s \"%s\" \"%s\" \"%f\" \"%d\" \"%s\"",
531                 scriptOnDisonnect.c_str(),
532                 login.c_str(),
533                 inet_ntostring(lastIPForDisconnect).c_str(),
534                 (double)cash,
535                 id,
536                 dirsStr);
537
538         ScriptExec(scriptOnDisonnectParams);
539         }
540     else
541         {
542         WriteServLog("Script OnDisconnect cannot be executed. File not found.");
543         }
544
545     connected = false;
546     }
547
548 if (store->WriteUserDisconnect(login, up, down, sessionUpload, sessionDownload, cash, freeMb, reason))
549     {
550     WriteServLog("Cannot write disconnect for user %s.", login.c_str());
551     WriteServLog("%s", store->GetStrError().c_str());
552     }
553
554 if (!fakeDisconnect)
555     lastIPForDisconnect = 0;
556
557 DIR_TRAFF zeroSesssion;
558
559 sessionUpload = zeroSesssion;
560 sessionDownload = zeroSesssion;
561 }
562 //-----------------------------------------------------------------------------
563 void USER::PrintUser() const
564 {
565 //return;
566 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
567 cout << "============================================================" << endl;
568 cout << "id=" << id << endl;
569 cout << "login=" << login << endl;
570 cout << "password=" << password << endl;
571 cout << "passive=" << passive << endl;
572 cout << "disabled=" << disabled << endl;
573 cout << "disabledDetailStat=" << disabledDetailStat << endl;
574 cout << "alwaysOnline=" << alwaysOnline << endl;
575 cout << "tariffName=" << tariffName << endl;
576 cout << "address=" << address << endl;
577 cout << "phone=" << phone << endl;
578 cout << "email=" << email << endl;
579 cout << "note=" << note << endl;
580 cout << "realName=" <<realName << endl;
581 cout << "group=" << group << endl;
582 cout << "credit=" << credit << endl;
583 cout << "nextTariff=" << nextTariff << endl;
584 cout << "userdata0" << userdata0 << endl;
585 cout << "userdata1" << userdata1 << endl;
586 cout << "creditExpire=" << creditExpire << endl;
587 cout << "ips=" << ips << endl;
588 cout << "------------------------" << endl;
589 cout << "up=" << up << endl;
590 cout << "down=" << down << endl;
591 cout << "cash=" << cash << endl;
592 cout << "freeMb=" << freeMb << endl;
593 cout << "lastCashAdd=" << lastCashAdd << endl;
594 cout << "lastCashAddTime=" << lastCashAddTime << endl;
595 cout << "passiveTime=" << passiveTime << endl;
596 cout << "lastActivityTime=" << lastActivityTime << endl;
597 cout << "============================================================" << endl;
598 }
599 //-----------------------------------------------------------------------------
600 void USER::Run()
601 {
602 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
603
604 if (stgTime > static_cast<time_t>(lastWriteStat + settings->GetStatWritePeriod()))
605     {
606     printfd(__FILE__, "USER::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 (connected)
628         {
629         lastActivityTime = *const_cast<time_t *>(&stgTime);
630         }
631     if (!connected && IsInetable())
632         {
633         Connect();
634         }
635     if (connected && !IsInetable())
636         {
637         if (disabled)
638             Disconnect(false, "disabled");
639         else if (passive)
640             Disconnect(false, "passive");
641         else
642             Disconnect(false, "no cash");
643         }
644
645     if (stgTime - lastScanMessages > 10)
646         {
647         ScanMessage();
648         lastScanMessages = stgTime;
649         }
650     }
651 else
652     {
653     if (connected)
654         {
655         Disconnect(false, "not authorized");
656         }
657     }
658
659 }
660 //-----------------------------------------------------------------------------
661 void USER::UpdatePingTime(time_t t)
662 {
663 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
664 //printfd(__FILE__, "UpdatePingTime(%d) %s\n", t, login.c_str());
665 if (t)
666     pingTime = t;
667 else
668     pingTime = stgTime;
669 }
670 //-----------------------------------------------------------------------------
671 bool USER::IsInetable()
672 {
673 //STG_LOCKER lock(&mutex, __FILE__, __LINE__);
674
675 if (disabled || passive)
676     return false;
677
678 if (settings->GetFreeMbAllowInet())
679     {
680     if (freeMb >= 0)
681         return true;
682     }
683
684 if (settings->GetShowFeeInCash())
685     {
686     return (cash >= -credit);
687     }
688
689 return (cash - tariff->GetFee() >= -credit);
690 }
691 //-----------------------------------------------------------------------------
692 string USER::GetEnabledDirs()
693 {
694 //STG_LOCKER lock(&mutex, __FILE__, __LINE__);
695
696 string dirs = "";
697 for(int i = 0; i < DIR_NUM; i++)
698     dirs += enabledDirs[i] ? "1" : "0";
699 return dirs;
700 }
701 //-----------------------------------------------------------------------------
702 #ifdef TRAFF_STAT_WITH_PORTS
703 void USER::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
704 #else
705 void USER::AddTraffStatU(int dir, uint32_t ip, uint32_t len)
706 #endif
707 {
708 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
709
710 if (!connected)
711     return;
712
713 double cost = 0;
714 DIR_TRAFF dt(up);
715
716 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
717 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
718
719 dt[dir] += len;
720
721 int tt = tariff->GetTraffType();
722 if (tt == TRAFF_UP ||
723     tt == TRAFF_UP_DOWN ||
724     // Check NEW traff data
725     (tt == TRAFF_MAX && dt[dir] > down.ConstData()[dir]))
726     {
727     double dc = 0;
728     if (traff < threshold &&
729         traff + len >= threshold)
730         {
731         // cash = partBeforeThreshold * priceBeforeThreshold +
732         //        partAfterThreshold * priceAfterThreshold
733         int64_t before = threshold - traff; // Chunk part before threshold
734         int64_t after = len - before; // Chunk part after threshold
735         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir], // Traff before chunk
736                                            down.ConstData()[dir],
737                                            dir,
738                                            stgTime) * before +
739              tariff->GetPriceWithTraffType(dt[dir], // Traff after chunk
740                                            down.ConstData()[dir],
741                                            dir,
742                                            stgTime) * after;
743         }
744     else
745         {
746         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
747                                            down.ConstData()[dir],
748                                            dir,
749                                            stgTime) * len;
750         }
751
752     if (freeMb.ConstData() <= 0) // FreeMb is exhausted
753         cost = dc;
754     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
755         cost = dc - freeMb.ConstData();
756
757     // Direct access to internal data structures via friend-specifier
758     property.stat.freeMb -= dc;
759     property.stat.cash -= cost;
760     cash.ModifyTime();
761     freeMb.ModifyTime();
762     }
763
764 up = dt;
765 sessionUpload[dir] += len;
766
767 //Add detailed stat
768
769 if (!settings->GetWriteFreeMbTraffCost() &&
770      freeMb.ConstData() >= 0)
771     cost = 0;
772
773 #ifdef TRAFF_STAT_WITH_PORTS
774 IP_DIR_PAIR idp(ip, dir, port);
775 #else
776 IP_DIR_PAIR idp(ip, dir);
777 #endif
778
779 map<IP_DIR_PAIR, STAT_NODE>::iterator lb;
780 lb = traffStat.lower_bound(idp);
781 if (lb == traffStat.end() || lb->first != idp)
782     {
783     traffStat.insert(lb,
784                      pair<IP_DIR_PAIR, STAT_NODE>(idp,
785                                                   STAT_NODE(len, 0, cost)));
786     }
787 else
788     {
789     lb->second.cash += cost;
790     lb->second.up += len;
791     }
792 }
793 //-----------------------------------------------------------------------------
794 #ifdef TRAFF_STAT_WITH_PORTS
795 void USER::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
796 #else
797 void USER::AddTraffStatD(int dir, uint32_t ip, uint32_t len)
798 #endif
799 {
800 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
801
802 if (!connected)
803     return;
804
805 double cost = 0;
806 DIR_TRAFF dt(down);
807
808 int64_t traff = tariff->GetTraffByType(up.ConstData()[dir], down.ConstData()[dir]);
809 int64_t threshold = tariff->GetThreshold(dir) * 1024 * 1024;
810
811 dt[dir] += len;
812
813 int tt = tariff->GetTraffType();
814 if (tt == TRAFF_DOWN ||
815     tt == TRAFF_UP_DOWN ||
816     // Check NEW traff data
817     (tt == TRAFF_MAX && up.ConstData()[dir] <= dt[dir]))
818     {
819     double dc = 0;
820     if (traff < threshold &&
821         traff + len >= threshold)
822         {
823         // cash = partBeforeThreshold * priceBeforeThreshold +
824         //        partAfterThreshold * priceAfterThreshold
825         int64_t before = threshold - traff; // Chunk part before threshold
826         int64_t after = len - before; // Chunk part after threshold
827         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
828                                            down.ConstData()[dir], // Traff before chunk
829                                            dir,
830                                            stgTime) * before +
831              tariff->GetPriceWithTraffType(up.ConstData()[dir],
832                                            dt[dir], // Traff after chunk
833                                            dir,
834                                            stgTime) * after;
835         }
836     else
837         {
838         dc = tariff->GetPriceWithTraffType(up.ConstData()[dir],
839                                            down.ConstData()[dir],
840                                            dir,
841                                            stgTime) * len;
842         }
843
844     if (freeMb.ConstData() <= 0) // FreeMb is exhausted
845         cost = dc;
846     else if (freeMb.ConstData() < dc) // FreeMb is partially exhausted
847         cost = dc - freeMb.ConstData();
848
849     property.stat.freeMb -= dc;
850     property.stat.cash -= cost;
851     cash.ModifyTime();
852     freeMb.ModifyTime();
853     }
854
855 down = dt;
856 sessionDownload[dir] += len;
857
858 //Add detailed stat
859
860 if (!settings->GetWriteFreeMbTraffCost() &&
861      freeMb.ConstData() >= 0)
862     cost = 0;
863
864 #ifdef TRAFF_STAT_WITH_PORTS
865 IP_DIR_PAIR idp(ip, dir, port);
866 #else
867 IP_DIR_PAIR idp(ip, dir);
868 #endif
869
870 map<IP_DIR_PAIR, STAT_NODE>::iterator lb;
871 lb = traffStat.lower_bound(idp);
872 if (lb == traffStat.end() || lb->first != idp)
873     {
874     traffStat.insert(lb,
875                      pair<IP_DIR_PAIR, STAT_NODE>(idp,
876                                                   STAT_NODE(0, len, cost)));
877     }
878 else
879     {
880     lb->second.cash += cost;
881     lb->second.down += len;
882     }
883 }
884 //-----------------------------------------------------------------------------
885 void USER::AddCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
886 {
887 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
888 currIP.AddBeforeNotifier(n);
889 }
890 //-----------------------------------------------------------------------------
891 void USER::DelCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
892 {
893 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
894 currIP.DelBeforeNotifier(n);
895 }
896 //-----------------------------------------------------------------------------
897 void USER::AddCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
898 {
899 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
900 currIP.AddAfterNotifier(n);
901 }
902 //-----------------------------------------------------------------------------
903 void USER::DelCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
904 {
905 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
906 currIP.DelAfterNotifier(n);
907 }
908 //-----------------------------------------------------------------------------
909 void USER::OnAdd()
910 {
911 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
912
913 string scriptOnAdd = settings->GetScriptDir() + "/OnUserAdd";
914
915 if (access(scriptOnAdd.c_str(), X_OK) == 0)
916     {
917     string scriptOnAddParams;
918     strprintf(&scriptOnAddParams,
919             "%s \"%s\"",
920             scriptOnAdd.c_str(),
921             login.c_str());
922
923     ScriptExec(scriptOnAddParams);
924     }
925 else
926     {
927     WriteServLog("Script %s cannot be executed. File not found.", scriptOnAdd.c_str());
928     }
929 }
930 //-----------------------------------------------------------------------------
931 void USER::OnDelete()
932 {
933 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
934
935 string scriptOnDel = settings->GetScriptDir() + "/OnUserDel";
936
937 if (access(scriptOnDel.c_str(), X_OK) == 0)
938     {
939     string scriptOnDelParams;
940     strprintf(&scriptOnDelParams,
941             "%s \"%s\"",
942             scriptOnDel.c_str(),
943             login.c_str());
944
945     ScriptExec(scriptOnDelParams);
946     }
947 else
948     {
949     WriteServLog("Script %s cannot be executed. File not found.", scriptOnDel.c_str());
950     }
951
952 Run();
953 }
954 //-----------------------------------------------------------------------------
955 int USER::WriteDetailStat(bool hard)
956 {
957 printfd(__FILE__, "USER::WriteDetailedStat() - saved size = %d\n", traffStatSaved.second.size());
958
959 if (!traffStatSaved.second.empty())
960     {
961     if (store->WriteDetailedStat(traffStatSaved.second, traffStatSaved.first, login))
962         {
963         printfd(__FILE__, "USER::WriteDetailStat() - failed to write detail stat from queue\n");
964         WriteServLog("Cannot write detail stat from queue (of size %d recs) for user %s.", traffStatSaved.second.size(), login.c_str());
965         WriteServLog("%s", store->GetStrError().c_str());
966         return -1;
967         }
968     traffStatSaved.second.erase(traffStatSaved.second.begin(), traffStatSaved.second.end());
969     }
970
971 TRAFF_STAT ts;
972
973     {
974     STG_LOCKER lock(&mutex, __FILE__, __LINE__);
975     ts.swap(traffStat);
976     }
977
978 printfd(__FILE__, "USER::WriteDetailedStat() - size = %d\n", ts.size());
979
980 if (ts.size() && !disabledDetailStat)
981     {
982     if (store->WriteDetailedStat(ts, lastWriteDeatiledStat, login))
983         {
984         printfd(__FILE__, "USER::WriteDetailStat() - failed to write current detail stat\n");
985         WriteServLog("Cannot write detail stat for user %s.", login.c_str());
986         WriteServLog("%s", store->GetStrError().c_str());
987         if (!hard)
988             {
989             printfd(__FILE__, "USER::WriteDetailStat() - pushing detail stat to queue\n");
990             STG_LOCKER lock(&mutex, __FILE__, __LINE__);
991             traffStatSaved.second.swap(ts);
992             traffStatSaved.first = lastWriteDeatiledStat;
993             }
994         return -1;
995         }
996     }
997 lastWriteDeatiledStat = stgTime;
998 return 0;
999 }
1000 //-----------------------------------------------------------------------------
1001 double USER::GetPassiveTimePart() const
1002 {
1003 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1004
1005 static int daysInMonth[12] =
1006 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1007
1008 struct tm * tms;
1009 time_t t = stgTime;
1010 tms = localtime(&t);
1011
1012 time_t secMonth = daysInMonth[(tms->tm_mon + 11) % 12] * 24 * 3600; // Previous month
1013
1014 if (tms->tm_year % 4 == 0 && tms->tm_mon == 1)
1015     {
1016     // Leap year
1017     secMonth += 24 * 3600;
1018     }
1019
1020 int dt = secMonth - passiveTime;
1021
1022 if (dt < 0)
1023     dt = 0;
1024
1025 return double(dt) / (secMonth);
1026 }
1027 //-----------------------------------------------------------------------------
1028 void USER::SetPassiveTimeAsNewUser()
1029 {
1030 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1031
1032 time_t t;
1033 struct tm * tm;
1034 t = stgTime;
1035 tm = localtime(&t);
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 //-----------------------------------------------------------------------------