blob: bd8726e0e4024e1dd7ccce82163056a090823c3d [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"
Zesstrae037c1d2020-02-02 22:02:14 +010036
MG Mud User88f12472016-06-24 23:31:02 +020037#include <telnet.h>
38#include <defines.h>
39#include <input_to.h>
Zesstrae037c1d2020-02-02 22:02:14 +010040#include <configuration.h>
MG Mud User88f12472016-06-24 23:31:02 +020041
42inherit "/secure/mini_props.c";
43inherit "/secure/telnetneg.c";
44
45#define SSL_GRRETING "REMOTE_HOST="
46#define PROXIES ({"127.0.0.1","87.79.24.60"})
47#define GUESTMASTER "/secure/guestmaster"
48
49#ifndef DEBUG
50#define DEBUG(x) if(find_player("tiamak")) tell_object(find_player("tiamak"),x)
51#define DEBUGM(x) if(find_player("muadib")) tell_object(find_player("muadib"),x)
52#endif
53
54/* Variables of the secure save file */
55int level, loginfails, creation_date;
56string password, name, shell, ep, ek, mq;
57string ektips;
58string fptips;
59string *domains, *guilds, *uidstotakecare;
60
61static int invis, neu;
62static string loginname;
63static string cap_name;
64static string *userentry;
65static string banish;
66static mixed *races;
67static int newbie;
68static string realip;
69
70// Prototypes
71static void SendTelopts();
72public nomask string loginname();
73// the following 4 lfuns deal with real logins
74public nomask int logon();
Zesstrab75a89c2020-01-31 18:47:57 +010075static void logon2( string str );
MG Mud User88f12472016-06-24 23:31:02 +020076static int load_player_object( int guestflag );
77static void load_player_ob_2( string obname, int guestflag );
78static int check_illegal( string str );
79static int valid_name( string str );
80static int new_password( string str );
81static int again_password( string str );
82static int check_password( string str );
83static void select_race();
84static void ask_race_question();
85static void get_race_answer( string str );
86
87protected void create();
88public string short();
89public string query_real_name();
90public nomask int query_prevent_shadow();
MG Mud User88f12472016-06-24 23:31:02 +020091public int remove();
92// the following 3 lfuns deal with dummy player creation
93public mixed new_logon( string str);
94static mixed new_load_player_object();
95static mixed new_load_player_ob_2( string obname );
96static void ask_mud_played_question();
97static void get_mud_played_answer(string str);
98
99
100public nomask string loginname()
101{
102 return loginname ? loginname : "";
103}
104
105
106public int remove()
107{
108 destruct( this_object() );
109 return 1;
110}
111
112
113static int check_too_many_logons()
114{
115 object *u;
116 string ip;
117
118 ip = query_ip_number(this_object());
119 // users() nehmen, falls nicht-interaktive Clones von login.c existieren.
120 u = filter( users(), function status (object ob, string addr) {
Zesstra077f3c62016-09-30 21:24:05 +0200121 return load_name(ob) == "/secure/login"
MG Mud User88f12472016-06-24 23:31:02 +0200122 && query_ip_number(ob) == addr;
123 }, ip );
124
Zesstracb45e3d2016-09-30 20:13:31 +0200125 if ( sizeof(u) > 2) {
MG Mud User88f12472016-06-24 23:31:02 +0200126 write( "\nEs laufen schon zu viele Anmeldungen von Deiner Adresse "
127 "aus.\nProbier es bitte in ein bis zwei Minuten noch "
128 "einmal.\n" );
129
Zesstracb45e3d2016-09-30 20:13:31 +0200130 log_file( "LOGIN_DENY", sprintf( "%s: >2 Logons von %-15s (%s)\n",
MG Mud User88f12472016-06-24 23:31:02 +0200131 ctime(time())[4..15],
132 query_ip_number(this_object()),
133 query_ip_name(this_object()) ) );
134 return 1;
135 }
136 else
137 return 0;
138}
139
Zesstrae037c1d2020-02-02 22:02:14 +0100140// Callback when a TLS connection negotiation was finished. This handler is
141// called for negotiations startet by the telnet option START_TLS.
142protected void tls_init_callback(int handshake_result)
143{
144 if (handshake_result < 0)
145 {
146 // Fehler im Vebindungsaufbau
147 write(break_string(sprintf(
148 "Can't establish a TLS/SSL encrypted connection: %s."
149 "Disconnecting now. If this error persists, please "
150 "disable the usage of TLS or STARTTLS in your client.",
151 tls_error(handshake_result)),78));
152 // Disconnect
153 destruct(this_object());
154 return;
155 }
156 // In this case, we will treat the newly negotiated TLS connection as as new
157 // connection and just start over again by calling logon(). And re-enable
158 // the telnet machine of the driver of course.
159 configure_interactive(this_object(), IC_TELNET_ENABLED, 1);
160 logon();
161}
162
163// Called from the telnetneg handler for TELOPT_STARTTLS to initiate the TLS
164// connection negotiation.
165protected void init_tls()
166{
167 ::init_tls();
168 configure_interactive(this_object(), IC_TELNET_ENABLED, 0);
169 tls_init_connection(this_object(), #'tls_init_callback);
170}
171
172// Wenn der Client via STARTTLS eine TLS negotiation angestossen hat und
173// die noch laeuft, darf keine Ausgabe erfolgen. In diesem Fall wird das
174// Loginverfahren ausgesetzt, bis die TLS-Verhandlung abgeschlossen ist.
175// Danach wird es fortgesetzt bzw. neugestartet. Dies gilt auch fuer Fall,
176// dass STARTTLS verhandelt wurde, aber die TLS-Verhandlung noch nicht
177// laeuft. (Bemerkung: beides pruefen ist nicht ueberfluessig. Den Zustand
178// der Telnet-Option muss man pruefen, weil der Client evtl. seine
179// Verhandlung noch nicht signalisiert hat (FOLLOWS vom Client) und die
180// efun muss man pruefen, weil nach Empfang von FOLLOWS vom Client der
181// Status der Telnet-Optiosn resettet wurde - standardkonform.)
182private int check_tls_negotiation()
183{
184 struct telopt_s s_tls = query_telnet_neg()[TELOPT_STARTTLS];
185 if (tls_query_connection_state(this_object()) < 0
186 || (structp(s_tls) && s_tls->state->remoteside) )
187 return 1;
188
189 return 0;
190}
MG Mud User88f12472016-06-24 23:31:02 +0200191
192/*
193 * This is the function that gets called by /secure/master for every user
194 */
195public nomask int logon()
196{
Zesstrae037c1d2020-02-02 22:02:14 +0100197 set_next_reset(300); // Timeout fuer Loginverfahren
MG Mud User88f12472016-06-24 23:31:02 +0200198 loginname = "logon";
199 newbie=0;
200 realip="";
201
Zesstrae037c1d2020-02-02 22:02:14 +0100202 SendTelopts();
203 // In theory, we should not send anything if SendTelops() offers
204 // TELOPT_STARTTLS. However, some clients to not answer unknown telnet
205 // options and it would introduce a delay in any case. Therefore we send
206 // the welcome message anway, unless we received a WILL from the Client.
207
208 // Es wird ein Lookup gemacht, ob die Quelladresse ein Tor-Exitnode ist,
209 // der erlaubt, zu uns zu kommunizieren. Das Lookup ist asynchron und
210 // braucht eine Weile, wenn das Ergebnis noch nicht gecacht ist.
211 // An dieser Stelle wird das Ergebnis nicht ausgewertet. Achja, wie
MG Mud User88f12472016-06-24 23:31:02 +0200212 // machen das natuerlich nicht fuer die IP vom Mudrechner...
213 if (query_ip_number(this_object()) != "87.79.24.60")
214 {
Zesstrae037c1d2020-02-02 22:02:14 +0100215 "/p/daemon/dnslookup"->check_tor(query_ip_number(this_object()),
216 query_mud_port());
MG Mud User88f12472016-06-24 23:31:02 +0200217 "/p/daemon/dnslookup"->check_dnsbl(query_ip_number(this_object()));
218 }
Zesstrae037c1d2020-02-02 22:02:14 +0100219
220 // ggf. muss TLS (initiiert durch STARTTLS) noch ausverhandelt werden.
221 if (check_tls_negotiation())
222 return 1; // Verbindung behalten
223
MG Mud User88f12472016-06-24 23:31:02 +0200224 printf("HTTP/1.0 302 Found\n"
225 "Location: http://mg.mud.de/\n\n"
226 "NetCologne, Koeln, Germany. Local time: %s\n\n"
227 MUDNAME" LDmud, NATIVE mode, driver version %s\n\n",
228 strftime("%c"), __VERSION__);
229
MG Mud User88f12472016-06-24 23:31:02 +0200230 if ( check_too_many_logons() ){
231 destruct(this_object());
232 return 0;
233 }
234
235 // ist die Verbindung schon wieder weg?
Zesstrae037c1d2020-02-02 22:02:14 +0100236 if (!objectp(this_object()) || !interactive(this_object()))
237 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200238
Zesstrae037c1d2020-02-02 22:02:14 +0100239
240 cat( "/etc/WELCOME" );
MG Mud User88f12472016-06-24 23:31:02 +0200241 input_to( "logon2", INPUT_PROMPT,
242 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
MG Mud User88f12472016-06-24 23:31:02 +0200243 return 1;
244}
245
246
247static int check_too_many_from_same_ip()
248{
249 object *u;
250 string ip;
251
252 ip = query_ip_number(this_object());
253 u = filter(users(), function status (object ob, string addr, int a) {
254 return query_ip_number(ob) == addr
255 && ob->QueryProp(P_AGE) < a;
256 }, ip, 12*60*60); // 24h in heart_beats
257
258 if ( sizeof(u) > 25 ){
259 write( "\nDa anscheinend gerade jemand von Deiner Adresse aus "
260 "versucht, das \n"MUDNAME" mit neuen Charakteren zu "
261 "ueberschwemmen, werden momentan \nnur aeltere Charaktere "
262 "von dieser Adresse zugelassen.\nWenn Du meinst, dass es "
263 "sich um einen Fehler handelt, logg Dich bitte als \n"
264 "Gast ein und sprich einen Erzmagier oder Gott an.\n" );
265
266 log_file( "LOGIN_DENY", sprintf( "%s: >10 Spieler von %-15s (%s)\n",
267 ctime(time())[4..15],
268 query_ip_number(this_object()),
269 query_ip_name(this_object()) ) );
270
271 destruct(this_object());
272 return 1;
273 }
274 else
275 return 0;
276}
277
278
279static int check_illegal( string str )
280{
281 string res;
282
283 res = (string)master()->QuerySBanished(query_ip_number(this_object()));
284 if (!res)
285 {
286 // check connection from Tor exit node
287 string eff_ip = (realip!="" ? realip : query_ip_number(this_object()));
288 if ("/p/daemon/dnslookup"->check_tor(eff_ip, query_mud_port())
289 || "/p/daemon/dnslookup"->check_dnsbl(eff_ip))
290 res =
291 "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
292 "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
293 "die Moeglichkeit,\neinfach neue Charaktere "
294 "anzulegen, fuer diese Adresse gesperrt.\n\n"
295 "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
296 "an\n\n mud@mg.mud.de\n\n"
297 "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
298 }
299
300 if ( res )
301 {
302 write( res );
303 log_file( "LOGIN_DENY", sprintf( "%s: %-11s %-15s (%s)\n",
304 ctime(time())[4..15], str,
305 query_ip_number(this_object()),
306 query_ip_name(this_object()) ) );
307 remove();
308 return 1;
309 }
310
311 return 0;
312}
313
314
315/*
316 * Check that a player name is valid. Only allow
317 * lowercase letters.
318 */
319static int valid_name( string str )
320{
321 int i;
322
323 if ( str == "logon" ){
324 write( "Der Name wuerde nur Verwirrung stiften.\n" );
325 return 0;
326 }
327
328 i = sizeof(str);
329
330 if ( i > 11 ){
331 write( "Dein Name ist zu lang, nimm bitte einen anderen.\n" );
332 return 0;
333 }
334
335 for ( ; i--; )
336 if ( str[i] < 'a' || str[i] > 'z' ) {
337 write( "Unerlaubtes Zeichen '" + str[i..i] + "' im Namen: " + str
338 + "\n" );
339 write( "Benutze bitte nur Buchstaben ohne Umlaute.\n" );
340 return 0;
341 }
342
343 return 1;
344}
345
Zesstrab75a89c2020-01-31 18:47:57 +0100346static void logon2( string str )
MG Mud User88f12472016-06-24 23:31:02 +0200347{
348 int i, arg;
349 mixed txt;
350
351 if ( !str || str == "" ){
352 write( "Abbruch!\n" );
353 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100354 return;
MG Mud User88f12472016-06-24 23:31:02 +0200355 }
356
MG Mud User88f12472016-06-24 23:31:02 +0200357 if(strstr(str,SSL_GRRETING)==0)
358 {
359 if( member(PROXIES,query_ip_number(this_object()))>-1 )
360 {
361 realip=str[sizeof(SSL_GRRETING)..];
362 } // andere IPs werden einfach ignoriert. -> log/PROXY.REQ ?
363 // ggf. Lookup fuer Torexits anstossen.
364 "/p/daemon/dnslookup"->check_tor(realip,query_mud_port());
365 "/p/daemon/dnslookup"->check_dnsbl(realip);
366
367 input_to( "logon2", INPUT_PROMPT,
368 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100369 return;
MG Mud User88f12472016-06-24 23:31:02 +0200370 }
371
372 if ( loginname != "logon" ) {
373 log_file( "ILLEGAL", sprintf( "%s Illegal patch of login: "
374 "loginname = %O\n",
375 dtime(time()), loginname ) );
376 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100377 return;
MG Mud User88f12472016-06-24 23:31:02 +0200378 }
379
380 str = lower_case(str);
381 cap_name = capitalize(str);
382
383 if ( str == "neu" && !neu ){
384 cat( "/etc/WELCOME_NEW" );
385 neu = 1;
386 input_to( "logon2", INPUT_PROMPT, "Name: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100387 return;
MG Mud User88f12472016-06-24 23:31:02 +0200388 }
389
390 if ( !valid_name(str) ){
391 string pr;
392 if ( !neu )
393 pr= "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ";
394 else
395 pr= "Bitte gib Dir einen anderen Namen: ";
396
397 input_to( "logon2", INPUT_PROMPT, pr );
Zesstrab75a89c2020-01-31 18:47:57 +0100398 return;
MG Mud User88f12472016-06-24 23:31:02 +0200399 }
400
401 if ( sscanf( str, "gast%d", arg ) == 1 ){
402 write( "Du meinst wohl 'Gast' ...\n" );
403 str = "gast";
404 }
405
406 loginname = str;
407
408 /* read the secure save file to see if character already exists */
Zesstra313eee92017-01-31 14:55:08 +0100409 string ssavef=master()->secure_savefile(loginname);
410 if ( loginname != "gast"
411 && (!ssavef || !sizeof(ssavef) || !restore_object(ssavef) ))
Zesstrae43dd602017-01-31 10:48:10 +0100412 {
MG Mud User88f12472016-06-24 23:31:02 +0200413 object *user;
414
415 if ( !neu )
416 {
417 write( "Es existiert kein Charakter mit diesem Namen.\n" );
418 write( "Falls Du einen neuen Charakter erschaffen moechtest, "
419 "tippe bitte \"neu\" ein.\n" );
420
421 loginname = "logon";
422 input_to( "logon2", INPUT_PROMPT,
423 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100424 return;
MG Mud User88f12472016-06-24 23:31:02 +0200425 }
426
427 for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
428 if ( object_name(user[i])[0..12] == "/secure/login" &&
429 ((string)user[i]->loginname()) == loginname ){
430 write( "Eine Anmeldung fuer diesen Namen laeuft bereits.\n" );
431 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100432 return;
MG Mud User88f12472016-06-24 23:31:02 +0200433 }
434
435 // Site-Banish checken
436 if ( check_illegal(loginname))
Zesstrab75a89c2020-01-31 18:47:57 +0100437 return;
MG Mud User88f12472016-06-24 23:31:02 +0200438
439 if ( check_too_many_from_same_ip() )
Zesstrab75a89c2020-01-31 18:47:57 +0100440 return;
MG Mud User88f12472016-06-24 23:31:02 +0200441
442 /* new character */
443 if ( sizeof(loginname) < 3 ){
444 write( "Der Name ist zu kurz.\n" );
445 loginname = "logon";
446 input_to( "logon2", INPUT_PROMPT,
447 "Versuch einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100448 return;
MG Mud User88f12472016-06-24 23:31:02 +0200449 }
450
451
452 if ( (txt = (string)master()->QueryBanished(loginname)) ){
453 if ( txt != "Dieser Name ist gesperrt." )
454 txt = sprintf("Hoppla - dieser Name ist reserviert oder gesperrt "
455 "(\"gebanisht\")!\nGrund: %s\n",txt);
456 else
457 txt = "Hoppla - dieser Name ist reserviert oder gesperrt "
458 "(\"gebanisht\")!\n";
459 write(txt);
460 loginname = "logon";
461 input_to( "logon2", INPUT_PROMPT,
462 "Bitte gib Dir einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100463 return;
MG Mud User88f12472016-06-24 23:31:02 +0200464 }
465
466 /* Initialize the new secure savefile */
467 name = loginname;
468 password = "";
469 level = 0;
470 domains = ({ });
471 guilds = ({ });
472 shell = "";
473 ep = "";
474 ek = "";
475 mq = "";
476 ektips="";
477 fptips="";
478 creation_date = time();
479
480 input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
481 "Waehle ein Passwort: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100482 return;
MG Mud User88f12472016-06-24 23:31:02 +0200483 }
484 else {
485 if ( loginname == "gast" ){
486 if ( check_illegal(loginname) )
Zesstrab75a89c2020-01-31 18:47:57 +0100487 return;
MG Mud User88f12472016-06-24 23:31:02 +0200488
489 load_player_object(1);
Zesstrab75a89c2020-01-31 18:47:57 +0100490 return;
MG Mud User88f12472016-06-24 23:31:02 +0200491 }
492
493 if ( neu ){
494 write( "Es existiert bereits ein Charakter dieses Namens.\n" );
495 loginname = "logon";
496 input_to( "logon2", INPUT_PROMPT,
497 "Gib Dir einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100498 return;
MG Mud User88f12472016-06-24 23:31:02 +0200499 }
500
501 if ( (int)master()->check_late_player(loginname) )
502 {
503 write( "Dieser Spieler hat uns leider fuer immer verlassen.\n" );
504 loginname = "logon";
505 input_to( "logon2", INPUT_PROMPT,
506 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100507 return;
MG Mud User88f12472016-06-24 23:31:02 +0200508 }
509
510 if ( txt = (string)master()->QueryTBanished(loginname) ){
511 write( txt );
512 loginname = "logon";
513 input_to( "logon2", INPUT_PROMPT,
514 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100515 return;
MG Mud User88f12472016-06-24 23:31:02 +0200516 }
517
518 if ( creation_date > (time() - 30*24*60*60)
519 && check_too_many_from_same_ip() )
Zesstrab75a89c2020-01-31 18:47:57 +0100520 return;
MG Mud User88f12472016-06-24 23:31:02 +0200521
522 write( "Schoen, dass Du wieder da bist, "+capitalize(loginname)+"!\n" );
523
524 if ( !stringp(password) || password == "" ) {
525 write( "Du hast KEIN PASSWORD!\n" );
526 write( "Benutze den \"password\"-Befehl, um das zu aendern !\n" );
527 load_player_object(0);
Zesstrab75a89c2020-01-31 18:47:57 +0100528 return;
MG Mud User88f12472016-06-24 23:31:02 +0200529 }
530
531 input_to( "check_password", INPUT_NOECHO|INPUT_PROMPT,
532 "Passwort: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100533 return;
MG Mud User88f12472016-06-24 23:31:02 +0200534 }
535}
536
537
538static int new_password( string str )
539{
540 write( "\n" );
541
542 if ( !str || str == "" )
543 return remove();
544
545 password = str;
546
547 if ( !master()->good_password( str, loginname ) ) {
548 input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
549 "Bitte gib ein Passwort an: ");
550 return 1;
551 }
552
553 write( "\nZur Erinnerung: Es ist v e r b o t e n, andere Spieler "
554 "anzugreifen!\n" );
555 write( "Das gilt auch fuer Froesche, bei denen \"Ein Frosch namens "
556 "XXXXX\" steht.\n\n" );
557 input_to( "again_password", INPUT_NOECHO|INPUT_PROMPT,
558 "Passwort bitte nochmal eingeben: ");
559 return 1;
560}
561
562static int again_password( string str )
563{
564 write( "\n" );
565
566 if ( str != password ){
567 write( "Die Passwoerter stimmten nicht ueberein!\n" );
568 destruct( this_object() );
569 return 1;
570 }
571
Zesstra28d72e52016-10-27 23:56:19 +0200572 set_next_reset(600);
MG Mud User88f12472016-06-24 23:31:02 +0200573 password = md5_crypt( password, 0 );
574 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
575 master()->RemoveFromCache( loginname );
576
577 load_player_object(0);
578 return 1;
579}
580
581static int check_password( string str )
582{
583 write( "\n" );
584
585 // Invis einloggen?
586 if (sizeof(str) > 1 && str[0] == '-') {
587 invis = 1;
588 str = str[1..];
589 }
590
591 // welcher Hash ists denn?
592 if (sizeof(password) > 13) {
593 // MD5-Hash
594 str = md5_crypt(str, password);
595 }
596 else if (sizeof(password) > 2) {
597 // Crypt-Hash
598 str = crypt(str, password[0..1]);
599 }
600 else {
601 // keiner von beiden Hashes -> ungueltiges PW
602 str = 0;
603 }
604
605 if ( !stringp(str) || str != password ) {
606 // Hashes stimmen nicht ueberein -> und schluss...
607 write( "Falsches Passwort!\n");
608 if ( loginfails > 2 )
609 write(break_string(
610 "Solltest Du weiterhin Probleme mit dem Einloggen haben, kannst "
611 "Du Dein Passwort zuruecksetzen lassen, indem Du Dich als Gast "
612 "anmeldest und einen Erzmagier ansprichst, oder indem Du Dich "
Zesstra0559c662017-02-06 19:14:51 +0100613 "mittels einer E-Mail an mud@mg.mud.de mit uns in Verbindung "
MG Mud User88f12472016-06-24 23:31:02 +0200614 "setzt.",78));
615
616 log_file( (level < 60 ? "LOGINFAIL" : "ARCH/LOGINFAIL"),
617 sprintf( "PASSWORD: %-11s %s, %-15s (%s)\n",
618 loginname, ctime(time())[4..15],
619 query_ip_number(this_object()),
620 query_ip_name(this_object()) ), 200000 );
621
622 loginfails++;
623 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
624 master()->RemoveFromCache( loginname );
625 destruct( this_object() );
626 return 1;
627 }
628
629 if ( loginfails ) {
630 write( loginfails + " fehlgeschlagene" + (loginfails == 1 ? "r" : "") +
631 " Login-Versuch" + (loginfails == 1 ? "" : "e") +
632 " seit dem letzten erfolgreichen Login.\n" );
633 loginfails = 0;
634 }
635
636 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
637 master()->RemoveFromCache( loginname );
638
639 load_player_object(0);
640 return 1;
641}
642
643
644static void select_race()
645{
646 int i;
Zesstra7e353662019-11-27 19:52:41 +0100647 string race;
648 int selectable;
MG Mud User88f12472016-06-24 23:31:02 +0200649
650 races = get_dir( "/std/shells/*.c" );
651
652 // Mensch soll immer als erstes in der Auswahlliste stehen.
653 if (member(races,"human.c")!=-1)
654 races=({"human.c"})+(races-({"human.c"}));
655
656 for ( i = sizeof(races); i--; ){
657 races[i] = "/std/shells/" + races[i][0..<3];
Zesstra7e353662019-11-27 19:52:41 +0100658 selectable = 0;
659 race = 0;
660 if ( catch(selectable = ({int})call_other( races[i],
661 "QueryAllowSelect" ); publish)
662 || !selectable)
663 selectable = 0;
664 else if ( catch(race = ({string})call_other(races[i],
665 "QueryProp", P_RACE );publish) )
666 race = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200667
Zesstra7e353662019-11-27 19:52:41 +0100668 if ( !selectable || !sizeof(race) )
MG Mud User88f12472016-06-24 23:31:02 +0200669 races[i..i] = ({});
670 else
Zesstra7e353662019-11-27 19:52:41 +0100671 races[i] = ({ races[i], race });
MG Mud User88f12472016-06-24 23:31:02 +0200672 }
673
674 if ( sizeof(races) == 1 ){
675 write( "Es gibt nur eine Rasse, Du hast also keine Wahl.\n" );
676
677 shell = races[0][0];
678 master()->set_player_object( loginname, shell );
679
680 return load_player_ob_2( shell, 0 );
681 }
682
683 return ask_mud_played_question();
684}
685
686static void ask_mud_played_question()
687{
688 write(break_string(
689 "\nWenn Du ein absoluter Neuling in diesem Spiel bist moechten "
690 "wir Dir mit einigen Tips zu Beginn beiseite stehen.\n\n",78,
691 0,BS_LEAVE_MY_LFS));
692 input_to( "get_mud_played_answer", INPUT_PROMPT,
693 "Hast Du schon einmal in einem MUD gespielt? (ja,nein): ");
694 return;
695}
696
697static void ask_race_question()
698{
699 int i, j;
700
701 write( break_string( "Du musst Dich jetzt entscheiden, welcher Rasse Du "
702 "in dieser Welt angehoeren moechtest. Alle Rassen "
703 "haben verschiedene Vor- und Nachteile, insgesamt "
704 "aber gleich gute Chancen. Auch das Startgebiet "
705 "haengt von der ausgewaehlten Rasse ab. Im "
706 "Normalfall kann die Rasse nicht mehr gewechselt "
707 "werden, nachdem sie einmal ausgewaehlt wurde. "
708 "Ueberlege Dir Deine Entscheidung also gut. Derzeit "
709 "stehen folgende Rassen zur Auswahl:\n\n", 78 ) );
710
711 for ( i = 0, j = sizeof(races); i < j; i++ )
712 printf( "% 2d. %-30s %s", i+1, capitalize(races[i][1]),
713 (i % 2 ? "\n" : "| ") );
714
715 if ( sizeof(races) % 2 )
716 write( "\n" );
717
718 write( break_string( "\nDurch Eingabe einer Ziffer waehlst Du die Rasse "
719 "aus, durch Eingabe eines \"\?\" gefolgt von einer "
720 "Ziffer erhaeltst Du naehere Informationen ueber "
721 "eine Rasse. Ein \"\?\" allein wiederholt diese "
722 "Liste.", 78, 0, 1 ) );
723
724 if (newbie)
725 {
726 write(break_string("\nAls Neuling solltest Du Dich NICHT fuer "
727 "die Dunkelelfen entscheiden. Diese "
728 "Rasse hat einige Probleme im Umgang "
729 "mit den anderen Rassen und mit dem "
730 "Sonnenlicht.",78,0,BS_LEAVE_MY_LFS));
731 }
732
733 input_to( "get_race_answer", INPUT_PROMPT,
734 "\nWas willst Du tun: ");
735 return;
736}
737
738
739static void get_race_answer( string str )
740{
741 int num;
742
743 if ( str == "?" )
744 return ask_race_question();
745
746 if ( sscanf( str, "?%d", num ) ){
747 if ( num < 1 || num > sizeof(races) ){
748 write( "Das geht nicht.\n\n");
749 input_to( "get_race_answer", INPUT_PROMPT,
750 "Was willst Du tun: ");
751 return;
752 }
753
754 write( call_other( races[num - 1][0], "QueryProp", P_RACE_DESCRIPTION ));
755 input_to( "get_race_answer", INPUT_PROMPT,
756 "\nWas willst Du tun: ");
757 return;
758 }
759
760 if ( sscanf( str, "%d", num ) && num >= 1 && num <= sizeof(races) ){
761 write( "Ok, Du bist jetzt ein "
762 + capitalize(races[num-1][1]) + ".\n" );
763
764 shell = races[num-1][0];
765 master()->set_player_object( loginname, shell );
766 return load_player_ob_2( shell, 0 );
767 }
768
769 write("Wie bitte?\n\n" );
770 input_to( "get_race_answer", INPUT_PROMPT,
771 "Was willst Du tun: ");
772}
773
774static void get_mud_played_answer (string str)
775{
776 if ( str == "ja" || str=="j")
777 {
778 newbie=0;
779 return ask_race_question();
780 }
781 if ( str != "nein" && str!="n")
782 {
783 write("\n\nAntworte bitte mit ja oder nein.\n\n");
784
785 return ask_mud_played_question();
786 }
787 newbie=1;
788 write("\n\nEine kleine Einfuehrung in das "MUDNAME" bekommst "
789 "Du auch hier:\n\n"
790 "http://mg.mud.de/newweb/hilfe/tutorial/inhalt.shtml\n\n");
791 return ask_race_question();
792
793}
794
795static int load_player_object( int guestflag )
796{
797 object ob;
798 string fname;
799 int was_interactive;
800
801 if ( sizeof(users()) >= 195 && !IS_WIZARD(loginname) ){
802 write( "Die maximale Spielerzahl wurde bereits erreicht!!!\n"
803 "Aus technischen Gruenden duerfen sich nur noch Magier "
804 "einloggen.\nVersuch es spaeter noch einmal ...\n" );
805 destruct( this_object() );
806 return 1;
807 }
808 else if ( sizeof(users()) >= 198 && !IS_ARCH(loginname) ){
809 write( "Die maximale Spieler- und Magierzahl wurde bereits erreicht!!!"
810 "\nAus technischen Gruenden duerfen sich nur noch Erzmagier "
811 "einloggen.\nVersuch es spaeter noch einmal ...\n" );
812 destruct( this_object() );
813 return 1;
814 }
815
816 if ( file_size("/etc/NOLOGIN")>=0 )
817 {
818 if (file_size("/etc/NOLOGIN.info")>0) {
819 //NOLOGIN.info enthaelt evtl. weitergehende Informationen fuer
820 //Spieler, z.B. vorrauss. Wiederverfuegbarkeit.
821 write(break_string(read_file("/etc/NOLOGIN.info"),78,"",
822 BS_LEAVE_MY_LFS|BS_SINGLE_SPACE));
823 }
824 else {
825 //sonst Standardmeldung ausgeben.
826 write ("\nAufgrund von technischen Problemen ist das Einloggen ins "
827 MUDNAME" zur \nZeit nicht moeglich. Bitte versuch es "
828 "spaeter noch einmal.\n\n");
829 }
830 if ( IS_ARCH(loginname) ||
831 member(explode(read_file("/etc/NOLOGIN")||"","\n"),
832 loginname)!=-1 )
833 {
834 write("Im Moment koennen nur Erzmagier einloggen. Um Spieler "
835 "wieder ins Spiel zu lassen, muss die Datei '/etc/NOLOGIN' "
836 "geloescht werden.\n\n ");
837 } else {
838 destruct( this_object() );
839 return 1;
840 }
841 }
842
843 if ( guestflag ){
844 if ( catch(guestflag = (int)GUESTMASTER->new_guest();publish)
845 || !guestflag ){
846 write( "Derzeit ist kein Gastlogin moeglich!\n" );
847 destruct( this_object() );
848 return 1;
849 }
850
851 loginname = "gast" + guestflag;
852 cap_name = capitalize(loginname);
853 name = cap_name;
854
855 if ( !(ob = find_player(loginname) || find_netdead(loginname)) ){
856 object *user;
857 int i;
858
859 // gegen Horden von Gast1 - wenn ein Gast noch am Prompt fuer
860 // das Geschlecht haengt, ist er ueber find_player() noch nicht
861 // zu finden ...
862 for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
863 if ( object_name(user[i])[0..11] == "/std/shells/" &&
864 getuid(user[i]) == loginname ){
865 ob = user[i];
866 break;
867 }
868 }
869
870 if ( ob ){
871 tell_object( ob, "Ein anderer Spieler moechte diesen Gastzugang "
872 "jetzt benutzen. Wenn es Dir hier\ngefallen hat, "
873 "ueberleg Dir doch einen Charakternamen und komm "
874 "unter diesem\nNamen wieder!\n" );
875 destruct(ob);
876 }
877
878 load_player_ob_2( "std/shells/human", guestflag );
879
880 return 1;
881 }
882 else {
883 /* Test if we are already playing */
884 was_interactive = 0;
885 ob = find_player(loginname) || find_netdead(loginname);
886
887 if (ob) {
888 write( "Du nimmst schon am Spiel teil!\n" );
889 write( "Verwende Deine alte sterbliche Huelle ...\n" );
890
891 if ( interactive(ob) )
892 {
893 /* The other object is still interactive; reconnect that "soul"
894 to a dummy object and destruct that, thus disconnecting the
895 other probably linkdead user. The real "body" is still
896 there for reconnecting by login.c */
897 remove_interactive(ob);
898 was_interactive = 1;
899 }
900 // Wenn Invislogin, P_INVIS setzen.
901 if ( invis && IS_WIZARD(ob) )
902 {
903 ob->SetProp( P_INVIS, ob->QueryProp(P_AGE) );
904 tell_object( ob, "DU BIST UNSICHTBAR!\n" );
905 }
906 /* Now reconnect to the old body */
907 exec( ob, this_object() );
908 ob->set_realip(realip);
909 if ( ((int)ob->QueryProp(P_LEVEL)) == -1 )
910 ob->start_player( cap_name );
911 else
912 ob->Reconnect( was_interactive );
913
914 call_out( "remove", 2 );
915 return 1;
916 }
917 }
918
919 /* read player object from passwd file */
920 if ( stringp(shell) && shell != "" )
921 load_player_ob_2 ( shell, 0 );
922 else
923 select_race();
924
925 return 1;
926}
927
928
929static void load_player_ob_2( string obname, int guestflag )
930{
931 object blueprint;
932 string err, ob_name;
933 object ob, old_ob;
934
935 if (!interactive()) {
936 destruct(this_object());
937 return;
938 }
939 /* start player activity */
Zesstrae88826c2016-09-24 20:37:05 +0200940 log_file( "syslog/shell/ENTER", sprintf( "%-11s %s, %-15s (%s).\n",
MG Mud User88f12472016-06-24 23:31:02 +0200941 capitalize(name), ctime(time())[4..15],
942 query_ip_number(this_object()),
943 query_ip_name(this_object()) ), 200000 );
944
945 seteuid(loginname);
946
947 /* load the "real" player object */
948 /* If some asshole has moved the blueprint */
949 if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
950 destruct(blueprint);
951
952 if ( err = catch(ob = clone_object(obname);publish) ){
953 log_file( "SHELLS", "Failed to load shell " + obname + ", " +
954 dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
955
956 write( "Konnte das passende Playerobjekt nicht laden. Lade "
957 "stattdessen\ndas Objekt Mensch. BITTE ERZMAGIER "
958 "BENACHRICHTIGEN !\n" );
959 err = catch(ob = clone_object("/std/shells/human");publish);
960 }
961
962 if ( !ob || err ) {
963 write( "Error on loading " + shell + "\nError = " + err + "\n" );
964 destruct( this_object() );
965 return;
966 }
967
968 if ( guestflag )
969 catch( GUESTMASTER->set_guest( guestflag, ob );publish );
970
971 ob_name = explode( object_name(ob), "#" )[0];
972
973 if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
974 ob_name = ob_name[11..];
975
976 ob_name = ob_name + ":" + loginname;
977
978 if( !guestflag )
979 {
980 if ( old_ob = find_object(ob_name) )
981 {
982 catch(old_ob->remove();publish);
983
984 if ( old_ob )
985 destruct( old_ob );
986 }
987 rename_object( ob, ob_name );
988 ob->__reload_explore();
989 }
990 exec( ob, this_object() );
991 ob->set_realip(realip);
992 ob->start_player( cap_name );
Zesstraab834bb2020-01-21 13:11:45 +0100993 // Hinweis: Das Spielerobjekt holt sich in updates_after_restore() von hier
994 // den Status von invis und setzt ggf. P_INVIS, ausserdem den Status der
995 // Telnet Negotiations.
MG Mud User88f12472016-06-24 23:31:02 +0200996
MG Mud User88f12472016-06-24 23:31:02 +0200997 // wenn der Spieler noch nicht im Mud gespielt hat, wird die aktuelle Zeit
998 // in die entsprechende Prop geschrieben. Die Prop ist transient und wird
999 // absichtlich nicht gespeichert.
1000 if (newbie)
1001 ob->SetProp("_lib_mud_newbie", creation_date);
1002
1003 destruct( this_object() );
1004}
1005
1006
1007/*
1008 * With arg = 0 this function should only be entered once!
1009 */
1010protected void create()
1011{
1012 loginname = "logon";
1013 creation_date = -1;
1014 catch( load_object( "/secure/merlin");publish );
1015 loginfails = 0;
1016 realip="";
1017 if (clonep())
1018 set_next_reset(900);
1019 else
1020 set_next_reset(-1);
1021}
1022
1023void reset()
1024{
1025 if (clonep())
Zesstra28d72e52016-10-27 23:56:19 +02001026 {
1027 if (interactive(this_object()))
1028 tell_object(this_object(),"Time out!");
MG Mud User88f12472016-06-24 23:31:02 +02001029 remove();
Zesstra28d72e52016-10-27 23:56:19 +02001030 }
MG Mud User88f12472016-06-24 23:31:02 +02001031}
1032
1033public string short()
1034{
1035 return "<Einloggender Teilnehmer>.\n";
1036}
1037
1038
1039public string query_real_name()
1040{
1041 return "<logon>";
1042}
1043
1044
1045public nomask int query_prevent_shadow()
1046{
1047 return 1;
1048}
1049
1050
MG Mud User88f12472016-06-24 23:31:02 +02001051// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1052// im Loginprozess involviert.
1053public mixed new_logon( string str)
1054{
1055 seteuid(getuid()); // sonst funkt ARCH_SECURITY nicht
1056
1057 if ( !ARCH_SECURITY || process_call() ){
1058 write( "Nur fuer Erzmagier erlaubt!\n" );
1059 destruct( this_object() );
1060 return -1;
1061 }
1062
1063 if ( !str || str == "" ){
1064 write( "Kein Name angegeben!\n" );
1065 destruct( this_object() );
1066 return 0;
1067 }
1068
1069 str = lower_case(str);
1070 cap_name = capitalize(str);
1071
1072 loginname = str;
1073 seteuid(ROOTID);
1074
1075 /* read the secure save file to see if character already exists */
1076 if ( !restore_object( master()->secure_savefile(loginname) ) ){
1077 write( "Kein solcher Spieler!\n" );
1078 destruct( this_object() );
1079 return 0;
1080 }
1081 else {
1082 write( "Ok, der Spieler " + capitalize(str) + " existiert!\n" );
1083 return new_load_player_object();
1084 }
1085}
1086
1087// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1088// im Loginprozess involviert.
1089static mixed new_load_player_object()
1090{
1091 if ( find_player(loginname) || find_netdead(loginname) ){
1092 write( "Der Spieler ist bereits online oder netztot!\n" );
1093 destruct( this_object() );
1094 return 2;
1095 }
1096
1097 /* read player object from passwd file */
1098 if ( stringp(shell) && shell != "" )
1099 return new_load_player_ob_2( shell );
1100 else {
1101 write( "Keine Shell angegeben!\n" );
1102 destruct( this_object() );
1103 return 0;
1104 }
1105}
1106
1107// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1108// im Loginprozess involviert.
1109static mixed new_load_player_ob_2( string obname )
1110{
1111 object blueprint;
1112 string err, ob_name;
1113 object ob, old_ob;
1114
1115 seteuid(loginname);
1116
1117 /* load the "real" player object */
1118 /* If some asshole has moved the blueprint */
1119 if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
1120 destruct( blueprint );
1121
1122 err = catch(ob = clone_object(obname);publish);
1123
1124 if ( err ){
1125 log_file( "SHELLS", "Failed to load shell " + obname + ", " +
1126 dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
1127
1128 write( "Konnte das passende Playerobjekt nicht laden. "
1129 "Lade stattdessen\ndas Objekt Mensch!\n" );
1130
1131 err = catch(ob = clone_object( "std/shells/human" );publish );
1132 }
1133
1134 if ( !ob || err ){
1135 write( "Error on loading " + shell + "\nError = " + err + "\n" );
1136 destruct( this_object() );
1137 return 0;
1138 }
1139
1140 ob_name = explode( object_name(ob), "#" )[0];
1141
1142 if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
1143 ob_name = ob_name[11..];
1144
1145 ob_name = ob_name + ":" + loginname;
1146
1147 if ( old_ob = find_object(ob_name) ){
1148 catch( old_ob->remove(); publish );
1149
1150 if ( old_ob )
1151 destruct( old_ob );
1152 }
1153
1154 rename_object( ob, ob_name );
1155 ob->__reload_explore();
1156 ob->set_realip(realip);
1157 ob->start_player(cap_name);
1158 ob->SetProp( "creation_date", creation_date );
1159 ob->Set( "creation_date", SAVE|SECURED|PROTECTED, F_MODE_AS );
1160
1161 ob->move( "/room/nowhere", M_NOCHECK );
1162 set_object_heart_beat( ob, 0 );
1163 destruct( this_object() );
1164
1165 return ob;
1166}
1167
1168string query_realip()
1169{
1170 return realip ? realip : "";
1171}
1172
1173int query_invis()
1174{
1175 return invis;
1176}
1177