/** Set a channel topic or report an error. * @param[in] sptr Original topic setter. * @param[in] cptr Neighbor that sent the topic message. * @param[in] chptr Channel to set topic on. * @param[in] topic New topic. * @param[in] ts Timestamp that topic was set (0 for current time). */ static void do_settopic(struct Client *sptr, struct Client *cptr, struct Channel *chptr, char *topic, time_t ts) { struct Client *from; int newtopic; if (feature_bool(FEAT_HIS_BANWHO) && IsServer(sptr)) from = &his; else from = sptr; /* Note if this is just a refresh of an old topic, and don't * send it to all the clients to save bandwidth. We still send * it to other servers as they may have split and lost the topic. */ newtopic=ircd_strncmp(chptr->topic,topic,TOPICLEN)!=0; /* setting a topic */ ircd_strncpy(chptr->topic, topic, TOPICLEN); ircd_strncpy(chptr->topic_nick, cli_name(from), NICKLEN); chptr->topic_time = ts ? ts : TStime(); /* Fixed in 2.10.11: Don't propagate local topics */ if (!IsLocalChannel(chptr->chname)) sendcmdto_serv(sptr, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr, chptr->creationtime, chptr->topic_time, chptr->topic); if (newtopic) sendcmdto_channel(from, CMD_TOPIC, chptr, NULL, SKIP_SERVERS, "%H :%s", chptr, chptr->topic); /* if this is the same topic as before we send it to the person that * set it (so they knew it went through ok), but don't bother sending * it to everyone else on the channel to save bandwidth */ else if (MyUser(sptr)) sendcmdto_one(sptr, CMD_TOPIC, sptr, "%H :%s", chptr, chptr->topic); }
/** Handle a WALLVOICES message from a local client. * * \a parv has the following elements: * \li \a parv[1] is the name of the channel to which to send * \li \a parv[\a parc - 1] is the message to send * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int m_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; const char *ch; assert(0 != cptr); assert(cptr == sptr); if (parc < 2 || EmptyString(parv[1])) return send_reply(sptr, ERR_NORECIPIENT, "WALLVOICES"); if (parc < 3 || EmptyString(parv[parc - 1])) return send_reply(sptr, ERR_NOTEXTTOSEND); if (IsChannelName(parv[1]) && (chptr = FindChannel(parv[1]))) { if (client_can_send_to_channel(sptr, chptr, 0) && !(chptr->mode.mode & MODE_NONOTICE)) { if ((chptr->mode.mode & MODE_NOPRIVMSGS) && check_target_limit(sptr, chptr, chptr->chname, 0)) return 0; #if 0 /* +cC checks */ if (chptr->mode.mode & MODE_NOCOLOUR) for (ch=parv[parc - 1];*ch;ch++) if (*ch==2 || *ch==3 || *ch==22 || *ch==27 || *ch==31) { return 0; } if ((chptr->mode.mode & MODE_NOCTCP) && ircd_strncmp(parv[parc - 1],"\001ACTION ",8)) for (ch=parv[parc - 1];*ch;) if (*ch++==1) { return 0; } #endif RevealDelayedJoinIfNeeded(sptr, chptr); sendcmdto_channel(sptr, CMD_WALLVOICES, chptr, cptr, SKIP_DEAF | SKIP_BURST | SKIP_NONVOICES, "%H :+ %s", chptr, parv[parc - 1]); } else send_reply(sptr, ERR_CANNOTSENDTOCHAN, parv[1]); } else send_reply(sptr, ERR_NOSUCHCHANNEL, parv[1]); return 0; }
/** Handle a WALLVOICES message from a server. * * \a parv has the following elements: * \li \a parv[1] is the name of the channel to which to send * \li \a parv[\a parc - 1] is the message to send * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int ms_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; assert(0 != cptr); assert(0 != sptr); if (parc < 3 || !IsUser(sptr)) return 0; if (!IsLocalChannel(parv[1]) && (chptr = FindChannel(parv[1]))) { if (client_can_send_to_channel(sptr, chptr, 1)) { sendcmdto_channel(sptr, CMD_WALLVOICES, chptr, cptr, SKIP_DEAF | SKIP_BURST | SKIP_NONVOICES, "%H :+ %s", chptr, parv[parc - 1]); } else send_reply(sptr, ERR_CANNOTSENDTOCHAN, parv[1]); } return 0; }
/** Handle a KICK message from a local connection. * * \a parv has the following elements: * \li \a parv[1] is the channel name to kick someone from * \li \a parv[2] is the nickname of the client to kick * \li \a parv[\a parc - 1] (optional) is the kick comment * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *who; struct Channel *chptr; struct Membership *member = 0; struct Membership* member2; char *name, *comment; if (parc < 3 || *parv[1] == '\0') return need_more_params(sptr, "KICK"); name = parv[1]; /* simple checks */ if (!(chptr = get_channel(sptr, name, CGT_NO_CREATE))) return send_reply(sptr, ERR_NOSUCHCHANNEL, name); #if defined(DDB) || defined(SERVICES) if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2) || !(IsChanOwner(member2) || IsChanOp(member2))) #else if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2) || !IsChanOp(member2)) #endif return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name); if (!(who = find_chasing(sptr, parv[2], 0))) return 0; /* find_chasing sends the reply for us */ /* Don't allow the channel service to be kicked */ if (IsChannelService(who)) return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname); /* Prevent kicking opers from local channels -DM- */ if (IsLocalChannel(chptr->chname) && HasPriv(who, PRIV_DEOP_LCHAN)) return send_reply(sptr, ERR_ISOPERLCHAN, cli_name(who), chptr->chname); /* check if kicked user is actually on the channel */ if (!(member = find_member_link(chptr, who)) || IsZombie(member)) return send_reply(sptr, ERR_USERNOTINCHANNEL, cli_name(who), chptr->chname); #if defined(UNDERNET) /* Don't allow to kick member with a higher op-level, * or members with the same op-level unless both are MAXOPLEVEL. */ if (OpLevel(member) < OpLevel(member2) || (OpLevel(member) == OpLevel(member2) && OpLevel(member) < MAXOPLEVEL)) return send_reply(sptr, ERR_NOTLOWEROPLEVEL, cli_name(who), chptr->chname, OpLevel(member2), OpLevel(member), "kick", OpLevel(member) == OpLevel(member2) ? "the same" : "a higher"); #elif defined(DDB) || defined(SERVICES) /* Don't allow to kick channel owner */ if (IsChanOwner(member) && !IsAnOper(sptr)) return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname); #endif /* We rely on ircd_snprintf to truncate the comment */ comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1]; if (!IsLocalChannel(name)) sendcmdto_serv(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who, comment); if (IsDelayedJoin(member)) { /* If it's a delayed join, only send the KICK to the person doing * the kicking and the victim */ if (MyUser(who)) sendcmdto_one(sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment); sendcmdto_one(who, CMD_JOIN, sptr, "%H", chptr); sendcmdto_one(sptr, CMD_KICK, sptr, "%H %C :%s", chptr, who, comment); } else sendcmdto_channel(sptr, CMD_KICK, chptr, NULL, SKIP_SERVERS, "%H %C :%s", chptr, who, comment); make_zombie(member, who, cptr, sptr, chptr); return 0; }
/** Handle a KICK message from a server connection. * * \a parv has the following elements: * \li \a parv[1] is the channel name to kick someone from * \li \a parv[2] is the numnick of the client to kick * \li \a parv[\a parc - 1] is the kick comment * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int ms_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *who; struct Channel *chptr; struct Membership *member = 0, *sptr_link = 0; char *name, *comment; if (parc < 3 || *parv[1] == '\0') return need_more_params(sptr, "KICK"); name = parv[1]; comment = parv[parc - 1]; /* figure out who gets kicked from what */ if (IsLocalChannel(name) || !(chptr = get_channel(sptr, name, CGT_NO_CREATE)) || !(who = findNUser(parv[2]))) return 0; /* We go ahead and pass on the KICK for users not on the channel */ member = find_member_link(chptr, who); if (member && IsZombie(member)) { /* We might get a KICK from a zombie's own server because the user * net-rode during a burst (which always generates a KICK) *and* * was kicked via another server. In that case, we must remove * the user from the channel. */ if (sptr == cli_user(who)->server) { remove_user_from_channel(who, chptr); } /* Otherwise, we treat zombies like they are not channel members. */ member = 0; } /* Send HACK notice, but not for servers in BURST */ /* 2002-10-17: Don't send HACK if the users local server is kicking them */ if (IsServer(sptr) && !IsBurstOrBurstAck(sptr) && sptr!=cli_user(who)->server) sendto_opmask(0, SNO_HACK4, "HACK: %C KICK %H %C %s", sptr, chptr, who, comment); /* Unless someone accepted it downstream (or the user isn't on the channel * here), if kicker is not on channel, or if kicker is not a channel * operator, bounce the kick */ #if defined(DDB) || defined(SERVICES) if (!IsServer(sptr) && member && cli_from(who) != cptr && !IsService(cli_user(sptr)->server) && (!(sptr_link = find_member_link(chptr, sptr)) || !(IsChanOwner(sptr_link) || IsChanOp(sptr_link)))) { #else if (!IsServer(sptr) && member && cli_from(who) != cptr && (!(sptr_link = find_member_link(chptr, sptr)) || !IsChanOp(sptr_link))) { #endif sendto_opmask(0, SNO_HACK2, "HACK: %C KICK %H %C %s", sptr, chptr, who, comment); sendcmdto_one(who, CMD_JOIN, cptr, "%H", chptr); /* Reop/revoice member */ if (IsChanOp(member) || HasVoice(member)) { struct ModeBuf mbuf; modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_SERVER | /* Send mode to a server */ MODEBUF_DEST_DEOP | /* Deop the source */ MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */ #if defined(UNDERNET) if (IsChanOp(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, who, OpLevel(member)); if (HasVoice(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who, MAXOPLEVEL + 1); #else if (IsChanOp(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, who, 0); if (HasVoice(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who, 0); #endif modebuf_flush(&mbuf); } } else { /* Propagate kick... */ sendcmdto_serv(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who, comment); if (member) { /* and tell the channel about it */ if (IsDelayedJoin(member)) { if (MyUser(who)) sendcmdto_one(IsServer(sptr) ? &his : sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment); } else { sendcmdto_channel(IsServer(sptr) ? &his : sptr, CMD_KICK, chptr, NULL, SKIP_SERVERS, "%H %C :%s", chptr, who, comment); } make_zombie(member, who, cptr, sptr, chptr); } } return 0; }