1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19
20 */
21
22 #ifndef CLIENTONLY
23 #include "qwsvdef.h"
24
25 #ifdef WEBSITE_LOGIN_SUPPORT
26 #undef WEBSITE_LOGIN_SUPPORT
27 #endif
28 #if defined(SERVERONLY) && defined(WWW_INTEGRATION)
29 #define WEBSITE_LOGIN_SUPPORT
30 #include "central.h"
31 #endif
32
33 #define MAX_ACCOUNTS 1000
34 #define MAX_FAILURES 10
35 #define MAX_LOGINNAME (DIGEST_SIZE * 2 + 1)
36 #define ACC_FILE "accounts"
37 #define ACC_DIR "users"
38
39 cvar_t sv_login = { "sv_login", "0" }; // if enabled, login required
40 #ifdef WEBSITE_LOGIN_SUPPORT
41 cvar_t sv_login_web = { "sv_login_web", "1" }; // 0=local files, 1=auth via website (bans can be in local files), 2=mandatory auth (must have account in local files)
42 #define LoginModeFileBased() ((int)sv_login_web.value == 0)
43 #define LoginModeOptionalWeb() ((int)sv_login_web.value == 1)
44 #define LoginModeMandatoryWeb() ((int)sv_login_web.value == 2)
45 #define LoginMustHaveLocalAccount() (LoginModeMandatoryWeb() || LoginModeFileBased())
46 #define WebLoginsEnabled() (!LoginModeFileBased())
47 #else
48 #define LoginModeFileBased() (1)
49 #define LoginModeOptionalWeb() (0)
50 #define LoginModeMandatoryWeb() (0)
51 #define LoginMustHaveLocalAccount() (1)
52 #define WebLoginsEnabled() (0)
53 #endif
54
55 extern cvar_t sv_hashpasswords;
56 static void SV_SuccessfulLogin(client_t* cl);
57 static void SV_BlockedLogin(client_t* cl);
58 static void SV_ForceClientName(client_t* cl, const char* forced_name);
59
60 typedef enum { a_free, a_ok, a_blocked } acc_state_t;
61 typedef enum { use_log, use_ip } quse_t;
62
63 typedef struct
64 {
65 char login[MAX_LOGINNAME];
66 char pass[MAX_LOGINNAME];
67 int failures;
68 int inuse;
69 ipfilter_t address;
70 acc_state_t state;
71 quse_t use;
72 } account_t;
73
74 static account_t accounts[MAX_ACCOUNTS];
75 static int num_accounts = 0;
76
validAcc(char * acc)77 static qbool validAcc(char* acc)
78 {
79 char* s = acc;
80
81 for (; *acc; acc++)
82 {
83 if (*acc < 'a' || *acc > 'z')
84 if (*acc < 'A' || *acc > 'Z')
85 if (*acc < '0' || *acc > '9')
86 if (*acc != '.' && *acc != '_')
87 return false;
88 }
89
90 return acc - s <= MAX_LOGINNAME && acc - s >= 3;
91 }
92
93 /*
94 =================
95 WriteAccounts
96
97 Writes account list to disk
98 =================
99 */
100
WriteAccounts(void)101 static void WriteAccounts(void)
102 {
103 int c;
104 FILE* f;
105 account_t* acc;
106
107 //Sys_mkdir(ACC_DIR);
108 if ((f = fopen(va("%s/" ACC_FILE, fs_gamedir), "wt")) == NULL)
109 {
110 Con_Printf("Warning: couldn't open for writing " ACC_FILE "\n");
111 return;
112 }
113
114 for (acc = accounts, c = 0; c < num_accounts; acc++)
115 {
116 if (acc->state == a_free)
117 continue;
118
119 if (acc->use == use_log)
120 fprintf(f, "%s %s %d %d\n", acc->login, acc->pass, acc->state, acc->failures);
121 else
122 fprintf(f, "%s %s %d\n", acc->login, acc->pass, acc->state);
123
124 c++;
125 }
126
127 fclose(f);
128
129 // force cache rebuild.
130 FS_FlushFSHash();
131 }
132
133 /*
134 =================
135 SV_LoadAccounts
136
137 loads account list from disk
138 =================
139 */
140 qbool StringToFilter(char* s, ipfilter_t* f);
SV_LoadAccounts(void)141 void SV_LoadAccounts(void)
142 {
143 int i;
144 FILE* f;
145 account_t* acc = accounts;
146 client_t* cl;
147
148 if ((f = fopen(va("%s/" ACC_FILE, fs_gamedir), "rt")) == NULL)
149 {
150 Con_DPrintf("couldn't open " ACC_FILE "\n");
151 // logout
152 num_accounts = 0;
153 for (cl = svs.clients; cl - svs.clients < MAX_CLIENTS; cl++)
154 {
155 if (cl->logged > 0) {
156 cl->logged = 0;
157 }
158 if (!cl->logged_in_via_web) {
159 cl->login[0] = 0;
160 }
161 }
162 return;
163 }
164
165 while (!feof(f))
166 {
167 memset(acc, 0, sizeof(account_t));
168 // Is realy safe to use 'fscanf(f, "%s", s)'? FIXME!
169 if (fscanf(f, "%s", acc->login) != 1) {
170 Con_Printf("Error reading account data\n");
171 break;
172 }
173 if (StringToFilter(acc->login, &acc->address))
174 {
175 strlcpy(acc->pass, acc->login, MAX_LOGINNAME);
176 acc->use = use_ip;
177 if (fscanf(f, "%s %d\n", acc->pass, (int*)&acc->state) != 2) {
178 Con_Printf("Error reading account data\n");
179 break;
180 }
181 }
182 else {
183 if (fscanf(f, "%s %d %d\n", acc->pass, (int*)&acc->state, &acc->failures) != 3) {
184 Con_Printf("Error reading account data\n");
185 break;
186 }
187 }
188
189 if (acc->state != a_free) // lol?
190 acc++;
191 }
192
193 num_accounts = acc - accounts;
194
195 fclose(f);
196
197 // for every connected client check if their login is still valid
198 for (cl = svs.clients; cl - svs.clients < MAX_CLIENTS; cl++)
199 {
200 if (cl->state < cs_connected)
201 continue;
202
203 if (cl->logged <= 0)
204 continue;
205
206 if (cl->logged_in_via_web)
207 continue;
208
209 for (i = 0, acc = accounts; i < num_accounts; i++, acc++)
210 if ((acc->use == use_log && !strncmp(acc->login, cl->login, CLIENT_LOGIN_LEN))
211 || (acc->use == use_ip && !strcmp(acc->login, va("%d.%d.%d.%d", cl->realip.ip[0], cl->realip.ip[1], cl->realip.ip[2], cl->realip.ip[3]))))
212 break;
213
214 if (i < num_accounts && acc->state == a_ok)
215 {
216 // login again if possible
217 if (!acc->inuse || acc->use == use_ip)
218 {
219 cl->logged = i + 1;
220 if (acc->use == use_ip)
221 strlcpy(cl->login, acc->pass, CLIENT_LOGIN_LEN);
222
223 acc->inuse++;
224 continue;
225 }
226 }
227 // login is not valid anymore, logout
228 cl->logged = 0;
229 cl->login[0] = 0;
230 }
231 }
232
233 /*
234 =================
235 SV_CreateAccount_f
236
237 acc_create <login> [<password>]
238 if password is not given, login will be used for password
239 login/pass has to be max 16 chars and at least 3, only regular chars are acceptable
240 =================
241 */
SV_CreateAccount_f(void)242 void SV_CreateAccount_f(void)
243 {
244 int i, spot, c;
245 ipfilter_t adr;
246 quse_t use;
247
248 if (Cmd_Argc() < 2)
249 {
250 Con_Printf("usage: acc_create <login> [<password>]\n acc_create <address> <username>\nmaximum %d characters for login/pass\n", MAX_LOGINNAME - 1); //bliP: address typo
251 return;
252 }
253
254 if (num_accounts == MAX_ACCOUNTS)
255 {
256 Con_Printf("MAX_ACCOUNTS reached\n");
257 return;
258 }
259
260 if (StringToFilter(Cmd_Argv(1), &adr))
261 {
262 use = use_ip;
263 if (Cmd_Argc() < 3)
264 {
265 Con_Printf("usage: acc_create <address> <username>\nmaximum %d characters for username\n", MAX_LOGINNAME - 1); //bliP; address typo
266 return;
267 }
268 }
269 else
270 {
271 use = use_log;
272
273 // validate user login/pass
274 if (!validAcc(Cmd_Argv(1)))
275 {
276 Con_Printf("Invalid login!\n");
277 return;
278 }
279
280 if (Cmd_Argc() == 4 && !validAcc(Cmd_Argv(2)))
281 {
282 Con_Printf("Invalid pass!\n");
283 return;
284 }
285 }
286
287 // find free spot, check if login exist;
288 for (i = 0, c = 0, spot = -1; c < num_accounts; i++)
289 {
290 if (accounts[i].state == a_free)
291 {
292 if (spot == -1) spot = i;
293 continue;
294 }
295
296 if (!strcasecmp(accounts[i].login, Cmd_Argv(1)) ||
297 (use == use_ip && !strcasecmp(accounts[i].login, Cmd_Argv(2))))
298 break;
299
300 c++;
301 }
302
303 if (c < num_accounts)
304 {
305 Con_Printf("Login already in use\n");
306 return;
307 }
308
309 if (spot == -1)
310 spot = i;
311
312 // create an account
313 num_accounts++;
314 strlcpy(accounts[spot].login, Cmd_Argv(1), MAX_LOGINNAME);
315 if (Cmd_Argc() == 3)
316 i = 2;
317 else
318 i = 1;
319 strlcpy(accounts[spot].pass, (int)sv_hashpasswords.value && use == use_log ?
320 SHA1(Cmd_Argv(i)) : Cmd_Argv(i), MAX_LOGINNAME);
321
322 accounts[spot].state = a_ok;
323 accounts[spot].use = use;
324
325 Con_Printf("login %s created\n", Cmd_Argv(1));
326 WriteAccounts();
327 }
328
329 /*
330 =================
331 SV_RemoveAccount_f
332
333 acc_remove <login>
334 removes the login
335 =================
336 */
SV_RemoveAccount_f(void)337 void SV_RemoveAccount_f(void)
338 {
339 int i, c, j;
340
341 if (Cmd_Argc() < 2)
342 {
343 Con_Printf("usage: acc_remove <login>\n");
344 return;
345 }
346
347 for (i = 0, c = 0; c < num_accounts; i++)
348 {
349 if (accounts[i].state == a_free)
350 continue;
351
352 if (!strcasecmp(accounts[i].login, Cmd_Argv(1))) {
353 // Logout anyone using this login
354 if ((int)sv_login.value == 1) {
355 // Mandatory web logins, or using local files
356 if (LoginMustHaveLocalAccount()) {
357 for (j = 0; j < MAX_CLIENTS; ++j) {
358 client_t* cl = &svs.clients[j];
359
360 if (!strcasecmp(cl->login, Cmd_Argv(1))) {
361 SV_Logout(cl);
362 SV_DropClient(cl);
363 }
364 }
365 }
366 }
367
368 // Update 'logged' pointers back to accounts list
369 if (i != num_accounts - 1) {
370 memcpy(&accounts[i], &accounts[num_accounts - 1], sizeof(accounts[i]));
371 memset(&accounts[num_accounts - 1], 0, sizeof(accounts[num_accounts - 1]));
372
373 // Update references from the last account which we just moved
374 for (j = 0; j < MAX_CLIENTS; ++j) {
375 client_t* cl = &svs.clients[j];
376 if (cl->logged == num_accounts) {
377 cl->logged = i + 1;
378 }
379 }
380 }
381
382 num_accounts--;
383 Con_Printf("login %s removed\n", Cmd_Argv(1));
384 WriteAccounts();
385 return;
386 }
387
388 c++;
389 }
390
391 Con_Printf("account for %s not found\n", Cmd_Argv(1));
392 }
393
394 /*
395 =================
396 SV_ListAccount_f
397
398 shows the list of accounts
399 =================
400 */
SV_ListAccount_f(void)401 void SV_ListAccount_f(void)
402 {
403 int i, c;
404
405 if (!num_accounts)
406 {
407 Con_Printf("account list is empty\n");
408 return;
409 }
410
411 Con_Printf("account list:\n");
412
413 for (i = 0, c = 0; c < num_accounts; i++)
414 {
415 if (accounts[i].state != a_free)
416 {
417 Con_Printf("%.16s %s\n", accounts[i].login, accounts[i].state == a_ok ? "" : "blocked");
418 c++;
419 }
420 }
421
422 Con_Printf("%d login(s) found\n", num_accounts);
423 }
424
425 /*
426 =================
427 SV_blockAccount
428
429 blocks/unblocks an account
430 =================
431 */
SV_blockAccount(qbool block)432 void SV_blockAccount(qbool block)
433 {
434 int i, j;
435
436 for (i = 0; i < num_accounts; i++)
437 {
438 if (accounts[i].state == a_free)
439 continue;
440
441 if (!strcasecmp(accounts[i].login, Cmd_Argv(1)))
442 {
443 if (block) {
444 accounts[i].state = a_blocked;
445 Con_Printf("account %s blocked\n", Cmd_Argv(1));
446
447 for (j = 0; j < MAX_CLIENTS; ++j) {
448 if (!strcasecmp(svs.clients[j].login, accounts[i].login)) {
449 SV_DropClient(&svs.clients[j]);
450 break;
451 }
452 }
453 return;
454 }
455
456 if (accounts[i].state != a_blocked) {
457 Con_Printf("account %s not blocked\n", Cmd_Argv(1));
458 }
459 else {
460 accounts[i].state = a_ok;
461 accounts[i].failures = 0;
462 Con_Printf("account %s unblocked\n", Cmd_Argv(1));
463 }
464
465 return;
466 }
467 }
468
469 Con_Printf("account %s not found\n", Cmd_Argv(1));
470 }
471
SV_UnblockAccount_f(void)472 void SV_UnblockAccount_f(void)
473 {
474 if (Cmd_Argc() < 2)
475 {
476 Con_Printf("usage: acc_unblock <login>\n");
477 return;
478 }
479
480 SV_blockAccount(false);
481 WriteAccounts();
482 }
483
SV_BlockAccount_f(void)484 void SV_BlockAccount_f(void)
485 {
486 if (Cmd_Argc() < 2)
487 {
488 Con_Printf("usage: acc_block <login>\n");
489 return;
490 }
491
492 SV_blockAccount(true);
493 WriteAccounts();
494 }
495
496
497 /*
498 =================
499 checklogin
500
501 returns positive value if login/pass are valid
502 values <= 0 indicates a failure
503 =================
504 */
checklogin(char * log1,char * pass,quse_t use)505 static int checklogin(char* log1, char* pass, quse_t use)
506 {
507 int i, c;
508
509 for (i = 0, c = 0; c < num_accounts; i++)
510 {
511 if (accounts[i].state == a_free)
512 continue;
513
514 if (use == accounts[i].use &&
515 /*use == use_log && accounts[i].use == use_log && */
516 !strcasecmp(log1, accounts[i].login))
517 {
518 if (accounts[i].state == a_blocked)
519 return -2;
520
521 // Only do logins/failures if using file-based login list
522 if (LoginMustHaveLocalAccount()) {
523 if (accounts[i].inuse && accounts[i].use == use_log) {
524 return -1;
525 }
526
527 if (use == use_ip ||
528 (!(int)sv_hashpasswords.value && !strcasecmp(pass, accounts[i].pass)) ||
529 ( (int)sv_hashpasswords.value && !strcasecmp(SHA1(pass), accounts[i].pass)))
530 {
531 accounts[i].failures = 0;
532 accounts[i].inuse++;
533 return i + 1;
534 }
535
536 if (++accounts[i].failures >= MAX_FAILURES) {
537 Sys_Printf("account %s blocked after %d failed login attempts\n", accounts[i].login, accounts[i].failures);
538 accounts[i].state = a_blocked;
539 }
540 WriteAccounts();
541 }
542 else {
543 return i + 1;
544 }
545
546 return 0;
547 }
548
549 c++;
550 }
551
552 return 0;
553 }
554
Login_Init(void)555 void Login_Init(void)
556 {
557 Cvar_Register(&sv_login);
558 #ifdef WEBSITE_LOGIN_SUPPORT
559 Cvar_Register(&sv_login_web);
560 #endif
561
562 Cmd_AddCommand("acc_create", SV_CreateAccount_f);
563 Cmd_AddCommand("acc_remove", SV_RemoveAccount_f);
564 Cmd_AddCommand("acc_list", SV_ListAccount_f);
565 Cmd_AddCommand("acc_unblock", SV_UnblockAccount_f);
566 Cmd_AddCommand("acc_block", SV_BlockAccount_f);
567
568 // load account list
569 //SV_LoadAccounts();
570 }
571
572 /*
573 ===============
574 SV_Login
575
576 called on connect after cmd new is issued
577 ===============
578 */
SV_Login(client_t * cl)579 qbool SV_Login(client_t* cl)
580 {
581 extern cvar_t sv_registrationinfo;
582 char* ip;
583
584 // is sv_login is disabled, login is not necessery
585 if (!(int)sv_login.value) {
586 // If using local files then logout
587 if (!cl->logged_in_via_web) {
588 SV_Logout(cl);
589 cl->logged = -1;
590 }
591 return true;
592 }
593
594 // if we're already logged return (probably map change)
595 if (cl->logged > 0 || cl->logged_in_via_web) {
596 return true;
597 }
598
599 // sv_login == 1 -> spectators don't login
600 if ((int)sv_login.value == 1 && cl->spectator)
601 {
602 SV_Logout(cl);
603 cl->logged = -1;
604 return true;
605 }
606
607 // check for account for ip
608 ip = va("%d.%d.%d.%d", cl->realip.ip[0], cl->realip.ip[1], cl->realip.ip[2], cl->realip.ip[3]);
609 if ((cl->logged = checklogin(ip, ip, use_ip)) > 0)
610 {
611 strlcpy(cl->login, accounts[cl->logged - 1].pass, CLIENT_LOGIN_LEN);
612 return true;
613 }
614
615 // need to login before connecting
616 cl->logged = 0;
617 cl->login[0] = 0;
618
619 if (sv_registrationinfo.string[0])
620 SV_ClientPrintf2(cl, PRINT_HIGH, "%s\n", sv_registrationinfo.string);
621
622 if (WebLoginsEnabled()) {
623 char buffer[128];
624 strlcpy(buffer, "//authprompt\n", sizeof(buffer));
625
626 ClientReliableWrite_Begin(cl, svc_stufftext, 2 + strlen(buffer));
627 ClientReliableWrite_String(cl, buffer);
628
629 SV_ClientPrintf2(cl, PRINT_HIGH, "Enter username:\n");
630 }
631 else {
632 SV_ClientPrintf2(cl, PRINT_HIGH, "Enter login & password:\n");
633 }
634
635 return false;
636 }
637
SV_Logout(client_t * cl)638 void SV_Logout(client_t* cl)
639 {
640 if (cl->logged > 0 && cl->logged <= sizeof(accounts) / sizeof(accounts[0])) {
641 accounts[cl->logged - 1].inuse--;
642 }
643
644 Info_SetStar(&cl->_userinfo_ctx_, "*auth", "");
645 Info_SetStar(&cl->_userinfo_ctx_, "*flag", "");
646 ProcessUserInfoChange(cl, "*auth", cl->login);
647 ProcessUserInfoChange(cl, "*flag", cl->login_flag);
648
649 memset(cl->login, 0, sizeof(cl->login));
650 memset(cl->login_alias, 0, sizeof(cl->login_alias));
651 memset(cl->login_flag, 0, sizeof(cl->login_flag));
652 memset(cl->login_challenge, 0, sizeof(cl->login_challenge));
653 memset(cl->login_confirmation, 0, sizeof(cl->login_confirmation));
654 cl->logged = 0;
655 cl->logged_in_via_web = false;
656 }
657
658 #ifdef WEBSITE_LOGIN_SUPPORT
SV_ParseWebLogin(client_t * cl)659 void SV_ParseWebLogin(client_t* cl)
660 {
661 char parameter[128] = { 0 };
662 char* p;
663
664 strlcpy(parameter, Cmd_Argv(1), sizeof(parameter));
665 for (p = parameter; *p > 32; ++p) {
666 }
667 *p = '\0';
668
669 if (!parameter[0]) {
670 return;
671 }
672
673 if (cl->login_challenge[0]) {
674 // This is response to challenge, treat as password
675 Central_VerifyChallengeResponse(cl, cl->login_challenge, parameter);
676
677 SV_ClientPrintf2(cl, PRINT_HIGH, "Challenge received, please wait...\n");
678 }
679 else if (curtime - cl->login_request_time < LOGIN_MIN_RETRY_TIME) {
680 SV_ClientPrintf2(cl, PRINT_HIGH, "Please wait and try again\n");
681 }
682 else {
683 // Treat as username
684 Central_GenerateChallenge(cl, parameter, true);
685
686 SV_ClientPrintf2(cl, PRINT_HIGH, "Generating challenge, please wait...\n");
687 }
688 }
689 #else
SV_ParseWebLogin(client_t * cl)690 void SV_ParseWebLogin(client_t* cl)
691 {
692 }
693 #endif
694
SV_ParseLogin(client_t * cl)695 void SV_ParseLogin(client_t* cl)
696 {
697 char *log1, *pass;
698
699 if (WebLoginsEnabled()) {
700 SV_ParseWebLogin(cl);
701 return;
702 }
703
704 if (Cmd_Argc() > 2)
705 {
706 log1 = Cmd_Argv(1);
707 pass = Cmd_Argv(2);
708 }
709 else
710 {
711 // bah usually whole text in 'say' is put into ""
712 log1 = pass = Cmd_Argv(1);
713 while (*pass && *pass != ' ')
714 pass++;
715
716 if (*pass)
717 *pass++ = 0;
718
719 while (*pass == ' ')
720 pass++;
721 }
722
723 // if login is parsed, we read just a password
724 if (cl->login[0])
725 {
726 pass = log1;
727 log1 = cl->login;
728 }
729 else
730 {
731 strlcpy(cl->login, log1, CLIENT_LOGIN_LEN);
732 }
733
734 if (!*pass)
735 {
736 strlcpy(cl->login, log1, CLIENT_LOGIN_LEN);
737 SV_ClientPrintf2(cl, PRINT_HIGH, "Enter password for %s:\n", cl->login);
738
739 return;
740 }
741
742 cl->logged = checklogin(log1, pass, use_log);
743
744 switch (cl->logged)
745 {
746 case -2:
747 SV_BlockedLogin(cl);
748 break;
749 case -1:
750 SV_ClientPrintf2(cl, PRINT_HIGH, "Login in use!\ntry again:\n");
751 cl->logged = 0;
752 cl->login[0] = 0;
753 break;
754 case 0:
755 SV_ClientPrintf2(cl, PRINT_HIGH, "Access denied\nPassword for %s:\n", cl->login);
756 break;
757 default:
758 strlcpy(cl->login_alias, cl->login, sizeof(cl->login_alias));
759 SV_SuccessfulLogin(cl);
760 break;
761 }
762 }
763
SV_BlockedLogin(client_t * cl)764 static void SV_BlockedLogin(client_t* cl)
765 {
766 SV_ClientPrintf2(cl, PRINT_HIGH, "Login blocked\n");
767 SV_DropClient(cl);
768 }
769
SV_SuccessfulLogin(client_t * cl)770 static void SV_SuccessfulLogin(client_t* cl)
771 {
772 extern cvar_t sv_forcenick;
773
774 if (!cl->spectator || !GameStarted()) {
775 SV_BroadcastPrintf(PRINT_HIGH, "%s logged in as %s\n", cl->name, cl->login);
776 }
777 if (cl->state < cs_spawned) {
778 SV_ClientPrintf2(cl, PRINT_HIGH, "Welcome %s\n", cl->login);
779 }
780
781 //VVD: forcenick ->
782 if ((int)sv_forcenick.value)
783 {
784 const char* forced_name = cl->login_alias[0] ? cl->login_alias : cl->login;
785
786 if (forced_name[0]) {
787 SV_ForceClientName(cl, forced_name);
788 }
789 }
790 //<-
791
792 if (cl->state < cs_spawned) {
793 MSG_WriteByte(&cl->netchan.message, svc_stufftext);
794 MSG_WriteString(&cl->netchan.message, "cmd new\n");
795 }
796 }
797
SV_ForceClientName(client_t * cl,const char * forced_name)798 static void SV_ForceClientName(client_t* cl, const char* forced_name)
799 {
800 char oldval[MAX_EXT_INFO_STRING];
801 int i;
802
803 // If any other clients are using this name, kick them
804 for (i = 0; i < MAX_CLIENTS; ++i) {
805 client_t* other = &svs.clients[i];
806
807 if (!other->state)
808 continue;
809 if (other == cl) {
810 continue;
811 }
812
813 if (!Q_namecmp(other->name, forced_name)) {
814 SV_KickClient(other, " (using authenticated user's name)");
815 }
816 }
817
818 // Set server-side name: allow colors/case changes
819 if (!Q_namecmp(cl->name, forced_name)) {
820 return;
821 }
822 strlcpy(oldval, cl->name, MAX_EXT_INFO_STRING);
823 Info_Set(&cl->_userinfo_ctx_, "name", forced_name);
824 ProcessUserInfoChange(cl, "name", oldval);
825
826 // Change name cvar in client
827 MSG_WriteByte(&cl->netchan.message, svc_stufftext);
828 MSG_WriteString(&cl->netchan.message, va("name %s\n", forced_name));
829 }
830
SV_LoginCheckTimeOut(client_t * cl)831 void SV_LoginCheckTimeOut(client_t* cl)
832 {
833 double connected = SV_ClientConnectedTime(cl);
834
835 if (connected && connected > 60)
836 {
837 Sys_Printf("Login time out for %s\n", cl->name);
838
839 SV_ClientPrintf2(cl, PRINT_HIGH, "Login timeout expired\n");
840 SV_DropClient(cl);
841 }
842 }
843
SV_LoginWebCheck(client_t * cl)844 void SV_LoginWebCheck(client_t* cl)
845 {
846 int status = checklogin(cl->login, cl->login, use_log);
847
848 if (status < 0) {
849 // Server admin explicitly blocked this account
850 SV_BlockedLogin(cl);
851 }
852 else if (status == 0 && LoginMustHaveLocalAccount()) {
853 // Server admin needs to create accounts for people to use
854 SV_BlockedLogin(cl);
855 }
856 else {
857 // Continue logging in
858 SV_SuccessfulLogin(cl);
859 }
860 }
861
SV_LoginWebFailed(client_t * cl)862 void SV_LoginWebFailed(client_t* cl)
863 {
864 memset(cl->login_challenge, 0, sizeof(cl->login_challenge));
865 cl->login_request_time = 0;
866
867 SV_ClientPrintf2(cl, PRINT_HIGH, "Challenge response failed.\n");
868 if (cl->state < cs_spawned) {
869 SV_BlockedLogin(cl);
870 }
871 }
872
SV_LoginRequired(client_t * cl)873 qbool SV_LoginRequired(client_t* cl)
874 {
875 int login = (int)sv_login.value;
876
877 if (login == 2 || (login == 1 && !cl->spectator)) {
878 if (WebLoginsEnabled()) {
879 return !cl->logged_in_via_web;
880 }
881 else {
882 return !cl->logged;
883 }
884 }
885 return false;
886 }
887
SV_LoginBlockJoinRequest(client_t * cl)888 qbool SV_LoginBlockJoinRequest(client_t* cl)
889 {
890 if (WebLoginsEnabled()) {
891 if (!cl->logged_in_via_web && (int)sv_login.value) {
892 SV_ClientPrintf(cl, PRINT_HIGH, "This server requires users to login. Please authenticate first (/cmd login <username>).\n");
893 return true;
894 }
895 }
896 else if (cl->logged <= 0 && (int)sv_login.value) {
897 SV_ClientPrintf(cl, PRINT_HIGH, "This server requires users to login. Please disconnect and reconnect as a player.\n");
898 return true;
899 }
900
901 // Allow
902 return false;
903 }
904
905 #endif // !CLIENTONLY
906