/** * Handler for the IRC "PART" 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_PART(CLIENT * Client, REQUEST * Req) { CLIENT *target; char *chan; assert(Client != NULL); assert(Req != NULL); _IRC_GET_SENDER_OR_RETURN_(target, Req, Client) /* Loop over all the given channel names */ chan = strtok(Req->argv[0], ","); /* Make sure that "chan" is not the empty string ("PART :") */ if (!chan) return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); while (chan) { Channel_Part(target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID(target)); chan = strtok(NULL, ","); } /* Update idle time, if local client */ if (Client_Conn(Client) > NONE) Conn_UpdateIdle(Client_Conn(Client)); return CONNECTED; } /* IRC_PART */
/** * Handler for the IRC command "KICK". * * @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_KICK(CLIENT *Client, REQUEST *Req) { CLIENT *from; char *itemList = Req->argv[0]; const char* currentNick, *currentChannel, *reason; unsigned int channelCount = 1; unsigned int nickCount = 1; assert( Client != NULL ); assert( Req != NULL ); _IRC_ARGC_BETWEEN_OR_RETURN_(Client, Req, 2, 3) _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) while (*itemList) { if (*itemList == ',') { *itemList = '\0'; channelCount++; } itemList++; } itemList = Req->argv[1]; while (*itemList) { if (*itemList == ',') { *itemList = '\0'; nickCount++; } itemList++; } reason = Req->argc == 3 ? Req->argv[2] : Client_ID(from); currentNick = Req->argv[1]; currentChannel = Req->argv[0]; if (channelCount == 1) { while (nickCount > 0) { if (!try_kick(Client, from, currentNick, currentChannel, reason)) return false; while (*currentNick) currentNick++; currentNick++; nickCount--; } } else if (channelCount == nickCount) { while (nickCount > 0) { if (!try_kick(Client, from, currentNick, currentChannel, reason)) return false; while (*currentNick) currentNick++; while (*currentChannel) currentChannel++; currentNick++; currentChannel++; nickCount--; } } else { return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); } return true; } /* IRC_KICK */
/** * 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 "TRACE" 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_TRACE(CLIENT *Client, REQUEST *Req) { CLIENT *from, *target, *c; CONN_ID idx, idx2; char user[CLIENT_USER_LEN]; assert(Client != NULL); assert(Req != NULL); _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from) /* Forward command to other server? */ if (target != Client_ThisServer()) { /* Send RPL_TRACELINK back to initiator */ idx = Client_Conn(Client); assert(idx > NONE); idx2 = Client_Conn(Client_NextHop(target)); assert(idx2 > NONE); if (!IRC_WriteStrClient(from, RPL_TRACELINK_MSG, Client_ID(from), PACKAGE_NAME, PACKAGE_VERSION, Client_ID(target), Client_ID(Client_NextHop(target)), Option_String(idx2), (long)(time(NULL) - Conn_StartTime(idx2)), Conn_SendQ(idx), Conn_SendQ(idx2))) return DISCONNECTED; /* Forward command */ IRC_WriteStrClientPrefix(target, from, "TRACE %s", Req->argv[0]); return CONNECTED; } /* Infos about all connected servers */ c = Client_First(); while (c) { if (Client_Conn(c) > NONE) { /* Local client */ if (Client_Type(c) == CLIENT_SERVER) { /* Server link */ strlcpy(user, Client_User(c), sizeof(user)); if (user[0] == '~') strlcpy(user, "unknown", sizeof(user)); if (!IRC_WriteStrClient(from, RPL_TRACESERVER_MSG, Client_ID(from), Client_ID(c), user, Client_Hostname(c), Client_Mask(Client_ThisServer()), Option_String(Client_Conn(c)))) return DISCONNECTED; } if (Client_Type(c) == CLIENT_USER && Client_HasMode(c, 'o')) { /* IRC Operator */ if (!IRC_WriteStrClient(from, RPL_TRACEOPERATOR_MSG, Client_ID(from), Client_ID(c))) return DISCONNECTED; } } c = Client_Next( c ); } return IRC_WriteStrClient(from, RPL_TRACEEND_MSG, Client_ID(from), Conf_ServerName, PACKAGE_NAME, PACKAGE_VERSION, NGIRCd_DebugLevel); } /* IRC_TRACE */
/** * 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 "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 */