// File: channels.c (A daemon) // Author: Inspiral@Tabor, et al // Created: 93-05-10 // Purpose: To efficiently handle player communication. // // REVISON HISTORY: // 930725 - Fixed problem with creating spurious channels! -- Inspiral // 930801 - Added scan_config() -- Inspiral // 930803 - Reformatted data structure to use // more arrays than mappings -- Inspiral // 930811 - Removed all reliances on query() and set() lfuns. -- Inspiral // 930818 - Added log(), tell_user(), secure() -- Inspiral // 930826 - fixed bug in del user -- Pallando // 931004 - added emotes -- Rust // 940322 - Rewritten to be faster, and to index users by // object -- Inspiral // 940911 - Added missing prototpye -- Leto // 950312 - cleaned up format & changed channels variable set // in the player to a mapping type instead of an // array since Earthmud uses the mapping version -- Zifi@earthmud // 951001 - Added parse_amcp_channel -- Leto@Earthmud // 960325 - Added backlog for each channel -- Perrin@Earthmud // 960429 - Fixed channel emotes and formatting problems -- Emeradii@Earthmud #include <channels.h> #include <commands.h> #include <emoted.h> #include <daemons.h> varargs mixed display_channel(string chan, object viewer); int register_channel(string chan, object user, int state); int log(string log_mesg); void add_chan_backlog(string chan,string mesg); mapping channels; // contains all our channel stats mapping alias; // a rude alias mechanism #define BACKLOG_MAX 15 int tell_user(object user, string mesg) { tell_object(user, wrap(sprintf("CHANNELS:\t%s\n", mesg))); return 1; } #define NO_NEW_CHANNELS static varargs int create_channel(string chan) { if (channels[chan]) return 1; channels[chan] = ([ ]); channels[chan]["vis"] = ({ }); channels[chan]["backlog"] = ({ }); return 1; } static int kill_user(object user, string chan) { if (undefinedp(channels[chan])) return 0; channels[chan]["vis"] -= ({user}); return 1; } static varargs int delete_channel(string chan) { if (undefinedp(channels[chan])) return 0; map_delete(channels, chan); return 1; } static varargs int add_user(string chan, object user) { string *toggle_list,priv; if (!chan || !user) return 0; if (!pointerp(toggle_list = (string *) user->query("channels/toggle"))) { user->set("channels/toggle",({ })); toggle_list = ({ }); } // chan doesn't exist...do we have permission to create it? if (undefinedp(channels[chan])) { #ifdef NO_NEW_CHANNELS return 0; #else create_channel(chan, user); #endif } if (!undefinedp(priv = channels[chan]["priv"]) && !member_group(geteuid(user), priv)) return 0; if (member_array(chan,toggle_list) == -1) user->add("channels/toggle",({chan})); channels[chan]["vis"] = uniq_array(channels[chan]["vis"] + ({user})); return 1; } static int kill_users(mixed chan) { int i; if (!chan) chan = keys(channels); else chan = ({chan}); i = sizeof(chan); while(i--) channels[chan[i]]["vis"] -= ({ 0 }); return 1; } varargs int initialize_user(object whom) { int i, j; object *list; string priv; string *current_list; if (!whom || !living(whom)) list = users(); else list = ({whom}); // We have one user. i = sizeof(list); while(i--) { current_list = (string *) list[i]->query("channels/toggle"); if (!pointerp(current_list)) { list[i]->set("channels/toggle",({ })); current_list = ({ }); } j = sizeof(current_list); while(j--) register_channel(current_list[j], list[i], ADD); } return 1; } varargs int parse_channel(string chan, string mesg, string name, int emote) { int i; string *ignore; object *list; // Holds the list of all players currently on chan string ext_ob, err; string plural; // Holds the plural form of the channel name, chan string verb, cmd; if (alias[chan]) chan = alias[chan]; if (undefinedp(channels[chan])) return 0; kill_users(chan); if (undefinedp((list = channels[chan]["vis"]))) return 0; i = sizeof(list); // Optimize is. // Channel format is wrong in the daemon..Attempt a fix, but log. if (!pointerp(list)) { channels[chan]["vis"] = ({ }); initialize_user(); // Shall we reconfigure? return log(sprintf("parse_channel: channel %s is not a pointer! Fixing.", identify(chan))); } // Is the name flag not given? or, is name not even in the list? if (this_player() && member_array(this_player(),list) == -1) return 0; if (this_player() && !name) name = (string) this_player()->query("cap_name"); if (!mesg || mesg == "") { CMD_SHOW->cmd_show(chan); return 1; } // Output backlog stuff here if (mesg[0..4]=="/last") { if (!sizeof(channels[chan]["backlog"])) message("channels","No backlog yet,sorry.\n",this_player()); else message("channels",sprintf("Backlog of channel %s:\n%s",chan, implode(channels[chan]["backlog"],"")+"\n"),this_player()); return 1; } if (undefinedp((plural = channels[chan]["plural"]))) plural = pluralize_verb(chan); if (mesg[0] == ';') { if (!sscanf(mesg[1..],"%s %s",verb,cmd)) verb = mesg[1..]; mesg = EMOTE_D->return_parse(verb,cmd); if (!mesg) { if (!cmd) cmd = ""; mesg = name + " " + verb + " " + cmd; } emote = 1; } else if (mesg[0..5] == ":emote") { if (!sscanf(mesg[7..],"%s %s",verb,cmd)) verb = mesg[7..]; mesg = EMOTE_D->return_parse(verb,cmd); if (!mesg) { if (!cmd) cmd = ""; mesg = name + " " + verb + " " + cmd; } emote = 1; } else if (emote) { mesg = name + " " + mesg; } while(i--) { if (!objectp(list[i])) continue; if (list[i] == this_player()) { if (emote) { message("channels",iwrap(capitalize(chan)+": "+mesg),list[i]); add_chan_backlog(chan,iwrap(capitalize(chan)+": "+mesg)); } else { message("channels",iwrap("You "+chan+": "+mesg),list[i]); add_chan_backlog(chan,iwrap(name+" "+plural+": "+mesg)); } } else { ignore = (string *) list[i]->query("ignore"); if (!pointerp(ignore)) ignore = ({ }); if (member_array(lower_case(name), ignore) != -1) continue; if (list[i]->getenv("add_newline")) tell_object(list[i], "\n"); if (emote) message("channels",iwrap(capitalize(chan)+": "+mesg),list[i]); else message("channels",iwrap(name+" "+plural+": "+mesg),list[i]); continue; } } // while() // Do we have an apply to an external object? chan = lower_case(chan); if (!undefinedp((ext_ob = channels[chan]["object"]))) { // format of object flag: alias#object sscanf(ext_ob,"%s#%s", chan, ext_ob); // Attempt the apply, log all errors if (err = catch(ext_ob->daemon_apply(this_player(), chan, mesg, emote))) log(sprintf("parse_channel:\tError in %s:\n%s", ext_ob, err)); } return 1; } varargs mixed display_channel(string chan, object viewer) { mixed list; if(!chan || chan == "") list = keys(channels); else { if(undefinedp(list = channels[chan])) return sprintf("There is no such channel: %s.",identify(chan)); list = channels[chan]["vis"]; } if(!pointerp(list) || !sizeof(list)) { channels[chan]["vis"] = ({ }); return sprintf("CHANNELS:\tNo Users Subscribed to %s", identify(chan)); } if(chan && chan != "") list = filter_array(list, "flush_list", this_object(), viewer); list = map_array(list, "get_cap", this_object()); list = (implode(list,",")); // Leto.. +" <End>"); return list; } static int scan_config() { string *list, *split_line, line; int i, j; list = update_file(CHANNELS_CONFIG); i = sizeof(list); // Once through the i for each non-"#" line in config file while(i--) { line = list[i]; if(line) { split_line = explode(line, ":"); j = sizeof(split_line); channels[split_line[0]] = allocate_mapping(6); channels[split_line[0]]["vis"] = ({ }); while(j--) { if((split_line[j] != "X") && (CHANNELS_INFO[j] != "")) channels[split_line[0]][CHANNELS_INFO[j]] = split_line[j]; } // while(j) } // IF (LINE) } // while(i) return 1; } int log(string log_mesg) { if(!log_mesg || log_mesg == "") return 0; log_file(CHANNELS_LOG, sprintf("%s %O - %s\n", intl_date_stamp(1), previous_object(), log_mesg)); return 0; } mapping q() { return copy(channels); } mapping a() { return copy(alias); } void process_alias() { int i; string *list, temp1; list = keys(channels); i = sizeof(list); while(i--) { if(!undefinedp(temp1 = channels[list[i]]["object"])) if(sscanf(temp1, "%s#%*s", temp1) == 2) alias += ([temp1 : list[i]]); } } string get_cap(object ob) { return (string) ob->query("cap_name"); } int flush_list(object user, object viewer) { return (objectp(user) && visible(user, viewer) && interactive(user)); } void create() { channels = allocate_mapping(20); alias = allocate_mapping(20); seteuid(getuid()); scan_config(); process_alias(); call_out("initialize_user", 2); } varargs int register_channel(string chan, object user, int state) { int ret; if (nullp(state)) return notify_fail("register_channel: no state given!"); switch(state) { case DEL: ret = delete_channel(chan, user); break; case CRE: ret = create_channel(chan, user, state); break; case ADD: ret = add_user(chan, user); break; case KIL: ret = kill_user(user, chan); break; default : ret = 0; break; } return ret; } // If all parsing of <foo>bar fails, we arrive at this function to see // if the request might be a valid message class. We return 0 if it // wasn't, and 1 if it was. We're assuming all strings are valid. int parse_amcp_channel(string command, string mesg) { string *channels, control, channelname; channels = previous_object()->query("channels/toggle"); command = lower_case(command); if(member_array(command,channels)!=-1) { if (!mesg) this_player()->receive_message(command,display_channel( command,this_player() )); return parse_channel(command,mesg); // channel message sent. } // is it a control message then ? if(sscanf(command,"channel&%s",control)!=1) return 0; // We don't get it. //write("CHAN:"+control); if(control=="list") { this_player()->dump_amcp_channels(); return 1; } if(sscanf(control,"%s&%s",control,channelname)!=2) { this_player()->receive_message("error","Unknown channel control message.\n"); return 1; } if(control=="subscribe") return CMD_TUNE->cmd_tune(channelname+" in"); if(control=="unsubscribe") return CMD_TUNE->cmd_tune(channelname+" out"); // Guess it's an unknown control message. this_player()->receive_message("error","Unknown channel control message.\n"); return 1; } void add_chan_backlog(string chan,string mesg) { string *backlog = channels[chan]["backlog"]; int count = sizeof(channels[chan]["backlog"]); // if(undefinedp(channels[chan]["backlog"])) if(!count) channels[chan]["backlog"] = ({}); if(count >= BACKLOG_MAX) backlog = backlog[1..]; if (!backlog) backlog = ({ mesg }); else backlog = backlog + ({ mesg }); channels[chan]["backlog"] = backlog; }