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