]> git.stg.codes - stg.git/blob - projects/stargazer/user_impl.cpp
34c922b2a8aaf8b318f1629d23e64081001f54e2
[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 "stg/users.h"
42 #include "stg/common.h"
43 #include "stg/scriptexecuter.h"
44 #include "stg/tariff.h"
45 #include "stg/tariffs.h"
46 #include "stg/admin.h"
47 #include "user_impl.h"
48 #include "settings_impl.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 lastWriteDetailedStat = stgTime;
120
121 property.tariffName.AddBeforeNotifier(&tariffNotifier);
122 property.passive.AddBeforeNotifier(&passiveNotifier);
123 property.cash.AddBeforeNotifier(&cashNotifier);
124 ips.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 lastWriteDetailedStat = u.lastWriteDetailedStat;
198
199 settings = u.settings;
200
201 property.tariffName.AddBeforeNotifier(&tariffNotifier);
202 property.passive.AddBeforeNotifier(&passiveNotifier);
203 property.cash.AddBeforeNotifier(&cashNotifier);
204 ips.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 if (store->SaveUserStat(stat, login))
320     {
321     WriteServLog("Cannot write stat for user %s.", login.c_str());
322     WriteServLog("%s", store->GetStrError().c_str());
323     printfd(__FILE__, "Cannot write stat for user %s.\n", login.c_str());
324     printfd(__FILE__, "%s\n", store->GetStrError().c_str());
325     return -1;
326     }
327
328 lastWriteStat = stgTime;
329
330 return 0;
331 }
332 //-----------------------------------------------------------------------------
333 int USER_IMPL::WriteMonthStat()
334 {
335 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
336 time_t tt = stgTime - 3600;
337 struct tm t1;
338 localtime_r(&tt, &t1);
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_IMPL::Authorize(uint32_t ip, uint32_t dirs, const 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 + " already authorized with IP address " + inet_ntostring(ip);
382         return -1;
383         }
384
385     USER * u = NULL;
386     if (!users->FindByIPIdx(ip, &u))
387         {
388         //  Address is already present in IP-index
389         //  If it's not our IP - throw an error
390         if (u != this)
391             {
392             errorStr = "IP address " + inet_ntostring(ip) + " already in use";
393             return -1;
394             }
395         }
396     }
397 else
398     {
399     if (users->IsIPInIndex(ip))
400         {
401         //  Address is already present in IP-index
402         errorStr = "IP address " + inet_ntostring(ip) + " already 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_IMPL::Unauthorize(const 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_IMPL::IsAuthorizedBy(const 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_IMPL::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->GetScriptsDir() + "/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_IMPL::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->GetScriptsDir() + "/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_IMPL::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_IMPL::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         property.Stat().lastActivityTime = 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_IMPL::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_IMPL::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_IMPL::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_IMPL::AddTraffStatU(int dir, uint32_t ip, uint16_t port, uint32_t len)
704 #else
705 void USER_IMPL::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_IMPL::AddTraffStatD(int dir, uint32_t ip, uint16_t port, uint32_t len)
796 #else
797 void USER_IMPL::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_IMPL::AddCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
886 {
887 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
888 currIP.AddBeforeNotifier(n);
889 }
890 //-----------------------------------------------------------------------------
891 void USER_IMPL::DelCurrIPBeforeNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
892 {
893 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
894 currIP.DelBeforeNotifier(n);
895 }
896 //-----------------------------------------------------------------------------
897 void USER_IMPL::AddCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
898 {
899 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
900 currIP.AddAfterNotifier(n);
901 }
902 //-----------------------------------------------------------------------------
903 void USER_IMPL::DelCurrIPAfterNotifier(PROPERTY_NOTIFIER_BASE<uint32_t> * n)
904 {
905 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
906 currIP.DelAfterNotifier(n);
907 }
908 //-----------------------------------------------------------------------------
909 void USER_IMPL::AddConnectedBeforeNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
910 {
911 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
912 connected.AddBeforeNotifier(n);
913 }
914 //-----------------------------------------------------------------------------
915 void USER_IMPL::DelConnectedBeforeNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
916 {
917 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
918 connected.DelBeforeNotifier(n);
919 }
920 //-----------------------------------------------------------------------------
921 void USER_IMPL::AddConnectedAfterNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
922 {
923 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
924 connected.AddAfterNotifier(n);
925 }
926 //-----------------------------------------------------------------------------
927 void USER_IMPL::DelConnectedAfterNotifier(PROPERTY_NOTIFIER_BASE<bool> * n)
928 {
929 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
930 connected.DelAfterNotifier(n);
931 }
932 //-----------------------------------------------------------------------------
933 void USER_IMPL::OnAdd()
934 {
935 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
936
937 string scriptOnAdd = settings->GetScriptsDir() + "/OnUserAdd";
938
939 if (access(scriptOnAdd.c_str(), X_OK) == 0)
940     {
941     string scriptOnAddParams;
942     strprintf(&scriptOnAddParams,
943             "%s \"%s\"",
944             scriptOnAdd.c_str(),
945             login.c_str());
946
947     ScriptExec(scriptOnAddParams);
948     }
949 else
950     {
951     WriteServLog("Script %s cannot be executed. File not found.", scriptOnAdd.c_str());
952     }
953 }
954 //-----------------------------------------------------------------------------
955 void USER_IMPL::OnDelete()
956 {
957 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
958
959 string scriptOnDel = settings->GetScriptsDir() + "/OnUserDel";
960
961 if (access(scriptOnDel.c_str(), X_OK) == 0)
962     {
963     string scriptOnDelParams;
964     strprintf(&scriptOnDelParams,
965             "%s \"%s\"",
966             scriptOnDel.c_str(),
967             login.c_str());
968
969     ScriptExec(scriptOnDelParams);
970     }
971 else
972     {
973     WriteServLog("Script %s cannot be executed. File not found.", scriptOnDel.c_str());
974     }
975
976 Run();
977 }
978 //-----------------------------------------------------------------------------
979 int USER_IMPL::WriteDetailStat(bool hard)
980 {
981 printfd(__FILE__, "USER::WriteDetailedStat() - saved size = %d\n", traffStatSaved.second.size());
982
983 if (!traffStatSaved.second.empty())
984     {
985     if (store->WriteDetailedStat(traffStatSaved.second, traffStatSaved.first, login))
986         {
987         printfd(__FILE__, "USER::WriteDetailStat() - failed to write detail stat from queue\n");
988         WriteServLog("Cannot write detail stat from queue (of size %d recs) for user %s.", traffStatSaved.second.size(), login.c_str());
989         WriteServLog("%s", store->GetStrError().c_str());
990         return -1;
991         }
992     traffStatSaved.second.erase(traffStatSaved.second.begin(), traffStatSaved.second.end());
993     }
994
995 TRAFF_STAT ts;
996
997     {
998     STG_LOCKER lock(&mutex, __FILE__, __LINE__);
999     ts.swap(traffStat);
1000     }
1001
1002 printfd(__FILE__, "USER::WriteDetailedStat() - size = %d\n", ts.size());
1003
1004 if (ts.size() && !disabledDetailStat)
1005     {
1006     if (store->WriteDetailedStat(ts, lastWriteDetailedStat, login))
1007         {
1008         printfd(__FILE__, "USER::WriteDetailStat() - failed to write current detail stat\n");
1009         WriteServLog("Cannot write detail stat for user %s.", login.c_str());
1010         WriteServLog("%s", store->GetStrError().c_str());
1011         if (!hard)
1012             {
1013             printfd(__FILE__, "USER::WriteDetailStat() - pushing detail stat to queue\n");
1014             STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1015             traffStatSaved.second.swap(ts);
1016             traffStatSaved.first = lastWriteDetailedStat;
1017             }
1018         return -1;
1019         }
1020     }
1021 lastWriteDetailedStat = stgTime;
1022 return 0;
1023 }
1024 //-----------------------------------------------------------------------------
1025 double USER_IMPL::GetPassiveTimePart() const
1026 {
1027 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1028
1029 static int daysInMonth[12] =
1030 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1031
1032 struct tm tms;
1033 time_t t = stgTime;
1034 localtime_r(&t, &tms);
1035
1036 time_t secMonth = daysInMonth[(tms.tm_mon + 11) % 12] * 24 * 3600; // Previous month
1037
1038 if (tms.tm_year % 4 == 0 && tms.tm_mon == 1)
1039     {
1040     // Leap year
1041     secMonth += 24 * 3600;
1042     }
1043
1044 int dt = secMonth - passiveTime;
1045
1046 if (dt < 0)
1047     dt = 0;
1048
1049 return double(dt) / (secMonth);
1050 }
1051 //-----------------------------------------------------------------------------
1052 void USER_IMPL::SetPassiveTimeAsNewUser()
1053 {
1054 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1055
1056 time_t t = stgTime;
1057 struct tm tm;
1058 localtime_r(&t, &tm);
1059 int daysCurrMon = DaysInCurrentMonth();
1060 double pt = (tm.tm_mday - 1) / (double)daysCurrMon;
1061
1062 passiveTime = (time_t)(pt * 24 * 3600 * daysCurrMon);
1063 }
1064 //-----------------------------------------------------------------------------
1065 void USER_IMPL::MidnightResetSessionStat()
1066 {
1067 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1068
1069 if (connected)
1070     {
1071     Disconnect(true, "fake");
1072     Connect(true);
1073     }
1074 }
1075 //-----------------------------------------------------------------------------
1076 void USER_IMPL::ProcessNewMonth()
1077 {
1078 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1079 //  Reset traff
1080 if (connected)
1081     {
1082     Disconnect(true, "fake");
1083     }
1084 DIR_TRAFF zeroTarff;
1085
1086 WriteMonthStat();
1087
1088 up = zeroTarff;
1089 down = zeroTarff;
1090
1091 if (connected)
1092     {
1093     Connect(true);
1094     }
1095
1096 //  Set new tariff
1097 if (nextTariff.ConstData() != "")
1098     {
1099     const TARIFF * nt;
1100     nt = tariffs->FindByName(nextTariff);
1101     if (nt == NULL)
1102         {
1103         WriteServLog("Cannot change tariff for user %s. Tariff %s not exist.",
1104                      login.c_str(), property.tariffName.Get().c_str());
1105         }
1106     else
1107         {
1108         property.tariffName.Set(nextTariff, sysAdmin, login, store);
1109         tariff = nt;
1110         }
1111     ResetNextTariff();
1112     WriteConf();
1113     }
1114 }
1115 //-----------------------------------------------------------------------------
1116 void USER_IMPL::ProcessDayFeeSpread()
1117 {
1118 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1119
1120 if (passive.ConstData())
1121     return;
1122
1123 double f = tariff->GetFee() / DaysInCurrentMonth();
1124
1125 if (f == 0.0)
1126     return;
1127
1128 double c = cash;
1129 property.cash.Set(c - f, sysAdmin, login, store, "Subscriber fee charge");
1130 ResetPassiveTime();
1131 }
1132 //-----------------------------------------------------------------------------
1133 void USER_IMPL::ProcessDayFee()
1134 {
1135 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1136
1137 double passiveTimePart = 1.0;
1138 if (!settings->GetFullFee())
1139     {
1140     passiveTimePart = GetPassiveTimePart();
1141     }
1142 else
1143     {
1144     if (passive.ConstData())
1145         {
1146         printfd(__FILE__, "Don't charge fee `cause we are passive\n");
1147         return;
1148         }
1149     }
1150 double f = tariff->GetFee() * passiveTimePart;
1151
1152 ResetPassiveTime();
1153
1154 if (f == 0.0)
1155     return;
1156
1157 double c = cash;
1158 printfd(__FILE__, "login: %8s   Fee=%f PassiveTimePart=%f fee=%f\n",
1159         login.c_str(),
1160         tariff->GetFee(),
1161         passiveTimePart,
1162         f);
1163 property.cash.Set(c - f, sysAdmin, login, store, "Subscriber fee charge");
1164 }
1165 //-----------------------------------------------------------------------------
1166 void USER_IMPL::SetPrepaidTraff()
1167 {
1168 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1169
1170 property.freeMb.Set(tariff->GetFree(), sysAdmin, login, store, "Prepaid traffic");
1171 }
1172 //-----------------------------------------------------------------------------
1173 int USER_IMPL::AddMessage(STG_MSG * msg)
1174 {
1175 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
1176
1177 if (SendMessage(*msg))
1178     {
1179     if (store->AddMessage(msg, login))
1180         {
1181         errorStr = store->GetStrError();
1182         WriteServLog("Error adding message: '%s'", errorStr.c_str());
1183         printfd(__FILE__, "Error adding message: '%s'\n", errorStr.c_str());
1184         return -1;
1185         }
1186     messages.push_back(*msg);
1187     }
1188 else
1189     {
1190     if (msg->header.repeat > 0)
1191         {
1192         msg->header.repeat--;
1193         #ifndef DEBUG
1194         //TODO: gcc v. 4.x generate ICE on x86_64
1195         msg->header.lastSendTime = time(NULL);
1196         #else
1197         msg->header.lastSendTime = stgTime;
1198         #endif
1199         if (store->AddMessage(msg, login))
1200             {
1201             errorStr = store->GetStrError();
1202             WriteServLog("Error adding repeatable message: '%s'", errorStr.c_str());
1203             printfd(__FILE__, "Error adding repeatable message: '%s'\n", errorStr.c_str());
1204             return -1;
1205             }
1206         messages.push_back(*msg);
1207         }
1208     }
1209 return 0;
1210 }
1211 //-----------------------------------------------------------------------------
1212 int USER_IMPL::SendMessage(STG_MSG & msg) const
1213 {
1214 // No lock `cause we are already locked from caller
1215 int ret = -1;
1216 set<const AUTH*>::iterator it(authorizedBy.begin());
1217 while (it != authorizedBy.end())
1218     {
1219     if (!(*it++)->SendMessage(msg, currIP))
1220         ret = 0;
1221     }
1222 if (!ret)
1223     {
1224 #ifndef DEBUG
1225     //TODO: gcc v. 4.x generate ICE on x86_64
1226     msg.header.lastSendTime = time(NULL);
1227 #else
1228     msg.header.lastSendTime = stgTime;
1229 #endif
1230     msg.header.repeat--;
1231     }
1232 return ret;
1233 }
1234 //-----------------------------------------------------------------------------
1235 void USER_IMPL::ScanMessage()
1236 {
1237 // No lock `cause we are already locked from caller
1238 // We need not check for the authorizedBy `cause it has already checked by caller
1239
1240 std::list<STG_MSG>::iterator it(messages.begin());
1241 while (it != messages.end())
1242     {
1243     if (settings->GetMessageTimeout() > 0 &&
1244         difftime(stgTime, it->header.creationTime) > settings->GetMessageTimeout())
1245         {
1246         // Timeout exceeded
1247         if (store->DelMessage(it->header.id, login))
1248             {
1249             WriteServLog("Error deleting message: '%s'", store->GetStrError().c_str());
1250             printfd(__FILE__, "Error deleting message: '%s'\n", store->GetStrError().c_str());
1251             }
1252         messages.erase(it++);
1253         continue;
1254         }
1255     if (it->GetNextSendTime() <= stgTime)
1256         {
1257         if (SendMessage(*it))
1258             {
1259             // We need to check all messages in queue for timeout
1260             ++it;
1261             continue;
1262             }
1263         if (it->header.repeat < 0)
1264             {
1265             if (store->DelMessage(it->header.id, login))
1266                 {
1267                 WriteServLog("Error deleting message: '%s'", store->GetStrError().c_str());
1268                 printfd(__FILE__, "Error deleting message: '%s'\n", store->GetStrError().c_str());
1269                 }
1270             messages.erase(it++);
1271             }
1272         else
1273             {
1274             if (store->EditMessage(*it, login))
1275                 {
1276                 WriteServLog("Error modifying message: '%s'", store->GetStrError().c_str());
1277                 printfd(__FILE__, "Error modifying message: '%s'\n", store->GetStrError().c_str());
1278                 }
1279             ++it;
1280             }
1281         }
1282     else
1283         {
1284         ++it;
1285         }
1286     }
1287 }
1288 //-----------------------------------------------------------------------------
1289 //-----------------------------------------------------------------------------
1290 //-----------------------------------------------------------------------------
1291 void CHG_PASSIVE_NOTIFIER::Notify(const int & oldPassive, const int & newPassive)
1292 {
1293 if (newPassive && !oldPassive)
1294     user->property.cash.Set(user->cash - user->tariff->GetPassiveCost(),
1295                             user->sysAdmin,
1296                             user->login,
1297                             user->store,
1298                             "Freeze");
1299 }
1300 //-----------------------------------------------------------------------------
1301 void CHG_TARIFF_NOTIFIER::Notify(const string &, const string & newTariff)
1302 {
1303 user->tariff = user->tariffs->FindByName(newTariff);
1304 }
1305 //-----------------------------------------------------------------------------
1306 void CHG_CASH_NOTIFIER::Notify(const double & oldCash, const double & newCash)
1307 {
1308 user->lastCashAddTime = *const_cast<time_t *>(&stgTime);
1309 user->lastCashAdd = newCash - oldCash;
1310 }
1311 //-----------------------------------------------------------------------------
1312 void CHG_IPS_NOTIFIER::Notify(const USER_IPS & from, const USER_IPS & to)
1313 {
1314     printfd(__FILE__, "Change IP from '%s' to '%s'\n", from.GetIpStr().c_str(), to.GetIpStr().c_str());
1315     if (user->connected)
1316         user->Disconnect(true, "Change IP");
1317     if (user->IsInetable())
1318         user->Connect(true);
1319 }
1320 //-----------------------------------------------------------------------------