/* * ms_tmode() * * inputs - parv[0] = UID * parv[1] = TS * parv[2] = channel name * parv[3] = modestring */ static void ms_tmode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; struct Membership *member = NULL; if ((chptr = hash_find_channel(parv[2])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), parv[2]); return; } if (atol(parv[1]) > chptr->channelts) return; if (IsServer(source_p)) set_channel_mode(client_p, source_p, chptr, NULL, parc - 3, parv + 3, chptr->chname); else { member = find_channel_link(source_p, chptr); /* XXX are we sure we just want to bail here? */ if (has_member_flags(member, CHFL_DEOPPED)) return; set_channel_mode(client_p, source_p, chptr, member, parc - 3, parv + 3, chptr->chname); } }
/* part_one_client() * * inputs - pointer to server * - pointer to source client to remove * - char pointer of name of channel to remove from * output - none * side effects - remove ONE client given the channel name */ static void part_one_client(struct Client *client_p, struct Client *source_p, const char *name, char *reason) { struct Channel *chptr = NULL; struct Membership *ms = NULL; if ((chptr = hash_find_channel(name)) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); return; } if ((ms = find_channel_link(source_p, chptr)) == NULL) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, name); return; } if (MyConnect(source_p) && !IsOper(source_p)) check_spambot_warning(source_p, NULL); /* * Remove user from the old channel (if any) * only allow /part reasons in -m chans */ if(msg_has_colors(reason) && (chptr->mode.mode & MODE_NOCOLOR)) reason = strip_color(reason); if (reason[0] && (!MyConnect(source_p) || ((can_send(chptr, source_p, ms) && (source_p->firsttime + ConfigFileEntry.anti_spam_exit_message_time) < CurrentTime)))) { sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s PART %s :%s", ID(source_p), chptr->chname, reason); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s PART %s :%s", source_p->name, chptr->chname, reason); sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s PART %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, reason); } else { sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s PART %s", ID(source_p), chptr->chname); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s PART %s", source_p->name, chptr->chname); sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s PART %s", source_p->name, source_p->username, source_p->host, chptr->chname); } remove_user_from_channel(ms); }
/*! \brief MODE command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = channel or nick name * - parv[2] = modes to be added or removed */ static int m_mode(struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; if (EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NEEDMOREPARAMS, "MODE"); return 0; } /* Now, try to find the channel in question */ if (!IsChanPrefix(*parv[1])) { /* If here, it has to be a non-channel name */ set_user_mode(source_p, parc, parv); return 0; } if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one_numeric(source_p, &me, ERR_NOSUCHCHANNEL, parv[1]); return 0; } /* Now known the channel exists */ if (parc < 3) { char modebuf[MODEBUFLEN] = ""; char parabuf[MODEBUFLEN] = ""; channel_modes(chptr, source_p, modebuf, parabuf); sendto_one_numeric(source_p, &me, RPL_CHANNELMODEIS, chptr->name, modebuf, parabuf); sendto_one_numeric(source_p, &me, RPL_CREATIONTIME, chptr->name, chptr->creationtime); return 0; } /* * bounce all modes from people we deop on sjoin * servers have always gotten away with murder, * including telnet servers *g* - Dianora */ if (IsServer(source_p) || HasFlag(source_p, FLAGS_SERVICE)) set_channel_mode(source_p, chptr, NULL, parc - 2, parv + 2); else { struct Membership *member = find_channel_link(source_p, chptr); /* Finish the flood grace period... */ if (MyClient(source_p) && !IsFloodDone(source_p)) if (!((parc == 3) && (parv[2][0] == 'b') && (parv[2][1] == '\0'))) flood_endgrace(source_p); set_channel_mode(source_p, chptr, member, parc - 2, parv + 2); } return 0; }
/* * m_mode - MODE command handler * parv[0] - sender * parv[1] - channel */ static void m_mode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; struct Membership *member; static char modebuf[MODEBUFLEN]; static char parabuf[MODEBUFLEN]; if (parv[1][0] == '\0') { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "MODE"); return; } /* Now, try to find the channel in question */ if (!IsChanPrefix(parv[1][0])) { /* if here, it has to be a non-channel name */ set_user_mode(client_p, source_p, parc, parv); return; } if (!check_channel_name(parv[1])) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, source_p->name, parv[1]); return; } chptr = hash_find_channel(parv[1]); if (chptr == NULL) { /* if chptr isn't found locally, it =could= exist * on the uplink. So ask. */ /* LazyLinks */ /* only send a mode upstream if a local client sent this request * -davidt */ if (MyClient(source_p) && !ServerInfo.hub && uplink && IsCapable(uplink, CAP_LL)) { sendto_one(uplink, ":%s MODE %s %s", ID_or_name(source_p, uplink), parv[1], (parv[2] ? parv[2] : "")); return; } else { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]); return; } } /* Now known the channel exists */ if (parc < 3) { channel_modes(chptr, source_p, modebuf, parabuf); sendto_one(source_p, form_str(RPL_CHANNELMODEIS), me.name, parv[0], parv[1], modebuf, parabuf); sendto_one(source_p, form_str(RPL_CREATIONTIME), me.name, parv[0], parv[1], chptr->channelts); } /* bounce all modes from people we deop on sjoin * servers have always gotten away with murder, * including telnet servers *g* - Dianora * * XXX Is it worth the bother to make an ms_mode() ? - Dianora */ else if (IsServer(source_p)) { set_channel_mode(client_p, source_p, chptr, NULL, parc - 2, parv + 2, chptr->chname); } else { member = find_channel_link(source_p, chptr); if (!has_member_flags(member, CHFL_DEOPPED)) { /* Finish the flood grace period... */ if (MyClient(source_p) && !IsFloodDone(source_p)) { if (!((parc == 3) && (parv[2][0] == 'b') && (parv[2][1] == '\0'))) flood_endgrace(source_p); } set_channel_mode(client_p, source_p, chptr, member, parc - 2, parv + 2, chptr->chname); } } }
/* build_target_list() * * inputs - pointer to given source (oper/client etc.) * - pointer to list of nicks/channels * - pointer to table to place results * - pointer to text (only used if source_p is an oper) * output - number of valid entities * side effects - target_table is modified to contain a list of * pointers to channels or clients * if source client is an oper * all the classic old bizzare oper privmsg tricks * are parsed and sent as is, if prefixed with $ * to disambiguate. * */ static int build_target_list(int p_or_n, const char *command, struct Client *source_p, char *nicks_channels, const char *text) { int type = 0; char *p = NULL, *nick = NULL; char *target_list = NULL; struct Channel *chptr = NULL; struct Client *target_p = NULL; target_list = nicks_channels; ntargets = 0; for (nick = strtoken(&p, target_list, ","); nick; nick = strtoken(&p, NULL, ",")) { const char *with_prefix = NULL; /* * Channels are privmsg'd a lot more than other clients, moved up * here plain old channel msg? */ if (IsChanPrefix(*nick)) { if ((chptr = hash_find_channel(nick))) { if (!duplicate_ptr(chptr)) { if (ntargets >= ConfigGeneral.max_targets) { sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS, nick, ConfigGeneral.max_targets); return 1; } targets[ntargets].ptr = chptr; targets[ntargets++].type = ENTITY_CHANNEL; } } else { if (p_or_n != NOTICE) sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick); } continue; } /* Look for a PRIVMSG/NOTICE to another client */ if ((target_p = find_person(source_p, nick))) { if (!duplicate_ptr(target_p)) { if (ntargets >= ConfigGeneral.max_targets) { sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS, nick, ConfigGeneral.max_targets); return 1; } targets[ntargets].ptr = target_p; targets[ntargets].type = ENTITY_CLIENT; targets[ntargets++].flags = 0; } continue; } /* @#channel or +#channel message ? */ type = 0; with_prefix = nick; /* Allow %+@ if someone wants to do that */ while (1) { if (*nick == '@') type |= CHFL_CHANOP; else if (*nick == '%') type |= CHFL_CHANOP | CHFL_HALFOP; else if (*nick == '+') type |= CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE; else break; ++nick; } if (type) { if (EmptyString(nick)) /* If it's a '\0' dump it, there is no recipient */ { sendto_one_numeric(source_p, &me, ERR_NORECIPIENT, command); continue; } /* * At this point, nick+1 should be a channel name i.e. #foo or &foo * if the channel is found, fine, if not report an error */ if ((chptr = hash_find_channel(nick))) { if (IsClient(source_p) && !HasFlag(source_p, FLAGS_SERVICE)) { if (!has_member_flags(find_channel_link(source_p, chptr), CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE)) { sendto_one_numeric(source_p, &me, ERR_CHANOPRIVSNEEDED, with_prefix); return -1; } } if (!duplicate_ptr(chptr)) { if (ntargets >= ConfigGeneral.max_targets) { sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS, nick, ConfigGeneral.max_targets); return 1; } targets[ntargets].ptr = chptr; targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL; targets[ntargets++].flags = type; } } else { if (p_or_n != NOTICE) sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick); } continue; } if (*nick == '$' || strchr(nick, '@')) handle_special(p_or_n, command, source_p, nick, text); else { if (p_or_n != NOTICE) { if (!IsDigit(*nick) || MyClient(source_p)) sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick); } } } return 1; }
/* ** m_invite ** parv[0] - sender prefix ** parv[1] - user to invite ** parv[2] - channel name ** parv[3] - invite timestamp */ static void m_invite(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL; struct Channel *chptr = NULL; struct Membership *ms = NULL; if (IsServer(source_p)) return; if (EmptyString(parv[2])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "INVITE"); return; } if (MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); if ((target_p = find_person(client_p, parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, source_p->name, parv[1]); return; } /* Do not send local channel invites to users if they are not on the * same server as the person sending the INVITE message. */ /* Possibly should be an error sent to source_p */ /* done .. there should be no problem because MyConnect(source_p) should * always be true if parse() and such is working correctly --is */ if (!MyConnect(target_p) && (*parv[2] == '&')) { if (ConfigServerHide.hide_servers == 0) sendto_one(source_p, form_str(ERR_USERNOTONSERV), me.name, source_p->name, target_p->name); return; } if ((chptr = hash_find_channel(parv[2])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, parv[2]); return; } if (MyConnect(source_p) && (ms = find_channel_link(source_p, chptr)) == NULL) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, chptr->chname); return; } if (MyConnect(source_p) && !has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, chptr->chname); return; } if ((chptr->mode.mode & MODE_OPERONLY)) { if (MyConnect(source_p) && !IsOper(source_p)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, chptr->chname); return; } } if (IsMember(target_p, chptr)) { sendto_one(source_p, form_str(ERR_USERONCHANNEL), me.name, source_p->name, target_p->name, chptr->chname); return; } if (MyConnect(source_p)) { sendto_one(source_p, form_str(RPL_INVITING), me.name, source_p->name, target_p->name, chptr->chname); if (target_p->away) sendto_one(source_p, form_str(RPL_AWAY), me.name, source_p->name, target_p->name, target_p->away); } else if (parc > 3 && IsDigit(*parv[3])) if (atoi(parv[3]) > chptr->channelts) return; if (MyConnect(target_p)) { sendto_one(target_p, ":%s!%s@%s INVITE %s :%s", source_p->name, source_p->username, source_p->host, target_p->name, chptr->chname); if (chptr->mode.mode & MODE_INVITEONLY) { sendto_channel_local(CHFL_CHANOP|CHFL_HALFOP, 0, chptr, ":%s NOTICE %s :%s is inviting %s to %s.", me.name, chptr->chname, source_p->name, target_p->name, chptr->chname); sendto_channel_remote(source_p, client_p, CHFL_CHANOP|CHFL_HALFOP, NOCAPS, NOCAPS, chptr, ":%s NOTICE %s :%s is inviting %s to %s.", source_p->name, chptr->chname, source_p->name, target_p->name, chptr->chname); /* Add the invite if channel is +i */ add_invite(chptr, target_p); } } else if (target_p->from != client_p) sendto_one(target_p, ":%s INVITE %s %s %lu", ID_or_name(source_p, target_p->from), ID_or_name(target_p, target_p->from), chptr->chname, (unsigned long)chptr->channelts); }
/* m_topic() * parv[0] = sender prefix * parv[1] = channel name * parv[2] = new topic, if setting topic */ static void m_topic(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; char *p; struct Membership *ms; const char *from, *to; if (!MyClient(source_p) && IsCapable(source_p->from, CAP_TS6) && HasID(source_p)) { from = me.id; to = source_p->id; } else { from = me.name; to = source_p->name; } if ((p = strchr(parv[1], ',')) != NULL) *p = '\0'; if (EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), from, to, "TOPIC"); return; } if (MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); if (IsChanPrefix(*parv[1])) { if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), from, to, parv[1]); return; } /* setting topic */ if (parc > 2) { if ((ms = find_channel_link(source_p, chptr)) == NULL && !IsService(source_p)) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, parv[1]); return; } if ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP) || IsGod(source_p) || IsService(source_p)) { char topic_info[USERHOST_REPLYLEN]; if(!has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP) && IsGod(source_p) && MyClient(source_p) && (chptr->mode.mode & MODE_TOPICLIMIT) != 0) { char tmp[IRCD_BUFSIZE]; ircsprintf(tmp, "%s is using God mode: TOPIC %s %s", source_p->name, chptr->chname, parv[2]); sendto_gnotice_flags(UMODE_SERVNOTICE, L_ALL, me.name, &me, NULL, tmp); oftc_log(tmp); } ircsprintf(topic_info, "%s!%s@%s", source_p->name, source_p->username, source_p->host); set_channel_topic(chptr, parv[2], topic_info, CurrentTime); sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s TOPIC %s :%s", ID(source_p), chptr->chname, chptr->topic == NULL ? "" : chptr->topic); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s TOPIC %s :%s", source_p->name, chptr->chname, chptr->topic == NULL ? "" : chptr->topic); sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, chptr->topic == NULL ? "" : chptr->topic); } else sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), from, to, chptr->chname); } else /* only asking for topic */ { if (!SecretChannel(chptr) || IsMember(source_p, chptr)) { if (chptr->topic == NULL) sendto_one(source_p, form_str(RPL_NOTOPIC), from, to, chptr->chname); else { sendto_one(source_p, form_str(RPL_TOPIC), from, to, chptr->chname, chptr->topic); sendto_one(source_p, form_str(RPL_TOPICWHOTIME), from, to, chptr->chname, chptr->topic_info, chptr->topic_time); } } else { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), from, to, chptr->chname); return; } } } else { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), from, to, parv[1]); } }
/* build_target_list() * * inputs - pointer to given client_p (server) * - pointer to given source (oper/client etc.) * - pointer to list of nicks/channels * - pointer to table to place results * - pointer to text (only used if source_p is an oper) * output - number of valid entities * side effects - target_table is modified to contain a list of * pointers to channels or clients * if source client is an oper * all the classic old bizzare oper privmsg tricks * are parsed and sent as is, if prefixed with $ * to disambiguate. * */ static int build_target_list(int p_or_n, const char *command, struct Client *client_p, struct Client *source_p, char *nicks_channels, char *text) { int type; char *p = NULL, *nick, *target_list, ncbuf[IRCD_BUFSIZE]; struct Channel *chptr = NULL; struct Client *target_p = NULL; /* Sigh, we can't mutilate parv[1] incase we need it to send to a hub */ if(!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) { strlcpy(ncbuf, nicks_channels, sizeof(ncbuf)); target_list = ncbuf; } else target_list = nicks_channels; /* skip strcpy for non-lazyleafs */ ntargets = 0; for(nick = strtoken(&p, target_list, ","); nick; nick = strtoken(&p, NULL, ",")) { char *with_prefix; /* * channels are privmsg'd a lot more than other clients, moved up * here plain old channel msg? */ if(IsChanPrefix(*nick)) { /* ignore send of local channel to a server (should not happen) */ if(*nick == '&' && IsServer(client_p)) continue; if((chptr = hash_find_channel(nick)) != NULL) { if(!duplicate_ptr(chptr)) { if(ntargets >= ConfigFileEntry.max_targets) { sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), nick, ConfigFileEntry.max_targets); return (1); } targets[ntargets].ptr = (void *) chptr; targets[ntargets++].type = ENTITY_CHANNEL; } } else { if(!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) return -1; else if(p_or_n != NOTICE) sendto_one(source_p, form_str(ERR_NOSUCHNICK), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), nick); } continue; } /* look for a privmsg to another client */ if((target_p = find_person(client_p, nick)) != NULL) { if(!duplicate_ptr(target_p)) { if(ntargets >= ConfigFileEntry.max_targets) { sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), nick, ConfigFileEntry.max_targets); return (1); } targets[ntargets].ptr = (void *) target_p; targets[ntargets].type = ENTITY_CLIENT; targets[ntargets++].flags = 0; } continue; } /* @#channel or +#channel message ? */ type = 0; with_prefix = nick; /* allow ~&%+@ if someone wants to do that */ for(;;) { #ifdef CHANAQ if(*nick == '~') type |= CHFL_OWNER; else if(*nick == '&') type |= CHFL_OWNER | CHFL_PROTECTED; else #endif if(*nick == '@') type |= CHFL_OWNER | CHFL_PROTECTED | CHFL_CHANOP; #ifdef HALFOPS else if(*nick == '%') type |= CHFL_OWNER | CHFL_PROTECTED | CHFL_CHANOP | CHFL_HALFOP; #endif else if(*nick == '+') type |= CHFL_OWNER | CHFL_PROTECTED | CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE; else break; nick++; } if(type != 0) { /* suggested by Mortiis */ if(*nick == '\0') /* if its a '\0' dump it, there is no recipient */ { sendto_one(source_p, form_str(ERR_NORECIPIENT), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), command); continue; } /* At this point, nick+1 should be a channel name i.e. #foo or &foo * if the channel is found, fine, if not report an error */ if((chptr = hash_find_channel(nick)) != NULL) { if(!has_member_flags(find_channel_link(source_p, chptr), CHFL_OWNER | CHFL_PROTECTED | CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), with_prefix); return (-1); } if(!duplicate_ptr(chptr)) { if(ntargets >= ConfigFileEntry.max_targets) { sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), nick, ConfigFileEntry.max_targets); return (1); } targets[ntargets].ptr = (void *) chptr; targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL; targets[ntargets++].flags = type; } } else { if(!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) return -1; else if(p_or_n != NOTICE) sendto_one(source_p, form_str(ERR_NOSUCHNICK), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), nick); } continue; } if((*nick == '$') || strchr(nick, '@') != NULL) { handle_special(p_or_n, command, client_p, source_p, nick, text); } else { if(!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) return -1; else if(p_or_n != NOTICE) { if(!IsDigit(*nick) || MyClient(source_p)) sendto_one(source_p, form_str(ERR_NOSUCHNICK), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), nick); } } /* continue; */ } return (1); }
/*! \brief TOPIC command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = channel name * - parv[2] = topic text, if setting topic */ static int m_topic(struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; if (EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NEEDMOREPARAMS, "TOPIC"); return 0; } if (!IsFloodDone(source_p)) flood_endgrace(source_p); if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one_numeric(source_p, &me, ERR_NOSUCHCHANNEL, parv[1]); return 0; } /* setting topic */ if (parc > 2) { const struct Membership *member = NULL; if ((member = find_channel_link(source_p, chptr)) == NULL) { sendto_one_numeric(source_p, &me, ERR_NOTONCHANNEL, chptr->name); return 0; } if (!(chptr->mode.mode & MODE_TOPICLIMIT) || has_member_flags(member, CHFL_CHANOP|CHFL_HALFOP)) { char topic_info[USERHOST_REPLYLEN]; snprintf(topic_info, sizeof(topic_info), "%s!%s@%s", source_p->name, source_p->username, source_p->host); channel_set_topic(chptr, parv[2], topic_info, CurrentTime, 1); sendto_server(source_p, 0, 0, ":%s TOPIC %s :%s", source_p->id, chptr->name, chptr->topic); sendto_channel_local(0, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->name, chptr->topic); } else sendto_one_numeric(source_p, &me, ERR_CHANOPRIVSNEEDED, chptr->name); } else /* only asking for topic */ { if (!SecretChannel(chptr) || IsMember(source_p, chptr)) { if (chptr->topic[0] == '\0') sendto_one_numeric(source_p, &me, RPL_NOTOPIC, chptr->name); else { sendto_one_numeric(source_p, &me, RPL_TOPIC, chptr->name, chptr->topic); sendto_one_numeric(source_p, &me, RPL_TOPICWHOTIME, chptr->name, chptr->topic_info, chptr->topic_time); } } else sendto_one_numeric(source_p, &me, ERR_NOTONCHANNEL, chptr->name); } return 0; }
/* m_kick() * parv[0] = sender prefix * parv[1] = channel * parv[2] = client to kick * parv[3] = kick comment */ static void m_kick(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *who; struct Channel *chptr; int chasing = 0; int gmode_used = 0; char *comment; char *name; char *p = NULL; char *user; const char *from, *to; struct Membership *ms = NULL; struct Membership *ms_target; if (!MyConnect(source_p) && IsCapable(source_p->from, CAP_TS6) && HasID(source_p)) { from = me.id; to = source_p->id; } else { from = me.name; to = source_p->name; } if (*parv[2] == '\0') { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), from, to, "KICK"); return; } if (MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); comment = (EmptyString(parv[3])) ? parv[2] : parv[3]; if (strlen(comment) > (size_t)KICKLEN) comment[KICKLEN] = '\0'; name = parv[1]; while (*name == ',') name++; if ((p = strchr(name,',')) != NULL) *p = '\0'; if (*name == '\0') return; if ((chptr = hash_find_channel(name)) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), from, to, name); return; } if (!IsServer(source_p)) { if ((ms = find_channel_link(source_p, chptr)) == NULL) { if (MyConnect(source_p)) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, name); return; } } if(chptr->mode.mode & MODE_NOCOLOR && msg_has_colors(comment)) comment = strip_color(comment); if (!has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP)) { /* was a user, not a server, and user isn't seen as a chanop here */ if (IsGod(source_p) && MyConnect(source_p)) gmode_used = TRUE; else if (MyConnect(source_p)) { /* user on _my_ server, with no chanops.. so go away */ sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, name); return; } if (chptr->channelts == 0 && !IsGod(source_p)) { /* If its a TS 0 channel, do it the old way */ sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, name); return; } /* Its a user doing a kick, but is not showing as chanop locally * its also not a user ON -my- server, and the channel has a TS. * There are two cases we can get to this point then... * * 1) connect burst is happening, and for some reason a legit * op has sent a KICK, but the SJOIN hasn't happened yet or * been seen. (who knows.. due to lag...) * * 2) The channel is desynced. That can STILL happen with TS * * Now, the old code roger wrote, would allow the KICK to * go through. Thats quite legit, but lets weird things like * KICKS by users who appear not to be chanopped happen, * or even neater, they appear not to be on the channel. * This fits every definition of a desync, doesn't it? ;-) * So I will allow the KICK, otherwise, things are MUCH worse. * But I will warn it as a possible desync. * * -Dianora */ } } user = parv[2]; while (*user == ',') user++; if ((p = strchr(user, ',')) != NULL) *p = '\0'; if (*user == '\0') return; if ((who = find_chasing(client_p, source_p, user, &chasing)) == NULL) return; if ((ms_target = find_channel_link(who, chptr)) != NULL) { if (IsGod(who)) { char tmp[IRCD_BUFSIZE]; ircsprintf(tmp, "%s is using God mode: to evade KICK from %s: %s %s %s", who->name, source_p->name, chptr->chname, parv[2], parv[3] ? parv[3] : ""); sendto_gnotice_flags(UMODE_SERVNOTICE, L_ALL, me.name, &me, NULL, tmp); oftc_log(tmp); return; } if(IsService(who)) return; #ifdef HALFOPS /* half ops cannot kick other halfops on private channels */ if (has_member_flags(ms, CHFL_HALFOP) && !has_member_flags(ms, CHFL_CHANOP)) { if (((chptr->mode.mode & MODE_PRIVATE) && has_member_flags(ms_target, CHFL_CHANOP|CHFL_HALFOP)) || has_member_flags(ms_target, CHFL_CHANOP)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, name); return; } } #endif /* jdc * - In the case of a server kicking a user (i.e. CLEARCHAN), * the kick should show up as coming from the server which did * the kick. * - Personally, flame and I believe that server kicks shouldn't * be sent anyways. Just waiting for some oper to abuse it... */ if (IsServer(source_p)) sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s KICK %s %s :%s", source_p->name, name, who->name, comment); else sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s KICK %s %s :%s", source_p->name, source_p->username, source_p->host, name, who->name, comment); sendto_server(client_p, NULL, chptr, CAP_TS6, NOCAPS, NOFLAGS, ":%s KICK %s %s :%s", ID(source_p), chptr->chname, ID(who), comment); sendto_server(client_p, NULL, chptr, NOCAPS, CAP_TS6, NOFLAGS, ":%s KICK %s %s :%s", source_p->name, chptr->chname, who->name, comment); remove_user_from_channel(ms_target); if(gmode_used) { char tmp[IRCD_BUFSIZE]; ircsprintf(tmp, "%s is using God mode: KICK %s %s %s", source_p->name, chptr->chname, parv[2], parv[3] ? parv[3] : ""); sendto_gnotice_flags(UMODE_SERVNOTICE, L_ALL, me.name, &me, NULL, tmp); oftc_log(tmp); } } else sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL), from, to, user, name); }
/* * m_mode - MODE command handler * parv[0] - sender * parv[1] - channel */ static void m_mode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; struct Membership *member; static char modebuf[MODEBUFLEN]; static char parabuf[MODEBUFLEN]; if (EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "MODE"); return; } /* Now, try to find the channel in question */ if (!IsChanPrefix(*parv[1])) { /* if here, it has to be a non-channel name */ set_user_mode(client_p, source_p, parc, parv); return; } if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), ID_or_name(&me, source_p->from), ID_or_name(source_p, source_p->from), parv[1]); return; } /* Now known the channel exists */ if (parc < 3) { channel_modes(chptr, source_p, modebuf, parabuf); sendto_one(source_p, form_str(RPL_CHANNELMODEIS), me.name, source_p->name, chptr->chname, modebuf, parabuf); sendto_one(source_p, form_str(RPL_CREATIONTIME), me.name, source_p->name, chptr->chname, chptr->channelts); } /* bounce all modes from people we deop on sjoin * servers have always gotten away with murder, * including telnet servers *g* - Dianora * * XXX Is it worth the bother to make an ms_mode() ? - Dianora */ else if (IsServer(source_p)) { set_channel_mode(client_p, source_p, chptr, NULL, parc - 2, parv + 2, chptr->chname); } else { member = find_channel_link(source_p, chptr); if (!has_member_flags(member, CHFL_DEOPPED)) { /* Finish the flood grace period... */ if (MyClient(source_p) && !IsFloodDone(source_p)) { if (!((parc == 3) && (parv[2][0] == 'b') && (parv[2][1] == '\0'))) flood_endgrace(source_p); } set_channel_mode(client_p, source_p, chptr, member, parc - 2, parv + 2, chptr->chname); } } }
/* m_topic() * parv[0] = sender prefix * parv[1] = channel name * parv[2] = new topic, if setting topic */ static void m_topic(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; if (EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "TOPIC"); return; } if (!IsFloodDone(source_p)) flood_endgrace(source_p); if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, parv[1]); return; } /* setting topic */ if (parc > 2) { struct Membership *ms; if ((ms = find_channel_link(source_p, chptr)) == NULL) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, parv[1]); return; } if (!(chptr->mode.mode & MODE_TOPICLIMIT) || has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP)) { char topic_info[USERHOST_REPLYLEN]; snprintf(topic_info, sizeof(topic_info), "%s!%s@%s", source_p->name, source_p->username, source_p->host); set_channel_topic(chptr, parv[2], topic_info, CurrentTime, 1); sendto_server(client_p, CAP_TS6, NOCAPS, ":%s TOPIC %s :%s", ID(source_p), chptr->chname, chptr->topic); sendto_server(client_p, NOCAPS, CAP_TS6, ":%s TOPIC %s :%s", source_p->name, chptr->chname, chptr->topic); sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, chptr->topic); } else sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, chptr->chname); } else /* only asking for topic */ { if (!SecretChannel(chptr) || IsMember(source_p, chptr)) { if (chptr->topic[0] == '\0') sendto_one(source_p, form_str(RPL_NOTOPIC), me.name, source_p->name, chptr->chname); else { sendto_one(source_p, form_str(RPL_TOPIC), me.name, source_p->name, chptr->chname, chptr->topic); sendto_one(source_p, form_str(RPL_TOPICWHOTIME), me.name, source_p->name, chptr->chname, chptr->topic_info, chptr->topic_time); } } else sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, chptr->chname); } }