/* * Taken the code from ExitOneClient() for this and placed it here. * - avalon */ void remove_client_from_list(struct Client *cptr) { assert(cli_verify(cptr)); assert(con_verify(cli_connect(cptr))); assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr))); assert(!cli_next(cptr) || cli_verify(cli_next(cptr))); assert(!IsMe(cptr)); /* Only remove from the list if it actually IS in the list. * cli_next(cptr) cannot be NULL here if cptr is in the list, * only &me is at the end, and we never try to remove &me -GW */ if(cli_next(cptr)) { if (cli_prev(cptr)) cli_next(cli_prev(cptr)) = cli_next(cptr); else { GlobalClientList = cli_next(cptr); cli_prev(GlobalClientList) = 0; } cli_prev(cli_next(cptr)) = cli_prev(cptr); } cli_next(cptr) = cli_prev(cptr) = 0; if (IsUser(cptr) && cli_user(cptr)) { add_history(cptr, 0); off_history(cptr); } if (cli_user(cptr)) { free_user(cli_user(cptr)); cli_user(cptr) = 0; } if (cli_serv(cptr)) { if (cli_serv(cptr)->user) { free_user(cli_serv(cptr)->user); cli_serv(cptr)->user = 0; } if (cli_serv(cptr)->client_list) MyFree(cli_serv(cptr)->client_list); MyFree(cli_serv(cptr)->last_error_msg); MyFree(cli_serv(cptr)); #ifdef DEBUGMODE --servs.inuse; #endif } free_client(cptr); }
/** Remove \a cptr from lists that it is a member of. * Specifically, this delinks \a cptr from #GlobalClientList, updates * the whowas history list, frees its Client::cli_user and * Client::cli_serv fields, and finally calls free_client() on it. * @param[in] cptr Client to remove from lists and free. */ void remove_client_from_list(struct Client *cptr) { assert(cli_verify(cptr)); assert(con_verify(cli_connect(cptr))); assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr))); assert(!cli_next(cptr) || cli_verify(cli_next(cptr))); assert(!IsMe(cptr)); /* Only try remove cptr from the list if it IS in the list. * cli_next(cptr) cannot be NULL here, as &me is always the end * the list, and we never remove &me. -GW */ if(cli_next(cptr)) { if (cli_prev(cptr)) cli_next(cli_prev(cptr)) = cli_next(cptr); else { GlobalClientList = cli_next(cptr); cli_prev(GlobalClientList) = 0; } cli_prev(cli_next(cptr)) = cli_prev(cptr); } cli_next(cptr) = cli_prev(cptr) = 0; if (IsUser(cptr) && cli_user(cptr)) { add_history(cptr, 0); off_history(cptr); } if (cli_user(cptr)) { free_user(cli_user(cptr)); cli_user(cptr) = 0; } if (cli_serv(cptr)) { if (cli_serv(cptr)->user) { free_user(cli_serv(cptr)->user); cli_serv(cptr)->user = 0; } if (cli_serv(cptr)->client_list) MyFree(cli_serv(cptr)->client_list); MyFree(cli_serv(cptr)->last_error_msg); MyFree(cli_serv(cptr)); --servs.inuse; --servs.alloc; } free_client(cptr); }
/** Release a Client structure by prepending it to #clientFreeList. * @param[in] cptr Client that is no longer being used. */ static void dealloc_client(struct Client* cptr) { assert(cli_verify(cptr)); assert(0 == cli_connect(cptr)); --clients.inuse; cli_next(cptr) = clientFreeList; clientFreeList = cptr; cli_magic(cptr) = 0; }
/** Find the default usermode for a client. * @param[in] sptr Client to find default usermode for. * @return Pointer to usermode string (or NULL, if there is no default). */ const char* client_get_default_umode(const struct Client* sptr) { struct ConfItem* aconf; struct SLink* link; assert(cli_verify(sptr)); for (link = cli_confs(sptr); link; link = link->next) { aconf = link->value.aconf; if ((aconf->status & CONF_CLIENT) && ConfUmode(aconf)) return ConfUmode(aconf); } return NULL; }
/** Perform a very CPU-intensive verification of %GlobalClientList. * This checks the Client::cli_magic and Client::cli_prev field for * each element in the list, and also checks that there are no loops. * Any detected error will lead to an assertion failure. */ void verify_client_list(void) { struct Client *client, *prev = 0; unsigned int visited = 0; for (client = GlobalClientList; client; client = cli_next(client), ++visited) { /* Verify that this is a valid client, not a free'd one */ assert(cli_verify(client)); /* Verify that the list hasn't suddenly jumped around */ assert(cli_prev(client) == prev); /* Verify that the list hasn't become circular */ assert(cli_next(client) != GlobalClientList); assert(visited <= clients.alloc); /* Remember what should precede us */ prev = client; } }
/** Link \a cptr into #GlobalClientList. * @param[in] cptr Client to link into the global list. */ void add_client_to_list(struct Client *cptr) { assert(cli_verify(cptr)); assert(cli_next(cptr) == 0); assert(cli_prev(cptr) == 0); /* * Since we always insert new clients to the top of the list, * this should mean the "me" is the bottom most item in the list. * XXX - don't always count on the above, things change */ cli_prev(cptr) = 0; cli_next(cptr) = GlobalClientList; GlobalClientList = cptr; if (cli_next(cptr)) cli_prev(cli_next(cptr)) = cptr; }
/* * Create a new struct Client structure and set it to initial state. * * from == NULL, create local client (a client connected to a socket). * * from != NULL, create remote client (behind a socket associated with * the client defined by 'from'). * ('from' is a local client!!). */ struct Client* make_client(struct Client *from, int status) { struct Client* cptr = 0; struct Connection* con = 0; assert(!from || cli_verify(from)); cptr = alloc_client(); assert(0 != cptr); assert(!cli_magic(cptr)); assert(0 == from || 0 != cli_connect(from)); if (!from) { /* local client, allocate a struct Connection */ con = alloc_connection(); assert(0 != con); assert(!con_magic(con)); con_magic(con) = CONNECTION_MAGIC; con_fd(con) = -1; /* initialize struct Connection */ con_freeflag(con) = 0; con_nextnick(con) = CurrentTime - NICK_DELAY; con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1)); con_handler(con) = UNREGISTERED_HANDLER; con_client(con) = cptr; cli_local(cptr) = 1; /* Set certain fields of the struct Client */ cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime; cli_lastnick(cptr) = TStime(); } else con = cli_connect(from); /* use 'from's connection */ assert(0 != con); assert(con_verify(con)); cli_magic(cptr) = CLIENT_MAGIC; cli_connect(cptr) = con; /* set the connection and other fields */ cli_status(cptr) = status; cli_hnext(cptr) = cptr; strcpy(cli_username(cptr), "unknown"); return cptr; }
/* WARNING: Major CPU sink! * * This is a debugging routine meant to verify the integrity of the client * linked list. It is meant to be comprehensive, to detect *any* corruption * of that list. This means that it will be majorly CPU-intensive, and * should *only* be enabled on servers that have DEBUGMODE enabled. Ignore * this warning at your peril! */ void verify_client_list(void) { struct Client *client, *prev = 0, *sentinel = 0; extern unsigned int ircrandom(void); for (client = GlobalClientList; client; client = cli_next(client)) { /* Verify that this is a valid client, not a free'd one */ assert(cli_verify(client)); /* Verify that the list hasn't suddenly jumped around */ assert(cli_prev(client) == prev); /* Verify that the list hasn't become circular */ assert(cli_next(client) != GlobalClientList); assert(!sentinel || client != sentinel); prev = client; /* Remember what should preceed us */ if (!(ircrandom() % 50)) /* probabilistic loop detector */ sentinel = client; } }
/** Allocate a new Server object for a client. * If Client::cli_serv == NULL, allocate a Server structure for it and * initialize it. * @param[in] cptr %Client to make into a server. * @return The value of cli_serv(\a cptr). */ struct Server *make_server(struct Client *cptr) { struct Server *serv = cli_serv(cptr); assert(cli_verify(cptr)); if (!serv) { serv = (struct Server*) MyMalloc(sizeof(struct Server)); assert(0 != serv); memset(serv, 0, sizeof(struct Server)); /* All variables are 0 by default */ servs.inuse++; servs.alloc++; cli_serv(cptr) = serv; cli_serv(cptr)->lag = 60000; *serv->by = '\0'; DupString(serv->last_error_msg, "<>"); /* String must be non-empty */ } return cli_serv(cptr); }
/** Release a Client. * In addition to the cleanup done by dealloc_client(), this will free * any pending auth request, free the connection for local clients, * and delete the processing timer for the client. * @param[in] cptr Client to free. */ void free_client(struct Client* cptr) { if (!cptr) return; /* * forget to remove the client from the hash table? */ assert(cli_verify(cptr)); assert(cli_hnext(cptr) == cptr); /* or from linked list? */ assert(cli_next(cptr) == 0); assert(cli_prev(cptr) == 0); Debug((DEBUG_LIST, "Freeing client %s [%p], connection %p", cli_name(cptr), cptr, cli_connect(cptr))); if (cli_auth(cptr)) destroy_auth_request(cli_auth(cptr), 0); /* Make sure we didn't magically get re-added to the list */ assert(cli_next(cptr) == 0); assert(cli_prev(cptr) == 0); if (cli_from(cptr) == cptr) { /* in other words, we're local */ cli_from(cptr) = 0; /* timer must be marked as not active */ if (!cli_freeflag(cptr) && !t_active(&(cli_proc(cptr)))) dealloc_connection(cli_connect(cptr)); /* connection not open anymore */ else { if (-1 < cli_fd(cptr) && cli_freeflag(cptr) & FREEFLAG_SOCKET) socket_del(&(cli_socket(cptr))); /* queue a socket delete */ if (cli_freeflag(cptr) & FREEFLAG_TIMER) timer_del(&(cli_proc(cptr))); /* queue a timer delete */ } } cli_connect(cptr) = 0; dealloc_client(cptr); /* actually destroy the client */ }
/** Find the shortest non-zero ping time attached to a client. * If all attached ping times are zero, return the value for * FEAT_PINGFREQUENCY. * @param[in] acptr Client to find ping time for. * @return Ping time in seconds. */ int client_get_ping(const struct Client* acptr) { int ping = 0; struct ConfItem* aconf; struct SLink* link; assert(cli_verify(acptr)); for (link = cli_confs(acptr); link; link = link->next) { aconf = link->value.aconf; if (aconf->status & (CONF_CLIENT | CONF_SERVER)) { int tmp = get_conf_ping(aconf); if (0 < tmp && (ping > tmp || !ping)) ping = tmp; } } if (0 == ping) ping = feature_int(FEAT_PINGFREQUENCY); Debug((DEBUG_DEBUG, "Client %s Ping %d", cli_name(acptr), ping)); return ping; }