static void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { int i; struct Client *acptr; struct Client *acptr2; const struct ConnectionClass* cl; char* tname; int doall; int *link_s; int *link_u; int cnt = 0; int wilds; int dow; if (parc < 2 || BadPtr(parv[1])) { /* just "TRACE" without parameters. Must be from local client */ parc = 1; acptr = &me; tname = cli_name(&me); i = HUNTED_ISME; } else if (parc < 3 || BadPtr(parv[2])) { /* No target specified. Make one before propagating. */ parc = 2; tname = parv[1]; if ((acptr = find_match_server(parv[1])) || ((acptr = FindClient(parv[1])) && !MyUser(acptr))) { if (IsUser(acptr)) parv[2] = cli_name(cli_user(acptr)->server); else parv[2] = cli_name(acptr); parc = 3; parv[3] = 0; if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr), "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; } else i = HUNTED_ISME; } else { /* Got "TRACE <tname> :<target>" */ parc = 3; if (MyUser(sptr) || Protocol(cptr) < 10) acptr = find_match_server(parv[2]); else acptr = FindNServer(parv[2]); if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; tname = parv[1]; } if (i == HUNTED_PASS) { if (!acptr) acptr = next_client(GlobalClientList, tname); else acptr = cli_from(acptr); send_reply(sptr, RPL_TRACELINK, version, debugmode, tname, acptr ? cli_name(cli_from(acptr)) : "<No_match>"); return; } doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1; wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?'); dow = wilds || doall; /* Don't give (long) remote listings to lusers */ if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) { send_reply(sptr, RPL_TRACEEND); return; } link_s = MyCalloc(2 * maxconnections, sizeof(link_s[0])); link_u = link_s + maxconnections; if (doall) { for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (IsUser(acptr)) link_u[cli_fd(cli_from(acptr))]++; else if (IsServer(acptr)) link_s[cli_fd(cli_from(acptr))]++; } } /* report all direct connections */ for (i = 0; i <= HighestFd; i++) { const char *conClass; if (!(acptr = LocalClientArray[i])) /* Local Connection? */ continue; if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) && !IsAnOper(acptr) && (acptr != sptr)) continue; if (!doall && wilds && match(tname, cli_name(acptr))) continue; if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr))) continue; conClass = get_client_class(acptr); switch (cli_status(acptr)) { case STAT_CONNECTING: send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr)); cnt++; break; case STAT_HANDSHAKE: send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr)); cnt++; break; case STAT_ME: break; case STAT_UNKNOWN: case STAT_UNKNOWN_USER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, get_client_name(acptr, HIDE_IP)); cnt++; break; case STAT_UNKNOWN_SERVER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server"); cnt++; break; case STAT_USER: /* Only opers see users if there is a wildcard but anyone can see all the opers. */ if ((IsAnOper(sptr) && (MyUser(sptr) || !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) { if (IsAnOper(acptr)) send_reply(sptr, RPL_TRACEOPERATOR, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); else send_reply(sptr, RPL_TRACEUSER, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); cnt++; } break; /* * Connection is a server * * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age> * * class Class the server is in * nS Number of servers reached via this link * nC Number of clients reached via this link * name Name of the server linked * ConnBy Who established this link * last Seconds since we got something from this link * age Seconds this link has been alive * * Additional comments etc...... -Cym-<*****@*****.**> */ case STAT_SERVER: if (cli_serv(acptr)->user) { if (!cli_serv(acptr)->by[0] || !(acptr2 = findNUser(cli_serv(acptr)->by)) || (cli_user(acptr2) != cli_serv(acptr)->user)) acptr2 = NULL; send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), acptr2 ? cli_name(acptr2) : "*", cli_serv(acptr)->user->username, cli_serv(acptr)->user->host, CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); } else send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*", cli_name(&me), CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); cnt++; break; default: /* We actually shouldn't come here, -msa */ send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP)); cnt++; break; } } /* * Add these lines to summarize the above which can get rather long * and messy when done remotely - Avalon */ if (IsAnOper(sptr) && doall) { for (cl = get_class_list(); cl; cl = cl->next) { if (Links(cl) > 1) send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1); } } send_reply(sptr, RPL_TRACEEND); MyFree(link_s); }
/* * m_challenge - generate RSA challenge for wouldbe oper * parv[1] = operator to challenge for, or +response * */ static int m_challenge(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct oper_conf *oper_p; char *challenge = NULL; /* to placate gcc */ char chal_line[CHALLENGE_WIDTH]; unsigned char *b_response; size_t cnt; int len = 0; /* if theyre an oper, reprint oper motd and ignore */ if(IsOper(source_p)) { sendto_one(source_p, form_str(RPL_YOUREOPER), me.name, source_p->name); send_oper_motd(source_p); return 0; } if(*parv[1] == '+') { /* Ignore it if we aren't expecting this... -A1kmm */ if(!source_p->localClient->challenge) return 0; if((rb_current_time() - source_p->localClient->chal_time) > CHALLENGE_EXPIRES) { sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name); ilog(L_FOPER, "EXPIRED CHALLENGE (%s) by (%s!%s@%s) (%s)", source_p->localClient->opername, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Expired CHALLENGE attempt by %s (%s@%s)", source_p->name, source_p->username, source_p->host); cleanup_challenge(source_p); return 0; } parv[1]++; b_response = rb_base64_decode((const unsigned char *)parv[1], strlen(parv[1]), &len); if(len != SHA_DIGEST_LENGTH || memcmp(source_p->localClient->challenge, b_response, SHA_DIGEST_LENGTH)) { sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name); ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)", source_p->localClient->opername, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Failed CHALLENGE attempt by %s (%s@%s)", source_p->name, source_p->username, source_p->host); rb_free(b_response); cleanup_challenge(source_p); return 0; } rb_free(b_response); oper_p = find_oper_conf(source_p->username, source_p->orighost, source_p->sockhost, source_p->localClient->opername); if(oper_p == NULL) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)", source_p->localClient->opername, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); return 0; } cleanup_challenge(source_p); oper_up(source_p, oper_p); ilog(L_OPERED, "OPER %s by %s!%s@%s (%s)", source_p->localClient->opername, source_p->name, source_p->username, source_p->host, source_p->sockhost); return 0; } cleanup_challenge(source_p); oper_p = find_oper_conf(source_p->username, source_p->orighost, source_p->sockhost, parv[1]); if(oper_p == NULL) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)", parv[1], source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); return 0; } if(!oper_p->rsa_pubkey) { sendto_one_notice(source_p, ":I'm sorry, PK authentication is not enabled for your oper{} block."); return 0; } if(IsOperConfNeedSSL(oper_p) && !IsSSLClient(source_p)) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s) -- requires SSL/TLS", parv[1], source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "Failed CHALLENGE attempt - missing SSL/TLS by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return 0; } if (oper_p->certfp != NULL) { if (source_p->certfp == NULL || strcasecmp(source_p->certfp, oper_p->certfp)) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s) -- client certificate fingerprint mismatch", parv[1], source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "Failed OPER attempt - client certificate fingerprint mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return 0; } } if(!generate_challenge(&challenge, &(source_p->localClient->challenge), oper_p->rsa_pubkey)) { char *chal = challenge; source_p->localClient->chal_time = rb_current_time(); for(;;) { cnt = rb_strlcpy(chal_line, chal, CHALLENGE_WIDTH); sendto_one(source_p, form_str(RPL_RSACHALLENGE2), me.name, source_p->name, chal_line); if(cnt > CHALLENGE_WIDTH) chal += CHALLENGE_WIDTH - 1; else break; } sendto_one(source_p, form_str(RPL_ENDOFRSACHALLENGE2), me.name, source_p->name); rb_free(challenge); source_p->localClient->opername = rb_strdup(oper_p->name); } else sendto_one_notice(source_p, ":Failed to generate challenge."); return 0; }
static int m_displaymsg(struct Client *source_p, const char *channel, int underline, int action, const char *nick, const char *text) { struct Channel *chptr; struct membership *msptr; char nick2[NICKLEN+1]; char nick3[NICKLEN+1]; char text2[BUFSIZE]; if((chptr = find_channel(channel)) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), channel); return 0; } if(!(msptr = find_channel_membership(chptr, source_p))) { sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), chptr->chname); return 0; } if(!(chptr->mode.mode & chmode_flags['N'])) { sendto_one_numeric(source_p, 573, "%s :Roleplay commands are not enabled on this channel.", chptr->chname); return 0; } if(!can_send(chptr, source_p, msptr)) { sendto_one_numeric(source_p, 573, "%s :Cannot send to channel.", chptr->chname); return 0; } /* enforce flood stuff on roleplay commands */ if(flood_attack_channel(0, source_p, chptr, chptr->chname)) return 0; /* enforce target change on roleplay commands */ if(!is_chanop_voiced(msptr) && !IsOper(source_p) && !add_channel_target(source_p, chptr)) { sendto_one(source_p, form_str(ERR_TARGCHANGE), me.name, source_p->name, chptr->chname); return 0; } rb_strlcpy(nick3, nick, sizeof(nick3)); if(underline) snprintf(nick2, sizeof(nick2), "\x1F%s\x1F", strip_unprintable(nick3)); else snprintf(nick2, sizeof(nick2), "%s", strip_unprintable(nick3)); /* don't allow nicks to be empty after stripping * this prevents nastiness like fake factions, etc. */ if(EmptyString(nick3)) { sendto_one_numeric(source_p, 573, "%s :No visible non-stripped characters in nick.", chptr->chname); return 0; } if(action) snprintf(text2, sizeof(text2), "\1ACTION %s\1", text); else snprintf(text2, sizeof(text2), "%s", text); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%[email protected] PRIVMSG %s :%s (%s)", nick2, source_p->name, channel, text2, source_p->name); sendto_match_servs(source_p, "*", CAP_ENCAP, NOCAPS, "ENCAP * ROLEPLAY %s %s :%s", channel, nick2, text2); return 0; }
/* ** m_kick ** 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; char text[10]; const char *user; static char buf[BUFSIZE]; int is_override = 0; if(MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); *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; } user = parv[2]; /* strtoken(&p2, parv[2], ","); */ if(!(who = find_chasing(source_p, user, &chasing))) { 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(!can_kick_deop(msptr, find_channel_membership(chptr, who))) { if(MyConnect(source_p)) { if(IsOverride(source_p)) is_override = 1; else { sendto_one(source_p, ":%s 482 %s %s :You do not have the proper privileges to kick this user", me.name, source_p->name, name); return 0; } } /* If its a TS 0 channel, do it the old way */ else 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'; 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; } if(MyClient(source_p) && chptr->mode.mode & MODE_NOKICK) { sendto_one_numeric(source_p, ERR_NOKICK, form_str(ERR_NOKICK), chptr->chname); return 0; } if (MyClient(source_p) && chptr->mode.mode & MODE_NOOPERKICK && IsOper(who)) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Overriding KICK from %s on %s in %s (channel is +M)", source_p->name, who->name, chptr->chname); sendto_one_numeric(source_p, ERR_ISCHANSERVICE, "%s %s :Cannot kick IRC operators from that channel.", who->name, chptr->chname); return 0; } if(MyClient(source_p)) { hook_data_channel_approval hookdata; hookdata.client = source_p; hookdata.chptr = chptr; hookdata.target = who; hookdata.approved = 1; call_hook(h_can_kick, &hookdata); if (!hookdata.approved) return 0; } comment = LOCAL_COPY((EmptyString(parv[3])) ? who->name : parv[3]); if(strlen(comment) > (size_t) REASONLEN) comment[REASONLEN] = '\0'; if(is_override) { sendto_wallops_flags(UMODE_WALLOP, &me, "%s is overriding KICK [%s] on [%s] [%s]", get_oper_name(source_p), who->name, chptr->chname, comment); sendto_server(NULL, chptr, NOCAPS, NOCAPS, ":%s WALLOPS :%s is overriding KICK [%s] on [%s] [%s]", me.name, get_oper_name(source_p), who->name, chptr->chname, comment); } /* 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); remove_user_from_channel(msptr); rb_snprintf(text, sizeof(text), "K%s", who->id); /* we don't need to track NOREJOIN stuff unless it's our client being kicked */ if(MyClient(who) && chptr->mode.mode & MODE_NOREJOIN) channel_metadata_time_add(chptr, text, rb_current_time(), "KICKNOREJOIN"); } else if (MyClient(source_p)) sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, form_str(ERR_USERNOTINCHANNEL), user, name); return 0; }
/* * m_topic * parv[1] = channel name * parv[2] = new topic, if setting topic */ static int m_topic(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Channel *chptr = NULL; struct membership *msptr; char *p = NULL; const char *name; int operspy = 0; if((p = strchr(parv[1], ','))) *p = '\0'; name = parv[1]; if(IsOperSpy(source_p) && parv[1][0] == '!') { name++; operspy = 1; if(EmptyString(name)) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "TOPIC"); return 0; } } if(MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); chptr = find_channel(name); if(chptr == NULL) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); return 0; } /* setting topic */ if(parc > 2) { msptr = find_channel_membership(chptr, source_p); if(msptr == NULL) { sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), name); return 0; } if(MyClient(source_p) && !is_chanop_voiced(msptr) && !IsOper(source_p) && !add_channel_target(source_p, chptr)) { sendto_one(source_p, form_str(ERR_TARGCHANGE), me.name, source_p->name, chptr->chname); return 0; } if(((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || get_channel_access(source_p, chptr, msptr, MODE_ADD, NULL) >= CHFL_CHANOP) && (!MyClient(source_p) || can_send(chptr, source_p, msptr))) { char topic[TOPICLEN + 1]; char topic_info[USERHOST_REPLYLEN]; rb_strlcpy(topic, parv[2], sizeof(topic)); rb_sprintf(topic_info, "%s!%s@%s", source_p->name, source_p->username, source_p->host); if (ConfigChannel.strip_topic_colors) strip_colour(topic); set_channel_topic(chptr, topic, topic_info, rb_current_time()); sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s TOPIC %s :%s", use_id(source_p), chptr->chname, chptr->topic == NULL ? "" : chptr->topic); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, chptr->topic == NULL ? "" : chptr->topic); } else sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), get_id(&me, source_p), get_id(source_p, source_p), name); } else if(MyClient(source_p)) { if(operspy) report_operspy(source_p, "TOPIC", chptr->chname); if(!IsMember(source_p, chptr) && SecretChannel(chptr) && !operspy) { sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), name); return 0; } if(chptr->topic == NULL) sendto_one(source_p, form_str(RPL_NOTOPIC), me.name, source_p->name, name); else { sendto_one(source_p, form_str(RPL_TOPIC), me.name, source_p->name, chptr->chname, chptr->topic); sendto_one(source_p, form_str(RPL_TOPICWHOTIME), me.name, source_p->name, chptr->chname, chptr->topic_info, (unsigned long)chptr->topic_time); } } return 0; }
static int m_cmessage(int p_or_n, const char *command, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Client *target_p; struct Channel *chptr; struct membership *msptr; if(!IsFloodDone(source_p)) flood_endgrace(source_p); if((target_p = find_named_person(parv[1])) == NULL) { if(p_or_n != NOTICE) sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), parv[1]); return 0; } if((chptr = find_channel(parv[2])) == NULL) { if(p_or_n != NOTICE) sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), parv[2]); return 0; } if((msptr = find_channel_membership(chptr, source_p)) == NULL) { if(p_or_n != NOTICE) sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), chptr->chname); return 0; } if(!is_chanop_voiced(msptr)) { if(p_or_n != NOTICE) sendto_one(source_p, form_str(ERR_VOICENEEDED), me.name, source_p->name, chptr->chname); return 0; } if(!IsMember(target_p, chptr)) { if(p_or_n != NOTICE) sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, form_str(ERR_USERNOTINCHANNEL), target_p->name, chptr->chname); return 0; } if(MyClient(target_p) && (IsSetCallerId(target_p) || (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])) && !accept_message(source_p, target_p) && !IsOper(source_p)) { if (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0]) { if (p_or_n != NOTICE) sendto_one_numeric(source_p, ERR_NONONREG, form_str(ERR_NONONREG), target_p->name); return 0; } if(p_or_n != NOTICE) sendto_one_numeric(source_p, ERR_TARGUMODEG, form_str(ERR_TARGUMODEG), target_p->name); if((target_p->localClient->last_caller_id_time + ConfigFileEntry.caller_id_wait) < rb_current_time()) { if(p_or_n != NOTICE) sendto_one_numeric(source_p, RPL_TARGNOTIFY, form_str(RPL_TARGNOTIFY), target_p->name); sendto_one(target_p, form_str(RPL_UMODEGMSG), me.name, target_p->name, source_p->name, source_p->username, source_p->host); target_p->localClient->last_caller_id_time = rb_current_time(); } return 0; } if(p_or_n != NOTICE) source_p->localClient->last = rb_current_time(); sendto_anywhere(target_p, source_p, command, ":%s", parv[3]); return 0; }
/*! \brief NICK command handler (called by already registered, * locally connected clients) * * \param client_p Pointer to allocated Client struct with physical connection * to this server, i.e. with an open socket connected. * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = sender prefix * - parv[1] = nickname */ static void m_nick(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char nick[NICKLEN]; struct Client *target_p = NULL; if (parc < 2 || EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN), me.name, source_p->name); return; } /* mark end of grace period, to prevent nickflooding */ if (!IsFloodDone(source_p)) flood_endgrace(source_p); /* terminate nick to NICKLEN */ strlcpy(nick, parv[1], sizeof(nick)); /* check the nickname is ok */ if (!clean_nick_name(nick, 1)) { sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, source_p->name, nick); return; } if (find_matching_name_conf(NRESV_TYPE, nick, NULL, NULL, 0) && !IsExemptResv(source_p) && !(IsOper(source_p) && ConfigFileEntry.oper_pass_resv)) { sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, source_p->name, nick); return; } if ((target_p = find_client(nick)) == NULL) change_local_nick(client_p, source_p, nick); else if (target_p == source_p) { /* * If (target_p == source_p) the client is changing nicks between * equivalent nicknames ie: [nick] -> {nick} */ /* check the nick isnt exactly the same */ if (strcmp(target_p->name, nick)) change_local_nick(client_p, source_p, nick); } else if (IsUnknown(target_p)) { /* * if the client that has the nick isn't registered yet (nick but no * user) then drop the unregged client */ exit_client(target_p, &me, "Overridden"); change_local_nick(client_p, source_p, nick); } else sendto_one(source_p, form_str(ERR_NICKNAMEINUSE), me.name, source_p->name, nick); }
/* * * parc number of arguments ('sender' counted as one!) * parv[0] pointer to 'sender' (may point to empty string) (not used) * parv[1]..parv[parc-1] * pointers to additional parameters, this is a NULL * terminated list (parv[parc] == NULL). * * *WARNING* * Numerics are mostly error reports. If there is something * wrong with the message, just *DROP* it! Don't even think of * sending back a neat error message -- big danger of creating * a ping pong error message... */ static void do_numeric(char numeric[], struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p; struct Channel *chptr; char *t; /* current position within the buffer */ int i, tl; /* current length of presently being built string in t */ if (parc < 2 || !IsServer(source_p)) return; /* Remap low number numerics. */ if (numeric[0] == '0') numeric[0] = '1'; /* Prepare the parameter portion of the message into 'buffer'. * (Because the buffer is twice as large as the message buffer * for the socket, no overflow can occur here... ...on current * assumptions--bets are off, if these are changed --msa) */ t = buffer; for (i = 2; i < (parc - 1); i++) { tl = ircsprintf(t, " %s", parv[i]); t += tl; } ircsprintf(t," :%s", parv[parc-1]); if (((target_p = find_person(client_p, parv[1])) != NULL) || ((target_p = find_server(parv[1])) != NULL)) { if (IsMe(target_p)) { int num; /* * We shouldn't get numerics sent to us, * any numerics we do get indicate a bug somewhere.. */ /* ugh. this is here because of nick collisions. when two servers * relink, they burst each other their nicks, then perform collides. * if there is a nick collision, BOTH servers will kill their own * nicks, and BOTH will kill the other servers nick, which wont exist, * because it will have been already killed by the local server. * * unfortunately, as we cant guarantee other servers will do the * "right thing" on a nick collision, we have to keep both kills. * ergo we need to ignore ERR_NOSUCHNICK. --fl_ */ /* quick comment. This _was_ tried. i.e. assume the other servers * will do the "right thing" and kill a nick that is colliding. * unfortunately, it did not work. --Dianora */ /* Yes, a good compiler would have optimised this, but * this is probably easier to read. -db */ num = atoi(numeric); if ((num != ERR_NOSUCHNICK)) sendto_gnotice_flags(UMODE_ALL, L_ALL, me.name, &me, NULL, "*** %s(via %s) sent a %s numeric to me: %s", source_p->name, client_p->name, numeric, buffer); return; } else if (target_p->from == client_p) { /* This message changed direction (nick collision?) * ignore it. */ return; } /* csircd will send out unknown umode flag for +a (admin), drop it here. */ if ((atoi(numeric) == ERR_UMODEUNKNOWNFLAG) && MyClient(target_p)) return; /* Fake it for server hiding, if its our client */ if (ConfigServerHide.hide_servers && MyClient(target_p) && !IsOper(target_p)) sendto_one(target_p, ":%s %s %s%s", me.name, numeric, target_p->name, buffer); else sendto_one(target_p, ":%s %s %s%s", ID_or_name(source_p, target_p->from), numeric, ID_or_name(target_p, target_p->from), buffer); return; } else if ((chptr = hash_find_channel(parv[1])) != NULL) sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s %s %s %s", source_p->name, numeric, chptr->chname, buffer); }
static void do_other_who(aClient *sptr, char *mask) { int oper = IsAnOper(sptr); /* wildcard? */ #ifndef NO_FDLIST if (lifesux && !IsOper(sptr) && *mask == '*' && *(mask+1) == 0) { sendto_one(sptr, err_str(ERR_HTMDISABLED), me.name, sptr->name, "/WHO"); return; } #endif if (strchr(mask, '*') || strchr(mask, '?')) { int i = 0; /* go through all users.. */ aClient *acptr; who_flags |= WF_WILDCARD; for (acptr = client; acptr; acptr = acptr->next) { int cansee; char status[20]; char *channel; int flg; if (!IsPerson(acptr)) continue; if (!oper) { /* non-opers can only search on nick here */ if (match(mask, acptr->name)) continue; } else { /* opers can search on name, ident, virthost, ip and realhost. * Yes, I like readable if's -- Syzop. */ if (!match(mask, acptr->name) || !match(mask, acptr->user->realhost) || !match(mask, acptr->user->username)) goto matchok; if (IsHidden(acptr) && !match(mask, acptr->user->virthost)) goto matchok; if (acptr->user->ip_str && !match(mask, acptr->user->ip_str)) goto matchok; /* nothing matched... */ continue; } matchok: if ((cansee = can_see(sptr, acptr, NULL)) & WHO_CANTSEE) continue; if (WHOLIMIT && !IsAnOper(sptr) && ++i > WHOLIMIT) { sendto_one(sptr, rpl_str(ERR_WHOLIMEXCEED), me.name, sptr->name, WHOLIMIT); return; } channel = first_visible_channel(sptr, acptr, &flg); make_who_status(sptr, acptr, NULL, NULL, status, cansee); send_who_reply(sptr, acptr, channel, status, (flg & FVC_HIDDEN) ? "~" : ""); } } else { /* just a single client (no wildcards detected) */ aClient *acptr = find_client(mask, NULL); int cansee; char status[20]; char *channel; int flg; if (!acptr) return; if ((cansee = can_see(sptr, acptr, NULL)) == WHO_CANTSEE) return; channel = first_visible_channel(sptr, acptr, &flg); make_who_status(sptr, acptr, NULL, NULL, status, cansee); send_who_reply(sptr, acptr, channel, status, (flg & FVC_HIDDEN) ? "~" : ""); } }
/* * m_userip is based on m_userhost * m_userhost added by Darren Reed 13/8/91 to aid clients and reduce * the need for complicated requests like WHOIS. It returns user/host * information only (no spurious AWAY labels or channels). * Re-written by Dianora 1999 */ DLLFUNC CMD_FUNC(m_userip) { char *p; /* scratch end pointer */ char *cn; /* current name */ char *ip, ipbuf[HOSTLEN+1]; struct Client *acptr; char response[5][NICKLEN * 2 + CHANNELLEN + USERLEN + HOSTLEN + 30]; int i; /* loop counter */ if (!MyClient(sptr)) return -1; if (parc < 2) { sendto_one(sptr, rpl_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USERIP"); return 0; } /* The idea is to build up the response string out of pieces * none of this strlen() nonsense. * 5 * (NICKLEN*2+CHANNELLEN+USERLEN+HOSTLEN+30) is still << sizeof(buf) * and our ircsprintf() truncates it to fit anyway. There is * no danger of an overflow here. -Dianora */ response[0][0] = response[1][0] = response[2][0] = response[3][0] = response[4][0] = '\0'; cn = parv[1]; for (i = 0; (i < 5) && cn; i++) { if ((p = strchr(cn, ' '))) *p = '\0'; if ((acptr = find_person(cn, NULL))) { if (!(ip = GetIP(acptr))) ip = "<unknown>"; if (sptr != acptr && !IsOper(sptr) && IsHidden(acptr)) { make_virthost(acptr, GetIP(acptr), ipbuf, 0); ip = ipbuf; } ircsprintf(response[i], "%s%s=%c%s@%s", acptr->name, (IsAnOper(acptr) && (!IsHideOper(acptr) || sptr == acptr || IsAnOper(sptr))) ? "*" : "", (acptr->user->away) ? '-' : '+', acptr->user->username, ip); /* add extra fakelag (penalty) because of all the work we need to do: 1s per entry: */ sptr->since += 1; } if (p) p++; cn = p; } sendto_one(sptr, rpl_str(RPL_USERIP), me.name, parv[0], response[0], response[1], response[2], response[3], response[4]); return 0; }
/* ** m_away ** parv[0] = sender prefix ** parv[1] = away message */ static int m_away(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { char *away; char *awy2 = LOCAL_COPY(parv[1]); if(MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); if(!IsClient(source_p)) return 0; away = source_p->user->away; if(parc < 2 || !*awy2) { /* Marking as not away */ if(away) { /* we now send this only if they were away before --is */ sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s AWAY", use_id(source_p)); sendto_server(client_p, NULL, NOCAPS, CAP_TS6, ":%s AWAY", source_p->name); MyFree(away); source_p->user->away = NULL; } if(MyConnect(source_p)) sendto_one(source_p, form_str(RPL_UNAWAY), me.name, source_p->name); return 0; } /* Marking as away */ if(MyConnect(source_p) && !IsOper(source_p) && (CurrentTime - source_p->user->last_away) < ConfigFileEntry.pace_wait) { sendto_one(source_p, form_str(RPL_LOAD2HI), me.name, source_p->name, "AWAY"); return 0; } source_p->user->last_away = CurrentTime; if(strlen(awy2) > (size_t) TOPICLEN) awy2[TOPICLEN] = '\0'; /* we now send this only if they weren't away already --is */ if(!away) { sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s AWAY :%s", use_id(source_p), awy2); sendto_server(client_p, NULL, NOCAPS, CAP_TS6, ":%s AWAY :%s", source_p->name, awy2); } else MyFree(away); DupString(away, awy2); source_p->user->away = away; if(MyConnect(source_p)) sendto_one(source_p, form_str(RPL_NOWAWAY), me.name, source_p->name); return 0; }
void versionscan_handler(nick* me, int type, void** args) { nick* sender; Command* cmd; char* cargv[50]; int cargc; vspattern* v; char* p; switch (type) { case LU_PRIVMSG: case LU_SECUREMSG: /* nick */ sender=args[0]; if (!strncmp("\001VERSION", args[1], 8)) { sendnoticetouser(versionscan_nick, sender, "\001VERSION QuakeNet %s v%s.\001", VS_RNDESC, VS_VERSION); return; } cargc=splitline((char*)args[1], cargv, 50, 0); cmd=findcommandintree(versionscan_commands, cargv[0], 1); if (!cmd) { sendnoticetouser(versionscan_nick, sender, "Unknown command."); return; } if ((cmd->level & VS_AUTHED) && !IsAccount(sender)) { sendnoticetouser(versionscan_nick, sender, "Sorry, you need to be authed to use this command."); return; } if ((cmd->level & VS_OPER) && !IsOper(sender)) { sendnoticetouser(versionscan_nick, sender, "Sorry, you need to be opered to use this command."); return; } if (((cmd->level & VS_STAFF) && !IsVersionscanStaff(sender)) || ((cmd->level & VS_GLINE) && !IsVersionscanGlineAccess(sender)) || ((cmd->level & VS_ADMIN) && !IsVersionscanAdmin(sender))) { sendnoticetouser(versionscan_nick, sender, "Sorry, you do not have access to this command."); return; } if (cmd->maxparams < (cargc-1)) { /* We need to do some rejoining */ rejoinline(cargv[cmd->maxparams], cargc-(cmd->maxparams)); cargc=(cmd->maxparams)+1; } (cmd->handler)((void*)sender, cargc-1, &(cargv[1])); break; case LU_PRIVNOTICE: sender=args[0]; if (strncmp("\001VERSION ", args[1], 9)) { break; } if ((p=strchr((char *)args[1] + 9, '\001'))) { *p++='\0'; } if (versionscan_mode == VS_SCAN) { if (IsOper(sender)) { break; } for (v=vspatterns; v; v=v->next) { if (match2strings(v->pattern, (char *)args[1] + 9)) { v->hitcount++; hcount++; switch (v->action) { case VS_WARN: sendnoticetouser(versionscan_nick, sender, "%s", v->data); wcount++; break; case VS_KILL: killuser(versionscan_nick, sender, "%s", v->data); kcount++; break; case VS_GLUSER: glinebynick(sender, 3600, v->data, GLINE_ALWAYS_USER, "versionscan"); gcount++; break; case VS_GLHOST: glinebynick(sender, 3600, v->data, 0, "versionscan"); gcount++; break; default: /* oh dear, something's f****d */ break; } break; } } } else if (versionscan_mode == VS_STAT) { versionscan_addstat((char *)args[1] + 9); } break; case LU_KILLED: versionscan_nick=NULL; scheduleoneshot(time(NULL)+1, &versionscan_createfakeuser, NULL); break; } }
/* ** m_whowas ** parv[1] = nickname queried */ static int m_whowas(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Whowas *temp; int cur = 0; int max = -1, found = 0; char *p; const char *nick; char tbuf[26]; long sendq_limit; static time_t last_used = 0L; if(!IsOper(source_p)) { if((last_used + ConfigFileEntry.pace_wait_simple) > rb_current_time()) { sendto_one(source_p, form_str(RPL_LOAD2HI), me.name, source_p->name, "WHOWAS"); sendto_one(source_p, form_str(RPL_ENDOFWHOWAS), me.name, source_p->name, parv[1]); return 0; } else last_used = rb_current_time(); } if(parc > 2) max = atoi(parv[2]); #if 0 if(parc > 3) if(hunt_server(client_p, source_p, ":%s WHOWAS %s %s :%s", 3, parc, parv)) return 0; #endif if((p = strchr(parv[1], ','))) *p = '\0'; nick = parv[1]; sendq_limit = get_sendq(client_p) * 9 / 10; temp = WHOWASHASH[hash_whowas_name(nick)]; found = 0; for (; temp; temp = temp->next) { if(!irccmp(nick, temp->name)) { if(cur > 0 && rb_linebuf_len(&client_p->localClient->buf_sendq) > sendq_limit) { sendto_one(source_p, form_str(ERR_TOOMANYMATCHES), me.name, source_p->name, "WHOWAS"); break; } sendto_one(source_p, form_str(RPL_WHOWASUSER), me.name, source_p->name, temp->name, temp->username, temp->hostname, temp->realname); if (MyOper(source_p) && !EmptyString(temp->sockhost)) #if 0 sendto_one(source_p, form_str(RPL_WHOWASREAL), me.name, source_p->name, temp->name, "<untracked>", temp->sockhost); #else sendto_one_numeric(source_p, RPL_WHOISACTUALLY, form_str(RPL_WHOISACTUALLY), temp->name, temp->sockhost); #endif if (!EmptyString(temp->suser)) sendto_one_numeric(source_p, RPL_WHOISLOGGEDIN, "%s %s :was logged in as", temp->name, temp->suser); sendto_one_numeric(source_p, RPL_WHOISSERVER, form_str(RPL_WHOISSERVER), temp->name, temp->servername, rb_ctime(temp->logoff, tbuf, sizeof(tbuf))); cur++; found++; } if(max > 0 && cur >= max) break; } if(!found) sendto_one(source_p, form_str(ERR_WASNOSUCHNICK), me.name, source_p->name, nick); sendto_one(source_p, form_str(RPL_ENDOFWHOWAS), me.name, source_p->name, parv[1]); return 0; }
/* ** m_ircops() By Claudio ** Rewritten by HAMLET ** Lists online IRCOps ** */ int m_ircops(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; char *status; char buf[BUFSIZE]; int locals = 0, globals = 0; if (!IRCopsForAll && !IsPrivileged(cptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } strcpy(buf, "========================================================================================"); sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], buf); strcpy(buf, "\2Nick Status Server\2"); sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], buf); strcpy(buf, "----------------------------------------------------------------------------------------"); sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], buf); for (acptr = GlobalClientList; acptr; acptr = acptr->next) { if (!IsService(acptr) && !IsStealth(acptr) && IsAnOper(acptr) && (!IsHideOper(acptr) || IsAnOper(sptr)) ) { if (!acptr->user) continue; if (IsTechAdmin(acptr)) status = "Technical Administrator"; else if (IsNetAdmin(acptr)) status = "Network Administrator"; else if (IsSAdmin(acptr)) status = "Services Administrator"; else if (IsAdmin(acptr)) status = "Server Administrator"; else if(IsOper(acptr)) status = "Global IRC Operator"; else status = "Local IRC Operator"; /* vlinks on /IRCops * code by openglx * idea to this by Midnight_Commander */ if (acptr->user && acptr->user->vlink) { ircsprintf(buf, "\2%-29s\2 %-23s %-6s %s", acptr->name ? acptr->name : "<unknown>", status, acptr->user->away ? "(AWAY)" : "", acptr->user->vlink->name); } else { ircsprintf(buf, "\2%-29s\2 %-23s %-6s %s", acptr->name ? acptr->name : "<unknown>", status, acptr->user->away ? "(AWAY)" : "", acptr->user->server); } /* end of the vlink support code */ sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], buf); sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], "-"); if (IsOper(acptr)) globals++; else locals++; } } ircsprintf(buf, "Total: \2%d\2 IRCOp%s connected - \2%d\2 Globa%s, \2%d\2 Loca%s", globals+locals, (globals+locals) > 1 ? "s" : "", globals, globals > 1 ? "ls" : "l", locals, locals > 1 ? "ls" : "l"); sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], buf); strcpy(buf, "========================================================================================"); sendto_one(sptr, form_str(RPL_LISTS), me.name, parv[0], buf); sendto_one(sptr, form_str(RPL_ENDOFLISTS), me.name, parv[0], "IRCOPS"); return 0; }
/* * msg_client * * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC * say NOTICE must not auto reply * - pointer to command, "PRIVMSG" or "NOTICE" * - pointer to source_p source (struct Client *) * - pointer to target_p target (struct Client *) * - pointer to text * output - NONE * side effects - message given channel either chanop or voice */ static void msg_client(int p_or_n, char *command, struct Client *source_p, struct Client *target_p, char *text) { if (MyClient(source_p)) { /* reset idle time for message only if its not to self * and its not a notice */ if ((p_or_n != NOTICE) && (source_p != target_p) && source_p->user) source_p->user->last = CurrentTime; if (IsSetSSLaccept(target_p)) { #ifdef HAVE_LIBCRYPTO int fd = source_p->localClient->fd; fde_t *F = (fd > -1)? &fd_table[fd] : NULL; if (F && !F->ssl) { #endif sendto_one(source_p, form_str(source_p,ERR_SSLACCEPTONLY), me.name, source_p->name, target_p->name); return; #ifdef HAVE_LIBCRYPTO } #endif } } if (MyConnect(source_p) && (p_or_n != NOTICE) && target_p->user && target_p->user->away) sendto_one(source_p, form_str(source_p,RPL_AWAY), me.name, source_p->name, target_p->name, target_p->user->away); if (MyClient(target_p)) { if (IsSetRegAccept(target_p) && !(source_p->svsflags & FLAGS_SVS_IDENT)) { if (p_or_n != NOTICE) sendto_one(source_p, form_str(source_p,ERR_REGACCEPTONLY), me.name, source_p->name, target_p->name); return; } if (ConfigFileEntry.spam_wait && (p_or_n != NOTICE) && ((target_p->localClient->last_join_time + ConfigFileEntry.spam_wait > CurrentTime) || (target_p->localClient->last_leave_time + ConfigFileEntry.spam_wait > CurrentTime))) { sendto_anywhere(source_p, target_p, "NOTICE %s :*** I'm joining/leaving a chan, please try again in a few seconds.", source_p->name); return; } if (!IsServer(source_p) && IsSetCallerId(target_p)) { /* Here is the anti-flood bot/spambot code -db */ if (accept_message(source_p, target_p) || (source_p == target_p) || find_z_conf((char *)source_p->user->server)) { sendto_one(target_p, ":%s!%s@%s %s %s :%s", source_p->name, source_p->username, source_p->host, command, target_p->name, translate(target_p, text)); } else { /* check for accept, flag recipient incoming message */ if (p_or_n != NOTICE) sendto_anywhere(source_p, target_p, "NOTICE %s :*** I'm in +g mode (server side ignore).", source_p->name); if ((target_p->localClient->last_caller_id_time + ConfigFileEntry.caller_id_wait) < CurrentTime) { if (p_or_n != NOTICE) sendto_anywhere(source_p, target_p, "NOTICE %s :*** I've been informed you messaged me.", source_p->name); sendto_one(target_p, ":%s NOTICE %s :*** Client %s [%s@%s] is messaging you and you are +g", me.name, target_p->name, source_p->name, source_p->username, source_p->host); target_p->localClient->last_caller_id_time = CurrentTime; } /* Only so opers can watch for floods */ (void)flood_attack_client(p_or_n, source_p, target_p); } } else { /* If the client is remote, we dont perform a special check for * flooding.. as we wouldnt block their message anyway.. this means * we dont give warnings.. we then check if theyre opered * (to avoid flood warnings), lastly if theyre our client * and flooding -- fl */ if (!MyClient(source_p) || IsOper(source_p) || (MyClient(source_p) && !flood_attack_client(p_or_n, source_p, target_p))) sendto_anywhere(target_p, source_p, "%s %s :%s", command, target_p->name, translate(target_p, text)); } } else /* The target is a remote user.. same things apply -- fl */ if (!MyClient(source_p) || IsOper(source_p) || (MyClient(source_p) && !flood_attack_client(p_or_n, source_p, target_p))) sendto_anywhere(target_p, source_p, "%s %s :%s", command, target_p->name, text); return; }
/* ** m_kill ** parv[0] = sender prefix ** parv[1] = kill victim(s) - comma separated list ** parv[2] = kill path */ DLLFUNC int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; anUser *auser; char inpath[HOSTLEN * 2 + USERLEN + 5]; char *oinpath = get_client_name(cptr, FALSE); char *user, *path, *killer, *nick, *p, *s; int chasing = 0, kcount = 0; if (parc < 2 || *parv[1] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL"); return 0; } user = parv[1]; path = parv[2]; /* Either defined or NULL (parc >= 2!!) */ strlcpy(inpath, oinpath, sizeof inpath); #ifndef ROXnet if (IsServer(cptr) && (s = (char *)index(inpath, '.')) != NULL) *s = '\0'; /* Truncate at first "." */ #endif if (!IsPrivileged(cptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (IsAnOper(cptr)) { if (BadPtr(path)) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL"); return 0; } if (strlen(path) > (size_t)TOPICLEN) path[TOPICLEN] = '\0'; } if (MyClient(sptr)) user = (char *)canonize(user); for (p = NULL, nick = strtoken(&p, user, ","); nick; nick = strtoken(&p, NULL, ",")) { chasing = 0; if (!(acptr = find_client(nick, NULL))) { /* ** If the user has recently changed nick, we automaticly ** rewrite the KILL for this new nickname--this keeps ** servers in synch when nick change and kill collide */ if (!(acptr = get_history(nick, (long)KILLCHASETIMELIMIT))) { sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); continue; } sendto_one(sptr, ":%s %s %s :*** KILL changed from %s to %s", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], nick, acptr->name); chasing = 1; } if ((!MyConnect(acptr) && MyClient(cptr) && !OPCanGKill(cptr)) || (MyConnect(acptr) && MyClient(cptr) && !OPCanLKill(cptr))) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); continue; } if (IsServer(acptr) || IsMe(acptr)) { sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]); continue; } if (!IsPerson(acptr)) { /* Nick exists but user is not registered yet: IOTW "doesn't exist". -- Syzop */ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); continue; } if (IsServices(acptr) && !(IsNetAdmin(sptr) || IsULine(sptr))) { sendto_one(sptr, err_str(ERR_KILLDENY), me.name, parv[0], parv[1]); return 0; } /* From here on, the kill is probably going to be successful. */ kcount++; if (!IsServer(sptr) && (kcount > MAXKILLS)) { sendto_one(sptr, ":%s %s %s :*** Too many targets, kill list was truncated. Maximum is %d.", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], MAXKILLS); break; } if (!IsServer(cptr)) { /* ** The kill originates from this server, initialize path. ** (In which case the 'path' may contain user suplied ** explanation ...or some nasty comment, sigh... >;-) ** ** ...!operhost!oper ** ...!operhost!oper (comment) */ strlcpy(inpath, GetHost(cptr), sizeof inpath); if (kcount < 2) { /* Only check the path the first time around, or it gets appended to itself. */ if (!BadPtr(path)) { (void)ircsprintf(buf, "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path); path = buf; } else path = cptr->name; } } else if (BadPtr(path)) path = "*no-path*"; /* Bogus server sending??? */ /* ** Notify all *local* opers about the KILL (this includes the one ** originating the kill, if from this server--the special numeric ** reply message is not generated anymore). ** ** Note: "acptr->name" is used instead of "user" because we may ** have changed the target because of the nickname change. */ auser = acptr->user; sendto_snomask_normal(SNO_KILLS, "*** Notice -- Received KILL message for %s!%s@%s from %s Path: %s!%s", acptr->name, auser->username, IsHidden(acptr) ? auser->virthost : auser->realhost, parv[0], inpath, path); #if defined(USE_SYSLOG) && defined(SYSLOG_KILL) if (IsOper(sptr)) syslog(LOG_DEBUG, "KILL From %s For %s Path %s!%s", parv[0], acptr->name, inpath, path); #endif /* * By otherguy */ ircd_log (LOG_KILL, "KILL (%s) by %s(%s!%s)", make_nick_user_host (acptr->name, acptr->user->username, GetHost(acptr)), parv[0], inpath, path); /* ** And pass on the message to other servers. Note, that if KILL ** was changed, the message has to be sent to all links, also ** back. ** Suicide kills are NOT passed on --SRB */ if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr)) { sendto_serv_butone(cptr, ":%s KILL %s :%s!%s", parv[0], acptr->name, inpath, path); if (chasing && IsServer(cptr)) sendto_one(cptr, ":%s KILL %s :%s!%s", me.name, acptr->name, inpath, path); acptr->flags |= FLAGS_KILLED; } /* ** Tell the victim she/he has been zapped, but *only* if ** the victim is on current server--no sense in sending the ** notification chasing the above kill, it won't get far ** anyway (as this user don't exist there any more either) */ if (MyConnect(acptr)) sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s", parv[0], acptr->name, inpath, path); /* ** Set FLAGS_KILLED. This prevents exit_one_client from sending ** the unnecessary QUIT for this. (This flag should never be ** set in any other place) */ if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr)) (void)ircsprintf(buf2, "[%s] Local kill by %s (%s)", me.name, sptr->name, BadPtr(parv[2]) ? sptr->name : parv[2]); else { if ((killer = index(path, ' '))) { while ((killer >= path) && *killer && *killer != '!') killer--; if (!*killer) killer = path; else killer++; } else killer = path; (void)ircsprintf(buf2, "Killed (%s)", killer); } if (MyClient(sptr)) RunHook3(HOOKTYPE_LOCAL_KILL, sptr, acptr, parv[2]); if (exit_client(cptr, acptr, sptr, buf2) == FLUSH_BUFFER) return FLUSH_BUFFER; } return 0; }
/* Join a channel, ignoring forwards, +ib, etc. It notifes source_p of any errors joining * NB: this assumes a local user. */ void user_join_override(struct Client * client_p, struct Client * source_p, struct Client * target_p, const char * channels) { static char jbuf[BUFSIZE]; struct ConfItem *aconf; struct Channel *chptr = NULL; char *name; const char *modes; char *p = NULL; int flags; char *chanlist; jbuf[0] = '\0'; if(channels == NULL) return; /* rebuild the list of channels theyre supposed to be joining. */ chanlist = LOCAL_COPY(channels); for(name = rb_strtok_r(chanlist, ",", &p); name; name = rb_strtok_r(NULL, ",", &p)) { /* check the length and name of channel is ok */ if(!check_channel_name_loc(target_p, name) || (strlen(name) > LOC_CHANNELLEN)) { sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), (unsigned char *) name); continue; } /* join 0 parts all channels */ if(*name == '0' && (name[1] == ',' || name[1] == '\0') && name == chanlist) { (void) strcpy(jbuf, "0"); continue; } /* check it begins with # or & */ else if(!IsChannelName(name)) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); continue; } /* see if its resv'd */ if(!IsExemptResv(target_p) && (aconf = hash_find_resv(name))) { sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), name); /* dont update tracking for jupe exempt users, these * are likely to be spamtrap leaves */ if(IsExemptJupe(source_p)) aconf->port--; continue; } if(splitmode && !IsOper(target_p) && (*name != '&') && ConfigChannel.no_join_on_split) { sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), me.name, source_p->name, name); continue; } if(*jbuf) (void) strcat(jbuf, ","); (void) rb_strlcat(jbuf, name, sizeof(jbuf)); } for(name = rb_strtok_r(jbuf, ",", &p); name; name = rb_strtok_r(NULL, ",", &p)) { /* JOIN 0 simply parts all channels the user is in */ if(*name == '0' && !atoi(name)) { if(target_p->user->channel.head == NULL) continue; do_join_0(&me, target_p); continue; } if((chptr = find_channel(name)) != NULL) { if(IsMember(target_p, chptr)) { /* debugging is fun... */ sendto_one_notice(source_p, ":*** Notice -- %s is already in %s", target_p->name, chptr->chname); return; } add_user_to_channel(chptr, target_p, CHFL_PEON); if (chptr->mode.join_num && rb_current_time() - chptr->join_delta >= chptr->mode.join_time) { chptr->join_count = 0; chptr->join_delta = rb_current_time(); } chptr->join_count++; sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", target_p->name, target_p->username, target_p->host, chptr->chname); sendto_server(target_p, chptr, CAP_TS6, NOCAPS, ":%s JOIN %ld %s +", get_id(target_p, client_p), (long) chptr->channelts, chptr->chname); del_invite(chptr, target_p); if(chptr->topic != NULL) { sendto_one(target_p, form_str(RPL_TOPIC), me.name, target_p->name, chptr->chname, chptr->topic); sendto_one(target_p, form_str(RPL_TOPICWHOTIME), me.name, source_p->name, chptr->chname, chptr->topic_info, chptr->topic_time); } channel_member_names(chptr, target_p, 1); } else { hook_data_channel_activity hook_info; char statusmodes[5] = ""; if(!check_channel_name(name)) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, source_p->name, (unsigned char *) name); return; } /* channel name must begin with & or # */ if(!IsChannelName(name)) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, source_p->name, (unsigned char *) name); return; } /* name can't be longer than CHANNELLEN */ if(strlen(name) > CHANNELLEN) { sendto_one_notice(source_p, ":Channel name is too long"); return; } chptr = get_or_create_channel(target_p, name, NULL); flags = CHFL_CHANOP; add_user_to_channel(chptr, target_p, flags); if (chptr->mode.join_num && rb_current_time() - chptr->join_delta >= chptr->mode.join_time) { chptr->join_count = 0; chptr->join_delta = rb_current_time(); } chptr->join_count++; sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", target_p->name, target_p->username, target_p->host, chptr->chname); chptr->mode.mode |= MODE_TOPICLIMIT; chptr->mode.mode |= MODE_NOPRIVMSGS; modes = channel_modes(chptr, &me); sendto_channel_local(ALL_MEMBERS, chptr, ":%s MODE %s %s", me.name, chptr->chname, modes); strcat(statusmodes, "@"); sendto_server(target_p, chptr, CAP_TS6, NOCAPS, ":%s SJOIN %ld %s %s :%s%s", me.id, (long) chptr->channelts, chptr->chname, modes, statusmodes, get_id(target_p, client_p)); target_p->localClient->last_join_time = rb_current_time(); channel_member_names(chptr, target_p, 1); /* Call channel join hooks */ hook_info.client = source_p; hook_info.chptr = chptr; hook_info.key = chptr->mode.key; call_hook(h_channel_join, &hook_info); /* we do this to let the oper know that a channel was created, this will be * seen from the server handling the command instead of the server that * the oper is on. */ sendto_one_notice(source_p, ":*** Notice -- Creating channel %s", chptr->chname); } } return; }
/* * mo_settime - oper message handler * * parv[0] = sender prefix * parv[1] = new time * parv[2] = servername (Only used when sptr is an Oper). */ int mo_settime(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { time_t t; long dt; static char tbuf[11]; /* Must be a global oper */ if (!IsOper(sptr)) return send_reply(sptr, ERR_NOPRIVILEGES); if (parc < 2) /* verify argument count */ return need_more_params(sptr, "SETTIME"); if (parc == 2 && MyUser(sptr)) /* default to me */ parv[parc++] = cli_name(&me); t = atoi(parv[1]); /* convert the time */ /* If we're reliable_clock or if the oper specified a 0 time, use current */ if (!t || feature_bool(FEAT_RELIABLE_CLOCK)) { t = TStime(); ircd_snprintf(0, tbuf, sizeof(tbuf), "%Tu", TStime()); parv[1] = tbuf; } dt = TStime() - t; /* calculate the delta */ if (t < OLDEST_TS || dt < -9000000) /* verify value */ { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value", sptr); return 0; } /* OK, send the message off to its destination */ if (hunt_server_prio_cmd(sptr, CMD_SETTIME, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME) return 0; if (feature_bool(FEAT_RELIABLE_CLOCK)) /* don't apply settime--reliable */ { if ((dt > 600) || (dt < -600)) sendcmdto_serv_butone(&me, CMD_DESYNCH, 0, ":Bad SETTIME from %s: %Tu " "(delta %ld)", cli_name(sptr), t, dt); if (IsUser(sptr)) /* Let user know we're ignoring him */ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is not set %ld seconds " "%s: RELIABLE_CLOCK is defined", sptr, (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); } else /* tell opers about time change */ { sendto_opmask_butone(0, SNO_OLDSNO, "SETTIME from %s, clock is set %ld " "seconds %s", cli_name(sptr), (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); TSoffset -= dt; /* apply time change */ if (IsUser(sptr)) /* let user know what we did */ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is set %ld seconds %s", sptr, (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); } return 0; }
/* m_knock * parv[0] = sender prefix * parv[1] = channel * * The KNOCK command has the following syntax: * :<sender> KNOCK <channel> * * If a user is not banned from the channel they can use the KNOCK * command to have the server NOTICE the channel operators notifying * they would like to join. Helpful if the channel is invite-only, the * key is forgotten, or the channel is full (INVITE can bypass each one * of these conditions. Concept by Dianora <*****@*****.**> and written by * <anonymous> */ static int m_knock(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Channel *chptr; char *p, *name; if(MyClient(source_p) && ConfigChannel.use_knock == 0) { sendto_one(source_p, form_str(ERR_KNOCKDISABLED), me.name, source_p->name); return 0; } name = LOCAL_COPY(parv[1]); /* dont allow one knock to multiple chans */ if((p = strchr(name, ','))) *p = '\0'; if(!IsChannelName(name)) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); return 0; } if((chptr = find_channel(name)) == NULL) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); return 0; } if(IsMember(source_p, chptr)) { if(MyClient(source_p)) sendto_one(source_p, form_str(ERR_KNOCKONCHAN), me.name, source_p->name, name); return 0; } if(!((chptr->mode.mode & MODE_INVITEONLY) || (*chptr->mode.key) || (chptr->mode.limit && dlink_list_length(&chptr->members) >= (unsigned long)chptr->mode.limit))) { sendto_one_numeric(source_p, ERR_CHANOPEN, form_str(ERR_CHANOPEN), name); return 0; } /* cant knock to a +p channel */ if(HiddenChannel(chptr)) { sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN, form_str(ERR_CANNOTSENDTOCHAN), name); return 0; } if(MyClient(source_p)) { /* don't allow a knock if the user is banned */ if(is_banned(chptr, source_p, NULL, NULL, NULL) == CHFL_BAN) { sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN, form_str(ERR_CANNOTSENDTOCHAN), name); return 0; } /* local flood protection: * allow one knock per user per knock_delay * allow one knock per channel per knock_delay_channel */ if(!IsOper(source_p) && (source_p->localClient->last_knock + ConfigChannel.knock_delay) > CurrentTime) { sendto_one(source_p, form_str(ERR_TOOMANYKNOCK), me.name, source_p->name, name, "user"); return 0; } else if((chptr->last_knock + ConfigChannel.knock_delay_channel) > CurrentTime) { sendto_one(source_p, form_str(ERR_TOOMANYKNOCK), me.name, source_p->name, name, "channel"); return 0; } /* ok, we actually can send the knock, tell client */ source_p->localClient->last_knock = CurrentTime; sendto_one(source_p, form_str(RPL_KNOCKDLVR), me.name, source_p->name, name); } chptr->last_knock = CurrentTime; if(ConfigChannel.use_knock) sendto_channel_local(ONLY_CHANOPS, chptr, form_str(RPL_KNOCK), me.name, name, name, source_p->name, source_p->username, source_p->host); sendto_server(client_p, chptr, CAP_KNOCK|CAP_TS6, NOCAPS, ":%s KNOCK %s", use_id(source_p), name); sendto_server(client_p, chptr, CAP_KNOCK, CAP_TS6, ":%s KNOCK %s", source_p->name, name); return 0; }
DLLFUNC int m_rmtkl(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aTKline *tk, *next = NULL; TKLType *tkltype; char *types, *uhmask, *cmask, *p; char gmt[256], flag; int tklindex; if (!IsULine(sptr) && !(IsPerson(sptr) && IsAnOper(sptr))) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return -1; } if (IsNotParam(1)) return dumpit(sptr, rmtkl_help); if (IsNotParam(2)) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RMTKL"); sendnotice(sptr, "Type '/RMTKL' for help"); return 0; } types = parv[1]; uhmask = parv[2]; cmask = IsParam(3) ? parv[3] : NULL; /* I don't add 'q' and 'Q' here. They are different. */ if (strchr(types, '*')) types = "KzGZs"; /* check access */ if (!IsULine(sptr)) for (p = types; *p; p++) { tkltype = find_TKLType_by_flag(*p); if (!tkltype->type) continue; if (((tkltype->type & TKL_GLOBAL) && !IsOper(sptr)) || !(sptr->oflag & tkltype->oflag)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return -1; } } for (tkltype = tkl_types; tkltype->type; tkltype++) { flag = tkltype->flag; tklindex = tkl_hash(flag); if (!strchr(types, flag)) continue; for (tk = tklines[tklindex]; tk; tk = next) { next = tk->next; if (tk->type != tkltype->type) continue; if (tk->type & TKL_NICK) { /* * If it's a services hold (ie. NickServ is holding * a nick), it's better not to touch it */ if (*tk->usermask == 'H') continue; if (match(uhmask, tk->hostmask)) continue; } else if (match(uhmask, make_user_host(tk->usermask, tk->hostmask))) continue; if (cmask && _match(cmask, tk->reason)) continue; strncpyzt(gmt, asctime(gmtime((TS *)&tk->set_at)), sizeof gmt); iCstrip(gmt); if (tk->type & TKL_NICK) { sendto_snomask(SNO_TKL, "%s removed %s %s (set at %s " "- reason: %s)", sptr->name, tkltype->txt, tk->hostmask, gmt, tk->reason); ircd_log(LOG_TKL, "%s removed %s %s (set at %s " "- reason: %s)", sptr->name, tkltype->txt, tk->hostmask, gmt, tk->reason); } else { sendto_snomask(SNO_TKL, "%s removed %s %s@%s (set at " "%s - reason: %s)", sptr->name, tkltype->txt, tk->usermask, tk->hostmask, gmt, tk->reason); ircd_log(LOG_TKL, "%s removed %s %s@%s (set at " "%s - reason: %s)", sptr->name, tkltype->txt, tk->usermask, tk->hostmask, gmt, tk->reason); } if ((tk->type & TKL_GLOBAL) && flag) sendto_serv_butone_token(&me, me.name, MSG_TKL, TOK_TKL, "- %c %s %s %s", flag, tk->usermask, tk->hostmask, parv[0]); if (tk->type & TKL_SHUN) tkl_check_local_remove_shun(tk); my_tkl_del_line(tk, tklindex); } } return 0; }
/* * nick_from_server() */ static void nick_from_server(struct Client *client_p, struct Client *source_p, int parc, char *parv[], time_t newts, char *nick, char *ngecos) { int samenick = 0; if (IsServer(source_p)) { /* A server introducing a new client, change source */ source_p = make_client(client_p); dlinkAdd(source_p, &source_p->node, &global_client_list); if (parc > 2) source_p->hopcount = atoi(parv[2]); if (newts) source_p->tsinfo = newts; else { newts = source_p->tsinfo = CurrentTime; ts_warn("Remote nick %s (%s) introduced without a TS", nick, parv[0]); } strlcpy(source_p->info, parv[8], sizeof(source_p->info)); /* copy the nick in place */ strcpy(source_p->name, nick); hash_add_client(source_p); if (parc > 8) { const char *m; /* parse usermodes */ for (m = &parv[4][1]; *m; ++m) { unsigned int flag = user_modes[(unsigned char)*m]; if ((flag & UMODE_INVISIBLE) && !IsInvisible(source_p)) ++Count.invisi; if ((flag & UMODE_OPER) && !IsOper(source_p)) ++Count.oper; source_p->umodes |= flag & SEND_UMODES; } register_remote_user(source_p, parv[5], parv[6], parv[7], ngecos); return; } } else if (source_p->name[0]) { samenick = !irccmp(source_p->name, nick); /* client changing their nick */ if (!samenick) { watch_check_hash(source_p, RPL_LOGOFF); source_p->tsinfo = newts ? newts : CurrentTime; } sendto_common_channels_local(source_p, 1, ":%s!%s@%s NICK :%s", source_p->name,source_p->username, source_p->host, nick); add_history(source_p, 1); sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s NICK %s :%lu", ID(source_p), nick, (unsigned long)source_p->tsinfo); sendto_server(client_p, NULL, NOCAPS, CAP_TS6, ":%s NICK %s :%lu", source_p->name, nick, (unsigned long)source_p->tsinfo); } /* set the new nick name */ if (source_p->name[0]) hash_del_client(source_p); strcpy(source_p->name, nick); hash_add_client(source_p); if (!samenick) watch_check_hash(source_p, RPL_LOGON); }
/* * 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; }
/* * m_userhost added by Darren Reed 13/8/91 to aid clients and reduce * the need for complicated requests like WHOIS. It returns user/host * information only (no spurious AWAY labels or channels). */ static void m_userhost(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p; char buf[IRCD_BUFSIZE]; char response[NICKLEN * 2 + USERLEN + HOSTLEN + 30]; char *t; int i, n; /* loop counter */ int cur_len; int rl; cur_len = ircsprintf(buf, form_str(RPL_USERHOST), me.name, parv[0], ""); t = buf + cur_len; for(i = 0; i < 5; i++) { if(parv[i + 1] == NULL) break; if((target_p = find_person(client_p, parv[i + 1])) != NULL) { /* * Show real IP for USERHOST on yourself. * This is needed for things like mIRC, which do a server-based * lookup (USERHOST) to figure out what the clients' local IP * is. Useful for things like NAT, and dynamic dial-up users. */ /* * If a lazyleaf relayed us this request, we don't know * the clients real IP. * So, if you're on a lazyleaf, and you send a userhost * including your nick and the nick of someone not known to * the leaf, you'll get your spoofed IP. tough. */ if(MyClient(target_p) && (target_p == source_p)) { rl = ircsprintf(response, "%s%s=%c%s@%s ", target_p->name, IsOper(target_p) ? "*" : "", (target_p->away) ? '-' : '+', target_p->username, target_p->sockhost); } else { rl = ircsprintf(response, "%s%s=%c%s@%s ", target_p->name, IsOper(target_p) ? "*" : "", (target_p->away) ? '-' : '+', target_p->username, target_p->host); } if((rl + cur_len) < (IRCD_BUFSIZE - 10)) { ircsprintf(t, "%s", response); t += rl; cur_len += rl; } else break; } else if(!ServerInfo.hub && uplink && IsCapable(uplink, CAP_LL)) { t = buf; for(n = 0; n < 5; n++) { if(parv[n + 1]) { rl = ircsprintf(t, "%s ", parv[n + 1]); t += rl; } else break; } /* Relay upstream, and let hub reply */ sendto_one(uplink, ":%s USERHOST %s", parv[0], buf); return; } } sendto_one(source_p, "%s", buf); }
/* * m_topic * parv[1] = channel name * parv[2] = new topic, if setting topic */ static int m_topic(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Channel *chptr = NULL; struct membership *msptr; char *p = NULL; const char *name; int operspy = 0; if((p = strchr(parv[1], ','))) *p = '\0'; name = parv[1]; if(IsOperAuspex(source_p) && parv[1][0] == '!') { name++; operspy = 1; if(EmptyString(name)) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "TOPIC"); return 0; } } if(MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); if(!IsChannelName(name)) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); return 0; } chptr = find_channel(name); if(chptr == NULL) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), name); return 0; } /* setting topic */ if(parc > 2) { char topic_info[USERHOST_REPLYLEN]; char topic[BUFSIZE]; msptr = find_channel_membership(chptr, source_p); if(msptr == NULL) { sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), name); return 0; } if(MyClient(source_p) && !is_chanop_voiced(msptr) && !IsOper(source_p) && !add_channel_target(source_p, chptr)) { sendto_one(source_p, form_str(ERR_TARGCHANGE), me.name, source_p->name, chptr->chname); return 0; } if(MyClient(source_p) && !(((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || is_chanop(msptr)) && can_send(chptr, source_p, msptr))) { if(IsOverride(source_p)) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "%s is overriding TOPIC on [%s]", get_oper_name(source_p), chptr->chname); else { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), get_id(&me, source_p), get_id(source_p, source_p), name); return 0; } } rb_strlcpy(topic, parv[2], BUFSIZE); strip_colour(topic); rb_sprintf(topic_info, "%s!%s@%s", source_p->name, source_p->username, source_p->host); set_channel_topic(chptr, topic, topic_info, rb_current_time()); sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s TOPIC %s :%s", use_id(source_p), chptr->chname, chptr->topic == NULL ? "" : chptr->topic); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, chptr->topic == NULL ? "" : chptr->topic); } else if(MyClient(source_p)) { if(operspy) report_operspy(source_p, "TOPIC", chptr->chname); if(!IsMember(source_p, chptr) && SecretChannel(chptr) && !operspy) { sendto_one_numeric(source_p, ERR_NOTONCHANNEL, form_str(ERR_NOTONCHANNEL), name); return 0; } if(chptr->topic == NULL) sendto_one(source_p, form_str(RPL_NOTOPIC), me.name, source_p->name, name); else { sendto_one(source_p, form_str(RPL_TOPIC), me.name, source_p->name, chptr->chname, chptr->topic); sendto_one(source_p, form_str(RPL_TOPICWHOTIME), me.name, source_p->name, chptr->chname, chptr->topic_info, chptr->topic_time); } } return 0; }
/* ** m_invite ** parv[0] - sender prefix ** parv[1] - user to invite ** parv[2] - channel name ** parv[3] - invite timestamp */ static void m_invite(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL; struct Channel *chptr = NULL; struct Membership *ms = NULL; if (IsServer(source_p)) return; if (EmptyString(parv[2])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "INVITE"); return; } if (MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); if ((target_p = find_person(client_p, parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, source_p->name, parv[1]); return; } /* Do not send local channel invites to users if they are not on the * same server as the person sending the INVITE message. */ /* Possibly should be an error sent to source_p */ /* done .. there should be no problem because MyConnect(source_p) should * always be true if parse() and such is working correctly --is */ if (!MyConnect(target_p) && (*parv[2] == '&')) { if (ConfigServerHide.hide_servers == 0) sendto_one(source_p, form_str(ERR_USERNOTONSERV), me.name, source_p->name, target_p->name); return; } if ((chptr = hash_find_channel(parv[2])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, parv[2]); return; } if (MyConnect(source_p) && (ms = find_channel_link(source_p, chptr)) == NULL) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, chptr->chname); return; } if (MyConnect(source_p) && !has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, chptr->chname); return; } if ((chptr->mode.mode & MODE_OPERONLY)) { if (MyConnect(source_p) && !IsOper(source_p)) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, chptr->chname); return; } } if (IsMember(target_p, chptr)) { sendto_one(source_p, form_str(ERR_USERONCHANNEL), me.name, source_p->name, target_p->name, chptr->chname); return; } if (MyConnect(source_p)) { sendto_one(source_p, form_str(RPL_INVITING), me.name, source_p->name, target_p->name, chptr->chname); if (target_p->away) sendto_one(source_p, form_str(RPL_AWAY), me.name, source_p->name, target_p->name, target_p->away); } else if (parc > 3 && IsDigit(*parv[3])) if (atoi(parv[3]) > chptr->channelts) return; if (MyConnect(target_p)) { sendto_one(target_p, ":%s!%s@%s INVITE %s :%s", source_p->name, source_p->username, source_p->host, target_p->name, chptr->chname); if (chptr->mode.mode & MODE_INVITEONLY) { sendto_channel_local(CHFL_CHANOP|CHFL_HALFOP, 0, chptr, ":%s NOTICE %s :%s is inviting %s to %s.", me.name, chptr->chname, source_p->name, target_p->name, chptr->chname); sendto_channel_remote(source_p, client_p, CHFL_CHANOP|CHFL_HALFOP, NOCAPS, NOCAPS, chptr, ":%s NOTICE %s :%s is inviting %s to %s.", source_p->name, chptr->chname, source_p->name, target_p->name, chptr->chname); /* Add the invite if channel is +i */ add_invite(chptr, target_p); } } else if (target_p->from != client_p) sendto_one(target_p, ":%s INVITE %s %s %lu", ID_or_name(source_p, target_p->from), ID_or_name(target_p, target_p->from), chptr->chname, (unsigned long)chptr->channelts); }
/* * m_oper * parv[1] = oper name * parv[2] = oper password */ static int m_oper(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct oper_conf *oper_p; const char *name; const char *password; name = parv[1]; password = parv[2]; if(IsOper(source_p)) { sendto_one(source_p, form_str(RPL_YOUREOPER), me.name, source_p->name); send_oper_motd(source_p); return 0; } /* end the grace period */ if(!IsFloodDone(source_p)) flood_endgrace(source_p); oper_p = find_oper_conf(source_p->username, source_p->orighost, source_p->sockhost, name); if(oper_p == NULL) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)", name, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Failed OPER attempt - host mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return 0; } if(IsOperConfNeedSSL(oper_p) && !IsSSLClient(source_p)) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s) -- requires SSL/TLS", name, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "Failed OPER attempt - missing SSL/TLS by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return 0; } if (oper_p->certfp != NULL) { if (source_p->certfp == NULL || strcasecmp(source_p->certfp, oper_p->certfp)) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s) -- client certificate fingerprint mismatch", name, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "Failed OPER attempt - client certificate fingerprint mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return 0; } } if(match_oper_password(password, oper_p)) { oper_up(source_p, oper_p); ilog(L_OPERED, "OPER %s by %s!%s@%s (%s)", name, source_p->name, source_p->username, source_p->host, source_p->sockhost); return 0; } else { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)", name, source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Failed OPER attempt by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } } return 0; }
/* * m_lljoin * parv[0] = sender prefix * parv[1] = channel * parv[2] = nick ("!nick" == cjoin) * parv[3] = key (optional) * * If a lljoin is received, from our uplink, join * the requested client to the given channel, or ignore it * if there is an error. * * Ok, the way this works. Leaf client tries to join a channel, * it doesn't exist so the join does a cburst request on behalf of the * client, and aborts that join. The cburst sjoin's the channel if it * exists on the hub, and sends back an LLJOIN to the leaf. Thats where * this is now.. * */ static void ms_lljoin(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char *chname = NULL; char *nick = NULL; char *key = NULL; int flags; int i; struct Client *target_p; struct Channel *chptr; if (uplink && !IsCapable(uplink,CAP_LL)) { sendto_realops_flags(UMODE_ALL, L_ALL, "*** LLJOIN requested from non LL server %s", client_p->name); return; } chname = parv[1]; if(chname == NULL) return; nick = parv[2]; if(nick == NULL) return; if (parc >3) key = parv[3]; flags = 0; target_p = find_person(client_p, nick); if (!target_p) return; if (!MyClient(target_p)) return; if (!check_channel_name(chname, 0)) { sendto_gnotice_flags(UMODE_DEBUG, L_ALL, me.name, &me, NULL, "*** Too long or invalid channel name from %s: %s", target_p->name, chname); return; } chptr = make_channel(chname); flags = CHFL_CHANOP; if(!chptr) return; if (dlink_list_length(&chptr->members) == 0) flags = CHFL_CHANOP; else flags = 0; /* XXX in m_join.c :( */ /* check_spambot_warning(target_p, chname); */ /* They _could_ join a channel twice due to lag */ if(chptr) { if (IsMember(target_p, chptr)) /* already a member, ignore this */ return; } else { sendto_one(target_p, form_str(ERR_UNAVAILRESOURCE), me.name, nick, chptr->chname); return; } if ((i = can_join(target_p, chptr, key))) { sendto_one(target_p, form_str(i), me.name, nick, chptr->chname); return; } if ((dlink_list_length(&target_p->channel) >= ConfigChannel.max_chans_per_user) && (!IsOper(target_p) || (dlink_list_length(&target_p->channel) >= ConfigChannel.max_chans_per_user*3))) { sendto_one(target_p, form_str(ERR_TOOMANYCHANNELS), me.name, nick, chptr->chname ); return; } if (flags == CHFL_CHANOP) { chptr->channelts = CurrentTime; sendto_one(uplink, ":%s SJOIN %lu %s + :@%s", me.name, (unsigned long) chptr->channelts, chptr->chname, nick); } sendto_one(uplink, ":%s SJOIN %lu %s + :%s", me.name, (unsigned long) chptr->channelts, chptr->chname, nick); add_user_to_channel(chptr, target_p, flags, YES); sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s JOIN :%s", target_p->name, target_p->username, target_p->host, chptr->chname); if (flags & CHFL_CHANOP) { chptr->mode.mode |= MODE_TOPICLIMIT; chptr->mode.mode |= MODE_NOPRIVMSGS; sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s MODE %s +nt", me.name, chptr->chname); sendto_one(uplink, ":%s MODE %s +nt", me.name, chptr->chname); } channel_member_names(target_p, chptr, 1); }
static int build_target_list(int p_or_n, char *command, struct Client *client_p, struct Client *source_p, char *nicks_channels, char *text) { int type; char *p, *nick, *target_list, ncbuf[BUFSIZE]; struct Channel *chptr=NULL; struct Client *target_p; /* Sigh, we can't mutilate parv[1] incase we need it to send to a hub */ if (!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) { strncpy(ncbuf, nicks_channels, BUFSIZE); target_list = ncbuf; } else target_list = nicks_channels; /* skip strcpy for non-lazyleafs */ ntargets = 0; for (nick = strtoken(&p, target_list, ","); nick; nick = strtoken(&p, (char *)NULL, ",")) { char *with_prefix; /* * channels are privmsg'd a lot more than other clients, moved up * here plain old channel msg? */ if (IsChanPrefix(*nick)) { /* ignore send of local channel to a server (should not happen) */ if (IsServer(client_p) && *nick == '&') continue; if ((chptr = hash_find_channel(nick)) != NULL) { if (!duplicate_ptr(chptr)) { if (ntargets >= ConfigFileEntry.max_targets) { sendto_one(source_p, form_str(source_p,ERR_TOOMANYTARGETS), me.name, source_p->name, nick); return (1); } targets[ntargets].ptr = (void *)chptr; targets[ntargets++].type = ENTITY_CHANNEL; } } else { if (!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) return -1; else if (p_or_n != NOTICE) sendto_one(source_p, form_str(source_p,ERR_NOSUCHNICK), me.name, source_p->name, nick); } continue; } /* look for a privmsg to another client */ if ((target_p = find_person(nick)) != NULL) { if (!duplicate_ptr(target_p)) { if (ntargets >= ConfigFileEntry.max_targets) { sendto_one(source_p, form_str(source_p,ERR_TOOMANYTARGETS), me.name, source_p->name, nick); return (1); } targets[ntargets].ptr = (void *)target_p; targets[ntargets].type = ENTITY_CLIENT; targets[ntargets++].flags = 0; } continue; } /* @#channel or +#channel message ? */ type = 0; with_prefix = nick; /* allow %+@ if someone wants to do that */ for (;;) { if (*nick == '@') type |= MODE_CHANOP; else if (*nick == '%') type |= MODE_CHANOP | MODE_HALFOP; else if (*nick == '+') type |= MODE_CHANOP | MODE_HALFOP | MODE_VOICE; else break; nick++; } if (type != 0) { /* suggested by Mortiis */ if (*nick == '\0') /* if its a '\0' dump it, there is no recipient */ { sendto_one(source_p, form_str(source_p,ERR_NORECIPIENT), me.name, source_p->name, command); continue; } /* At this point, nick+1 should be a channel name i.e. #foo or &foo * if the channel is found, fine, if not report an error */ if ((chptr = hash_find_channel(nick)) != NULL) { if(!is_any_op(chptr, source_p) && !is_voiced(chptr, source_p)) { sendto_one(source_p, form_str(source_p,ERR_CHANOPRIVSNEEDED), me.name, source_p->name, with_prefix); return(-1); } if (!duplicate_ptr(chptr)) { if (ntargets >= ConfigFileEntry.max_targets) { sendto_one(source_p, form_str(source_p,ERR_TOOMANYTARGETS), me.name, source_p->name, nick); return (1); } targets[ntargets].ptr = (void *)chptr; targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL; targets[ntargets++].flags = type; } } else { if (!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) return -1; else if (p_or_n != NOTICE) sendto_one(source_p, form_str(source_p,ERR_NOSUCHNICK), me.name, source_p->name, nick); } continue; } if(IsOper(source_p) && ((*nick == '$') || strchr(nick, '@'))) { handle_opers(p_or_n, command, client_p, source_p, nick, text); } else { if (!ServerInfo.hub && (uplink != NULL) && IsCapable(uplink, CAP_LL)) return -1; else if(p_or_n != NOTICE) sendto_one(source_p, form_str(source_p,ERR_NOSUCHNICK), me.name, source_p->name, nick); } /* continue; */ } return (1); }
int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *ac; chanMember *cm; Link *lp; int shown=0, i=0, showall=IsAnOper(sptr); char status[4]; /* drop nonlocal clients */ if(!MyClient(sptr)) return 0; if(!build_searchopts(sptr, parc-1, parv+1)) return 0; /* /who was no good */ if(wsopts.gcos!=NULL && (strchr(wsopts.gcos, '?'))==NULL && (strchr(wsopts.gcos, '*'))==NULL) gchkfn=mycmp; else gchkfn=match; if(wsopts.nick!=NULL && (strchr(wsopts.nick, '?'))==NULL && (strchr(wsopts.nick, '*'))==NULL) nchkfn=mycmp; else nchkfn=match; if(wsopts.user!=NULL && (strchr(wsopts.user, '?'))==NULL && (strchr(wsopts.user, '*'))==NULL) uchkfn=mycmp; else uchkfn=match; if(wsopts.host!=NULL && (strchr(wsopts.host, '?'))==NULL && (strchr(wsopts.host, '*'))==NULL) hchkfn=mycmp; else hchkfn=match; if(wsopts.ip!=NULL && (strchr(wsopts.ip, '?'))==NULL && (strchr(wsopts.ip, '*'))==NULL) ichkfn=mycmp; else ichkfn=match; if(wsopts.channel!=NULL) { if(IsMember(sptr,wsopts.channel) && (!(wsopts.channel->mode.mode & MODE_AUDITORIUM) || is_chan_opvoice(sptr, wsopts.channel))) showall=1; else if(SecretChannel(wsopts.channel) && IsAdmin(sptr)) showall=1; else if(!SecretChannel(wsopts.channel) && IsAnOper(sptr)) showall=1; else showall=0; if(showall || !SecretChannel(wsopts.channel)) { for(cm=wsopts.channel->members; cm; cm=cm->next) { ac=cm->cptr; i=0; if(!chk_who(ac,showall)) continue; /* If we have channel flags set, verify they match */ if(wsopts.channelflags && ((cm->flags & wsopts.channelflags) == 0)) continue; /* get rid of the pidly stuff first */ /* wow, they passed it all, give them the reply... * IF they haven't reached the max, or they're an oper */ status[i++]=(ac->user->away==NULL ? 'H' : 'G'); status[i]=(IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsOper(sptr)) ? '%' : 0)); status[((status[i]) ? ++i : i)]=((cm->flags&CHFL_CHANOP) ? '@' : ((cm->flags&CHFL_VOICE) ? '+' : 0)); status[++i]=0; sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name, wsopts.channel->chname, ac->user->username, WHO_HOST(ac), WHO_SERVER(sptr, ac), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); } } sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, wsopts.channel->chname, "WHO"); return 0; } /* if (for whatever reason) they gave us a nick with no * wildcards, just do a find_person, bewm! */ else if(nchkfn==mycmp) { ac=find_person(wsopts.nick,NULL); if(ac!=NULL) { if(!chk_who(ac,1)) { sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, wsopts.host!=NULL ? wsopts.host : wsopts.nick, "WHO"); return 0; } else { status[0]=(ac->user->away==NULL ? 'H' : 'G'); status[1]=(IsAnOper(ac) ? '*' : (IsInvisible(ac) && IsAnOper(sptr) ? '%' : 0)); status[2]=0; sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name, wsopts.show_chan ? first_visible_channel(ac, sptr) : "*", ac->user->username, WHO_HOST(ac), WHO_SERVER(sptr, ac), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, wsopts.host!=NULL ? wsopts.host : wsopts.nick, "WHO"); return 0; } } sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, wsopts.host!=NULL ? wsopts.host : wsopts.nick, "WHO"); return 0; } if(wsopts.search_chan) { for(lp = sptr->user->channel; lp; lp = lp->next) { for(cm = lp->value.chptr->members; cm; cm = cm->next) { ac = cm->cptr; if(!chk_who(ac, 1)) continue; if(shown==MAXWHOREPLIES && !IsAnOper(sptr)) { sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, sptr->name, MAXWHOREPLIES, "WHO"); break; } i = 0; status[i++]=(ac->user->away==NULL ? 'H' : 'G'); status[i]=(IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsOper(sptr)) ? '%' : 0)); status[((status[i]) ? ++i : i)]=((cm->flags&CHFL_CHANOP) ? '@' : ((cm->flags&CHFL_VOICE) ? '+' : 0)); status[++i]=0; sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name, lp->value.chptr->chname, ac->user->username, WHO_HOST(ac),WHO_SERVER(sptr, ac), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); shown++; } } } else { for(ac=client;ac;ac=ac->next) { if(!chk_who(ac,showall)) continue; /* wow, they passed it all, give them the reply... * IF they haven't reached the max, or they're an oper */ if(shown==MAXWHOREPLIES && !IsAnOper(sptr)) { sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, sptr->name, MAXWHOREPLIES, "WHO"); break; /* break out of loop so we can send end of who */ } status[0]=(ac->user->away==NULL ? 'H' : 'G'); status[1]=(IsAnOper(ac) ? '*' : (IsInvisible(ac) && IsAnOper(sptr) ? '%' : 0)); status[2]=0; sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name, wsopts.show_chan ? first_visible_channel(ac, sptr) : "*", ac->user->username, WHO_HOST(ac), WHO_SERVER(sptr, ac), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); shown++; } } sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, (wsopts.host!=NULL ? wsopts.host : (wsopts.nick!=NULL ? wsopts.nick : (wsopts.user!=NULL ? wsopts.user : (wsopts.gcos!=NULL ? wsopts.gcos : (wsopts.server!=NULL ? wsopts.server->name : "*"))))), "WHO"); return 0; }
int can_send(aClient *cptr, aChannel *chptr, char *msgtext, int notice) { Membership *lp; int member; /* * #0000053 by |savage|, speedup */ if (!MyClient(cptr)) { if (IsClient(cptr)) { /* channelmode +mu is a special case.. sux!. -- Syzop */ lp = find_membership_link(cptr->user->channel, chptr); if ((chptr->mode.mode & MODE_MODERATED) && (chptr->mode.mode & MODE_AUDITORIUM) && !IsOper(cptr) && (!lp || !(lp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANPROT))) && !is_irc_banned(chptr)) { sendto_chmodemucrap(cptr, chptr, msgtext); return (CANNOT_SEND_MODERATED); } } return 0; } if (chptr->mode.mode & MODE_NOCOLOR) { /* A bit faster */ char *c; for (c = msgtext; *c; c++) { if (*c == 3 || *c == 27 || *c == 4 || *c == 22) /* mirc color, ansi, rgb, reverse */ return (CANNOT_SEND_NOCOLOR); } } member = IsMember(cptr, chptr); if (chptr->mode.mode & MODE_NOPRIVMSGS && !member) return (CANNOT_SEND_NOPRIVMSGS); lp = find_membership_link(cptr->user->channel, chptr); if ((chptr->mode.mode & MODE_MODREG) && !op_can_override(cptr) && !IsRegNick(cptr) && (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER | CHFL_HALFOP | CHFL_CHANPROT)))) return CANNOT_SEND_MODREG; if (chptr->mode.mode & MODE_MODERATED && !op_can_override(cptr) && (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER | CHFL_HALFOP | CHFL_CHANPROT)))) { if ((chptr->mode.mode & MODE_AUDITORIUM) && !is_irc_banned(chptr) && !is_banned(cptr, chptr, BANCHK_MSG)) sendto_chmodemucrap(cptr, chptr, msgtext); return (CANNOT_SEND_MODERATED); } if (chptr->mode.mode & MODE_NOCTCP && (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_CHANOWNER | CHFL_CHANPROT)))) if (msgtext[0] == 1 && strncmp(&msgtext[1], "ACTION ", 7)) return (CANNOT_SEND_NOCTCP); #ifdef EXTCMODE if (notice && (chptr->mode.extmode & EXTMODE_NONOTICE) && (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_CHANOWNER | CHFL_CHANPROT)))) return (CANNOT_SEND_NOTICE); #endif /* Makes opers able to talk thru bans -Stskeeps suggested by The_Cat */ if (IsOper(cptr)) return 0; if ((!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER | CHFL_HALFOP | CHFL_CHANPROT))) && MyClient(cptr) && is_banned(cptr, chptr, BANCHK_MSG)) return (CANNOT_SEND_BAN); return 0; }