static void ms_pong(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p; const char *origin, *destination; if(parc < 2 || *parv[1] == '\0') { sendto_one(source_p, form_str(ERR_NOORIGIN), me.name, parv[0]); return; } origin = parv[1]; destination = parv[2]; /* Now attempt to route the PONG, comstud pointed out routable PING * is used for SPING. routable PING should also probably be left in * -Dianora * That being the case, we will route, but only for registered clients (a * case can be made to allow them only from servers). -Shadowfax */ if(!EmptyString(destination) && !match(destination, me.name) && irccmp(destination, me.id)) { if((target_p = find_client(destination)) || (target_p = find_server(destination))) sendto_one(target_p, ":%s PONG %s %s", parv[0], origin, destination); else { sendto_one(source_p, form_str(ERR_NOSUCHSERVER), me.name, parv[0], destination); return; } } else if(!HasSentEob(source_p)) server_eob(source_p); }
/* ** ms_eob ** parv[0] = sender prefix ** parv[1] = opt. comma separated list of SIDs for which this EOB is ** also valid */ static int ms_eob(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { char *copy, *state, *id; struct Client *target_p; int act = 0; if(!HasSentEob(source_p)) { if(MyConnect(source_p)) { sendto_realops_flags(UMODE_ALL, L_ALL, "End of burst from %s (%d seconds)", source_p->name, (signed int)(rb_current_time() - source_p->localClient->firsttime)); sendto_one(source_p, ":%s EOBACK", me.id); } act = 1; SetEob(source_p); eob_count++; } if(parc > 1 && *parv[1] != '\0') { copy = LOCAL_COPY(parv[1]); for(id = rb_strtok_r(copy, ",", &state); id != NULL; id = rb_strtok_r(NULL, ",", &state)) { target_p = find_id(id); if(target_p != NULL && IsServer(target_p) && target_p->from == client_p && !HasSentEob(target_p)) { SetEob(target_p); eob_count++; act = 1; } } } if(!act) return 0; sendto_server(client_p, NULL, CAP_IRCNET, NOCAPS, ":%s EOB%s%s", source_p->id, parc > 1 ? " :" : "", parc > 1 ? parv[1] : ""); return 0; }
static void h_gcn_new_remote_user(struct Client *source_p) { if (!HasSentEob(source_p->servptr)) return; sendto_realops_snomask_from(snomask_modes['F'], L_ALL, source_p->servptr, "Client connecting: %s (%s@%s) [%s] {%s} [%s]", source_p->name, source_p->username, source_p->orighost, show_ip(NULL, source_p) ? source_p->sockhost : "255.255.255.255", "?", source_p->info); }
/* m_tb() * * parv[1] - channel * parv[2] - topic ts * parv[3] - optional topicwho/topic * parv[4] - topic */ static int ms_tb(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Channel *chptr; const char *newtopic; const char *newtopicwho; time_t newtopicts; struct Client *fakesource_p; chptr = find_channel(parv[1]); if(chptr == NULL) return 0; newtopicts = atol(parv[2]); /* Hide connecting server on netburst -- jilles */ if (ConfigServerHide.flatten_links && !HasSentEob(source_p)) fakesource_p = &me; else fakesource_p = source_p; if(parc == 5) { newtopic = parv[4]; newtopicwho = parv[3]; } else { newtopic = parv[3]; newtopicwho = fakesource_p->name; } if (EmptyString(newtopic)) return 0; if(chptr->topic == NULL || chptr->topic_time > newtopicts) { /* its possible the topicts is a few seconds out on some * servers, due to lag when propagating it, so if theyre the * same topic just drop the message --fl */ if(chptr->topic != NULL && strcmp(chptr->topic, newtopic) == 0) return 0; set_channel_topic(chptr, newtopic, newtopicwho, newtopicts); sendto_channel_local(ALL_MEMBERS, chptr, ":%s TOPIC %s :%s", fakesource_p->name, chptr->chname, newtopic); sendto_server(client_p, chptr, CAP_TB|CAP_TS6, NOCAPS, ":%s TB %s %ld %s%s:%s", use_id(source_p), chptr->chname, (long) chptr->topic_time, ConfigChannel.burst_topicwho ? chptr->topic_info : "", ConfigChannel.burst_topicwho ? " " : "", chptr->topic); } return 0; }
static void h_sgo_umode_changed(void *vdata) { hook_data_umode_changed *data = (hook_data_umode_changed *)vdata; struct Client *source_p = data->client; if (MyConnect(source_p) || !HasSentEob(source_p->servptr)) return; if (!(data->oldumodes & UMODE_OPER) && IsOper(source_p)) sendto_realops_snomask_from(SNO_GENERAL, L_ALL, source_p->servptr, "%s (%s@%s) is now an operator", source_p->name, source_p->username, source_p->host); }
static int ms_pong(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Client *target_p; const char *destination; destination = parv[2]; source_p->flags &= ~FLAGS_PINGSENT; /* Now attempt to route the PONG, comstud pointed out routable PING * is used for SPING. routable PING should also probably be left in * -Dianora * That being the case, we will route, but only for registered clients (a * case can be made to allow them only from servers). -Shadowfax */ if(!EmptyString(destination) && !match(destination, me.name) && irccmp(destination, me.id)) { if((target_p = find_client(destination)) || (target_p = find_server(NULL, destination))) sendto_one(target_p, ":%s PONG %s %s", get_id(source_p, target_p), parv[1], get_id(target_p, target_p)); else { if(!IsDigit(*destination)) sendto_one_numeric(source_p, ERR_NOSUCHSERVER, form_str(ERR_NOSUCHSERVER), destination); return 0; } } /* destination is us, emulate EOB */ if(IsServer(source_p) && !HasSentEob(source_p)) { if(MyConnect(source_p)) sendto_realops_snomask(SNO_GENERAL, L_ALL, "End of burst (emulated) from %s (%d seconds)", source_p->name, (signed int) (CurrentTime - source_p->localClient->firsttime)); SetEob(source_p); eob_count++; call_hook(h_server_eob, source_p); } return 0; }
static void h_gcn_client_exit(hook_data_client_exit *hdata) { struct Client *source_p; source_p = hdata->target; if (MyConnect(source_p) || !IsClient(source_p)) return; if (!HasSentEob(source_p->servptr)) return; sendto_realops_snomask_from(snomask_modes['F'], L_ALL, source_p->servptr, "Client exiting: %s (%s@%s) [%s] [%s]", source_p->name, source_p->username, source_p->host, hdata->comment, show_ip(NULL, source_p) ? source_p->sockhost : "255.255.255.255"); }
/* ms_ban() * * parv[1] - type * parv[2] - username mask or * * parv[3] - hostname mask * parv[4] - creation TS * parv[5] - duration (relative to creation) * parv[6] - lifetime (relative to creation) * parv[7] - oper or * * parv[8] - reason (possibly with |operreason) */ static int ms_ban(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { rb_dlink_node *ptr; struct ConfItem *aconf; unsigned int ntype; const char *oper, *stype; time_t now, created, hold, lifetime; char *p; int act; int valid; now = rb_current_time(); if (strlen(parv[1]) != 1) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Unknown BAN type %s from %s", parv[1], source_p->name); return 0; } switch (parv[1][0]) { case 'K': ntype = CONF_KILL; stype = "K-Line"; break; case 'X': ntype = CONF_XLINE; stype = "X-Line"; break; case 'R': ntype = IsChannelName(parv[3]) ? CONF_RESV_CHANNEL : CONF_RESV_NICK; stype = "RESV"; break; default: sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Unknown BAN type %s from %s", parv[1], source_p->name); return 0; } created = atol(parv[4]); hold = created + atoi(parv[5]); lifetime = created + atoi(parv[6]); if (!strcmp(parv[7], "*")) oper = IsServer(source_p) ? source_p->name : get_oper_name(source_p); else oper = parv[7]; ptr = find_prop_ban(ntype, parv[2], parv[3]); if (ptr != NULL) { /* We already know about this ban mask. */ aconf = ptr->data; if (aconf->created > created || (aconf->created == created && aconf->lifetime >= lifetime)) { if (IsPerson(source_p)) sendto_one_notice(source_p, ":Your %s [%s%s%s] has been superseded", stype, aconf->user ? aconf->user : "", aconf->user ? "@" : "", aconf->host); return 0; } /* act indicates if something happened (from the oper's * point of view). This is the case if the ban was * previously active (not deleted) or if the new ban * is not a removal and not already expired. */ act = !(aconf->status & CONF_ILLEGAL) || (hold != created && hold > now); if (lifetime > aconf->lifetime) aconf->lifetime = lifetime; /* already expired, hmm */ if (aconf->lifetime <= now) return 0; /* Deactivate, it will be reactivated later if appropriate. */ deactivate_conf(aconf, ptr, now); rb_free(aconf->user); aconf->user = NULL; rb_free(aconf->host); aconf->host = NULL; operhash_delete(aconf->info.oper); aconf->info.oper = NULL; rb_free(aconf->passwd); aconf->passwd = NULL; rb_free(aconf->spasswd); aconf->spasswd = NULL; } else { /* New ban mask. */ aconf = make_conf(); aconf->status = CONF_ILLEGAL | ntype; aconf->lifetime = lifetime; rb_dlinkAddAlloc(aconf, &prop_bans); act = hold != created && hold > now; } aconf->flags &= ~CONF_FLAGS_MYOPER; aconf->flags |= CONF_FLAGS_TEMPORARY; aconf->user = ntype == CONF_KILL ? rb_strdup(parv[2]) : NULL; aconf->host = rb_strdup(parv[3]); aconf->info.oper = operhash_add(oper); aconf->created = created; aconf->hold = hold; if (ntype != CONF_KILL || (p = strchr(parv[parc - 1], '|')) == NULL) aconf->passwd = rb_strdup(parv[parc - 1]); else { aconf->passwd = rb_strndup(parv[parc - 1], p - parv[parc - 1] + 1); aconf->spasswd = rb_strdup(p + 1); } /* The ban is fully filled in and in the prop_bans list * but still deactivated. Now determine if it should be activated * and send the server notices. */ /* We only reject *@* and the like here. * Otherwise malformed bans are fairly harmless and can be removed. */ switch (ntype) { case CONF_KILL: valid = valid_wild_card(aconf->user, aconf->host); break; case CONF_RESV_CHANNEL: valid = 1; break; default: valid = valid_wild_card_simple(aconf->host); break; } if (act && hold != created && !valid) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "Ignoring global %d min. %s from %s%s%s for [%s%s%s]: too few non-wildcard characters", (int)((hold - now) / 60), stype, IsServer(source_p) ? source_p->name : get_oper_name(source_p), strcmp(parv[7], "*") ? " on behalf of " : "", strcmp(parv[7], "*") ? parv[7] : "", aconf->user ? aconf->user : "", aconf->user ? "@" : "", aconf->host); if(IsPerson(source_p)) sendto_one_notice(source_p, ":Your %s [%s%s%s] has too few non-wildcard characters", stype, aconf->user ? aconf->user : "", aconf->user ? "@" : "", aconf->host); /* Propagate it, but do not apply it locally. */ } else if (act && hold != created) { /* Keep the notices in sync with modules/m_kline.c etc. */ sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s added global %d min. %s%s%s for [%s%s%s] [%s]", IsServer(source_p) ? source_p->name : get_oper_name(source_p), (int)((hold - now) / 60), stype, strcmp(parv[7], "*") ? " from " : "", strcmp(parv[7], "*") ? parv[7] : "", aconf->user ? aconf->user : "", aconf->user ? "@" : "", aconf->host, parv[parc - 1]); ilog(L_KLINE, "%s %s %d %s%s%s %s", parv[1], IsServer(source_p) ? source_p->name : get_oper_name(source_p), (int)((hold - now) / 60), aconf->user ? aconf->user : "", aconf->user ? " " : "", aconf->host, parv[parc - 1]); aconf->status &= ~CONF_ILLEGAL; } else if (act) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s has removed the global %s for: [%s%s%s]%s%s", IsServer(source_p) ? source_p->name : get_oper_name(source_p), stype, aconf->user ? aconf->user : "", aconf->user ? "@" : "", aconf->host, strcmp(parv[7], "*") ? " on behalf of " : "", strcmp(parv[7], "*") ? parv[7] : ""); ilog(L_KLINE, "U%s %s %s%s %s", parv[1], IsServer(source_p) ? source_p->name : get_oper_name(source_p), aconf->user ? aconf->user : "", aconf->user ? " " : "", aconf->host); } /* If CONF_ILLEGAL is still set at this point, remove entries from the * reject cache (for klines and xlines). * If CONF_ILLEGAL is not set, add the ban to the type-specific data * structure and take action on matched clients/channels. */ switch (ntype) { case CONF_KILL: if (aconf->status & CONF_ILLEGAL) remove_reject_mask(aconf->user, aconf->host); else { add_conf_by_address(aconf->host, CONF_KILL, aconf->user, NULL, aconf); if(ConfigFileEntry.kline_delay || (IsServer(source_p) && !HasSentEob(source_p))) { if(kline_queued == 0) { rb_event_addonce("check_klines", check_klines_event, NULL, ConfigFileEntry.kline_delay ? ConfigFileEntry.kline_delay : 1); kline_queued = 1; } } else check_klines(); } break; case CONF_XLINE: if (aconf->status & CONF_ILLEGAL) remove_reject_mask(aconf->host, NULL); else { rb_dlinkAddAlloc(aconf, &xline_conf_list); check_xlines(); } break; case CONF_RESV_CHANNEL: if (!(aconf->status & CONF_ILLEGAL)) { add_to_resv_hash(aconf->host, aconf); resv_chan_forcepart(aconf->host, aconf->passwd, hold - now); } break; case CONF_RESV_NICK: if (!(aconf->status & CONF_ILLEGAL)) rb_dlinkAddAlloc(aconf, &resv_conf_list); break; } sendto_server(client_p, NULL, CAP_BAN|CAP_TS6, NOCAPS, ":%s BAN %s %s %s %s %s %s %s :%s", source_p->id, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[parc - 1]); return 0; }
static int ms_bmask(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { static char modebuf[BUFSIZE]; static char parabuf[BUFSIZE]; struct Channel *chptr; rb_dlink_list *banlist; const char *s; char *t; char *mbuf; char *pbuf; long mode_type; int mlen; int plen = 0; int tlen; int arglen; int modecount = 0; int needcap = NOCAPS; int mems; struct Client *fakesource_p; if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2])) return 0; if((chptr = find_channel(parv[2])) == NULL) return 0; /* TS is higher, drop it. */ if(atol(parv[1]) > chptr->channelts) return 0; switch (parv[3][0]) { case 'b': banlist = &chptr->banlist; mode_type = CHFL_BAN; mems = ALL_MEMBERS; break; case 'e': banlist = &chptr->exceptlist; mode_type = CHFL_EXCEPTION; needcap = CAP_EX; mems = ONLY_HALFOPSANDUP; break; case 'I': banlist = &chptr->invexlist; mode_type = CHFL_INVEX; needcap = CAP_IE; mems = ONLY_HALFOPSANDUP; break; case 'Z': banlist = &chptr->quietlist; mode_type = CHFL_QUIET; mems = ALL_MEMBERS; break; /* XXX should use encap? */ case 'y': banlist = &chptr->censorlist; mode_type = CHFL_CENSOR; mems = ALL_MEMBERS; break; /* maybe we should just blindly propagate this? */ default: return 0; } parabuf[0] = '\0'; s = LOCAL_COPY(parv[4]); /* Hide connecting server on netburst -- jilles */ if (ConfigServerHide.flatten_links && !HasSentEob(source_p)) fakesource_p = &me; else fakesource_p = source_p; mlen = rb_sprintf(modebuf, ":%s MODE %s +", fakesource_p->name, chptr->chname); mbuf = modebuf + mlen; pbuf = parabuf; while(*s == ' ') s++; /* next char isnt a space, point t to the next one */ if((t = strchr(s, ' ')) != NULL) { *t++ = '\0'; /* double spaces will break the parser */ while(*t == ' ') t++; } /* couldve skipped spaces and got nothing.. */ while(!EmptyString(s)) { /* ban with a leading ':' -- this will break the protocol */ if(*s == ':') goto nextban; tlen = strlen(s); /* I dont even want to begin parsing this.. */ if(tlen > MODEBUFLEN) break; if(add_id(fakesource_p, chptr, s, banlist, mode_type)) { /* this new one wont fit.. */ if(mlen + MAXMODEPARAMS + plen + tlen > BUFSIZE - 5 || modecount >= MAXMODEPARAMS) { *mbuf = '\0'; *(pbuf - 1) = '\0'; sendto_channel_local(mems, chptr, "%s %s", modebuf, parabuf); sendto_server(client_p, chptr, needcap, CAP_TS6, "%s %s", modebuf, parabuf); mbuf = modebuf + mlen; pbuf = parabuf; plen = modecount = 0; } *mbuf++ = parv[3][0]; arglen = rb_sprintf(pbuf, "%s ", s); pbuf += arglen; plen += arglen; modecount++; } nextban: s = t; if(s != NULL) { if((t = strchr(s, ' ')) != NULL) { *t++ = '\0'; while(*t == ' ') t++; } } } if(modecount) { *mbuf = '\0'; *(pbuf - 1) = '\0'; sendto_channel_local(mems, chptr, "%s %s", modebuf, parabuf); sendto_server(client_p, chptr, needcap, CAP_TS6, "%s %s", modebuf, parabuf); } sendto_server(client_p, chptr, CAP_TS6 | needcap, NOCAPS, ":%s BMASK %ld %s %s :%s", source_p->id, (long) chptr->channelts, chptr->chname, parv[3], parv[4]); return 0; }
/* ms_etb() * * parv[1] - channel ts * parv[2] - channel * parv[3] - topic ts * parv[4] - topicwho * parv[5] - topic */ static int ms_etb(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct Channel *chptr; const char *newtopic; const char *newtopicwho; time_t channelts, newtopicts; struct Client *fakesource_p, *source_server_p; int textchange, can_use_tb, member; channelts = atol(parv[1]); chptr = find_channel(parv[2]); if(chptr == NULL) return 0; newtopicts = atol(parv[3]); /* Hide connecting server on netburst -- jilles */ if (IsServer(source_p) && ConfigServerHide.flatten_links && !HasSentEob(source_p)) fakesource_p = &me; else fakesource_p = source_p; newtopicwho = parv[4]; newtopic = parv[parc - 1]; if(chptr->topic == NULL || chptr->channelts > channelts || (chptr->channelts == channelts && chptr->topic_time < newtopicts)) { textchange = chptr->topic == NULL || strcmp(chptr->topic, newtopic); can_use_tb = textchange && !EmptyString(newtopic) && (chptr->topic == NULL || chptr->topic_time > newtopicts); set_channel_topic(chptr, newtopic, newtopicwho, newtopicts); newtopic = chptr->topic ? chptr->topic : ""; if (chptr->topic_info) newtopicwho = chptr->topic_info; /* Do not send a textually identical topic to clients, * but do propagate the new topicts/topicwho to servers. */ if(textchange) { if (IsPerson(fakesource_p)) sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s TOPIC %s :%s", fakesource_p->name, fakesource_p->username, fakesource_p->host, chptr->chname, newtopic); else sendto_channel_local(ALL_MEMBERS, chptr, ":%s TOPIC %s :%s", fakesource_p->name, chptr->chname, newtopic); } /* Propagate channelts as given, because an older channelts * forces any change. */ sendto_server(client_p, chptr, CAP_EOPMOD|CAP_TS6, NOCAPS, ":%s ETB %ld %s %ld %s :%s", use_id(source_p), (long)channelts, chptr->chname, (long)newtopicts, newtopicwho, newtopic); source_server_p = IsServer(source_p) ? source_p : source_p->servptr; if (can_use_tb) sendto_server(client_p, chptr, CAP_TB|CAP_TS6, CAP_EOPMOD, ":%s TB %s %ld %s :%s", use_id(source_server_p), chptr->chname, (long)newtopicts, newtopicwho, newtopic); else if (IsPerson(source_p) && textchange) { member = IsMember(source_p, chptr); if (!member) sendto_server(client_p, chptr, CAP_TS6, CAP_EOPMOD, ":%s SJOIN %ld %s + :@%s", use_id(source_server_p), (long)chptr->channelts, chptr->chname, use_id(source_p)); if (EmptyString(newtopic) || newtopicts >= rb_current_time() - 60) sendto_server(client_p, chptr, CAP_TS6, CAP_EOPMOD, ":%s TOPIC %s :%s", use_id(source_p), chptr->chname, newtopic); else { sendto_server(client_p, chptr, CAP_TS6, CAP_EOPMOD, ":%s TOPIC %s :%s", use_id(source_p), chptr->chname, ""); sendto_server(client_p, chptr, CAP_TB|CAP_TS6, CAP_EOPMOD, ":%s TB %s %ld %s :%s", use_id(source_server_p), chptr->chname, (long)newtopicts, newtopicwho, newtopic); } if (!member) sendto_server(client_p, chptr, CAP_TS6, CAP_EOPMOD, ":%s PART %s :Topic set for %s", use_id(source_p), chptr->chname, newtopicwho); } else if (textchange) { /* Should not send :server ETB if not all servers * support EOPMOD. */ sendto_server(client_p, chptr, CAP_TS6, CAP_EOPMOD, ":%s NOTICE %s :*** Notice -- Dropping topic change for %s", me.id, chptr->chname, chptr->chname); } } return 0; }