void exit_one_client_in_split(aClient *cptr, aClient *dead, char *reason) { Link *lp; /* send all the quit reasons to all the non-noquit servers we have */ /* yikes. We only want to do this if dead was OUR server. */ /* erm, no, that's not true. Doing that breaks things. * If a non-noquit server is telling us a server has split, * we will have already recieved hundreds of QUIT messages * from it, which will be passed anyway, and this procedure * will never be called. - lucas */ #ifdef NOQUIT sendto_non_noquit_servs_butone(dead, ":%s QUIT :%s", cptr->name, reason); #endif sendto_common_channels(cptr, ":%s QUIT :%s", cptr->name, reason); while ((lp = cptr->user->channel)) remove_user_from_channel(cptr, lp->value.chptr); while ((lp = cptr->user->invited)) del_invite(cptr, lp->value.chptr); while ((lp = cptr->user->silence)) del_silence(cptr, lp->value.cp); if (cptr->user->alias) cptr->user->alias->client = NULL; clones_remove(cptr); #ifdef RWHO_PROBABILITY probability_remove(cptr); #endif remove_dcc_references(cptr); del_from_client_hash_table(cptr->name, cptr); hash_check_watch(cptr, RPL_LOGOFF); remove_client_from_list(cptr); }
/* Routine that actually makes a user join the channel * this does no actual checking (banned, etc.) it just adds the user */ DLLFUNC void _join_channel(aChannel *chptr, aClient *cptr, aClient *sptr, int flags) { char *parv[] = { 0, 0 }; /* ** Complete user entry to the new channel (if any) */ add_user_to_channel(chptr, sptr, flags); /* ** notify all other users on the new channel */ if (chptr->mode.mode & MODE_AUDITORIUM) { if (MyClient(sptr)) sendto_one(sptr, ":%s!%s@%s JOIN :%s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname); sendto_chanops_butone(NULL, chptr, ":%s!%s@%s JOIN :%s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname); } else sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", sptr->name, chptr->chname); sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, sptr->name, MSG_JOIN, TOK_JOIN, "%s", chptr->chname); #ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN if ((MyClient(sptr) && !(flags & CHFL_CHANOP)) || !MyClient(sptr)) sendto_serv_butone_token_opt(cptr, OPT_SJ3, sptr->name, MSG_JOIN, TOK_JOIN, "%s", chptr->chname); if (flags && !(flags & CHFL_DEOPPED)) { #endif /* I _know_ that the "@%s " look a bit wierd with the space and all .. but its to get around a SJOIN bug --stskeeps */ sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_SJB64, me.name, MSG_SJOIN, TOK_SJOIN, "%B %s :%s%s ", (long)chptr->creationtime, chptr->chname, chfl_to_sjoin_symbol(flags), sptr->name); sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_NOT_SJB64, me.name, MSG_SJOIN, TOK_SJOIN, "%li %s :%s%s ", chptr->creationtime, chptr->chname, chfl_to_sjoin_symbol(flags), sptr->name); #ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN } #endif if (MyClient(sptr)) { /* ** Make a (temporal) creationtime, if someone joins ** during a net.reconnect : between remote join and ** the mode with TS. --Run */ if (chptr->creationtime == 0) { chptr->creationtime = TStime(); sendto_serv_butone_token(cptr, me.name, MSG_MODE, TOK_MODE, "%s + %lu", chptr->chname, chptr->creationtime); } del_invite(sptr, chptr); if (flags && !(flags & CHFL_DEOPPED)) { #ifndef PREFIX_AQ if ((flags & CHFL_CHANOWNER) || (flags & CHFL_CHANPROT)) { /* +ao / +qo for when PREFIX_AQ is off */ sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, me.name, MSG_MODE, TOK_MODE, "%s +o%c %s %s %lu", chptr->chname, chfl_to_chanmode(flags), sptr->name, sptr->name, chptr->creationtime); } else { #endif /* +v/+h/+o (and +a/+q if PREFIX_AQ is on) */ sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, me.name, MSG_MODE, TOK_MODE, "%s +%c %s %lu", chptr->chname, chfl_to_chanmode(flags), sptr->name, chptr->creationtime); #ifndef PREFIX_AQ } #endif } if (chptr->topic) { sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, sptr->name, chptr->chname, chptr->topic); sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, sptr->name, chptr->chname, chptr->topic_nick, chptr->topic_time); } if (chptr->users == 1 && (MODES_ON_JOIN #ifdef EXTCMODE || iConf.modes_on_join.extmodes) #endif ) { #ifdef EXTCMODE int i; chptr->mode.extmode = iConf.modes_on_join.extmodes; /* Param fun */ for (i = 0; i <= Channelmode_highest; i++) { if (!Channelmode_Table[i].flag || !Channelmode_Table[i].paracount) continue; if (chptr->mode.extmode & Channelmode_Table[i].mode) { CmodeParam *p; p = Channelmode_Table[i].put_param(NULL, iConf.modes_on_join.extparams[i]); AddListItem(p, chptr->mode.extmodeparam); } } #endif chptr->mode.mode = MODES_ON_JOIN; #ifdef NEWCHFLOODPROT if (iConf.modes_on_join.floodprot.per) { chptr->mode.floodprot = MyMalloc(sizeof(ChanFloodProt)); memcpy(chptr->mode.floodprot, &iConf.modes_on_join.floodprot, sizeof(ChanFloodProt)); } #else chptr->mode.kmode = iConf.modes_on_join.kmode; chptr->mode.per = iConf.modes_on_join.per; chptr->mode.msgs = iConf.modes_on_join.msgs; #endif *modebuf = *parabuf = 0; channel_modes(sptr, modebuf, parabuf, chptr); /* This should probably be in the SJOIN stuff */ sendto_serv_butone_token(&me, me.name, MSG_MODE, TOK_MODE, "%s %s %s %lu", chptr->chname, modebuf, parabuf, chptr->creationtime); sendto_one(sptr, ":%s MODE %s %s %s", me.name, chptr->chname, modebuf, parabuf); } parv[0] = sptr->name; parv[1] = chptr->chname; do_cmd(cptr, sptr, "NAMES", 2, parv); RunHook4(HOOKTYPE_LOCAL_JOIN, cptr, sptr,chptr,parv); } else { RunHook4(HOOKTYPE_REMOTE_JOIN, cptr, sptr, chptr, parv); /* (rarely used) */ } #ifdef NEWCHFLOODPROT /* I'll explain this only once: * 1. if channel is +f * 2. local client OR synced server * 3. then, increase floodcounter * 4. if we reached the limit AND only if source was a local client.. do the action (+i). * Nr 4 is done because otherwise you would have a noticeflood with 'joinflood detected' * from all servers. */ if (chptr->mode.floodprot && (MyClient(sptr) || sptr->srvptr->serv->flags.synced) && !IsULine(sptr) && do_chanflood(chptr->mode.floodprot, FLD_JOIN) && MyClient(sptr)) { do_chanflood_action(chptr, FLD_JOIN, "join"); } #endif }
/** Handle a JOIN message from a client connection. * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr; struct JoinBuf join; struct JoinBuf create; struct Gline *gline; char *p = 0; char *chanlist; char *name; char *keys; if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime()); chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */ keys = parv[2]; /* remember where keys are */ for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) { char *key = 0; /* If we have any more keys, take the first for this channel. */ if (!BadPtr(keys) && (keys = strchr(key = keys, ','))) *keys++ = '\0'; /* Empty keys are the same as no keys. */ if (key && !key[0]) key = 0; if (!IsChannelName(name) || !strIsIrcCh(name)) { /* bad channel name */ send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (cli_user(sptr)->joined >= feature_int(FEAT_MAXCHANNELSPERUSER) && !HasPriv(sptr, PRIV_CHAN_LIMIT)) { send_reply(sptr, ERR_TOOMANYCHANNELS, name); break; /* no point processing the other channels */ } /* BADCHANed channel */ if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BANNEDFROMCHAN, name); continue; } if (!(chptr = FindChannel(name))) { if (((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(name) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (!(chptr = get_channel(sptr, name, CGT_CREATE))) continue; /* Try to add the new channel as a recent target for the user. */ if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); continue; } joinbuf_join(&create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER); } else if (find_member_link(chptr, sptr)) { continue; /* already on channel */ } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) { continue; } else { int flags = CHFL_DEOPPED; int err = 0; /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; else if (chptr->users == 0 && !chptr->mode.apass[0]) { /* Joining a zombie channel (zannel): give ops and increment TS. */ flags = CHFL_CHANOP; chptr->creationtime++; } else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (chptr->mode.mode & MODE_INVITEONLY) err = ERR_INVITEONLYCHAN; else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit)) err = ERR_CHANNELISFULL; else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr)) err = ERR_NEEDREGGEDNICK; else if (find_ban(sptr, chptr->banlist)) err = ERR_BANNEDFROMCHAN; else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key))) err = ERR_BADCHANNELKEY; /* An oper with WALK_LCHAN privilege can join a local channel * he otherwise could not join by using "OVERRIDE" as the key. * This will generate a HACK(4) notice, but fails if the oper * could normally join the channel. */ if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: if (strcmp(chptr->mode.key, "OVERRIDE") && strcmp(chptr->mode.apass, "OVERRIDE") && strcmp(chptr->mode.upass, "OVERRIDE")) { send_reply(sptr, ERR_DONTCHEAT, chptr->chname); continue; } break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; case ERR_BADCHANNELKEY: err = 'k'; break; case ERR_NEEDREGGEDNICK: err = 'r'; break; default: err = '?'; break; } /* send accountability notice */ if (err) sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { switch(err) { case ERR_NEEDREGGEDNICK: send_reply(sptr, ERR_NEEDREGGEDNICK, chptr->chname, feature_str(FEAT_URLREG)); break; default: send_reply(sptr, err, chptr->chname); break; } continue; } joinbuf_join(&join, chptr, flags); if (flags & CHFL_CHANOP) { struct ModeBuf mbuf; /* Always let the server op him: this is needed on a net with older servers because they 'destruct' channels immediately when they become empty without sending out a DESTRUCT message. As a result, they would always bounce a mode (as HACK(2)) when the user ops himself. (There is also no particularly good reason to have the user op himself.) */ modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); modebuf_flush(&mbuf); } } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ } joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */ joinbuf_flush(&create); 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; }
/* * Exit one client, local or remote. Assuming all dependants have * been already removed, and socket closed for local client. */ static void exit_one_client(aClient *cptr, aClient *sptr, aClient *from, char *comment) { Link *lp; /* * For a server or user quitting, propogate the information to * other servers (except to the one where is came from (cptr)) */ if (IsMe(sptr)) { sendto_ops("ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(sptr)) { #ifdef ALWAYS_SEND_DURING_SPLIT currently_processing_netsplit = YES; #endif exit_server(cptr, sptr, from, comment); #ifdef ALWAYS_SEND_DURING_SPLIT currently_processing_netsplit = NO; #endif return; } else if (!(IsPerson(sptr))) /* * ...this test is *dubious*, would need * some thought.. but for * now it plugs a * nasty hole in the server... --msa */ ; /* Nothing */ else if (sptr->name[0]) { /* ...just clean all others with QUIT... */ /* * If this exit is generated from "m_kill", then there is no * sense in sending the QUIT--KILL's have been sent instead. */ if ((sptr->flags & FLAGS_KILLED) == 0) { sendto_serv_butone(cptr, ":%s QUIT :%s", sptr->name, comment); } /* * * If a person is on a channel, send a QUIT notice * to every * client (person) on the same channel (so * that the client can * show the "**signoff" message). * (Note: The notice is to the * local clients *only*) */ if (sptr->user) { send_part_to_common_channels(sptr, comment); send_quit_to_common_channels(sptr, comment); while ((lp = sptr->user->channel)) remove_user_from_channel(sptr, lp->value.chptr); clones_remove(sptr); #ifdef RWHO_PROBABILITY probability_remove(sptr); #endif /* Clean up invitefield */ while ((lp = sptr->user->invited)) del_invite(sptr, lp->value.chptr); /* Clean up silences */ while ((lp = sptr->user->silence)) del_silence(sptr, lp->value.cp); remove_dcc_references(sptr); /* again, this is all that is needed */ } } /* Remove sptr from the client list */ if (del_from_client_hash_table(sptr->name, sptr) != 1) { Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x", sptr, sptr->name, sptr->from ? sptr->from->sockhost : "??host", sptr->from, sptr->next, sptr->prev, sptr->fd, sptr->status, sptr->user)); } /* remove user from watchlists */ if(IsRegistered(sptr)) hash_check_watch(sptr, RPL_LOGOFF); remove_client_from_list(sptr); return; }
/* ** Subtract one user from channel i (and free channel ** block, if channel became empty). */ void sub1_from_channel(aChannel *chptr) { Ban *ban; Link *lp; /* if (--chptr->users <= 0) */ if (chptr->users == 0 || --chptr->users == 0) { /* * Now, find all invite links from channel structure */ RunHook(HOOKTYPE_CHANNEL_DESTROY, chptr); while ((lp = chptr->invites)) del_invite(lp->value.cptr, chptr); while (chptr->banlist) { ban = chptr->banlist; chptr->banlist = ban->next; MyFree(ban->banstr); MyFree(ban->who); free_ban(ban); } while (chptr->exlist) { ban = chptr->exlist; chptr->exlist = ban->next; MyFree(ban->banstr); MyFree(ban->who); free_ban(ban); } while (chptr->invexlist) { ban = chptr->invexlist; chptr->invexlist = ban->next; MyFree(ban->banstr); MyFree(ban->who); free_ban(ban); } #ifdef EXTCMODE /* free extcmode params */ extcmode_free_paramlist(chptr->mode.extmodeparam); chptr->mode.extmodeparam = NULL; #endif #ifdef NEWCHFLOODPROT chanfloodtimer_stopchantimers(chptr); if (chptr->mode.floodprot) MyFree(chptr->mode.floodprot); #endif #ifdef JOINTHROTTLE cmodej_delchannelentries(chptr); #endif if (chptr->topic) MyFree(chptr->topic); if (chptr->topic_nick) MyFree(chptr->topic_nick); if (chptr->prevch) chptr->prevch->nextch = chptr->nextch; else channel = chptr->nextch; if (chptr->nextch) chptr->nextch->prevch = chptr->prevch; (void)del_from_channel_hash_table(chptr->chname, chptr); IRCstats.channels--; MyFree((char *)chptr); } }
/* ** Exit one client, local or remote. Assuming all dependants have ** been already removed, and socket closed for local client. */ static void exit_one_client(aClient *cptr, aClient *sptr, aClient *from, const char *comment) { Reg aClient *acptr; Reg int i; Reg Link *lp; invLink *ilp; /* ** For a server or user quitting, propagage the information to ** other servers (except to the one where is came from (cptr)) */ if (IsMe(sptr)) { sendto_flag(SCH_ERROR, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(sptr)) { /* ** Old sendto_serv_but_one() call removed because we now ** need to send different names to different servers ** (domain name matching) */ if (!IsMasked(sptr)) { istat.is_serv--; } if (!IsBursting(sptr)) { istat.is_eobservers--; } for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (!(sptr->flags & FLAGS_SQUIT)) { /* Make sure we only send the last SQUIT ** to a 2.11. */ continue; } if ((acptr->flags & FLAGS_HIDDEN) && !IsMasked(sptr)) { /* We need a special SQUIT reason, so ** the remote server can send the ** right quit message. */ sendto_one(acptr, ":%s SQUIT %s :%s %s", sptr->serv->up->serv->sid, sptr->serv->sid, sptr->serv->up->name, sptr->name); } else { sendto_one(acptr, ":%s SQUIT %s :%s", sptr->serv->up->serv->sid, sptr->serv->sid, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SQUIT, sptr->serv, sptr, ":%s SQUIT %s :%s", from->name, sptr->name, comment); #endif del_from_sid_hash_table(sptr->serv); remove_server_from_tree(sptr); /* remove server from svrtop */ unregister_server(sptr); } else if (!IsPerson(sptr) && !IsService(sptr)) { /* ...this test is *dubious*, would need ** some thougth.. but for now it plugs a ** nasty hole in the server... --msa */ ; /* Nothing */ } else if (sptr->name[0] && !IsService(sptr)) /* clean with QUIT... */ { /* ** If this exit is generated from "m_kill", then there ** is no sense in sending the QUIT--KILL's have been ** sent instead. */ if ((sptr->flags & FLAGS_KILLED) == 0) { if ((sptr->flags & FLAGS_SPLIT) == 0) { sendto_serv_butone(cptr, ":%s QUIT :%s", sptr->user->uid, comment); #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT| SERVICE_WANT_RQUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } else { if (sptr->flags & FLAGS_HIDDEN) /* joys of hostmasking */ for (i = fdas.highest; i >= 0; i--) { if (!(acptr =local[fdas.fd[i]]) || acptr == cptr || IsMe(acptr)) continue; if (acptr->flags & FLAGS_HIDDEN) sendto_one(acptr, ":%s QUIT :%s", sptr->user->uid, comment); } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } } #ifdef USE_SERVICES else { /* Send QUIT to services which desire such as well. ** Services with both _QUIT and _KILL will get both ** for now --jv */ check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); } #endif /* ** If a person is on a channel, send a QUIT notice ** to every client (person) on the same channel (so ** that the client can show the "**signoff" message). ** (Note: The notice is to the local clients *only*) */ if (sptr->user) { if (IsInvisible(sptr)) { istat.is_user[1]--; sptr->user->servp->usercnt[1]--; } else { istat.is_user[0]--; sptr->user->servp->usercnt[0]--; } if (IsAnOper(sptr)) { sptr->user->servp->usercnt[2]--; istat.is_oper--; } sendto_common_channels(sptr, ":%s QUIT :%s", sptr->name, comment); if (!(acptr = cptr ? cptr : sptr->from)) acptr = sptr; while ((lp = sptr->user->channel)) { /* ** Mark channels from where remote chop left, ** this will eventually lock the channel. ** close_connection() has already been called, ** it makes MyConnect == False - krys */ if (sptr != cptr) { if (*lp->value.chptr->chname == '!') { if (!(sptr->flags &FLAGS_QUIT)) lp->value.chptr->history = timeofday + LDELAYCHASETIMELIMIT; } else if ( #ifndef BETTER_CDELAY !(sptr->flags & FLAGS_QUIT) && #endif is_chan_op(sptr, lp->value.chptr)) { lp->value.chptr->history = timeofday + DELAYCHASETIMELIMIT; } } if (IsAnonymous(lp->value.chptr) && !IsQuiet(lp->value.chptr)) { sendto_channel_butserv(lp->value.chptr, sptr, ":%s PART %s :None", sptr->name, lp->value.chptr->chname); } remove_user_from_channel(sptr,lp->value.chptr); } /* Clean up invitefield */ while ((ilp = sptr->user->invited)) { del_invite(sptr, ilp->chptr); /* again, this is all that is needed */ } /* remove from uid hash table */ if (sptr->user) { del_from_uid_hash_table(sptr->user->uid, sptr); } /* Add user to history */ #ifndef BETTER_NDELAY add_history(sptr, (sptr->flags & FLAGS_QUIT) ? &me : NULL); #else add_history(sptr, (sptr == cptr) ? &me : NULL); #endif off_history(sptr); #ifdef USE_HOSTHASH del_from_hostname_hash_table(sptr->user->host, sptr->user); #endif #ifdef USE_IPHASH del_from_ip_hash_table(sptr->user->sip, sptr->user); #endif } } else if (sptr->name[0] && IsService(sptr)) { /* ** If this exit is generated from "m_kill", then there ** is no sense in sending the QUIT--KILL's have been ** sent instead. */ if ((sptr->flags & FLAGS_KILLED) == 0) { /* ** A service quitting is annoying, It has to be sent ** to connected servers depending on ** sptr->service->dist */ for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (match(sptr->service->dist, acptr->name) && match(sptr->service->dist, acptr->serv->sid)) { continue; } sendto_one(acptr, ":%s QUIT :%s", sptr->name, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SERVICE, NULL, NULL, ":%s QUIT :%s", sptr->name, comment); #endif /* MyConnect(sptr) is always FALSE here */ if (cptr == sptr) { sendto_flag(SCH_NOTICE, "Service %s disconnected", get_client_name(sptr, TRUE)); } sendto_flag(SCH_SERVICE, "Received QUIT %s from %s (%s)", sptr->name, from->name, comment); istat.is_service--; } /* Remove sptr from the client list */ if (del_from_client_hash_table(sptr->name, sptr) != 1) { Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x", sptr, sptr->name, sptr->from ? sptr->from->sockhost : "??host", sptr->from, sptr->next, sptr->prev, sptr->fd, sptr->status, sptr->user)); } remove_client_from_list(sptr); return; }
void do_join(struct Client *cptr, struct Client *sptr, struct JoinBuf *join, struct JoinBuf *create, char *chan, char *key, int level) { struct Channel *chptr; struct Gline *gline; /* BADCHANed channel */ if ((gline = gline_find(chan, GLINE_BADCHAN | GLINE_EXACT)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BANNEDFROMCHAN, chan); return; } /* Bouncy +L joins */ if (level > feature_int(FEAT_MAX_BOUNCE)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :*** Couldn't join %s ! - Redirection (+L) setting was too bouncy", sptr, chan); return; } if (!(chptr = FindChannel(chan))) { if (((chan[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(chan) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, chan); return; } if (feature_bool(FEAT_CHANNEL_CREATE_IRCOPONLY) && !IsAnOper(sptr) && !IsChannelService(sptr)) { send_reply(sptr, ERR_NOSUCHCHANNEL, chan); return; } if (!(chptr = get_channel(sptr, chan, CGT_CREATE))) return; /* Try to add the new channel as a recent target for the user. */ if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); return; } joinbuf_join(create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER); } else if (find_member_link(chptr, sptr)) { return; /* already on channel */ } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) { return; } else { int flags = CHFL_DEOPPED; int err = 0; int excepted = 0; int exceptkli = 0; struct Ban *ban = NULL; if (*chptr->mode.redir && (*chptr->mode.redir != '\0')) { if (chptr->users >= chptr->mode.limit) { if (IsNoLink(sptr)) send_reply(sptr, ERR_LINKCHAN, chptr->chname, chptr->mode.redir); else if (!IsChannelName(chptr->mode.redir) || !strIsIrcCh(chptr->mode.redir)) send_reply(sptr, ERR_NOSUCHCHANNEL, chptr->mode.redir); else { send_reply(sptr, ERR_LINKSET, chptr->chname, chptr->chname, chptr->mode.redir); do_join(cptr, sptr, join, create, chptr->mode.redir, key, level+1); } return; } } if (find_ban(sptr, chptr->exceptlist, EBAN_EXCEPTLIST, 0)) { if (feature_bool(FEAT_CHMODE_e_CHMODEEXCEPTION)) exceptkli = 1; excepted = 1; } /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (feature_bool(FEAT_CHMODE_Z_STRICT) && (chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr)) err = ERR_SSLONLYCHAN; else if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; else if (chptr->users == 0 && !chptr->mode.apass[0] && !(chptr->mode.exmode & EXMODE_PERSIST)) { /* Joining a zombie channel (zannel): give ops and increment TS. */ flags = CHFL_CHANOP; chptr->creationtime++; } else if (IsXtraOp(sptr)) { /* XtraOp bypasses all other checks. */ } else if ((chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr)) err = ERR_SSLONLYCHAN; else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key)) && !exceptkli) err = ERR_BADCHANNELKEY; else if (*chptr->mode.key && feature_bool(FEAT_FLEXIBLEKEYS) && (key && !strcmp(key, chptr->mode.key))) { /* Assume key checked by previous condition was found to be correct and allow join because FEAT_FLEXIBLEKEYS was enabled */ } else if ((chptr->mode.mode & MODE_INVITEONLY) && !exceptkli) err = ERR_INVITEONLYCHAN; else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit) && !exceptkli) err = ERR_CHANNELISFULL; else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr)) err = ERR_NEEDREGGEDNICK; else if ((chptr->mode.exmode & EXMODE_ADMINONLY) && !IsAdmin(sptr)) err = ERR_ADMINONLYCHAN; else if ((chptr->mode.exmode & EXMODE_OPERONLY) && !IsAnOper(sptr)) err = ERR_OPERONLYCHAN; else if ((ban = find_ban(sptr, chptr->banlist, EBAN_NONE, 0)) && !excepted) err = ERR_BANNEDFROMCHAN; /* An oper with WALK_LCHAN privilege can join a local channel * he otherwise could not join by using "OVERRIDE" as the key. * This will generate a HACK(4) notice, but fails if the oper * could normally join the channel. */ if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: if (strcmp(chptr->mode.key, "OVERRIDE") && strcmp(chptr->mode.apass, "OVERRIDE") && strcmp(chptr->mode.upass, "OVERRIDE")) { send_reply(sptr, ERR_DONTCHEAT, chptr->chname); return; } break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; case ERR_BADCHANNELKEY: err = 'k'; break; case ERR_NEEDREGGEDNICK: err = 'r'; break; case ERR_ADMINONLYCHAN: err = 'a'; break; case ERR_OPERONLYCHAN: err = 'O'; break; case ERR_SSLONLYCHAN: err = 'Z'; break; default: err = '?'; break; } /* send accountability notice */ if (err) sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { switch(err) { case ERR_NEEDREGGEDNICK: send_reply(sptr, ERR_NEEDREGGEDNICK, chptr->chname, feature_str(FEAT_URLREG)); break; default: send_reply(sptr, err, chptr->chname); break; } return; } joinbuf_join(join, chptr, flags); if (flags & CHFL_CHANOP) { struct ModeBuf mbuf; /* Always let the server op him: this is needed on a net with older servers because they 'destruct' channels immediately when they become empty without sending out a DESTRUCT message. As a result, they would always bounce a mode (as HACK(2)) when the user ops himself. (There is also no particularly good reason to have the user op himself.) */ modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); modebuf_flush(&mbuf); } } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ }
/* Rewritten by Run - 24 sept 94 */ static void exit_one_client(struct Client* bcptr, const char* comment) { struct SLink *lp; struct Ban *bp; if (cli_serv(bcptr) && cli_serv(bcptr)->client_list) /* Was SetServerYXX called ? */ ClearServerYXX(bcptr); /* Removes server from server_list[] */ if (IsUser(bcptr)) { /* * clear out uping requests */ if (IsUPing(bcptr)) uping_cancel(bcptr, 0); /* * Stop a running /LIST clean */ if (MyUser(bcptr) && cli_listing(bcptr)) { MyFree(cli_listing(bcptr)); cli_listing(bcptr) = NULL; } /* * If a person is on a channel, send a QUIT notice * to every client (person) on the same channel (so * that the client can show the "**signoff" message). * (Note: The notice is to the local clients *only*) */ sendcmdto_common_channels_butone(bcptr, CMD_QUIT, NULL, ":%s", comment); remove_user_from_all_channels(bcptr); /* Clean up invitefield */ while ((lp = cli_user(bcptr)->invited)) del_invite(bcptr, lp->value.chptr); /* Clean up silencefield */ while ((bp = cli_user(bcptr)->silence)) { cli_user(bcptr)->silence = bp->next; free_ban(bp); } /* Clean up snotice lists */ if (MyUser(bcptr)) set_snomask(bcptr, ~0, SNO_DEL); if (IsInvisible(bcptr)) { assert(UserStats.inv_clients > 0); --UserStats.inv_clients; } if (IsOper(bcptr)) { assert(UserStats.opers > 0); --UserStats.opers; } if (MyConnect(bcptr)) Count_clientdisconnects(bcptr, UserStats); else Count_remoteclientquits(UserStats, bcptr); } else if (IsServer(bcptr)) { /* Remove downlink list node of uplink */ remove_dlink(&(cli_serv(cli_serv(bcptr)->up))->down, cli_serv(bcptr)->updown); cli_serv(bcptr)->updown = 0; if (MyConnect(bcptr)) Count_serverdisconnects(UserStats); else Count_remoteserverquits(UserStats); } else if (IsMe(bcptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self! */ } else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr)) Count_unknowndisconnects(UserStats); /* * Update IPregistry */ if (IsIPChecked(bcptr)) IPcheck_disconnect(bcptr); /* * Remove from serv->client_list * NOTE: user is *always* NULL if this is a server */ if (cli_user(bcptr)) { assert(!IsServer(bcptr)); /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */ RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr)); } /* Remove bcptr from the client list */ #ifdef DEBUGMODE if (hRemClient(bcptr) != 0) Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p", bcptr, cli_name(bcptr), cli_from(bcptr) ? cli_sockhost(cli_from(bcptr)) : "??host", cli_from(bcptr), cli_next(bcptr), cli_prev(bcptr), cli_fd(bcptr), cli_status(bcptr), cli_user(bcptr))); #else hRemClient(bcptr); #endif remove_client_from_list(bcptr); }
/* ** Exit one client, local or remote. Assuming all dependents have ** been already removed, and socket closed for local client. */ static void exit_one_client(struct Client *client_p, struct Client *source_p, struct Client *from, const char *comment) { struct Client* target_p; dlink_node *lp; dlink_node *next_lp; if (IsServer(source_p)) { if (source_p->servptr && source_p->servptr->serv) del_client_from_llist(&(source_p->servptr->serv->servers), source_p); else ts_warn("server %s without servptr!", source_p->name); if(!IsMe(source_p)) remove_server_from_list(source_p); } else if (source_p->servptr && source_p->servptr->serv) { del_client_from_llist(&(source_p->servptr->serv->users), source_p); } /* there are clients w/o a servptr: unregistered ones */ /* ** For a server or user quitting, propogate the information to ** other servers (except to the one where is came from (client_p)) */ if (IsMe(source_p)) { sendto_realops_flags(FLAGS_ALL, L_ALL, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(source_p)) { /* ** Old sendto_serv_but_one() call removed because we now ** need to send different names to different servers ** (domain name matching) */ /* ** The bulk of this is done in remove_dependents now, all ** we have left to do is send the SQUIT upstream. -orabidoo */ if (source_p->localClient) { if(source_p->localClient->ctrlfd > -1) { fd_close(source_p->localClient->ctrlfd); source_p->localClient->ctrlfd = -1; #ifndef HAVE_SOCKETPAIR fd_close(source_p->localClient->ctrlfd_r); fd_close(source_p->localClient->fd_r); source_p->localClient->ctrlfd_r = -1; source_p->localClient->fd_r = -1; #endif } } target_p = source_p->from; if (target_p && IsServer(target_p) && target_p != client_p && !IsMe(target_p) && (source_p->flags & FLAGS_KILLED) == 0) sendto_one(target_p, ":%s SQUIT %s :%s", from->name, source_p->name, comment); } else if (source_p->name[0]) /* ...just clean all others with QUIT... */ { /* ** If this exit is generated from "m_kill", then there ** is no sense in sending the QUIT--KILL's have been ** sent instead. */ if ((source_p->flags & FLAGS_KILLED) == 0) { sendto_server(client_p, source_p, NULL, NOCAPS, NOCAPS, NOFLAGS, ":%s QUIT :%s", source_p->name, comment); } /* ** If a person is on a channel, send a QUIT notice ** to every client (person) on the same channel (so ** that the client can show the "**signoff" message). ** (Note: The notice is to the local clients *only*) */ if (source_p->user) { sendto_common_channels_local(source_p, ":%s!%s@%s QUIT :%s", source_p->name, source_p->username, source_p->host, comment); for (lp = source_p->user->channel.head; lp; lp = next_lp) { next_lp = lp->next; remove_user_from_channel(lp->data, source_p); } /* Should not be in any channels now */ assert(source_p->user->channel.head == NULL); /* Clean up invitefield */ for (lp = source_p->user->invited.head; lp; lp = next_lp) { next_lp = lp->next; del_invite(lp->data, source_p); } /* Clean up allow lists */ del_all_accepts(source_p); add_history(source_p, 0); off_history(source_p); if (HasID(source_p)) del_from_id_hash_table(source_p->user->id, source_p); /* again, this is all that is needed */ } } /* * Remove source_p from the client lists */ del_from_client_hash_table(source_p->name, source_p); /* remove from global client list */ remove_client_from_list(source_p); /* Check to see if the client isn't already on the dead list */ assert(dlinkFind(&dead_list, source_p) == NULL); /* add to dead client dlist */ lp = make_dlink_node(); SetDead(source_p); dlinkAdd(source_p, lp, &dead_list); }
/* * m_join * parv[0] = sender prefix * parv[1] = channel * parv[2] = channel password (key) */ static void m_join(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Channel *chptr = NULL; char *name, *key = NULL; int i, flags = 0; char *p = NULL, *p2 = NULL; int successful_join_count = 0; /* Number of channels successfully joined */ if (*parv[1] == '\0') { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "JOIN"); return; } if (parc > 2) { key = strtoken(&p2, parv[2], ","); } for (name = strtoken(&p, parv[1], ","); name; key = (key) ? strtoken(&p2, NULL, ",") : NULL, name = strtoken(&p, NULL, ",")) { if(!check_channel_name(name)) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, source_p->name, (unsigned char*)name); continue; } /* ** JOIN 0 sends out a part for all channels a user ** has joined. ** ** this should be either disabled or selectable in ** config file .. it's abused a lot more than it's ** used these days :/ --is */ if (*name == '0' && !atoi(name)) { if (source_p->user->channel.head == NULL) continue; do_join_0(&me,source_p); continue; } /* check it begins with # or & */ else if(!IsChannelName(name)) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); continue; } if(ConfigServerHide.disable_local_channels && (*name == '&')) { sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), me.name, source_p->name, name); continue; } /* check the length */ if (strlen(name) > CHANNELLEN) { sendto_one(source_p, form_str(ERR_BADCHANNAME), me.name, source_p->name, name); continue; } /* see if its resv'd */ if(find_channel_resv(name) && (!IsOper(source_p) || !ConfigChannel.no_oper_resvs)) { sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), me.name, source_p->name, name); sendto_realops_flags(UMODE_SPY, L_ALL, "User %s (%s@%s) is attempting to join locally juped channel %s", source_p->name, source_p->username, source_p->host, name); continue; } /* look for the channel */ if((chptr = hash_find_channel(name)) != NULL) { if(IsMember(source_p, chptr)) return; if(splitmode && !IsOper(source_p) && (*name != '&') && ConfigChannel.no_join_on_split) { sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), me.name, source_p->name, name); continue; } if (chptr->users == 0) flags = CHFL_CHANOP; else flags = 0; } else { if(splitmode && !IsOper(source_p) && (*name != '&') && (ConfigChannel.no_create_on_split || ConfigChannel.no_join_on_split)) { sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), me.name, source_p->name, name); continue; } flags = CHFL_CHANOP; } if ((source_p->user->joined >= ConfigChannel.max_chans_per_user) && (!IsOper(source_p) || (source_p->user->joined >= ConfigChannel.max_chans_per_user*3))) { sendto_one(source_p, form_str(ERR_TOOMANYCHANNELS), me.name, parv[0], name); if(successful_join_count) source_p->localClient->last_join_time = CurrentTime; return; } if(flags == 0) /* if channel doesn't exist, don't penalize */ successful_join_count++; if(chptr == NULL) /* If I already have a chptr, no point doing this */ { chptr = get_or_create_channel(source_p, name, NULL); } if(chptr == NULL) { sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), me.name, parv[0], name); if(successful_join_count > 0) successful_join_count--; continue; } if (!IsOper(source_p)) check_spambot_warning(source_p, name); /* can_join checks for +i key, bans etc */ if ( (i = can_join(source_p, chptr, key)) ) { sendto_one(source_p, form_str(i), me.name, parv[0], name); if(successful_join_count > 0) successful_join_count--; continue; } /* add the user to the channel */ add_user_to_channel(chptr, source_p, flags); /* we send the user their join here, because we could have to * send a mode out next. */ sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", source_p->name, source_p->username, source_p->host, chptr->chname); /* if theyre joining opped (ie, new chan or joining one thats * persisting) then set timestamp to current, set +nt and * broadcast the sjoin with its old modes, or +nt. */ if (flags & CHFL_CHANOP) { char mbuf[MODEBUFLEN]; char pbuf[MODEBUFLEN]; chptr->channelts = CurrentTime; chptr->mode.mode |= MODE_TOPICLIMIT; chptr->mode.mode |= MODE_NOPRIVMSGS; sendto_channel_local(ONLY_CHANOPS, chptr, ":%s MODE %s +nt", me.name, chptr->chname); if(*chptr->chname == '#') { channel_modes(chptr, source_p, mbuf, pbuf); strlcat(mbuf, " ", sizeof(mbuf)); if(pbuf[0] != '\0') strlcat(mbuf, pbuf, sizeof(mbuf)); /* note: mbuf here will have a trailing space. we add one above, * and channel_modes() will leave a trailing space on pbuf if * its used --fl */ sendto_server(client_p, chptr, NOCAPS, NOCAPS, ":%s SJOIN %lu %s %s:@%s", me.name, (unsigned long) chptr->channelts, chptr->chname, mbuf, parv[0]); } /* drop our +beI modes */ free_channel_list(&chptr->banlist); free_channel_list(&chptr->exceptlist); free_channel_list(&chptr->invexlist); } else { sendto_server(client_p, chptr, NOCAPS, NOCAPS, ":%s SJOIN %lu %s + :%s", me.name, (unsigned long) chptr->channelts, chptr->chname, parv[0]); } del_invite(chptr, source_p); if (chptr->topic != NULL) { sendto_one(source_p, form_str(RPL_TOPIC), me.name, parv[0], chptr->chname, chptr->topic); sendto_one(source_p, form_str(RPL_TOPICWHOTIME), me.name, parv[0], chptr->chname, chptr->topic_info, chptr->topic_time); } channel_member_names(source_p, chptr, chptr->chname, 1); if(successful_join_count) source_p->localClient->last_join_time = CurrentTime; } }
/* 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 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. * this code has a side effect of losing keys, but.. */ 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 &, and local chans are disabled */ else if(!IsChannelName(name) || (!ConfigChannel.use_local_channels && name[0] == '&')) { sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, form_str(ERR_NOSUCHCHANNEL), 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 { 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; if(ConfigChannel.founder_on_channel_create && ConfigChannel.use_founder) flags |= CHFL_FOUNDER; if(ConfigChannel.admin_on_channel_create && ConfigChannel.use_admin) flags |= CHFL_ADMIN; 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); /* autochanmodes stuff */ if(ConfigChannel.autochanmodes) { char * ch; for(ch = ConfigChannel.autochanmodes; *ch != '\0'; ch++) { chptr->mode.mode |= chmode_table[(unsigned int)*ch].mode_type; } } else { chptr->mode.mode |= MODE_TOPICLIMIT; chptr->mode.mode |= MODE_NOPRIVMSGS; } modes = channel_modes(chptr, &me); sendto_channel_local(ONLY_HALFOPSANDUP, chptr, ":%s MODE %s %s", me.name, chptr->chname, modes); sendto_server(target_p, chptr, CAP_TS6, NOCAPS, ":%s SJOIN %ld %s %s :%s%s", me.id, (long) chptr->channelts, chptr->chname, modes, flags & CHFL_FOUNDER ? "~@" : "@", get_id(target_p, client_p)); target_p->localClient->last_join_time = rb_current_time(); channel_member_names(chptr, target_p, 1); /* 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; }
/* ** m_cjoin ** parv[0] = sender prefix ** parv[1] = channel ** parv[2] = channel password (key) */ static void m_cjoin(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { static char jbuf[BUFSIZE]; struct Channel *chptr = NULL; struct Channel *vchan_chptr = NULL; struct Channel *root_vchan = NULL; char *name; char *p = NULL; if (!(source_p->user)) { /* something is *f****d* - bail */ return; } if(ConfigChannel.use_vchans == 0) { sendto_one(source_p, form_str(source_p,ERR_VCHANDISABLED), me.name, parv[0]); return; } if (ConfigChannel.vchans_oper_only && !IsOper(source_p)) { sendto_one(source_p, form_str(source_p,ERR_NOPRIVILEGES), me.name, parv[0]); return; } if (*parv[1] == '\0') { sendto_one(source_p, form_str(source_p,ERR_NEEDMOREPARAMS), me.name, parv[0], "CJOIN"); return; } /* Ok, only allowed to CJOIN already existing channels * so first part simply verifies the "root" channel exists first */ *jbuf = '\0'; name = parv[1]; while (*name == ',') name++; if ((p = strchr(name,',')) != NULL) *p = '\0'; if (!*name) return; if (!check_channel_name(name)) { sendto_one(source_p, form_str(source_p,ERR_BADCHANNAME), me.name, parv[0], (unsigned char*) name); return; } if (*name == '&') { sendto_one(source_p, form_str(source_p,ERR_BADCHANNAME), me.name, parv[0], (unsigned char*) name); return; } if (!IsChannelName(name)) { sendto_one(source_p, form_str(source_p,ERR_NOSUCHCHANNEL), me.name, parv[0], name); return; } strlcpy(jbuf, name, sizeof(jbuf)); if ((chptr = hash_find_channel(name)) == NULL) { /* if chptr isn't found locally, it =could= exist * on the uplink. So ask. */ if ( !ServerInfo.hub && uplink && IsCapable(uplink, CAP_LL)) { /* cache the channel if it exists on uplink * If the channel as seen by the uplink, has vchans, * the uplink will have to SJOIN all of those. */ sendto_one(uplink, ":%s CBURST %s !%s", me.name, parv[1], source_p->name); return; } else { sendto_one(source_p, form_str(source_p,ERR_NOSUCHCHANNEL), me.name, source_p->name, name); } return; } if (! (vchan_chptr = cjoin_channel(chptr, source_p, name)) ) return; root_vchan = chptr; chptr = vchan_chptr; /* ** Complete user entry to the new channel */ add_user_to_channel(chptr, source_p, CHFL_CHANOP); sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", source_p->name, source_p->username, source_p->host, root_vchan->chname); sendto_server(client_p, NULL, chptr, NOCAPS, NOCAPS, NOFLAGS, ":%s SJOIN %lu %s + :@%s", me.name, (unsigned long) chptr->channelts, chptr->chname, source_p->name); vchan_chptr->mode.mode |= MODE_TOPICLIMIT; vchan_chptr->mode.mode |= MODE_NOPRIVMSGS; sendto_channel_local(ALL_MEMBERS,chptr, ":%s MODE %s +nt", me.name, root_vchan->chname); sendto_server(source_p, NULL, vchan_chptr, NOCAPS, NOCAPS, NOFLAGS, ":%s MODE %s +nt", me.name, vchan_chptr->chname); del_invite(vchan_chptr, source_p); channel_member_names(source_p, vchan_chptr, root_vchan->chname, 1); }