blob: d0bc8f140941731ac16d910f8e7dbfdc4ed97303 [file] [log] [blame]
#pragma strict_types
#pragma no_clone
#pragma no_shadow
#pragma no_inherit
//#pragma pedantic
//#pragma range_check
#pragma warn_deprecated
#include <wizlevels.h>
#include <daemon.h>
#include <events.h>
#include <strings.h>
#include <files.h>
#define FTPSAVE "/secure/ARCH/ftpd"
#define MAXLOGSIZE 2000000
#define SMALLLOGSIZE 200000
#define LOGTIME 120
#define MAXBUFFSIZE 2000
#define DEBUG(x) if (find_player("zesstra"))\
tell_object(find_player("zesstra"),\
"EDBG: "+x+"\n")
// fuer FTP.
private mapping monitored;
#define D_LOGTIME 0 // Logendezeit, <int>
#define D_FLAGS 1 // Flags
#define D_ERSTIE 2 // Name des Ersties.
#define D_IPSTRING 3
#define D_INDEX 4 // Index fuer D_LOG, naechster freier index.
#define D_LOG 5 // Logdaten, <mixed> (Array)
/* Datenstruktur von D_LOG:
({ ({<zeit>, <kommando>, <environment>}), .... })
*/
#define DL_TIME 0 // int
#define DL_CMD 1 // string
#define DL_ENV 2 // string
private nosave mapping ldata = m_allocate(0,D_LOG);
// Flags:
#define FL_PERMANENT 1 // 'permanent', d.h. nicht nur kurz nach Einloggen
#define FL_SYNC 2 // nicht puffern, synchron auf Platte schreiben.
// Ja. Macht das bloss nicht nach.
#define P_SECOND "second"
public int query_bb();
public void writebb( string msg );
public varargs void BBWrite(string msg, int catmode);
public int add( string user, int timeout );
public int sub( string user );
public void ftpbb( string user, string msg );
private void scan_bb_opfer();
private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata);
private void ProcessBuffer(string uid);
private void RemoveTemporaryPlayer(string uid);
public void create()
{
seteuid( getuid(this_object()) );
restore_object( FTPSAVE );
scan_bb_opfer();
EVENTD->RegisterEvent(EVT_LIB_LOGIN, "Eventhandler", this_object());
EVENTD->RegisterEvent(EVT_LIB_LOGOUT, "Eventhandler", this_object());
log_file("ARCH/bbmaster.log", strftime("%c: bbmaster wurde geladen.\n"));
}
// Auf den asynchronen Logout-event zu warten ermoeglicht es theoretisch, 1-2s
// das Log zu umgehen. Andererseits geht das ohnehin nur dann, wenn die
// Logzeit eh abgelaufen ist.
public void Eventhandler(string eid, object trigob, mixed data) {
if (previous_object() == find_object(EVENTD)) {
string uid;
if (objectp(trigob)
&& strstr(load_name(trigob),"/std/shells/") == 0
&& !trigob->QueryGuest()) {
// Bei Login und Logout den BBMode einschalten (weil der Loginevent ja
// erst 1-2s nach Einloggen abgearbeitet wird.
trigob->__set_bb(1);
uid=getuid(trigob);
}
else {
// kein Objekt mehr da. Vermutlich hat ein Spieler 'ende' gemacht, aber
// es koennte auch sein, dass jemand mit nem Selbstzerstoerer den Event
// gefakt hat. Aber selbst wenn, viel kann man damit nicht erreichen.
uid = data[E_PLNAME];
if (!stringp(uid)) return;
// Pruefung auf nicht-Anwesenheit von uid waere noch moeglich, hat aber
// Probleme, wenn ein Spieler sehr schnell wieder einloggt.
}
if (eid == EVT_LIB_LOGOUT && member(ldata,uid)) {
// Wenn Logout und es gibt Daten im Puffer, koennte man die evtl.
// wegschreiben oder loeschen.
ProcessBuffer(uid);
// auf jeden Fall temporaere Spieler entfernen. (Wichtig!)
RemoveTemporaryPlayer(uid);
}
}
}
// schreibt alle Puffer synchron, ohne Callout... Kann laggen.
public int ProcessAllBuffers() {
if (extern_call() && !ARCH_SECURITY)
return -1;
foreach(string uid, int logtime, int flags, string erstie, string ip,
int index, mixed data: ldata) {
if (index) {
DumpData(uid, erstie, ip, flags, data);
ldata[uid,D_LOG]=({});
ldata[uid,D_INDEX]=0;
}
}
return 1;
}
private void ProcessBuffer(string uid) {
if (time() <= ldata[uid,D_LOGTIME]
&& ldata[uid,D_INDEX]) {
// Daten wegschreiben, wenn Logzeit nicht abgelaufen. Sonst nicht.
call_out(#'DumpData, 2, uid, ldata[uid,D_ERSTIE], ldata[uid,D_IPSTRING],
ldata[uid,D_FLAGS], ldata[uid,D_LOG]);
}
ldata[uid,D_LOG] = ({});
ldata[uid,D_INDEX] = 0;
}
private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata) {
string res = sprintf("\n%s%s, IP: %s\n", capitalize(uid),
(stringp(erstie) ? " ("+capitalize(erstie)+")" : ""),
(stringp(ip) ? ip : "Unbekannt"));
logdata-=({0});
foreach(mixed arr : logdata) {
res+=sprintf("%O: %O [%s]\n",
strftime("%y%m%d-%H%M%S",arr[DL_TIME]),
arr[DL_CMD], arr[DL_ENV] || "<unbekannt>");
}
//DEBUG("DumpData: "+res);
if (flags & FL_PERMANENT)
catch(log_file("ARCH/bb."+uid, res, MAXLOGSIZE));
else if (file_size(LIBLOGDIR"/ARCH/bbmaster") == FSIZE_DIR)
catch(log_file("ARCH/bbmaster/"+uid, res, SMALLLOGSIZE));
// kein else, in anderen Faellen werden die Daten verworfen.
}
private void AddTemporaryPlayer(string uid) {
// natuerlich nur, wenn noch nix eingetragen.
if (!member(ldata, uid)) {
object ob = find_player(uid) || find_netdead(uid);
mixed erstie;
if (ob)
erstie = ({string})ob->QueryProp(P_SECOND);
ldata += ([uid: time() + LOGTIME + random(LOGTIME/2);
0;
(stringp(erstie) ? erstie : 0);
query_ip_number(ob);
0; ({})
]);
}
}
private void RemoveTemporaryPlayer(string uid) {
if (!(ldata[uid,D_FLAGS] & FL_PERMANENT)) {
m_delete(ldata, uid);
}
}
// Vom Spielererobjekt bei Erschaffung in InitPlayer() gerufen.
public int query_bb()
{
if (load_name(previous_object())[0..11] != "/std/shells/")
return 0;
// in jedem Fall wird nun (temporaer) der BB-Modus aktiviert.
if (!previous_object()->QueryGuest())
previous_object()->__set_bb(1);
// nur fuer 'permanente' auch 1 zurueckgeben.
return ldata[getuid(previous_object()),D_FLAGS] & FL_PERMANENT;
}
// neue Funktion. Kriegt nur Kommandosstring uebergegen, werden ggf. gepuffert
// und dann weggeschrieben.
public varargs void BBWrite(string msg, int catmode) {
if ( !this_interactive() ||
(extern_call() &&
strstr(load_name(previous_object()), "/std/shells/") != 0 ) )
return;
string uid = getuid(this_interactive());
if (!member(ldata, uid))
AddTemporaryPlayer(uid);
else if (ldata[uid,D_LOGTIME] < time()) {
// Logzeit abgelaufen. -> ENDE.
if (ldata[uid,D_INDEX]) {
this_interactive()->__set_bb(0);
// es kann vorkommen, dass hier nen ProcessBuffer mit anderer uid
// drinhaengt. Ist aber egal, dann wird der Puffer halt naechstesmal
// geschrieben.
if (find_call_out(#'ProcessBuffer) == -1)
call_out(#'ProcessBuffer, 2, uid);
}
return;
}
// im synchronen Modus direkt auf Platte schreiben.
//DEBUG("BBWrite: Flags von +"+uid+": "+to_string(ldata[uid,D_FLAGS])
// +"\n");
if (ldata[uid,D_FLAGS] & FL_SYNC) {
//DEBUG("BBWrite: Syncmodus\n");
if (!catmode) {
msg = sprintf("%s: %s [%O]\n", strftime("%y%m%d-%H%M%S"),
msg, environment(this_interactive()));
}
else
msg = msg + "\n";
log_file( "ARCH/bb."+uid, msg, MAXLOGSIZE );
return;
}
// alle anderen werden erstmal gepuffert.
// wenn catmode und nen Index > 0 wird der Kram an den vorherigen Eintragen
// angehaengt.
int index = ldata[uid,D_INDEX];
if (catmode && index > 0) {
--index;
ldata[uid,D_LOG][index][DL_CMD] += msg;
}
else {
// Puffer vergroessern?
if (index >= sizeof(ldata[uid,D_LOG]))
ldata[uid,D_LOG]+=allocate(100);
ldata[uid,D_LOG][index] = ({ time(), msg,
object_name(environment(this_interactive()))
});
ldata[uid,D_INDEX]++;
// es kann vorkommen, dass hier nen ProcessBuffer mit anderer uid
// drinhaengt. Ist aber egal, dann wird der Puffer halt naechstesmal
// geschrieben.
if (index > MAXBUFFSIZE
&& find_call_out(#'ProcessBuffer) == -1)
call_out(#'ProcessBuffer, 2, uid);
}
}
// Alte Funktion, kriegt Strings, teilweise mit Datum, teilweise ohne,
// schrieb frueher nur weg. msg faengt entweder mit einem Datum/Zeit-String
// oder mit einem "->" an.
public void writebb( string msg )
{
int catmode;
if ( !this_interactive() ||
(extern_call() &&
strstr(load_name(previous_object()), "/std/shells/") != 0 ) )
return;
// erstmal String bereinigen.
msg = trim(msg,TRIM_RIGHT,"\n");
if (strstr(msg,"->") == 0) {
catmode=1;
msg= " -> " + msg[2..];
}
else {
// faengt mit Datumsstring an, erstes Leerzeichen ab dem zehnten Zeichen
// suchen und von dort abschneiden.
msg = msg[strstr(msg," ",10)+1 ..];
}
// Dann weitergeben
BBWrite(msg, catmode);
}
private void scan_bb_opfer()
{
string* lines;
object pl;
string uid;
// diese user werden 'permanent' ueberwacht, nicht nur direkt nach dem
// Einloggen.
lines = explode( lower_case( read_file("/secure/ARCH/BB_OPFER.dump")
|| "" ), "\n" )[2..];
foreach(string line : lines) {
if( sizeof(line) && line[0] != '#' ) {
uid=line[0 .. member(line,' ')-1];
AddTemporaryPlayer(uid);
ldata[uid,D_LOGTIME] = __INT_MAX__;
ldata[uid,D_FLAGS] = FL_PERMANENT;
pl = find_player(uid) || find_netdead(uid);
if (pl)
pl->__set_bb(1);
}
}
}
// Neuladen ist kein grosses Problem, weil der bbmaster ja automatisch
// neugeladen wird. Nebeneffekt ist lediglich, dass fuer alle laufenden Logs
// die Logzeit wieder von vorne anfaengt. Da das Schreiben der Puffer aber Lag
// verursachen kann, duerfen es nur EM+.
public varargs int remove(int silent) {
if (!ARCH_SECURITY)
return 0;
log_file("ARCH/bbmaster.log", strftime("%c: remove() called.\n"));
// alle Puffer leeren...
// ProcessAllBuffers() wird hierbei _ohne_ Limits aufgerufen! Kann fieses
// Lag erzeugen, aber sonst wurde der Kram evtl. nicht ordentlich
// geschrieben.
limited(#'ProcessAllBuffers);
destruct(this_object());
return 1;
}
// Alles ab hier nur zum Ueberwachen von FTP-Aktivitaeten.
private int player_exists( string user )
{
return master()->find_userinfo(user);
}
public int add( string user, int timeout )
{
if ( !ARCH_SECURITY || process_call() )
return -1;
if( !stringp(user) || !player_exists(lower_case(user)) || !intp(timeout) ||
!timeout )
return -2;
monitored[lower_case(user)] = timeout;
save_object( FTPSAVE );
return 1;
}
public int sub( string user )
{
if ( !ARCH_SECURITY || process_call() )
return -1;
if( !stringp(user) || !member( monitored, lower_case(user) ) )
return -2;
m_delete( monitored, lower_case(user) );
save_object( FTPSAVE );
return 1;
}
public void ftpbb( string user, string msg )
{
if( getuid(previous_object()) != ROOTID )
return;
if ( ldata[user,D_FLAGS] & FL_PERMANENT )
log_file( "ARCH/bb."+user, msg, 2000000 );
if ( monitored[user] ){
if ( monitored[user] > 0 && monitored[user] < time() )
sub( user );
else
CHMASTER->send( "FTP", capitalize(user), msg );
}
}