/** * Send information about changed channel invite/ban/exception lists to clients. * * @param IsAdd true if the list item has been added, false otherwise. * @param ModeChar The mode to use (e. g. 'b' or 'I') * @param Prefix The originator of the mode list change. * @param Client The sender of the command. * @param Channel The channel of which the list has been modified. * @param Mask The mask which has been added or removed. * @return CONNECTED or DISCONNECTED. */ static bool Send_ListChange(const bool IsAdd, const char ModeChar, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Mask) { bool ok = true; /* Send confirmation to the client */ if (Client_Type(Client) == CLIENT_USER) ok = IRC_WriteStrClientPrefix(Client, Prefix, "MODE %s %c%c %s", Channel_Name(Channel), IsAdd ? '+' : '-', ModeChar, Mask); /* to other servers */ IRC_WriteStrServersPrefix(Client, Prefix, "MODE %s %c%c %s", Channel_Name(Channel), IsAdd ? '+' : '-', ModeChar, Mask); /* and local users in channel */ IRC_WriteStrChannelPrefix(Client, Channel, Prefix, false, "MODE %s %c%c %s", Channel_Name(Channel), IsAdd ? '+' : '-', ModeChar, Mask ); return ok; } /* Send_ListChange */
/* * Reply to a channel mode request. * * @param Origin The originator of the MODE command (prefix). * @param Channel The channel of which the modes should be sent. * @return CONNECTED or DISCONNECTED. */ static bool Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel) { char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN]; const char *mode_ptr; if (!Channel_IsMemberOf(Channel, Origin)) { /* Not a member: "simple" mode reply */ if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG, Client_ID(Origin), Channel_Name(Channel), Channel_Modes(Channel))) return DISCONNECTED; } else { /* The sender is a member: generate extended reply */ strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes)); mode_ptr = the_modes; the_args[0] = '\0'; while(*mode_ptr) { switch(*mode_ptr) { case 'l': snprintf(argadd, sizeof(argadd), " %lu", Channel_MaxUsers(Channel)); strlcat(the_args, argadd, sizeof(the_args)); break; case 'k': strlcat(the_args, " ", sizeof(the_args)); strlcat(the_args, Channel_Key(Channel), sizeof(the_args)); break; } mode_ptr++; } if (the_args[0]) strlcat(the_modes, the_args, sizeof(the_modes)); if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG, Client_ID(Origin), Channel_Name(Channel), the_modes)) return DISCONNECTED; } #ifndef STRICT_RFC /* Channel creation time */ if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG, Client_ID(Origin), Channel_Name(Channel), Channel_CreationTime(Channel))) return DISCONNECTED; #endif return CONNECTED; }
GLOBAL bool Channel_AddBan(CHANNEL *c, const char *mask ) { struct list_head *h = Channel_GetListBans(c); LogDebug("Adding \"%s\" to \"%s\" ban list", mask, Channel_Name(c)); return Lists_Add(h, mask, false, NULL); }
/** * Acknowledge user JOIN request and send "channel info" numerics. * * @param Client Client used to prefix the genrated commands * @param target Forward commands/numerics to this user * @param chan Channel structure * @param channame Channel name */ static bool join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan, const char *channame) { const char *topic; if (Client_Type(Client) != CLIENT_USER) return true; /* acknowledge join */ if (!IRC_WriteStrClientPrefix(Client, target, "JOIN :%s", channame)) return false; /* Send topic to client, if any */ topic = Channel_Topic(chan); assert(topic != NULL); if (*topic) { if (!IRC_WriteStrClient(Client, RPL_TOPIC_MSG, Client_ID(Client), channame, topic)) return false; #ifndef STRICT_RFC if (!IRC_WriteStrClient(Client, RPL_TOPICSETBY_MSG, Client_ID(Client), channame, Channel_TopicWho(chan), Channel_TopicTime(chan))) return false; #endif } /* send list of channel members to client */ if (!IRC_Send_NAMES(Client, chan)) return false; return IRC_WriteStrClient(Client, RPL_ENDOFNAMES_MSG, Client_ID(Client), Channel_Name(chan)); } /* join_send_topic */
GLOBAL bool Channel_AddExcept(CHANNEL *c, const char *mask, const char *who ) { struct list_head *h = Channel_GetListExcepts(c); LogDebug("Adding \"%s\" to \"%s\" exception list", mask, Channel_Name(c)); return Lists_Add(h, mask, time(NULL), who, false); }
GLOBAL bool Channel_AddInvite(CHANNEL *c, const char *mask, bool onlyonce, const char *who ) { struct list_head *h = Channel_GetListInvites(c); LogDebug("Adding \"%s\" to \"%s\" invite list", mask, Channel_Name(c)); return Lists_Add(h, mask, time(NULL), who, onlyonce); }
/** * Add entries to channel invite, ban and exception lists. * * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list. * @param Prefix The originator of the command. * @param Client The sender of the command. * @param Channel The channel of which the list should be modified. * @param Pattern The pattern to add to the list. * @return CONNECTED or DISCONNECTED. */ static bool Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern) { char mask[MASK_LEN]; struct list_head *list = NULL; long int current_count; assert(Client != NULL); assert(Channel != NULL); assert(Pattern != NULL); assert(what == 'I' || what == 'b' || what == 'e'); Lists_MakeMask(Pattern, mask, sizeof(mask)); current_count = Lists_Count(Channel_GetListInvites(Channel)) + Lists_Count(Channel_GetListExcepts(Channel)) + Lists_Count(Channel_GetListBans(Channel)); switch(what) { case 'I': list = Channel_GetListInvites(Channel); break; case 'b': list = Channel_GetListBans(Channel); break; case 'e': list = Channel_GetListExcepts(Channel); break; } if (Lists_CheckDupeMask(list, mask)) return CONNECTED; if (Client_Type(Client) == CLIENT_USER && current_count >= MAX_HNDL_CHANNEL_LISTS) return IRC_WriteErrClient(Client, ERR_LISTFULL_MSG, Client_ID(Client), Channel_Name(Channel), mask, MAX_HNDL_CHANNEL_LISTS); switch (what) { case 'I': if (!Channel_AddInvite(Channel, mask, false, Client_ID(Client))) return CONNECTED; break; case 'b': if (!Channel_AddBan(Channel, mask, Client_ID(Client))) return CONNECTED; break; case 'e': if (!Channel_AddExcept(Channel, mask, Client_ID(Client))) return CONNECTED; break; } return Send_ListChange(true, what, Prefix, Client, Channel, mask); }
GLOBAL bool Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command, bool SendErrors, const char *Text) { if (!Can_Send_To_Channel(Chan, From)) { if (! SendErrors) return CONNECTED; /* no error, see RFC 2812 */ if (strchr(Channel_Modes(Chan), 'M')) return IRC_WriteStrClient(From, ERR_NEEDREGGEDNICK_MSG, Client_ID(From), Channel_Name(Chan)); else return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID(From), Channel_Name(Chan)); } if (Client_Conn(From) > NONE) Conn_UpdateIdle(Client_Conn(From)); return IRC_WriteStrChannelPrefix(Client, Chan, From, true, "%s %s :%s", Command, Channel_Name(Chan), Text); }
static bool ShowChannelList(struct list_head *head, CLIENT *Client, CHANNEL *Channel, char *msg, char *msg_end) { struct list_elem *e; assert (Client != NULL); assert (Channel != NULL); e = Lists_GetFirst(head); while (e) { if (!IRC_WriteStrClient(Client, msg, Client_ID(Client), Channel_Name(Channel), Lists_GetMask(e))) return DISCONNECTED; e = Lists_GetNext(e); } return IRC_WriteStrClient(Client, msg_end, Client_ID(Client), Channel_Name(Channel)); }
/** * Part from all channels. * * RFC 2812, (3.2.1 Join message Command): * Note that this message accepts a special argument ("0"), which is a * special request to leave all channels the user is currently a member of. * The server will process this message as if the user had sent a PART * command (See Section 3.2.2) for each channel he is a member of. * * @param client Client that initiated the part request * @param target Client that should part all joined channels * @returns CONNECTED or DISCONNECTED */ static bool part_from_all_channels(CLIENT* client, CLIENT *target) { CL2CHAN *cl2chan; CHANNEL *chan; while ((cl2chan = Channel_FirstChannelOf(target))) { chan = Channel_GetChannel(cl2chan); assert( chan != NULL ); Channel_Part(target, client, Channel_Name(chan), Client_ID(target)); } return CONNECTED; } /* part_from_all_channels */
/** * Send CHANINFO commands to a new server (inform it about existing channels). * @param Client New server * @param Chan Channel */ static bool Send_CHANINFO(CLIENT * Client, CHANNEL * Chan) { char *modes, *topic; bool has_k, has_l; #ifdef DEBUG Log(LOG_DEBUG, "Sending CHANINFO commands for \"%s\" ...", Channel_Name(Chan)); #endif modes = Channel_Modes(Chan); topic = Channel_Topic(Chan); if (!*modes && !*topic) return CONNECTED; has_k = Channel_HasMode(Chan, 'k'); has_l = Channel_HasMode(Chan, 'l'); /* send CHANINFO */ if (!has_k && !has_l) { if (!*topic) { /* "CHANINFO <chan> +<modes>" */ return IRC_WriteStrClient(Client, "CHANINFO %s +%s", Channel_Name(Chan), modes); } /* "CHANINFO <chan> +<modes> :<topic>" */ return IRC_WriteStrClient(Client, "CHANINFO %s +%s :%s", Channel_Name(Chan), modes, topic); } /* "CHANINFO <chan> +<modes> <key> <limit> :<topic>" */ return IRC_WriteStrClient(Client, "CHANINFO %s +%s %s %lu :%s", Channel_Name(Chan), modes, has_k ? Channel_Key(Chan) : "*", has_l ? Channel_MaxUsers(Chan) : 0, topic); } /* Send_CHANINFO */
/** * Send a specific list to a remote server. */ static bool Send_List(CLIENT *Client, CHANNEL *Chan, struct list_head *Head, char Type) { struct list_elem *elem; elem = Lists_GetFirst(Head); while (elem) { if (!IRC_WriteStrClient(Client, "MODE %s +%c %s", Channel_Name(Chan), Type, Lists_GetMask(elem))) { return DISCONNECTED; } elem = Lists_GetNext(elem); } return CONNECTED; }
// Function to render the guide item on the screen. Send it the required text void MultiList_RenderChannel( word rgn, word svcId, int row, int col ){ int x, y, w, h; dword fc = DISPLAY_EVENT; int svcNum, svcType; TYPE_TapChInfo chInfo; char text[128]; // Calculate the position to render the item // w = (720-((Panel_Left_X-DISPLAY_X_OFFSET)*2))/MultiList_Cols; // [ ][ ][ ] w = DISPLAY_ROW_WIDTH/MultiList_Cols; // [ ][ ][ ] x = Panel_Left_X + (w*col) + DISPLAY_GAP; // Offset plus the coloumn h = 22; y = (Panel_Left_Y + (( h + DISPLAY_GAP) * (0)))+(((( h + DISPLAY_GAP) * (MultiList_MaxEvents))+( h + DISPLAY_GAP))*row); // Draw background for item it previous or selected otherwise the background on text will do the job TAP_Osd_FillBox(rgn, x-1, y, w-2, h, DISPLAY_MAIN); svcNum = GetSvcNum(svcId); svcType = GetSvcType(svcId); sprintf( text, "%d, %d, %s\0", svcNum, svcType, Channel_Name(svcNum,svcType)); TAP_Channel_GetInfo( svcType, svcNum, &chInfo ); TAP_Osd_PutStringAf1419( rgn, x+5, y+1, x+(w-10), chInfo.chName, fc, COLOR_None ); }
/** * Handle channel mode and channel-user mode changes * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @param Origin The originator of the MODE command (prefix). * @param Channel The target channel of this MODE command. * @return CONNECTED or DISCONNECTED. */ static bool Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) { char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2], argadd[CLIENT_PASS_LEN], *mode_ptr; bool connected, set, skiponce, retval, use_servermode, is_halfop, is_op, is_admin, is_owner, is_machine, is_oper; int mode_arg, arg_arg, mode_arg_count = 0; CLIENT *client; long l; size_t len; is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false; if (Channel_IsModeless(Channel)) return IRC_WriteErrClient(Client, ERR_NOCHANMODES_MSG, Client_ID(Client), Channel_Name(Channel)); /* Mode request: let's answer it :-) */ if (Req->argc <= 1) return Channel_Mode_Answer_Request(Origin, Channel); /* Check if origin is oper and opers can use mode */ use_servermode = Conf_OperServerMode; if(Client_HasMode(Client, 'o') && Conf_OperCanMode) { is_oper = true; } /* Check if client is a server/service */ if(Client_Type(Client) == CLIENT_SERVER || Client_Type(Client) == CLIENT_SERVICE) { is_machine = true; } /* Check if client is member of channel or an oper or an server/service */ if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine) return IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG, Client_ID(Origin), Channel_Name(Channel)); mode_arg = 1; mode_ptr = Req->argv[mode_arg]; if (Req->argc > mode_arg + 1) arg_arg = mode_arg + 1; else arg_arg = -1; /* Initial state: set or unset modes? */ skiponce = false; switch (*mode_ptr) { case '-': set = false; break; case '+': set = true; break; default: set = true; skiponce = true; } /* Prepare reply string */ strcpy(the_modes, set ? "+" : "-"); the_args[0] = '\0'; x[1] = '\0'; connected = CONNECTED; while (mode_ptr) { if (!skiponce) mode_ptr++; if (!*mode_ptr) { /* Try next argument if there's any */ if (arg_arg < 0) break; if (arg_arg > mode_arg) mode_arg = arg_arg; else mode_arg++; if (mode_arg >= Req->argc) break; mode_ptr = Req->argv[mode_arg]; if (Req->argc > mode_arg + 1) arg_arg = mode_arg + 1; else arg_arg = -1; } skiponce = false; switch (*mode_ptr) { case '+': case '-': if (((*mode_ptr == '+') && !set) || ((*mode_ptr == '-') && set)) { /* Action modifier ("+"/"-") must be changed ... */ len = strlen(the_modes) - 1; if (the_modes[len] == '+' || the_modes[len] == '-') { /* Adjust last action modifier in result */ the_modes[len] = *mode_ptr; } else { /* Append modifier character to result string */ x[0] = *mode_ptr; strlcat(the_modes, x, sizeof(the_modes)); } set = *mode_ptr == '+'; } continue; } /* Are there arguments left? */ if (arg_arg >= Req->argc) arg_arg = -1; if(!is_machine && !is_oper) { if (Channel_UserHasMode(Channel, Client, 'q')) is_owner = true; if (Channel_UserHasMode(Channel, Client, 'a')) is_admin = true; if (Channel_UserHasMode(Channel, Client, 'o')) is_op = true; if (Channel_UserHasMode(Channel, Client, 'h')) is_halfop = true; } /* Validate modes */ x[0] = '\0'; argadd[0] = '\0'; client = NULL; switch (*mode_ptr) { /* --- Channel modes --- */ case 'R': /* Registered users only */ case 's': /* Secret channel */ case 'z': /* Secure connections only */ if(!is_oper && !is_machine && !is_owner && !is_admin && !is_op) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'i': /* Invite only */ case 'V': /* Invite disallow */ case 'M': /* Only identified nicks can write */ case 'm': /* Moderated */ case 'n': /* Only members can write */ case 'N': /* Can't change nick while on this channel */ case 'Q': /* No kicks */ case 't': /* Topic locked */ if(is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; case 'k': /* Channel key */ if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (!set) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { x[0] = *mode_ptr; if (Channel_HasMode(Channel, 'k')) strlcpy(argadd, "*", sizeof(argadd)); if (arg_arg > mode_arg) arg_arg++; } else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; } if (arg_arg > mode_arg) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { Channel_ModeDel(Channel, 'k'); Channel_SetKey(Channel, Req->argv[arg_arg]); strlcpy(argadd, Channel_Key(Channel), sizeof(argadd)); x[0] = *mode_ptr; } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { #ifdef STRICT_RFC /* Only send error message in "strict" mode, * this is how ircd2.11 and others behave ... */ connected = IRC_WriteErrClient(Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID(Origin), Req->command); #endif goto chan_exit; } break; case 'l': /* Member limit */ if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (!set) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; } if (arg_arg > mode_arg) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { l = atol(Req->argv[arg_arg]); if (l > 0 && l < 0xFFFF) { Channel_ModeDel(Channel, 'l'); Channel_SetMaxUsers(Channel, l); snprintf(argadd, sizeof(argadd), "%ld", l); x[0] = *mode_ptr; } } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { #ifdef STRICT_RFC /* Only send error message in "strict" mode, * this is how ircd2.11 and others behave ... */ connected = IRC_WriteErrClient(Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID(Origin), Req->command); #endif goto chan_exit; } break; case 'O': /* IRC operators only */ if (set) { /* Only IRC operators are allowed to * set the 'O' channel mode! */ if(is_oper || is_machine) x[0] = 'O'; else connected = IRC_WriteErrClient(Origin, ERR_NOPRIVILEGES_MSG, Client_ID(Origin)); } else if(is_oper || is_machine || is_owner || is_admin || is_op) x[0] = 'O'; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; case 'P': /* Persistent channel */ if (set) { /* Only IRC operators are allowed to * set the 'P' channel mode! */ if(is_oper || is_machine) x[0] = 'P'; else connected = IRC_WriteErrClient(Origin, ERR_NOPRIVILEGES_MSG, Client_ID(Origin)); } else if(is_oper || is_machine || is_owner || is_admin || is_op) x[0] = 'P'; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; /* --- Channel user modes --- */ case 'q': /* Owner */ case 'a': /* Channel admin */ if(!is_oper && !is_machine && !is_owner && !is_admin) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'o': /* Channel operator */ if(!is_oper && !is_machine && !is_owner && !is_admin && !is_op) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'h': /* Half Op */ if(!is_oper && !is_machine && !is_owner && !is_admin && !is_op) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'v': /* Voice */ if (arg_arg > mode_arg) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { client = Client_Search(Req->argv[arg_arg]); if (client) x[0] = *mode_ptr; else connected = IRC_WriteErrClient(Origin, ERR_NOSUCHNICK_MSG, Client_ID(Origin), Req->argv[arg_arg]); } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { #ifdef STRICT_RFC /* Report an error to the client, when a user * mode should be changed but no nickname is * given. But don't do it when not in "strict" * mode, because most other servers don't do * it as well and some clients send "wired" * MODE commands like "MODE #chan -ooo nick". */ connected = IRC_WriteErrClient(Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID(Origin), Req->command); #endif goto chan_exit; } break; /* --- Channel lists --- */ case 'I': /* Invite lists */ case 'b': /* Ban lists */ case 'e': /* Channel exception lists */ if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (arg_arg > mode_arg) { /* modify list */ if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { connected = set ? Add_To_List(*mode_ptr, Origin, Client, Channel, Req->argv[arg_arg]) : Del_From_List(*mode_ptr, Origin, Client, Channel, Req->argv[arg_arg]); } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { switch (*mode_ptr) { case 'I': Channel_ShowInvites(Origin, Channel); break; case 'b': Channel_ShowBans(Origin, Channel); break; case 'e': Channel_ShowExcepts(Origin, Channel); break; } } break; default: if (Client_Type(Client) != CLIENT_SERVER) { Log(LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\" on %s!?", set ? '+' : '-', *mode_ptr, Client_ID(Origin), Channel_Name(Channel)); connected = IRC_WriteErrClient(Origin, ERR_UNKNOWNMODE_MSG, Client_ID(Origin), *mode_ptr, Channel_Name(Channel)); x[0] = '\0'; } else { Log(LOG_DEBUG, "Handling unknown mode \"%c%c\" from \"%s\" on %s ...", set ? '+' : '-', *mode_ptr, Client_ID(Origin), Channel_Name(Channel)); x[0] = *mode_ptr; } } if (!connected) break; /* Is there a valid mode change? */ if (!x[0]) continue; /* Validate target client */ if (client && (!Channel_IsMemberOf(Channel, client))) { if (!IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID(Origin), Client_ID(client), Channel_Name(Channel))) break; continue; } if (client) { /* Channel-User-Mode */ retval = set ? Channel_UserModeAdd(Channel, client, x[0]) : Channel_UserModeDel(Channel, client, x[0]); if (retval) { strlcat(the_args, " ", sizeof(the_args)); strlcat(the_args, Client_ID(client), sizeof(the_args)); strlcat(the_modes, x, sizeof(the_modes)); LogDebug ("User \"%s\": Mode change on %s, now \"%s\"", Client_Mask(client), Channel_Name(Channel), Channel_UserModes(Channel, client)); } } else { /* Channel-Mode */ retval = set ? Channel_ModeAdd(Channel, x[0]) : Channel_ModeDel(Channel, x[0]); if (retval) { strlcat(the_modes, x, sizeof(the_modes)); LogDebug("Channel %s: Mode change, now \"%s\".", Channel_Name(Channel), Channel_Modes(Channel)); } } /* Are there additional arguments to add? */ if (argadd[0]) { strlcat(the_args, " ", sizeof(the_args)); strlcat(the_args, argadd, sizeof(the_args)); } } chan_exit: /* Are there changed modes? */ if (the_modes[1]) { /* Clean up mode string */ len = strlen(the_modes) - 1; if ((the_modes[len] == '+') || (the_modes[len] == '-')) the_modes[len] = '\0'; if (Client_Type(Client) == CLIENT_SERVER) { /* MODE requests for local channels from other servers * are definitely invalid! */ if (Channel_IsLocal(Channel)) { Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored."); return CONNECTED; } /* Forward mode changes to channel users and all the * other remote servers: */ IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); } else { if (use_servermode) Origin = Client_ThisServer(); /* Send reply to client and inform other servers and channel users */ connected = IRC_WriteStrClientPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); /* Only forward requests for non-local channels */ if (!Channel_IsLocal(Channel)) IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); } } return connected; } /* Channel_Mode */
/** * Handler for the IRC "LIST" command. * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @return CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_LIST( CLIENT *Client, REQUEST *Req ) { char *pattern; CHANNEL *chan; CLIENT *from, *target; int count = 0; assert(Client != NULL); assert(Req != NULL); _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) if (Req->argc > 0) pattern = strtok(Req->argv[0], ","); else pattern = "*"; if (Req->argc == 2) { /* Forward to other server? */ target = Client_Search(Req->argv[1]); if (! target || Client_Type(target) != CLIENT_SERVER) return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG, Client_ID(Client), Req->argv[1]); if (target != Client_ThisServer()) { /* Target is indeed an other server, forward it! */ return IRC_WriteStrClientPrefix(target, from, "LIST %s :%s", Req->argv[0], Req->argv[1]); } } while (pattern) { /* Loop through all the channels */ if (Req->argc > 0) ngt_LowerStr(pattern); chan = Channel_First(); while (chan) { /* Check search pattern */ if (MatchCaseInsensitive(pattern, Channel_Name(chan))) { /* Gotcha! */ if (!Channel_HasMode(chan, 's') || Channel_IsMemberOf(chan, from) || (!Conf_MorePrivacy && Client_HasMode(Client, 'o') && Client_Conn(Client) > NONE)) { if ((Conf_MaxListSize > 0) && IRC_CheckListTooBig(from, count, Conf_MaxListSize, "LIST")) break; if (!IRC_WriteStrClient(from, RPL_LIST_MSG, Client_ID(from), Channel_Name(chan), Channel_MemberCount(chan), Channel_Topic( chan ))) return DISCONNECTED; count++; } } chan = Channel_Next(chan); } /* Get next name ... */ if(Req->argc > 0) pattern = strtok(NULL, ","); else pattern = NULL; } return IRC_WriteStrClient(from, RPL_LISTEND_MSG, Client_ID(from)); } /* IRC_LIST */
/** * Handler for the IRC command "INVITE". * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @return CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_INVITE(CLIENT *Client, REQUEST *Req) { CHANNEL *chan; CLIENT *target, *from; const char *colon_if_necessary; bool remember = false; assert( Client != NULL ); assert( Req != NULL ); _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 2) _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) /* Search user */ target = Client_Search(Req->argv[0]); if (!target || (Client_Type(target) != CLIENT_USER)) return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->argv[0]); chan = Channel_Search(Req->argv[1]); if (chan) { /* Channel exists. Is the user a valid member of the channel? */ if (!Channel_IsMemberOf(chan, from)) return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Req->argv[1]); /* Is the channel "invite-disallow"? */ if (strchr(Channel_Modes(chan), 'V')) return IRC_WriteStrClient(from, ERR_NOINVITE_MSG, Client_ID(from), Channel_Name(chan)); /* Is the channel "invite-only"? */ if (strchr(Channel_Modes(chan), 'i')) { /* Yes. The user must be channel owner/admin/operator/halfop! */ if (!strchr(Channel_UserModes(chan, from), 'q') && !strchr(Channel_UserModes(chan, from), 'a') && !strchr(Channel_UserModes(chan, from), 'o') && !strchr(Channel_UserModes(chan, from), 'h')) return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(from), Channel_Name(chan)); remember = true; } /* Is the target user already member of the channel? */ if (Channel_IsMemberOf(chan, target)) return IRC_WriteStrClient(from, ERR_USERONCHANNEL_MSG, Client_ID(from), Req->argv[0], Req->argv[1]); /* If the target user is banned on that channel: remember invite */ if (Lists_Check(Channel_GetListBans(chan), target)) remember = true; if (remember) { /* We must remember this invite */ if (!Channel_AddInvite(chan, Client_Mask(target), true)) return CONNECTED; } } LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from), Req->argv[0], Req->argv[1]); /* * RFC 2812 says: * 'There is no requirement that the channel [..] must exist or be a valid channel' * The problem with this is that this allows the "channel" to contain spaces, * in which case we must prefix its name with a colon to make it clear that * it is only a single argument. */ colon_if_necessary = strchr(Req->argv[1], ' ') ? ":":""; /* Inform target client */ IRC_WriteStrClientPrefix(target, from, "INVITE %s %s%s", Req->argv[0], colon_if_necessary, Req->argv[1]); if (Client_Conn(target) > NONE) { /* The target user is local, so we have to send the status code */ if (!IRC_WriteStrClientPrefix(from, target, RPL_INVITING_MSG, Client_ID(from), Req->argv[0], colon_if_necessary, Req->argv[1])) return DISCONNECTED; if (strchr(Client_Modes(target), 'a') && !IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from), Client_ID(target), Client_Away(target))) return DISCONNECTED; } return CONNECTED; } /* IRC_INVITE */
/** * Handler for the IRC "TOPIC" command. * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @return CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_TOPIC( CLIENT *Client, REQUEST *Req ) { CHANNEL *chan; CLIENT *from; char *topic; bool r, topic_power; assert( Client != NULL ); assert( Req != NULL ); _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) chan = Channel_Search(Req->argv[0]); if (!chan) return IRC_WriteErrClient(from, ERR_NOSUCHCHANNEL_MSG, Client_ID(from), Req->argv[0]); /* Only remote servers and channel members are allowed to change the * channel topic, and IRC operators when the Conf_OperCanMode option * is set in the server configuration. */ if (Client_Type(Client) != CLIENT_SERVER) { topic_power = Client_HasMode(from, 'o'); if (!Channel_IsMemberOf(chan, from) && !(Conf_OperCanMode && topic_power)) return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(from), Req->argv[0]); } else topic_power = true; if (Req->argc == 1) { /* Request actual topic */ topic = Channel_Topic(chan); if (*topic) { r = IRC_WriteStrClient(from, RPL_TOPIC_MSG, Client_ID(Client), Channel_Name(chan), topic); #ifndef STRICT_RFC if (!r) return r; r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG, Client_ID(Client), Channel_Name(chan), Channel_TopicWho(chan), Channel_TopicTime(chan)); #endif return r; } else return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG, Client_ID(from), Channel_Name(chan)); } if (Channel_HasMode(chan, 't')) { /* Topic Lock. Is the user a channel op or IRC operator? */ if(!topic_power && !Channel_UserHasMode(chan, from, 'h') && !Channel_UserHasMode(chan, from, 'o') && !Channel_UserHasMode(chan, from, 'a') && !Channel_UserHasMode(chan, from, 'q')) return IRC_WriteErrClient(from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(from), Channel_Name(chan)); } /* Set new topic */ Channel_SetTopic(chan, from, Req->argv[1]); LogDebug("%s \"%s\" set topic on \"%s\": %s", Client_TypeText(from), Client_Mask(from), Channel_Name(chan), Req->argv[1][0] ? Req->argv[1] : "<none>"); if (Conf_OperServerMode) from = Client_ThisServer(); /* Update channel and forward new topic to other servers */ if (!Channel_IsLocal(chan)) IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1]); IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s", Req->argv[0], Req->argv[1]); if (Client_Type(Client) == CLIENT_USER) return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1]); else return CONNECTED; } /* IRC_TOPIC */
/** * Announce a channel and its users in the network. */ static bool Announce_Channel(CLIENT *Client, CHANNEL *Chan) { CL2CHAN *cl2chan; CLIENT *cl; char str[COMMAND_LEN], *ptr; bool njoin, xop; /* Check features of remote server */ njoin = Conn_Options(Client_Conn(Client)) & CONN_RFC1459 ? false : true; xop = Client_HasFlag(Client, 'X') ? true : false; /* Get all the members of this channel */ cl2chan = Channel_FirstMember(Chan); snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(Chan)); while (cl2chan) { cl = Channel_GetClient(cl2chan); assert(cl != NULL); if (njoin) { /* RFC 2813: send NJOIN with nicknames and modes * (if user is channel operator or has voice) */ if (str[strlen(str) - 1] != ':') strlcat(str, ",", sizeof(str)); /* Prepare user prefix (ChanOp, voiced, ...) */ if (xop && Channel_UserHasMode(Chan, cl, 'q')) strlcat(str, "~", sizeof(str)); if (xop && Channel_UserHasMode(Chan, cl, 'a')) strlcat(str, "&", sizeof(str)); if (Channel_UserHasMode(Chan, cl, 'o')) strlcat(str, "@", sizeof(str)); if (xop && Channel_UserHasMode(Chan, cl, 'h')) strlcat(str, "%", sizeof(str)); if (Channel_UserHasMode(Chan, cl, 'v')) strlcat(str, "+", sizeof(str)); strlcat(str, Client_ID(cl), sizeof(str)); /* Send the data if the buffer is "full" */ if (strlen(str) > (sizeof(str) - CLIENT_NICK_LEN - 8)) { if (!IRC_WriteStrClient(Client, "%s", str)) return DISCONNECTED; snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(Chan)); } } else { /* RFC 1459: no NJOIN, send JOIN and MODE */ if (!IRC_WriteStrClientPrefix(Client, cl, "JOIN %s", Channel_Name(Chan))) return DISCONNECTED; ptr = Channel_UserModes(Chan, cl); while (*ptr) { if (!IRC_WriteStrClientPrefix(Client, cl, "MODE %s +%c %s", Channel_Name(Chan), ptr[0], Client_ID(cl))) return DISCONNECTED; ptr++; } } cl2chan = Channel_NextMember(Chan, cl2chan); } /* Data left in the buffer? */ if (str[strlen(str) - 1] != ':') { /* Yes, send it ... */ if (!IRC_WriteStrClient(Client, "%s", str)) return DISCONNECTED; } return CONNECTED; } /* Announce_Channel */