static void mo_chghost(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL; if (MyClient(source_p) && !IsOperAdmin(source_p)) { sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "CHGHOST"); return; } if (EmptyString(parv[2])) { parv[2] = parv[1]; target_p = source_p; if (!IsClient(target_p)) return; } else { target_p = find_client(parv[1]); if (target_p == NULL || !IsClient(target_p)) { sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, source_p->name, parv[1]); return; } } if (strlen(parv[2]) > HOSTLEN || !*parv[2] || !valid_hostname(parv[2])) { sendto_one(source_p, ":%s NOTICE %s :Invalid hostname", me.name, source_p->name); return; } if (IsUserHostIp(target_p)) delete_user_host(target_p->username, target_p->host, !MyConnect(target_p)); strlcpy(target_p->host, parv[2], sizeof(target_p->host)); SetIPSpoof(target_p); add_user_host(target_p->username, target_p->host, !MyConnect(target_p)); SetUserHost(target_p); if (MyClient(source_p)) { sendto_server(client_p, NULL, NOCAPS, NOCAPS, ":%s ENCAP * CHGHOST %s %s", source_p->name, target_p->name, parv[2]); sendto_one(source_p, ":%s NOTICE %s :%s changed to %s@%s", me.name, source_p->name, target_p->name, target_p->username, target_p->host); } if (MyConnect(target_p) && IsClient(source_p)) sendto_one(target_p, ":%s NOTICE %s :You are now %s@%s", me.name, target_p->name, target_p->username, target_p->host); }
/* ** m_kick ** parv[0] = sender prefix ** parv[1] = channel ** parv[2] = client to kick ** parv[3] = kick comment */ static int m_kick(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct membership *msptr; struct Client *who; struct Channel *chptr; int chasing = 0; char *comment; const char *name; char *p = NULL; const char *user; static char buf[BUFSIZE]; if(MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); comment = LOCAL_COPY((EmptyString(parv[3])) ? parv[2] : parv[3]); if(strlen(comment) > (size_t) REASONLEN) comment[REASONLEN] = '\0'; *buf = '\0'; if((p = strchr(parv[1], ','))) *p = '\0'; name = parv[1]; chptr = find_channel(name); if(chptr == NULL) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); return 0; } if(!IsServer(source_p)) { msptr = find_channel_membership(chptr, source_p); if((msptr == NULL) && MyConnect(source_p)) { sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), name); return 0; } if(!is_chanop(msptr)) { if(MyConnect(source_p)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, name); return 0; } /* If its a TS 0 channel, do it the old way */ if(chptr->channelts == 0) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), get_id(&me, source_p), get_id(source_p, source_p), name); return 0; } } /* Its a user doing a kick, but is not showing as chanop locally * its also not a user ON -my- server, and the channel has a TS. * There are two cases we can get to this point then... * * 1) connect burst is happening, and for some reason a legit * op has sent a KICK, but the SJOIN hasn't happened yet or * been seen. (who knows.. due to lag...) * * 2) The channel is desynced. That can STILL happen with TS * * Now, the old code roger wrote, would allow the KICK to * go through. Thats quite legit, but lets weird things like * KICKS by users who appear not to be chanopped happen, * or even neater, they appear not to be on the channel. * This fits every definition of a desync, doesn't it? ;-) * So I will allow the KICK, otherwise, things are MUCH worse. * But I will warn it as a possible desync. * * -Dianora */ } if((p = strchr(parv[2], ','))) *p = '\0'; user = parv[2]; /* strtoken(&p2, parv[2], ","); */ if(!(who = find_chasing(source_p, user, &chasing))) { return 0; } msptr = find_channel_membership(chptr, who); if(msptr != NULL) { if(MyClient(source_p) && IsService(who)) { sendto_one(source_p, form_str(ERR_ISCHANSERVICE), me.name, source_p->name, who->name, chptr->chname); return 0; } /* jdc * - In the case of a server kicking a user (i.e. CLEARCHAN), * the kick should show up as coming from the server which did * the kick. * - Personally, flame and I believe that server kicks shouldn't * be sent anyways. Just waiting for some oper to abuse it... */ if(IsServer(source_p)) sendto_channel_local(ALL_MEMBERS, chptr, ":%s KICK %s %s :%s", source_p->name, name, who->name, comment); else sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s KICK %s %s :%s", source_p->name, source_p->username, source_p->host, name, who->name, comment); sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s KICK %s %s :%s", use_id(source_p), chptr->chname, use_id(who), comment); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s KICK %s %s :%s", source_p->name, chptr->chname, who->name, comment); remove_user_from_channel(msptr); } else if (MyClient(source_p)) sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, form_str(ERR_USERNOTINCHANNEL), user, name); return 0; }
/* * ms_svsnick() * * parv[0] = sender prefix * parv[1] = oldnick * parv[2] = newnick */ static void ms_svsnick (struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char oldnick[NICKLEN]; char newnick[NICKLEN]; struct Client *oldnickname; struct Client *newnickname; if (!IsServer (source_p)) return; if (parc < 3 || !parv[1] || !parv[2]) { sendto_one (source_p, form_str (ERR_NONICKNAMEGIVEN), me.name, parv[0]); return; } /* terminate nick to NICKLEN */ strlcpy (oldnick, parv[1], NICKLEN); strlcpy (newnick, parv[2], NICKLEN); if (!clean_nick_name (oldnick)) { sendto_one (source_p, form_str (ERR_ERRONEUSNICKNAME), me.name, parv[0], oldnick); return; } /* check the nickname is ok */ if (!clean_nick_name (newnick)) { sendto_one (source_p, form_str (ERR_ERRONEUSNICKNAME), me.name, parv[0], newnick); return; } if (!(oldnickname = find_client (oldnick))) { sendto_one (source_p, form_str (ERR_NOSUCHNICK), me.name, source_p->name, oldnick); return; } if ((newnickname = find_client (newnick))) { sendto_one (source_p, form_str (ERR_NICKNAMEINUSE), me.name, parv[0], newnick); return; } if (MyConnect (oldnickname)) { if (!IsFloodDone (oldnickname)) flood_endgrace (oldnickname); if ((newnickname = find_client (oldnick))) { if (newnickname == oldnickname) { /* check the nick isnt exactly the same */ if (strcmp (oldnickname->name, newnick)) { change_local_nick (oldnickname->servptr, oldnickname, newnick); return; } else { /* client is doing :old NICK old * ignore it.. */ return; } } /* if the client that has the nick isnt registered yet (nick but no * user) then drop the unregged client */ if (IsUnknown (newnickname)) { /* the old code had an if(MyConnect(target_p)) here.. but I cant see * how that can happen, m_nick() is local only --fl_ */ exit_client (NULL, newnickname, &me, "Overridden"); change_local_nick (oldnickname->servptr, oldnickname, newnick); return; } else { sendto_one (source_p, form_str (ERR_NICKNAMEINUSE), me.name, parv[0], newnick); return; } } else { if (!ServerInfo.hub && uplink && IsCapable (uplink, CAP_LL)) { /* The uplink might know someone by this name already. */ sendto_one (uplink, ":%s NBURST %s %s %s", me.name, newnick, newnick, oldnickname->name); return; } else { change_local_nick (oldnickname->servptr, oldnickname, newnick); return; } } } else { sendto_server (client_p, source_p, NULL, NOCAPS, NOCAPS, LL_ICLIENT, ":%s SVSNICK %s %s", me.name, oldnick, newnick); return; } }
/* parse() * * given a raw buffer, parses it and generates parv, parc and sender */ void parse(struct Client *client_p, char *pbuffer, char *bufend) { struct Client *from = client_p; static char *para[MAXPARA + 2]; static char *sender; char *ch; char *s; char *end; int i = 1; char *numeric = 0; struct Message *mptr; s_assert(MyConnect(client_p)); if(IsAnyDead(client_p)) return; for(ch = pbuffer; *ch == ' '; ch++) /* skip spaces */ /* null statement */ ; if(from->name != NULL) para[0] = LOCAL_COPY(from->name); else para[0] = NULL; if(*ch == ':') { ch++; /* point sender to the sender param */ sender = ch; if((s = strchr(ch, ' '))) { *s = '\0'; s++; ch = s; } if(*sender && IsServer(client_p)) { from = find_any_client(sender); /* didnt find any matching client, issue a kill */ if(from == NULL) { ServerStats.is_unpf++; remove_unknown(client_p, sender, pbuffer); return; } para[0] = LOCAL_COPY(from->name); /* fake direction, hmm. */ if(from->from != client_p) { ServerStats.is_wrdi++; cancel_clients(client_p, from); return; } } while(*ch == ' ') ch++; } if(*ch == '\0') { ServerStats.is_empt++; return; } /* at this point there must be some sort of command parameter */ /* * Extract the command code from the packet. Point s to the end * of the command code and calculate the length using pointer * arithmetic. Note: only need length for numerics and *all* * numerics must have parameters and thus a space after the command * code. -avalon */ /* EOB is 3 chars long but is not a numeric */ if(*(ch + 3) == ' ' && /* ok, lets see if its a possible numeric.. */ IsDigit(*ch) && IsDigit(*(ch + 1)) && IsDigit(*(ch + 2))) { mptr = NULL; numeric = ch; ServerStats.is_num++; s = ch + 3; /* I know this is ' ' from above if */ *s++ = '\0'; /* blow away the ' ', and point s to next part */ } else { int ii = 0; if((s = strchr(ch, ' '))) *s++ = '\0'; mptr = hash_find_data(HASH_COMMAND, ch); /* no command or its encap only, error */ if(mptr == NULL || mptr->cmd == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ if(pbuffer[0] != '\0') { if(IsClient(from)) sendto_one_numeric(from, s_RPL(ERR_UNKNOWNCOMMAND), ch); } ServerStats.is_unco++; return; } ii = bufend - ((s) ? s : ch); mptr->bytes += ii; } end = bufend - 1; /* XXX this should be done before parse() is called */ if(*end == '\n') *end-- = '\0'; if(*end == '\r') *end = '\0'; /* ugh. so rb_string_to_array isn't brain damaged like the original one * however..so accomdate the old behavior, pass ¶[1] * and add + 1 to i...the rest of this mess can e fixed */ if(s != NULL) i = rb_string_to_array(s, ¶[1], MAXPARA) + 1; if(mptr == NULL) { do_numeric(numeric, client_p, from, i, para); return; } if(handle_command(mptr, client_p, from, i, /* XXX discards const!!! */ (const char **)(uintptr_t) para) < -1) { char *p; for(p = pbuffer; p <= end; p += 8) { /* HACK HACK */ /* Its expected this nasty code can be removed * or rewritten later if still needed. */ if((unsigned long)(p + 8) > (unsigned long)end) { for(; p <= end; p++) { ilog(L_MAIN, "%02x |%c", p[0], p[0]); } } else ilog(L_MAIN, "%02x %02x %02x %02x %02x %02x %02x %02x |%c%c%c%c%c%c%c%c", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); } } }
/* * m_links - LINKS message handler * parv[0] = sender prefix * parv[1] = servername mask * or * parv[0] = sender prefix * parv[1] = server to query * parv[2] = servername mask */ int m_links(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { const char* mask = ""; struct Client* acptr; struct ConfItem* aconf; char clean_mask[2 * HOSTLEN + 4]; char* p; int bogus_server = 0; static time_t last_used = 0L; if (parc > 2) { if (!IsServer(sptr) && !HasUmode(sptr,UMODE_REMOTEINFO)) { if (SeesOperMessages(sptr)) sendto_one(sptr,":%s NOTICE %s :You have no S umode", me.name, parv[0]); else sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (hunt_server(cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv) != HUNTED_ISME) return 0; mask = parv[2]; } else if (parc == 2) mask = parv[1]; assert(0 != mask); if(!NoFloodProtection(sptr)) { /* reject non local requests */ if(!MyClient(sptr)) return 0; if(IsHoneypot(sptr) || (last_used + PACE_WAIT) > CurrentTime) { /* safe enough to give this on a local connect only */ sendto_one(sptr,form_str(RPL_LOAD2HI),me.name,parv[0],"LINKS"); return 0; } else { last_used = CurrentTime; } } /* * *sigh* Before the kiddies find this new and exciting way of * annoying opers, lets clean up what is sent to all opers * -Dianora */ { const char* s = mask; while (*s) { if (!IsServChar(*s)) { bogus_server = 1; break; } s++; } } if (bogus_server) { sendto_ops_flag(UMODE_SPY, "BOGUS LINKS '%s' requested by %s (%s@%s) [%s]", clean_string(clean_mask, (const unsigned char *) mask, 2 * HOSTLEN), sptr->name, sptr->username, sptr->host, sptr->user->server); return 0; } if (*mask) /* only necessary if there is a mask */ mask = collapse(clean_string(clean_mask, (const unsigned char *) mask, 2 * HOSTLEN)); /* Here's a cute spacing hack for you -- asuffield */ if (MyConnect(sptr)) sendto_ops_flag(UMODE_SPY, "LINKS %s%srequested by %s (%s@%s) [%s]", mask, mask ? " " : "" , sptr->name, sptr->username, sptr->host, sptr->user->server); for (aconf = ConfigItemList; aconf; aconf = aconf->next) { if (aconf->status != CONF_NOCONNECT_SERVER) continue; if ((aconf->flags & CONF_FLAGS_HIDDEN_SERVER) && !HasUmode(sptr, UMODE_AUSPEX)) continue; acptr = find_server(aconf->name); if (!acptr) continue; if (!IsServer(acptr) && !IsMe(acptr)) continue; if (*mask && !match(mask, acptr->name)) continue; if (HasUmode(sptr,UMODE_AUSPEX)) sendto_one(sptr, form_str(RPL_LINKS), me.name, parv[0], acptr->name, acptr->serv->up, HasUmode(sptr, UMODE_SEEROUTING) ? acptr->hopcount : 0, acptr->info[0] ? acptr->info : "(Unknown Location)"); else { if(acptr->info[0]) { /* kludge, you didn't see this nor am I going to admit * that I coded this. */ p = strchr(acptr->info,']'); if(p) p += 2; /* skip the nasty [IP] part */ else p = acptr->info; } else { static char unknown[] = "(Unknown Location)"; p = unknown; } #ifdef SERVERHIDE if (strlen(acptr->name) < 6 || strcmp(".quiet", (acptr->name)+(strlen(acptr->name)-6)) || strcmp(".local", (acptr->name)+(strlen(acptr->name)-6)) || HasUmode(sptr,UMODE_SEEROUTING)) sendto_one(sptr, form_str(RPL_LINKS), me.name, parv[0], acptr->name, HasUmode(sptr,UMODE_SEEROUTING) ? acptr->serv->up : NETWORK_NAME, 0, p); #else sendto_one(sptr, form_str(RPL_LINKS), me.name, parv[0], acptr->name, acptr->serv->up, acptr->hopcount, p); #endif } } sendto_one(sptr, form_str(RPL_ENDOFLINKS), me.name, parv[0], EmptyString(mask) ? "*" : mask); return 0; }
/* * mo_connect - CONNECT command handler * * Added by Jto 11 Feb 1989 * * m_connect * parv[1] = servername * parv[2] = port number * parv[3] = remote server */ static void mo_connect(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { int port; int tmpport; struct server_conf *server_p; struct Client *target_p; /* always privileged with handlers */ if(MyConnect(source_p) && !IsOperRemote(source_p) && parc > 3) { sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "remote"); return; } if(hunt_server(client_p, source_p, ":%s CONNECT %s %s :%s", 3, parc, parv) != HUNTED_ISME) return; if((target_p = find_server(source_p, parv[1]))) { sendto_one_notice(source_p, ":Connect: Server %s already exists from %s.", parv[1], target_p->from->name); return; } /* * try to find the name, then host, if both fail notify ops and bail */ if((server_p = find_server_conf(parv[1])) == NULL) { sendto_one_notice(source_p, ":Connect: Host %s not listed in ircd.conf", parv[1]); return; } if(ServerConfSSL(server_p) && (!ircd_ssl_ok || !get_ssld_count())) { sendto_one_notice(source_p, ":Connect: Server %s is set to use SSL/TLS but SSL/TLS is not configured.", parv[1]); return; } /* * Get port number from user, if given. If not specified, * use the default form configuration structure. If missing * from there, then use the precompiled default. */ port = 0; if(parc > 2 && !EmptyString(parv[2])) port = atoi(parv[2]); if(port == 0 && server_p->port) port = server_p->port; else if(port <= 0) { sendto_one_notice(source_p, ":Connect: illegal port number"); return; } /* * Notify all operators about remote connect requests */ ilog(L_SERVER, "CONNECT From %s : %s %s", source_p->name, parv[1], parc > 2 ? parv[2] : ""); tmpport = server_p->port; server_p->port = port; /* * at this point we should be calling connect_server with a valid * C:line and a valid port in the C:line */ if(serv_connect(server_p, source_p)) { sendto_one_notice(source_p, ":*** Connecting to %s.%d", server_p->name, server_p->port); } else { sendto_one_notice(source_p, ":*** Couldn't connect to %s.%d", server_p->name, server_p->port); } /* * client is either connecting with all the data it needs or has been * destroyed, so reset it back to the configured settings */ server_p->port = tmpport; }
/* ** m_part ** parv[0] = sender prefix ** parv[1] = channel ** parv[2] = comment (added by Lefler) */ DLLFUNC CMD_FUNC(m_part) { aChannel *chptr; Membership *lp; char *p = NULL, *name; char *commentx = (parc > 2 && parv[2]) ? parv[2] : NULL; char *comment; int n; if (parc < 2 || parv[1][0] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PART"); return 0; } if (MyClient(sptr)) { if (IsShunned(sptr)) commentx = NULL; if (STATIC_PART) { if (!strcasecmp(STATIC_PART, "yes") || !strcmp(STATIC_PART, "1")) commentx = NULL; else if (!strcasecmp(STATIC_PART, "no") || !strcmp(STATIC_PART, "0")) ; /* keep original reason */ else commentx = STATIC_PART; } if (commentx) { n = dospamfilter(sptr, commentx, SPAMF_PART, parv[1], 0, NULL); if (n == FLUSH_BUFFER) return n; if (n < 0) commentx = NULL; } } for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) { chptr = get_channel(sptr, name, 0); if (!chptr) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); continue; } if (check_channelmask(sptr, cptr, name)) continue; /* 'commentx' is the general part msg, but it can be changed * per-channel (eg some chans block badwords, strip colors, etc) * so we copy it to 'comment' and use that in this for loop :) */ comment = commentx; if (!(lp = find_membership_link(sptr->user->channel, chptr))) { /* Normal to get get when our client did a kick ** for a remote client (who sends back a PART), ** so check for remote client or not --Run */ if (MyClient(sptr)) sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], name); continue; } if (!IsAnOper(sptr) && !is_chanownprotop(sptr, chptr)) { #ifdef STRIPBADWORDS int blocked = 0; #endif /* Banned? No comment allowed ;) */ if (comment && is_banned(sptr, chptr, BANCHK_MSG)) comment = NULL; /* And other things... */ if ((chptr->mode.mode & MODE_NOCOLOR) && comment) { if (strchr((char *)comment, 3) || strchr((char *)comment, 27)) { comment = NULL; } } if ((chptr->mode.mode & MODE_MODERATED) && comment && !has_voice(sptr, chptr) && !is_halfop(sptr, chptr)) { comment = NULL; } if ((chptr->mode.mode & MODE_STRIP) && comment) { comment = (char *)StripColors(comment); } #ifdef STRIPBADWORDS #ifdef STRIPBADWORDS_CHAN_ALWAYS if (comment) { comment = (char *)stripbadwords_channel(comment, &blocked); } #else if ((chptr->mode.extmode & EXTMODE_STRIPBADWORDS) && comment) { comment = (char *)stripbadwords_channel(comment, &blocked); } #endif #endif } /* +M and not +r? */ if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(sptr) && !IsAnOper(sptr)) comment = NULL; if (MyConnect(sptr)) { Hook *tmphook; for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_PART]; tmphook; tmphook = tmphook->next) { comment = (*(tmphook->func.pcharfunc))(sptr, chptr, comment); if (!comment) break; } } /* Send to other servers... */ if (!comment) sendto_serv_butone_token(cptr, parv[0], MSG_PART, TOK_PART, "%s", chptr->chname); else sendto_serv_butone_token(cptr, parv[0], MSG_PART, TOK_PART, "%s :%s", chptr->chname, comment); if (1) { if ((chptr->mode.mode & MODE_AUDITORIUM) && !is_chanownprotop(sptr, chptr)) { if (!comment) { sendto_chanops_butone(NULL, chptr, ":%s!%s@%s PART %s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname); if (!is_chan_op(sptr, chptr) && MyClient(sptr)) sendto_one(sptr, ":%s!%s@%s PART %s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname); } else { sendto_chanops_butone(NULL, chptr, ":%s!%s@%s PART %s %s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname, comment); if (!is_chan_op(cptr, chptr) && MyClient(sptr)) sendto_one(sptr, ":%s!%s@%s PART %s %s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname, comment); } } else { if (!comment) sendto_channel_butserv(chptr, sptr, PARTFMT, parv[0], chptr->chname); else sendto_channel_butserv(chptr, sptr, PARTFMT2, parv[0], chptr->chname, comment); } if (MyClient(sptr)) RunHook4(HOOKTYPE_LOCAL_PART, cptr, sptr, chptr, comment); else RunHook4(HOOKTYPE_REMOTE_PART, cptr, sptr, chptr, comment); remove_user_from_channel(sptr, chptr); } } return 0; }
static void mo_chgident(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL; if (MyClient(source_p) && !HasUMode(source_p, UMODE_ADMIN)) { sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "CHGIDENT"); return; } if (EmptyString(parv[2])) { parv[2] = parv[1]; target_p = source_p; if (!IsClient(target_p)) return; } else { target_p = hash_find_client(parv[1]); if (target_p == NULL || !IsClient(target_p)) { sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, source_p->name, parv[1]); return; } } if (strlen(parv[2]) > USERLEN || !*parv[2] || !valid_username(parv[2])) { sendto_one(source_p, ":%s NOTICE %s :Invalid username", me.name, source_p->name); return; } if (IsUserHostIp(target_p)) delete_user_host(target_p->username, target_p->host, !MyConnect(target_p)); strlcpy(target_p->username, parv[2], sizeof(target_p->username)); add_user_host(target_p->username, target_p->host, !MyConnect(target_p)); SetUserHost(target_p); if (MyClient(source_p)) { sendto_server(client_p, NOCAPS, NOCAPS, ":%s ENCAP * CHGIDENT %s %s", source_p->name, target_p->name, parv[2]); sendto_one(source_p, ":%s NOTICE %s :%s changed to %s@%s", me.name, source_p->name, target_p->name, target_p->username, target_p->host); } if (MyClient(target_p)) { if (IsClient(source_p)) sendto_one(target_p, ":%s NOTICE %s :You are now %s@%s", me.name, target_p->name, target_p->username, target_p->host); clear_ban_cache_client(target_p); } }