/* 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 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)) { 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); }
/* * 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, char *comment) { Link *lp; /* * For a server or user quitting, propogate the information to * other servers (except to the one where is came from (cptr)) */ if (IsMe(sptr)) { sendto_ops("ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(sptr)) { #ifdef ALWAYS_SEND_DURING_SPLIT currently_processing_netsplit = YES; #endif exit_server(cptr, sptr, from, comment); #ifdef ALWAYS_SEND_DURING_SPLIT currently_processing_netsplit = NO; #endif return; } else if (!(IsPerson(sptr))) /* * ...this test is *dubious*, would need * some thought.. but for * now it plugs a * nasty hole in the server... --msa */ ; /* Nothing */ else if (sptr->name[0]) { /* ...just clean all others 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) { sendto_serv_butone(cptr, ":%s QUIT :%s", sptr->name, comment); } /* * * 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) { send_part_to_common_channels(sptr, comment); send_quit_to_common_channels(sptr, comment); while ((lp = sptr->user->channel)) remove_user_from_channel(sptr, lp->value.chptr); clones_remove(sptr); #ifdef RWHO_PROBABILITY probability_remove(sptr); #endif /* Clean up invitefield */ while ((lp = sptr->user->invited)) del_invite(sptr, lp->value.chptr); /* Clean up silences */ while ((lp = sptr->user->silence)) del_silence(sptr, lp->value.cp); remove_dcc_references(sptr); /* again, this is all that is needed */ } } /* 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 user from watchlists */ if(IsRegistered(sptr)) hash_check_watch(sptr, RPL_LOGOFF); remove_client_from_list(sptr); return; }
/* ** Exit one client, local or remote. Assuming all dependents have ** been already removed, and socket closed for local client. */ static void exit_one_client(struct Client *client_p, struct Client *source_p, struct Client *from, const char *comment) { struct Client* target_p; dlink_node *lp; dlink_node *next_lp; if (IsServer(source_p)) { if (source_p->servptr && source_p->servptr->serv) del_client_from_llist(&(source_p->servptr->serv->servers), source_p); else ts_warn("server %s without servptr!", source_p->name); if(!IsMe(source_p)) remove_server_from_list(source_p); } else if (source_p->servptr && source_p->servptr->serv) { del_client_from_llist(&(source_p->servptr->serv->users), source_p); } /* there are clients w/o a servptr: unregistered ones */ /* ** For a server or user quitting, propogate the information to ** other servers (except to the one where is came from (client_p)) */ if (IsMe(source_p)) { sendto_realops_flags(FLAGS_ALL, L_ALL, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(source_p)) { /* ** Old sendto_serv_but_one() call removed because we now ** need to send different names to different servers ** (domain name matching) */ /* ** The bulk of this is done in remove_dependents now, all ** we have left to do is send the SQUIT upstream. -orabidoo */ if (source_p->localClient) { if(source_p->localClient->ctrlfd > -1) { fd_close(source_p->localClient->ctrlfd); source_p->localClient->ctrlfd = -1; #ifndef HAVE_SOCKETPAIR fd_close(source_p->localClient->ctrlfd_r); fd_close(source_p->localClient->fd_r); source_p->localClient->ctrlfd_r = -1; source_p->localClient->fd_r = -1; #endif } } target_p = source_p->from; if (target_p && IsServer(target_p) && target_p != client_p && !IsMe(target_p) && (source_p->flags & FLAGS_KILLED) == 0) sendto_one(target_p, ":%s SQUIT %s :%s", from->name, source_p->name, comment); } else if (source_p->name[0]) /* ...just clean all others 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 ((source_p->flags & FLAGS_KILLED) == 0) { sendto_server(client_p, source_p, NULL, NOCAPS, NOCAPS, NOFLAGS, ":%s QUIT :%s", source_p->name, comment); } /* ** 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 (source_p->user) { sendto_common_channels_local(source_p, ":%s!%s@%s QUIT :%s", source_p->name, source_p->username, source_p->host, comment); for (lp = source_p->user->channel.head; lp; lp = next_lp) { next_lp = lp->next; remove_user_from_channel(lp->data, source_p); } /* Should not be in any channels now */ assert(source_p->user->channel.head == NULL); /* Clean up invitefield */ for (lp = source_p->user->invited.head; lp; lp = next_lp) { next_lp = lp->next; del_invite(lp->data, source_p); } /* Clean up allow lists */ del_all_accepts(source_p); add_history(source_p, 0); off_history(source_p); if (HasID(source_p)) del_from_id_hash_table(source_p->user->id, source_p); /* again, this is all that is needed */ } } /* * Remove source_p from the client lists */ del_from_client_hash_table(source_p->name, source_p); /* remove from global client list */ remove_client_from_list(source_p); /* Check to see if the client isn't already on the dead list */ assert(dlinkFind(&dead_list, source_p) == NULL); /* add to dead client dlist */ lp = make_dlink_node(); SetDead(source_p); dlinkAdd(source_p, lp, &dead_list); }
void exit_one_server(aClient *cptr, aClient *dead, aClient *from, aClient *lcptr, char *spinfo, char *comment) { aClient *acptr, *next; DLink *lp; /* okay, this is annoying. * first off, we need two loops. * one: to remove all the clients. * two: to remove all the servers. * HOWEVER! removing a server may cause removal of more servers * and more clients. * and this may make our pointer to next bad. therefore, we have to restart * the server loop each time we find a server. * We _NEED_ two different loops: all clients must be removed " * before the server is * removed. Otherwise, bad things (tm) can happen. */ Debug((DEBUG_NOTICE, "server noquit: %s", cptr->name)); for (acptr = client; acptr; acptr = next) { next = acptr->next; /* we might destroy this client record * in the loop. */ if(acptr->uplink != cptr || !IsPerson(acptr)) continue; exit_one_client_in_split(acptr, dead, spinfo); } for (acptr = client; acptr; acptr = next) { next = acptr->next; /* we might destroy this client record in * the loop. */ if(acptr->uplink != cptr || !IsServer(acptr)) continue; exit_one_server(acptr, dead, from, lcptr, spinfo, comment); next = client; /* restart the loop */ } Debug((DEBUG_NOTICE, "done exiting server: %s", cptr->name)); for (lp = server_list; lp; lp = lp->next) { acptr = lp->value.cptr; if (acptr == cptr || IsMe(acptr) || acptr == dead || acptr == lcptr) continue; /* if the server is noquit, we only want to send it * information about 'dead' * if it's not, this server gets split information for ALL * dead servers. */ #ifdef NOQUIT if(IsNoquit(acptr)) #endif if(cptr != dead) continue; if (cptr->from == acptr) /* "upstream" squit */ sendto_one(acptr, ":%s SQUIT %s :%s", from->name, cptr->name, comment); else sendto_one(acptr, "SQUIT %s :%s", cptr->name, comment); } del_from_client_hash_table(cptr->name, cptr); hash_check_watch(cptr, RPL_LOGOFF); remove_client_from_list(cptr); }