/* * 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; }
/** * 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 */
/** * Check weather a local client is allowed to join an already existing * channel or not. * * @param Client Client that sent the JOIN command * @param chan Channel to check * @param channame Name of the channel * @param key Provided channel key (or NULL) * @returns true if client is allowed to join, false otherwise */ static bool join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame, const char *key) { bool is_invited, is_banned, is_exception; /* Allow IRC operators to overwrite channel limits */ if (Client_HasMode(Client, 'o')) return true; is_banned = Lists_Check(Channel_GetListBans(chan), Client); is_exception = Lists_Check(Channel_GetListExcepts(chan), Client); is_invited = Lists_Check(Channel_GetListInvites(chan), Client); if (is_banned && !is_invited && !is_exception) { /* Client is banned from channel (and not on invite list) */ IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame); return false; } if (Channel_HasMode(chan, 'i') && !is_invited) { /* Channel is "invite-only" and client is not on invite list */ IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG, Client_ID(Client), channame); return false; } if (!Channel_CheckKey(chan, Client, key ? key : "")) { /* Channel is protected by a channel key and the client * didn't specify the correct one */ IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG, Client_ID(Client), channame); return false; } if (Channel_HasMode(chan, 'l') && (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) { /* There are more clints joined to this channel than allowed */ IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG, Client_ID(Client), channame); return false; } if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) { /* Only "secure" clients are allowed, but clients doesn't * use SSL encryption */ IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG, Client_ID(Client), channame); return false; } if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) { /* Only IRC operators are allowed! */ IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG, Client_ID(Client), channame); return false; } if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) { /* Only registered users are allowed! */ IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG, Client_ID(Client), channame); return false; } return true; } /* join_allowed */
/** * Handler for the IRC+ "CHANINFO" 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_CHANINFO( CLIENT *Client, REQUEST *Req ) { char modes_add[COMMAND_LEN], l[16]; CLIENT *from; CHANNEL *chan; int arg_topic; assert( Client != NULL ); assert( Req != NULL ); /* Bad number of parameters? */ if (Req->argc < 2 || Req->argc == 4 || Req->argc > 5) return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); /* Compatibility kludge */ if (Req->argc == 5) arg_topic = 4; else if(Req->argc == 3) arg_topic = 2; else arg_topic = -1; _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) /* Search or create channel */ chan = Channel_Search( Req->argv[0] ); if (!chan) chan = Channel_Create( Req->argv[0] ); if (!chan) return CONNECTED; if (Req->argv[1][0] == '+') { if (!*Channel_Modes(chan)) { /* OK, this channel doesn't have modes yet, * set the received ones: */ Channel_SetModes(chan, &Req->argv[1][1]); if(Req->argc == 5) { if(Channel_HasMode(chan, 'k')) Channel_SetKey(chan, Req->argv[2]); if(Channel_HasMode(chan, 'l')) Channel_SetMaxUsers(chan, atol(Req->argv[3])); } else { /* Delete modes which we never want to inherit */ Channel_ModeDel(chan, 'l'); Channel_ModeDel(chan, 'k'); } strcpy(modes_add, ""); if (Channel_HasMode(chan, 'l')) { snprintf(l, sizeof(l), " %lu", Channel_MaxUsers(chan)); strlcat(modes_add, l, sizeof(modes_add)); } if (Channel_HasMode(chan, 'k')) { strlcat(modes_add, " ", sizeof(modes_add)); strlcat(modes_add, Channel_Key(chan), sizeof(modes_add)); } /* Inform members of this channel */ IRC_WriteStrChannelPrefix(Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes(chan), modes_add); } } else Log(LOG_WARNING, "CHANINFO: invalid MODE format ignored!"); if (arg_topic > 0) { /* We got a topic */ if (!*Channel_Topic(chan) && Req->argv[arg_topic][0]) { /* OK, there is no topic jet */ Channel_SetTopic(chan, Client, Req->argv[arg_topic]); IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan)); } } /* Forward CHANINFO to other servers */ if (Req->argc == 5) IRC_WriteStrServersPrefixFlag(Client, from, 'C', "CHANINFO %s %s %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2], Req->argv[3], Req->argv[4]); else if (Req->argc == 3) IRC_WriteStrServersPrefixFlag(Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2]); else IRC_WriteStrServersPrefixFlag(Client, from, 'C', "CHANINFO %s %s", Req->argv[0], Req->argv[1]); return CONNECTED; } /* IRC_CHANINFO */