/* Contains the logic to decide wether we want to punish someone. Returns * true (1) if we want to, false (0) if not. */ static int want_to_revenge(struct chanset_t *chan, struct userrec *u, struct userrec *u2, char *badnick, char *victim, int mevictim) { struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; /* Do not take revenge upon ourselves. */ if (match_my_nick(badnick)) return 0; get_user_flagrec(u, &fr, chan->dname); /* Kickee is not a friend? */ if (!chan_friend(fr) && !glob_friend(fr) && rfc_casecmp(badnick, victim)) { if (mevictim && channel_revengebot(chan)) return 1; else if (channel_revenge(chan) && u2) { struct flag_record fr2 = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; get_user_flagrec(u2, &fr2, chan->dname); /* Protecting friends? */ if ((channel_protectfriends(chan) && (chan_friend(fr2) || (glob_friend(fr2) && !chan_deop(fr2)))) || (channel_protectops(chan) && (chan_op(fr2) || (glob_op(fr2) && !chan_deop(fr2))))) return 1; } } return 0; }
/* private returns 0 if user has access, and 1 if they dont because of +private * This function does not check if the user has "op" access, it only checks if the user is * restricted by +private for the channel */ int privchan(struct flag_record fr, struct chanset_t *chan, int type) { if (!chan || !channel_privchan(chan) || glob_bot(fr) || glob_owner(fr)) return 0; /* user is implicitly not restricted by +private, they may however be lacking other flags */ if (type == PRIV_OP) { /* |o implies all flags above. n| has access to all +private. Bots are exempt. */ if (chan_op(fr)) return 0; } else if (type == PRIV_VOICE) { if (chan_voice(fr) || chan_op(fr)) return 0; } return 1; /* user is restricted by +private */ }
static void got_unban(struct chanset_t *chan, char *nick, char *from, char *who, char *ch, struct userrec *u) { masklist *b, *old; old = NULL; for (b = chan->channel.ban; b->mask[0] && rfc_casecmp(b->mask, who); old = b, b = b->next); if (b->mask[0]) { if (old) old->next = b->next; else chan->channel.ban = b->next; nfree(b->mask); nfree(b->who); nfree(b); } check_tcl_mode(nick, from, u, chan->dname, "-b", who); if (!(chan = modebind_refresh(ch, from, &user, NULL, NULL))) return; if (channel_pending(chan)) return; if ((u_sticky_mask(chan->bans, who) || u_sticky_mask(global_bans, who)) || ((u_equals_mask(global_bans, who) || u_equals_mask(chan->bans, who)) && !channel_dynamicbans(chan) && ((!glob_bot(user) || !(bot_flags(u) & BOT_SHARE)) && ((!glob_op(user) || chan_deop(user)) && !chan_op(user)) && ((!glob_halfop(user) || chan_dehalfop(user)) && !chan_halfop(user))))) add_mode(chan, '+', 'b', who); }
int real_chk_deop(struct flag_record fr, struct chanset_t *chan, bool botbitch) { if (chan && botbitch && channel_botbitch(chan) && !glob_bot(fr)) return 1; if (chan_deop(fr) || (glob_deop(fr) && !chan_op(fr))) return 1; else return 0; }
int real_chk_op(struct flag_record fr, struct chanset_t *chan, bool botbitch) { if (!chan && glob_op(fr)) return 1; else if (!chan) return 0; if (!privchan(fr, chan, PRIV_OP) && !real_chk_deop(fr, chan, botbitch)) { if (chan_op(fr) || (glob_op(fr) && !chan_deop(fr))) return 1; } return 0; }
/* get icon symbol for a user (depending on access level) * (*)owner on any channel * (+)master on any channel * (%) botnet master * (@) op on any channel * (-) other */ char geticon(int idx) { struct flag_record fr = {FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0}; if (!dcc[idx].user) return '-'; get_user_flagrec(dcc[idx].user, &fr, 0); if (chan_owner(fr)) return '*'; if (chan_master(fr)) return '+'; if (glob_botmast(fr)) return '%'; if (chan_op(fr)) return '@'; return '-'; }
static void check_expired_chanstuff() { masklist *b, *e; memberlist *m, *n; char *key, s[UHOSTLEN]; struct chanset_t *chan; struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; if (!server_online) return; for (chan = chanset; chan; chan = chan->next) { if (channel_active(chan)) { if (me_op(chan) || me_halfop(chan)) { if (channel_dynamicbans(chan) && chan->ban_time) for (b = chan->channel.ban; b->mask[0]; b = b->next) if (now - b->timer > 60 * chan->ban_time && !u_sticky_mask(chan->bans, b->mask) && !u_sticky_mask(global_bans, b->mask) && expired_mask(chan, b->who)) { putlog(LOG_MODES, chan->dname, "(%s) Channel ban on %s expired.", chan->dname, b->mask); add_mode(chan, '-', 'b', b->mask); b->timer = now; } if (use_exempts && channel_dynamicexempts(chan) && chan->exempt_time) for (e = chan->channel.exempt; e->mask[0]; e = e->next) if (now - e->timer > 60 * chan->exempt_time && !u_sticky_mask(chan->exempts, e->mask) && !u_sticky_mask(global_exempts, e->mask) && expired_mask(chan, e->who)) { /* Check to see if it matches a ban */ int match = 0; for (b = chan->channel.ban; b->mask[0]; b = b->next) if (mask_match(b->mask, e->mask)) { match = 1; break; } /* Leave this extra logging in for now. Can be removed later * Jason */ if (match) { putlog(LOG_MODES, chan->dname, "(%s) Channel exemption %s NOT expired. Exempt still set!", chan->dname, e->mask); } else { putlog(LOG_MODES, chan->dname, "(%s) Channel exemption on %s expired.", chan->dname, e->mask); add_mode(chan, '-', 'e', e->mask); } e->timer = now; } if (use_invites && channel_dynamicinvites(chan) && chan->invite_time && !(chan->channel.mode & CHANINV)) for (b = chan->channel.invite; b->mask[0]; b = b->next) if (now - b->timer > 60 * chan->invite_time && !u_sticky_mask(chan->invites, b->mask) && !u_sticky_mask(global_invites, b->mask) && expired_mask(chan, b->who)) { putlog(LOG_MODES, chan->dname, "(%s) Channel invitation on %s expired.", chan->dname, b->mask); add_mode(chan, '-', 'I', b->mask); b->timer = now; } if (chan->idle_kick) for (m = chan->channel.member; m && m->nick[0]; m = m->next) if (now - m->last >= chan->idle_kick * 60 && !match_my_nick(m->nick) && !chan_issplit(m)) { sprintf(s, "%s!%s", m->nick, m->userhost); get_user_flagrec(m->user ? m->user : get_user_by_host(s), &fr, chan->dname); if ((!(glob_bot(fr) || glob_friend(fr) || (glob_op(fr) && !chan_deop(fr)) || chan_friend(fr) || chan_op(fr))) && (me_op(chan) || (me_halfop(chan) && !chan_hasop(m)))) { dprintf(DP_SERVER, "KICK %s %s :idle %d min\n", chan->name, m->nick, chan->idle_kick); m->flags |= SENTKICK; } } } for (m = chan->channel.member; m && m->nick[0]; m = n) { n = m->next; if (m->split && now - m->split > wait_split) { sprintf(s, "%s!%s", m->nick, m->userhost); check_tcl_sign(m->nick, m->userhost, m->user ? m->user : get_user_by_host(s), chan->dname, "lost in the netsplit"); putlog(LOG_JOIN, chan->dname, "%s (%s) got lost in the net-split.", m->nick, m->userhost); killmember(chan, m->nick); } m = n; } check_lonely_channel(chan); } else if (!channel_inactive(chan) && !channel_pending(chan)) { key = chan->channel.key[0] ? chan->channel.key : chan->key_prot; if (key[0]) dprintf(DP_SERVER, "JOIN %s %s\n", chan->name[0] ? chan->name : chan->dname, key); else dprintf(DP_SERVER, "JOIN %s\n", chan->name[0] ? chan->name : chan->dname); } } }
/* Dependant on revenge_mode, punish the offender. */ static void punish_badguy(struct chanset_t *chan, char *whobad, struct userrec *u, char *badnick, char *victim, int mevictim, int type) { char reason[1024], ct[81], *kick_msg; memberlist *m; struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; m = ismember(chan, badnick); if (!m) return; get_user_flagrec(u, &fr, chan->dname); /* Get current time into a string */ egg_strftime(ct, 7, "%d %b", localtime(&now)); /* Put together log and kick messages */ reason[0] = 0; switch (type) { case REVENGE_KICK: kick_msg = IRC_KICK_PROTECT; simple_sprintf(reason, "kicked %s off %s", victim, chan->dname); break; case REVENGE_DEOP: simple_sprintf(reason, "deopped %s on %s", victim, chan->dname); kick_msg = IRC_DEOP_PROTECT; break; default: kick_msg = "revenge!"; } putlog(LOG_MISC, chan->dname, "Punishing %s (%s)", badnick, reason); /* Set the offender +d */ if ((chan->revenge_mode > 0) && !(chan_deop(fr) || glob_deop(fr))) { char s[UHOSTLEN], s1[UHOSTLEN]; memberlist *mx = NULL; /* Removing op */ if (chan_op(fr) || (glob_op(fr) && !chan_deop(fr))) { fr.match = FR_CHAN; if (chan_op(fr)) fr.chan &= ~USER_OP; else fr.chan |= USER_DEOP; set_user_flagrec(u, &fr, chan->dname); putlog(LOG_MISC, "*", "No longer opping %s[%s] (%s)", u->handle, whobad, reason); } /* ... or just setting to deop */ else if (u) { /* In the user list already, cool :) */ fr.match = FR_CHAN; fr.chan |= USER_DEOP; set_user_flagrec(u, &fr, chan->dname); simple_sprintf(s, "(%s) %s", ct, reason); putlog(LOG_MISC, "*", "Now deopping %s[%s] (%s)", u->handle, whobad, s); } /* ... or creating new user and setting that to deop */ else { strcpy(s1, whobad); maskaddr(s1, s, chan->ban_type); strcpy(s1, badnick); /* If that handle exists use "badX" (where X is an increasing number) * instead. */ while (get_user_by_handle(userlist, s1)) { if (!strncmp(s1, "bad", 3)) { int i; i = atoi(s1 + 3); simple_sprintf(s1 + 3, "%d", i + 1); } else strcpy(s1, "bad1"); /* Start with '1' */ } userlist = adduser(userlist, s1, s, "-", 0); fr.match = FR_CHAN; fr.chan = USER_DEOP; fr.udef_chan = 0; u = get_user_by_handle(userlist, s1); if ((mx = ismember(chan, badnick))) mx->user = u; set_user_flagrec(u, &fr, chan->dname); simple_sprintf(s, "(%s) %s (%s)", ct, reason, whobad); set_user(&USERENTRY_COMMENT, u, (void *) s); putlog(LOG_MISC, "*", "Now deopping %s (%s)", whobad, reason); } } /* Always try to deop the offender */ if (!mevictim) add_mode(chan, '-', 'o', badnick); /* Ban. Should be done before kicking. */ if (chan->revenge_mode > 2) { char s[UHOSTLEN], s1[UHOSTLEN]; splitnick(&whobad); maskaddr(whobad, s1, chan->ban_type); simple_sprintf(s, "(%s) %s", ct, reason); u_addban(chan, s1, botnetnick, s, now + (60 * chan->ban_time), 0); if (!mevictim && HALFOP_CANDOMODE('b')) { add_mode(chan, '+', 'b', s1); flush_mode(chan, QUICK); } } /* Kick the offender */ if (!mevictim && (chan->revenge_mode > 1) && (!channel_dontkickops(chan) || (!chan_op(fr) && (!glob_op(fr) || chan_deop(fr)))) && !chan_sentkick(m) && (me_op(chan) || (me_halfop(chan) && !chan_hasop(m) && (strchr(NOHALFOPS_MODES, 'b') == NULL)))) { dprintf(DP_MODE, "KICK %s %s :%s\n", chan->name, badnick, kick_msg); m->flags |= SENTKICK; } }
/* this only handles CHAT requests, otherwise it's handled in filesys */ static int filesys_DCC_CHAT(char *nick, char *from, char *handle, char *object, char *keyword, char *text) { char *param, *ip, *prt, buf[512], *msg = buf; int i, sock; struct userrec *u = get_user_by_handle(userlist, handle); struct flag_record fr = {FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0}; Context; if (!strncasecmp(text, "SEND ", 5)) { filesys_dcc_send(nick, from, u, text + 5); return 1; } if (strncasecmp(text, "CHAT ", 5) || !u) return 0; strcpy(buf, text + 5); get_user_flagrec(u, &fr, 0); param = newsplit(&msg); if (dcc_total == max_dcc) { putlog(LOG_MISC, "*", DCC_TOOMANYDCCS2, "CHAT(file)", param, nick, from); } else if (glob_party(fr) || (!require_p && chan_op(fr))) return 0; /* allow ctcp.so to pick up the chat */ else if (!glob_xfer(fr)) { if (!quiet_reject) dprintf(DP_HELP, "NOTICE %s :.\n", nick, DCC_REFUSED3); putlog(LOG_MISC, "*", "%s: %s!%s", DCC_REFUSED, nick, from); } else if (u_pass_match(u, "-")) { if (!quiet_reject) dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, DCC_REFUSED3); putlog(LOG_MISC, "*", "%s: %s!%s", DCC_REFUSED4, nick, from); } else if (!dccdir[0]) { putlog(LOG_MISC, "*", "%s: %s!%s", DCC_REFUSED5, nick, from); } else { ip = newsplit(&msg); prt = newsplit(&msg); sock = getsock(0); if (open_telnet_dcc(sock, ip, prt) < 0) { neterror(buf); if (!quiet_reject) dprintf(DP_HELP, "NOTICE %s :%s (%s)\n", nick, DCC_CONNECTFAILED1, buf); putlog(LOG_MISC, "*", "%s: CHAT(file) (%s!%s)", DCC_CONNECTFAILED2, nick, from); putlog(LOG_MISC, "*", " (%s)", buf); killsock(sock); } else if ((atoi(prt) < min_dcc_port) || (atoi(prt) > max_dcc_port)) { /* invalid port range, do clients even use over 5000?? */ if (!quiet_reject) dprintf(DP_HELP, "NOTICE %s :%s (invalid port)\n", nick, DCC_CONNECTFAILED1); putlog(LOG_FILES, "*", "%s: %s!%s", DCC_REFUSED7, nick, from); } else { i = new_dcc(&DCC_FILES_PASS, sizeof(struct file_info)); dcc[i].addr = my_atoul(ip); dcc[i].port = atoi(prt); dcc[i].sock = sock; strcpy(dcc[i].nick, u->handle); strcpy(dcc[i].host, from); dcc[i].status = STAT_ECHO; dcc[i].timeval = now; dcc[i].u.file->chat = get_data_ptr(sizeof(struct chat_info)); bzero(dcc[i].u.file->chat, sizeof(struct chat_info)); strcpy(dcc[i].u.file->chat->con_chan, "*"); dcc[i].user = u; putlog(LOG_MISC, "*", "DCC connection: CHAT(file) (%s!%s)", nick, from); dprintf(i, "%s\n", DCC_ENTERPASS); } } return 1; }
static void got_ban(struct chanset_t *chan, char *nick, char *from, char *who, char *ch, struct userrec *u) { char me[UHOSTLEN], s[UHOSTLEN], s1[UHOSTLEN]; memberlist *m; struct userrec *targ; egg_snprintf(me, sizeof me, "%s!%s", botname, botuserhost); egg_snprintf(s, sizeof s, "%s!%s", nick, from); newban(chan, who, s); check_tcl_mode(nick, from, u, chan->dname, "+b", who); if (!(chan = modebind_refresh(ch, from, &user, NULL, NULL))) return; if (channel_pending(chan) || HALFOP_CANTDOMODE('b')) return; if (match_addr(who, me) && !isexempted(chan, me)) { add_mode(chan, '-', 'b', who); reversing = 1; return; } if (!match_my_nick(nick)) { if (nick[0] && channel_nouserbans(chan) && !glob_bot(user) && !glob_master(user) && !chan_master(user)) { add_mode(chan, '-', 'b', who); return; } for (m = chan->channel.member; m && m->nick[0]; m = m->next) { egg_snprintf(s1, sizeof s1, "%s!%s", m->nick, m->userhost); if (match_addr(who, s1)) { targ = get_user_by_host(s1); if (targ) { get_user_flagrec(targ, &victim, chan->dname); if ((glob_friend(victim) || (glob_op(victim) && !chan_deop(victim)) || chan_friend(victim) || chan_op(victim)) && !glob_master(user) && !glob_bot(user) && !chan_master(user) && !isexempted(chan, s1)) { add_mode(chan, '-', 'b', who); return; } } } } } refresh_exempt(chan, who); if (nick[0] && channel_enforcebans(chan)) { register maskrec *b; int cycle; char resn[512] = ""; for (cycle = 0; cycle < 2; cycle++) { for (b = cycle ? chan->bans : global_bans; b; b = b->next) { if (match_addr(b->mask, who)) { if (b->desc && b->desc[0] != '@') egg_snprintf(resn, sizeof resn, "%s %s", IRC_PREBANNED, b->desc); else resn[0] = 0; } } } kick_all(chan, who, resn[0] ? resn : IRC_BANNED, match_my_nick(nick) ? 0 : 1); } if (!nick[0] && (bounce_bans || bounce_modes) && (!u_equals_mask(global_bans, who) || !u_equals_mask(chan->bans, who))) add_mode(chan, '-', 'b', who); }
static void got_deop(struct chanset_t *chan, char *nick, char *from, char *who, struct userrec *opu) { memberlist *m; char ch[sizeof chan->name]; char s[UHOSTLEN], s1[UHOSTLEN]; struct userrec *u; int had_halfop; m = ismember(chan, who); if (!m) { if (channel_pending(chan)) return; putlog(LOG_MISC, chan->dname, CHAN_BADCHANMODE, chan->dname, who); chan->status |= CHAN_PEND; refresh_who_chan(chan->name); return; } strcpy(ch, chan->name); simple_sprintf(s, "%s!%s", m->nick, m->userhost); simple_sprintf(s1, "%s!%s", nick, from); u = get_user_by_host(s); get_user_flagrec(u, &victim, chan->dname); had_halfop = chan_hasop(m); /* Flags need to be set correctly right from the beginning now, so that * add_mode() doesn't get irritated. */ m->flags &= ~(CHANOP | SENTDEOP | FAKEOP); check_tcl_mode(nick, from, opu, chan->dname, "-o", who); if (!(chan = modebind_refresh(ch, from, &user, s, &victim)) || !(m = ismember(chan, who))) return; m->flags &= ~WASOP; if (channel_pending(chan)) return; if (HALFOP_CANDOMODE('o')) { int ok = 1; if (!glob_deop(victim) && !chan_deop(victim)) { if (channel_protectops(chan) && (glob_master(victim) || chan_master(victim) || glob_op(victim) || chan_op(victim))) ok = 0; else if (channel_protectfriends(chan) && (glob_friend(victim) || chan_friend(victim))) ok = 0; } if ((reversing || !ok) && had_halfop && !match_my_nick(nick) && rfc_casecmp(who, nick) && !match_my_nick(who) && !glob_master(user) && !chan_master(user) && !glob_bot(user) && ((chan_op(victim) || (glob_op(victim) && !chan_deop(victim))) || !channel_bitch(chan))) add_mode(chan, '+', 'o', who); } if (!nick[0]) putlog(LOG_MODES, chan->dname, "TS resync (%s): %s deopped by %s", chan->dname, who, from); /* Check for mass deop */ if (nick[0]) detect_chan_flood(nick, from, s1, chan, FLOOD_DEOP, who, 0); /* Having op hides your +v and +h status -- so now that someone's lost ops, * check to see if they have +v or +h */ if (!(m->flags & (CHANVOICE | CHANHALFOP | STOPWHO))) { chan->status |= CHAN_PEND; refresh_who_chan(chan->name); m->flags |= STOPWHO; } /* Was the bot deopped? */ if (match_my_nick(who)) { /* Cancel any pending kicks and modes */ memberlist *m2; for (m2 = chan->channel.member; m2 && m2->nick[0]; m2 = m2->next) m2->flags &= ~(SENTKICK | SENTDEOP | SENTOP | SENTVOICE | SENTDEVOICE); check_tcl_need(chan->dname, "op"); if (chan->need_op[0]) do_tcl("need-op", chan->need_op); if (!nick[0]) putlog(LOG_MODES, chan->dname, "TS resync deopped me on %s :(", chan->dname); } if (nick[0]) maybe_revenge(chan, s1, s, REVENGE_DEOP); }
static void got_halfop(struct chanset_t *chan, char *nick, char *from, char *who, struct userrec *opu, struct flag_record *opper) { memberlist *m; char s[UHOSTLEN]; char ch[sizeof chan->name]; struct userrec *u; int check_chan = 0; int snm = chan->stopnethack_mode; m = ismember(chan, who); if (!m) { if (channel_pending(chan)) return; putlog(LOG_MISC, chan->dname, CHAN_BADCHANMODE, chan->dname, who); chan->status |= CHAN_PEND; refresh_who_chan(chan->name); return; } /* Did *I* just get halfopped? */ if (!me_op(chan) && !me_halfop(chan) && match_my_nick(who)) check_chan = 1; strcpy(ch, chan->name); simple_sprintf(s, "%s!%s", m->nick, m->userhost); if (!m->user) u = get_user_by_host(s); else u = m->user; get_user_flagrec(u, &victim, chan->dname); /* Flags need to be set correctly right from the beginning now, so that * add_mode() doesn't get irritated. */ m->flags |= CHANHALFOP; check_tcl_mode(nick, from, opu, chan->dname, "+h", who); if (!(chan = modebind_refresh(ch, from, opper, s, &victim)) || !(m = ismember(chan, who))) return; m->flags &= ~SENTHALFOP; if (channel_pending(chan)) return; if (nick[0] && HALFOP_CANDOMODE('h') && !match_my_nick(who) && !match_my_nick(nick)) { if (channel_bitch(chan) && !(glob_master(*opper) || glob_bot(*opper)) && !chan_master(*opper) && !(glob_halfop(victim) || glob_op(victim) || glob_bot(victim)) && !chan_op(victim) && !chan_halfop(victim)) add_mode(chan, '-', 'h', who); else if ((chan_dehalfop(victim) || (glob_dehalfop(victim) && !chan_halfop(victim))) && !glob_master(*opper) && !chan_master(*opper)) add_mode(chan, '-', 'h', who); else if (reversing) add_mode(chan, '-', 'h', who); } else if (reversing && HALFOP_CANDOMODE('h') && !match_my_nick(who) && !match_my_nick(nick)) add_mode(chan, '-', 'h', who); if (!nick[0] && HALFOP_CANDOMODE('h') && !match_my_nick(who)) { if (chan_dehalfop(victim) || (glob_dehalfop(victim) && !chan_halfop(victim))) { m->flags |= FAKEHALFOP; add_mode(chan, '-', 'h', who); } else if (snm > 0 && snm < 7 && !((channel_autohalfop(chan) || glob_autohalfop(victim) || chan_autohalfop(victim)) && (chan_halfop(victim) || (glob_halfop(victim) && !chan_dehalfop(victim)))) && !glob_exempt(victim) && !chan_exempt(victim)) { if (snm == 5) snm = channel_bitch(chan) ? 1 : 3; if (snm == 6) snm = channel_bitch(chan) ? 4 : 2; if (chan_washalfoptest(victim) || glob_washalfoptest(victim) || snm == 2) { if (!chan_washalfop(m)) { m->flags |= FAKEHALFOP; add_mode(chan, '-', 'h', who); } } else if (!(chan_halfop(victim) || (glob_halfop(victim) && !chan_dehalfop(victim)))) { if (snm == 1 || snm == 4 || (snm == 3 && !chan_washalfop(m))) { add_mode(chan, '-', 'h', who); m->flags |= FAKEHALFOP; } } else if (snm == 4 && !chan_washalfop(m)) { add_mode(chan, '-', 'h', who); m->flags |= FAKEHALFOP; } } } m->flags |= WASHALFOP; if (check_chan) recheck_channel(chan, 1); }
static int gotmode(char *from, char *origmsg) { char *nick, *ch, *op, *chg, *msg; char s[UHOSTLEN], buf[511]; char ms2[3]; int z; struct userrec *u; memberlist *m; struct chanset_t *chan; strncpy(buf, origmsg, 510); buf[510] = 0; msg = buf; /* Usermode changes? */ if (msg[0] && (strchr(CHANMETA, msg[0]) != NULL)) { ch = newsplit(&msg); chg = newsplit(&msg); reversing = 0; chan = findchan(ch); if (!chan) { putlog(LOG_MISC, "*", CHAN_FORCEJOIN, ch); dprintf(DP_SERVER, "PART %s\n", ch); } else if (channel_active(chan) || channel_pending(chan)) { z = strlen(msg); if (msg[--z] == ' ') /* I hate cosmetic bugs :P -poptix */ msg[z] = 0; putlog(LOG_MODES, chan->dname, "%s: mode change '%s %s' by %s", ch, chg, msg, from); u = get_user_by_host(from); get_user_flagrec(u, &user, ch); nick = splitnick(&from); m = ismember(chan, nick); if (m) m->last = now; if (m && channel_active(chan) && (me_op(chan) || (me_halfop(chan) && !chan_hasop(m))) && !(glob_friend(user) || chan_friend(user) || (channel_dontkickops(chan) && (chan_op(user) || (glob_op(user) && !chan_deop(user))))) && !match_my_nick(nick)) { if (chan_fakeop(m) || chan_fakehalfop(m)) { putlog(LOG_MODES, ch, CHAN_FAKEMODE, ch); dprintf(DP_MODE, "KICK %s %s :%s\n", ch, nick, CHAN_FAKEMODE_KICK); m->flags |= SENTKICK; reversing = 1; } else if (!chan_hasop(m) && !chan_hashalfop(m) && !channel_nodesynch(chan)) { putlog(LOG_MODES, ch, CHAN_DESYNCMODE, ch); dprintf(DP_MODE, "KICK %s %s :%s\n", ch, nick, CHAN_DESYNCMODE_KICK); m->flags |= SENTKICK; reversing = 1; } } ms2[0] = '+'; ms2[2] = 0; while ((ms2[1] = *chg)) { int todo = 0; switch (*chg) { case '+': ms2[0] = '+'; break; case '-': ms2[0] = '-'; break; case 'i': todo = CHANINV; if (!nick[0] && bounce_modes) reversing = 1; break; case 'p': todo = CHANPRIV; if (!nick[0] && bounce_modes) reversing = 1; break; case 's': todo = CHANSEC; if (!nick[0] && bounce_modes) reversing = 1; break; case 'm': todo = CHANMODER; if (!nick[0] && bounce_modes) reversing = 1; break; case 'c': todo = CHANNOCLR; if (!nick[0] && bounce_modes) reversing = 1; break; case 'C': todo = CHANNOCTCP; if (!nick[0] && bounce_modes) reversing = 1; break; case 'R': todo = CHANREGON; if (!nick[0] && bounce_modes) reversing = 1; break; case 'M': todo = CHANMODREG; if (!nick[0] && bounce_modes) reversing = 1; break; case 'r': todo = CHANLONLY; if (!nick[0] && bounce_modes) reversing = 1; break; case 'D': todo = CHANDELJN; if (!nick[0] && bounce_modes) reversing = 1; break; case 'u': todo = CHANSTRIP; if (!nick[0] && bounce_modes) reversing = 1; break; case 'N': todo = CHANNONOTC; if (!nick[0] && bounce_modes) reversing = 1; break; case 'T': todo = CHANNOAMSG; if (!nick[0] && bounce_modes) reversing = 1; break; case 'd': todo = CHANINVIS; break; case 't': todo = CHANTOPIC; if (!nick[0] && bounce_modes) reversing = 1; break; case 'n': todo = CHANNOMSG; if (!nick[0] && bounce_modes) reversing = 1; break; case 'a': todo = CHANANON; if (!nick[0] && bounce_modes) reversing = 1; break; case 'q': todo = CHANQUIET; if (!nick[0] && bounce_modes) reversing = 1; break; case 'l': if (!nick[0] && bounce_modes) reversing = 1; if (ms2[0] == '-') { check_tcl_mode(nick, from, u, chan->dname, ms2, ""); /* The Tcl proc might have modified/removed the chan or user */ if (!(chan = modebind_refresh(ch, from, &user, NULL, NULL))) return 0; if (channel_active(chan)) { if (reversing && (chan->channel.maxmembers != 0)) { simple_sprintf(s, "%d", chan->channel.maxmembers); add_mode(chan, '+', 'l', s); } else if ((chan->limit_prot != 0) && !glob_master(user) && !chan_master(user) && !match_my_nick(nick)) { simple_sprintf(s, "%d", chan->limit_prot); add_mode(chan, '+', 'l', s); } } chan->channel.maxmembers = 0; } else { op = newsplit(&msg); fixcolon(op); if (op == '\0') break; chan->channel.maxmembers = atoi(op); check_tcl_mode(nick, from, u, chan->dname, ms2, int_to_base10(chan->channel.maxmembers)); /* The Tcl proc might have modified/removed the chan or user */ if (!(chan = modebind_refresh(ch, from, &user, NULL, NULL))) return 0; if (channel_pending(chan)) break; if ((reversing && !(chan->mode_pls_prot & CHANLIMIT)) || ((chan->mode_mns_prot & CHANLIMIT) && !glob_master(user) && !chan_master(user))) add_mode(chan, '-', 'l', ""); if ((chan->limit_prot != chan->channel.maxmembers) && (chan->mode_pls_prot & CHANLIMIT) && (chan->limit_prot != 0) && !glob_master(user) && !chan_master(user)) { simple_sprintf(s, "%d", chan->limit_prot); add_mode(chan, '+', 'l', s); } } break; case 'k': if (ms2[0] == '+') chan->channel.mode |= CHANKEY; else chan->channel.mode &= ~CHANKEY; op = newsplit(&msg); fixcolon(op); if (op == '\0') { break; } check_tcl_mode(nick, from, u, chan->dname, ms2, op); /* The Tcl proc might have modified/removed the chan or user */ if (!(chan = modebind_refresh(ch, from, &user, NULL, NULL))) return 0; if (ms2[0] == '+') { set_key(chan, op); if (channel_active(chan)) got_key(chan, nick, from, op); } else { if (channel_active(chan)) { if (reversing && chan->channel.key[0]) add_mode(chan, '+', 'k', chan->channel.key); else if (chan->key_prot[0] && !glob_master(user) && !chan_master(user) && !match_my_nick(nick)) add_mode(chan, '+', 'k', chan->key_prot); } set_key(chan, NULL); } break; case 'o': op = newsplit(&msg); fixcolon(op); if (ms2[0] == '+') got_op(chan, nick, from, op, u, &user); else got_deop(chan, nick, from, op, u); break; case 'h': op = newsplit(&msg); fixcolon(op); if (ms2[0] == '+') got_halfop(chan, nick, from, op, u, &user); else got_dehalfop(chan, nick, from, op, u); break; case 'v': op = newsplit(&msg); fixcolon(op); m = ismember(chan, op); if (!m) { if (channel_pending(chan)) break; putlog(LOG_MISC, chan->dname, CHAN_BADCHANMODE, chan->dname, op); chan->status |= CHAN_PEND; refresh_who_chan(chan->name); } else { simple_sprintf(s, "%s!%s", m->nick, m->userhost); get_user_flagrec(m->user ? m->user : get_user_by_host(s), &victim, chan->dname); if (ms2[0] == '+') { m->flags &= ~SENTVOICE; m->flags |= CHANVOICE; check_tcl_mode(nick, from, u, chan->dname, ms2, op); if (!(chan = modebind_refresh(ch, from, &user, s, &victim))) return 0; if (channel_active(chan) && !glob_master(user) && !chan_master(user) && !match_my_nick(nick)) { if (chan_quiet(victim) || (glob_quiet(victim) && !chan_voice(victim))) add_mode(chan, '-', 'v', op); else if (reversing) add_mode(chan, '-', 'v', op); } } else { m->flags &= ~SENTDEVOICE; m->flags &= ~CHANVOICE; check_tcl_mode(nick, from, u, chan->dname, ms2, op); if (!(chan = modebind_refresh(ch, from, &user, s, &victim))) return 0; if (channel_active(chan) && !glob_master(user) && !chan_master(user) && !match_my_nick(nick)) { if ((channel_autovoice(chan) && !chan_quiet(victim) && (chan_voice(victim) || glob_voice(victim))) || (!chan_quiet(victim) && (glob_gvoice(victim) || chan_gvoice(victim)))) add_mode(chan, '+', 'v', op); else if (reversing) add_mode(chan, '+', 'v', op); } } } break; case 'b': op = newsplit(&msg); fixcolon(op); if (ms2[0] == '+') got_ban(chan, nick, from, op, ch, u); else got_unban(chan, nick, from, op, ch, u); break; case 'e': op = newsplit(&msg); fixcolon(op); if (ms2[0] == '+') got_exempt(chan, nick, from, op, ch, u); else got_unexempt(chan, nick, from, op, ch, u); break; case 'I': op = newsplit(&msg); fixcolon(op); if (ms2[0] == '+') got_invite(chan, nick, from, op, ch, u); else got_uninvite(chan, nick, from, op, ch, u); break; } if (todo) { check_tcl_mode(nick, from, u, chan->dname, ms2, ""); if (!(chan = modebind_refresh(ch, from, &user, NULL, NULL))) return 0; if (ms2[0] == '+') chan->channel.mode |= todo; else chan->channel.mode &= ~todo; if (channel_active(chan)) { if ((((ms2[0] == '+') && (chan->mode_mns_prot & todo)) || ((ms2[0] == '-') && (chan->mode_pls_prot & todo))) && !glob_master(user) && !chan_master(user) && !match_my_nick(nick)) add_mode(chan, ms2[0] == '+' ? '-' : '+', *chg, ""); else if (reversing && ((ms2[0] == '+') || (chan->mode_pls_prot & todo)) && ((ms2[0] == '-') || (chan->mode_mns_prot & todo))) add_mode(chan, ms2[0] == '+' ? '-' : '+', *chg, ""); } } chg++; } if (!me_op(chan) && !nick[0]) chan->status |= CHAN_ASKEDMODES; } } return 0; }