blob: a958b91895587cd408d0d94760e78b64adbb0202 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// login.c -- Object for players just logging in
4//
5// $Id: login.c 9245 2015-06-04 13:04:39Z Arathorn $
6
7 /*
8 * secure/login.c
9 *
10 * This object is cloned for every user trying to log in
11 * We are still running root.
12 *
13 * login.c looks up the username in the secure/PASSWD file. If it is
14 * found, the password is checked. If the user is already logged in,
15 * he will be reconnected to the running object. If the other object
16 * is still interactive, that will be disconnected before the user is
17 * reconnected to that object.
18 *
19 * If the user is not in PASSWD, a new entry with level 0 is created.
20 * All PASSWD writing is done in secure/master.c.
21 *
22 */
23#pragma strict_types
24#pragma no_shadow
25#pragma no_inherit
26#pragma verbose_errors
27#pragma combine_strings
28//#pragma pedantic
29//#pragma range_check
30#pragma warn_deprecated
31
32#include <config.h>
33#include <properties.h>
34#include <moving.h>
35#include "/secure/wizlevels.h"
36#include <telnet.h>
37#include <defines.h>
38#include <input_to.h>
39
40inherit "/secure/mini_props.c";
41inherit "/secure/telnetneg.c";
42
43#define SSL_GRRETING "REMOTE_HOST="
44#define PROXIES ({"127.0.0.1","87.79.24.60"})
45#define GUESTMASTER "/secure/guestmaster"
46
47#ifndef DEBUG
48#define DEBUG(x) if(find_player("tiamak")) tell_object(find_player("tiamak"),x)
49#define DEBUGM(x) if(find_player("muadib")) tell_object(find_player("muadib"),x)
50#endif
51
52/* Variables of the secure save file */
53int level, loginfails, creation_date;
54string password, name, shell, ep, ek, mq;
55string ektips;
56string fptips;
57string *domains, *guilds, *uidstotakecare;
58
59static int invis, neu;
60static string loginname;
61static string cap_name;
62static string *userentry;
63static string banish;
64static mixed *races;
65static int newbie;
66static string realip;
67
68// Prototypes
69static void SendTelopts();
70public nomask string loginname();
71// the following 4 lfuns deal with real logins
72public nomask int logon();
Zesstrab75a89c2020-01-31 18:47:57 +010073static void logon2( string str );
MG Mud User88f12472016-06-24 23:31:02 +020074static int load_player_object( int guestflag );
75static void load_player_ob_2( string obname, int guestflag );
76static int check_illegal( string str );
77static int valid_name( string str );
78static int new_password( string str );
79static int again_password( string str );
80static int check_password( string str );
81static void select_race();
82static void ask_race_question();
83static void get_race_answer( string str );
84
85protected void create();
86public string short();
87public string query_real_name();
88public nomask int query_prevent_shadow();
MG Mud User88f12472016-06-24 23:31:02 +020089public int remove();
90// the following 3 lfuns deal with dummy player creation
91public mixed new_logon( string str);
92static mixed new_load_player_object();
93static mixed new_load_player_ob_2( string obname );
94static void ask_mud_played_question();
95static void get_mud_played_answer(string str);
96
97
98public nomask string loginname()
99{
100 return loginname ? loginname : "";
101}
102
103
104public int remove()
105{
106 destruct( this_object() );
107 return 1;
108}
109
110
111static int check_too_many_logons()
112{
113 object *u;
114 string ip;
115
116 ip = query_ip_number(this_object());
117 // users() nehmen, falls nicht-interaktive Clones von login.c existieren.
118 u = filter( users(), function status (object ob, string addr) {
Zesstra077f3c62016-09-30 21:24:05 +0200119 return load_name(ob) == "/secure/login"
MG Mud User88f12472016-06-24 23:31:02 +0200120 && query_ip_number(ob) == addr;
121 }, ip );
122
Zesstracb45e3d2016-09-30 20:13:31 +0200123 if ( sizeof(u) > 2) {
MG Mud User88f12472016-06-24 23:31:02 +0200124 write( "\nEs laufen schon zu viele Anmeldungen von Deiner Adresse "
125 "aus.\nProbier es bitte in ein bis zwei Minuten noch "
126 "einmal.\n" );
127
Zesstracb45e3d2016-09-30 20:13:31 +0200128 log_file( "LOGIN_DENY", sprintf( "%s: >2 Logons von %-15s (%s)\n",
MG Mud User88f12472016-06-24 23:31:02 +0200129 ctime(time())[4..15],
130 query_ip_number(this_object()),
131 query_ip_name(this_object()) ) );
132 return 1;
133 }
134 else
135 return 0;
136}
137
138
139/*
140 * This is the function that gets called by /secure/master for every user
141 */
142public nomask int logon()
143{
144 loginname = "logon";
145 newbie=0;
146 realip="";
147
148 // als erstes wird ein Lookup gemacht, ob die Quelladresse ein
149 // Tor-Exitnode ist, der erlaubt, zu uns zu kommunizieren. Das Lookup ist
150 // asynchron und braucht eine Weile, wenn das Ergebnis noch nicht gecacht
151 // ist. An dieser Stelle wird das Ergebnis nicht ausgewertet. Achja, wie
152 // machen das natuerlich nicht fuer die IP vom Mudrechner...
153 if (query_ip_number(this_object()) != "87.79.24.60")
154 {
155 "/p/daemon/dnslookup"->check_tor(query_ip_number(this_object()),query_mud_port());
156 "/p/daemon/dnslookup"->check_dnsbl(query_ip_number(this_object()));
157 }
158 printf("HTTP/1.0 302 Found\n"
159 "Location: http://mg.mud.de/\n\n"
160 "NetCologne, Koeln, Germany. Local time: %s\n\n"
161 MUDNAME" LDmud, NATIVE mode, driver version %s\n\n",
162 strftime("%c"), __VERSION__);
163
164 SendTelopts();
165
166 if ( check_too_many_logons() ){
167 destruct(this_object());
168 return 0;
169 }
170
171 // ist die Verbindung schon wieder weg?
172 if (objectp(this_object()) && interactive(this_object())) {
173 cat( "/etc/WELCOME" );
174 }
175
176 input_to( "logon2", INPUT_PROMPT,
177 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstra28d72e52016-10-27 23:56:19 +0200178 set_next_reset(300);
MG Mud User88f12472016-06-24 23:31:02 +0200179 return 1;
180}
181
182
183static int check_too_many_from_same_ip()
184{
185 object *u;
186 string ip;
187
188 ip = query_ip_number(this_object());
189 u = filter(users(), function status (object ob, string addr, int a) {
190 return query_ip_number(ob) == addr
191 && ob->QueryProp(P_AGE) < a;
192 }, ip, 12*60*60); // 24h in heart_beats
193
194 if ( sizeof(u) > 25 ){
195 write( "\nDa anscheinend gerade jemand von Deiner Adresse aus "
196 "versucht, das \n"MUDNAME" mit neuen Charakteren zu "
197 "ueberschwemmen, werden momentan \nnur aeltere Charaktere "
198 "von dieser Adresse zugelassen.\nWenn Du meinst, dass es "
199 "sich um einen Fehler handelt, logg Dich bitte als \n"
200 "Gast ein und sprich einen Erzmagier oder Gott an.\n" );
201
202 log_file( "LOGIN_DENY", sprintf( "%s: >10 Spieler von %-15s (%s)\n",
203 ctime(time())[4..15],
204 query_ip_number(this_object()),
205 query_ip_name(this_object()) ) );
206
207 destruct(this_object());
208 return 1;
209 }
210 else
211 return 0;
212}
213
214
215static int check_illegal( string str )
216{
217 string res;
218
219 res = (string)master()->QuerySBanished(query_ip_number(this_object()));
220 if (!res)
221 {
222 // check connection from Tor exit node
223 string eff_ip = (realip!="" ? realip : query_ip_number(this_object()));
224 if ("/p/daemon/dnslookup"->check_tor(eff_ip, query_mud_port())
225 || "/p/daemon/dnslookup"->check_dnsbl(eff_ip))
226 res =
227 "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
228 "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
229 "die Moeglichkeit,\neinfach neue Charaktere "
230 "anzulegen, fuer diese Adresse gesperrt.\n\n"
231 "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
232 "an\n\n mud@mg.mud.de\n\n"
233 "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
234 }
235
236 if ( res )
237 {
238 write( res );
239 log_file( "LOGIN_DENY", sprintf( "%s: %-11s %-15s (%s)\n",
240 ctime(time())[4..15], str,
241 query_ip_number(this_object()),
242 query_ip_name(this_object()) ) );
243 remove();
244 return 1;
245 }
246
247 return 0;
248}
249
250
251/*
252 * Check that a player name is valid. Only allow
253 * lowercase letters.
254 */
255static int valid_name( string str )
256{
257 int i;
258
259 if ( str == "logon" ){
260 write( "Der Name wuerde nur Verwirrung stiften.\n" );
261 return 0;
262 }
263
264 i = sizeof(str);
265
266 if ( i > 11 ){
267 write( "Dein Name ist zu lang, nimm bitte einen anderen.\n" );
268 return 0;
269 }
270
271 for ( ; i--; )
272 if ( str[i] < 'a' || str[i] > 'z' ) {
273 write( "Unerlaubtes Zeichen '" + str[i..i] + "' im Namen: " + str
274 + "\n" );
275 write( "Benutze bitte nur Buchstaben ohne Umlaute.\n" );
276 return 0;
277 }
278
279 return 1;
280}
281
282
Zesstrab75a89c2020-01-31 18:47:57 +0100283static void logon2( string str )
MG Mud User88f12472016-06-24 23:31:02 +0200284{
285 int i, arg;
286 mixed txt;
287
288 if ( !str || str == "" ){
289 write( "Abbruch!\n" );
290 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100291 return;
MG Mud User88f12472016-06-24 23:31:02 +0200292 }
293
MG Mud User88f12472016-06-24 23:31:02 +0200294 if(strstr(str,SSL_GRRETING)==0)
295 {
296 if( member(PROXIES,query_ip_number(this_object()))>-1 )
297 {
298 realip=str[sizeof(SSL_GRRETING)..];
299 } // andere IPs werden einfach ignoriert. -> log/PROXY.REQ ?
300 // ggf. Lookup fuer Torexits anstossen.
301 "/p/daemon/dnslookup"->check_tor(realip,query_mud_port());
302 "/p/daemon/dnslookup"->check_dnsbl(realip);
303
304 input_to( "logon2", INPUT_PROMPT,
305 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100306 return;
MG Mud User88f12472016-06-24 23:31:02 +0200307 }
308
309 if ( loginname != "logon" ) {
310 log_file( "ILLEGAL", sprintf( "%s Illegal patch of login: "
311 "loginname = %O\n",
312 dtime(time()), loginname ) );
313 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100314 return;
MG Mud User88f12472016-06-24 23:31:02 +0200315 }
316
317 str = lower_case(str);
318 cap_name = capitalize(str);
319
320 if ( str == "neu" && !neu ){
321 cat( "/etc/WELCOME_NEW" );
322 neu = 1;
323 input_to( "logon2", INPUT_PROMPT, "Name: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100324 return;
MG Mud User88f12472016-06-24 23:31:02 +0200325 }
326
327 if ( !valid_name(str) ){
328 string pr;
329 if ( !neu )
330 pr= "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ";
331 else
332 pr= "Bitte gib Dir einen anderen Namen: ";
333
334 input_to( "logon2", INPUT_PROMPT, pr );
Zesstrab75a89c2020-01-31 18:47:57 +0100335 return;
MG Mud User88f12472016-06-24 23:31:02 +0200336 }
337
338 if ( sscanf( str, "gast%d", arg ) == 1 ){
339 write( "Du meinst wohl 'Gast' ...\n" );
340 str = "gast";
341 }
342
343 loginname = str;
344
345 /* read the secure save file to see if character already exists */
Zesstra313eee92017-01-31 14:55:08 +0100346 string ssavef=master()->secure_savefile(loginname);
347 if ( loginname != "gast"
348 && (!ssavef || !sizeof(ssavef) || !restore_object(ssavef) ))
Zesstrae43dd602017-01-31 10:48:10 +0100349 {
MG Mud User88f12472016-06-24 23:31:02 +0200350 object *user;
351
352 if ( !neu )
353 {
354 write( "Es existiert kein Charakter mit diesem Namen.\n" );
355 write( "Falls Du einen neuen Charakter erschaffen moechtest, "
356 "tippe bitte \"neu\" ein.\n" );
357
358 loginname = "logon";
359 input_to( "logon2", INPUT_PROMPT,
360 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100361 return;
MG Mud User88f12472016-06-24 23:31:02 +0200362 }
363
364 for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
365 if ( object_name(user[i])[0..12] == "/secure/login" &&
366 ((string)user[i]->loginname()) == loginname ){
367 write( "Eine Anmeldung fuer diesen Namen laeuft bereits.\n" );
368 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100369 return;
MG Mud User88f12472016-06-24 23:31:02 +0200370 }
371
372 // Site-Banish checken
373 if ( check_illegal(loginname))
Zesstrab75a89c2020-01-31 18:47:57 +0100374 return;
MG Mud User88f12472016-06-24 23:31:02 +0200375
376 if ( check_too_many_from_same_ip() )
Zesstrab75a89c2020-01-31 18:47:57 +0100377 return;
MG Mud User88f12472016-06-24 23:31:02 +0200378
379 /* new character */
380 if ( sizeof(loginname) < 3 ){
381 write( "Der Name ist zu kurz.\n" );
382 loginname = "logon";
383 input_to( "logon2", INPUT_PROMPT,
384 "Versuch einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100385 return;
MG Mud User88f12472016-06-24 23:31:02 +0200386 }
387
388
389 if ( (txt = (string)master()->QueryBanished(loginname)) ){
390 if ( txt != "Dieser Name ist gesperrt." )
391 txt = sprintf("Hoppla - dieser Name ist reserviert oder gesperrt "
392 "(\"gebanisht\")!\nGrund: %s\n",txt);
393 else
394 txt = "Hoppla - dieser Name ist reserviert oder gesperrt "
395 "(\"gebanisht\")!\n";
396 write(txt);
397 loginname = "logon";
398 input_to( "logon2", INPUT_PROMPT,
399 "Bitte gib Dir einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100400 return;
MG Mud User88f12472016-06-24 23:31:02 +0200401 }
402
403 /* Initialize the new secure savefile */
404 name = loginname;
405 password = "";
406 level = 0;
407 domains = ({ });
408 guilds = ({ });
409 shell = "";
410 ep = "";
411 ek = "";
412 mq = "";
413 ektips="";
414 fptips="";
415 creation_date = time();
416
417 input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
418 "Waehle ein Passwort: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100419 return;
MG Mud User88f12472016-06-24 23:31:02 +0200420 }
421 else {
422 if ( loginname == "gast" ){
423 if ( check_illegal(loginname) )
Zesstrab75a89c2020-01-31 18:47:57 +0100424 return;
MG Mud User88f12472016-06-24 23:31:02 +0200425
426 load_player_object(1);
Zesstrab75a89c2020-01-31 18:47:57 +0100427 return;
MG Mud User88f12472016-06-24 23:31:02 +0200428 }
429
430 if ( neu ){
431 write( "Es existiert bereits ein Charakter dieses Namens.\n" );
432 loginname = "logon";
433 input_to( "logon2", INPUT_PROMPT,
434 "Gib Dir einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100435 return;
MG Mud User88f12472016-06-24 23:31:02 +0200436 }
437
438 if ( (int)master()->check_late_player(loginname) )
439 {
440 write( "Dieser Spieler hat uns leider fuer immer verlassen.\n" );
441 loginname = "logon";
442 input_to( "logon2", INPUT_PROMPT,
443 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100444 return;
MG Mud User88f12472016-06-24 23:31:02 +0200445 }
446
447 if ( txt = (string)master()->QueryTBanished(loginname) ){
448 write( txt );
449 loginname = "logon";
450 input_to( "logon2", INPUT_PROMPT,
451 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100452 return;
MG Mud User88f12472016-06-24 23:31:02 +0200453 }
454
455 if ( creation_date > (time() - 30*24*60*60)
456 && check_too_many_from_same_ip() )
Zesstrab75a89c2020-01-31 18:47:57 +0100457 return;
MG Mud User88f12472016-06-24 23:31:02 +0200458
459 write( "Schoen, dass Du wieder da bist, "+capitalize(loginname)+"!\n" );
460
461 if ( !stringp(password) || password == "" ) {
462 write( "Du hast KEIN PASSWORD!\n" );
463 write( "Benutze den \"password\"-Befehl, um das zu aendern !\n" );
464 load_player_object(0);
Zesstrab75a89c2020-01-31 18:47:57 +0100465 return;
MG Mud User88f12472016-06-24 23:31:02 +0200466 }
467
468 input_to( "check_password", INPUT_NOECHO|INPUT_PROMPT,
469 "Passwort: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100470 return;
MG Mud User88f12472016-06-24 23:31:02 +0200471 }
472}
473
474
475static int new_password( string str )
476{
477 write( "\n" );
478
479 if ( !str || str == "" )
480 return remove();
481
482 password = str;
483
484 if ( !master()->good_password( str, loginname ) ) {
485 input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
486 "Bitte gib ein Passwort an: ");
487 return 1;
488 }
489
490 write( "\nZur Erinnerung: Es ist v e r b o t e n, andere Spieler "
491 "anzugreifen!\n" );
492 write( "Das gilt auch fuer Froesche, bei denen \"Ein Frosch namens "
493 "XXXXX\" steht.\n\n" );
494 input_to( "again_password", INPUT_NOECHO|INPUT_PROMPT,
495 "Passwort bitte nochmal eingeben: ");
496 return 1;
497}
498
499static int again_password( string str )
500{
501 write( "\n" );
502
503 if ( str != password ){
504 write( "Die Passwoerter stimmten nicht ueberein!\n" );
505 destruct( this_object() );
506 return 1;
507 }
508
Zesstra28d72e52016-10-27 23:56:19 +0200509 set_next_reset(600);
MG Mud User88f12472016-06-24 23:31:02 +0200510 password = md5_crypt( password, 0 );
511 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
512 master()->RemoveFromCache( loginname );
513
514 load_player_object(0);
515 return 1;
516}
517
518static int check_password( string str )
519{
520 write( "\n" );
521
522 // Invis einloggen?
523 if (sizeof(str) > 1 && str[0] == '-') {
524 invis = 1;
525 str = str[1..];
526 }
527
528 // welcher Hash ists denn?
529 if (sizeof(password) > 13) {
530 // MD5-Hash
531 str = md5_crypt(str, password);
532 }
533 else if (sizeof(password) > 2) {
534 // Crypt-Hash
535 str = crypt(str, password[0..1]);
536 }
537 else {
538 // keiner von beiden Hashes -> ungueltiges PW
539 str = 0;
540 }
541
542 if ( !stringp(str) || str != password ) {
543 // Hashes stimmen nicht ueberein -> und schluss...
544 write( "Falsches Passwort!\n");
545 if ( loginfails > 2 )
546 write(break_string(
547 "Solltest Du weiterhin Probleme mit dem Einloggen haben, kannst "
548 "Du Dein Passwort zuruecksetzen lassen, indem Du Dich als Gast "
549 "anmeldest und einen Erzmagier ansprichst, oder indem Du Dich "
Zesstra0559c662017-02-06 19:14:51 +0100550 "mittels einer E-Mail an mud@mg.mud.de mit uns in Verbindung "
MG Mud User88f12472016-06-24 23:31:02 +0200551 "setzt.",78));
552
553 log_file( (level < 60 ? "LOGINFAIL" : "ARCH/LOGINFAIL"),
554 sprintf( "PASSWORD: %-11s %s, %-15s (%s)\n",
555 loginname, ctime(time())[4..15],
556 query_ip_number(this_object()),
557 query_ip_name(this_object()) ), 200000 );
558
559 loginfails++;
560 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
561 master()->RemoveFromCache( loginname );
562 destruct( this_object() );
563 return 1;
564 }
565
566 if ( loginfails ) {
567 write( loginfails + " fehlgeschlagene" + (loginfails == 1 ? "r" : "") +
568 " Login-Versuch" + (loginfails == 1 ? "" : "e") +
569 " seit dem letzten erfolgreichen Login.\n" );
570 loginfails = 0;
571 }
572
573 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
574 master()->RemoveFromCache( loginname );
575
576 load_player_object(0);
577 return 1;
578}
579
580
581static void select_race()
582{
583 int i;
Zesstra7e353662019-11-27 19:52:41 +0100584 string race;
585 int selectable;
MG Mud User88f12472016-06-24 23:31:02 +0200586
587 races = get_dir( "/std/shells/*.c" );
588
589 // Mensch soll immer als erstes in der Auswahlliste stehen.
590 if (member(races,"human.c")!=-1)
591 races=({"human.c"})+(races-({"human.c"}));
592
593 for ( i = sizeof(races); i--; ){
594 races[i] = "/std/shells/" + races[i][0..<3];
Zesstra7e353662019-11-27 19:52:41 +0100595 selectable = 0;
596 race = 0;
597 if ( catch(selectable = ({int})call_other( races[i],
598 "QueryAllowSelect" ); publish)
599 || !selectable)
600 selectable = 0;
601 else if ( catch(race = ({string})call_other(races[i],
602 "QueryProp", P_RACE );publish) )
603 race = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200604
Zesstra7e353662019-11-27 19:52:41 +0100605 if ( !selectable || !sizeof(race) )
MG Mud User88f12472016-06-24 23:31:02 +0200606 races[i..i] = ({});
607 else
Zesstra7e353662019-11-27 19:52:41 +0100608 races[i] = ({ races[i], race });
MG Mud User88f12472016-06-24 23:31:02 +0200609 }
610
611 if ( sizeof(races) == 1 ){
612 write( "Es gibt nur eine Rasse, Du hast also keine Wahl.\n" );
613
614 shell = races[0][0];
615 master()->set_player_object( loginname, shell );
616
617 return load_player_ob_2( shell, 0 );
618 }
619
620 return ask_mud_played_question();
621}
622
623static void ask_mud_played_question()
624{
625 write(break_string(
626 "\nWenn Du ein absoluter Neuling in diesem Spiel bist moechten "
627 "wir Dir mit einigen Tips zu Beginn beiseite stehen.\n\n",78,
628 0,BS_LEAVE_MY_LFS));
629 input_to( "get_mud_played_answer", INPUT_PROMPT,
630 "Hast Du schon einmal in einem MUD gespielt? (ja,nein): ");
631 return;
632}
633
634static void ask_race_question()
635{
636 int i, j;
637
638 write( break_string( "Du musst Dich jetzt entscheiden, welcher Rasse Du "
639 "in dieser Welt angehoeren moechtest. Alle Rassen "
640 "haben verschiedene Vor- und Nachteile, insgesamt "
641 "aber gleich gute Chancen. Auch das Startgebiet "
642 "haengt von der ausgewaehlten Rasse ab. Im "
643 "Normalfall kann die Rasse nicht mehr gewechselt "
644 "werden, nachdem sie einmal ausgewaehlt wurde. "
645 "Ueberlege Dir Deine Entscheidung also gut. Derzeit "
646 "stehen folgende Rassen zur Auswahl:\n\n", 78 ) );
647
648 for ( i = 0, j = sizeof(races); i < j; i++ )
649 printf( "% 2d. %-30s %s", i+1, capitalize(races[i][1]),
650 (i % 2 ? "\n" : "| ") );
651
652 if ( sizeof(races) % 2 )
653 write( "\n" );
654
655 write( break_string( "\nDurch Eingabe einer Ziffer waehlst Du die Rasse "
656 "aus, durch Eingabe eines \"\?\" gefolgt von einer "
657 "Ziffer erhaeltst Du naehere Informationen ueber "
658 "eine Rasse. Ein \"\?\" allein wiederholt diese "
659 "Liste.", 78, 0, 1 ) );
660
661 if (newbie)
662 {
663 write(break_string("\nAls Neuling solltest Du Dich NICHT fuer "
664 "die Dunkelelfen entscheiden. Diese "
665 "Rasse hat einige Probleme im Umgang "
666 "mit den anderen Rassen und mit dem "
667 "Sonnenlicht.",78,0,BS_LEAVE_MY_LFS));
668 }
669
670 input_to( "get_race_answer", INPUT_PROMPT,
671 "\nWas willst Du tun: ");
672 return;
673}
674
675
676static void get_race_answer( string str )
677{
678 int num;
679
680 if ( str == "?" )
681 return ask_race_question();
682
683 if ( sscanf( str, "?%d", num ) ){
684 if ( num < 1 || num > sizeof(races) ){
685 write( "Das geht nicht.\n\n");
686 input_to( "get_race_answer", INPUT_PROMPT,
687 "Was willst Du tun: ");
688 return;
689 }
690
691 write( call_other( races[num - 1][0], "QueryProp", P_RACE_DESCRIPTION ));
692 input_to( "get_race_answer", INPUT_PROMPT,
693 "\nWas willst Du tun: ");
694 return;
695 }
696
697 if ( sscanf( str, "%d", num ) && num >= 1 && num <= sizeof(races) ){
698 write( "Ok, Du bist jetzt ein "
699 + capitalize(races[num-1][1]) + ".\n" );
700
701 shell = races[num-1][0];
702 master()->set_player_object( loginname, shell );
703 return load_player_ob_2( shell, 0 );
704 }
705
706 write("Wie bitte?\n\n" );
707 input_to( "get_race_answer", INPUT_PROMPT,
708 "Was willst Du tun: ");
709}
710
711static void get_mud_played_answer (string str)
712{
713 if ( str == "ja" || str=="j")
714 {
715 newbie=0;
716 return ask_race_question();
717 }
718 if ( str != "nein" && str!="n")
719 {
720 write("\n\nAntworte bitte mit ja oder nein.\n\n");
721
722 return ask_mud_played_question();
723 }
724 newbie=1;
725 write("\n\nEine kleine Einfuehrung in das "MUDNAME" bekommst "
726 "Du auch hier:\n\n"
727 "http://mg.mud.de/newweb/hilfe/tutorial/inhalt.shtml\n\n");
728 return ask_race_question();
729
730}
731
732static int load_player_object( int guestflag )
733{
734 object ob;
735 string fname;
736 int was_interactive;
737
738 if ( sizeof(users()) >= 195 && !IS_WIZARD(loginname) ){
739 write( "Die maximale Spielerzahl wurde bereits erreicht!!!\n"
740 "Aus technischen Gruenden duerfen sich nur noch Magier "
741 "einloggen.\nVersuch es spaeter noch einmal ...\n" );
742 destruct( this_object() );
743 return 1;
744 }
745 else if ( sizeof(users()) >= 198 && !IS_ARCH(loginname) ){
746 write( "Die maximale Spieler- und Magierzahl wurde bereits erreicht!!!"
747 "\nAus technischen Gruenden duerfen sich nur noch Erzmagier "
748 "einloggen.\nVersuch es spaeter noch einmal ...\n" );
749 destruct( this_object() );
750 return 1;
751 }
752
753 if ( file_size("/etc/NOLOGIN")>=0 )
754 {
755 if (file_size("/etc/NOLOGIN.info")>0) {
756 //NOLOGIN.info enthaelt evtl. weitergehende Informationen fuer
757 //Spieler, z.B. vorrauss. Wiederverfuegbarkeit.
758 write(break_string(read_file("/etc/NOLOGIN.info"),78,"",
759 BS_LEAVE_MY_LFS|BS_SINGLE_SPACE));
760 }
761 else {
762 //sonst Standardmeldung ausgeben.
763 write ("\nAufgrund von technischen Problemen ist das Einloggen ins "
764 MUDNAME" zur \nZeit nicht moeglich. Bitte versuch es "
765 "spaeter noch einmal.\n\n");
766 }
767 if ( IS_ARCH(loginname) ||
768 member(explode(read_file("/etc/NOLOGIN")||"","\n"),
769 loginname)!=-1 )
770 {
771 write("Im Moment koennen nur Erzmagier einloggen. Um Spieler "
772 "wieder ins Spiel zu lassen, muss die Datei '/etc/NOLOGIN' "
773 "geloescht werden.\n\n ");
774 } else {
775 destruct( this_object() );
776 return 1;
777 }
778 }
779
780 if ( guestflag ){
781 if ( catch(guestflag = (int)GUESTMASTER->new_guest();publish)
782 || !guestflag ){
783 write( "Derzeit ist kein Gastlogin moeglich!\n" );
784 destruct( this_object() );
785 return 1;
786 }
787
788 loginname = "gast" + guestflag;
789 cap_name = capitalize(loginname);
790 name = cap_name;
791
792 if ( !(ob = find_player(loginname) || find_netdead(loginname)) ){
793 object *user;
794 int i;
795
796 // gegen Horden von Gast1 - wenn ein Gast noch am Prompt fuer
797 // das Geschlecht haengt, ist er ueber find_player() noch nicht
798 // zu finden ...
799 for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
800 if ( object_name(user[i])[0..11] == "/std/shells/" &&
801 getuid(user[i]) == loginname ){
802 ob = user[i];
803 break;
804 }
805 }
806
807 if ( ob ){
808 tell_object( ob, "Ein anderer Spieler moechte diesen Gastzugang "
809 "jetzt benutzen. Wenn es Dir hier\ngefallen hat, "
810 "ueberleg Dir doch einen Charakternamen und komm "
811 "unter diesem\nNamen wieder!\n" );
812 destruct(ob);
813 }
814
815 load_player_ob_2( "std/shells/human", guestflag );
816
817 return 1;
818 }
819 else {
820 /* Test if we are already playing */
821 was_interactive = 0;
822 ob = find_player(loginname) || find_netdead(loginname);
823
824 if (ob) {
825 write( "Du nimmst schon am Spiel teil!\n" );
826 write( "Verwende Deine alte sterbliche Huelle ...\n" );
827
828 if ( interactive(ob) )
829 {
830 /* The other object is still interactive; reconnect that "soul"
831 to a dummy object and destruct that, thus disconnecting the
832 other probably linkdead user. The real "body" is still
833 there for reconnecting by login.c */
834 remove_interactive(ob);
835 was_interactive = 1;
836 }
837 // Wenn Invislogin, P_INVIS setzen.
838 if ( invis && IS_WIZARD(ob) )
839 {
840 ob->SetProp( P_INVIS, ob->QueryProp(P_AGE) );
841 tell_object( ob, "DU BIST UNSICHTBAR!\n" );
842 }
843 /* Now reconnect to the old body */
844 exec( ob, this_object() );
845 ob->set_realip(realip);
846 if ( ((int)ob->QueryProp(P_LEVEL)) == -1 )
847 ob->start_player( cap_name );
848 else
849 ob->Reconnect( was_interactive );
850
851 call_out( "remove", 2 );
852 return 1;
853 }
854 }
855
856 /* read player object from passwd file */
857 if ( stringp(shell) && shell != "" )
858 load_player_ob_2 ( shell, 0 );
859 else
860 select_race();
861
862 return 1;
863}
864
865
866static void load_player_ob_2( string obname, int guestflag )
867{
868 object blueprint;
869 string err, ob_name;
870 object ob, old_ob;
871
872 if (!interactive()) {
873 destruct(this_object());
874 return;
875 }
876 /* start player activity */
Zesstrae88826c2016-09-24 20:37:05 +0200877 log_file( "syslog/shell/ENTER", sprintf( "%-11s %s, %-15s (%s).\n",
MG Mud User88f12472016-06-24 23:31:02 +0200878 capitalize(name), ctime(time())[4..15],
879 query_ip_number(this_object()),
880 query_ip_name(this_object()) ), 200000 );
881
882 seteuid(loginname);
883
884 /* load the "real" player object */
885 /* If some asshole has moved the blueprint */
886 if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
887 destruct(blueprint);
888
889 if ( err = catch(ob = clone_object(obname);publish) ){
890 log_file( "SHELLS", "Failed to load shell " + obname + ", " +
891 dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
892
893 write( "Konnte das passende Playerobjekt nicht laden. Lade "
894 "stattdessen\ndas Objekt Mensch. BITTE ERZMAGIER "
895 "BENACHRICHTIGEN !\n" );
896 err = catch(ob = clone_object("/std/shells/human");publish);
897 }
898
899 if ( !ob || err ) {
900 write( "Error on loading " + shell + "\nError = " + err + "\n" );
901 destruct( this_object() );
902 return;
903 }
904
905 if ( guestflag )
906 catch( GUESTMASTER->set_guest( guestflag, ob );publish );
907
908 ob_name = explode( object_name(ob), "#" )[0];
909
910 if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
911 ob_name = ob_name[11..];
912
913 ob_name = ob_name + ":" + loginname;
914
915 if( !guestflag )
916 {
917 if ( old_ob = find_object(ob_name) )
918 {
919 catch(old_ob->remove();publish);
920
921 if ( old_ob )
922 destruct( old_ob );
923 }
924 rename_object( ob, ob_name );
925 ob->__reload_explore();
926 }
927 exec( ob, this_object() );
928 ob->set_realip(realip);
929 ob->start_player( cap_name );
Zesstraab834bb2020-01-21 13:11:45 +0100930 // Hinweis: Das Spielerobjekt holt sich in updates_after_restore() von hier
931 // den Status von invis und setzt ggf. P_INVIS, ausserdem den Status der
932 // Telnet Negotiations.
MG Mud User88f12472016-06-24 23:31:02 +0200933
MG Mud User88f12472016-06-24 23:31:02 +0200934 // wenn der Spieler noch nicht im Mud gespielt hat, wird die aktuelle Zeit
935 // in die entsprechende Prop geschrieben. Die Prop ist transient und wird
936 // absichtlich nicht gespeichert.
937 if (newbie)
938 ob->SetProp("_lib_mud_newbie", creation_date);
939
940 destruct( this_object() );
941}
942
943
944/*
945 * With arg = 0 this function should only be entered once!
946 */
947protected void create()
948{
949 loginname = "logon";
950 creation_date = -1;
951 catch( load_object( "/secure/merlin");publish );
952 loginfails = 0;
953 realip="";
954 if (clonep())
955 set_next_reset(900);
956 else
957 set_next_reset(-1);
958}
959
960void reset()
961{
962 if (clonep())
Zesstra28d72e52016-10-27 23:56:19 +0200963 {
964 if (interactive(this_object()))
965 tell_object(this_object(),"Time out!");
MG Mud User88f12472016-06-24 23:31:02 +0200966 remove();
Zesstra28d72e52016-10-27 23:56:19 +0200967 }
MG Mud User88f12472016-06-24 23:31:02 +0200968}
969
970public string short()
971{
972 return "<Einloggender Teilnehmer>.\n";
973}
974
975
976public string query_real_name()
977{
978 return "<logon>";
979}
980
981
982public nomask int query_prevent_shadow()
983{
984 return 1;
985}
986
987
MG Mud User88f12472016-06-24 23:31:02 +0200988// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
989// im Loginprozess involviert.
990public mixed new_logon( string str)
991{
992 seteuid(getuid()); // sonst funkt ARCH_SECURITY nicht
993
994 if ( !ARCH_SECURITY || process_call() ){
995 write( "Nur fuer Erzmagier erlaubt!\n" );
996 destruct( this_object() );
997 return -1;
998 }
999
1000 if ( !str || str == "" ){
1001 write( "Kein Name angegeben!\n" );
1002 destruct( this_object() );
1003 return 0;
1004 }
1005
1006 str = lower_case(str);
1007 cap_name = capitalize(str);
1008
1009 loginname = str;
1010 seteuid(ROOTID);
1011
1012 /* read the secure save file to see if character already exists */
1013 if ( !restore_object( master()->secure_savefile(loginname) ) ){
1014 write( "Kein solcher Spieler!\n" );
1015 destruct( this_object() );
1016 return 0;
1017 }
1018 else {
1019 write( "Ok, der Spieler " + capitalize(str) + " existiert!\n" );
1020 return new_load_player_object();
1021 }
1022}
1023
1024// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1025// im Loginprozess involviert.
1026static mixed new_load_player_object()
1027{
1028 if ( find_player(loginname) || find_netdead(loginname) ){
1029 write( "Der Spieler ist bereits online oder netztot!\n" );
1030 destruct( this_object() );
1031 return 2;
1032 }
1033
1034 /* read player object from passwd file */
1035 if ( stringp(shell) && shell != "" )
1036 return new_load_player_ob_2( shell );
1037 else {
1038 write( "Keine Shell angegeben!\n" );
1039 destruct( this_object() );
1040 return 0;
1041 }
1042}
1043
1044// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1045// im Loginprozess involviert.
1046static mixed new_load_player_ob_2( string obname )
1047{
1048 object blueprint;
1049 string err, ob_name;
1050 object ob, old_ob;
1051
1052 seteuid(loginname);
1053
1054 /* load the "real" player object */
1055 /* If some asshole has moved the blueprint */
1056 if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
1057 destruct( blueprint );
1058
1059 err = catch(ob = clone_object(obname);publish);
1060
1061 if ( err ){
1062 log_file( "SHELLS", "Failed to load shell " + obname + ", " +
1063 dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
1064
1065 write( "Konnte das passende Playerobjekt nicht laden. "
1066 "Lade stattdessen\ndas Objekt Mensch!\n" );
1067
1068 err = catch(ob = clone_object( "std/shells/human" );publish );
1069 }
1070
1071 if ( !ob || err ){
1072 write( "Error on loading " + shell + "\nError = " + err + "\n" );
1073 destruct( this_object() );
1074 return 0;
1075 }
1076
1077 ob_name = explode( object_name(ob), "#" )[0];
1078
1079 if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
1080 ob_name = ob_name[11..];
1081
1082 ob_name = ob_name + ":" + loginname;
1083
1084 if ( old_ob = find_object(ob_name) ){
1085 catch( old_ob->remove(); publish );
1086
1087 if ( old_ob )
1088 destruct( old_ob );
1089 }
1090
1091 rename_object( ob, ob_name );
1092 ob->__reload_explore();
1093 ob->set_realip(realip);
1094 ob->start_player(cap_name);
1095 ob->SetProp( "creation_date", creation_date );
1096 ob->Set( "creation_date", SAVE|SECURED|PROTECTED, F_MODE_AS );
1097
1098 ob->move( "/room/nowhere", M_NOCHECK );
1099 set_object_heart_beat( ob, 0 );
1100 destruct( this_object() );
1101
1102 return ob;
1103}
1104
1105string query_realip()
1106{
1107 return realip ? realip : "";
1108}
1109
1110int query_invis()
1111{
1112 return invis;
1113}
1114