/* * m_quit - client message handler * * parv[0] = sender prefix * parv[parc - 1] = comment */ int m_quit(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { assert(0 != cptr); assert(0 != sptr); assert(cptr == sptr); if (cli_user(sptr)) { struct Membership* chan; for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) { if (!IsZombie(chan) && !IsDelayedJoin(chan) && !member_can_send_to_channel(chan, 0)) return exit_client(cptr, sptr, sptr, "Signed off"); } } if (parc > 1 && !BadPtr(parv[parc - 1]) && !IsMute(sptr)) return exit_client_msg(cptr, sptr, sptr, "Quit: %s", parv[parc - 1]); else return exit_client(cptr, sptr, sptr, "Quit"); }
/* * ms_part - server message handler * * parv[0] = sender prefix * parv[1] = channel * parv[parc - 1] = comment */ int ms_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; struct Membership *member; struct JoinBuf parts; unsigned int flags; char *p = 0; char *name; ClrFlag(sptr, FLAG_TS8); /* check number of arguments */ if (parc < 2 || parv[1][0] == '\0') return need_more_params(sptr, "PART"); /* init join/part buffer */ joinbuf_init(&parts, sptr, cptr, JOINBUF_TYPE_PART, (parc > 2 && !EmptyString(parv[parc - 1])) ? parv[parc - 1] : 0, 0); /* scan through channel list */ for (name = ircd_strtok(&p, parv[1], ","); name; name = ircd_strtok(&p, 0, ",")) { flags = 0; chptr = get_channel(sptr, name, CGT_NO_CREATE); /* look up channel */ if (!chptr || IsLocalChannel(name) || !(member = find_member_link(chptr, sptr))) continue; /* ignore from remote clients */ if (IsZombie(member)) /* figure out special flags... */ flags |= CHFL_ZOMBIE; if (IsDelayedJoin(member)) flags |= CHFL_DELAYED; /* part user from channel */ joinbuf_join(&parts, chptr, flags); } return joinbuf_flush(&parts); /* flush channel parts */ }
/** 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) { struct Membership *member; /* If the member is delayed-join, show them. */ member = find_channel_member(sptr, chptr); if (member && IsDelayedJoin(member)) RevealDelayedJoin(member); 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); }
/** Searches for and handles a 0 in a join list. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] chanlist List of channels to join. * @return First token in \a chanlist after the final 0 entry, which * may be its nul terminator (if the final entry is a 0 entry). */ static char * last0(struct Client *cptr, struct Client *sptr, char *chanlist) { char *p; int join0 = 0; for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */ if (p[0] == '0' && (p[1] == ',' || p[1] == '\0')) { if (p[1] == ',') p++; chanlist = p + 1; join0 = 1; } else { while (p[0] != ',' && p[0] != '\0') /* skip past channel name */ p++; if (!p[0]) /* hit the end */ break; } if (join0) { struct JoinBuf part; struct Membership *member; joinbuf_init(&part, sptr, cptr, JOINBUF_TYPE_PARTALL, "Left all channels", 0); joinbuf_join(&part, 0, 0); while ((member = cli_user(sptr)->channel)) joinbuf_join(&part, member->channel, IsZombie(member) ? CHFL_ZOMBIE : IsDelayedJoin(member) ? CHFL_DELAYED : 0); joinbuf_flush(&part); } return chanlist; }
/* * ms_kick - server message handler * * parv[0] = sender prefix * parv[1] = channel * parv[2] = client to kick * parv[parc-1] = kick comment */ 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; ClrFlag(sptr, FLAG_TS8); 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_butone(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 (!IsServer(sptr) && member && cli_from(who) != cptr && (!(sptr_link = find_member_link(chptr, sptr)) || !IsChanOp(sptr_link))) { sendto_opmask_butone(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 (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); modebuf_flush(&mbuf); } } else { /* Propagate kick... */ sendcmdto_serv_butone(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_butserv_butone(IsServer(sptr) ? &his : sptr, CMD_KICK, chptr, NULL, 0, "%H %C :%s", chptr, who, comment); } make_zombie(member, who, cptr, sptr, chptr); } } return 0; }
/* * m_kick - generic message handler * * parv[0] = sender prefix * parv[1] = channel * parv[2] = client to kick * parv[parc-1] = kick comment */ 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; ClrFlag(sptr, FLAG_TS8); 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 (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2) || !IsChanOp(member2)) 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); /* 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"); /* We rely on ircd_snprintf to truncate the comment */ comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1]; if (!IsLocalChannel(name)) sendcmdto_serv_butone(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); CheckDelayedJoins(chptr); } else sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, 0, "%H %C :%s", chptr, who, comment); make_zombie(member, who, cptr, sptr, chptr); return 0; }
void do_names(struct Client* sptr, struct Channel* chptr, int filter) { int mlen; int idx; int flag; int needs_space; int len; char buf[BUFSIZE]; struct Client *c2ptr; struct Membership* member; assert(chptr); assert(sptr); assert((filter&NAMES_ALL) != (filter&NAMES_VIS)); /* Tag Pub/Secret channels accordingly. */ strcpy(buf, "* "); if (PubChannel(chptr)) *buf = '='; else if (SecretChannel(chptr)) *buf = '@'; len = strlen(chptr->chname); strcpy(buf + 2, chptr->chname); strcpy(buf + 2 + len, " :"); idx = len + 4; flag = 1; needs_space = 0; if (!ShowChannel(sptr, chptr)) /* Don't list private channels unless we are on them. */ return; /* Iterate over all channel members, and build up the list. */ mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); for (member = chptr->members; member; member = member->next_member) { c2ptr = member->user; if (((filter&NAMES_VIS)!=0) && IsInvisible(c2ptr)) continue; if (IsZombie(member) && member->user != sptr) continue; if (IsDelayedJoin(member) && (member->user != sptr) && !(filter & NAMES_DEL)) continue; if ((!IsDelayedJoin(member) || (member->user == sptr)) && (filter & NAMES_DEL)) continue; if (needs_space) buf[idx++] = ' '; needs_space=1; if (IsZombie(member)) buf[idx++] = '!'; else if (IsChanOp(member)) buf[idx++] = '@'; else if (HasVoice(member)) buf[idx++] = '+'; strcpy(buf + idx, cli_name(c2ptr)); idx += strlen(cli_name(c2ptr)); flag = 1; if (mlen + idx + NICKLEN + 5 > BUFSIZE) /* space, modifier, nick, \r \n \0 */ { send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); idx = len + 4; flag = 0; needs_space=0; } } if (flag) send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); if (filter&NAMES_EON) send_reply(sptr, RPL_ENDOFNAMES, chptr->chname); }
/* * Send whois information for acptr to sptr */ static void do_whois(struct Client* sptr, struct Client *acptr, int parc) { struct Client *a2cptr=0; struct Channel *chptr=0; int mlen; int len; static char buf[512]; const struct User* user = cli_user(acptr); const char* name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); a2cptr = feature_bool(FEAT_HIS_WHOIS_SERVERNAME) && !IsAnOper(sptr) && sptr != acptr ? &his : user->server; assert(user); send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, cli_info(acptr)); /* Display the channels this user is on. */ if (!IsChannelService(acptr)) { struct Membership* chan; mlen = strlen(cli_name(&me)) + strlen(cli_name(sptr)) + 12 + strlen(name); len = 0; *buf = '\0'; for (chan = user->channel; chan; chan = chan->next_channel) { chptr = chan->channel; if (!ShowChannel(sptr, chptr) && !(IsOper(sptr) && IsLocalChannel(chptr->chname))) continue; if (acptr != sptr && IsZombie(chan)) continue; /* Don't show local channels when HIS is defined, unless it's a * remote WHOIS --ULtimaTe_ */ if (IsLocalChannel(chptr->chname) && (acptr != sptr) && (parc == 2) && feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && !IsAnOper(sptr)) continue; if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf); *buf = '\0'; len = 0; } if (IsDeaf(acptr)) *(buf + len++) = '-'; if (!ShowChannel(sptr, chptr)) *(buf + len++) = '*'; if (IsDelayedJoin(chan) && (sptr != acptr)) *(buf + len++) = '<'; else if (IsChanOp(chan)) *(buf + len++) = '@'; else if (HasVoice(chan)) *(buf + len++) = '+'; else if (IsZombie(chan)) *(buf + len++) = '!'; if (len) *(buf + len) = '\0'; strcpy(buf + len, chptr->chname); len += strlen(chptr->chname); strcat(buf + len, " "); len++; } if (buf[0] != '\0') send_reply(sptr, RPL_WHOISCHANNELS, name, buf); } send_reply(sptr, RPL_WHOISSERVER, name, cli_name(a2cptr), cli_info(a2cptr)); if (user) { if (user->away) send_reply(sptr, RPL_AWAY, name, user->away); if (SeeOper(sptr,acptr)) send_reply(sptr, RPL_WHOISOPERATOR, name); if (IsAccount(acptr)) send_reply(sptr, RPL_WHOISACCOUNT, name, user->account); if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr)) send_reply(sptr, RPL_WHOISACTUALLY, name, user->username, user->realhost, ircd_ntoa(&cli_ip(acptr))); /* Hint: if your looking to add more flags to a user, eg +h, here's * probably a good place to add them :) */ if (MyConnect(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) || (sptr == acptr || IsAnOper(sptr) || parc >= 3))) send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, cli_firsttime(acptr)); } }