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