/** * 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); }
/** * 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 */