/** Set a server's numeric nick. * @param[in] cptr %Client that announced the server (ignored). * @param[in,out] server %Server that is being assigned a numnick. * @param[in] yxx %Numeric nickname for server. */ void SetServerYXX(struct Client* cptr, struct Client* server, const char* yxx) { unsigned int index; if (5 == strlen(yxx)) { ircd_strncpy(cli_yxx(server), yxx, 2); ircd_strncpy(cli_serv(server)->nn_capacity, yxx + 2, 3); } else { (cli_yxx(server))[0] = yxx[0]; cli_serv(server)->nn_capacity[0] = yxx[1]; cli_serv(server)->nn_capacity[1] = yxx[2]; } cli_serv(server)->nn_mask = base64toint(cli_serv(server)->nn_capacity); index = base64toint(cli_yxx(server)); if (index >= lastNNServer) lastNNServer = index + 1; server_list[index] = server; /* Note, exit_one_client uses the fact that `client_list' != NULL to * determine that SetServerYXX has been called - and then calls * ClearServerYXX. However, freeing the allocation happens in free_client() */ cli_serv(server)->client_list = (struct Client**) MyCalloc(cli_serv(server)->nn_mask + 1, sizeof(struct Client*)); }
/** Register numeric of new (remote) client. * See @ref numnicks for more details. * Add it to the appropriate client_list. * @param[in] acptr %User being registered. * @param[in] yxx User's numnick. */ void SetRemoteNumNick(struct Client* acptr, const char *yxx) { struct Client** acptrp; struct Client* server = cli_user(acptr)->server; if (5 == strlen(yxx)) { strcpy(cli_yxx(acptr), yxx + 2); } else { (cli_yxx(acptr))[0] = *++yxx; (cli_yxx(acptr))[1] = *++yxx; (cli_yxx(acptr))[2] = 0; } Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", cli_yxx(acptr), base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask)); acptrp = &(cli_serv(server))->client_list[base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask]; if (*acptrp) { /* * this exits the old client in the array, not the client * that is being set */ exit_client(cli_from(acptr), *acptrp, server, "Numeric nick collision (Ghost)"); } *acptrp = acptr; }
/** Look up a server by numnick string. * See @ref numnicks for more details. * @param[in] numeric %Numeric nickname of server (may contain trailing junk). * @return %Server with that numnick (or NULL). */ static struct Client* FindXNServer(const char* numeric) { char buf[3]; buf[0] = *numeric++; buf[1] = *numeric; buf[2] = '\0'; Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf))); return server_list[base64toint(buf)]; }
/** Remove a client from a server's user array. * @param[in] server %Server that owns the user to remove. * @param[in] yxx Numnick of client to remove. */ void RemoveYXXClient(struct Client* server, const char* yxx) { assert(0 != server); assert(0 != yxx); if (*yxx) { Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx, base64toint(yxx) & cli_serv(server)->nn_mask)); cli_serv(server)->client_list[base64toint(yxx) & cli_serv(server)->nn_mask] = 0; } }
/** Look up a server by numnick string. * See @ref numnicks for more details. * @param[in] numeric %Numeric nickname of server. * @return %Server with that numnick (or NULL). */ struct Client* FindNServer(const char* numeric) { unsigned int len = strlen(numeric); if (len < 3) { Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric))); return server_list[base64toint(numeric)]; } else if (len == 3) { Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric, convert2n[(unsigned char) *numeric])); return server_list[convert2n[(unsigned char) *numeric]]; } return FindXNServer(numeric); }
void checkServer(struct Client *sptr, struct Client *acptr) { char outbuf[BUFSIZE]; /* Header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at:: %s (%Tu)", myctime(acptr->cli_serv->timestamp), acptr->cli_serv->timestamp); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name:: %s", acptr->cli_name); send_reply(sptr, RPL_DATASTR, outbuf); if (cli_sslclifp(acptr) && (strlen(cli_sslclifp(acptr)) > 0)) { ircd_snprintf(0, outbuf, sizeof(outbuf), "SSL Fingerprint:: %s", cli_sslclifp(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric:: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Users:: %d / %d", (acptr == &me) ? UserStats.local_clients : cli_serv(acptr)->clients, base64toint(cli_serv(acptr)->nn_capacity)); send_reply(sptr, RPL_DATASTR, outbuf); if (IsBurst(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Bursting"); else if (IsBurstAck(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Awaiting EOB Ack"); else if (IsService(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Network Service"); else if (IsHub(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Network Hub"); ircd_snprintf(0, outbuf, sizeof(outbuf), " Class:: %s", get_client_class(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); if (feature_bool(FEAT_CHECK_EXTENDED)) { int dlinkc = 0; struct DLink* slink = NULL; send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_DATASTR, "Downlinks::"); for (slink = cli_serv(acptr)->down; slink; slink = slink->next) { ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc, IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" : IsService(slink->value.cptr) ? "=" : IsHub(slink->value.cptr) ? "+" : " ", cli_name(slink->value.cptr)); send_reply(sptr, RPL_DATASTR, outbuf); } if (!dlinkc) send_reply(sptr, RPL_DATASTR, "<none>"); } /* Send 'END OF CHECK' message */ send_reply(sptr, RPL_ENDOFCHECK, " "); }
/** Look up a user by numnick string. * See @ref numnicks for more details. * @param[in] yxx %Numeric nickname of user. * @return %User with that numnick (or NULL). */ struct Client* findNUser(const char* yxx) { struct Client* server = 0; if (5 == strlen(yxx)) { if (0 != (server = FindXNServer(yxx))) { Debug((DEBUG_DEBUG, "findNUser: %s(%d)", yxx, base64toint(yxx + 2) & cli_serv(server)->nn_mask)); return cli_serv(server)->client_list[base64toint(yxx + 2) & cli_serv(server)->nn_mask]; } } else if (0 != (server = FindNServer(yxx))) { Debug((DEBUG_DEBUG, "findNUser: %s(%d)", yxx, base64toint(yxx + 1) & cli_serv(server)->nn_mask)); return cli_serv(server)->client_list[base64toint(yxx + 1) & cli_serv(server)->nn_mask]; } return 0; }
char *asuka_nickip(char *host) { struct in_addr addr; int decoded; decoded = base64toint(host); addr.s_addr = ntohl(decoded); return sstrdup(inet_ntoa(addr)); }
bool CProtocol::Process ( const CString& szLine ) { bool bGotText = false; std::vector < CString > vec; // Separamos los tokens del comando size_t iPos = szLine.find ( ':' ); if ( iPos != CString::npos ) { bGotText = true; szLine.Split ( vec, ' ', 0, iPos - 1 ); vec [ vec.size () - 1 ] = std::string ( szLine, iPos + 1 ); } else szLine.Split ( vec, ' ' ); if ( !m_bGotServer ) { // El primer mensaje esperado es el de la información del servidor al que nos conectamos if ( bGotText && szLine.compare ( 0, 6, "SERVER" ) == 0 ) { // Procesamos el mensaje CMessageSERVER message; message.SetSource ( NULL ); if ( ! message.ProcessMessage ( szLine, vec ) ) return false; // Generamos el numérico m_bGotServer = true; new CServer ( &m_me, message.GetYXX (), message.GetHost (), message.GetDesc (), message.GetFlags () ); return true; } return false; } // Buscamos el orígen del mensaje CClient* pSource = 0; unsigned long ulNumeric = base64toint ( vec [ 0 ] ); if ( ulNumeric > 4095 ) { // Es un usuario CServer* pServer; if ( ulNumeric > 262143 ) { // Servidor con numérico de dos dígitos pServer = m_me.GetServer ( ulNumeric >> 18 ); if ( pServer ) pSource = pServer->GetUser ( ulNumeric & 262143 ); }
/** Decode an IP address from base64. * @param[in] input Input buffer to decode. * @param[out] addr IP address structure to populate. */ void base64toip(const char* input, struct irc_in_addr* addr) { memset(addr, 0, sizeof(*addr)); if (strlen(input) == 6) { unsigned int in = base64toint(input); /* An all-zero address should stay that way. */ if (in) { addr->in6_16[5] = htons(65535); addr->in6_16[6] = htons(in >> 16); addr->in6_16[7] = htons(in & 65535); } } else {
static void stats_servers_verbose(struct Client* sptr, struct StatDesc* sd, int stat, char* param) { struct Client *acptr; /* lowercase 'v' is for human-readable, * uppercase 'V' is for machine-readable */ if (stat == 'v') send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, "%-20s %-20s Flags Hops Numeric Lag RTT Up Down " "Clients/Max Proto %-10s :Info", "Servername", "Uplink", "LinkTS"); for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (!IsServer(acptr) && !IsMe(acptr)) continue; if (param && match(param, cli_name(acptr))) /* narrow search */ continue; send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, stat == 'v' ? "%-20s %-20s %c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i " "P%-2i %Tu :%s" : "%s %s %c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s", cli_name(acptr), cli_name(cli_serv(acptr)->up), IsBurst(acptr) ? 'B' : '-', IsBurstAck(acptr) ? 'A' : '-', IsHub(acptr) ? 'H' : '-', IsService(acptr) ? 'S' : '-', cli_hopcount(acptr), NumServ(acptr), base64toint(cli_yxx(acptr)), cli_serv(acptr)->lag, cli_serv(acptr)->asll_rtt, cli_serv(acptr)->asll_to, cli_serv(acptr)->asll_from, cli_serv(acptr)->clients, cli_serv(acptr)->nn_mask, cli_serv(acptr)->prot, cli_serv(acptr)->timestamp, cli_info(acptr)); } }
/** Set a server's capacity. * @param[in] c %Server whose capacity is being set. * @param[in] capacity Maximum number of clients the server supports. */ void SetYXXCapacity(struct Client* c, unsigned int capacity) { unsigned int max_clients = 16; /* * Calculate mask to be used for the maximum number of clients */ while (max_clients < capacity) max_clients <<= 1; /* * Sanity checks */ if (max_clients > NN_MAX_CLIENT) { fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d " "too large ! Please decrease this value.\n", max_clients - NN_MAX_CLIENT); exit(-1); } --max_clients; inttobase64(cli_serv(c)->nn_capacity, max_clients, 3); cli_serv(c)->nn_mask = max_clients; /* Our Numeric Nick mask */ cli_serv(c)->client_list = (struct Client**) MyCalloc(max_clients + 1, sizeof(struct Client*)); server_list[base64toint(cli_yxx(c))] = c; }
/* * ms_nick - server message handler for nicks * parv[0] = sender prefix * parv[1] = nickname * * If from server, source is client: * parv[2] = timestamp * * Source is server: * parv[2] = hopcount * parv[3] = timestamp * parv[4] = username * parv[5] = hostname * parv[6] = umode (optional) * parv[parc-3] = IP# <- Only Protocol >= 10 * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10 * parv[parc-1] = info * parv[0] = server */ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* acptr; char nick[NICKLEN + 2]; time_t lastnick = 0; int differ = 1; int samelastnick = 0; assert(0 != cptr); assert(0 != sptr); assert(IsServer(cptr)); if ((IsServer(sptr) && parc < 8) || parc < 3) { sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C", parv[1], cptr); return need_more_params(sptr, "NICK"); } ircd_strncpy(nick, parv[1], NICKLEN); nick[NICKLEN] = '\0'; if (IsServer(sptr)) { lastnick = atoi(parv[3]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(sptr)->lag = TStime() - lastnick; } else { lastnick = atoi(parv[2]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick; } /* * 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 (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) { send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]); ++ServerStats->is_kill; sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1], parv[0], cptr); sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])", IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1], nick, cli_name(cptr)); if (!IsServer(sptr)) { /* * bad nick _change_ */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)", parv[0], cli_name(&me), cli_name(cptr), parv[0], cli_user(sptr) ? cli_username(sptr) : "", cli_user(sptr) ? cli_name(cli_user(sptr)->server) : cli_name(cptr)); } 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 */ assert(NULL == strchr(nick,'.')); acptr = FindClient(nick); if (!acptr) { /* * No collisions, all clear... */ return set_nick_name(cptr, sptr, nick, parc, parv); } assert(0 != acptr); /* * 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(cli_name(acptr), nick) == 0) /* * 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 */ else /* * Allows change of case in his/her nick */ return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Note: From this point forward it can be assumed that * acptr != sptr (point to different client structures). */ assert(acptr != sptr); /* * 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) && MyConnect(acptr)) { ++ServerStats->is_ref; IPcheck_connect_fail(cli_ip(acptr)); exit_client(cptr, acptr, &me, "Overridden by other sign on"); return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Decide, we really have a nick collision and deal with it */ /* * 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 the TimeStamps are equal, we kill both (or only 'new' * if it was a ":server NICK new ..."). * Otherwise we kill the youngest when user@host differ, * or the oldest when they are the same. * We treat user and ~user as different, because if it wasn't * a faked ~user the AUTH wouldn't have added the '~'. * --Run * */ if (IsServer(sptr)) { /* * A new NICK being introduced by a neighbouring * server (e.g. message type ":server NICK new ..." received) * * compare IP address and username */ differ = (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) || (0 != ircd_strcmp(cli_user(acptr)->username, parv[4])); sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- " "%C %Tu (%s user@host))", acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick, differ ? "Different" : "Same"); } else { /* * A NICK change has collided (e.g. message type ":old NICK new"). * * compare IP address and username */ differ = (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) || (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username)); sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to " "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick); } /* * Now remove (kill) the nick on our side if it is the youngest. * If no timestamp was received, we ignore the incoming nick * (and expect a KILL for our legit nick soon ): * When the timestamps are equal we kill both nicks. --Run * acptr->from != cptr should *always* be true (?). * * This exits the client sending the NICK message */ if (cli_from(acptr) != cptr) { if ((differ && lastnick >= cli_lastnick(acptr)) || (!differ && lastnick <= cli_lastnick(acptr))) { if (!IsServer(sptr)) { ++ServerStats->is_kill; sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (Nick collision)", sptr, cli_name(&me)); assert(!MyConnect(sptr)); SetFlag(sptr, FLAG_KILLED); exit_client_msg(cptr, sptr, &me, "Killed (%s (Nick collision))", feature_str(FEAT_HIS_SERVERNAME)); sptr = 0; /* Make sure we don't use the dead client */ } else { /* We need to kill this incoming client, which hasn't been properly registered yet. * Send a KILL message upstream to the server it came from */ sendcmdto_one(&me, CMD_KILL, sptr, "%s :%s (Nick collision)", parv[parc-2], cli_name(&me)); } /* If the two have the same TS then we want to kill both sides, so * don't leave yet! */ if (lastnick != cli_lastnick(acptr)) return 0; /* Ignore the NICK */ } send_reply(acptr, ERR_NICKCOLLISION, nick); } ++ServerStats->is_kill; SetFlag(acptr, FLAG_KILLED); if (lastnick == cli_lastnick(acptr)) samelastnick = 1; /* * This exits the client we had before getting the NICK message */ if (differ) { sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (older nick " "overruled)", acptr, cli_name(&me)); if (MyConnect(acptr)) { sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (older " "nick overruled))", feature_str(FEAT_HIS_SERVERNAME)); sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick " "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME)); } exit_client_msg(cptr, acptr, &me, "Killed (%s (older nick " "overruled))", feature_str(FEAT_HIS_SERVERNAME)); } else { sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (nick collision from " "same user@host)", acptr, cli_name(&me)); if (MyConnect(acptr)) { sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (nick " "collision from same user@host))", feature_str(FEAT_HIS_SERVERNAME)); sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick " "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME)); } exit_client_msg(cptr, acptr, &me, "Killed (%s (nick collision from " "same user@host))", feature_str(FEAT_HIS_SERVERNAME)); } if (samelastnick) return 0; assert(0 != sptr); return set_nick_name(cptr, sptr, nick, parc, parv); }
/** Unassign a server's numnick. * @param[in] server %Server that should be removed from the numnick table. */ void ClearServerYXX(const struct Client *server) { unsigned int index = base64toint(cli_yxx(server)); if (server_list[index] == server) /* Sanity check */ server_list[index] = 0; }
void CChannel::SetModes ( const CString& szModes, const std::vector < CString >& vecModeParams ) { unsigned int uiParamIndex = 0; enum { ADD, DEL } eDirection = ADD; CServer& me = CProtocol::GetSingleton ().GetMe (); const char* p = szModes.c_str (); while ( *p != '\0' ) { switch ( *p ) { case '+': { eDirection = ADD; break; } case '-': { eDirection = DEL; break; } default: { unsigned long ulMode = ms_ulChannelModes [ (unsigned char)*p ]; if ( ulMode != 0 ) { if ( ulMode < CMODE_PARAMSMAX ) { if ( eDirection == ADD ) m_ulModes |= ulMode; else m_ulModes &= ~ulMode; if ( ulMode >= CMODE_MAX ) { // El modo lleva parámetros switch ( ulMode ) { case CMODE_KEY: if ( eDirection == ADD ) SetKey ( vecModeParams [ uiParamIndex ] ); else SetKey ( "" ); ++uiParamIndex; break; case CMODE_LIMIT: if ( eDirection == ADD ) { SetLimit ( atoi ( vecModeParams [ uiParamIndex ] ) ); ++uiParamIndex; } else SetLimit ( 0 ); break; } } } else { // Cambiamos flags de usuarios o bans if ( ulMode == CFLAG_BAN ) { if ( eDirection == ADD ) AddBan ( vecModeParams [ uiParamIndex ] ); else RemoveBan ( vecModeParams [ uiParamIndex ] ); ++uiParamIndex; } else { CUser* pUser = me.GetUserAnywhere ( base64toint ( vecModeParams [ uiParamIndex ] ) ); ++uiParamIndex; if ( pUser ) { CMembership* pMembership = GetMembership ( pUser ); if ( pMembership ) { unsigned long ulCurFlags = pMembership->GetFlags (); if ( eDirection == ADD ) pMembership->SetFlags ( ulCurFlags | ulMode ); else pMembership->SetFlags ( ulCurFlags & ~ulMode ); } } } } } } } ++p; } }
static void dump_map(struct Client *cptr, struct Client *server, char *mask, int prompt_length) { static char prompt[64]; struct DLink *lp; char *p = &prompt[prompt_length]; int cnt = 0; *p = '\0'; if (prompt_length > 60) send_reply(cptr, RPL_MAPMORE, prompt, cli_name(server)); else { char lag[512]; int showserv = 1; unsigned int totalusers = UserStats.clients; unsigned int percentage; unsigned int serv_clients; if (totalusers == 0) totalusers = 1; /* ¿? NO deberia ocurrir nunca... */ percentage = (10000 * (IsMe(server) ? UserStats.local_clients : cli_serv(server)->clients)) / totalusers; if (IsMe(server)) strcpy(lag,"(0s)"); else if (cli_serv(server)->lag>10000) lag[0]=0; else if (cli_serv(server)->lag<0) strcpy(lag,"(0s)"); else sprintf(lag,"(%is)",cli_serv(server)->lag); if (IsHiddenserv(server) && !feature_bool(FEAT_SHOWSERVON_MAP)) if (!IsAnOper(cptr) && !es_representante(cptr)) showserv = 0; serv_clients = (server == &me) ? UserStats.local_clients : cli_serv(server)->clients; send_reply(cptr, RPL_MAP, prompt, ( (IsBurst(server)) ? "*" : (IsBurstAck(server) ? "!" : "")), showserv ? cli_name(server) : feature_str(FEAT_HIS_SERVERNAME), lag, NumServ(server), base64toint(NumServ(server)), serv_clients, (serv_clients == 1) ? "" : "s", (percentage / 100), (percentage % 100)); } if (prompt_length > 0) { p[-1] = ' '; if (p[-2] == '`') p[-2] = ' '; } if (prompt_length > 60) return; strcpy(p, "|-"); for (lp = cli_serv(server)->down; lp; lp = lp->next) if (match(mask, cli_name(lp->value.cptr))) cli_flags(lp->value.cptr) &= ~FLAGS_MAP; else { cli_flags(lp->value.cptr) |= FLAGS_MAP; cnt++; } for (lp = cli_serv(server)->down; lp; lp = lp->next) { if ((cli_flags(lp->value.cptr) & FLAGS_MAP) == 0) continue; if (--cnt == 0) *p = '`'; dump_map(cptr, lp->value.cptr, mask, prompt_length + 2); } if (prompt_length > 0) p[-1] = '-'; }