| // channeld.c |
| // |
| // $Id: channeld.c 9138 2015-02-03 21:46:56Z Zesstra $ |
| // |
| |
| #pragma strong_types |
| #pragma no_shadow // keine Shadowing... |
| #pragma no_clone |
| #pragma no_inherit |
| #pragma save_types |
| |
| #include <sys_debug.h> |
| #include <lpctypes.h> |
| #include <wizlevels.h> |
| |
| #include <properties.h> |
| #include <config.h> |
| #include <language.h> |
| |
| #define NEED_PROTOTYPES |
| #include "channel.h" |
| |
| #define CMNAME "<MasteR>" |
| #define CHANNEL_SAVE "/p/daemon/save/channeld" |
| #define MEMORY "/secure/memory" |
| #define TIMEOUT (time() - 60) |
| #define CMDS ({C_FIND, C_LIST, C_JOIN, C_LEAVE, C_SEND, C_NEW}) |
| |
| /* list of channels and their corresponding data (members, etc.) |
| channels = ([string channelname : ({ ({object* members}), |
| closure access_rights, |
| string channel_info, |
| string|object master_object, |
| string channelname }) ]) */ |
| private nosave mapping channels; |
| //private nosave mapping lowerch; // unused |
| |
| /* channel history |
| mapping channelH = ([ string channelname : ({ string channelname, |
| string sender, |
| string msg, |
| int msg_type }) ]) */ |
| private nosave mapping channelH; |
| |
| /* list of global channelmaster stats |
| mapping stats: ([ "time" : int object_time(), |
| "boot" : string load_name(previous_object()), |
| "new" : int total_channels_created, |
| "disposed" : int total_channels_removed ]) */ |
| private nosave mapping stats; |
| |
| /* channel cache |
| mapping channelC = ([ string channelname : ({ string I_NAME, |
| string I_INFO, |
| int time() }) ]) */ |
| private mapping channelC; |
| |
| /* list of players' banned commands, if any |
| mapping channelB = ([ string playername : ({ string* banned_commands })])*/ |
| private mapping channelB; |
| |
| /* timeout cache for player commands (timeout = 60 s, see above) |
| ensures that the same command is only executed once per minute, max |
| mapping Tcmd = ([ "lag": int timestamp, |
| "uptime": int timestamp, |
| "statistik": int timestamp]) */ |
| private mapping Tcmd = ([]); |
| |
| /* Flag to indicate that data changes have occurred and that we need saving |
| in the next save_object() run. |
| set to 0 or 1 */ |
| private int save_me_soon; |
| |
| |
| // BEGIN OF THE CHANNEL MASTER ADMINISTRATIVE PART |
| |
| #define RECV 0 |
| #define SEND 1 |
| #define FLAG 2 |
| |
| // Channel flags |
| // Levelbeschraenkungen gegen Magierlevel (query_wiz_level) pruefen, nicht |
| // P_LEVEL. |
| #define F_WIZARD 1 |
| // Keine Gaeste. ;-) |
| #define F_NOGUEST 2 |
| |
| private nosave mapping admin = m_allocate(0, 3); |
| |
| int check(string ch, object pl, string cmd) |
| { |
| int level; |
| |
| if ((admin[ch, FLAG] & F_NOGUEST) && pl->QueryGuest()) |
| return 0; |
| |
| if ((admin[ch, FLAG] & F_WIZARD) && query_wiz_level(pl) < SEER_LVL) |
| return 0; |
| |
| level = (admin[ch, FLAG] & F_WIZARD |
| ? query_wiz_level(pl) |
| : pl->QueryProp(P_LEVEL)); |
| |
| switch (cmd) |
| { |
| case C_FIND: |
| case C_LIST: |
| case C_JOIN: |
| if (admin[ch, RECV] == -1) |
| return 0; |
| if (admin[ch, RECV] <= level) |
| return 1; |
| break; |
| |
| case C_SEND: |
| if (admin[ch, SEND] == -1) |
| return 0; |
| if (admin[ch, SEND] <= level) |
| return 1; |
| break; |
| |
| case C_LEAVE: |
| return 1; |
| |
| default: |
| break; |
| } |
| return (0); |
| } |
| |
| private int CountUser(mapping l) |
| { |
| mapping n = ([]); |
| walk_mapping(l, function void (string chan_name, mixed * chan_data) |
| { |
| n += mkmapping(chan_data[I_MEMBER]); |
| }); |
| return sizeof(n); |
| } |
| |
| private void banned(string n, string* cmds, string res) |
| { |
| res += sprintf("%s [%s], ", capitalize(n), implode(cmds, ",")); |
| } |
| |
| void ChannelMessage(mixed msg) |
| { |
| string ret, mesg; |
| mixed lag; |
| int max, rekord; |
| string tmp; |
| |
| if (msg[1] == this_object() || !stringp(msg[2]) || |
| msg[0] != CMNAME || previous_object() != this_object()) |
| return; |
| |
| mesg = lower_case(msg[2]); |
| |
| if (!strstr("hilfe", mesg) && sizeof(mesg) <= 5) |
| { |
| ret = "Folgende Kommandos gibt es: hilfe, lag, up[time], statistik, bann"; |
| } |
| else if (!strstr("lag", mesg) && sizeof(mesg) <= 3) |
| { |
| // TODO: invert logic to make this better understandable, e.g. |
| // if ( CMD_AVAILABLE("lag") ) and rearrange code block |
| if (Tcmd["lag"] > TIMEOUT) |
| return; |
| |
| Tcmd["lag"] = time(); |
| lag = "/p/daemon/lag-o-daemon"->read_ext_lag_data(); |
| ret = sprintf("Lag: %.1f%%/60, %.1f%%/15, %.1f%%/5, %.1f%%/1, " |
| "%.1f%%/20s, %.1f%%/2s", |
| lag[5], lag[4], lag[3], lag[2], lag[1], lag[0]); |
| call_out(#'send, 2, CMNAME, this_object(), ret); |
| ret = query_load_average(); |
| } |
| // TODO: move this logic to a function with a self-explanatory name |
| else if (!strstr("uptime", mesg) && sizeof(mesg) <= 6) |
| { |
| if (Tcmd["uptime"] > TIMEOUT) |
| return; |
| |
| Tcmd["uptime"] = time(); |
| |
| if (file_size("/etc/maxusers") <= 0) |
| { |
| ret = "Diese Information liegt nicht vor."; |
| } |
| else |
| { |
| sscanf(read_file("/etc/maxusers"), "%d %s", max, tmp); |
| sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, tmp); |
| ret = sprintf("Das MUD laeuft jetzt %s. Es sind momentan %d Spieler " |
| "eingeloggt; das Maximum lag heute bei %d und der Rekord " |
| "bisher ist %d.", uptime(), sizeof(users()), max, rekord); |
| } |
| } |
| else if (!strstr("statistik", mesg) && sizeof(mesg) <= 9) |
| { |
| if (Tcmd["statistik"] > TIMEOUT) |
| return; |
| |
| Tcmd["statistik"] = time(); |
| ret = sprintf( |
| "Im Moment sind insgesamt %d Ebenen mit %d Teilnehmern aktiv.\n" |
| "Der %s wurde das letzte mal am %s von %s neu gestartet.\n" |
| "Seitdem wurden %d Ebenen neu erzeugt und %d zerstoert.\n", |
| sizeof(channels), CountUser(channels), CMNAME, |
| dtime(stats["time"]), stats["boot"], stats["new"], stats["dispose"]); |
| } |
| else if (!strstr(mesg, "bann")) |
| { |
| string pl, cmd; |
| |
| if (mesg == "bann") |
| { |
| if (sizeof(channelB)) |
| { |
| ret = ""; |
| walk_mapping(channelB, #'banned, &ret); |
| ret = "Fuer folgende Spieler besteht ein Bann: " + ret; |
| } |
| else |
| { |
| ret = "Zur Zeit ist kein Bann aktiv."; |
| } |
| } |
| else |
| { |
| if (sscanf(mesg, "bann %s %s", pl, cmd) == 2 && IS_DEPUTY(msg[1])) |
| { |
| pl = lower_case(pl); |
| cmd = lower_case(cmd); |
| |
| if (member(CMDS, cmd) != -1) |
| { |
| if (!pointerp(channelB[pl])) |
| channelB[pl] = ({}); |
| |
| if (member(channelB[pl], cmd) != -1) |
| channelB[pl] -= ({ cmd }); |
| else |
| channelB[pl] += ({ cmd }); |
| ret = "Fuer '" + capitalize(pl) + "' besteht " |
| + (sizeof(channelB[pl]) ? |
| "folgender Bann: " + implode(channelB[pl], ", ") : |
| "kein Bann mehr."); |
| |
| if (!sizeof(channelB[pl])) |
| channelB = m_copy_delete(channelB, pl); |
| |
| save_object(CHANNEL_SAVE); |
| } |
| else |
| { |
| ret = "Das Kommando '" + cmd + "' ist unbekannt. " |
| "Erlaubte Kommandos: "+ implode(CMDS, ", "); |
| } |
| } |
| else |
| { |
| if (!IS_ARCH(msg[1])) |
| return; |
| else |
| ret = "Syntax: bann <name> <kommando>"; |
| } |
| } |
| } |
| else if (mesg == "lust") |
| { |
| mixed t, up; |
| |
| if (Tcmd["lag"] > TIMEOUT || |
| Tcmd["statistik"] > TIMEOUT || |
| Tcmd["uptime"] > TIMEOUT) |
| return; |
| |
| Tcmd["lag"] = time(); |
| Tcmd["statistik"] = time(); |
| Tcmd["uptime"] = time(); |
| lag = "/p/daemon/lag-o-daemon"->read_lag_data(); |
| sscanf(read_file("/etc/maxusers"), "%d %s", max, tmp); |
| sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, tmp); |
| t = time() - last_reboot_time(); |
| up = ""; |
| |
| if (t >= 86400) |
| up += sprintf("%dT", t / 86400); |
| |
| if (t >= 3600) |
| up += sprintf("%dh", (t = t % 86400) / 3600); |
| |
| if (t > 60) |
| up += sprintf("%dm", (t = t % 3600) / 60); |
| |
| up += sprintf("%ds", t % 60); |
| ret = sprintf("%.1f%%/15 %.1f%%/1 %s %d:%d:%d E:%d T:%d", |
| lag[1], lag[2], up, sizeof(users()), max, rekord, |
| sizeof(channels), CountUser(channels)); |
| } |
| else |
| { |
| return; |
| } |
| |
| call_out(#'send, 2, CMNAME, this_object(), ret); |
| } |
| |
| // setup() -- set up a channel and register it |
| // arguments are stored in the following order: |
| // ({ channel name, |
| // receive level, send level, |
| // flags, |
| // description, |
| // master obj |
| // }) |
| private void setup(mixed c) |
| { |
| closure cl; |
| object m; |
| string d; |
| d = "- Keine Beschreibung -"; |
| m = this_object(); |
| |
| if (sizeof(c) && sizeof(c[0]) > 1 && c[0][0] == '\\') |
| c[0] = c[0][1..]; |
| |
| switch (sizeof(c)) |
| { |
| case 6: |
| if (!stringp(c[5]) || !sizeof(c[5]) || |
| (catch(m = load_object(c[5]); publish) || |
| !objectp(m))) |
| m = this_object(); |
| |
| case 5: |
| d = stringp(c[4]) || closurep(c[4]) ? c[4] : d; |
| |
| case 4: |
| admin[c[0], FLAG] = to_int(c[3]); |
| |
| case 3: |
| admin[c[0], SEND] = to_int(c[2]); |
| |
| case 2: |
| admin[c[0], RECV] = to_int(c[1]); |
| break; |
| |
| case 0: |
| default: |
| return; |
| } |
| |
| switch (new(c[0], m, d)) |
| { |
| case E_ACCESS_DENIED: |
| log_file("CHANNEL", sprintf("[%s] %s: %O: error, access denied\n", |
| dtime(time()), c[0], m)); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return; |
| } |
| |
| void initialize() |
| { |
| mixed tmp; |
| #if !defined(__TESTMUD__) && MUDNAME=="MorgenGrauen" |
| tmp = read_file(object_name(this_object()) + ".init"); |
| #else |
| tmp = read_file(object_name(this_object()) + ".init.testmud"); |
| #endif |
| |
| if (!stringp(tmp)) |
| return; |
| |
| tmp = regexp(old_explode(tmp, "\n"), "^[^#]"); |
| tmp = map(tmp, #'regexplode, "[^:][^:]*$|[ \\t]*:[ \\t]*"); |
| tmp = map(tmp, #'regexp, "^[^: \\t]"); |
| map(tmp, #'setup); |
| } |
| |
| // BEGIN OF THE CHANNEL MASTER IMPLEMENTATION |
| |
| protected void create() |
| { |
| seteuid(getuid()); |
| restore_object(CHANNEL_SAVE); |
| |
| if (!channelC) |
| channelC = ([]); |
| |
| if (!channelB) |
| channelB = ([]); |
| |
| channels = ([]); |
| |
| /* Die Channel-History wird nicht nur lokal sondern auch noch im Memory |
| gespeichert, dadurch bleibt sie auch ueber ein Reload erhalten. |
| Der folgende Code versucht, den Zeiger aus dem Memory zu holen. Falls |
| das nicht moeglich ist, wird ein neuer erzeugt und gegebenenfalls im |
| Memory abgelegt. */ |
| |
| // Hab ich die noetigen Rechte im Memory? |
| if (call_other(MEMORY, "HaveRights")) |
| { |
| // Objektpointer laden |
| channelH = (mixed) call_other(MEMORY, "Load", "History"); |
| |
| // Wenns nich geklappt hat, hat der Memory noch keinen Zeiger, dann |
| if (!mappingp(channelH)) { |
| // Zeiger erzeugen |
| channelH = ([]); |
| // und in den Memory schreiben |
| call_other(MEMORY, "Save", "History", channelH); |
| } |
| } |
| else |
| { |
| // Keine Rechte im Memory, dann wird mit einem lokalen Zeiger gearbeitet. |
| channelH = ([]); |
| } |
| |
| stats = (["time": time(), |
| "boot": capitalize(getuid(previous_object()) || "<Unbekannt>")]); |
| new (CMNAME, this_object(), "Zentrale Informationen zu den Ebenen"); |
| initialize(); |
| map_objects(efun::users(), "RegisterChannels"); |
| this_object()->send(CMNAME, this_object(), |
| sprintf("%d Ebenen mit %d Teilnehmern initialisiert.", |
| sizeof(channels), |
| CountUser(channels))); |
| } |
| |
| // reset() and cache_to() - Cache Timeout, remove timed out cached channels |
| // SEE: new, send |
| private int cache_to(string key, mapping m, int t) |
| { |
| if (!pointerp(m[key]) || m[key][2] + 43200 > t) |
| return 1; |
| |
| return (0); |
| } |
| |
| varargs void reset(int nonstd) |
| { |
| channelC = filter_indices(channelC, #'cache_to, channelC, time()); |
| |
| if (save_me_soon) |
| { |
| save_me_soon = 0; |
| save_object(CHANNEL_SAVE); |
| } |
| } |
| |
| // name() - define the name of this object. |
| string name() |
| { |
| return CMNAME; |
| } |
| |
| string Name() |
| { |
| return CMNAME; |
| } |
| |
| // access() - check access by looking for the right argument types and |
| // calling access closures respectively |
| // SEE: new, join, leave, send, list, users |
| // Note: <pl> is usually an object, only the master supplies a string during |
| // runtime error handling. |
| varargs private int access(mixed ch, mixed pl, string cmd, string txt) |
| { |
| mixed co, m; |
| |
| if (!stringp(ch) || !sizeof(ch = lower_case(ch)) || !channels[ch]) |
| return 0; |
| |
| if (!channels[ch][I_ACCESS] || !previous_object(1) || !extern_call() || |
| previous_object(1) == this_object() || |
| (stringp(channels[ch][I_MASTER]) && |
| previous_object(1) == find_object(channels[ch][I_MASTER])) || |
| getuid(previous_object(1)) == ROOTID) |
| return 2; |
| |
| if (!objectp(pl) || |
| ((previous_object(1) != pl) && (previous_object(1) != this_object()))) |
| return 0; |
| |
| if (pointerp(channelB[getuid(pl)]) && |
| member(channelB[getuid(pl)], cmd) != -1) |
| return 0; |
| |
| if (stringp(channels[ch][I_MASTER]) && |
| (!(m = find_object(channels[ch][I_MASTER])) || |
| (!to_object(channels[ch][I_ACCESS]) || |
| get_type_info(channels[ch][I_ACCESS])[1]))) |
| { |
| string err; |
| |
| if (!objectp(m)) |
| err = catch(load_object(channels[ch][I_MASTER]); publish); |
| |
| if (!err && |
| ((!to_object(channels[ch][I_ACCESS]) || |
| get_type_info(channels[ch][I_ACCESS])[1]) && |
| !closurep(channels[ch][I_ACCESS] = |
| symbol_function("check", find_object(channels[ch][I_MASTER]))))) |
| { |
| log_file("CHANNEL", sprintf("[%s] %O -> %O\n", |
| dtime(time()), channels[ch][I_MASTER], |
| err)); |
| channels = m_copy_delete(channels, ch); |
| return 0; |
| } |
| |
| this_object()->join(ch, find_object(channels[ch][I_MASTER])); |
| } |
| |
| if (closurep(channels[ch][I_ACCESS])) |
| return funcall(channels[ch][I_ACCESS], |
| channels[ch][I_NAME], pl, cmd, &txt); |
| } |
| |
| // new() - create a new channel |
| // a channel with name 'ch' is created, the player is the master |
| // info may contain a string which describes the channel or a closure |
| // to display up-to-date information, check may contain a closure |
| // called when a join/leave/send/list/users message is received |
| // SEE: access |
| #define IGNORE "^/xx" |
| |
| varargs int new(string ch, object pl, mixed info) |
| { |
| mixed pls; |
| |
| if (!objectp(pl) || !stringp(ch) || !sizeof(ch) || |
| channels[lower_case(ch)] || (pl == this_object() && extern_call()) || |
| sizeof(channels) >= MAX_CHANNELS || |
| sizeof(regexp(({ object_name(pl) }), IGNORE)) || |
| (pointerp(channelB[getuid(pl)]) && |
| member(channelB[getuid(pl)], C_NEW) != -1)) |
| return E_ACCESS_DENIED; |
| |
| if (!info) |
| { |
| if (channelC[lower_case(ch)]) |
| { |
| ch = channelC[lower_case(ch)][0]; |
| info = channelC[lower_case(ch)][1]; |
| } |
| else |
| { |
| return E_ACCESS_DENIED; |
| } |
| } |
| else |
| { |
| channelC[lower_case(ch)] = ({ ch, info, time() }); |
| } |
| |
| pls = ({ pl }); |
| channels[lower_case(ch)] = ({ |
| pls, |
| symbol_function("check", pl) || #'check, info, |
| (!living(pl) && !clonep(pl) && pl != this_object() |
| ? object_name(pl) |
| : pl), |
| ch}); |
| |
| // ChannelH fuer einen Kanal nur dann initialisieren, wenn es sie noch nich gibt. |
| if (!pointerp(channelH[lower_case(ch)])) |
| channelH[lower_case(ch)] = ({}); |
| |
| if (pl != this_object()) |
| log_file("CHANNEL.new", sprintf("[%s] %O: %O %O\n", |
| dtime(time()), ch, pl, info)); |
| |
| if (!pl->QueryProp(P_INVIS)) |
| this_object()->send(CMNAME, pl, |
| "laesst die Ebene '" + ch + "' entstehen.", MSG_EMOTE); |
| |
| stats["new"]++; |
| save_me_soon = 1; |
| return (0); |
| } |
| |
| // join() - join a channel |
| // this function checks whether the player 'pl' is allowed to join |
| // the channel 'ch' and add if successful, one cannot join a channel |
| // twice |
| // SEE: leave, access |
| int join(string ch, object pl) |
| { |
| if (!funcall(#'access,&ch, pl, C_JOIN)) |
| return E_ACCESS_DENIED; |
| |
| if (member(channels[ch][I_MEMBER], pl) != -1) |
| return E_ALREADY_JOINED; |
| |
| channels[ch][I_MEMBER] += ({ pl }); |
| return (0); |
| } |
| |
| // leave() - leave a channel |
| // the access check in this function is just there for completeness |
| // one should always be allowed to leave a channel. |
| // if there are no players left on the channel it will vanish, unless |
| // its master is this object. |
| // SEE: join, access |
| int leave(string ch, object pl) |
| { |
| int pos; |
| |
| if (!funcall(#'access,&ch, pl, C_LEAVE)) |
| return E_ACCESS_DENIED; |
| |
| channels[ch][I_MEMBER] -= ({0}); // kaputte Objekte erstmal raus |
| |
| if ((pos = member(channels[ch][I_MEMBER], pl)) == -1) |
| return E_NOT_MEMBER; |
| |
| if (pl == channels[ch][I_MASTER] && sizeof(channels[ch][I_MEMBER]) > 1) |
| { |
| channels[ch][I_MASTER] = channels[ch][I_MEMBER][1]; |
| |
| if (!pl->QueryProp(P_INVIS)) |
| this_object()->send(ch, pl, "uebergibt die Ebene an " + |
| channels[ch][I_MASTER]->name(WEN) + ".", MSG_EMOTE); |
| } |
| channels[ch][I_MEMBER][pos..pos] = ({ }); |
| |
| if (!sizeof(channels[ch][I_MEMBER]) && !stringp(channels[ch][I_MASTER])) |
| { |
| // delete the channel that has no members |
| if (!pl->QueryProp(P_INVIS)) |
| this_object()->send(CMNAME, pl, |
| "verlaesst als "+ |
| (pl->QueryProp(P_GENDER) == 1 ? "Letzter" : "Letzte")+ |
| " die Ebene '"+channels[ch][I_NAME]+"', worauf diese sich in " |
| "einem Blitz oktarinen Lichts aufloest.", MSG_EMOTE); |
| |
| channelC[lower_case(ch)] = |
| ({ channels[ch][I_NAME], channels[ch][I_INFO], time() }); |
| m_delete(channels, lower_case(ch)); |
| // Wird ein Channel entfernt, wird auch seine History geloescht |
| // TODO: this foils the attempts at creating a persistent channel |
| // history via /secure/memory.c |
| channelH = m_copy_delete(channelH, lower_case(ch)); |
| stats["dispose"]++; |
| save_me_soon = 1; |
| } |
| return (0); |
| } |
| |
| // send() - send a message to all recipients of the specified channel 'ch' |
| // checks if 'pl' is allowed to send a message and sends if success- |
| // ful a message with type 'type' |
| // 'pl' must be an object, the message is attributed to it. e.g. |
| // ignore checks use it. It can be != previous_object() |
| // SEE: access, ch.h |
| varargs int send(string ch, object pl, string msg, int type) |
| { |
| int a; |
| |
| if (!(a = funcall(#'access, &ch, pl, C_SEND, &msg))) |
| return E_ACCESS_DENIED; |
| |
| if (a < 2 && member(channels[ch][I_MEMBER], pl) == -1) |
| return E_NOT_MEMBER; |
| |
| if (!msg || !stringp(msg) || !sizeof(msg)) |
| return E_EMPTY_MESSAGE; |
| |
| map_objects(channels[ch][I_MEMBER], "ChannelMessage", |
| ({ channels[ch][I_NAME], pl, msg, type })); |
| |
| if (sizeof(channelH[ch]) > MAX_HIST_SIZE) |
| channelH[ch] = channelH[ch][1..]; |
| |
| channelH[ch] += |
| ({ ({ channels[ch][I_NAME], |
| (stringp(pl) |
| ? pl |
| : (pl->QueryProp(P_INVIS) |
| ? "/(" + capitalize(getuid(pl)) + ")$" |
| : "") |
| + (pl->Name(WER, 2) || "<Unbekannt>")), |
| msg + " <" + strftime("%a, %H:%M:%S") + ">\n", |
| type }) }); |
| return (0); |
| } |
| |
| private void clean(string n, mixed a) |
| { |
| a[0] -= ({ 0 }); |
| } |
| |
| // list() - list all channels, that are at least receivable by 'pl' |
| // returns a mapping, |
| // SEE: access, channels |
| mixed list(object pl) |
| { |
| mapping chs = filter_indices(channels, #'access, pl, C_LIST); |
| walk_mapping(chs, #'clean); |
| |
| if (!sizeof(chs)) |
| return E_ACCESS_DENIED; |
| return deep_copy(chs); |
| } |
| |
| // find() - find a channel by its name (may be partial) |
| // returns an array for multiple results and 0 for no matching name |
| // SEE: access |
| mixed find(string ch, object pl) |
| { |
| mixed chs, s; |
| |
| if (stringp(ch)) |
| ch = lower_case(ch); |
| |
| if (!sizeof(regexp(({ch}), "^[<>a-z0-9#-]*$"))) |
| return 0; // RUM |
| |
| if (!sizeof(chs = regexp(m_indices(channels), "^" + ch + "$"))) |
| chs = regexp(m_indices(channels), "^" + ch); |
| |
| if ((s = sizeof(chs)) > 1) |
| { |
| if (sizeof(chs = filter(chs, #'access, pl, C_FIND)) == 1) |
| return channels[chs[0]][I_NAME]; |
| else |
| return chs; |
| } |
| |
| return ((s && funcall(#'access,chs[0], pl, C_FIND)) |
| ? channels[chs[0]][I_NAME] |
| : 0); |
| } |
| |
| // history() - get the history of a channel |
| // SEE: access |
| mixed history(string ch, object pl) |
| { |
| if (!funcall(#'access, &ch, pl, C_JOIN)) |
| return E_ACCESS_DENIED; |
| return deep_copy(channelH[ch]); |
| } |
| |
| // remove - remove a channel (wird aus der Shell aufgerufen) |
| // SEE: new |
| mixed remove(string ch, object pl) |
| { |
| mixed members; |
| |
| if (previous_object() != this_object()) |
| { |
| if (!stringp(ch) || |
| pl != this_player() || this_player() != this_interactive() || |
| this_interactive() != previous_object() || |
| !IS_ARCH(this_interactive())) |
| return E_ACCESS_DENIED; |
| } |
| |
| if (channels[lower_case(ch)]) |
| { |
| channels[lower_case(ch)][I_MEMBER] = |
| filter_objects(channels[lower_case(ch)][I_MEMBER], |
| "QueryProp", P_CHANNELS); |
| map(channels[lower_case(ch)][I_MEMBER], |
| function mixed(object listener) |
| { |
| string* chans = listener->QueryProp(P_CHANNELS); |
| chans -= ({lower_case(ch)}); |
| ({string*})listener->SetProp(P_CHANNELS, chans); |
| }); |
| channels = m_copy_delete(channels, lower_case(ch)); |
| |
| // Wird ein Channel entfernt, wird auch seine History geloescht |
| if (pointerp(channelH[lower_case(ch)])) |
| channelH = m_copy_delete(channelH, lower_case(ch)); |
| |
| stats["dispose"]++; |
| } |
| |
| if (!channelC[lower_case(ch)]) |
| return E_ACCESS_DENIED; |
| |
| channelC = m_copy_delete(channelC, lower_case(ch)); |
| save_me_soon = 1; |
| return (0); |
| } |
| |
| // Wird aus der Shell aufgerufen |
| mixed clear_history(string ch) |
| { |
| // Sicherheitsabfragen |
| if (previous_object() != this_object()) |
| { |
| if (!stringp(ch) || |
| this_player() != this_interactive() || |
| this_interactive() != previous_object() || |
| !IS_ARCH(this_interactive())) |
| return E_ACCESS_DENIED; |
| } |
| |
| // History des Channels loeschen |
| if (pointerp(channelH[lower_case(ch)])) |
| channelH[lower_case(ch)] = ({}); |
| |
| return 0; |
| } |