void relay_private_notice(struct Client* sptr, const char* name, const char* text) { struct Client* acptr; assert(0 != sptr); assert(0 != name); assert(0 != text); if (0 == (acptr = FindUser(name))) return; if (IsOnlyreg(acptr) && !IsRegnick(sptr)) { send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr)); return; } if (!IsChannelService(acptr)) { if (check_target_limit(sptr, acptr, cli_name(acptr), 0)) { return; } if (is_silenced(sptr, acptr)) { send_reply(sptr, ERR_SILENCED, cli_name(acptr)); return; } } /* * deliver the message */ if (MyUser(acptr)) add_target(acptr, sptr); sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text); }
void server_relay_private_message(struct Client* sptr, const char* name, const char* text) { struct Client* acptr; assert(0 != sptr); assert(0 != name); assert(0 != text); /* * nickname addressed? */ if (0 == (acptr = findNUser(name)) || !IsUser(acptr)) { send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHNICK, "* :Target left %s. " "Failed to deliver: [%.20s]", feature_str(FEAT_NETWORK), text); return; } if (!IsChannelService(sptr) && is_silenced(sptr, acptr)) { send_reply(sptr, ERR_SILENCED, cli_name(acptr)); return; } if (IsOnlyreg(acptr) && !IsRegnick(sptr)) { send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr)); return; } if (MyUser(acptr)) add_target(acptr, sptr); sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text); }
void server_relay_channel_message(struct Client* sptr, const char* name, const char* text) { struct Channel* chptr; assert(0 != sptr); assert(0 != name); assert(0 != text); if (0 == (chptr = FindChannel(name))) { /* * XXX - do we need to send this back from a remote server? */ send_reply(sptr, ERR_NOSUCHCHANNEL, name); return; } /* * This first: Almost never a server/service * Servers may have channel services, need to check for it here */ if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) { sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr), SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text); } else send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname); }
void server_relay_private_notice(struct Client* sptr, const char* name, const char* text) { struct Client* acptr; assert(0 != sptr); assert(0 != name); assert(0 != text); /* * nickname addressed? */ if (0 == (acptr = findNUser(name)) || !IsUser(acptr)) return; if (!IsChannelService(sptr) && is_silenced(sptr, acptr)) { send_reply(sptr, ERR_SILENCED, cli_name(acptr)); return; } if (IsOnlyreg(acptr) && !IsRegnick(sptr)) { send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr)); return; } if (MyUser(acptr)) add_target(acptr, sptr); sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text); }
int ms_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr = 0; struct ModeBuf mbuf; struct Membership *member = 0; if (parc < 3) return need_more_params(sptr, "MODE"); if (IsLocalChannel(parv[1])) return 0; if (!(chptr = FindChannel(parv[1]))) return set_user_mode(cptr, sptr, parc, parv); ClrFlag(sptr, FLAG_TS8); if (IsServer(sptr)) { if (cli_uworld(sptr)) modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ MODEBUF_DEST_SERVER | /* Send mode to servers */ MODEBUF_DEST_HACK4)); /* Send a HACK(4) message */ else modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ MODEBUF_DEST_SERVER | /* Send mode to servers */ MODEBUF_DEST_HACK3)); /* Send a HACK(3) message */ mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, (MODE_PARSE_SET | /* Set the mode */ MODE_PARSE_STRICT | /* Interpret it strictly */ MODE_PARSE_FORCE), NULL); /* And force it to be accepted */ } else { if (!IsChannelService(sptr) && (!(member = find_member_link(chptr, sptr)) || (!IsChanOp(member) && !IsHalfOp(member)))) { modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_SERVER | /* Send mode to server */ MODEBUF_DEST_HACK2 | /* Send a HACK(2) message */ MODEBUF_DEST_DEOP | /* Deop the source */ MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */ mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, (MODE_PARSE_STRICT | /* Interpret it strictly */ MODE_PARSE_BOUNCE), member); /* And bounce the MODE */ } else { modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ MODEBUF_DEST_SERVER)); /* Send mode to servers */ mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, (MODE_PARSE_SET | /* Set the mode */ MODE_PARSE_STRICT | /* Interpret it strictly */ MODE_PARSE_FORCE), member); /* And force it to be accepted */ } } return modebuf_flush(&mbuf); }
/* * mo_kill - oper message handler * * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true * IsServer(cptr), IsServer(sptr) == false * * parv[0] = sender prefix * parv[1] = kill victim * parv[parc-1] = kill path */ int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* victim; char* user; char msg[TOPICLEN + 3]; /* (, ), and \0 */ assert(0 != cptr); assert(0 != sptr); /* * oper connection to this server, cptr is always sptr */ assert(cptr == sptr); assert(IsAnOper(sptr)); if (parc < 3 || EmptyString(parv[parc - 1])) return need_more_params(sptr, "KILL"); user = parv[1]; ircd_snprintf(0, msg, sizeof(msg), "(%.*s)", TOPICLEN, parv[parc - 1]); if (!(victim = FindClient(user))) { /* * If the user has recently changed nick, we automaticly * rewrite the KILL for this new nickname--this keeps * servers in synch when nick change and kill collide */ if (!(victim = get_history(user, (long)15))) return send_reply(sptr, ERR_NOSUCHNICK, user); sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Changed KILL %s into %s", sptr, user, cli_name(victim)); } if (!HasPriv(sptr, MyConnect(victim) ? PRIV_LOCAL_KILL : PRIV_KILL)) return send_reply(sptr, ERR_NOPRIVILEGES); if (IsServer(victim) || IsMe(victim)) { return send_reply(sptr, ERR_CANTKILLSERVER); } /* * if the user is +k, prevent a kill from local user */ if (IsChannelService(victim)) return send_reply(sptr, ERR_ISCHANSERVICE, "KILL", cli_name(victim)); if (!MyConnect(victim) && !HasPriv(sptr, PRIV_KILL)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Nick %s isnt on your server", sptr, cli_name(victim)); return 0; } return do_kill(cptr, sptr, victim, cli_user(sptr)->host, cli_name(sptr), msg); }
void relay_directed_notice(struct Client* sptr, char* name, char* server, const char* text) { struct Client* acptr; char* host; assert(0 != sptr); assert(0 != name); assert(0 != text); assert(0 != server); if (0 == (acptr = FindServer(server + 1))) return; /* * NICK[%host]@server addressed? See if <server> is me first */ if (!IsMe(acptr)) { sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text); return; } /* * Look for an user whose NICK is equal to <name> and then * check if it's hostname matches <host> and if it's a local * user. */ *server = '\0'; if ((host = strchr(name, '%'))) *host++ = '\0'; if (!(acptr = FindUser(name)) || !MyUser(acptr) || (!EmptyString(host) && 0 != match(host, cli_user(acptr)->realhost))) return; *server = '@'; if (host) *--host = '%'; if (!IsChannelService(sptr) && is_silenced(sptr, acptr)) { send_reply(sptr, ERR_SILENCED, cli_name(acptr)); return; } if (IsOnlyreg(acptr) && !IsRegnick(sptr)) send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr)); else sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text); }
void server_relay_channel_notice(struct Client* sptr, const char* name, const char* text) { struct Channel* chptr; assert(0 != sptr); assert(0 != name); assert(0 != text); if (0 == (chptr = FindChannel(name))) return; /* * This first: Almost never a server/service * Servers may have channel services, need to check for it here */ if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) { sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr), SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text); } }
/* * ms_svsident - server message handler * * parv[0] = sender prefix * parv[1] = Target numeric * parv[2] = New ident */ int ms_svsident(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; char *s; char *newident = NULL; int legalident=1; if (parc < 3) return need_more_params(sptr, "SVSIDENT"); /* Ignore SVSIDENT for a user that has quit */ if (!(acptr = findNUser(parv[1]))) return 0; if (IsChannelService(acptr)) return 0; newident = strdup(parv[2]); if (strlen(newident) > USERLEN) return protocol_violation(sptr, "Ident too long in SVSIDENT command"); for (s = newident; *s; s++) { if (!IsUserChar(*s)) { legalident = 0; break; } } if (legalident == 0) return protocol_violation(sptr, "Illegal characters in SVSIDENT ident"); ircd_strncpy(cli_user(acptr)->username, newident, USERLEN); ircd_strncpy(cli_username(acptr), newident, USERLEN); sendcmdto_serv_butone(sptr, CMD_SVSIDENT, cptr, "%s%s %s", acptr->cli_user->server->cli_yxx, acptr->cli_yxx, newident); 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; }
/* * 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_from(who)) 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 * * Don't bounce the kick if it's coming from a channel service, regardless * of channel membership. -- aaronc */ if (!IsServer(sptr) && !IsChannelService(sptr) && member && cli_from(who) != cptr && (!(sptr_link = find_member_link(chptr, sptr)) || (!IsChanOp(sptr_link) && !IsHalfOp(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) || IsHalfOp(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); if (IsHalfOp(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_HALFOP, who); if (HasVoice(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who); 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 */ sendcmdto_channel_butserv_butone(IsServer(sptr) ? (feature_bool(FEAT_HIS_HIDEWHO) ? &his : &me) : 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; 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 (!is_chan_op(sptr, chptr) && !is_half_op(sptr, chptr)) 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 */ /* * ASUKA_X: * Allow +X'ed users to kick +k'ed, but not U-lined services. * --Bigfoot */ if (IsChannelService(who) && IsService(cli_user(who)->server)) return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname, "a network service"); if (IsChannelService(who) && !IsXtraOp(sptr) && (who!=sptr)) return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname, "an IRC operator"); /* Don't allow halfops to kick chanops */ if (is_chan_op(who, chptr) && is_half_op(sptr, chptr) && !is_chan_op(sptr, chptr)) return send_reply(sptr, ERR_HALFCANTKICKOP, name); /* 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); /* 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); sendcmdto_channel_butserv_butone((IsServer(sptr) ? &me : sptr), CMD_KICK, chptr, NULL, 0, "%H %C :%s", chptr, who, comment); make_zombie(member, who, cptr, sptr, chptr); return 0; }
void do_join(struct Client *cptr, struct Client *sptr, struct JoinBuf *join, struct JoinBuf *create, char *chan, char *key, int level) { struct Channel *chptr; struct Gline *gline; /* BADCHANed channel */ if ((gline = gline_find(chan, GLINE_BADCHAN | GLINE_EXACT)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BANNEDFROMCHAN, chan); return; } /* Bouncy +L joins */ if (level > feature_int(FEAT_MAX_BOUNCE)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :*** Couldn't join %s ! - Redirection (+L) setting was too bouncy", sptr, chan); return; } if (!(chptr = FindChannel(chan))) { if (((chan[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(chan) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, chan); return; } if (feature_bool(FEAT_CHANNEL_CREATE_IRCOPONLY) && !IsAnOper(sptr) && !IsChannelService(sptr)) { send_reply(sptr, ERR_NOSUCHCHANNEL, chan); return; } if (!(chptr = get_channel(sptr, chan, CGT_CREATE))) return; /* Try to add the new channel as a recent target for the user. */ if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); return; } joinbuf_join(create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER); } else if (find_member_link(chptr, sptr)) { return; /* already on channel */ } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) { return; } else { int flags = CHFL_DEOPPED; int err = 0; int excepted = 0; int exceptkli = 0; struct Ban *ban = NULL; if (*chptr->mode.redir && (*chptr->mode.redir != '\0')) { if (chptr->users >= chptr->mode.limit) { if (IsNoLink(sptr)) send_reply(sptr, ERR_LINKCHAN, chptr->chname, chptr->mode.redir); else if (!IsChannelName(chptr->mode.redir) || !strIsIrcCh(chptr->mode.redir)) send_reply(sptr, ERR_NOSUCHCHANNEL, chptr->mode.redir); else { send_reply(sptr, ERR_LINKSET, chptr->chname, chptr->chname, chptr->mode.redir); do_join(cptr, sptr, join, create, chptr->mode.redir, key, level+1); } return; } } if (find_ban(sptr, chptr->exceptlist, EBAN_EXCEPTLIST, 0)) { if (feature_bool(FEAT_CHMODE_e_CHMODEEXCEPTION)) exceptkli = 1; excepted = 1; } /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (feature_bool(FEAT_CHMODE_Z_STRICT) && (chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr)) err = ERR_SSLONLYCHAN; else if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; else if (chptr->users == 0 && !chptr->mode.apass[0] && !(chptr->mode.exmode & EXMODE_PERSIST)) { /* Joining a zombie channel (zannel): give ops and increment TS. */ flags = CHFL_CHANOP; chptr->creationtime++; } else if (IsXtraOp(sptr)) { /* XtraOp bypasses all other checks. */ } else if ((chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr)) err = ERR_SSLONLYCHAN; else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key)) && !exceptkli) err = ERR_BADCHANNELKEY; else if (*chptr->mode.key && feature_bool(FEAT_FLEXIBLEKEYS) && (key && !strcmp(key, chptr->mode.key))) { /* Assume key checked by previous condition was found to be correct and allow join because FEAT_FLEXIBLEKEYS was enabled */ } else if ((chptr->mode.mode & MODE_INVITEONLY) && !exceptkli) err = ERR_INVITEONLYCHAN; else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit) && !exceptkli) err = ERR_CHANNELISFULL; else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr)) err = ERR_NEEDREGGEDNICK; else if ((chptr->mode.exmode & EXMODE_ADMINONLY) && !IsAdmin(sptr)) err = ERR_ADMINONLYCHAN; else if ((chptr->mode.exmode & EXMODE_OPERONLY) && !IsAnOper(sptr)) err = ERR_OPERONLYCHAN; else if ((ban = find_ban(sptr, chptr->banlist, EBAN_NONE, 0)) && !excepted) err = ERR_BANNEDFROMCHAN; /* An oper with WALK_LCHAN privilege can join a local channel * he otherwise could not join by using "OVERRIDE" as the key. * This will generate a HACK(4) notice, but fails if the oper * could normally join the channel. */ if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: if (strcmp(chptr->mode.key, "OVERRIDE") && strcmp(chptr->mode.apass, "OVERRIDE") && strcmp(chptr->mode.upass, "OVERRIDE")) { send_reply(sptr, ERR_DONTCHEAT, chptr->chname); return; } break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; case ERR_BADCHANNELKEY: err = 'k'; break; case ERR_NEEDREGGEDNICK: err = 'r'; break; case ERR_ADMINONLYCHAN: err = 'a'; break; case ERR_OPERONLYCHAN: err = 'O'; break; case ERR_SSLONLYCHAN: err = 'Z'; break; default: err = '?'; break; } /* send accountability notice */ if (err) sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { switch(err) { case ERR_NEEDREGGEDNICK: send_reply(sptr, ERR_NEEDREGGEDNICK, chptr->chname, feature_str(FEAT_URLREG)); break; default: send_reply(sptr, err, chptr->chname); break; } return; } joinbuf_join(join, chptr, flags); if (flags & CHFL_CHANOP) { struct ModeBuf mbuf; /* Always let the server op him: this is needed on a net with older servers because they 'destruct' channels immediately when they become empty without sending out a DESTRUCT message. As a result, they would always bounce a mode (as HACK(2)) when the user ops himself. (There is also no particularly good reason to have the user op himself.) */ modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); modebuf_flush(&mbuf); } } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ }
/* * 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)); } }
void relay_private_message(struct Client *cptr, struct Client* sptr, const char* name, const char* text) { struct Client* acptr; static char *pmsg = NULL; static int last_length = 0; int len; assert(0 != sptr); assert(0 != name); assert(0 != text); if (0 == (acptr = FindUser(name))) { send_reply(sptr, ERR_NOSUCHNICK, name); return; } if (IsOnlyreg(acptr) && !IsRegnick(sptr)) { send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr)); return; } if (!IsChannelService(acptr)) { if (check_target_limit(sptr, acptr, cli_name(acptr), 0)) { return; } if (is_silenced(sptr, acptr)) { send_reply(sptr, ERR_SILENCED, cli_name(acptr)); return; } } if (MyUser(cptr) && !es_representante(cptr)) { if ((len = strlen(text)+1) > last_length) { if (pmsg) free(pmsg); pmsg = (char *)MyMalloc(sizeof(char)*len); last_length = len; } strcpy(pmsg, text); correct_colors(pmsg); if (process_badwords(pmsg, BADWORDS_QUERY) != NULL) return; } /* * send away message if user away */ if (cli_user(acptr) && cli_user(acptr)->away) send_reply(sptr, RPL_AWAY, cli_name(acptr), cli_user(acptr)->away); /* * deliver the message */ if (MyUser(acptr)) add_target(acptr, sptr); sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text); }
void relay_directed_message(struct Client* sptr, char* name, char* server, const char* text) { struct Client* acptr; char* host; assert(0 != sptr); assert(0 != name); assert(0 != text); assert(0 != server); if (0 == (acptr = FindServer(server + 1))) { send_reply(sptr, ERR_NOSUCHNICK, name); return; } /* * NICK[%host]@server addressed? See if <server> is me first */ if (!IsMe(acptr)) { sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text); return; } /* * Look for an user whose NICK is equal to <name> and then * check if it's hostname matches <host> and if it's a local * user. */ *server = '\0'; if ((host = strchr(name, '%'))) *host++ = '\0'; /* As reported by Vampire-, it's possible to brute force finding users * by sending a message to each server and see which one succeeded. * This means we have to remove error reporting. Sigh. Better than * removing the ability to send directed messages to client servers * Thanks for the suggestion Vampire=. -- Isomer 2001-08-28 * Argh, /ping nick@server, disallow messages to non +k clients :/ I hate * this. -- Isomer 2001-09-16 */ if (!(acptr = FindUser(name)) || !MyUser(acptr) || (!EmptyString(host) && 0 != match(host, cli_user(acptr)->realhost)) || !IsChannelService(acptr)) { #if 0 send_reply(sptr, ERR_NOSUCHNICK, name); #endif return; } *server = '@'; if (host) *--host = '%'; if (!IsChannelService(sptr) && is_silenced(sptr, acptr)) { send_reply(sptr, ERR_SILENCED, cli_name(acptr)); return; } if (IsOnlyreg(acptr) && !IsRegnick(sptr)) send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr)); else sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text); }
/* * The function that actually prints out the WHO reply for a client found */ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, int fields, char* qrt) { char *p1; struct Channel *chptr = repchan; static char buf1[512]; /* NOTE: with current fields list and sizes this _cannot_ overrun, and also the message finally sent shouldn't ever be truncated */ p1 = buf1; buf1[1] = '\0'; /* If we don't have a channel and we need one... try to find it, unless the listing is for a channel service, we already know that there are no common channels, thus use PubChannel and not SeeChannel */ if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) && !IsChannelService(acptr)) { struct Membership* chan; for (chan = cli_user(acptr)->channel; chan && !chptr; chan = chan->next_channel) if (PubChannel(chan->channel) && (acptr == sptr || !IsZombie(chan))) chptr = chan->channel; } /* Place the fields one by one in the buffer and send it note that fields == NULL means "default query" */ if (fields & WHO_FIELD_QTY) /* Query type */ { *(p1++) = ' '; if (BadPtr(qrt)) *(p1++) = '0'; else while ((*qrt) && (*(p1++) = *(qrt++))); } if (!fields || (fields & WHO_FIELD_CHA)) { char *p2; *(p1++) = ' '; if ((p2 = (chptr ? chptr->chname : NULL))) while ((*p2) && (*(p1++) = *(p2++))); else *(p1++) = '*'; } if (!fields || (fields & WHO_FIELD_UID)) { char *p2 = cli_user(acptr)->username; *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (fields & WHO_FIELD_NIP) { const char* p2 = IsHiddenHost(acptr) && (!IsViewip(sptr) && acptr != sptr) ? feature_str(FEAT_HIDDEN_IP) : ircd_ntoa((const char*) &(cli_ip(acptr))); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_HOS)) { char *p2 = (IsViewip(sptr) || acptr == sptr) ? get_realhost(acptr) : get_virtualhost(acptr); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_SER)) { char *p2; int haspriv = IsAnOper(sptr) || es_representante(sptr); if (IsMe(cli_user(acptr)->server)) { if ((sptr != acptr) && feature_bool(FEAT_HIS_WHO_SERVERNAME) && !haspriv) p2 = (char *)feature_str(FEAT_HIS_SERVERNAME); else p2 = cli_name(&me); } else { if (IsHiddenserv(cli_user(acptr)->server) && !haspriv) p2 = (char *)feature_str(FEAT_HIS_SERVERNAME); else p2 = cli_name(cli_user(acptr)->server); } *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_NIC)) { char *p2 = cli_name(acptr); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_FLA)) { *(p1++) = ' '; if (cli_user(acptr)->away) *(p1++) = 'G'; else *(p1++) = 'H'; if ((IsAnOper(acptr) || es_representante(acptr) || IsPreoper(acptr))/* && (HasPriv(acptr, PRIV_DISPLAY) || HasPriv(sptr, PRIV_SEE_OPERS))*/) *(p1++) = '*'; if (fields) { /* If you specified flags then we assume you know how to parse * multiple channel status flags, as this is currently the only * way to know if someone has @'s *and* is +'d. */ if (chptr && is_chan_op(acptr, chptr)) *(p1++) = '@'; if (chptr && has_voice(acptr, chptr)) *(p1++) = '+'; if (chptr && is_chan_halfop(acptr, chptr)) *(p1++) = '%'; if (chptr && is_chan_owner(acptr, chptr)) *(p1++) = '.'; if (chptr && is_zombie(acptr, chptr)) *(p1++) = '!'; } else { if (chptr && is_chan_op(acptr, chptr)) *(p1++) = '@'; else if (chptr && has_voice(acptr, chptr)) *(p1++) = '+'; else if (chptr && is_zombie(acptr, chptr)) *(p1++) = '!'; else if (chptr && is_chan_halfop(acptr, chptr)) *(p1++) = '%'; else if (chptr && is_chan_owner(acptr, chptr)) *(p1++) = '.'; } if (IsDeaf(acptr)) *(p1++) = 'd'; if (IsAnOper(sptr)) { if (IsInvisible(acptr)) *(p1++) = 'i'; if (SendWallops(acptr)) *(p1++) = 'w'; if (SendDebug(acptr)) *(p1++) = 'g'; } if (HasHiddenHost(acptr)) *(p1++) = 'x'; if (IsAnOper(acptr)) *(p1++) = 'o'; if (IsPreoper(acptr)) *(p1++) = 'p'; if (IsHelpOp(acptr)) *(p1++) = 'h'; if (IsCoadmin(acptr)) *(p1++) = 'a'; if (IsAdmin(acptr)) *(p1++) = 'A'; if (IsDevel(acptr)) *(p1++) = 'D'; if (IsViewip(acptr)) *(p1++) = 'X'; } if (!fields || (fields & WHO_FIELD_DIS)) { *p1++ = ' '; if (!fields) *p1++ = ':'; /* Place colon here for default reply */ if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr)) *p1++ = (sptr == acptr) ? '0' : '3'; else /* three digit hopcount maximum */ p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr)); } if (fields & WHO_FIELD_IDL) { *p1++ = ' '; if (MyUser(acptr)) { p1 += ircd_snprintf(0, p1, 11, "%d", CurrentTime - cli_user(acptr)->last); } else { *p1++ = '0'; } } if (!fields || (fields & WHO_FIELD_REN)) { char *p2 = cli_info(acptr); *p1++ = ' '; if (fields) *p1++ = ':'; /* Place colon here for special reply */ while ((*p2) && (*(p1++) = *(p2++))); } if (fields & WHO_FIELD_ACC) { char *p2 = cli_user(acptr)->account; *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } /* The first char will always be an useless blank and we need to terminate buf1 */ *p1 = '\0'; p1 = buf1; send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1); }
int m_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr = 0; unsigned int hoflags = 0; struct ModeBuf mbuf; struct Membership *member = 0; if (parc < 2) return need_more_params(sptr, "MODE"); clean_channelname(parv[1]); if (!(chptr = FindChannel(parv[1]))) return set_user_mode(cptr, sptr, parc, parv); ClrFlag(sptr, FLAG_TS8); member = find_member_link(chptr, sptr); if (parc < 3) { char modebuf[MODEBUFLEN]; char parabuf[MODEBUFLEN]; *modebuf = *parabuf = '\0'; modebuf[1] = '\0'; channel_modes(sptr, modebuf, parabuf, sizeof(parabuf), chptr); send_reply(sptr, RPL_CHANNELMODEIS, chptr->chname, modebuf, parabuf); send_reply(sptr, RPL_CREATIONTIME, chptr->chname, chptr->creationtime); return 0; } if ((!member && !IsChannelService(sptr)) || (member && !IsChanOp(member) && !IsHalfOp(member))) { if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_MODE_LCHAN)) { modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to channel */ MODEBUF_DEST_HACK4)); /* Send HACK(4) notice */ mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, (MODE_PARSE_SET | /* Set the mode */ MODE_PARSE_FORCE), NULL); /* Force it to take */ return modebuf_flush(&mbuf); } else mode_parse(0, cptr, sptr, chptr, parc - 2, parv + 2, (member ? MODE_PARSE_NOTOPER : MODE_PARSE_NOTMEMBER), NULL); return 0; } modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to channel */ MODEBUF_DEST_SERVER)); /* Send mode to servers */ if (member && IsChanOp(member)) hoflags = MODE_PARSE_SET; /* set unconditionally */ else if (member && IsHalfOp(member)) hoflags = MODE_PARSE_ISHALFOP|MODE_PARSE_SET|MODE_PARSE_NOTOPER; /* allowed to +v */ else hoflags = MODE_PARSE_NOTOPER; mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, hoflags, member); return modebuf_flush(&mbuf); }
void do_oper(struct Client* cptr, struct Client* sptr, struct ConfItem* aconf) { struct Flags old_mode = cli_flags(sptr); char* modes; char* parv[2]; char* join[3]; char chan[CHANNELLEN-1]; char* ajoinchan; char* ajoinnotice; unsigned int snomask = 0; parv[0] = cli_name(sptr); parv[1] = NULL; SetOper(sptr); client_set_privs(sptr, aconf); ClearOper(sptr); snomask = ConfSnoMask(aconf) & SNO_ALL; snomask |= aconf->snomask & SNO_ALL; ajoinchan = ConfAjoinChan(aconf); ajoinnotice = ConfAjoinNotice(aconf); if (MyUser(sptr)) { SetLocOp(sptr); if (HasPriv(sptr, PRIV_PROPAGATE)) { ClearLocOp(sptr); SetOper(sptr); if (HasPriv(sptr, PRIV_ADMIN)) SetAdmin(sptr); if (!IsHideOper(sptr) && !IsChannelService(sptr) && !IsBot(sptr)) ++UserStats.opers; } cli_handler(sptr) = OPER_HANDLER; SetFlag(sptr, FLAG_WALLOP); SetFlag(sptr, FLAG_SERVNOTICE); SetFlag(sptr, FLAG_DEBUG); if (snomask) set_snomask(sptr, snomask, SNO_ADD); else set_snomask(sptr, feature_int(FEAT_SNOMASK_OPERDEFAULT), SNO_ADD); cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */ cli_max_recvq(sptr) = 0; /* Get the recvq from the oper's class */ cli_lag_min(sptr) = -2; /* Get the fake lag minimum from the oper's class */ cli_lag_factor(sptr) = -2; /* Get the fake lag factor from the oper's class */ send_umode_out(sptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); } else { client_send_privs(&me, sptr, sptr); if (HasPriv(sptr, PRIV_PROPAGATE)) { modes = (HasPriv(sptr, PRIV_ADMIN) ? "aowsg" : "owsg"); } else { modes = "Owsg"; } sendcmdto_one(&me, CMD_MODE, sptr, "%s %s", cli_name(sptr), modes); } modes = ConfUmode(aconf); if (modes) { if (MyUser(sptr)) { char *umodev[] = { NULL, NULL, NULL, NULL }; umodev[1] = cli_name(sptr); umodev[2] = modes; old_mode = cli_flags(sptr); set_user_mode(sptr, sptr, 3, umodev, ALLOWMODES_ANY); send_umode(NULL, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); if ((cli_snomask(sptr) != feature_int(FEAT_SNOMASK_OPERDEFAULT)) && HasFlag(sptr, FLAG_SERVNOTICE)) send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr)); } else { if (snomask) sendcmdto_one(&me, CMD_MODE, sptr, "%s %s+s +%d", cli_name(sptr), modes, snomask); else sendcmdto_one(&me, CMD_MODE, sptr, "%s %s", cli_name(sptr), modes); } } send_reply(sptr, RPL_YOUREOPER); if ((feature_int(FEAT_HOST_HIDING_STYLE) == 1) || (feature_int(FEAT_HOST_HIDING_STYLE) == 3)) hide_hostmask(sptr); if (!EmptyString(ajoinchan)) { if (!EmptyString(ajoinnotice)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, ajoinnotice); ircd_strncpy(chan, ajoinchan, CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; join[2] = NULL; m_join(sptr, sptr, 2, join); } if (!EmptyString(aconf->autojoinchan)) { if (!EmptyString(aconf->autojoinnotice)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, aconf->autojoinnotice); ircd_strncpy(chan, aconf->autojoinchan, CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; join[2] = NULL; m_join(sptr, sptr, 2, join); } sendto_opmask_butone_global((MyUser(sptr) ? &me : NULL), SNO_OLDSNO, "%s (%s@%s) is now operator (%c)", cli_name(sptr), cli_user(sptr)->username, cli_user(sptr)->realhost, IsOper(sptr) ? 'O' : 'o'); if (feature_bool(FEAT_OPERMOTD)) m_opermotd(sptr, sptr, 1, parv); log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", aconf->name, sptr); }
/** Read a 'packet' of data from a connection and process it. Read in * 8k chunks to give a better performance rating (for server * connections). Do some tricky stuff for client connections to make * sure they don't do any flooding >:-) -avalon * @param cptr Client from which to read data. * @param socket_ready If non-zero, more data can be read from the client's socket. * @return Positive number on success, zero on connection-fatal failure, negative * if user is killed. */ static int read_packet(struct Client *cptr, int socket_ready) { unsigned int dolen = 0; unsigned int length = 0; if (socket_ready && !(IsUser(cptr) && DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD))) { #if defined(USE_SSL) switch (client_recv(cptr, readbuf, sizeof(readbuf), &length)) { #else switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) { #endif case IO_SUCCESS: if (length) { cli_lasttime(cptr) = CurrentTime; ClearPingSent(cptr); ClrFlag(cptr, FLAG_NONL); if (cli_lasttime(cptr) > cli_since(cptr)) cli_since(cptr) = cli_lasttime(cptr); } break; case IO_BLOCKED: break; case IO_FAILURE: cli_error(cptr) = errno; /* SetFlag(cptr, FLAG_DEADSOCKET); */ return 0; } } /* * For server connections, we process as many as we can without * worrying about the time of day or anything :) */ if (length > 0 && IsServer(cptr)) return server_dopacket(cptr, readbuf, length); else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr))) return connect_dopacket(cptr, readbuf, length); else { /* * Before we even think of parsing what we just read, stick * it on the end of the receive queue and do it when its * turn comes around. */ if (length > 0 && dbuf_put(cptr, &(cli_recvQ(cptr)), readbuf, length) == 0) return exit_client(cptr, cptr, &me, "dbuf_put fail"); if ((DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD)) && !IsChannelService(cptr)) return exit_client(cptr, cptr, &me, "Excess Flood"); while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && (IsTrusted(cptr) || IsChannelService(cptr) || cli_since(cptr) - CurrentTime < 10)) { dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE); /* * Devious looking...whats it do ? well..if a client * sends a *long* message without any CR or LF, then * dbuf_getmsg fails and we pull it out using this * loop which just gets the next 512 bytes and then * deletes the rest of the buffer contents. * -avalon */ if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else { /* More than 512 bytes in the line - drop the input and yell * at the client. */ DBufClear(&(cli_recvQ(cptr))); send_reply(cptr, ERR_INPUTTOOLONG); } } else if (client_dopacket(cptr, dolen) == CPTR_KILLED) return CPTR_KILLED; /* * If it has become registered as a Server * then skip the per-message parsing below. */ if (IsHandshake(cptr) || IsServer(cptr)) { while (-1) { dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf)); if (dolen <= 0) return 1; else if (dolen == 0) { if (DBufLength(&(cli_recvQ(cptr))) < 510) SetFlag(cptr, FLAG_NONL); else { DBufClear(&(cli_recvQ(cptr))); /* send_reply(cptr, ERR_INPUTTOOLONG); */ } } else if ((IsServer(cptr) && server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) || (!IsServer(cptr) && connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED)) return CPTR_KILLED; } } } /* If there's still data to process, wait 2 seconds first */ if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && !t_onqueue(&(cli_proc(cptr)))) { Debug((DEBUG_LIST, "Adding client process timer for %C", cptr)); cli_freeflag(cptr) |= FREEFLAG_TIMER; timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr), TT_RELATIVE, 2); } } return 1; } /** Start a connection to another server. * @param aconf Connect block data for target server. * @param by Client who requested the connection (if any). * @return Non-zero on success; zero on failure. */ int connect_server(struct ConfItem* aconf, struct Client* by) { struct Client* cptr = 0; assert(0 != aconf); if (aconf->dns_pending) { sendto_opmask(0, SNO_OLDSNO, "Server %s connect DNS pending", aconf->name); return 0; } Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name, ircd_ntoa(&aconf->address.addr))); if ((cptr = FindClient(aconf->name))) { if (IsServer(cptr) || IsMe(cptr)) { sendto_opmask(0, SNO_OLDSNO, "Server %s already present from %s", aconf->name, cli_name(cli_from(cptr))); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present " "from %s", by, aconf->name, cli_name(cli_from(cptr))); } return 0; } else if (IsHandshake(cptr) || IsConnecting(cptr)) { if (by && IsUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in " "progress", by, cli_name(cptr)); } return 0; } } /* * If we don't know the IP# for this host and it is a hostname and * not a ip# string, then try and find the appropriate host record. */ if (!irc_in_addr_valid(&aconf->address.addr) && !ircd_aton(&aconf->address.addr, aconf->host)) { char buf[HOSTLEN + 1]; host_from_uh(buf, aconf->host, HOSTLEN); gethost_byname(buf, connect_dns_callback, aconf); aconf->dns_pending = 1; return 0; } cptr = make_client(NULL, STAT_UNKNOWN_SERVER); /* * Copy these in so we have something for error detection. */ ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN); ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN); /* * Attach config entries to client here rather than in * completed_connection. This to avoid null pointer references */ attach_confs_byhost(cptr, aconf->host, CONF_SERVER); if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) { sendto_opmask(0, SNO_OLDSNO, "Host %s is not enabled for " "connecting: no Connect block", aconf->name); if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no " "Connect block", by, aconf->name); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * attempt to connect to the server in the conf line */ if (!connect_inet(aconf, cptr)) { if (by && IsUser(by) && !MyUser(by)) { sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by, cli_name(cptr)); } det_confs_butmask(cptr, 0); free_client(cptr); return 0; } /* * NOTE: if we're here we have a valid C:Line and the client should * have started the connection and stored the remote address/port and * ip address name in itself * * The socket has been connected or connect is in progress. */ make_server(cptr); if (by && IsUser(by)) { ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s", NumNick(by)); assert(0 == cli_serv(cptr)->user); cli_serv(cptr)->user = cli_user(by); cli_user(by)->refcnt++; } else { *(cli_serv(cptr))->by = '\0'; /* strcpy(cptr->serv->by, "Auto"); */ } cli_serv(cptr)->up = &me; SetConnecting(cptr); if (cli_fd(cptr) > HighestFd) HighestFd = cli_fd(cptr); LocalClientArray[cli_fd(cptr)] = cptr; Count_newunknown(UserStats); /* Actually we lie, the connect hasn't succeeded yet, but we have a valid * cptr, so we register it now. * Maybe these two calls should be merged. */ add_client_to_list(cptr); hAddClient(cptr); /* nextping = CurrentTime; */ return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ? completed_connection(cptr) : 1; } /** Find the real hostname for the host running the server (or one which * matches the server's name) and its primary IP#. Hostname is stored * in the client structure passed as a pointer. */ void init_server_identity(void) { const struct LocalConf* conf = conf_get_local(); assert(0 != conf); ircd_strncpy(cli_name(&me), conf->name, HOSTLEN); SetYXXServerName(&me, conf->numeric); } /** Process events on a client socket. * @param ev Socket event structure that has a struct Connection as * its associated data. */ static void client_sock_callback(struct Event* ev) { struct Client* cptr; struct Connection* con; char *fmt = "%s"; char *fallback = 0; assert(0 != ev_socket(ev)); assert(0 != s_data(ev_socket(ev))); con = (struct Connection*) s_data(ev_socket(ev)); assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY); cptr = con_client(con); assert(0 == cptr || con == cli_connect(cptr)); switch (ev_type(ev)) { case ET_DESTROY: con_freeflag(con) &= ~FREEFLAG_SOCKET; if (!con_freeflag(con) && !cptr) free_connection(con); #if defined(USE_SSL) ssl_free(ev_socket(ev)); #endif break; case ET_CONNECT: /* socket connection completed */ if (!completed_connection(cptr) || IsDead(cptr)) fallback = cli_info(cptr); break; case ET_ERROR: /* an error occurred */ fallback = cli_info(cptr); cli_error(cptr) = ev_data(ev); /* If the OS told us we have a bad file descriptor, we should * record that for future reference. */ if (cli_error(cptr) == EBADF) cli_fd(cptr) = -1; if (s_state(&(con_socket(con))) == SS_CONNECTING) { completed_connection(cptr); /* for some reason, the os_get_sockerr() in completed_connection() * can return 0 even when ev_data(ev) indicates a real error, so * re-assign the client error here. */ cli_error(cptr) = ev_data(ev); break; } /*FALLTHROUGH*/ case ET_EOF: /* end of file on socket */ Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr), cli_error(cptr))); SetFlag(cptr, FLAG_DEADSOCKET); if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) { exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)", cli_name(cptr), cli_serv(cptr)->last_error_msg); return; } else { fmt = "Read error: %s"; fallback = "EOF from client"; } break; case ET_WRITE: /* socket is writable */ ClrFlag(cptr, FLAG_BLOCKED); if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048) list_next_channels(cptr); Debug((DEBUG_SEND, "Sending queued data to %C", cptr)); send_queued(cptr); break; case ET_READ: /* socket is readable */ if (!IsDead(cptr)) { Debug((DEBUG_DEBUG, "Reading data from %C", cptr)); if (read_packet(cptr, 1) == 0) /* error while reading packet */ fallback = "EOF from client"; } break; default: assert(0 && "Unrecognized socket event in client_sock_callback()"); break; } assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr)); if (fallback) { const char* msg = (cli_error(cptr)) ? strerror(cli_error(cptr)) : fallback; if (!msg) msg = "Unknown error"; exit_client_msg(cptr, cptr, &me, fmt, msg); } } /** Process a timer on client socket. * @param ev Timer event that has a struct Connection as its * associated data. */ static void client_timer_callback(struct Event* ev) { struct Client* cptr; struct Connection* con; assert(0 != ev_timer(ev)); assert(0 != t_data(ev_timer(ev))); assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev)); con = (struct Connection*) t_data(ev_timer(ev)); assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY); cptr = con_client(con); assert(0 == cptr || con == cli_connect(cptr)); if (ev_type(ev)== ET_DESTROY) { con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */ if (!con_freeflag(con) && !cptr) free_connection(con); /* client is being destroyed */ } else { Debug((DEBUG_LIST, "Client process timer for %C expired; processing", cptr)); read_packet(cptr, 0); /* read_packet will re-add timer if needed */ } assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr)); }
/* * ms_invite - server message handler * * parv[0] - sender prefix * parv[1] - user to invite * parv[2] - channel name * parv[3] - (optional) channel timestamp * * - INVITE now is accepted only if who does it is chanop (this of course * implies that channel must exist and he must be on it). * * - On the other side it IS processed even if channel is NOT invite only * leaving room for other enhancements like inviting banned ppl. -- Nemesi * * - Invite with no parameters now lists the channels you are invited to. * - Isomer 23 Oct 99 * * - Invite with too-late timestamp, or with no timestamp from a bursting * server, is silently discarded. - Entrope 19 Jan 05 */ int ms_invite(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; struct Channel *chptr; time_t invite_ts; if (IsServer(sptr)) { /* * this will blow up if we get an invite from a server * we look for channel membership in sptr below. */ return protocol_violation(sptr,"Server attempting to invite"); } if (parc < 3 || EmptyString(parv[2])) { /* * should have been handled upstream, ignore it. */ protocol_violation(sptr,"Too few arguments to invite"); return need_more_params(sptr,"INVITE"); } if (!IsGlobalChannel(parv[2])) { /* * should not be sent */ return protocol_violation(sptr, "Invite to a non-standard channel %s",parv[2]); } if (!(acptr = FindUser(parv[1]))) { send_reply(sptr, ERR_NOSUCHNICK, parv[1]); return 0; } if (!(chptr = FindChannel(parv[2]))) { /* * allow invites to non existent channels, bleah * avoid JOIN, INVITE, PART abuse */ sendcmdto_one(sptr, CMD_INVITE, acptr, "%C :%s", acptr, parv[2]); return 0; } if (parc > 3) { invite_ts = atoi(parv[3]); if (invite_ts > chptr->creationtime) return 0; } else if (IsBurstOrBurstAck(cptr)) return 0; if (!IsChannelService(sptr) && !find_channel_member(sptr, chptr)) { send_reply(sptr, ERR_NOTONCHANNEL, chptr->chname); return 0; } if (find_channel_member(acptr, chptr)) { send_reply(sptr, ERR_USERONCHANNEL, cli_name(acptr), chptr->chname); return 0; } if (is_silenced(sptr, acptr)) return 0; if (MyConnect(acptr)) { add_invite(acptr, chptr); sendcmdto_one(sptr, CMD_INVITE, acptr, "%s %H", cli_name(acptr), chptr); } else { sendcmdto_one(sptr, CMD_INVITE, acptr, "%s %H %Tu", cli_name(acptr), chptr, chptr->creationtime); } if (feature_bool(FEAT_ANNOUNCE_INVITES)) { /* Announce to channel operators. */ sendcmdto_channel_butserv_butone(&his, get_error_numeric(RPL_ISSUEDINVITE)->str, NULL, chptr, sptr, SKIP_NONOPS, "%H %C %C :%C has been invited by %C", chptr, acptr, sptr, acptr, sptr); /* Announce to servers with channel operators. */ sendcmdto_channel_servers_butone(sptr, NULL, TOK_INVITE, chptr, acptr, SKIP_NONOPS, "%s %H %Tu", cli_name(acptr), chptr, chptr->creationtime); } return 0; }
/* Rewritten by Run - 24 sept 94 */ static void exit_one_client(struct Client* bcptr, const char* comment) { struct SLink *lp; struct Ban *bp; if (cli_serv(bcptr) && cli_serv(bcptr)->client_list) /* Was SetServerYXX called ? */ ClearServerYXX(bcptr); /* Removes server from server_list[] */ if (IsUser(bcptr)) { /* * clear out uping requests */ if (IsUPing(bcptr)) uping_cancel(bcptr, 0); /* * Stop a running /LIST clean */ if (MyUser(bcptr) && cli_listing(bcptr)) { MyFree(cli_listing(bcptr)); cli_listing(bcptr) = NULL; } /* * If a person is on a channel, send a QUIT notice * to every client (person) on the same channel (so * that the client can show the "**signoff" message). * (Note: The notice is to the local clients *only*) */ sendcmdto_common_channels_butone(bcptr, CMD_QUIT, NULL, ":%s", comment); remove_user_from_all_channels(bcptr); /* Clean up invitefield */ while ((lp = cli_user(bcptr)->invited)) del_invite(bcptr, lp->value.chptr); /* Clean up silencefield */ while ((bp = cli_user(bcptr)->silence)) { cli_user(bcptr)->silence = bp->next; free_ban(bp); } /* Clean up smarks field */ del_marks(bcptr); /* Clean up watch lists */ if (MyUser(bcptr)) del_list_watch(bcptr); /* Notify Logout */ check_status_watch(bcptr, RPL_LOGOFF); /* Clean up snotice lists */ if (MyUser(bcptr)) set_snomask(bcptr, ~0, SNO_DEL); if (IsInvisible(bcptr)) { assert(UserStats.inv_clients > 0); --UserStats.inv_clients; } if (IsOper(bcptr) && !IsHideOper(bcptr) && !IsChannelService(bcptr) && !IsBot(bcptr)) { assert(UserStats.opers > 0); --UserStats.opers; } if (MyConnect(bcptr)) Count_clientdisconnects(bcptr, UserStats); else Count_remoteclientquits(UserStats, bcptr); } else if (IsServer(bcptr)) { /* Remove downlink list node of uplink */ remove_dlink(&(cli_serv(cli_serv(bcptr)->up))->down, cli_serv(bcptr)->updown); cli_serv(bcptr)->updown = 0; if (MyConnect(bcptr)) Count_serverdisconnects(UserStats); else Count_remoteserverquits(UserStats); } else if (IsMe(bcptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self! */ } else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr)) Count_unknowndisconnects(UserStats); /* * Update IPregistry */ if (IsIPChecked(bcptr)) IPcheck_disconnect(bcptr); /* * Remove from serv->client_list * NOTE: user is *always* NULL if this is a server */ if (cli_user(bcptr)) { assert(!IsServer(bcptr)); /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */ RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr)); } /* Remove bcptr from the client list */ #ifdef DEBUGMODE if (hRemClient(bcptr) != 0) Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p", bcptr, cli_name(bcptr), cli_from(bcptr) ? cli_sockhost(cli_from(bcptr)) : "??host", cli_from(bcptr), cli_next(bcptr), cli_prev(bcptr), cli_fd(bcptr), cli_status(bcptr), cli_user(bcptr))); #else hRemClient(bcptr); #endif remove_client_from_list(bcptr); }