/** * 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 */
/** * 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 */
/** * 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 */
/** * 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 "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 */