/* * rejoin_doquits: * sends a QUIT to all common channels (to local users only) */ void rejoin_doquits(aClient *sptr) { Membership *tmp; aChannel *chptr; char *comment = "Changing host"; int i = 0; sendto_common_channels(sptr, ":%s!%s@%s QUIT :%s", sptr->name, sptr->user->username, GetHost(sptr), comment); }
/* ** m_svsnick ** parv[0] = sender ** parv[1] = old nickname ** parv[2] = new nickname ** parv[3] = timestamp */ int m_svsnick(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; aClient *ocptr; /* Other client */ if (!IsULine(sptr) || parc < 4 || (strlen(parv[2]) > NICKLEN)) return -1; /* This looks like an error anyway -Studded */ if (hunt_server_token(cptr, sptr, MSG_SVSNICK, TOK_SVSNICK, "%s %s :%s", 1, parc, parv) != HUNTED_ISME) return 0; /* Forwarded, done */ if (do_nick_name(parv[2]) == 0) return 0; if (!(acptr = find_person(parv[1], NULL))) return 0; /* User not found, bail out */ if ((ocptr = find_client(parv[2], NULL)) && ocptr != acptr) /* Collision */ { exit_client(acptr, acptr, sptr, "Nickname collision due to Services enforced " "nickname change, your nick was overruled"); return 0; } if (acptr != ocptr) acptr->umodes &= ~UMODE_REGNICK; acptr->lastnick = TS2ts(parv[3]); sendto_common_channels(acptr, ":%s NICK :%s", parv[1], parv[2]); add_history(acptr, 1); sendto_serv_butone_token(NULL, parv[1], MSG_NICK, TOK_NICK, "%s :%ld", parv[2], TS2ts(parv[3])); (void)del_from_client_hash_table(acptr->name, acptr); hash_check_watch(acptr, RPL_LOGOFF); sendto_snomask(SNO_NICKCHANGE, "*** Notice -- %s (%s@%s) has been forced to change his/her nickname to %s", acptr->name, acptr->user->username, acptr->user->realhost, parv[2]); RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, acptr, parv[2]); strlcpy(acptr->name, parv[2], sizeof acptr->name); add_to_client_hash_table(parv[2], acptr); hash_check_watch(acptr, RPL_LOGON); return 0; }
void exit_one_client_in_split(aClient *cptr, aClient *dead, char *reason) { Link *lp; /* send all the quit reasons to all the non-noquit servers we have */ /* yikes. We only want to do this if dead was OUR server. */ /* erm, no, that's not true. Doing that breaks things. * If a non-noquit server is telling us a server has split, * we will have already recieved hundreds of QUIT messages * from it, which will be passed anyway, and this procedure * will never be called. - lucas */ #ifdef NOQUIT sendto_non_noquit_servs_butone(dead, ":%s QUIT :%s", cptr->name, reason); #endif sendto_common_channels(cptr, ":%s QUIT :%s", cptr->name, reason); while ((lp = cptr->user->channel)) remove_user_from_channel(cptr, lp->value.chptr); while ((lp = cptr->user->invited)) del_invite(cptr, lp->value.chptr); while ((lp = cptr->user->silence)) del_silence(cptr, lp->value.cp); if (cptr->user->alias) cptr->user->alias->client = NULL; clones_remove(cptr); #ifdef RWHO_PROBABILITY probability_remove(cptr); #endif remove_dcc_references(cptr); del_from_client_hash_table(cptr->name, cptr); hash_check_watch(cptr, RPL_LOGOFF); remove_client_from_list(cptr); }
void change_nick(aClient *acptr, char *newnick) { char nick[NICKLEN + 2]; strncpy_irc(nick, newnick, NICKLEN); nick[NICKLEN] = '\0'; ClearIdentified(acptr); /* maybe not needed, but safe -Lamego */ /* send change to common channels */ sendto_common_channels(acptr, ":%s NICK :%s", acptr->name, nick); if (acptr->user) { acptr->tsinfo = CurrentTime; add_history(acptr,1); /* propagate to all servers */ sendto_serv_butone(NULL, ":%s NICK %s :%lu", acptr->name, nick, acptr->tsinfo); } /* ** Finally set new nick name. */ if (acptr->name[0]) { del_from_client_hash_table(acptr->name, acptr); hash_check_watch(acptr, RPL_LOGOFF); } strcpy(acptr->name, nick); add_to_client_hash_table(nick, acptr); hash_check_watch(acptr, RPL_LOGON); /* Lets apply nick change delay (if needed) */ acptr->services_nick_change++; if(acptr->services_nick_change>1) { acptr->number_of_nick_changes = MAX_NICK_CHANGES+1; acptr->last_nick_change = CurrentTime; acptr->services_nick_change = 0; } }
/* ** m_nick ** parv[0] = sender prefix ** parv[1] = nickname ** if from new client -taz ** parv[2] = nick password ** if from server: ** parv[2] = hopcount ** parv[3] = timestamp ** parv[4] = username ** parv[5] = hostname ** parv[6] = servername ** if NICK version 1: ** parv[7] = servicestamp ** parv[8] = info ** if NICK version 2: ** parv[7] = servicestamp ** parv[8] = umodes ** parv[9] = virthost, * if none ** parv[10] = info ** if NICKIP: ** parv[10] = ip ** parv[11] = info */ DLLFUNC CMD_FUNC(m_nick) { aTKline *tklban; int ishold; aClient *acptr, *serv = NULL; aClient *acptrs; char nick[NICKLEN + 2], *s; Membership *mp; time_t lastnick = (time_t) 0; int differ = 1, update_watch = 1; unsigned char newusr = 0, removemoder = 1; /* * If the user didn't specify a nickname, complain */ if (parc < 2) { sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); return 0; } strncpyzt(nick, parv[1], NICKLEN + 1); if (MyConnect(sptr) && sptr->user && !IsAnOper(sptr)) { if ((sptr->user->flood.nick_c >= NICK_COUNT) && (TStime() - sptr->user->flood.nick_t < NICK_PERIOD)) { /* Throttle... */ sendto_one(sptr, err_str(ERR_NCHANGETOOFAST), me.name, sptr->name, nick, (int)(NICK_PERIOD - (TStime() - sptr->user->flood.nick_t))); return 0; } } /* For a local clients, do proper nickname checking via do_nick_name() * and reject the nick if it returns false. * For remote clients, do a quick check by using do_remote_nick_name(), * if this returned false then reject and kill it. -- Syzop */ if ((IsServer(cptr) && !do_remote_nick_name(nick)) || (!IsServer(cptr) && !do_nick_name(nick))) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, parv[0], parv[1], "Illegal characters"); if (IsServer(cptr)) { ircstp->is_kill++; sendto_failops("Bad Nick: %s From: %s %s", parv[1], parv[0], get_client_name(cptr, FALSE)); sendto_one(cptr, ":%s KILL %s :%s (%s <- %s[%s])", me.name, parv[1], me.name, parv[1], nick, cptr->name); if (sptr != cptr) { /* bad nick change */ sendto_serv_butone(cptr, ":%s KILL %s :%s (%s <- %s!%s@%s)", me.name, parv[0], me.name, get_client_name(cptr, FALSE), parv[0], sptr->user ? sptr->username : "", sptr->user ? sptr->user->server : cptr->name); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "BadNick"); } } return 0; } /* Kill quarantined opers early... */ if (IsServer(cptr) && (sptr->from->flags & FLAGS_QUARANTINE) && (parc >= 11) && strchr(parv[8], 'o')) { ircstp->is_kill++; /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ sendto_one(cptr, ":%s KILL %s :%s (Quarantined: no global oper privileges allowed)", me.name, parv[1], me.name); sendto_realops("QUARANTINE: Oper %s on server %s killed, due to quarantine", parv[1], sptr->name); /* (nothing to exit_client or to free, since user was never added) */ return 0; } /* ** Protocol 4 doesn't send the server as prefix, so it is possible ** the server doesn't exist (a lagged net.burst), in which case ** we simply need to ignore the NICK. Also when we got that server ** name (again) but from another direction. --Run */ /* ** We should really only deal with this for msgs from servers. ** -- Aeto */ if (IsServer(cptr) && (parc > 7 && (!(serv = (aClient *)find_server_b64_or_real(parv[6])) || serv->from != cptr->from))) { sendto_realops("Cannot find server %s (%s)", parv[6], backupbuf); return 0; } /* ** Check against nick name collisions. ** ** Put this 'if' here so that the nesting goes nicely on the screen :) ** We check against server name list before determining if the nickname ** is present in the nicklist (due to the way the below for loop is ** constructed). -avalon */ /* I managed to f**k this up i guess --stskeeps */ if ((acptr = find_server(nick, NULL))) { if (MyConnect(sptr)) { #ifdef GUEST if (IsUnknown(sptr)) { RunHook4(HOOKTYPE_GUEST, cptr, sptr, parc, parv); return 0; } #endif sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick); return 0; /* NICK message ignored */ } } /* ** Check for a Q-lined nickname. If we find it, and it's our ** client, just reject it. -Lefler ** Allow opers to use Q-lined nicknames. -Russell */ if (!stricmp("ircd", nick)) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, "Reserved for internal IRCd purposes"); return 0; } if (!stricmp("irc", nick)) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, "Reserved for internal IRCd purposes"); return 0; } if (MyClient(sptr)) /* local client changin nick afterwards.. */ { int xx; spamfilter_build_user_string(spamfilter_user, nick, sptr); xx = dospamfilter(sptr, spamfilter_user, SPAMF_USER, NULL, 0, NULL); if (xx < 0) return xx; } if (!IsULine(sptr) && (tklban = find_qline(sptr, nick, &ishold))) { if (IsServer(sptr) && !ishold) /* server introducing new client */ { acptrs = (aClient *)find_server_b64_or_real(sptr->user == NULL ? (char *)parv[6] : (char *)sptr->user-> server); /* (NEW: no unregistered q:line msgs anymore during linking) */ if (!acptrs || (acptrs->serv && acptrs->serv->flags.synced)) sendto_snomask(SNO_QLINE, "Q:lined nick %s from %s on %s", nick, (*sptr->name != 0 && !IsServer(sptr) ? sptr->name : "<unregistered>"), acptrs ? acptrs->name : "unknown server"); } if (IsServer(cptr) && IsPerson(sptr) && !ishold) /* remote user changing nick */ { sendto_snomask(SNO_QLINE, "Q:lined nick %s from %s on %s", nick, sptr->name, sptr->srvptr ? sptr->srvptr->name : "<unknown>"); } if (!IsServer(cptr)) /* local */ { if (ishold) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, tklban->reason); return 0; } if (!IsOper(cptr)) { sptr->since += 4; /* lag them up */ sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, tklban->reason); sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s.", nick, get_client_name(cptr, FALSE)); return 0; /* NICK message ignored */ } } } /* ** acptr already has result from previous find_server() */ if (acptr) { /* ** We have a nickname trying to use the same name as ** a server. Send out a nick collision KILL to remove ** the nickname. As long as only a KILL is sent out, ** there is no danger of the server being disconnected. ** Ultimate way to jupiter a nick ? >;-). -avalon */ sendto_failops("Nick collision on %s(%s <- %s)", sptr->name, acptr->from->name, get_client_name(cptr, FALSE)); ircstp->is_kill++; sendto_one(cptr, ":%s KILL %s :%s (%s <- %s)", me.name, sptr->name, me.name, acptr->from->name, /* NOTE: Cannot use get_client_name ** twice here, it returns static ** string pointer--the other info ** would be lost */ get_client_name(cptr, FALSE)); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "Nick/Server collision"); } if (MyClient(cptr) && !IsOper(cptr)) cptr->since += 3; /* Nick-flood prot. -Donwulff */ if (!(acptr = find_client(nick, NULL))) goto nickkilldone; /* No collisions, all clear... */ /* ** If the older one is "non-person", the new entry is just ** allowed to overwrite it. Just silently drop non-person, ** and proceed with the nick. This should take care of the ** "dormant nick" way of generating collisions... */ /* Moved before Lost User Field to fix some bugs... -- Barubary */ if (IsUnknown(acptr) && MyConnect(acptr)) { /* This may help - copying code below */ if (acptr == cptr) return 0; acptr->flags |= FLAGS_KILLED; exit_client(NULL, acptr, &me, "Overridden"); goto nickkilldone; } /* A sanity check in the user field... */ if (acptr->user == NULL) { /* This is a Bad Thing */ sendto_failops("Lost user field for %s in change from %s", acptr->name, get_client_name(cptr, FALSE)); ircstp->is_kill++; sendto_one(acptr, ":%s KILL %s :%s (Lost user field!)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; /* Here's the previous versions' desynch. If the old one is messed up, trash the old one and accept the new one. Remember - at this point there is a new nick coming in! Handle appropriately. -- Barubary */ exit_client(NULL, acptr, &me, "Lost user field"); goto nickkilldone; } /* ** If acptr == sptr, then we have a client doing a nick ** change between *equivalent* nicknames as far as server ** is concerned (user is changing the case of his/her ** nickname or somesuch) */ if (acptr == sptr) { if (strcmp(acptr->name, nick) != 0) { /* Allows change of case in his/her nick */ removemoder = 0; /* don't set the user -r */ goto nickkilldone; /* -- go and process change */ } else /* ** This is just ':old NICK old' type thing. ** Just forget the whole thing here. There is ** no point forwarding it to anywhere, ** especially since servers prior to this ** version would treat it as nick collision. */ return 0; /* NICK Message ignored */ } /* ** Note: From this point forward it can be assumed that ** acptr != sptr (point to different client structures). */ /* ** Decide, we really have a nick collision and deal with it */ if (!IsServer(cptr)) { /* ** NICK is coming from local client connection. Just ** send error reply and ignore the command. */ #ifdef GUEST if (IsUnknown(sptr)) { RunHook4(HOOKTYPE_GUEST, cptr, sptr, parc, parv); return 0; } #endif sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), /* parv[0] is empty when connecting */ me.name, BadPtr(parv[0]) ? "*" : parv[0], nick); return 0; /* NICK message ignored */ } /* ** NICK was coming from a server connection. ** This means we have a race condition (two users signing on ** at the same time), or two net fragments reconnecting with ** the same nick. ** The latter can happen because two different users connected ** or because one and the same user switched server during a ** net break. ** If we have the old protocol (no TimeStamp and no user@host) ** or if the TimeStamps are equal, we kill both (or only 'new' ** if it was a "NICK new"). Otherwise we kill the youngest ** when user@host differ, or the oldest when they are the same. ** --Run ** */ if (IsServer(sptr)) { /* ** A new NICK being introduced by a neighbouring ** server (e.g. message type "NICK new" received) */ if (parc > 3) { lastnick = TS2ts(parv[3]); if (parc > 5) differ = (mycmp(acptr->user->username, parv[4]) || mycmp(acptr->user->realhost, parv[5])); } sendto_failops("Nick collision on %s (%s %ld <- %s %ld)", acptr->name, acptr->from->name, acptr->lastnick, cptr->name, lastnick); /* ** I'm putting the KILL handling here just to make it easier ** to read, it's hard to follow it the way it used to be. ** Basically, this is what it will do. It will kill both ** users if no timestamp is given, or they are equal. It will ** kill the user on our side if the other server is "correct" ** (user@host differ and their user is older, or user@host are ** the same and their user is younger), otherwise just kill the ** user an reintroduce our correct user. ** The old code just sat there and "hoped" the other server ** would kill their user. Not anymore. ** -- binary */ if (!(parc > 3) || (acptr->lastnick == lastnick)) { ircstp->is_kill++; sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, acptr, &me, "Nick collision with no timestamp/equal timestamps"); return 0; /* We killed both users, now stop the process. */ } if ((differ && (acptr->lastnick > lastnick)) || (!differ && (acptr->lastnick < lastnick)) || acptr->from == cptr) /* we missed a QUIT somewhere ? */ { ircstp->is_kill++; sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, acptr, &me, "Nick collision"); goto nickkilldone; /* OK, we got rid of the "wrong" user, ** now we're going to add the user the ** other server introduced. */ } if ((differ && (acptr->lastnick < lastnick)) || (!differ && (acptr->lastnick > lastnick))) { /* * Introduce our "correct" user to the other server */ sendto_one(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, parv[1], me.name); send_umode(NULL, acptr, 0, SEND_UMODES, buf); sendto_one_nickcmd(cptr, acptr, buf); if (acptr->user->away) sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away); send_user_joins(cptr, acptr); return 0; /* Ignore the NICK */ } return 0; } else { /* ** A NICK change has collided (e.g. message type ":old NICK new"). */ if (parc > 2) lastnick = TS2ts(parv[2]); differ = (mycmp(acptr->user->username, sptr->user->username) || mycmp(acptr->user->realhost, sptr->user->realhost)); sendto_failops ("Nick change collision from %s to %s (%s %ld <- %s %ld)", sptr->name, acptr->name, acptr->from->name, acptr->lastnick, sptr->from->name, lastnick); if (!(parc > 2) || lastnick == acptr->lastnick) { ircstp->is_kill += 2; sendto_serv_butone(NULL, /* First kill the new nick. */ ":%s KILL %s :%s (Self Collision)", me.name, acptr->name, me.name); sendto_serv_butone(cptr, /* Tell my servers to kill the old */ ":%s KILL %s :%s (Self Collision)", me.name, sptr->name, me.name); sptr->flags |= FLAGS_KILLED; acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, sptr, &me, "Self Collision"); (void)exit_client(NULL, acptr, &me, "Self Collision"); return 0; /* Now that I killed them both, ignore the NICK */ } if ((differ && (acptr->lastnick > lastnick)) || (!differ && (acptr->lastnick < lastnick))) { /* sptr (their user) won, let's kill acptr (our user) */ ircstp->is_kill++; sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick collision: %s <- %s)", me.name, acptr->name, me.name, acptr->from->name, sptr->from->name); acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, acptr, &me, "Nick collision"); goto nickkilldone; /* their user won, introduce new nick */ } if ((differ && (acptr->lastnick < lastnick)) || (!differ && (acptr->lastnick > lastnick))) { /* acptr (our user) won, let's kill sptr (their user), ** and reintroduce our "correct" user */ ircstp->is_kill++; /* Kill the user trying to change their nick. */ sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick collision: %s <- %s)", me.name, sptr->name, me.name, sptr->from->name, acptr->from->name); sptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, sptr, &me, "Nick collision"); /* * Introduce our "correct" user to the other server */ /* Kill their user. */ sendto_one(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, parv[1], me.name); send_umode(NULL, acptr, 0, SEND_UMODES, buf); sendto_one_nickcmd(cptr, acptr, buf); if (acptr->user->away) sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away); send_user_joins(cptr, acptr); return 0; /* their user lost, ignore the NICK */ } } return 0; /* just in case */ nickkilldone: if (IsServer(sptr)) { /* A server introducing a new client, change source */ sptr = make_client(cptr, serv); add_client_to_list(sptr); if (parc > 2) sptr->hopcount = TS2ts(parv[2]); if (parc > 3) sptr->lastnick = TS2ts(parv[3]); else /* Little bit better, as long as not all upgraded */ sptr->lastnick = TStime(); if (sptr->lastnick < 0) { sendto_realops ("Negative timestamp recieved from %s, resetting to TStime (%s)", cptr->name, backupbuf); sptr->lastnick = TStime(); } newusr = 1; } else if (sptr->name[0] && IsPerson(sptr)) { /* ** If the client belongs to me, then check to see ** if client is currently on any channels where it ** is currently banned. If so, do not allow the nick ** change to occur. ** Also set 'lastnick' to current time, if changed. */ if (MyClient(sptr)) { for (mp = sptr->user->channel; mp; mp = mp->next) { if (!is_skochanop(sptr, mp->chptr) && is_banned(sptr, mp->chptr, BANCHK_NICK)) { sendto_one(sptr, err_str(ERR_BANNICKCHANGE), me.name, parv[0], mp->chptr->chname); return 0; } if (CHECK_TARGET_NICK_BANS && !is_skochanop(sptr, mp->chptr) && is_banned_with_nick(sptr, mp->chptr, BANCHK_NICK, nick)) { sendto_one(sptr, ":%s 437 %s %s :Cannot change to a nickname banned on channel", me.name, parv[0], mp->chptr->chname); return 0; } if (!IsOper(sptr) && !IsULine(sptr) && mp->chptr->mode.mode & MODE_NONICKCHANGE && !is_chanownprotop(sptr, mp->chptr)) { sendto_one(sptr, err_str(ERR_NONICKCHANGE), me.name, parv[0], mp->chptr->chname); return 0; } } if (TStime() - sptr->user->flood.nick_t >= NICK_PERIOD) { sptr->user->flood.nick_t = TStime(); sptr->user->flood.nick_c = 1; } else sptr->user->flood.nick_c++; sendto_snomask(SNO_NICKCHANGE, "*** Notice -- %s (%s@%s) has changed his/her nickname to %s", sptr->name, sptr->user->username, sptr->user->realhost, nick); RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, sptr, nick); } else { if (!IsULine(sptr)) sendto_snomask(SNO_FNICKCHANGE, "*** Notice -- %s (%s@%s) has changed his/her nickname to %s", sptr->name, sptr->user->username, sptr->user->realhost, nick); RunHook3(HOOKTYPE_REMOTE_NICKCHANGE, cptr, sptr, nick); } /* * Client just changing his/her nick. If he/she is * on a channel, send note of change to all clients * on that channel. Propagate notice to other servers. */ if (mycmp(parv[0], nick) || /* Next line can be removed when all upgraded --Run */ (!MyClient(sptr) && parc > 2 && TS2ts(parv[2]) < sptr->lastnick)) sptr->lastnick = (MyClient(sptr) || parc < 3) ? TStime() : TS2ts(parv[2]); if (sptr->lastnick < 0) { sendto_realops("Negative timestamp (%s)", backupbuf); sptr->lastnick = TStime(); } add_history(sptr, 1); sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick); sendto_serv_butone_token(cptr, parv[0], MSG_NICK, TOK_NICK, "%s %ld", nick, sptr->lastnick); if (removemoder) sptr->umodes &= ~UMODE_REGNICK; } else if (!sptr->name[0]) { #ifdef NOSPOOF /* * Client setting NICK the first time. * * Generate a random string for them to pong with. */ sptr->nospoof = getrandom32(); if (PINGPONG_WARNING) sendto_one(sptr, ":%s NOTICE %s :*** If you are having problems" " connecting due to ping timeouts, please" " type /quote pong %X or /raw pong %X now.", me.name, nick, sptr->nospoof, sptr->nospoof); sendto_one(sptr, "PING :%X", sptr->nospoof); #endif /* NOSPOOF */ #ifdef CONTACT_EMAIL sendto_one(sptr, ":%s NOTICE %s :*** If you need assistance with a" " connection problem, please email " CONTACT_EMAIL " with the name and version of the client you are" " using, and the server you tried to connect to: %s", me.name, nick, me.name); #endif /* CONTACT_EMAIL */ #ifdef CONTACT_URL sendto_one(sptr, ":%s NOTICE %s :*** If you need assistance with" " connecting to this server, %s, please refer to: " CONTACT_URL, me.name, nick, me.name); #endif /* CONTACT_URL */ /* Copy password to the passwd field if it's given after NICK * - originally by taz, modified by Wizzu */ if ((parc > 2) && (strlen(parv[2]) <= PASSWDLEN) && !(sptr->listener->umodes & LISTENER_JAVACLIENT)) { if (sptr->passwd) MyFree(sptr->passwd); sptr->passwd = MyMalloc(strlen(parv[2]) + 1); (void)strcpy(sptr->passwd, parv[2]); } /* This had to be copied here to avoid problems.. */ (void)strcpy(sptr->name, nick); if (sptr->user && IsNotSpoof(sptr)) { /* ** USER already received, now we have NICK. ** *NOTE* For servers "NICK" *must* precede the ** user message (giving USER before NICK is possible ** only for local client connection!). register_user ** may reject the client and call exit_client for it ** --must test this and exit m_nick too!!! */ #ifndef NOSPOOF if (USE_BAN_VERSION && MyConnect(sptr)) sendto_one(sptr, ":IRC!IRC@%s PRIVMSG %s :\1VERSION\1", me.name, nick); #endif sptr->lastnick = TStime(); /* Always local client */ if (register_user(cptr, sptr, nick, sptr->user->username, NULL, NULL, NULL) == FLUSH_BUFFER) return FLUSH_BUFFER; strcpy(nick, sptr->name); /* don't ask, but I need this. do not remove! -- Syzop */ update_watch = 0; newusr = 1; } } /* * Finally set new nick name. */ if (update_watch && sptr->name[0]) { (void)del_from_client_hash_table(sptr->name, sptr); if (IsPerson(sptr)) hash_check_watch(sptr, RPL_LOGOFF); } (void)strcpy(sptr->name, nick); (void)add_to_client_hash_table(nick, sptr); if (IsServer(cptr) && parc > 7) { parv[3] = nick; do_cmd(cptr, sptr, "USER", parc - 3, &parv[3]); if (GotNetInfo(cptr) && !IsULine(sptr)) sendto_fconnectnotice(sptr->name, sptr->user, sptr, 0, NULL); } else if (IsPerson(sptr) && update_watch) hash_check_watch(sptr, RPL_LOGON); #ifdef NEWCHFLOODPROT if (sptr->user && !newusr && !IsULine(sptr)) { for (mp = sptr->user->channel; mp; mp = mp->next) { aChannel *chptr = mp->chptr; if (chptr && !(mp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANPROT)) && chptr->mode.floodprot && do_chanflood(chptr->mode.floodprot, FLD_NICK) && MyClient(sptr)) { do_chanflood_action(chptr, FLD_NICK, "nick"); } } } #endif if (newusr && !MyClient(sptr) && IsPerson(sptr)) { RunHook(HOOKTYPE_REMOTE_CONNECT, sptr); } return 0; }
/* * m_nick * parv[0] = sender prefix * parv[1] = nickname * parv[2] = hopcount when new user; TS when nick change * parv[3] = TS * ---- new user only below ---- * parv[4] = umode * parv[5] = username * parv[6] = hostname * parv[7] = server * parv[8] = serviceid * parv[9] = IP * parv[10] = ircname * -- endif */ int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[]) { struct simBan *ban; aClient *acptr, *uplink; Link *lp, *lp2; char nick[NICKLEN + 2]; ts_val newts = 0; int sameuser = 0, samenick = 0; if (parc < 2) { sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); return 0; } if (!IsServer(sptr) && IsServer(cptr) && parc > 2) newts = atol(parv[2]); else if (IsServer(sptr) && parc > 3) newts = atol(parv[3]); else parc = 2; /* * parc == 2 on a normal client sign on (local) and a normal client * nick change * parc == 4 on a normal server-to-server client nick change * parc == 11 on a normal TS style server-to-server NICK introduction */ if ((IsServer(sptr) || (parc > 4)) && (parc < 11)) { /* * We got the wrong number of params. Someone is trying to trick * us. Kill it. -ThemBones As discussed with ThemBones, not much * point to this code now sending a whack of global kills would * also be more annoying then its worth, just note the problem, * and continue -Dianora */ sendto_realops("IGNORING BAD NICK: %s[%s@%s] on %s (from %s)", parv[1], (parc >= 6) ? parv[5] : "-", (parc >= 7) ? parv[6] : "-", (parc >= 8) ? parv[7] : "-", parv[0]); return 0; } strncpyzt(nick, parv[1], NICKLEN + 1); /* * if do_nick_name() returns a null name OR if the server sent a * nick name and do_nick_name() changed it in some way (due to rules * of nick creation) then reject it. If from a server and we reject * it, and KILL it. -avalon 4/4/92 */ if (do_nick_name(nick) == 0 || (IsServer(cptr) && strcmp(nick, parv[1]))) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, parv[0], parv[1], "Erroneous Nickname"); if (IsServer(cptr)) { ircstp->is_kill++; sendto_realops_lev(DEBUG_LEV, "Bad Nick: %s From: %s Via: %s", parv[1], parv[0], get_client_name(cptr, HIDEME)); sendto_one(cptr, ":%s KILL %s :%s (Bad Nick)", me.name, parv[1], me.name); if (sptr != cptr) { /* bad nick change */ sendto_serv_butone(cptr, ":%s KILL %s :%s (Bad Nick)", me.name, parv[0], me.name); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "BadNick"); } } return 0; } /* * Check against nick name collisions. * * Put this 'if' here so that the nesting goes nicely on the screen * :) We check against server name list before determining if the * nickname is present in the nicklist (due to the way the below * for loop is constructed). -avalon */ do { if ((acptr = find_server(nick, NULL))) if (MyConnect(sptr)) { sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick); return 0; } /* * acptr already has result from find_server * Well. unless we have a capricious server on the net, a nick can * never be the same as a server name - Dianora * That's not the only case; maybe someone broke do_nick_name * or changed it so they could use "." in nicks on their network * - sedition */ if (acptr) { /* * We have a nickname trying to use the same name as a * server. Send out a nick collision KILL to remove the * nickname. As long as only a KILL is sent out, there is no * danger of the server being disconnected. Ultimate way to * jupiter a nick ? >;-). -avalon */ sendto_realops_lev(SKILL_LEV, "Nick collision on %s", sptr->name); ircstp->is_kill++; sendto_one(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, sptr->name, me.name); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "Nick/Server collision"); } if (!(acptr = find_client(nick, NULL))) break; /* * If acptr == sptr, then we have a client doing a nick change * between *equivalent* nicknames as far as server is concerned * (user is changing the case of his/her nickname or somesuch) */ if (acptr == sptr) { if (strcmp(acptr->name, nick) == 0) return 0; else break; } /* If user is changing nick to itself no point in propogating */ /* * Note: From this point forward it can be assumed that acptr != * sptr (point to different client structures). * * If the older one is "non-person", the new entry is just * allowed to overwrite it. Just silently drop non-person, and * proceed with the nick. This should take care of the "dormant * nick" way of generating collisions... */ if (IsUnknown(acptr)) { if (MyConnect(acptr)) { exit_client(NULL, acptr, &me, "Overridden"); break; } else if (!(acptr->user)) { sendto_realops_lev(SKILL_LEV, "Nick Collision on %s", parv[1]); sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; /* Having no USER struct should be ok... */ return exit_client(cptr, acptr, &me, "Got TS NICK before Non-TS USER"); } } if (!IsServer(cptr)) { /* * NICK is coming from local client connection. Just send * error reply and ignore the command. * parv[0] is empty on connecting clients */ sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick); return 0; } /* * NICK was coming from a server connection. Means that the same * nick is registered for different users by different server. * This is either a race condition (two users coming online about * same time, or net reconnecting) or just two net fragments * becoming joined and having same nicks in use. We cannot have * TWO users with same nick--purge this NICK from the system with * a KILL... >;) * * Changed to something reasonable like IsServer(sptr) (true if * "NICK new", false if ":old NICK new") -orabidoo */ if (IsServer(sptr)) { /* * A new NICK being introduced by a neighbouring server (e.g. * message type "NICK new" received) */ if (!newts || !acptr->tsinfo || (newts == acptr->tsinfo)) { sendto_realops_lev(SKILL_LEV, "Nick collision on %s", parv[1]); ircstp->is_kill++; sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, acptr->name); sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; return exit_client(cptr, acptr, &me, "Nick collision"); } else { /* XXX This looks messed up to me XXX - Raist */ sameuser = (acptr->user) && mycmp(acptr->user->username, parv[5]) == 0 && mycmp(acptr->user->host, parv[6]) == 0; if ((sameuser && newts < acptr->tsinfo) || (!sameuser && newts > acptr->tsinfo)) { return 0; } else { sendto_realops_lev(SKILL_LEV, "Nick collision on %s",parv[1]); ircstp->is_kill++; sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, acptr->name); sendto_serv_butone(sptr, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void) exit_client(cptr, acptr, &me, "Nick collision"); break; } } } /* * * A NICK change has collided (e.g. message type * ":old NICK * new". This requires more complex cleanout. * Both clients must be * purged from this server, the "new" * must be killed from the * incoming connection, and "old" must * be purged from all outgoing * connections. */ if (!newts || !acptr->tsinfo || (newts == acptr->tsinfo) || !sptr->user) { sendto_realops_lev(SKILL_LEV, "Nick change collision: %s", parv[1]); ircstp->is_kill++; sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, acptr->name); sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)",me.name, sptr->name, me.name); ircstp->is_kill++; sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)",me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void) exit_client(NULL, acptr, &me, "Nick collision(new)"); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "Nick collision(old)"); } else { /* XXX This looks messed up XXX */ sameuser = mycmp(acptr->user->username, sptr->user->username) == 0 && mycmp(acptr->user->host, sptr->user->host) == 0; if ((sameuser && newts < acptr->tsinfo) || (!sameuser && newts > acptr->tsinfo)) { if (sameuser) sendto_realops_lev(SKILL_LEV, "Nick change collision from %s to %s", sptr->name, acptr->name); ircstp->is_kill++; sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, sptr->name, me.name); sptr->flags |= FLAGS_KILLED; if (sameuser) return exit_client(cptr, sptr, &me, "Nick collision(old)"); else return exit_client(cptr, sptr, &me, "Nick collision(new)"); } else { sendto_realops_lev(SKILL_LEV, "Nick collision on %s", acptr->name); ircstp->is_kill++; sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, acptr->name); sendto_serv_butone(sptr, ":%s KILL %s :%s (Nick Collision)",me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void) exit_client(cptr, acptr, &me, "Nick collision"); } } } while (0); if (IsServer(sptr)) { uplink = find_server(parv[7], NULL); if(!uplink) { /* if we can't find the server this nick is on, * complain loudly and ignore it. - lucas */ sendto_realops("Remote nick %s on UNKNOWN server %s", nick, parv[7]); return 0; } sptr = make_client(cptr, uplink); /* If this is on a U: lined server, it's a U: lined client. */ if(IsULine(uplink)) sptr->flags|=FLAGS_ULINE; add_client_to_list(sptr); if (parc > 2) sptr->hopcount = atoi(parv[2]); if (newts) { sptr->tsinfo = newts; } else { newts = sptr->tsinfo = (ts_val) timeofday; ts_warn("Remote nick %s introduced without a TS", nick); } /* copy the nick in place */ (void) strcpy(sptr->name, nick); (void) add_to_client_hash_table(nick, sptr); if (parc >= 10) { int *s, flag; char *m; /* parse the usermodes -orabidoo */ m = &parv[4][1]; while (*m) { for (s = user_modes; (flag = *s); s += 2) if (*m == *(s + 1)) { if ((flag == UMODE_o) || (flag == UMODE_O)) Count.oper++; sptr->umode |= flag & SEND_UMODES; break; } m++; } if (parc==10) { return do_user(nick, cptr, sptr, parv[5], parv[6], parv[7], strtoul(parv[8], NULL, 0), "0.0.0.0", parv[9]); } else if (parc==11) { return do_user(nick, cptr, sptr, parv[5], parv[6], parv[7], strtoul(parv[8], NULL, 0), parv[9], parv[10]); } } } else if (sptr->name[0]) { #ifdef DONT_CHECK_QLINE_REMOTE if (MyConnect(sptr)) { #endif if ((ban = check_mask_simbanned(nick, SBAN_NICK))) { #ifndef DONT_CHECK_QLINE_REMOTE if (!MyConnect(sptr)) sendto_realops("Restricted nick %s from %s on %s", nick, (*sptr->name != 0 && !IsServer(sptr)) ? sptr->name : "<unregistered>", (sptr->user == NULL) ? ((IsServer(sptr)) ? parv[6] : me.name) : sptr->user->server); #endif if (MyConnect(sptr) && (!IsServer(cptr)) && (!IsOper(cptr)) && (!IsULine(sptr))) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, BadPtr(ban->reason) ? "Erroneous Nickname" : ban->reason); if (call_hooks(CHOOK_FORBID, cptr, nick, ban) != FLUSH_BUFFER) sendto_realops_lev(REJ_LEV, "Forbidding restricted nick %s from %s", nick, get_client_name(cptr, FALSE)); return 0; } } #ifdef DONT_CHECK_QLINE_REMOTE } #endif if (MyConnect(sptr)) { if (IsRegisteredUser(sptr)) { /* before we change their nick, make sure they're not banned * on any channels, and!! make sure they're not changing to * a banned nick -sed */ /* a little cleaner - lucas */ for (lp = sptr->user->channel; lp; lp = lp->next) { if (can_send(sptr, lp->value.chptr, NULL)) { sendto_one(sptr, err_str(ERR_BANNICKCHANGE), me.name, sptr->name, lp->value.chptr->chname); return 0; } if (nick_is_banned(lp->value.chptr, nick, sptr) != NULL) { sendto_one(sptr, err_str(ERR_BANONCHAN), me.name, sptr->name, nick, lp->value.chptr->chname); return 0; } } #ifdef ANTI_NICK_FLOOD if ((sptr->last_nick_change + MAX_NICK_TIME) < NOW) sptr->number_of_nick_changes = 0; sptr->last_nick_change = NOW; sptr->number_of_nick_changes++; if (sptr->number_of_nick_changes > MAX_NICK_CHANGES && !IsAnOper(sptr)) { sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Too many nick " "changes. Wait %d seconds before trying again.", me.name, sptr->name, MAX_NICK_TIME); return 0; } #endif /* If it changed nicks, -r it */ if ((sptr->umode & UMODE_r) && (mycmp(parv[0], nick) != 0)) { unsigned int oldumode; char mbuf[BUFSIZE]; oldumode = sptr->umode; sptr->umode &= ~UMODE_r; send_umode(sptr, sptr, oldumode, ALL_UMODES, mbuf); } /* LOCAL NICKHANGE */ /* * Client just changing his/her nick. If he/she is on a * channel, send note of change to all clients on that channel. * Propagate notice to other servers. */ /* if the nickname is different, set the TS */ if (mycmp(parv[0], nick)) { sptr->tsinfo = newts ? newts : (ts_val) timeofday; } sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick); if (sptr->user) { add_history(sptr, 1); sendto_serv_butone(cptr, ":%s NICK %s :%ld", parv[0], nick, sptr->tsinfo); } } } else { /* REMOTE NICKCHANGE */ /* * Client just changing his/her nick. If he/she is on a * channel, send note of change to all clients on that channel. * Propagate notice to other servers. */ /* if the nickname is different, set the TS */ if (mycmp(parv[0], nick)) { sptr->tsinfo = newts ? newts : (ts_val) timeofday; } sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick); if (sptr->user) { add_history(sptr, 1); sendto_serv_butone(cptr, ":%s NICK %s :%ld", parv[0], nick, sptr->tsinfo); } /* If it changed nicks, -r it */ if (mycmp(parv[0], nick)) sptr->umode &= ~UMODE_r; /* * Flush the banserial for the channels the user is in, since this * could be a SVSNICK induced nick change, which overrides any ban * checking on the originating server. */ flush_user_banserial(sptr); } /* Remove dccallow entries for users who don't share common channel(s) unless they only change their nick capitalization -Kobi_S */ if(sptr->user && mycmp(parv[0], nick)) { for(lp = sptr->user->dccallow; lp; lp = lp2) { lp2 = lp->next; if(lp->flags == DCC_LINK_ME) continue; if(!find_shared_chan(sptr, lp->value.cptr)) { sendto_one(lp->value.cptr, ":%s %d %s :%s has been removed from " "your DCC allow list for signing off", me.name, RPL_DCCINFO, lp->value.cptr->name, parv[0]); del_dccallow(lp->value.cptr, sptr, 1); } } } } else { /* Client setting NICK the first time */ if (MyConnect(sptr)) { if ((ban = check_mask_simbanned(nick, SBAN_NICK))) { if (MyConnect(sptr) && (!IsServer(cptr)) && (!IsOper(cptr)) && (!IsULine(sptr))) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, BadPtr(ban->reason) ? "Erroneous Nickname" : ban->reason); if (call_hooks(CHOOK_FORBID, cptr, nick, ban) != FLUSH_BUFFER) sendto_realops_lev(REJ_LEV, "Forbidding restricted nick %s from %s", nick, get_client_name(cptr, FALSE)); return 0; } } } strcpy(sptr->name, nick); sptr->tsinfo = timeofday; if (sptr->user) { /* USER already received, now we have NICK */ if (register_user(cptr, sptr, nick, sptr->user->username, NULL) == FLUSH_BUFFER) return FLUSH_BUFFER; } } /* Finally set new nick name. */ if (sptr->name[0]) { del_from_client_hash_table(sptr->name, sptr); samenick = mycmp(sptr->name, nick) ? 0 : 1; if (IsPerson(sptr)) { if (!samenick) hash_check_watch(sptr, RPL_LOGOFF); #ifdef RWHO_PROBABILITY probability_change(sptr->name, nick); #endif } } strcpy(sptr->name, nick); add_to_client_hash_table(nick, sptr); if (IsPerson(sptr) && !samenick) hash_check_watch(sptr, RPL_LOGON); return 0; }
/* ** Exit one client, local or remote. Assuming all dependants have ** been already removed, and socket closed for local client. */ static void exit_one_client(aClient *cptr, aClient *sptr, aClient *from, const char *comment) { Reg aClient *acptr; Reg int i; Reg Link *lp; invLink *ilp; /* ** For a server or user quitting, propagage the information to ** other servers (except to the one where is came from (cptr)) */ if (IsMe(sptr)) { sendto_flag(SCH_ERROR, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(sptr)) { /* ** Old sendto_serv_but_one() call removed because we now ** need to send different names to different servers ** (domain name matching) */ if (!IsMasked(sptr)) { istat.is_serv--; } if (!IsBursting(sptr)) { istat.is_eobservers--; } for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (!(sptr->flags & FLAGS_SQUIT)) { /* Make sure we only send the last SQUIT ** to a 2.11. */ continue; } if ((acptr->flags & FLAGS_HIDDEN) && !IsMasked(sptr)) { /* We need a special SQUIT reason, so ** the remote server can send the ** right quit message. */ sendto_one(acptr, ":%s SQUIT %s :%s %s", sptr->serv->up->serv->sid, sptr->serv->sid, sptr->serv->up->name, sptr->name); } else { sendto_one(acptr, ":%s SQUIT %s :%s", sptr->serv->up->serv->sid, sptr->serv->sid, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SQUIT, sptr->serv, sptr, ":%s SQUIT %s :%s", from->name, sptr->name, comment); #endif del_from_sid_hash_table(sptr->serv); remove_server_from_tree(sptr); /* remove server from svrtop */ unregister_server(sptr); } else if (!IsPerson(sptr) && !IsService(sptr)) { /* ...this test is *dubious*, would need ** some thougth.. but for now it plugs a ** nasty hole in the server... --msa */ ; /* Nothing */ } else if (sptr->name[0] && !IsService(sptr)) /* clean with QUIT... */ { /* ** If this exit is generated from "m_kill", then there ** is no sense in sending the QUIT--KILL's have been ** sent instead. */ if ((sptr->flags & FLAGS_KILLED) == 0) { if ((sptr->flags & FLAGS_SPLIT) == 0) { sendto_serv_butone(cptr, ":%s QUIT :%s", sptr->user->uid, comment); #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT| SERVICE_WANT_RQUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } else { if (sptr->flags & FLAGS_HIDDEN) /* joys of hostmasking */ for (i = fdas.highest; i >= 0; i--) { if (!(acptr =local[fdas.fd[i]]) || acptr == cptr || IsMe(acptr)) continue; if (acptr->flags & FLAGS_HIDDEN) sendto_one(acptr, ":%s QUIT :%s", sptr->user->uid, comment); } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } } #ifdef USE_SERVICES else { /* Send QUIT to services which desire such as well. ** Services with both _QUIT and _KILL will get both ** for now --jv */ check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); } #endif /* ** 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*) */ if (sptr->user) { if (IsInvisible(sptr)) { istat.is_user[1]--; sptr->user->servp->usercnt[1]--; } else { istat.is_user[0]--; sptr->user->servp->usercnt[0]--; } if (IsAnOper(sptr)) { sptr->user->servp->usercnt[2]--; istat.is_oper--; } sendto_common_channels(sptr, ":%s QUIT :%s", sptr->name, comment); if (!(acptr = cptr ? cptr : sptr->from)) acptr = sptr; while ((lp = sptr->user->channel)) { /* ** Mark channels from where remote chop left, ** this will eventually lock the channel. ** close_connection() has already been called, ** it makes MyConnect == False - krys */ if (sptr != cptr) { if (*lp->value.chptr->chname == '!') { if (!(sptr->flags &FLAGS_QUIT)) lp->value.chptr->history = timeofday + LDELAYCHASETIMELIMIT; } else if ( #ifndef BETTER_CDELAY !(sptr->flags & FLAGS_QUIT) && #endif is_chan_op(sptr, lp->value.chptr)) { lp->value.chptr->history = timeofday + DELAYCHASETIMELIMIT; } } if (IsAnonymous(lp->value.chptr) && !IsQuiet(lp->value.chptr)) { sendto_channel_butserv(lp->value.chptr, sptr, ":%s PART %s :None", sptr->name, lp->value.chptr->chname); } remove_user_from_channel(sptr,lp->value.chptr); } /* Clean up invitefield */ while ((ilp = sptr->user->invited)) { del_invite(sptr, ilp->chptr); /* again, this is all that is needed */ } /* remove from uid hash table */ if (sptr->user) { del_from_uid_hash_table(sptr->user->uid, sptr); } /* Add user to history */ #ifndef BETTER_NDELAY add_history(sptr, (sptr->flags & FLAGS_QUIT) ? &me : NULL); #else add_history(sptr, (sptr == cptr) ? &me : NULL); #endif off_history(sptr); #ifdef USE_HOSTHASH del_from_hostname_hash_table(sptr->user->host, sptr->user); #endif #ifdef USE_IPHASH del_from_ip_hash_table(sptr->user->sip, sptr->user); #endif } } else if (sptr->name[0] && IsService(sptr)) { /* ** If this exit is generated from "m_kill", then there ** is no sense in sending the QUIT--KILL's have been ** sent instead. */ if ((sptr->flags & FLAGS_KILLED) == 0) { /* ** A service quitting is annoying, It has to be sent ** to connected servers depending on ** sptr->service->dist */ for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (match(sptr->service->dist, acptr->name) && match(sptr->service->dist, acptr->serv->sid)) { continue; } sendto_one(acptr, ":%s QUIT :%s", sptr->name, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SERVICE, NULL, NULL, ":%s QUIT :%s", sptr->name, comment); #endif /* MyConnect(sptr) is always FALSE here */ if (cptr == sptr) { sendto_flag(SCH_NOTICE, "Service %s disconnected", get_client_name(sptr, TRUE)); } sendto_flag(SCH_SERVICE, "Received QUIT %s from %s (%s)", sptr->name, from->name, comment); istat.is_service--; } /* Remove sptr from the client list */ if (del_from_client_hash_table(sptr->name, sptr) != 1) { Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x", sptr, sptr->name, sptr->from ? sptr->from->sockhost : "??host", sptr->from, sptr->next, sptr->prev, sptr->fd, sptr->status, sptr->user)); } remove_client_from_list(sptr); return; }