blob: 9cf04f662016308b8a659f27b199f0635c7e3615 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// master/userinfo.c -- Cache mit Spielerinfos
4//
5// $Id: userinfo.c 9467 2016-02-19 19:48:24Z Zesstra $
6
7#pragma strict_types
8
9#include "/secure/master.h"
10#include "/sys/files.h"
11#include "/sys/object_info.h"
12
13// Makro aus wizlevels.h ersetzen, da secure_level ne sefun ist. *seufz*
14#undef ARCH_SECURITY
15#define ARCH_SECURITY (call_sefun("secure_level") >= ARCH_LVL)
16
17private string* ExpandUIDAlias(string alias, int rec);
18
19nosave mapping userlist;
20string name, password;
21string ektips;
22string fptips;
23int level;
24string shell;
25int creation_date;
26string ep, ek, mq;
27string *domains,*guilds, *uidstotakecare;
28nosave string* lateplayers;
29// hier wird ein Mapping von UID-zu-Magier-Zuordnungen gespeichert. Als Keys
30// werden UIDs verwendet, der Value ist jeweils ein Array mit den magiern, die
31// dafuer zustaendig sind.
32nosave private mapping userids = ([]);
33// Ein Cache fuer UID-Aliase, damit ExpandUIDAlias() nicht staendig ganze
34// Verzeichnisse einlesen muss, sondern der Master das nur im Reset machen
35// muss.
36nosave private mapping uidaliase = ([]);
37
38#ifdef _PUREFTPD_
39// Liste von FTP-berechtigten Usern
40nosave private mapping ftpuser = ([]);
41
42public void read_ftp_users() {
43 string str = read_file("/"SECUREDIR"/ARCH/pureftpd.passwd") || "";
44 string *data = explode(str, "\n");
45 if (!sizeof(data)) return;
46 ftpuser = m_allocate(sizeof(data), 1);
47 foreach(str : data) {
48 string *tmp=explode(str, ":");
49 if(sizeof(tmp) >= 2)
50 m_add(ftpuser, tmp[0], tmp[1]);
51 }
52}
53
54public int write_ftp_users() {
55 string *record = allocate(18,"");
56 mapping tmp = m_allocate(sizeof(ftpuser));
57 // set UID/GID of system user
58 record[2] = "1000";
59 record[3] = "1000";
60 foreach(string u, string pwhash : ftpuser) {
61 record[0] = u; // UID
62 record[1] = pwhash;
63 record[5] = "/home/mud/mudlib/"WIZARDDIR"/" + u + "/./";
64 m_add(tmp, implode(record, ":"));
65 }
66 write_file("/"SECUREDIR"/ARCH/pureftpd.passwd", implode(m_indices(tmp),"\n"),1);
67 return sizeof(tmp);
68}
69
70// geloeschte Magier oder Magier, die 2 Wochen nicht da waren, expiren
71public void expire_ftp_user() {
72 foreach(string u : ftpuser) {
73 mixed uinfo = get_full_userinfo(u);
74 if (!uinfo) {
75 m_delete(ftpuser,u);
76 continue;
77 }
Zesstra40038b22017-01-31 09:32:40 +010078 int zeit = call_sefun("file_time",secure_savefile(u)+".o");
79 if (zeit < time()-1209600)
80 {
MG Mud User88f12472016-06-24 23:31:02 +020081 m_delete(ftpuser,u);
82 continue;
83 }
84 }
85 call_out(#'write_ftp_users, 2);
86}
87
88#endif // _PUREFTPD_
89
90// ********** oeffentliche Funktionen ***********************
91
92//Verantwortliche Magier fuer eine UID ausgeben
93public varargs string* QueryWizardsForUID(string uid, int recursive) {
94
95 if (!stringp(uid) || !sizeof(uid))
96 return(({}));
97
98 string *tolookup=({uid}); //diese spaeter in userids nachgucken.
99 string *wizards=({});
100 // Schauen, was automatisch ermittelt werden kann.
101 string *parts=explode(uid,".");
102 switch(sizeof(parts)) {
103 case 3:
104 // z.B. d.region.magier und p.service.magier
105 if (find_userinfo(parts[2]))
106 //Magier existiert, reinschreiben.
107 wizards+=({parts[2]});
108 if (parts[0]=="d")
109 // d.region noch nachgucken (RMs)
110 tolookup=({implode(parts[0..1],".")});
111 break;
112 //case 2:
113 // GUILD.gilde, p.project, d.region
114 // koennen nur in userids nachgeguckt werden (s.u. tolookup)
115 // muessen da als GUILD.*, d.* und p.* drinstehen!
116 case 1:
117 // kein Punkt drin. Entweder Magier-ID oder spezielle ID
118 // (letztere wird unten noch per tolookup nachgeguckt)
119 if (find_userinfo(parts[0]))
120 wizards+=({parts[0]});
121 break;
122 }
123 // jetzt in userids nachschlagen
124 foreach(uid: tolookup) {
125 if (member(userids,uid))
126 wizards+=userids[uid];
127 }
128 // so. Nun kann es aber noch sein, dass in userids sowas wie
129 // "d.wald.atamur":({atamur}) und "atamur":({"rumata"}) drinsteht, also
130 // ein Magier sozusagen fuer Kram eines anderen verantwortlich ist. D.h.
131 // nochmal durch QueryWizardsForUID() schicken (das ist dann allerdings nicht
132 // weiter rekursiv).
133 if (!recursive) {
134 foreach(uid: wizards) {
135 //es ist moeglich, in der Schleife wizards zu vergroessern, ohne dass
136 //es Einfluss auf die Schleife hat.
137 wizards += QueryWizardsForUID(uid, 1) - ({uid});
138 }
139 }
140 return(m_indices(mkmapping(wizards)));
141}
142
143//das Define ist nicht sonderlich elegant, aber ich kann hier nicht das
144//newskills.h hier includen (1. zu viel, 2. ists nen Sicherheitsproblem)
145#define P_GUILD_DEFAULT_SPELLBOOK "guild_sb"
146// dito fuer den Gildenmaster
147#define GUILDMASTER "/secure/gildenmaster"
148
149// Den Alias-Cache loeschen (normalerweise einmal am Tag)
150public void ResetUIDAliase() {
151 // RM+ duerfen den Cache loeschen (wenn sie z.B. nen neues Verzeichnis
152 // angelegt haben.)
153 if (extern_call()
154 && call_sefun("secure_level") < LORD_LVL)
155 return;
156
157 uidaliase=([]);
158}
159
160// expandiert einige 'Aliase' in ein Array von allen UIDs, fuer die sie
161// stehen. Bsp: "region" -> d.region.* + region + d.region,
162// "zauberer" -> GUILD.zauberer, "p.service" -> p.service.*
163// Nutzt Eintrag aus dem uidalias-Cache, sofern vorhanden.
164public varargs string* QueryUIDAlias(string alias, int rec) {
165 string *uids;
166 if (!stringp(alias) || !sizeof(alias))
167 return ({});
168 // Wen im cache, gehts schnell.
169 if (member(uidaliase, alias))
170 uids = uidaliase[alias];
171 else
172 uids = ExpandUIDAlias(alias, rec);
173
174 if (extern_call())
175 return copy(uids);
176
177 return(uids);
178}
179
180// Fuer welche UIDs ist ein Magier verantwortlich? (Ist er RM,
181// Gildenmagier, in welchen Regionen hat er ein Verzeichnis, etc.)
182// recursive != 0 bedeutet, dass der Aufruf indirekt aus einem laufenden
183// QueryUIDsForWizard() erfolgt. In dem Fall soll QueryUIDAlias() keine
184// weitere Rekursion durchfuehren. Wird einfach durchgereicht.
185public varargs string* QueryUIDsForWizard(string wizuid, int recursive) {
186 string *uids, *tmp, *uidstoadd;
187 int i;
188
189 if (!stringp(wizuid) || !sizeof(wizuid) || !IS_LEARNER(wizuid))
190 return(({}));
191
192 if (!find_userinfo(wizuid))
193 return(({}));
194
195 uidstoadd=({}); //diese werden hinterher in userids gespeichert.
196
197 // als erstes die ratebaren UIDs. ;-)
198 uids=({wizuid});
199 // Regionen ermitteln, wo wizuid ein Verzeichnis hat und entsprechende
200 // UIDs anhaengen:
201 foreach(string region: get_domain_homes(wizuid)) {
202 uids+=({ sprintf(DOMAINDIR".%s.%s",region,wizuid) });
203 }
204 // Verzeichnis in /p/service?
205 if (file_size(PROJECTDIR"/service/"+wizuid) == FSIZE_DIR)
206 uids+=({PROJECTDIR".service."+wizuid});
207
208 // Gildenchef?
209 if (pointerp(userlist[wizuid,USER_GUILD])) {
210 foreach(string gilde: userlist[wizuid,USER_GUILD]) {
211 uidstoadd += QueryUIDAlias(gilde);
212 }
213 }
214 // Regionsmagier?
215 if (pointerp(userlist[wizuid,USER_DOMAIN])) {
216 foreach(string domain: userlist[wizuid,USER_DOMAIN]) {
217 //generelle Pseudo-UID 'd.region' und 'region' fuer geloeschte
218 //Magier dieser Region vormerken, um sie hinterher fuers
219 //Reverse-Lookup ins uid-Mapping zu schreiben.
220 string *pseudo=({DOMAINDIR"."+domain, domain});
221 uidstoadd += pseudo;
222 // Rest macht QueryUIDAlias, dabei aber die von der Funktion
223 // ebenfalls gelieferten Pseudo-UIDs wieder abziehen.
224 uids += QueryUIDAlias(domain) - pseudo;
225 }
226 }
227 // jetzt noch nachgucken, fuer welche UIDs dieser Magier explizit noch
228 // zustaendig ist.
229 if (pointerp(userlist[wizuid,USER_UIDS_TO_TAKE_CARE])) {
230 // dies koennte etwas a la "region" oder "anderermagier" sein, d.h. dieser
231 // Magier ist fuer alle UIDs von 'andermagier' auch zustaendig. Daher muss
232 // jedes davon durch QueryUIDAlias() (was im Falle von Spielern wiederum
233 // QueryUIDsForWizard() ruft, aber die Rekursion im Falle von Spielern ist
234 // auf 1 begrenzt).
235 foreach(string uid2: userlist[wizuid,USER_UIDS_TO_TAKE_CARE]) {
236 uidstoadd += QueryUIDAlias(uid2, recursive);
237 }
238 }
239
240 // so, in uidstoadd stehen UIDs drin, die nicht Magiername selber,
241 // d.region.magier oder p.service.magier sind und bei welchen folglich das
242 // Mapping UIDs-nach-Magier nur mit einer Liste moeglich ist. In die
243 // werden die uids nun eingetragen. (z.B. d.region)
244 if (sizeof(uidstoadd)) {
245 // genug Platz in userids? Sonst welche rauswerfen. :-/
246 // (besser als bug) TODO: Auf 10k begrenzen -> max Arraygroesse!
247 if ( sizeof(userids)+(i=sizeof(uidstoadd))
248 >= __MAX_MAPPING_KEYS__) {
249 foreach(string tmpuid: m_indices(userids)[0..i])
250 m_delete(userids,tmpuid);
251 }
252 foreach(string tmpuid: uidstoadd) {
253 if (member(userids,tmpuid)) {
254 //User dem Array hinzufuegen, wenn noch nicht drin.
255 if (member(userids[tmpuid],wizuid)==-1)
256 userids[tmpuid]=userids[tmpuid]+({wizuid});
257 }
258 //sonst neuen Eintragen hinzufuegen
259 else
260 m_add(userids,tmpuid,({wizuid}));
261 }
262 } // Ende spez. uids speichern
263
264 return(uids+uidstoadd);
265}
266
267//Einen Magier als verantwortlich fuer eine bestimmte UID eintragen
268public string* AddWizardForUID(string uid, string wizuid) {
269 if (!stringp(wizuid) || !sizeof(wizuid)
270 || !IS_LEARNER(wizuid))
271 return(({}));
272
273 //Zugriff nur als EM oder jemand, der fuer die UID zustaendig ist.
274 if ( !ARCH_SECURITY
275 && member(
276 QueryUIDsForWizard(call_sefun("secure_euid")),
277 uid) == -1)
278 return(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
279
280 if (!pointerp(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]))
281 //Array neu anlegen
282 userlist[wizuid,USER_UIDS_TO_TAKE_CARE]=({uid});
283 else {
284 //Ein Array schon vorhanden
285 if (member(userlist[wizuid,USER_UIDS_TO_TAKE_CARE],uid)==-1)
286 //uid nicht drin
287 userlist[wizuid,USER_UIDS_TO_TAKE_CARE]=
288 userlist[wizuid,USER_UIDS_TO_TAKE_CARE]+({uid});
289 }
290 save_userinfo(wizuid);
291 // aus dem UID-Alias-Cache werfen
292 m_delete(uidaliase, wizuid);
293 // Aufruf, um userids und uidaliase zu aktualisieren
294 QueryUIDsForWizard(wizuid);
295 return(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
296}
297
298// Einen Magier wieder austragen, wenn er nicht mehr zustaendig ist.
299public string* RemoveWizardFromUID(string uid, string wizuid) {
300 if (!stringp(wizuid) || !sizeof(wizuid)
301 || !find_userinfo(wizuid))
302 return(({}));
303
304 //Zugriff nur als EM oder jemand, der fuer die UID zustaendig ist.
305 if ( !ARCH_SECURITY
306 && member(
307 QueryUIDsForWizard(call_sefun("secure_euid")),
308 uid)==-1)
309 return copy(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
310
311 // jetzt muss diese wizuid aus allen UIDs in userids geloescht werden, die
312 // sie bisher enthalten. Hierzu sollte QueryUIDAlias die potentiell
313 // drinstehenden UIDs liefern.
314 foreach(string tuid: QueryUIDAlias(wizuid,0)) {
315 if (member(userids, tuid) &&
316 member(userids[tuid],wizuid)!=-1 )
317 userids[tuid] -= ({wizuid});
318 }
319 // wenn es eine UID war, fuer die der Magier explizit zustaendig war,
320 // entsprechend loeschen. Sonst ist hier Ende.
321 if (!pointerp(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]))
322 return ({});
323 if (member(userlist[wizuid,USER_UIDS_TO_TAKE_CARE],uid)==-1)
324 return copy(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
325
326 // Jetzt aus userlist loeschen.
327 userlist[wizuid,USER_UIDS_TO_TAKE_CARE] -= ({uid});
328 save_userinfo(wizuid);
329 // und userids/uidaliase aktualisieren.
330 QueryUIDsForWizard(wizuid);
331 return copy(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
332}
333
334//entfernt einen user aus dem usercache
335void RemoveFromCache(string user) {
336 m_delete(userlist,user);
337}
338
339//loescht den gesamten Usercache
340int clear_cache() {
341 userlist=m_allocate(0,widthof(userlist));
342 update_late_players();
343 return 1;
344}
345
346// Loescht UID->Magier Lookuptabelle. Sollte nicht gerufen werden, wenn man
347// nicht genau weiss, was man damit kaputtmacht.
348// als Nebeneffekt wird clear_cache() gerufen.
349int ResetUIDCache() {
350 // da diese Funktion auch das UID<->Magier-Lookup loescht, darf das nicht
351 // jeder rufen.
352 if (extern_call() &&
353 call_sefun("secure_level") < ELDER_LVL)
354 return -1;
355 userids=([]);
356 clear_cache();
357 return 1;
358}
359
360//verstorbene Spieler auflisten
361string list_late_players()
362{
363 string ret;
364 int size,i;
365
366 ret= "************************************\n";
367 ret+="* Verstorbene Spieler *\n";
368 ret+="************************************\n";
369
370 if(!lateplayers || (size=sizeof(lateplayers))==0)
371 {
372 return ret;
373 }
374
375 for(i=0;i<size;i++)
376 {
377 ret+=lateplayers[i]+"\n";
378 }
379
380 return ret;
381}
382
383// ist der Spieler gestorben?
384int check_late_player(string str)
385{
386 if(!lateplayers || !str || str=="")
387 {
388 return 0;
389 }
390
391 if(member(lateplayers,str)!=-1)
392 {
393 return 1;
394 }
395
396 return 0;
397}
398
399// alle Eintraege im usercache ausgeben
400public mixed *show_cache() {
401 return m_indices(userlist);
402}
403
404// gibt es so einen User? Anfrage ausserdem immer in den Cache eintragen
405public int find_userinfo(string user) {
406 string file;
407 int i;
408 if (!stringp(user) || !sizeof(user)
409 || member(user,' ')!=-1 || member(user,':')!=-1)
410 return 0;
411 if (!member(userlist,user)) {
412 //erstmal schauen, ob wir mit einem neuen Eintrag nicht die max.
413 //Mapping-Groessen ueberschreiten, wenn ja, einen Eintrag loeschen
414 //BTW: widthof()+1 ist richtig so.
415 //BTW2: Ich hoffe, die max. Arraygroesse ist immer gross genug, sollte
416 //sie aber sein bei ner Mappingbreite von 10.
417 // BTW3: Dieses Rausloeschen von einem Eintrag bei Erreichen der Grenze
418 // erhoeht die benoetigten Ticks fuer diese Funktion um das 5-6fache und
419 // die noetige Zeit um das 70fache. Daher wird der Cache bei Erreichen
420 // der Grenze jetzt einfach geloescht.
421 if ( ( (i=sizeof(userlist)+1) >= __MAX_MAPPING_KEYS__)
422 || (( i * (widthof(userlist)+1)) >
423 __MAX_MAPPING_SIZE__))
424 //m_delete(userlist,m_indices(userlist)[0]);
425 userlist=m_allocate(1,widthof(userlist));
426
427 // Usersavefile finden
428 if ((file=secure_savefile(user))=="") {
429 // User gibt es nicht, aber Anfrage cachen
430 userlist+=([user: "NP"; -1; ({}); "LOCKED"; -1; time(); ""; ""; "";
431 ({});"";""; 0 ]);
432 return 0;
433 }
434 password="";
435 ep="";
436 ek="";
437 mq="";
438 guilds=({});
439 ektips="";
440 fptips="";
441 uidstotakecare=0;
442 if (!restore_object(file)) return 0;
443 userlist+=([user: password; level; domains; shell; creation_date;
444 time();ep; ek; mq; guilds; ektips; fptips; uidstotakecare]);
445 // die speziellen UIDs, fuer die dieser Magier zustaendig ist, ermitten
446 // und gleichzeitig im entsprechenden Mapping fuers Reverse-Loopup
447 // speichern.
448 if (level >= LEARNER_LVL)
449 QueryUIDsForWizard(user);
450 }
451 userlist[user,USER_TOUCH]=time();
452 if (userlist[user,USER_LEVEL]==-1) return 0;
453 return 1;
454}
455
456// Daten aus der Userlist fuer diesen User liefern. Macht ggf. find_userinfo()
457public mixed *get_userinfo(string user) {
458 if(!user||user=="")
459 return 0;
460 user=explode(user,".")[0];
461 if (!member(userlist,user) && !find_userinfo(user))
462 return 0;
463
464 if (userlist[user,USER_LEVEL]==-1) return 0;
465
466 return({user,"##"+user,userlist[user,USER_LEVEL],
467 userlist[user,USER_DOMAIN], userlist[user,USER_OBJECT],
468 userlist[user,USER_CREATION_DATE], 0, 0,
469 userlist[user,USER_GUILD],
470 userlist[user,USER_EKTIPS],userlist[user,USER_FPTIPS],
471 userlist[user,USER_UIDS_TO_TAKE_CARE]});
472}
473
474// liefert das Objekt des users aus der Userlist zurueck.
475public string query_player_object( string name )
476{
477 mixed *userentry;
478 if( !find_userinfo(name) ) return "";
479 return userlist[name,USER_OBJECT];
480}
481
482// Passwort ok?
483public int good_password( string str, string name )
484{
485 string rts;
486 int i, n;
487
488 if ( str[0] == '!' ){
489 tell_object( this_player() || this_object(), "Ein Ausrufungszeichen "
490 "('!') als erster Buchstabe des Passwortes wuerde zu\n"
491 "Problemen fuehren. Such Dir bitte ein anderes Passwort "
492 "aus.\n" );
493 return 0;
494 }
495
496 if ( sizeof(str) < 6 ){
497 tell_object( this_player() || this_object(),
498 "Das Passwort muss wenigstens 6 Zeichen lang sein.\n" );
499 return 0;
500 }
501
502 str = lower_case(str);
503 rts = "";
504
505 // Zahlen/Sonderzeichen am Anfang oder Ende des Passwortes
506 // (z.B. "merlin99") sind nicht wirklich einfallsreich.
507 while ( sizeof(str) && (str[0] > 'z' || str[0] < 'a') ){
508 rts += str[0..0];
509 str = str[1..];
510 }
511
512 while ( sizeof(str) && (str[<1] > 'z' || str[<1] < 'a') ){
513 rts += str[<1..];
514 str = str[0..<2];
515 }
516
517 // Anzahl unterschiedlicher Zeichen merken, die herausgeschnitten wurden
518 n = sizeof( mkmapping(efun::explode( rts, "" )) );
519 rts = "";
520
521 for ( i = sizeof(str); i--; )
522 rts += str[i..i];
523
524 // Eigener Name als Passwort bzw. eigener Name rueckwaerts
525 if ( str == lower_case(name) || rts == lower_case(name) ||
526 // Name eines anderen Mudders (Erstie?)
527 secure_savefile(str) != "" || secure_savefile(rts) != "" ||
528 // Name von der Banish-Liste
529 QueryBanished(str) || QueryBanished(rts) ||
530 // Name eines vorhandenen NPC o.ae.
531 call_sefun("find_living", str ) ||
532 call_sefun("find_living", rts ) ||
533 // Zuwenig verschiedene Zeichen
534 sizeof( mkmapping(efun::explode( str, "" )) ) + n < 4 ){
535 tell_object( this_player() || this_object(),
536 "Das Passwort waere zu einfach zu erraten. Nimm bitte "
537 "ein anderes.\n" );
538 return 0;
539 }
540
541 return 1;
542}
543
544// User-ID fuer ein File ermitteln.
545public string get_wiz_name(string file) {
546 return creator_file(file);
547}
548
549// Wizlevel aus dem Userinfo-Cache holen
550public int get_wiz_level(string user) {
551 if (user && find_userinfo(user))
552 return userlist[user,USER_LEVEL];
553 //return 0 if no user given (return type needed)
554 return(0);
555}
556
557// Wizlevel fuer eine UID ermitteln.
558public int query_wiz_level( mixed player )
559{
560 if ( objectp(player) && efun::object_info(player, OI_ONCE_INTERACTIVE) )
561 return get_wiz_level( getuid(player) );
562 else {
563 // erstmal UID ermitteln, falls Objekt
564 //if (objectp(player))
565 // player=getuid(player);
566 if ( stringp(player) ) {
567 if( player[0..1]==DOMAINDIR"." ) return 25;
568 if( player[0..5]==GUILDID"." )
569 return WIZLVLS[GUILDID];
570 if( player[0..1]==PROJECTDIR"." ) return 21;
571 // die alte Loesung mit || verhaelt sich falsch, wenn ein UID ne
572 // spezielle ist, der Level 0 zugeordnet wurde und es einen
573 // Spieler mit diesem namen gibt.
574 if (member(WIZLVLS,player))
575 return(WIZLVLS[player]);
576 return get_wiz_level(player);
577 }
578 }
579 return 0;
580}
581
582// Savefile aus /secure/save* zurueckliefern
583public string secure_savefile(string name)
584{
585 if(!name||name=="")
586 return "";
587 name=explode(name,".")[0];
Zesstra40038b22017-01-31 09:32:40 +0100588 if (file_size(SECURESAVEPATH+name[0..0]+"/"+name+".o")>=0)
MG Mud User88f12472016-06-24 23:31:02 +0200589 return SECURESAVEPATH+name[0..0]+"/"+name;
Zesstra4ac30292017-01-31 16:04:06 +0100590 else if (file_size("/"SECUREDIR"/save/"+name[0..0]+"/"+name+".o")>=0)
Zesstra40038b22017-01-31 09:32:40 +0100591 return "/"SECUREDIR"/save/"+name[0..0]+"/"+name;
MG Mud User88f12472016-06-24 23:31:02 +0200592
593 return "";
594}
595
596// *************** 'halb-oeffentliche Funktionen *********************
597#ifdef _PUREFTPD_
598// FTP-Berechtigung fuer Magier
599int update_ftp_access(string user, int state) {
600 // wenn nicht EM+ oder ROOT, darf nur fuer this_interactive geaendert
601 // werden.
602 if (getuid(PO) != ROOTID && extern_call()
603 && !ARCH_SECURITY) {
604 if (this_interactive())
605 user = getuid(this_interactive());
606 else
607 user = 0;
608 }
609 if (!user || !sizeof(user))
610 return -1;
611
612 if (!find_userinfo(user))
613 return 0;
614
615 // Passwort muss manuell vom Magier neu gesetzt werden, da es keine
616 // Moeglichkeit gibt, an das aktuelle im Klartext heranzukommen.
617 if (state) {
618 if (!member(ftpuser,user))
619 m_add(ftpuser, user, "*");
620 }
621 else
622 m_delete(ftpuser, user);
623
624 call_out(#'write_ftp_users, 4);
625 return state;
626}
627#endif
628
629// Spieler ein neues Wizlevel verpassen.
630int update_wiz_level(string user,int lev) {
631 object ob;
632
633 if (getuid(PO) != ROOTID && extern_call()) return 0;
634 if (!find_userinfo(user)) return 0;
635 userlist[user,USER_LEVEL] = lev;
636 save_userinfo(user);
637 return 1;
638}
639
640// neue Forscherpunkte fuer den User abspeichern.
641int update_ep(string user,string ep_neu) {
642 if (getuid(PO) != ROOTID) return 0;
643 if (!find_userinfo(user)) return 0;
644 userlist[user,USER_EP] = ep_neu;
645 save_userinfo(user);
646 return 1;
647}
648
649// neue Erstkills fuer den User abspeichern.
650int update_ek(string user,string ek_neu) {
651 if (getuid(PO) != ROOTID) return 0;
652 if (!find_userinfo(user)) return 0;
653 userlist[user,USER_EK] = ek_neu;
654 save_userinfo(user);
655 return 1;
656}
657
658// Miniquestdaten speichern.
659int update_mq(string user,string mq_neu) {
660 if (getuid(PO) != ROOTID) return 0;
661 if (!find_userinfo(user)) return 0;
662 userlist[user,USER_MQ] = mq_neu;
663 save_userinfo(user);
664 return 1;
665}
666
667// Erstkillpunkt-Tips speichern.
668int update_ektips(string user,string ek_neu) {
669 if (getuid(PO) != ROOTID) return 0;
670 if (!find_userinfo(user)) return 0;
671 userlist[user,USER_EKTIPS] = ek_neu;
672 save_userinfo(user);
673 return 1;
674}
675
676// Forscherpunkttips abspeichern.
677int update_fptips(string user,string fp_neu) {
678 if (getuid(PO) != ROOTID) return 0;
679 if (!find_userinfo(user)) return 0;
680 userlist[user,USER_FPTIPS] = fp_neu;
681 save_userinfo(user);
682 return 1;
683}
684
685// forscherpunkte abfragen.
686string query_ep(string user) {
687 if (getuid(PO) != ROOTID) return 0;
688 if (!find_userinfo(user)) return 0;
689 return userlist[user,USER_EP];
690}
691
692// Erstkills abfragen
693string query_ek(string user) {
694 if (getuid(PO) != ROOTID) return 0;
695 if (!find_userinfo(user)) return 0;
696 return userlist[user,USER_EK];
697}
698
699// Miniquests abfragen.
700string query_mq(string user) {
701 if (getuid(PO) != ROOTID) return 0;
702 if (!find_userinfo(user)) return 0;
703 return userlist[user,USER_MQ];
704}
705
706// EK-Tips abfragen.
707string query_ektips(string user) {
708 if (getuid(PO) != ROOTID) return 0;
709 if (!find_userinfo(user)) return 0;
710 return userlist[user,USER_EKTIPS];
711}
712
713// FP-Tips abfragen.
714string query_fptips(string user) {
715 if (getuid(PO) != ROOTID) return 0;
716 if (!find_userinfo(user)) return 0;
717 return userlist[user,USER_FPTIPS];
718}
719
MG Mud User88f12472016-06-24 23:31:02 +0200720// Aendert die Shells eines Users.
721int set_player_object( string user, string objectname )
722{
723 mixed *path;
724 string prev;
725
726 // nur EM und ROOT duerfen die Shell eines Charakters aendern
727 if ( !ARCH_SECURITY &&
728 (!previous_object() || getuid(previous_object()) != ROOTID) ) {
729 return -1;
730 }
731
732 if ( objectname == "" )
733 objectname = 0;
734
735 if ( !stringp(user) || user == "" )
736 return -6;
737
738 if ( !stringp(objectname) ){
739 if ( !find_userinfo(user) )
740 return -4;
741
742 userlist[user, USER_OBJECT] = 0;
743 save_userinfo(user);
744 return 1;
745 }
746
747 if ( catch(load_object(objectname);publish) ) {
748 write( "Fehler in " + objectname + "!\n" );
749 return -2;
750 }
751
752 objectname = _get_path( objectname, 0 );
753 path = (efun::explode( objectname, "/" ) - ({ "", 0 }));
754
755 if ( sizeof(path) < 3 || path[0] != "std" || path[1] != "shells" )
756 return -3;
757
758 if ( !find_userinfo(user) )
759 return -4;
760
761 prev = userlist[user, USER_OBJECT];
762 userlist[user, USER_OBJECT] = objectname;
763 save_userinfo(user);
764
765 // Loggen, falls die Aenderung nicht von Login beim Anlegen des Chars
766 // erfolgt.
767 if (load_name(this_interactive()) != "/secure/login"
768 || prev != "") {
769 if (prev == "") prev ="<keine>";
770 call_sefun("log_file", "ARCH/SHELL_AENDERUNGEN",
771 sprintf( "%s: %O aendert die Shell von %s von %s auf %s (PO: %O)\n",
772 strftime("%Y%m%d-%H%M%S"),
773 this_interactive(), capitalize(user), prev, objectname,
774 previous_object()) );
775 }
776
777 return 1;
778}
779
780// Passwort aktualisieren.
781int update_password( string old, string new )
782{
783 string user;
784
785 // nanu, kein user?
786 if ( !find_userinfo(user = getuid(PO)) )
787 return 0;
788
789 // wenn das neue PW unterschiedlich ist, schauen, ob das neue PW ok ist.
790 if ( old != new && !good_password( new, user ) )
791 return 0;
792
793 string pwhash = userlist[user, USER_PASSWORD];
794 string oldpwhash;
795 if (sizeof(pwhash) > 13) {
796 // MD5-Hash
797 oldpwhash = md5_crypt(old, pwhash);
798 }
799 else if (sizeof(pwhash) > 2) {
800 // Crypt-Hash
801 oldpwhash = crypt(old, pwhash[0..1]);
802 }
803
804 // wenn es einen PW-hash gibt, also ein PW gesetzt wird, muss der Hash von
805 // old mit dem pwhash uebereinstimmen. Leerer Hash oder gar kein Hash
806 // erlaubt jedes beliebige PW.
807 if ( stringp(pwhash) && sizeof(pwhash) && pwhash != oldpwhash)
808 return 0;
809 // an dieser Stelle stimmt 'old' wohl mit dem gesetzten Passwort ueberein.
810 // Wenn allerdings die Funktion mit old==new aufgerufen wurde, wird hier
811 // nur 1 zurueckgeben und sonst an sich nix gemacht. Kann nicht weiter
812 // oben schon gemacht werden, die Shells dies als PW-Pruefung nutzen.
813 // *seufz*
814 if (old == new) return 1;
815
816 // dann mal neu setzen
817 userlist[ user, USER_PASSWORD ] = md5_crypt( new, 0 );
818 save_userinfo(user);
819#ifdef _PUREFTPD_
820 // Bedauerlicherweise versteht pureftpd unser md5_crypt nicht. :-(
821 if (member(ftpuser,user)) {
822 ftpuser[user] = crypt(new);
823 if (find_call_out(#'write_ftp_users) == -1)
824 call_out(#'write_ftp_users,4);
825 }
826#endif
827 return 1;
828}
829
830// Spieler loeschen.
831int delete_player(string passwd, string real_name)
832{
833 int wlevel;
834 string part_filename;
835
836 if (!PO || PO!=TP || PO!=TI || real_name != getuid(PO) ||
837 !find_userinfo(real_name))
838 return 0;
839 mixed erstie=(mixed)this_interactive()->QueryProp(P_SECOND);
840 password = userlist[real_name,USER_PASSWORD];
841 wlevel = get_wiz_level(real_name);
842 if (!update_password(passwd, passwd)) return 0;
843
844 // Spielpausen aufheben (sonst kann man als Spieler nen Namen sperren).
845 TBanishName(real_name, 0);
846
847 part_filename="/"+real_name[0..0]+"/"+real_name+".o";
848 rm("/"SECUREDIR"/save"+part_filename);
849 rm("/"LIBSAVEDIR"/"+part_filename);
850 rm("/"MAILDIR"/"+part_filename);
851
852 m_delete(userlist,real_name);
853
854 if (wlevel >= LEARNER_LVL)
855 TO->BanishName(real_name, "So hiess mal ein Magier hier");
856 else if (wlevel >= SEER_LVL)
857 TO->BanishName(real_name, "So hiess mal ein Seher hier");
858
859#ifdef _PUREFTPD_
860 if (member(ftpuser,real_name)) {
861 m_delete(ftpuser,real_name);
862 call_out(#'write_ftp_users,4);
863 }
864#endif
865
866 call_sefun("log_file", "USERDELETE",
867 sprintf("%s: %s %s(%s)\n",
868 ctime(time()),real_name,
869 (stringp(erstie)?sprintf("[Erstie: %s] ",erstie):""),
870 call_sefun("query_ip_number",TI)));
871
872 return 1;
873}
874
875// ermittelt alle existierenden Spieler, die ein Savefile in /secure/save
876// haben.
877// ([ "a": ({...namen...}), "b": ({...namen...}), ... ])
878public mapping get_all_players() {
879 string *dirs=({"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
880 "p","q","r","s","t","u","v","w","x","y","z"});
881 mapping allplayer=([]);
882 string *tmp;
883 foreach(string dir: dirs) {
Zesstra38834942017-06-20 22:10:29 +0200884 tmp=get_dir(SECURESAVEPATH+dir+"/*")
MG Mud User88f12472016-06-24 23:31:02 +0200885 - ({".","..",".KEEP",".svn"});
886 allplayer[dir] = map(tmp,function string (string fn)
887 { return explode(fn,".")[0]; } );
888 }
889 return allplayer;
890}
891
892// *************** interne Funktionen ********************************
893protected void create()
894{
895 userlist=m_allocate(0,12);
896 update_late_players();
897#ifdef _PUREFTPD_
898 read_ftp_users();
899#endif
900}
901
902//verstorbene Spieler einlesen
903void update_late_players() {
904 string read;
905 string *tmp;
906
907 lateplayers=0;
908
909 read=read_file("/"SECUREDIR"/LATE_PLAYERS");
910 if(!read || read=="")
911 {
912 return;
913 }
914
915 tmp=explode(read,"\n");
916 if(!sizeof(tmp))
917 {
918 return;
919 }
920
921 lateplayers=tmp;
922}
923
924
925// Daten aus der Userlist inkl. Passworthash zurueckliefern. Macht ggf.
926// find_userinfo(). Nur masterintern aufrufbar.
927protected mixed *get_full_userinfo(string user) {
928 if(!user||user=="")
929 return 0;
930 user=explode(user,".")[0];
931 if (!member(userlist,user) && !find_userinfo(user))
932 return 0;
933
934 return({user,userlist[user,USER_PASSWORD],userlist[user,USER_LEVEL],
935 userlist[user,USER_DOMAIN], userlist[user,USER_OBJECT],
936 userlist[user,USER_CREATION_DATE], 0, 0, userlist[user,USER_GUILD],
937 userlist[user,USER_EKTIPS],userlist[user,USER_FPTIPS],
938 userlist[user,USER_UIDS_TO_TAKE_CARE]});
939}
940
941// Userdaten aus der Userlist im Savefile in /secure/save/ speichern.
942protected void save_userinfo(string user) {
943 if(!user||user=="")
944 return;
945 user=explode(user,".")[0];
946 if (!member(userlist,user)) return;
947 name = user;
948 level = userlist[name,USER_LEVEL];
949 domains = userlist[name,USER_DOMAIN];
950 shell = userlist[name,USER_OBJECT];
951 password = userlist[name,USER_PASSWORD];
952 creation_date = userlist[name,USER_CREATION_DATE];
953 if (!creation_date) creation_date = -1;
954 ep = userlist[name,USER_EP];
955 if (!ep) ep="";
956 ek = userlist[name,USER_EK];
957 if (!ek) ek="";
958 mq = userlist[name,USER_MQ];
959 if (!mq) mq="";
960 guilds = userlist[name,USER_GUILD];
961 ektips=userlist[name,USER_EKTIPS];
962 if(!ektips) ektips="";
963 fptips=userlist[name,USER_FPTIPS];
964 if(!fptips) fptips="";
965 uidstotakecare=userlist[name,USER_UIDS_TO_TAKE_CARE];
966
Zesstra40038b22017-01-31 09:32:40 +0100967 string ssavef=secure_savefile(name);
968 if (save_object(ssavef) != 0) {
MG Mud User88f12472016-06-24 23:31:02 +0200969 // autsch. Buggen damit dieser moeglichst schnell auffaellt, dass hier
970 // Savefiles in /secure/save/ nicht geschrieben wurden.
971 raise_error(sprintf(
972 "Savefile %O konnte nicht erstellt werden!\n",
Zesstra40038b22017-01-31 09:32:40 +0100973 ssavef));
MG Mud User88f12472016-06-24 23:31:02 +0200974 }
975}
976
977// In welchen Regionen ist der Spieler Regionsmagier?
978protected void set_domains(string player, string *domains)
979{
980 // wenn der Magier jetzt Domains nicht mehr hat, muessen die aus 'userids'
981 // entfernt werden, das uebernimmt RemoveWizardFromUID().
982 if (pointerp(userlist[player, USER_DOMAIN])) {
983 string *removeduids=
984 ((string*)userlist[player, USER_DOMAIN] | domains) - domains;
985 foreach(string uid: removeduids)
986 RemoveWizardFromUID(uid, player);
987 }
988 // gecachtes Alias fuer player loeschen
989 m_delete(uidaliase,player);
990 userlist[player, USER_DOMAIN]=domains;
991 save_userinfo(player);
992 // UID-zu-Magier-Mapping aktualisieren
993 QueryUIDsForWizard(player);
994}
995
996// In welche Gilden ist der Spieler Gildenmagier?
997protected void set_guilds(string player, string *guilds)
998{
999 // wenn der Magier jetzt Gilden nicht mehr hat, muessen die aus 'userids'
1000 // entfernt werden, das uebernimmt RemoveWizardFromUID().
1001 if (pointerp(userlist[player, USER_GUILD])) {
1002 string *removeduids=
1003 ((string*)userlist[player, USER_GUILD] | guilds) - guilds;
1004 foreach(string uid: removeduids)
1005 RemoveWizardFromUID(uid, player);
1006 }
1007 // gecachtes Alias fuer player loeschen
1008 m_delete(uidaliase,player);
1009 userlist[player, USER_GUILD]=guilds;
1010 save_userinfo(player);
1011 // UID-zu-Magier-Mapping aktualisieren
1012 QueryUIDsForWizard(player);
1013}
1014
1015// Userinfo-Cache expiren...
1016protected void _cleanup_uinfo()
1017{
1018 foreach(string user: userlist) {
1019 if ((time() - userlist[user,USER_TOUCH]) > 1800
1020 && !call_sefun("find_player",user))
1021 m_delete(userlist,user);
1022 }
1023}
1024
1025// expandiert einige 'Aliase' in ein Array von allen UIDs, fuer die sie
1026// stehen. Bsp: "region" -> d.region.* + region + d.region,
1027// "zauberer" -> GUILD.zauberer, "p.service" -> p.service.* oder auch
1028// "magier" -> QueryUIDsForWizard().
1029// Das erfolgt ueber Lookup der entsprechenden Verzeichnisse auf der PLatte.
1030// Da zusaetzlich auch noch jede Menge find_userinfo() dazu kommen, ist das
1031// recht aufwaendig. Damit das ganze nicht jedesmal erfolgen muss, wird das
1032// Ergebnis gecacht und QueryUIDAlias() nutzt den Cache.
1033private string* ExpandUIDAlias(string alias, int rec) {
1034
1035 string *uids=({});
1036
1037 // Regionsname?
1038 if (file_size("/"DOMAINDIR"/"+alias) == FSIZE_DIR) {
1039 //generelle Pseudo-UID 'd.region' und 'region' fuer geloeschte
1040 //Magier dieser Region
1041 uids += ({DOMAINDIR"."+alias, alias});
1042 //alle Magier-Verzeichnisse ermitteln:
1043 string tmpdir="/"DOMAINDIR"/"+alias+"/";
1044 foreach(string dir: (get_dir(tmpdir+"*") || ({}))
1045 - ({".","..",".svn"})) {
1046 // schauen, obs nen (sichtbares) Verzeichnis ist und ob der Magier
1047 // existiert. Letzteres aber nur, falls die Rekursionstiefe min. 100
1048 // ist, da beim Nachschauen eines Magiers mit (mehreren)
1049 // RM-Posten, der in der Region RMs aus anderen Regionen hat, leicht
1050 // Rekursionstiefen von 20 (*4) auftreten koennen, wenn noch gar
1051 // keine UIDs im Cache sind (find_userinfo() ruft indirekt diese
1052 // Funktion).
1053 if (dir[0]!='.'
1054 && file_size(tmpdir+dir) == FSIZE_DIR
1055#if __MAX_RECURSION__ > 99
1056 && find_userinfo(dir)
1057#endif
1058 )
1059 uids += ({DOMAINDIR"."+alias+"."+dir});
1060 }
1061 }
1062 // Gildenname?
1063 else if (GUILDMASTER->ValidGuild(alias)) {
1064 uids += ({GUILDID"."+alias});
1065 //hat die Gilde ein Projektverzeichnis?
1066 if (file_size("/"PROJECTDIR"/"+alias) == FSIZE_DIR) {
1067 uids += ({PROJECTDIR"."+alias});
1068 }
1069 // jetzt haben dummerweise die Spellbooks meist nicht den gleichen
1070 // Namen wie die Gilde. D.h. die Gilde muss nun noch nach dem
1071 // Namen des Spellbooks gefragt werden, weil dessen UID nicht
1072 // unbedingt gleich dem der Gilde ist. *seufz*
1073 string spbook;
1074 object guild;
1075 catch(guild=load_object(GUILDDIR"/"+alias));
1076 if (objectp(guild)
1077 && (sizeof(spbook=(string)
1078 guild->QueryProp(P_GUILD_DEFAULT_SPELLBOOK)))
1079 && spbook!=alias)
1080 uids += ({GUILDID"."+spbook});
1081 }
1082 // Spieler/Magier-UID?
1083 else if (find_userinfo(alias)) {
1084 // wenn rec > 0, wird eine Spieler-UID als Alias aufgefasst und zu seinen
1085 // UIDs expandiert. Hierbei erfolgt aber nur eine Rekursion.
1086 if (!rec) uids = QueryUIDsForWizard(alias, 1);
1087 else uids = ({alias});
1088 }
1089 // Projektkrams? -> alle Subdirs von /p/ ausser /p/service selber.
1090 else if (alias==PROJECTDIR) {
1091 foreach(string dir: (get_dir("/"PROJECTDIR"/*") || ({}))
1092 - ({".","..",".svn","service"})) {
1093 if (dir[0]!='.' &&
1094 file_size("/"PROJECTDIR"/"+dir) == FSIZE_DIR)
1095 uids += ({PROJECTDIR"."+dir});
1096 }
1097 }
1098 // p.service? -> Alle Subdirs von /p/service.
1099 else if (alias==PROJECTDIR".service") {
1100 foreach(string dir: (get_dir("/"PROJECTDIR"/service/*") || ({}))
1101 - ({".","..",".svn"})) {
1102 if (dir[0]!='.' &&
1103 file_size("/"PROJECTDIR"/service/"+dir) == FSIZE_DIR)
1104 uids += ({PROJECTDIR".service."+dir});
1105 }
1106 }
1107 // wenn nix zutrifft -> unexpandiert zurueckgeben
1108 else
1109 uids = ({alias});
1110
1111 // im Cache vermerken
1112 if (sizeof(uidaliase) >= __MAX_MAPPING_KEYS__)
1113 uidaliase=m_allocate(1);
1114 // auch vermerken, wenn keine UIDs ermittelt wurden.
1115 uidaliase += ([alias: uids]);
1116 return uids;
1117}
1118