/* * ms_tmode() * * inputs - parv[0] = UID * parv[1] = TS * parv[2] = channel name * parv[3] = modestring */ static void ms_tmode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; struct Membership *member = NULL; if ((chptr = hash_find_channel(parv[2])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), ID_or_name(&me, client_p), ID_or_name(source_p, client_p), parv[2]); return; } if (atol(parv[1]) > chptr->channelts) return; if (IsServer(source_p)) set_channel_mode(client_p, source_p, chptr, NULL, parc - 3, parv + 3, chptr->chname); else { member = find_channel_link(source_p, chptr); /* XXX are we sure we just want to bail here? */ if (has_member_flags(member, CHFL_DEOPPED)) return; set_channel_mode(client_p, source_p, chptr, member, parc - 3, parv + 3, chptr->chname); } }
static void ms_cburst(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char *name; char *nick; const char *key; struct Channel *chptr; if (parc < 2 || *parv[1] == '\0' ) return; name = parv[1]; if (parc > 2) nick = parv[2]; else nick = NULL; if (parc > 3) key = parv[3]; else key = ""; #ifdef DEBUGLL sendto_realops_flags(UMODE_ALL, L_ALL, "CBURST called by %s for %s %s %s", client_p->name, name, nick ? nick : "", key ? key : ""); #endif if ((chptr = hash_find_channel(name)) == NULL) { if ((!nick) || (nick && *nick != '!')) { chptr = get_or_create_channel(source_p, name, NULL); chptr->channelts = (time_t)(-1); /* highest possible TS so its always * over-ruled */ } else if(nick && *nick=='!') { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, nick+1, name); return; } } if (IsCapable(client_p,CAP_LL)) { burst_channel(client_p,chptr); if (nick) sendto_one(client_p, ":%s LLJOIN %s %s %s", me.name, name, nick, key); } else { sendto_realops_flags(UMODE_ALL, L_ALL, "*** CBURST request received from non LL capable server! [%s]", client_p->name); } }
/* * add_to_channel_hash_table */ void add_to_channel_hash_table(const char* name, struct Channel* chptr) { struct Channel *achptr; unsigned int hashv; assert(0 != name); assert(0 != chptr); if ((achptr = hash_find_channel(name, NULL))) { sendto_ops_flag(UMODE_DEBUG, "WTF: Already got %s in hash table but something tried to add it again, dumping core", name); logprintf(L_WARN, "WTF: Already got %s in hash table but something tried to add it again, dumping core", name); flush_connections(0); abort(); } hashv = hash_channel_name(name); if ((channelTable[hashv].links == 0) && (channelTable[hashv].list != NULL)) { sendto_ops_flag(UMODE_DEBUG, "WTF: channelTable[%d].links == 0 but list not NULL. Whacking list...", hashv); logprintf(L_WARN, "WTF: channelTable[%d].links == 0 but list not NULL. Whacking list...", hashv); channelTable[hashv].list = NULL; } chptr->hnextch = (struct Channel*) channelTable[hashv].list; channelTable[hashv].list = (void*) chptr; ++channelTable[hashv].links; ++channelTable[hashv].hits; }
/* * list_named_channel * inputs - pointer to client requesting list * output - 0/1 * side effects - list all channels to source_p */ static int list_named_channel(struct Client *source_p,char *name) { struct Channel *chptr; char id_and_topic[TOPICLEN+NICKLEN+6]; /* <!!>, space and null */ char *p; #ifdef VCHANS dlink_node *ptr; struct Channel *root_chptr; struct Channel *tmpchptr; #endif sendto_one(source_p, form_str(source_p,RPL_LISTSTART), me.name, source_p->name); while (*name == ',') name++; if ((p = strchr(name,',')) != NULL) *p = '\0'; if (!*name) return; chptr = hash_find_channel(name); if (chptr == NULL) { sendto_one(source_p, form_str(source_p,ERR_NOSUCHNICK),me.name, source_p->name, name); sendto_one(source_p, form_str(source_p,RPL_LISTEND), me.name, source_p->name); return 0; } #ifdef VCHANS if (HasVchans(chptr)) ircsprintf(id_and_topic, "<!%s> %s", pick_vchan_id(chptr), chptr->topic == NULL ? "" : chptr->topic ); else #endif ircsprintf(id_and_topic, "%s", chptr->topic == NULL ? "" : chptr->topic); if (ShowChannel(source_p, chptr)) sendto_one(source_p, form_str(source_p,RPL_LIST), me.name, source_p->name, chptr->chname, chptr->users, id_and_topic); /* Deal with subvchans */ #ifdef VCHANS for (ptr = chptr->vchan_list.head; ptr; ptr = ptr->next) { tmpchptr = ptr->data; if (ShowChannel(source_p, tmpchptr)) { root_chptr = find_bchan(tmpchptr); ircsprintf(id_and_topic, "<!%s> %s", pick_vchan_id(tmpchptr), tmpchptr->topic == NULL ? "" : chptr->topic); sendto_one(source_p, form_str(source_p,RPL_LIST), me.name, source_p->name, root_chptr->chname, tmpchptr->users, id_and_topic); } } #endif sendto_one(source_p, form_str(source_p,RPL_LISTEND), me.name, source_p->name); return 0; }
/* * m_mode - MODE command handler * parv[0] - sender * parv[1] - channel */ int m_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel* chptr; static char modebuf[MODEBUFLEN]; static char parabuf[MODEBUFLEN]; char *mename = me.name; if(sptr->user && sptr->user->vlink) mename = sptr->user->vlink->name; /* Now, try to find the channel in question */ if (parc > 1) { if( IsChanPrefix(parv[1][0]) ) { /* Don't do any of this stuff at all * unless it looks like a channel name */ if (!check_channel_name(parv[1])) { sendto_one(sptr, form_str(ERR_BADCHANNAME), mename, parv[0], (unsigned char *)parv[1]); return 0; } chptr = hash_find_channel(parv[1], NullChn); if (!chptr) return user_mode(cptr, sptr, parc, parv); } else { /* if here, it has to be a non-channel name */ return user_mode(cptr, sptr, parc, parv); } } else { sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS), mename, parv[0], "MODE"); return 0; } if (parc < 3) { *modebuf = *parabuf = '\0'; modebuf[1] = '\0'; channel_modes(sptr, modebuf, parabuf, chptr, 0); sendto_one(sptr, form_str(RPL_CHANNELMODEIS), mename, parv[0], chptr->chname, modebuf, parabuf); sendto_one(sptr, form_str(RPL_CREATIONTIME), mename, parv[0], chptr->chname, chptr->channelts); return 0; } set_channel_mode(cptr, sptr, chptr, parc - 2, parv + 2); return 0; }
static void ms_topic(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; const char *from, *to; char topic_info[USERHOST_REPLYLEN]; if (IsCapable(source_p->from, CAP_TS6) && HasID(source_p)) { from = me.id; to = source_p->id; } else { from = me.name; to = source_p->name; } if (EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), from, to, "TOPIC"); return; } if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), from, to, parv[1]); return; } if (!IsClient(source_p)) strlcpy(topic_info, source_p->name, sizeof(topic_info)); else snprintf(topic_info, sizeof(topic_info), "%s!%s@%s", source_p->name, source_p->username, source_p->host); set_channel_topic(chptr, parv[2], topic_info, CurrentTime, 0); sendto_server(client_p, CAP_TS6, NOCAPS, ":%s TOPIC %s :%s", ID(source_p), chptr->chname, chptr->topic); sendto_server(client_p, NOCAPS, CAP_TS6, ":%s TOPIC %s :%s", source_p->name, chptr->chname, chptr->topic); if (!IsClient(source_p)) sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s TOPIC %s :%s", source_p->name, chptr->chname, chptr->topic); else sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, chptr->topic); }
/* part_one_client() * * inputs - pointer to server * - pointer to source client to remove * - char pointer of name of channel to remove from * output - none * side effects - remove ONE client given the channel name */ static void part_one_client(struct Client *client_p, struct Client *source_p, const char *name, char *reason) { struct Channel *chptr = NULL; struct Membership *ms = NULL; if ((chptr = hash_find_channel(name)) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); return; } if ((ms = find_channel_link(source_p, chptr)) == NULL) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, name); return; } if (MyConnect(source_p) && !IsOper(source_p)) check_spambot_warning(source_p, NULL); /* * Remove user from the old channel (if any) * only allow /part reasons in -m chans */ if(msg_has_colors(reason) && (chptr->mode.mode & MODE_NOCOLOR)) reason = strip_color(reason); if (reason[0] && (!MyConnect(source_p) || ((can_send(chptr, source_p, ms) && (source_p->firsttime + ConfigFileEntry.anti_spam_exit_message_time) < CurrentTime)))) { sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s PART %s :%s", ID(source_p), chptr->chname, reason); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s PART %s :%s", source_p->name, chptr->chname, reason); sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s PART %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, reason); } else { sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s PART %s", ID(source_p), chptr->chname); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s PART %s", source_p->name, chptr->chname); sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s PART %s", source_p->name, source_p->username, source_p->host, chptr->chname); } remove_user_from_channel(ms); }
/*! \brief MODE command handler * * \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] = command * - parv[1] = channel or nick name * - parv[2] = modes to be added or removed */ static int m_mode(struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; if (EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NEEDMOREPARAMS, "MODE"); return 0; } /* Now, try to find the channel in question */ if (!IsChanPrefix(*parv[1])) { /* If here, it has to be a non-channel name */ set_user_mode(source_p, parc, parv); return 0; } if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one_numeric(source_p, &me, ERR_NOSUCHCHANNEL, parv[1]); return 0; } /* Now known the channel exists */ if (parc < 3) { char modebuf[MODEBUFLEN] = ""; char parabuf[MODEBUFLEN] = ""; channel_modes(chptr, source_p, modebuf, parabuf); sendto_one_numeric(source_p, &me, RPL_CHANNELMODEIS, chptr->name, modebuf, parabuf); sendto_one_numeric(source_p, &me, RPL_CREATIONTIME, chptr->name, chptr->creationtime); return 0; } /* * bounce all modes from people we deop on sjoin * servers have always gotten away with murder, * including telnet servers *g* - Dianora */ if (IsServer(source_p) || HasFlag(source_p, FLAGS_SERVICE)) set_channel_mode(source_p, chptr, NULL, parc - 2, parv + 2); else { struct Membership *member = find_channel_link(source_p, chptr); /* Finish the flood grace period... */ if (MyClient(source_p) && !IsFloodDone(source_p)) if (!((parc == 3) && (parv[2][0] == 'b') && (parv[2][1] == '\0'))) flood_endgrace(source_p); set_channel_mode(source_p, chptr, member, parc - 2, parv + 2); } return 0; }
/* * * 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... * * Rewritten by Nemesi, Jan 1999, to support numeric nicks in parv[1] * * Called when we get a numeric message from a remote _server_ and we are * supposed to forward it somewhere. Note that we always ignore numerics sent * to 'me' and simply drop the message if we can't handle with this properly: * the savvy approach is NEVER generate an error in response to an... error :) */ static void parse_handle_numeric(unsigned int numeric, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL; struct Channel *chptr = NULL; /* * Avoid trash, we need it to come from a server and have a target */ if (parc < 2 || !IsServer(source_p)) return; /* * Who should receive this message ? Will we do something with it ? * Note that we use findUser functions, so the target can't be neither * a server, nor a channel (?) nor a list of targets (?) .. u2.10 * should never generate numeric replies to non-users anyway * Ahem... it can be a channel actually, csc bots use it :\ --Nem */ if (IsChanPrefix(*parv[1])) chptr = hash_find_channel(parv[1]); else target_p = find_person(source_p, parv[1]); if ((target_p == NULL || target_p->from == source_p->from) && chptr == NULL) return; /* * Remap low number numerics, not that I understand WHY.. --Nemesi */ /* * Numerics below 100 talk about the current 'connection', you're not * connected to a remote server so it doesn't make sense to send them * remotely - but the information they contain may be useful, so we * remap them up. Weird, but true. -- Isomer */ if (numeric < 100) numeric += 100; if (target_p) { /* Fake it for server hiding, if it's our client */ if ((ConfigServerHide.hide_servers || IsHidden(source_p)) && MyConnect(target_p) && !HasUMode(target_p, UMODE_OPER)) sendto_one_numeric(target_p, &me, numeric | SND_EXPLICIT, "%s", parv[2]); else sendto_one_numeric(target_p, source_p, numeric | SND_EXPLICIT, "%s", parv[2]); } else sendto_channel_butone(source_p, source_p, chptr, 0, "%u %s %s", numeric, chptr->name, parv[2]); }
/* * part_one_client * * inputs - pointer to server * - pointer to source client to remove * - char pointer of name of channel to remove from * output - none * side effects - remove ONE client given the channel name */ static void part_one_client(struct Client *client_p, struct Client *source_p, char *name, char *reason) { struct Channel *chptr; struct membership *msptr; if((chptr = hash_find_channel(name)) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); return; } msptr = find_channel_membership(chptr, source_p); if(msptr == NULL) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, name); return; } if(MyConnect(source_p) && !IsOper(source_p) && !IsExemptSpambot(source_p)) check_spambot_warning(source_p, NULL); /* * Remove user from the old channel (if any) * only allow /part reasons in -m chans */ if(reason[0] && (is_chanop(msptr) || !MyConnect(source_p) || ((can_send(chptr, source_p, msptr) > 0 && (source_p->firsttime + ConfigFileEntry.anti_spam_exit_message_time) < CurrentTime)))) { sendto_server(client_p, chptr, NOCAPS, NOCAPS, ":%s PART %s :%s", source_p->name, chptr->chname, reason); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s :%s", source_p->name, source_p->username, source_p->host, chptr->chname, reason); } else { sendto_server(client_p, chptr, NOCAPS, NOCAPS, ":%s PART %s", source_p->name, chptr->chname); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s", source_p->name, source_p->username, source_p->host, chptr->chname); } remove_user_from_channel(msptr); }
/*! \brief NAMES command handler * * \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] = command * - parv[1] = channel name */ static int m_names(struct Client *source_p, int parc, char *parv[]) { const char *const para = parc > 1 ? parv[1] : NULL; if (!EmptyString(para)) { struct Channel *chptr = hash_find_channel(para); if (chptr) channel_member_names(source_p, chptr, true); else sendto_one_numeric(source_p, &me, RPL_ENDOFNAMES, para); } else sendto_one_numeric(source_p, &me, RPL_ENDOFNAMES, "*"); return 0; }
/*! \brief TMODE command handler * * \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] = command * - parv[1] = timestamp * - parv[2] = channel name * - parv[3] = modes to be added or removed */ static int ms_tmode(struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; assert(!MyClient(source_p)); if ((chptr = hash_find_channel(parv[2])) == NULL) { sendto_one_numeric(source_p, &me, ERR_NOSUCHCHANNEL, parv[2]); return 0; } if (strtoimax(parv[1], NULL, 10) > chptr->creationtime) return 0; set_channel_mode(source_p, chptr, NULL, parc - 3, parv + 3); return 0; }
/*! \brief TOPIC command handler * * \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] = command * - parv[1] = channel name * - parv[2] = topic text */ static int ms_topic(struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; char topic_info[USERHOST_REPLYLEN]; if (EmptyString(parv[1])) { sendto_one_numeric(source_p, &me, ERR_NEEDMOREPARAMS, "TOPIC"); return 0; } if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one_numeric(source_p, &me, ERR_NOSUCHCHANNEL, parv[1]); return 0; } if (!IsClient(source_p)) strlcpy(topic_info, source_p->name, sizeof(topic_info)); else snprintf(topic_info, sizeof(topic_info), "%s!%s@%s", source_p->name, source_p->username, source_p->host); channel_set_topic(chptr, parv[2], topic_info, CurrentTime, 0); sendto_server(source_p, 0, 0, ":%s TOPIC %s :%s", source_p->id, chptr->name, chptr->topic); if (!IsClient(source_p)) sendto_channel_local(0, chptr, ":%s TOPIC %s :%s", (IsHidden(source_p) || ConfigServerHide.hide_servers) ? me.name : source_p->name, chptr->name, chptr->topic); else sendto_channel_local(0, chptr, ":%s!%s@%s TOPIC %s :%s", source_p->name, source_p->username, source_p->host, chptr->name, chptr->topic); return 0; }
/* ms_tburst() * * parv[0] = sender prefix * parv[1] = channel timestamp * parv[2] = channel * parv[3] = topic timestamp * parv[4] = topic setter * parv[5] = topic */ static void ms_tburst(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr; time_t newchannelts = atol(parv[1]); time_t newtopicts = atol(parv[3]); if((chptr = hash_find_channel(parv[2]))) { /* Don't allow newly created channels to change the topic ever, * only change the topic if we are on the old channel TS with * a new topic_time. -metalrock */ if (chptr->channelts > newchannelts) return; else if (chptr->topic == NULL || (chptr->topic_time > newtopicts)) set_topic(source_p, chptr, newtopicts, parv[4], parv[5]); } }
/* * ms_topic * parv[0] = sender prefix * parv[1] = channel name * parv[2] = topic_info * parv[3] = topic_info time * parv[4] = new channel topic * * Let servers always set a topic */ static void ms_topic(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; if (!IsServer(source_p)) { m_topic(client_p, source_p, parc, parv); return; } if( parc < 5 ) return; if (parv[1] && IsChannelName(parv[1])) { if ((chptr = hash_find_channel(parv[1])) == NULL) return; set_channel_topic(chptr, parv[4], parv[2], atoi(parv[3])); if(ConfigServerHide.hide_servers) { sendto_channel_local(ALL_MEMBERS, chptr, ":%s TOPIC %s :%s", me.name, parv[1], chptr->topic == NULL ? "" : chptr->topic); } else { sendto_channel_local(ALL_MEMBERS, chptr, ":%s TOPIC %s :%s", source_p->name, parv[1], chptr->topic == NULL ? "" : chptr->topic); } } }
/* ** ms_drop ** parv[0] = sender prefix ** parv[1] = channel ** parv[2] = channel password (key) ** ** "drop" a channel from consideration on a lazy link */ static void ms_drop(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char *name; struct Channel *chptr; if(parc < 2 || *parv[1] == '\0') return; name = parv[1]; #ifdef DEBUGLL sendto_realops(UMODE_ALL, "DROP called by %s for %s", client_p->name, name ); #endif if((chptr=hash_find_channel(name)) == NULL) return; if(client_p->localClient->serverMask) /* JIC */ chptr->lazyLinkChannelExists &= ~client_p->localClient->serverMask; return; }
static void ms_tb(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr; time_t tb_topicts = atol(tb_topicts_str); char *tb_whoset = NULL; char *tb_topic = NULL; if ((chptr = hash_find_channel(tb_channel)) == NULL) return; if (parc == 5) { tb_whoset = parv[3]; tb_topic = parv[4]; } else { tb_whoset = source_p->name; tb_topic = parv[3]; } set_topic(source_p, chptr, tb_topicts, tb_whoset, tb_topic); }
/* mo_ojoin() * parv[0] = sender prefix * parv[1] = channels separated by commas (#ifdef OJOIN_MULTIJOIN) */ static void mo_ojoin(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr; char *name = parv[1], modeletter; #ifdef OJOIN_MULTIJOIN char *t; #endif short move_me = 1; unsigned int tmp_flags; /* admins only */ if (!IsAdmin(source_p)) { sendto_one(source_p, form_str(ERR_NOPRIVILEGES), me.name, source_p->name); return; } #ifdef OJOIN_MULTIJOIN for (name = strtoken (&t, name, ","); name; name = strtoken (&t, NULL, ",")) { #endif move_me = 1; switch (*name) { #ifndef DISABLE_CHAN_OWNER case '!': tmp_flags = CHFL_CHANOWNER; modeletter = 'u'; name++; break; #endif case '@': tmp_flags = CHFL_CHANOP; modeletter = 'o'; name++; break; case '+': tmp_flags = CHFL_VOICE; modeletter = 'v'; name++; break; case '%': tmp_flags = CHFL_HALFOP; modeletter = 'h'; name++; break; case '#': case '&': tmp_flags = 0; modeletter = '\0'; break; /* We're not joining a channel, or we don't know the mode, * what ARE we joining? */ default: sendto_one (source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); #ifdef OJOIN_MULTIJOIN continue; #else return; #endif } /* Error checking here */ if ((chptr = hash_find_channel(name)) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); } else if (IsMember(source_p, chptr)) { sendto_one(source_p, ":%s NOTICE %s :Please part %s before using OJOIN", me.name, source_p->name, name); } else { if (move_me == 1) name--; add_user_to_channel(chptr, source_p, tmp_flags); if (chptr->chname[0] == '#') { sendto_server(client_p, CAP_TS6, NOCAPS, ":%s SJOIN %lu %s + :%c%s", me.id, (unsigned long)chptr->channelts, chptr->chname, (modeletter != '\0') ? *name : ' ', source_p->id); sendto_server(client_p, NOCAPS, CAP_TS6, ":%s SJOIN %lu %s + :%c%s", me.name, (unsigned long)chptr->channelts, chptr->chname, (modeletter != '\0') ? *name : ' ', source_p->name); } sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN %s", source_p->name, source_p->username, GET_CLIENT_HOST(source_p), chptr->chname); if (modeletter != '\0') { sendto_channel_local(ALL_MEMBERS, chptr, ":%s MODE %s +%c %s", me.name, chptr->chname, modeletter, source_p->name); } /* send the topic... */ if (chptr->topic != NULL) { 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); } source_p->localClient->last_join_time = CurrentTime; channel_member_names(source_p, chptr, 1); } #ifdef OJOIN_MULTIJOIN } #endif }
/*! \brief BMASK command handler * * \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] = command * - parv[1] = timestamp * - parv[2] = channel name * - parv[3] = type of ban to add ('b' 'I' or 'e') * - parv[4] = space delimited list of masks to add */ static int ms_bmask(struct Client *source_p, int parc, char *parv[]) { char modebuf[IRCD_BUFSIZE] = ""; char parabuf[IRCD_BUFSIZE] = ""; char banbuf[IRCD_BUFSIZE] = ""; struct Channel *chptr = NULL; char *s, *t, *mbuf, *pbuf; unsigned int mode_type = 0; int mlen = 0, tlen = 0; int modecount = 0; if ((chptr = hash_find_channel(parv[2])) == NULL) return 0; /* TS is higher, drop it. */ if (atol(parv[1]) > chptr->channelts) return 0; switch (*parv[3]) { case 'b': mode_type = CHFL_BAN; break; case 'e': mode_type = CHFL_EXCEPTION; break; case 'I': mode_type = CHFL_INVEX; break; default: return 0; } strlcpy(banbuf, parv[4], sizeof(banbuf)); s = banbuf; mlen = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s +", (IsHidden(source_p) || ConfigServerHide.hide_servers) ? me.name : source_p->name, chptr->chname); mbuf = modebuf + mlen; pbuf = parabuf; do { if ((t = strchr(s, ' '))) *t++ = '\0'; tlen = strlen(s); /* I don't even want to begin parsing this.. */ if (tlen > MODEBUFLEN) break; if (tlen && *s != ':' && add_id(source_p, chptr, s, mode_type)) { /* this new one wont fit.. */ if (mbuf - modebuf + 2 + pbuf - parabuf + tlen > IRCD_BUFSIZE - 2 || modecount >= MAXMODEPARAMS) { *mbuf = *(pbuf - 1) = '\0'; sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", modebuf, parabuf); mbuf = modebuf + mlen; pbuf = parabuf; modecount = 0; } *mbuf++ = *parv[3]; pbuf += sprintf(pbuf, "%s ", s); ++modecount; } s = t; } while (s); if (modecount) { *mbuf = *(pbuf - 1) = '\0'; sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", modebuf, parabuf); } sendto_server(source_p, NOCAPS, NOCAPS, ":%s BMASK %lu %s %s :%s", source_p->id, (unsigned long)chptr->channelts, chptr->chname, parv[3], parv[4]); return 0; }
/* ** m_list ** parv[0] = sender prefix ** parv[1] = channel */ int m_list(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr; char *name, *p = NULL; /* anti flooding code, * I did have this in parse.c with a table lookup * but I think this will be less inefficient doing it in each * function that absolutely needs it * * -Dianora */ static time_t last_used=0L; int i,j,minusers; minusers = HasUmode(sptr,UMODE_USER_AUSPEX) ? 0 : MIN_USERS_FOR_LIST; /* throw away non local list requests that do get here -Dianora */ if(!MyConnect(sptr)) return 0; if(!NoFloodProtection(sptr)) { if(IsHoneypot(sptr) || (((last_used + PACE_WAIT) > CurrentTime) && (!IsDoingList(sptr)))) { sendto_one(sptr,form_str(RPL_LOAD2HI),me.name,parv[0],"LIST"); sendto_one(sptr,form_str(RPL_LISTEND),me.name,parv[0]); return 0; } else last_used = CurrentTime; } /* right.. if we are already involved in a "blocked" /list, we will simply continue where we left off */ if (IsDoingList(sptr)) { if (sptr->listprogress != -1) { for (i=sptr->listprogress; i<CH_MAX; i++) { int progress2 = sptr->listprogress2; for (j=0, chptr=(struct Channel*)(hash_get_channel_block(i).list); (chptr) && (j<hash_get_channel_block(i).links); chptr=chptr->hnextch, j++) { if (j<progress2) continue; /* wind up to listprogress2 */ /* Safety check */ if (!sptr->user) continue; /* If it's secret, and none of the overriding conditions are true, don't send it */ if (SecretChannel(chptr) && !HasUmode(sptr,UMODE_USER_AUSPEX) && !IsMember(sptr, chptr) && !IsLogger(sptr, chptr)) continue; if (chptr->users < minusers) continue; sendto_one(sptr, form_str(RPL_LIST), me.name, parv[0], chptr->chname, chptr->users, chptr->topic); if (IsSendqPopped(sptr)) { sptr->listprogress=i; sptr->listprogress2=j; return 0; } } sptr->listprogress2 = 0; } } sendto_one(sptr, form_str(RPL_LISTEND), me.name, parv[0]); if (IsSendqPopped(sptr)) { /* popped with the RPL_LISTEND code. d0h */ sptr->listprogress = -1; return 0; } ClearDoingList(sptr); /* yupo, its over */ return 0; } /* Grr, mIRC -- jilles */ if (parc == 2 && !strcmp(parv[1], "<10000")) { #if 0 /* discussed #hyperion 20050807, considered unnecessary and confusing */ sendto_one(sptr, ":%s NOTICE %s :Your client is buggy, it sends LIST <10000 even though we don't advertise ELIST=U in 005. Please upgrade or contact the developers.", me.name, parv[0]); #endif parc--; } sendto_one(sptr, form_str(RPL_LISTSTART), me.name, parv[0]); if (parc < 2 || BadPtr(parv[1])) { SetDoingList(sptr); /* only set if its a full list */ ClearSendqPop(sptr); /* just to make sure */ /* we'll do this by looking through each hash table bucket */ for (i=0; i<CH_MAX; i++) { for (j=0, chptr = (struct Channel*)(hash_get_channel_block(i).list); (chptr) && (j<hash_get_channel_block(i).links); chptr = chptr->hnextch, j++) { /* Safety check */ if (!sptr->user) continue; /* If it's secret, and none of the overriding conditions are true, don't send it */ if (SecretChannel(chptr) && !HasUmode(sptr,UMODE_USER_AUSPEX) && !IsMember(sptr, chptr) && !IsLogger(sptr, chptr)) continue; if (chptr->users < minusers) continue; sendto_one(sptr, form_str(RPL_LIST), me.name, parv[0], chptr->chname, chptr->users, chptr->topic); if (IsSendqPopped(sptr)) { sptr->listprogress=i; sptr->listprogress2=j; return 0; } } } sendto_one(sptr, form_str(RPL_LISTEND), me.name, parv[0]); if (IsSendqPopped(sptr)) { sptr->listprogress=-1; return 0; } ClearDoingList(sptr); /* yupo, its over */ return 0; } p = strchr(parv[1],','); if(p) *p = '\0'; name = parv[1]; /* strtoken(&p, parv[1], ","); */ /* while(name) */ if(name) { chptr = hash_find_channel(name, NullChn); if (chptr && ShowChannel(sptr, chptr) && sptr->user) sendto_one(sptr, form_str(RPL_LIST), me.name, parv[0], name, chptr->users, chptr->topic); /* name = strtoken(&p, (char *)NULL, ","); */ } sendto_one(sptr, form_str(RPL_LISTEND), me.name, parv[0]); return 0; }
/* ** mo_clearchan ** parv[0] = sender prefix ** parv[1] = channel */ static void mo_clearchan(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr, *root_chptr; int on_vchan = 0; /* admins only */ if (!IsOperAdmin(source_p)) { sendto_one(source_p, ":%s NOTICE %s :You have no A flag", me.name, parv[0]); return; } /* XXX - we might not have CBURSTed this channel if we are a lazylink * yet. */ chptr= hash_find_channel(parv[1]); root_chptr = chptr; #ifdef VCHANS if (chptr && parc > 2 && parv[2][0] == '!') { chptr = find_vchan(chptr, parv[2]); if (root_chptr != chptr) on_vchan++; } #endif if( chptr == NULL ) { sendto_one(source_p, form_str(source_p,ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]); return; } if(IsMember(source_p, chptr)) { sendto_one(source_p, ":%s NOTICE %s :*** Please part %s before using CLEARCHAN", me.name, source_p->name, parv[1]); return; } if (!on_vchan) { sendto_wallops_flags(FLAGS_WALLOP, &me, "CLEARCHAN called for [%s] by %s!%s@%s", parv[1], source_p->name, source_p->username, source_p->host); sendto_server(NULL, source_p, NULL, NOCAPS, NOCAPS, LL_ICLIENT, ":%s WALLOPS :CLEARCHAN called for [%s] by %s!%s@%s", me.name, parv[1], source_p->name, source_p->username, source_p->host); ilog(L_NOTICE, "CLEARCHAN called for [%s] by %s!%s@%s", parv[1], source_p->name, source_p->username, source_p->host); } else { sendto_wallops_flags(FLAGS_WALLOP, &me, "CLEARCHAN called for [%s %s] by %s!%s@%s", parv[1], parv[2], source_p->name, source_p->username, source_p->host); sendto_server(NULL, source_p, NULL, NOCAPS, NOCAPS, LL_ICLIENT, ":%s WALLOPS :CLEARCHAN called for [%s %s] by %s!%s@%s", me.name, parv[1], parv[2], source_p->name, source_p->username, source_p->host); ilog(L_NOTICE, "CLEARCHAN called for [%s %s] by %s!%s@%s", parv[1], parv[2], source_p->name, source_p->username, source_p->host); } /* Kill all the modes we have about the channel.. making everyone a peon */ remove_our_modes(0, chptr, root_chptr, source_p); /* SJOIN the user to give them ops, and lock the channel */ sendto_server(client_p, source_p, chptr, NOCAPS, NOCAPS, LL_ICLIENT, ":%s SJOIN %lu %s +ntsi :@%s", me.name, (unsigned long) (chptr->channelts - 1), chptr->chname, source_p->name); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN %s", source_p->name, source_p->username, source_p->host, root_chptr->chname); sendto_channel_local(ALL_MEMBERS, chptr, ":%s MODE %s +o %s", me.name, chptr->chname, source_p->name); add_user_to_channel(chptr, source_p, CHFL_CHANOP); /* Take the TS down by 1, so we don't see the channel taken over * again. */ if (chptr->channelts) chptr->channelts--; #ifdef VCHANS if (on_vchan) add_vchan_to_client_cache(source_p,root_chptr,chptr); #endif chptr->mode.mode = MODE_SECRET | MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS; free_topic(chptr); chptr->mode.key[0] = 0; /* Kick the users out and join the oper */ kick_list(client_p, source_p, chptr, &chptr->peons, chptr->chname); }
/* * m_mode - MODE command handler * parv[0] - sender * parv[1] - channel */ static void m_mode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; struct Membership *member; static char modebuf[MODEBUFLEN]; static char parabuf[MODEBUFLEN]; if (parv[1][0] == '\0') { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "MODE"); return; } /* Now, try to find the channel in question */ if (!IsChanPrefix(parv[1][0])) { /* if here, it has to be a non-channel name */ set_user_mode(client_p, source_p, parc, parv); return; } if (!check_channel_name(parv[1])) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, source_p->name, parv[1]); return; } chptr = hash_find_channel(parv[1]); if (chptr == NULL) { /* if chptr isn't found locally, it =could= exist * on the uplink. So ask. */ /* LazyLinks */ /* only send a mode upstream if a local client sent this request * -davidt */ if (MyClient(source_p) && !ServerInfo.hub && uplink && IsCapable(uplink, CAP_LL)) { sendto_one(uplink, ":%s MODE %s %s", ID_or_name(source_p, uplink), parv[1], (parv[2] ? parv[2] : "")); return; } else { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]); return; } } /* Now known the channel exists */ if (parc < 3) { channel_modes(chptr, source_p, modebuf, parabuf); sendto_one(source_p, form_str(RPL_CHANNELMODEIS), me.name, parv[0], parv[1], modebuf, parabuf); sendto_one(source_p, form_str(RPL_CREATIONTIME), me.name, parv[0], parv[1], chptr->channelts); } /* bounce all modes from people we deop on sjoin * servers have always gotten away with murder, * including telnet servers *g* - Dianora * * XXX Is it worth the bother to make an ms_mode() ? - Dianora */ else if (IsServer(source_p)) { set_channel_mode(client_p, source_p, chptr, NULL, parc - 2, parv + 2, chptr->chname); } else { member = find_channel_link(source_p, chptr); if (!has_member_flags(member, CHFL_DEOPPED)) { /* Finish the flood grace period... */ if (MyClient(source_p) && !IsFloodDone(source_p)) { if (!((parc == 3) && (parv[2][0] == 'b') && (parv[2][1] == '\0'))) flood_endgrace(source_p); } set_channel_mode(client_p, source_p, chptr, member, parc - 2, parv + 2, chptr->chname); } } }
/* * m_omode - MODE command handler * parv[0] - sender * parv[1] - channel */ static void m_omode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; struct membership *msptr; static char modebuf[MODEBUFLEN]; static char parabuf[MODEBUFLEN]; int n = 2; if(EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "MODE"); return; } /* Now, try to find the channel in question */ if(!IsChanPrefix(parv[1][0])) { /* if here, it has to be a non-channel name */ user_mode(client_p, source_p, parc, parv); return; } if(!check_channel_name(parv[1])) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, parv[0], (unsigned char *) parv[1]); return; } chptr = hash_find_channel(parv[1]); if(chptr == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]); return; } sendto_wallops_flags(UMODE_WALLOP, &me, "OMODE called for [%s] by %s!%s@%s", parv[1], source_p->name, source_p->username, source_p->host); ilog(L_NOTICE, "OMODE called for [%s] by %s!%s@%s", parv[1], source_p->name, source_p->username, source_p->host); if(*chptr->chname != '&') sendto_server(NULL, NULL, NOCAPS, NOCAPS, ":%s WALLOPS :OMODE called for [%s] by %s!%s@%s", me.name, parv[1], source_p->name, source_p->username, source_p->host); /* Now know the channel exists */ if(parc < n + 1) { channel_modes(chptr, source_p, modebuf, parabuf); sendto_one(source_p, form_str(RPL_CHANNELMODEIS), me.name, parv[0], parv[1], modebuf, parabuf); sendto_one(source_p, form_str(RPL_CREATIONTIME), me.name, parv[0], parv[1], chptr->channelts); } else if(IsServer(source_p)) { set_channel_mode(client_p, source_p, chptr, NULL, parc - n, parv + n, chptr->chname); } else { msptr = find_channel_membership(chptr, source_p); set_channel_mode(client_p, source_p->servptr, chptr, msptr, parc - n, parv + n, chptr->chname); } }
/* * m_lljoin * parv[0] = sender prefix * parv[1] = channel * parv[2] = nick ("!nick" == cjoin) * parv[3] = vchan/key (optional) * parv[4] = 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; char *vkey = NULL; int flags; int i; struct Client *target_p; struct Channel *chptr, *vchan_chptr, *root_vchan; #ifdef VCHANS int cjoin = 0; int vc_ts; char *pvc = NULL; #endif if(uplink && !IsCapable(uplink,CAP_LL)) { sendto_realops_flags(FLAGS_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; #ifdef VCHANS if (nick[0] == '!') { cjoin = 1; nick++; } if(parc > 4) { key = parv[4]; vkey = parv[3]; } #endif else if(parc >3) { key = vkey = parv[3]; } flags = 0; target_p = find_client(nick); if( !target_p || !target_p->user ) return; if( !MyClient(target_p) ) return; chptr = hash_find_channel(chname); #ifdef VCHANS if (cjoin) { if(!chptr) /* Uhm, bad! */ { sendto_realops_flags(FLAGS_ALL, L_ALL, "LLJOIN %s %s called by %s, but root chan doesn't exist!", chname, nick, client_p->name); return; } flags = CHFL_CHANOP; if(! (vchan_chptr = cjoin_channel(chptr, target_p, chname))) return; root_vchan = chptr; chptr = vchan_chptr; } else #endif { #ifdef VCHANS if (chptr) { vchan_chptr = select_vchan(chptr, target_p, vkey, chname); } else #endif { chptr = vchan_chptr = get_or_create_channel(target_p, chname, NULL); flags = CHFL_CHANOP; } #ifdef VCHANS if (vchan_chptr != chptr) { root_vchan = chptr; chptr = vchan_chptr; } else #endif root_vchan = chptr; if(!chptr || !root_vchan) return; if (chptr->users == 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, root_vchan->chname); return; } if( (i = can_join(target_p, chptr, key)) ) { sendto_one(target_p, form_str(i), me.name, nick, root_vchan->chname); return; } } if ((target_p->user->joined >= ConfigChannel.max_chans_per_user) && (!IsOper(target_p) || (target_p->user->joined >= ConfigChannel.max_chans_per_user*3))) { sendto_one(target_p, form_str(ERR_TOOMANYCHANNELS), me.name, nick, root_vchan->chname ); return; } if(flags == CHFL_CHANOP) { chptr->channelts = CurrentTime; /* * XXX - this is a rather ugly hack. * * Unfortunately, there's no way to pass * the fact that it is a vchan through SJOIN... */ /* Prevent users creating a fake vchan */ #ifdef VCHANS if (chname[0] == '#' && chname[1] == '#') { if ((pvc = strrchr(chname+3, '_'))) { /* * OK, name matches possible vchan: * ##channel_blah */ pvc++; /* point pvc after last _ */ vc_ts = atol(pvc); /* * if blah is the same as the TS, up the TS * by one, to prevent this channel being * seen as a vchan */ if (vc_ts == CurrentTime) chptr->channelts++; } } #endif sendto_one(uplink, ":%s SJOIN %lu %s + :@%s", me.name, (unsigned long) chptr->channelts, chptr->chname, nick); } /* a user can create a channel with halfops..? */ #if 0 else if ((flags == CHFL_HALFOP) && (IsCapable(uplink, CAP_HOPS))) { sendto_one(uplink, ":%s SJOIN %lu %s + :%%%s", me.name, (unsigned long) chptr->channelts, chptr->chname, nick); } #endif else { 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); #ifdef VCHANS if ( chptr != root_vchan ) add_vchan_to_client_cache(target_p,root_vchan,chptr); #endif sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", target_p->name, target_p->username, target_p->host, root_vchan->chname); if( flags & CHFL_CHANOP ) { chptr->mode.mode |= MODE_TOPICLIMIT; chptr->mode.mode |= MODE_NOPRIVMSGS; sendto_channel_local(ALL_MEMBERS,chptr, ":%s MODE %s +nt", me.name, root_vchan->chname); sendto_one(uplink, ":%s MODE %s +nt", me.name, chptr->chname); } channel_member_names(target_p, chptr, chname, 1); }
/* * ms_bmask() * * inputs - parv[0] = SID * parv[1] = TS * parv[2] = channel name * parv[3] = type of ban to add ('b' 'I' or 'e') * parv[4] = space delimited list of masks to add * outputs - none * side effects - propgates unchanged bmask line to CAP_TS6 servers, * sends plain modes to the others. nothing is sent * to the server the issuing server is connected through */ static void ms_bmask(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { static char modebuf[BUFSIZE]; static char parabuf[BUFSIZE]; static char banbuf[BUFSIZE]; struct Channel *chptr; char *s, *t, *mbuf, *pbuf; long mode_type; int mlen; int plen = 0; int tlen; int modecount = 0; int needcap = NOCAPS; if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2])) return; if((chptr = hash_find_channel(parv[2])) == NULL) return; /* TS is higher, drop it. */ if(atol(parv[1]) > chptr->channelts) return; switch(parv[3][0]) { case 'b': mode_type = CHFL_BAN; break; case 'e': mode_type = CHFL_EXCEPTION; needcap = CAP_EX; break; case 'I': mode_type = CHFL_INVEX; needcap = CAP_IE; break; /* maybe we should just blindly propagate this? */ default: return; } parabuf[0] = '\0'; s = banbuf; strlcpy(s, parv[4], sizeof(banbuf)); /* only need to construct one buffer, for non-ts6 servers */ mlen = ircsprintf(modebuf, ":%s MODE %s +", source_p->name, chptr->chname); mbuf = modebuf + mlen; pbuf = parabuf; if((t = strchr(s, ' ')) != NULL) *t++ = '\0'; while(s != NULL) { tlen = strlen(s); /* I dont even want to begin parsing this.. */ if(tlen > MODEBUFLEN) break; if(tlen && add_id(source_p, chptr, s, mode_type)) { /* this new one wont fit.. */ if(mlen + MAXMODEPARAMS + plen + tlen > BUFSIZE - 4 || modecount >= MAXMODEPARAMS) { *mbuf = '\0'; *(pbuf - 1) = '\0'; sendto_channel_local(ALL_MEMBERS, chptr, "%s %s", modebuf, parabuf); sendto_server(client_p, NULL, chptr, needcap, CAP_TS6, NOFLAGS, "%s %s", modebuf, parabuf); mbuf = modebuf + mlen; pbuf = parabuf; plen = modecount = 0; } *mbuf++ = parv[3][0]; plen = ircsprintf(pbuf, "%s ", s); pbuf += plen; modecount++; } s = t; if(s != NULL) { /* trailing space marking the end. */ if(*s == '\0') break; if((t = strchr(s, ' ')) != NULL) *t++ = '\0'; } } if(modecount) { *mbuf = *(pbuf - 1) = '\0'; sendto_channel_local(ALL_MEMBERS, chptr, "%s %s", modebuf, parabuf); sendto_server(client_p, NULL, chptr, needcap, CAP_TS6, NOFLAGS, "%s %s", modebuf, parabuf); } /* assumption here is that since the server sent BMASK, they are TS6, so they have an ID */ sendto_server(client_p, NULL, chptr, CAP_TS6|needcap, NOCAPS, NOFLAGS, ":%s BMASK %lu %s %s :%s", source_p->id, (unsigned long)chptr->channelts, chptr->chname, parv[3], parv[4]); }
/* build_target_list() * * inputs - pointer to given source (oper/client etc.) * - pointer to list of nicks/channels * - pointer to table to place results * - pointer to text (only used if source_p is an oper) * output - number of valid entities * side effects - target_table is modified to contain a list of * pointers to channels or clients * if source client is an oper * all the classic old bizzare oper privmsg tricks * are parsed and sent as is, if prefixed with $ * to disambiguate. * */ static int build_target_list(int p_or_n, const char *command, struct Client *source_p, char *nicks_channels, const char *text) { int type = 0; char *p = NULL, *nick = NULL; char *target_list = NULL; struct Channel *chptr = NULL; struct Client *target_p = NULL; target_list = nicks_channels; ntargets = 0; for (nick = strtoken(&p, target_list, ","); nick; nick = strtoken(&p, NULL, ",")) { const char *with_prefix = NULL; /* * Channels are privmsg'd a lot more than other clients, moved up * here plain old channel msg? */ if (IsChanPrefix(*nick)) { if ((chptr = hash_find_channel(nick))) { if (!duplicate_ptr(chptr)) { if (ntargets >= ConfigGeneral.max_targets) { sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS, nick, ConfigGeneral.max_targets); return 1; } targets[ntargets].ptr = chptr; targets[ntargets++].type = ENTITY_CHANNEL; } } else { if (p_or_n != NOTICE) sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick); } continue; } /* Look for a PRIVMSG/NOTICE to another client */ if ((target_p = find_person(source_p, nick))) { if (!duplicate_ptr(target_p)) { if (ntargets >= ConfigGeneral.max_targets) { sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS, nick, ConfigGeneral.max_targets); return 1; } targets[ntargets].ptr = 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 */ while (1) { if (*nick == '@') type |= CHFL_CHANOP; else if (*nick == '%') type |= CHFL_CHANOP | CHFL_HALFOP; else if (*nick == '+') type |= CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE; else break; ++nick; } if (type) { if (EmptyString(nick)) /* If it's a '\0' dump it, there is no recipient */ { sendto_one_numeric(source_p, &me, ERR_NORECIPIENT, 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))) { if (IsClient(source_p) && !HasFlag(source_p, FLAGS_SERVICE)) { if (!has_member_flags(find_channel_link(source_p, chptr), CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE)) { sendto_one_numeric(source_p, &me, ERR_CHANOPRIVSNEEDED, with_prefix); return -1; } } if (!duplicate_ptr(chptr)) { if (ntargets >= ConfigGeneral.max_targets) { sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS, nick, ConfigGeneral.max_targets); return 1; } targets[ntargets].ptr = chptr; targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL; targets[ntargets++].flags = type; } } else { if (p_or_n != NOTICE) sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick); } continue; } if (*nick == '$' || strchr(nick, '@')) handle_special(p_or_n, command, source_p, nick, text); else { if (p_or_n != NOTICE) { if (!IsDigit(*nick) || MyClient(source_p)) sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick); } } } return 1; }
/* * * 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; 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) * Note: if buffer is non-empty, it will begin with SPACE. */ if(parc > 1) { char *t = buffer; /* Current position within the buffer */ int i; int tl; /* current length of presently being built string in t */ 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_client(parv[1])) != NULL) { if(IsMe(target_p)) { /* * 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 */ if(atoi(numeric) != ERR_NOSUCHNICK) sendto_realops_flags(UMODE_ALL, L_ADMIN, "*** %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, parv[1], buffer); else sendto_one(target_p, ":%s %s %s%s", source_p->name, numeric, parv[1], buffer); return; } else if((chptr = hash_find_channel(parv[1])) != NULL) sendto_channel_local(ALL_MEMBERS, chptr, ":%s %s %s %s", source_p->name, numeric, chptr->chname, buffer); }
/* m_topic() * parv[0] = sender prefix * parv[1] = channel name * parv[2] = new topic, if setting topic */ static void m_topic(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; char *p; struct Membership *ms; const char *from, *to; if (!MyClient(source_p) && IsCapable(source_p->from, CAP_TS6) && HasID(source_p)) { from = me.id; to = source_p->id; } else { from = me.name; to = source_p->name; } if ((p = strchr(parv[1], ',')) != NULL) *p = '\0'; if (EmptyString(parv[1])) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), from, to, "TOPIC"); return; } if (MyClient(source_p) && !IsFloodDone(source_p)) flood_endgrace(source_p); if (IsChanPrefix(*parv[1])) { if ((chptr = hash_find_channel(parv[1])) == NULL) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), from, to, parv[1]); return; } /* setting topic */ if (parc > 2) { if ((ms = find_channel_link(source_p, chptr)) == NULL && !IsService(source_p)) { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), me.name, source_p->name, parv[1]); return; } if ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP) || IsGod(source_p) || IsService(source_p)) { char topic_info[USERHOST_REPLYLEN]; if(!has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP) && IsGod(source_p) && MyClient(source_p) && (chptr->mode.mode & MODE_TOPICLIMIT) != 0) { char tmp[IRCD_BUFSIZE]; ircsprintf(tmp, "%s is using God mode: TOPIC %s %s", source_p->name, chptr->chname, parv[2]); sendto_gnotice_flags(UMODE_SERVNOTICE, L_ALL, me.name, &me, NULL, tmp); oftc_log(tmp); } ircsprintf(topic_info, "%s!%s@%s", source_p->name, source_p->username, source_p->host); set_channel_topic(chptr, parv[2], topic_info, CurrentTime); sendto_server(client_p, chptr, CAP_TS6, NOCAPS, ":%s TOPIC %s :%s", ID(source_p), chptr->chname, chptr->topic == NULL ? "" : chptr->topic); sendto_server(client_p, chptr, NOCAPS, CAP_TS6, ":%s TOPIC %s :%s", source_p->name, chptr->chname, chptr->topic == NULL ? "" : chptr->topic); sendto_channel_local(ALL_MEMBERS, NO, 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), from, to, chptr->chname); } else /* only asking for topic */ { if (!SecretChannel(chptr) || IsMember(source_p, chptr)) { if (chptr->topic == NULL) sendto_one(source_p, form_str(RPL_NOTOPIC), from, to, chptr->chname); else { sendto_one(source_p, form_str(RPL_TOPIC), from, to, chptr->chname, chptr->topic); sendto_one(source_p, form_str(RPL_TOPICWHOTIME), from, to, chptr->chname, chptr->topic_info, chptr->topic_time); } } else { sendto_one(source_p, form_str(ERR_NOTONCHANNEL), from, to, chptr->chname); return; } } } else { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), from, to, parv[1]); } }
/* ** 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_who ** parv[0] = sender prefix ** parv[1] = nickname mask list ** parv[2] = additional selection flag, only 'o' for now. */ int m_who(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *acptr; char *mask = parc > 1 ? parv[1] : NULL; Link *lp; struct Channel *chptr; struct Channel *mychannel = NULL; char *channame = NULL; int oper = parc > 2 ? (*parv[2] == 'o' ): 0; /* Show OPERS only */ int member; int maxmatches = 500; mychannel = NullChn; if (sptr->user) if ((lp = sptr->user->channel)) mychannel = lp->value.chptr; /* ** Following code is some ugly hacking to preserve the ** functions of the old implementation. (Also, people ** will complain when they try to use masks like "12tes*" ** and get people on channel 12 ;) --msa */ if (mask) { if (!strcmp(mask, "**")) mask = NULL; else if (*mask == (char) 0) mask = NULL; else (void)collapse(mask); } if (!mask || (*mask == (char) 0)) { if (!HasUmode(sptr,UMODE_USER_AUSPEX)) { sendto_one(sptr, form_str(RPL_ENDOFWHO), me.name, parv[0], EmptyString(mask) ? "*" : mask); return 0; } } else if ((*(mask+1) == (char) 0) && (*mask == '*')) { if (!mychannel) { sendto_one(sptr, form_str(RPL_ENDOFWHO), me.name, parv[0], EmptyString(mask) ? "*" : mask); return 0; } channame = mychannel->chname; } else channame = mask; if (IsChannelName(channame)) { /* * List all users on a given channel */ if (!oper || SeesOpers(sptr)) /* If you're doing an oper search, you need SeesOpers() */ { chptr = hash_find_channel(channame, NULL); if (chptr) { member = IsMember(sptr, chptr) || IsLogger(sptr, chptr) || HasUmode(sptr,UMODE_USER_AUSPEX); if (member || !SecretChannel(chptr)) for (lp = chptr->members; lp; lp = lp->next) { if (oper && !HasUmode(lp->value.cptr,UMODE_OPER)) continue; if (IsInvisible(lp->value.cptr) && !member) continue; do_who(sptr, lp->value.cptr, chptr, lp); } } } } else if (mask && ((acptr = find_client(mask, NULL)) != NULL) && IsPerson(acptr) && (!oper || (HasUmode(acptr,UMODE_OPER) && SeesOpers(sptr)))) { int isinvis = 0; struct Channel *ch2ptr = NULL; isinvis = IsInvisible(acptr); for (lp = acptr->user->channel; lp; lp = lp->next) { chptr = lp->value.chptr; member = IsMember(sptr, chptr) || HasUmode(sptr,UMODE_USER_AUSPEX); if (isinvis && !member) continue; if (member || (!isinvis && PubChannel(chptr))) { ch2ptr = chptr; break; } } do_who(sptr, acptr, ch2ptr, NULL); } else if (!oper || SeesOpers(sptr)) for (acptr = GlobalClientList; acptr; acptr = acptr->next) { struct Channel *ch2ptr = NULL; int showperson, isinvis; if (!IsPerson(acptr)) continue; if (oper && !HasUmode(acptr,UMODE_OPER)) continue; showperson = 0; /* * Show user if they are on the same channel, or not * invisible and on a non secret channel (if any). * Do this before brute force match on all relevant fields * since these are less cpu intensive (I hope :-) and should * provide better/more shortcuts - avalon */ isinvis = IsInvisible(acptr); for (lp = acptr->user->channel; lp; lp = lp->next) { chptr = lp->value.chptr; member = IsMember(sptr, chptr) || HasUmode(sptr, UMODE_USER_AUSPEX); if (isinvis && !member) continue; if (member || (!isinvis && PubChannel(chptr))) { ch2ptr = chptr; showperson = 1; break; } if (HiddenChannel(chptr) && !SecretChannel(chptr) && !isinvis) showperson = 1; } if (!acptr->user->channel && !isinvis) showperson = 1; if ((HasUmode(sptr,UMODE_USER_AUSPEX) || showperson) && (!mask || match(mask, acptr->name) || match(mask, acptr->username) || match(mask, acptr->host) || (HasUmode(sptr, UMODE_AUSPEX) && match(mask, acptr->user->server)) || match(mask, acptr->info))) { do_who(sptr, acptr, ch2ptr, NULL); if (!HasUmode(sptr,UMODE_USER_AUSPEX) && !--maxmatches) { sendto_one(sptr, form_str(RPL_ENDOFWHO), me.name, parv[0], EmptyString(mask) ? "*" : mask); return 0; } } } sendto_one(sptr, form_str(RPL_ENDOFWHO), me.name, parv[0], EmptyString(mask) ? "*" : mask); return 0; }