blob: 3cd2b218115522a8a0f40d1df65ed9be5646990b [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="
Zesstrad771f4e2025-06-27 19:39:13 +020046#define PROXIES ({"127.0.0.1",__HOST_IP_NUMBER__})
MG Mud User88f12472016-06-24 23:31:02 +020047#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
MG Mud User88f12472016-06-24 23:31:02 +0200172/*
173 * This is the function that gets called by /secure/master for every user
174 */
175public nomask int logon()
176{
Zesstrae037c1d2020-02-02 22:02:14 +0100177 set_next_reset(300); // Timeout fuer Loginverfahren
MG Mud User88f12472016-06-24 23:31:02 +0200178 loginname = "logon";
179 newbie=0;
Zesstraca502032020-02-05 19:56:09 +0100180 realip=0;
MG Mud User88f12472016-06-24 23:31:02 +0200181
Zesstrae037c1d2020-02-02 22:02:14 +0100182 SendTelopts();
183 // In theory, we should not send anything if SendTelops() offers
184 // TELOPT_STARTTLS. However, some clients to not answer unknown telnet
185 // options and it would introduce a delay in any case. Therefore we send
186 // the welcome message anway, unless we received a WILL from the Client.
187
188 // Es wird ein Lookup gemacht, ob die Quelladresse ein Tor-Exitnode ist,
189 // der erlaubt, zu uns zu kommunizieren. Das Lookup ist asynchron und
190 // braucht eine Weile, wenn das Ergebnis noch nicht gecacht ist.
191 // An dieser Stelle wird das Ergebnis nicht ausgewertet. Achja, wie
MG Mud User88f12472016-06-24 23:31:02 +0200192 // machen das natuerlich nicht fuer die IP vom Mudrechner...
Zesstrad771f4e2025-06-27 19:39:13 +0200193 if (query_ip_number(this_object()) != __HOST_IP_NUMBER__)
MG Mud User88f12472016-06-24 23:31:02 +0200194 {
bugfixd94d0932020-04-08 11:27:13 +0200195 ({int})"/p/daemon/dnslookup"->check_tor(query_ip_number(this_object()),
Zesstrae037c1d2020-02-02 22:02:14 +0100196 query_mud_port());
bugfixd94d0932020-04-08 11:27:13 +0200197 ({int})"/p/daemon/dnslookup"->check_dnsbl(query_ip_number(this_object()));
MG Mud User88f12472016-06-24 23:31:02 +0200198 }
Zesstrae037c1d2020-02-02 22:02:14 +0100199
200 // ggf. muss TLS (initiiert durch STARTTLS) noch ausverhandelt werden.
201 if (check_tls_negotiation())
202 return 1; // Verbindung behalten
203
MG Mud User88f12472016-06-24 23:31:02 +0200204 printf("HTTP/1.0 302 Found\n"
205 "Location: http://mg.mud.de/\n\n"
Zesstrae90fbb22025-06-29 21:33:48 +0200206 "Local time: %s\n\n"
MG Mud User88f12472016-06-24 23:31:02 +0200207 MUDNAME" LDmud, NATIVE mode, driver version %s\n\n",
208 strftime("%c"), __VERSION__);
209
MG Mud User88f12472016-06-24 23:31:02 +0200210 if ( check_too_many_logons() ){
211 destruct(this_object());
212 return 0;
213 }
214
215 // ist die Verbindung schon wieder weg?
Zesstrae037c1d2020-02-02 22:02:14 +0100216 if (!objectp(this_object()) || !interactive(this_object()))
217 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200218
Zesstrae037c1d2020-02-02 22:02:14 +0100219
220 cat( "/etc/WELCOME" );
MG Mud User88f12472016-06-24 23:31:02 +0200221 input_to( "logon2", INPUT_PROMPT,
222 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
MG Mud User88f12472016-06-24 23:31:02 +0200223 return 1;
224}
225
226
227static int check_too_many_from_same_ip()
228{
229 object *u;
230 string ip;
231
232 ip = query_ip_number(this_object());
233 u = filter(users(), function status (object ob, string addr, int a) {
234 return query_ip_number(ob) == addr
bugfixd94d0932020-04-08 11:27:13 +0200235 && ({int})ob->QueryProp(P_AGE) < a;
MG Mud User88f12472016-06-24 23:31:02 +0200236 }, ip, 12*60*60); // 24h in heart_beats
237
238 if ( sizeof(u) > 25 ){
239 write( "\nDa anscheinend gerade jemand von Deiner Adresse aus "
240 "versucht, das \n"MUDNAME" mit neuen Charakteren zu "
241 "ueberschwemmen, werden momentan \nnur aeltere Charaktere "
242 "von dieser Adresse zugelassen.\nWenn Du meinst, dass es "
243 "sich um einen Fehler handelt, logg Dich bitte als \n"
244 "Gast ein und sprich einen Erzmagier oder Gott an.\n" );
245
246 log_file( "LOGIN_DENY", sprintf( "%s: >10 Spieler von %-15s (%s)\n",
247 ctime(time())[4..15],
248 query_ip_number(this_object()),
249 query_ip_name(this_object()) ) );
250
251 destruct(this_object());
252 return 1;
253 }
254 else
255 return 0;
256}
257
258
259static int check_illegal( string str )
260{
261 string res;
262
Vanion50652322020-03-10 21:13:25 +0100263 res = ({string})master()->QuerySBanished(query_ip_number(this_object()));
MG Mud User88f12472016-06-24 23:31:02 +0200264 if (!res)
265 {
266 // check connection from Tor exit node
Zesstraca502032020-02-05 19:56:09 +0100267 string eff_ip = (sizeof(realip) ? realip
268 : query_ip_number(this_object()));
bugfixd94d0932020-04-08 11:27:13 +0200269 if (({int})"/p/daemon/dnslookup"->check_tor(eff_ip, query_mud_port())
270 || ({int})"/p/daemon/dnslookup"->check_dnsbl(eff_ip))
MG Mud User88f12472016-06-24 23:31:02 +0200271 res =
272 "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
273 "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
274 "die Moeglichkeit,\neinfach neue Charaktere "
275 "anzulegen, fuer diese Adresse gesperrt.\n\n"
276 "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
277 "an\n\n mud@mg.mud.de\n\n"
278 "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
279 }
280
281 if ( res )
282 {
283 write( res );
284 log_file( "LOGIN_DENY", sprintf( "%s: %-11s %-15s (%s)\n",
285 ctime(time())[4..15], str,
286 query_ip_number(this_object()),
287 query_ip_name(this_object()) ) );
288 remove();
289 return 1;
290 }
291
292 return 0;
293}
294
295
296/*
297 * Check that a player name is valid. Only allow
298 * lowercase letters.
299 */
300static int valid_name( string str )
301{
302 int i;
303
304 if ( str == "logon" ){
305 write( "Der Name wuerde nur Verwirrung stiften.\n" );
306 return 0;
307 }
308
309 i = sizeof(str);
310
311 if ( i > 11 ){
312 write( "Dein Name ist zu lang, nimm bitte einen anderen.\n" );
313 return 0;
314 }
315
316 for ( ; i--; )
317 if ( str[i] < 'a' || str[i] > 'z' ) {
318 write( "Unerlaubtes Zeichen '" + str[i..i] + "' im Namen: " + str
319 + "\n" );
Zesstrac84fb7b2021-05-13 15:25:13 +0200320 write("Benutze bitte nur Buchstaben ohne Umlaute und keine "
321 "Ziffern.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200322 return 0;
323 }
324
325 return 1;
326}
327
Zesstrab75a89c2020-01-31 18:47:57 +0100328static void logon2( string str )
MG Mud User88f12472016-06-24 23:31:02 +0200329{
Zesstrac84fb7b2021-05-13 15:25:13 +0200330 int i;
MG Mud User88f12472016-06-24 23:31:02 +0200331 mixed txt;
332
Zesstra4c418f92020-02-03 20:14:35 +0100333 // ggf. muss TLS (initiiert durch STARTTLS) noch ausverhandelt werden.
334 if (check_tls_negotiation())
335 return;
336
MG Mud User88f12472016-06-24 23:31:02 +0200337 if ( !str || str == "" ){
338 write( "Abbruch!\n" );
339 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100340 return;
MG Mud User88f12472016-06-24 23:31:02 +0200341 }
342
MG Mud User88f12472016-06-24 23:31:02 +0200343 if(strstr(str,SSL_GRRETING)==0)
344 {
345 if( member(PROXIES,query_ip_number(this_object()))>-1 )
346 {
347 realip=str[sizeof(SSL_GRRETING)..];
348 } // andere IPs werden einfach ignoriert. -> log/PROXY.REQ ?
349 // ggf. Lookup fuer Torexits anstossen.
bugfixd94d0932020-04-08 11:27:13 +0200350 ({int})"/p/daemon/dnslookup"->check_tor(realip,query_mud_port());
351 ({int})"/p/daemon/dnslookup"->check_dnsbl(realip);
MG Mud User88f12472016-06-24 23:31:02 +0200352
Zesstrafb642a12020-05-19 14:16:24 +0200353 input_to("logon2");
Zesstrab75a89c2020-01-31 18:47:57 +0100354 return;
MG Mud User88f12472016-06-24 23:31:02 +0200355 }
356
357 if ( loginname != "logon" ) {
358 log_file( "ILLEGAL", sprintf( "%s Illegal patch of login: "
359 "loginname = %O\n",
360 dtime(time()), loginname ) );
361 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100362 return;
MG Mud User88f12472016-06-24 23:31:02 +0200363 }
364
365 str = lower_case(str);
366 cap_name = capitalize(str);
367
368 if ( str == "neu" && !neu ){
369 cat( "/etc/WELCOME_NEW" );
370 neu = 1;
371 input_to( "logon2", INPUT_PROMPT, "Name: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100372 return;
MG Mud User88f12472016-06-24 23:31:02 +0200373 }
374
375 if ( !valid_name(str) ){
376 string pr;
377 if ( !neu )
378 pr= "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ";
379 else
380 pr= "Bitte gib Dir einen anderen Namen: ";
381
382 input_to( "logon2", INPUT_PROMPT, pr );
Zesstrab75a89c2020-01-31 18:47:57 +0100383 return;
MG Mud User88f12472016-06-24 23:31:02 +0200384 }
385
MG Mud User88f12472016-06-24 23:31:02 +0200386 loginname = str;
387
388 /* read the secure save file to see if character already exists */
bugfixd94d0932020-04-08 11:27:13 +0200389 string ssavef=({string})master()->secure_savefile(loginname);
Zesstra313eee92017-01-31 14:55:08 +0100390 if ( loginname != "gast"
391 && (!ssavef || !sizeof(ssavef) || !restore_object(ssavef) ))
Zesstrae43dd602017-01-31 10:48:10 +0100392 {
MG Mud User88f12472016-06-24 23:31:02 +0200393 object *user;
394
395 if ( !neu )
396 {
397 write( "Es existiert kein Charakter mit diesem Namen.\n" );
398 write( "Falls Du einen neuen Charakter erschaffen moechtest, "
399 "tippe bitte \"neu\" ein.\n" );
400
401 loginname = "logon";
402 input_to( "logon2", INPUT_PROMPT,
403 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100404 return;
MG Mud User88f12472016-06-24 23:31:02 +0200405 }
406
407 for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
408 if ( object_name(user[i])[0..12] == "/secure/login" &&
Vanion50652322020-03-10 21:13:25 +0100409 (({string})user[i]->loginname()) == loginname ){
MG Mud User88f12472016-06-24 23:31:02 +0200410 write( "Eine Anmeldung fuer diesen Namen laeuft bereits.\n" );
411 destruct( this_object() );
Zesstrab75a89c2020-01-31 18:47:57 +0100412 return;
MG Mud User88f12472016-06-24 23:31:02 +0200413 }
414
415 // Site-Banish checken
416 if ( check_illegal(loginname))
Zesstrab75a89c2020-01-31 18:47:57 +0100417 return;
MG Mud User88f12472016-06-24 23:31:02 +0200418
419 if ( check_too_many_from_same_ip() )
Zesstrab75a89c2020-01-31 18:47:57 +0100420 return;
MG Mud User88f12472016-06-24 23:31:02 +0200421
422 /* new character */
423 if ( sizeof(loginname) < 3 ){
424 write( "Der Name ist zu kurz.\n" );
425 loginname = "logon";
426 input_to( "logon2", INPUT_PROMPT,
427 "Versuch einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100428 return;
MG Mud User88f12472016-06-24 23:31:02 +0200429 }
430
431
Vanion50652322020-03-10 21:13:25 +0100432 if ( (txt = ({string})master()->QueryBanished(loginname)) ){
MG Mud User88f12472016-06-24 23:31:02 +0200433 if ( txt != "Dieser Name ist gesperrt." )
434 txt = sprintf("Hoppla - dieser Name ist reserviert oder gesperrt "
435 "(\"gebanisht\")!\nGrund: %s\n",txt);
436 else
437 txt = "Hoppla - dieser Name ist reserviert oder gesperrt "
438 "(\"gebanisht\")!\n";
439 write(txt);
440 loginname = "logon";
441 input_to( "logon2", INPUT_PROMPT,
442 "Bitte gib Dir einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100443 return;
MG Mud User88f12472016-06-24 23:31:02 +0200444 }
445
446 /* Initialize the new secure savefile */
447 name = loginname;
448 password = "";
449 level = 0;
450 domains = ({ });
451 guilds = ({ });
452 shell = "";
453 ep = "";
454 ek = "";
455 mq = "";
456 ektips="";
457 fptips="";
458 creation_date = time();
459
460 input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
461 "Waehle ein Passwort: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100462 return;
MG Mud User88f12472016-06-24 23:31:02 +0200463 }
464 else {
465 if ( loginname == "gast" ){
466 if ( check_illegal(loginname) )
Zesstrab75a89c2020-01-31 18:47:57 +0100467 return;
MG Mud User88f12472016-06-24 23:31:02 +0200468
469 load_player_object(1);
Zesstrab75a89c2020-01-31 18:47:57 +0100470 return;
MG Mud User88f12472016-06-24 23:31:02 +0200471 }
472
473 if ( neu ){
474 write( "Es existiert bereits ein Charakter dieses Namens.\n" );
475 loginname = "logon";
476 input_to( "logon2", INPUT_PROMPT,
477 "Gib Dir einen anderen Namen: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100478 return;
MG Mud User88f12472016-06-24 23:31:02 +0200479 }
480
Vanion50652322020-03-10 21:13:25 +0100481 if ( ({int})master()->check_late_player(loginname) )
MG Mud User88f12472016-06-24 23:31:02 +0200482 {
483 write( "Dieser Spieler hat uns leider fuer immer verlassen.\n" );
484 loginname = "logon";
485 input_to( "logon2", INPUT_PROMPT,
486 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100487 return;
MG Mud User88f12472016-06-24 23:31:02 +0200488 }
489
Vanion50652322020-03-10 21:13:25 +0100490 if ( txt = ({string})master()->QueryTBanished(loginname) ){
MG Mud User88f12472016-06-24 23:31:02 +0200491 write( txt );
492 loginname = "logon";
493 input_to( "logon2", INPUT_PROMPT,
494 "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
Zesstrab75a89c2020-01-31 18:47:57 +0100495 return;
MG Mud User88f12472016-06-24 23:31:02 +0200496 }
497
498 if ( creation_date > (time() - 30*24*60*60)
499 && check_too_many_from_same_ip() )
Zesstrab75a89c2020-01-31 18:47:57 +0100500 return;
MG Mud User88f12472016-06-24 23:31:02 +0200501
502 write( "Schoen, dass Du wieder da bist, "+capitalize(loginname)+"!\n" );
503
504 if ( !stringp(password) || password == "" ) {
505 write( "Du hast KEIN PASSWORD!\n" );
506 write( "Benutze den \"password\"-Befehl, um das zu aendern !\n" );
507 load_player_object(0);
Zesstrab75a89c2020-01-31 18:47:57 +0100508 return;
MG Mud User88f12472016-06-24 23:31:02 +0200509 }
510
511 input_to( "check_password", INPUT_NOECHO|INPUT_PROMPT,
512 "Passwort: ");
Zesstrab75a89c2020-01-31 18:47:57 +0100513 return;
MG Mud User88f12472016-06-24 23:31:02 +0200514 }
515}
516
517
518static int new_password( string str )
519{
520 write( "\n" );
521
522 if ( !str || str == "" )
523 return remove();
524
525 password = str;
526
bugfixd94d0932020-04-08 11:27:13 +0200527 if ( !({int})master()->good_password( str, loginname ) ) {
MG Mud User88f12472016-06-24 23:31:02 +0200528 input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
529 "Bitte gib ein Passwort an: ");
530 return 1;
531 }
532
533 write( "\nZur Erinnerung: Es ist v e r b o t e n, andere Spieler "
534 "anzugreifen!\n" );
535 write( "Das gilt auch fuer Froesche, bei denen \"Ein Frosch namens "
536 "XXXXX\" steht.\n\n" );
537 input_to( "again_password", INPUT_NOECHO|INPUT_PROMPT,
538 "Passwort bitte nochmal eingeben: ");
539 return 1;
540}
541
542static int again_password( string str )
543{
544 write( "\n" );
545
546 if ( str != password ){
547 write( "Die Passwoerter stimmten nicht ueberein!\n" );
548 destruct( this_object() );
549 return 1;
550 }
551
Zesstra28d72e52016-10-27 23:56:19 +0200552 set_next_reset(600);
MG Mud User88f12472016-06-24 23:31:02 +0200553 password = md5_crypt( password, 0 );
554 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
555 master()->RemoveFromCache( loginname );
556
557 load_player_object(0);
558 return 1;
559}
560
561static int check_password( string str )
562{
563 write( "\n" );
564
565 // Invis einloggen?
566 if (sizeof(str) > 1 && str[0] == '-') {
567 invis = 1;
568 str = str[1..];
569 }
570
571 // welcher Hash ists denn?
572 if (sizeof(password) > 13) {
573 // MD5-Hash
574 str = md5_crypt(str, password);
575 }
576 else if (sizeof(password) > 2) {
577 // Crypt-Hash
578 str = crypt(str, password[0..1]);
579 }
580 else {
581 // keiner von beiden Hashes -> ungueltiges PW
582 str = 0;
583 }
584
585 if ( !stringp(str) || str != password ) {
586 // Hashes stimmen nicht ueberein -> und schluss...
587 write( "Falsches Passwort!\n");
588 if ( loginfails > 2 )
589 write(break_string(
590 "Solltest Du weiterhin Probleme mit dem Einloggen haben, kannst "
591 "Du Dein Passwort zuruecksetzen lassen, indem Du Dich als Gast "
592 "anmeldest und einen Erzmagier ansprichst, oder indem Du Dich "
Zesstra0559c662017-02-06 19:14:51 +0100593 "mittels einer E-Mail an mud@mg.mud.de mit uns in Verbindung "
MG Mud User88f12472016-06-24 23:31:02 +0200594 "setzt.",78));
595
596 log_file( (level < 60 ? "LOGINFAIL" : "ARCH/LOGINFAIL"),
597 sprintf( "PASSWORD: %-11s %s, %-15s (%s)\n",
598 loginname, ctime(time())[4..15],
599 query_ip_number(this_object()),
600 query_ip_name(this_object()) ), 200000 );
601
602 loginfails++;
603 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
604 master()->RemoveFromCache( loginname );
605 destruct( this_object() );
606 return 1;
607 }
608
609 if ( loginfails ) {
610 write( loginfails + " fehlgeschlagene" + (loginfails == 1 ? "r" : "") +
611 " Login-Versuch" + (loginfails == 1 ? "" : "e") +
612 " seit dem letzten erfolgreichen Login.\n" );
613 loginfails = 0;
614 }
615
616 save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
617 master()->RemoveFromCache( loginname );
618
619 load_player_object(0);
620 return 1;
621}
622
623
624static void select_race()
625{
626 int i;
Zesstra7e353662019-11-27 19:52:41 +0100627 string race;
628 int selectable;
MG Mud User88f12472016-06-24 23:31:02 +0200629
630 races = get_dir( "/std/shells/*.c" );
631
632 // Mensch soll immer als erstes in der Auswahlliste stehen.
633 if (member(races,"human.c")!=-1)
634 races=({"human.c"})+(races-({"human.c"}));
635
636 for ( i = sizeof(races); i--; ){
637 races[i] = "/std/shells/" + races[i][0..<3];
Zesstra7e353662019-11-27 19:52:41 +0100638 selectable = 0;
639 race = 0;
640 if ( catch(selectable = ({int})call_other( races[i],
641 "QueryAllowSelect" ); publish)
642 || !selectable)
643 selectable = 0;
644 else if ( catch(race = ({string})call_other(races[i],
645 "QueryProp", P_RACE );publish) )
646 race = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200647
Zesstra7e353662019-11-27 19:52:41 +0100648 if ( !selectable || !sizeof(race) )
MG Mud User88f12472016-06-24 23:31:02 +0200649 races[i..i] = ({});
650 else
Zesstra7e353662019-11-27 19:52:41 +0100651 races[i] = ({ races[i], race });
MG Mud User88f12472016-06-24 23:31:02 +0200652 }
653
654 if ( sizeof(races) == 1 ){
655 write( "Es gibt nur eine Rasse, Du hast also keine Wahl.\n" );
656
657 shell = races[0][0];
658 master()->set_player_object( loginname, shell );
659
660 return load_player_ob_2( shell, 0 );
661 }
662
663 return ask_mud_played_question();
664}
665
666static void ask_mud_played_question()
667{
668 write(break_string(
669 "\nWenn Du ein absoluter Neuling in diesem Spiel bist moechten "
670 "wir Dir mit einigen Tips zu Beginn beiseite stehen.\n\n",78,
671 0,BS_LEAVE_MY_LFS));
672 input_to( "get_mud_played_answer", INPUT_PROMPT,
673 "Hast Du schon einmal in einem MUD gespielt? (ja,nein): ");
674 return;
675}
676
677static void ask_race_question()
678{
679 int i, j;
680
681 write( break_string( "Du musst Dich jetzt entscheiden, welcher Rasse Du "
682 "in dieser Welt angehoeren moechtest. Alle Rassen "
683 "haben verschiedene Vor- und Nachteile, insgesamt "
684 "aber gleich gute Chancen. Auch das Startgebiet "
685 "haengt von der ausgewaehlten Rasse ab. Im "
686 "Normalfall kann die Rasse nicht mehr gewechselt "
687 "werden, nachdem sie einmal ausgewaehlt wurde. "
688 "Ueberlege Dir Deine Entscheidung also gut. Derzeit "
689 "stehen folgende Rassen zur Auswahl:\n\n", 78 ) );
690
691 for ( i = 0, j = sizeof(races); i < j; i++ )
692 printf( "% 2d. %-30s %s", i+1, capitalize(races[i][1]),
693 (i % 2 ? "\n" : "| ") );
694
695 if ( sizeof(races) % 2 )
696 write( "\n" );
697
698 write( break_string( "\nDurch Eingabe einer Ziffer waehlst Du die Rasse "
699 "aus, durch Eingabe eines \"\?\" gefolgt von einer "
700 "Ziffer erhaeltst Du naehere Informationen ueber "
701 "eine Rasse. Ein \"\?\" allein wiederholt diese "
702 "Liste.", 78, 0, 1 ) );
703
704 if (newbie)
705 {
706 write(break_string("\nAls Neuling solltest Du Dich NICHT fuer "
707 "die Dunkelelfen entscheiden. Diese "
708 "Rasse hat einige Probleme im Umgang "
709 "mit den anderen Rassen und mit dem "
710 "Sonnenlicht.",78,0,BS_LEAVE_MY_LFS));
711 }
712
713 input_to( "get_race_answer", INPUT_PROMPT,
714 "\nWas willst Du tun: ");
715 return;
716}
717
718
719static void get_race_answer( string str )
720{
721 int num;
722
723 if ( str == "?" )
724 return ask_race_question();
725
726 if ( sscanf( str, "?%d", num ) ){
727 if ( num < 1 || num > sizeof(races) ){
728 write( "Das geht nicht.\n\n");
729 input_to( "get_race_answer", INPUT_PROMPT,
730 "Was willst Du tun: ");
731 return;
732 }
733
bugfixd94d0932020-04-08 11:27:13 +0200734 write( ({string})call_other( races[num - 1][0], "QueryProp", P_RACE_DESCRIPTION ));
MG Mud User88f12472016-06-24 23:31:02 +0200735 input_to( "get_race_answer", INPUT_PROMPT,
736 "\nWas willst Du tun: ");
737 return;
738 }
739
740 if ( sscanf( str, "%d", num ) && num >= 1 && num <= sizeof(races) ){
741 write( "Ok, Du bist jetzt ein "
742 + capitalize(races[num-1][1]) + ".\n" );
743
744 shell = races[num-1][0];
745 master()->set_player_object( loginname, shell );
746 return load_player_ob_2( shell, 0 );
747 }
748
749 write("Wie bitte?\n\n" );
750 input_to( "get_race_answer", INPUT_PROMPT,
751 "Was willst Du tun: ");
752}
753
754static void get_mud_played_answer (string str)
755{
756 if ( str == "ja" || str=="j")
757 {
758 newbie=0;
759 return ask_race_question();
760 }
761 if ( str != "nein" && str!="n")
762 {
763 write("\n\nAntworte bitte mit ja oder nein.\n\n");
764
765 return ask_mud_played_question();
766 }
767 newbie=1;
768 write("\n\nEine kleine Einfuehrung in das "MUDNAME" bekommst "
769 "Du auch hier:\n\n"
770 "http://mg.mud.de/newweb/hilfe/tutorial/inhalt.shtml\n\n");
771 return ask_race_question();
772
773}
774
775static int load_player_object( int guestflag )
776{
777 object ob;
MG Mud User88f12472016-06-24 23:31:02 +0200778
779 if ( sizeof(users()) >= 195 && !IS_WIZARD(loginname) ){
780 write( "Die maximale Spielerzahl wurde bereits erreicht!!!\n"
781 "Aus technischen Gruenden duerfen sich nur noch Magier "
782 "einloggen.\nVersuch es spaeter noch einmal ...\n" );
783 destruct( this_object() );
784 return 1;
785 }
786 else if ( sizeof(users()) >= 198 && !IS_ARCH(loginname) ){
787 write( "Die maximale Spieler- und Magierzahl wurde bereits erreicht!!!"
788 "\nAus technischen Gruenden duerfen sich nur noch Erzmagier "
789 "einloggen.\nVersuch es spaeter noch einmal ...\n" );
790 destruct( this_object() );
791 return 1;
792 }
793
794 if ( file_size("/etc/NOLOGIN")>=0 )
795 {
796 if (file_size("/etc/NOLOGIN.info")>0) {
797 //NOLOGIN.info enthaelt evtl. weitergehende Informationen fuer
798 //Spieler, z.B. vorrauss. Wiederverfuegbarkeit.
799 write(break_string(read_file("/etc/NOLOGIN.info"),78,"",
800 BS_LEAVE_MY_LFS|BS_SINGLE_SPACE));
801 }
802 else {
803 //sonst Standardmeldung ausgeben.
804 write ("\nAufgrund von technischen Problemen ist das Einloggen ins "
805 MUDNAME" zur \nZeit nicht moeglich. Bitte versuch es "
806 "spaeter noch einmal.\n\n");
807 }
808 if ( IS_ARCH(loginname) ||
809 member(explode(read_file("/etc/NOLOGIN")||"","\n"),
810 loginname)!=-1 )
811 {
812 write("Im Moment koennen nur Erzmagier einloggen. Um Spieler "
813 "wieder ins Spiel zu lassen, muss die Datei '/etc/NOLOGIN' "
814 "geloescht werden.\n\n ");
815 } else {
816 destruct( this_object() );
817 return 1;
818 }
819 }
820
821 if ( guestflag ){
Vanion50652322020-03-10 21:13:25 +0100822 if ( catch(guestflag = ({int})GUESTMASTER->new_guest();publish)
MG Mud User88f12472016-06-24 23:31:02 +0200823 || !guestflag ){
824 write( "Derzeit ist kein Gastlogin moeglich!\n" );
825 destruct( this_object() );
826 return 1;
827 }
828
829 loginname = "gast" + guestflag;
830 cap_name = capitalize(loginname);
831 name = cap_name;
832
833 if ( !(ob = find_player(loginname) || find_netdead(loginname)) ){
834 object *user;
835 int i;
836
837 // gegen Horden von Gast1 - wenn ein Gast noch am Prompt fuer
838 // das Geschlecht haengt, ist er ueber find_player() noch nicht
839 // zu finden ...
840 for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
841 if ( object_name(user[i])[0..11] == "/std/shells/" &&
842 getuid(user[i]) == loginname ){
843 ob = user[i];
844 break;
845 }
846 }
847
848 if ( ob ){
849 tell_object( ob, "Ein anderer Spieler moechte diesen Gastzugang "
850 "jetzt benutzen. Wenn es Dir hier\ngefallen hat, "
851 "ueberleg Dir doch einen Charakternamen und komm "
852 "unter diesem\nNamen wieder!\n" );
853 destruct(ob);
854 }
855
856 load_player_ob_2( "std/shells/human", guestflag );
857
858 return 1;
859 }
860 else {
861 /* Test if we are already playing */
MG Mud User88f12472016-06-24 23:31:02 +0200862 ob = find_player(loginname) || find_netdead(loginname);
MG Mud User88f12472016-06-24 23:31:02 +0200863 if (ob) {
864 write( "Du nimmst schon am Spiel teil!\n" );
865 write( "Verwende Deine alte sterbliche Huelle ...\n" );
866
867 if ( interactive(ob) )
868 {
Zesstra9ea7e9a2021-11-01 11:33:34 +0100869 // The other object is still interactive; disconnect it first
870 // and attach our connection to the player object.
MG Mud User88f12472016-06-24 23:31:02 +0200871 remove_interactive(ob);
MG Mud User88f12472016-06-24 23:31:02 +0200872 }
873 // Wenn Invislogin, P_INVIS setzen.
874 if ( invis && IS_WIZARD(ob) )
875 {
876 ob->SetProp( P_INVIS, ob->QueryProp(P_AGE) );
877 tell_object( ob, "DU BIST UNSICHTBAR!\n" );
878 }
879 /* Now reconnect to the old body */
880 exec( ob, this_object() );
881 ob->set_realip(realip);
Vanion50652322020-03-10 21:13:25 +0100882 if ( (({int})ob->QueryProp(P_LEVEL)) == -1 )
MG Mud User88f12472016-06-24 23:31:02 +0200883 ob->start_player( cap_name );
884 else
Zesstra46a5b9c2021-11-11 22:47:36 +0100885 ob->Reconnect();
MG Mud User88f12472016-06-24 23:31:02 +0200886
887 call_out( "remove", 2 );
888 return 1;
889 }
890 }
891
892 /* read player object from passwd file */
893 if ( stringp(shell) && shell != "" )
894 load_player_ob_2 ( shell, 0 );
895 else
896 select_race();
897
898 return 1;
899}
900
901
902static void load_player_ob_2( string obname, int guestflag )
903{
904 object blueprint;
905 string err, ob_name;
906 object ob, old_ob;
907
908 if (!interactive()) {
909 destruct(this_object());
910 return;
911 }
912 /* start player activity */
Zesstrae88826c2016-09-24 20:37:05 +0200913 log_file( "syslog/shell/ENTER", sprintf( "%-11s %s, %-15s (%s).\n",
MG Mud User88f12472016-06-24 23:31:02 +0200914 capitalize(name), ctime(time())[4..15],
915 query_ip_number(this_object()),
916 query_ip_name(this_object()) ), 200000 );
917
918 seteuid(loginname);
919
920 /* load the "real" player object */
921 /* If some asshole has moved the blueprint */
922 if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
923 destruct(blueprint);
924
925 if ( err = catch(ob = clone_object(obname);publish) ){
926 log_file( "SHELLS", "Failed to load shell " + obname + ", " +
927 dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
928
929 write( "Konnte das passende Playerobjekt nicht laden. Lade "
930 "stattdessen\ndas Objekt Mensch. BITTE ERZMAGIER "
931 "BENACHRICHTIGEN !\n" );
932 err = catch(ob = clone_object("/std/shells/human");publish);
933 }
934
935 if ( !ob || err ) {
936 write( "Error on loading " + shell + "\nError = " + err + "\n" );
937 destruct( this_object() );
938 return;
939 }
940
941 if ( guestflag )
942 catch( GUESTMASTER->set_guest( guestflag, ob );publish );
943
944 ob_name = explode( object_name(ob), "#" )[0];
945
946 if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
947 ob_name = ob_name[11..];
948
949 ob_name = ob_name + ":" + loginname;
950
951 if( !guestflag )
952 {
953 if ( old_ob = find_object(ob_name) )
954 {
955 catch(old_ob->remove();publish);
956
957 if ( old_ob )
958 destruct( old_ob );
959 }
960 rename_object( ob, ob_name );
961 ob->__reload_explore();
962 }
963 exec( ob, this_object() );
964 ob->set_realip(realip);
965 ob->start_player( cap_name );
Zesstraab834bb2020-01-21 13:11:45 +0100966 // Hinweis: Das Spielerobjekt holt sich in updates_after_restore() von hier
967 // den Status von invis und setzt ggf. P_INVIS, ausserdem den Status der
968 // Telnet Negotiations.
MG Mud User88f12472016-06-24 23:31:02 +0200969
MG Mud User88f12472016-06-24 23:31:02 +0200970 // wenn der Spieler noch nicht im Mud gespielt hat, wird die aktuelle Zeit
971 // in die entsprechende Prop geschrieben. Die Prop ist transient und wird
972 // absichtlich nicht gespeichert.
973 if (newbie)
974 ob->SetProp("_lib_mud_newbie", creation_date);
975
976 destruct( this_object() );
977}
978
979
980/*
981 * With arg = 0 this function should only be entered once!
982 */
983protected void create()
984{
985 loginname = "logon";
986 creation_date = -1;
987 catch( load_object( "/secure/merlin");publish );
988 loginfails = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200989 if (clonep())
990 set_next_reset(900);
991 else
992 set_next_reset(-1);
993}
994
995void reset()
996{
997 if (clonep())
Zesstra28d72e52016-10-27 23:56:19 +0200998 {
999 if (interactive(this_object()))
1000 tell_object(this_object(),"Time out!");
MG Mud User88f12472016-06-24 23:31:02 +02001001 remove();
Zesstra28d72e52016-10-27 23:56:19 +02001002 }
MG Mud User88f12472016-06-24 23:31:02 +02001003}
1004
1005public string short()
1006{
1007 return "<Einloggender Teilnehmer>.\n";
1008}
1009
1010
1011public string query_real_name()
1012{
1013 return "<logon>";
1014}
1015
1016
1017public nomask int query_prevent_shadow()
1018{
1019 return 1;
1020}
1021
1022
MG Mud User88f12472016-06-24 23:31:02 +02001023// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1024// im Loginprozess involviert.
1025public mixed new_logon( string str)
1026{
1027 seteuid(getuid()); // sonst funkt ARCH_SECURITY nicht
1028
1029 if ( !ARCH_SECURITY || process_call() ){
1030 write( "Nur fuer Erzmagier erlaubt!\n" );
1031 destruct( this_object() );
1032 return -1;
1033 }
1034
1035 if ( !str || str == "" ){
1036 write( "Kein Name angegeben!\n" );
1037 destruct( this_object() );
1038 return 0;
1039 }
1040
1041 str = lower_case(str);
1042 cap_name = capitalize(str);
1043
1044 loginname = str;
1045 seteuid(ROOTID);
1046
1047 /* read the secure save file to see if character already exists */
bugfixd94d0932020-04-08 11:27:13 +02001048 if ( !restore_object( ({string})master()->secure_savefile(loginname) ) ){
MG Mud User88f12472016-06-24 23:31:02 +02001049 write( "Kein solcher Spieler!\n" );
1050 destruct( this_object() );
1051 return 0;
1052 }
1053 else {
1054 write( "Ok, der Spieler " + capitalize(str) + " existiert!\n" );
1055 return new_load_player_object();
1056 }
1057}
1058
1059// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1060// im Loginprozess involviert.
1061static mixed new_load_player_object()
1062{
1063 if ( find_player(loginname) || find_netdead(loginname) ){
1064 write( "Der Spieler ist bereits online oder netztot!\n" );
1065 destruct( this_object() );
1066 return 2;
1067 }
1068
1069 /* read player object from passwd file */
1070 if ( stringp(shell) && shell != "" )
1071 return new_load_player_ob_2( shell );
1072 else {
1073 write( "Keine Shell angegeben!\n" );
1074 destruct( this_object() );
1075 return 0;
1076 }
1077}
1078
1079// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
1080// im Loginprozess involviert.
1081static mixed new_load_player_ob_2( string obname )
1082{
1083 object blueprint;
1084 string err, ob_name;
1085 object ob, old_ob;
1086
1087 seteuid(loginname);
1088
1089 /* load the "real" player object */
1090 /* If some asshole has moved the blueprint */
1091 if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
1092 destruct( blueprint );
1093
1094 err = catch(ob = clone_object(obname);publish);
1095
1096 if ( err ){
1097 log_file( "SHELLS", "Failed to load shell " + obname + ", " +
1098 dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
1099
1100 write( "Konnte das passende Playerobjekt nicht laden. "
1101 "Lade stattdessen\ndas Objekt Mensch!\n" );
1102
1103 err = catch(ob = clone_object( "std/shells/human" );publish );
1104 }
1105
1106 if ( !ob || err ){
1107 write( "Error on loading " + shell + "\nError = " + err + "\n" );
1108 destruct( this_object() );
1109 return 0;
1110 }
1111
1112 ob_name = explode( object_name(ob), "#" )[0];
1113
1114 if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
1115 ob_name = ob_name[11..];
1116
1117 ob_name = ob_name + ":" + loginname;
1118
1119 if ( old_ob = find_object(ob_name) ){
1120 catch( old_ob->remove(); publish );
1121
1122 if ( old_ob )
1123 destruct( old_ob );
1124 }
1125
1126 rename_object( ob, ob_name );
1127 ob->__reload_explore();
1128 ob->set_realip(realip);
1129 ob->start_player(cap_name);
1130 ob->SetProp( "creation_date", creation_date );
1131 ob->Set( "creation_date", SAVE|SECURED|PROTECTED, F_MODE_AS );
1132
1133 ob->move( "/room/nowhere", M_NOCHECK );
1134 set_object_heart_beat( ob, 0 );
1135 destruct( this_object() );
1136
1137 return ob;
1138}
1139
Zesstraca502032020-02-05 19:56:09 +01001140public string query_realip()
MG Mud User88f12472016-06-24 23:31:02 +02001141{
Zesstraca502032020-02-05 19:56:09 +01001142 return realip ? realip : 0;
MG Mud User88f12472016-06-24 23:31:02 +02001143}
1144
1145int query_invis()
1146{
1147 return invis;
1148}
1149