/* * IRC - Internet Relay Chat, src/channel.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Co Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id: channel.c,v 1.19 2006/06/25 22:53:01 sheik Exp $ */ #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "channel.h" #include "h.h" #include "userban.h" #include "memcount.h" int server_was_split = YES; aChannel *channel = NullChn; #ifdef INVITE_LISTS /* +I list functions */ int add_invite_id(aClient*, aChannel*, char*); int del_invite_id(aChannel*, char*); anInvite* is_invited(aClient*, aChannel*); #endif #ifdef EXEMPT_LISTS /* +e list functions */ int add_exempt_id(aClient*, aChannel*, char*); int del_exempt_id(aChannel*, char*); #endif static int add_banid(aClient *, aChannel *, char *); static int can_join(aClient *, aChannel *, char *); static void channel_modes(aClient *, char *, char *, aChannel *); static int del_banid(aChannel *, char *); static int is_banned(aClient *, aChannel *, chanMember *); static int set_mode(aClient *, aClient *, aChannel *, int, int, char **, char *, char *); static void sub1_from_channel(aChannel *); int check_channelname(aClient *, unsigned char *); void clean_channelname(unsigned char *); static void add_invite(aClient *, aChannel *); void del_invite(aClient *, aChannel *); #ifdef ORATIMING struct timeval tsdnow, tsdthen; unsigned long tsdms; #endif /* number of seconds to add to all readings of time() when making TS's */ static char *PartFmt = ":%s PART %s"; static char *PartFmt2 = ":%s PART %s :%s"; /* server <-> server SJOIN format */ static char *SJOINFmt = ":%s SJOIN %ld %s %s %s :%s"; /* NP means no paramaters, don't send the extra space there */ static char *SJOINFmtNP = ":%s SJOIN %ld %s %s :%s"; /* client SJOIN format, for no channel creation */ static char *CliSJOINFmt = ":%s SJOIN %ld %s"; /* some buffers for rebuilding channel/nick lists with ,'s */ static char nickbuf[BUFSIZE], buf[BUFSIZE]; static char modebuf[REALMODEBUFLEN], parabuf[REALMODEBUFLEN]; /* externally defined function */ extern Link *find_channel_link(Link *, aChannel *); /* defined in list.c */ #ifdef ANTI_SPAMBOT extern int spam_num; /* defined in s_serv.c */ extern int spam_time; /* defined in s_serv.c */ #endif /* return the length (>=0) of a chain of links. */ static int list_length(Link *lp) { int count = 0; for (; lp; lp = lp->next) count++; return count; } /* check to see if the message has any control chars in it. */ static int msg_has_ctrls(char *msg) { unsigned char *c; if (msg == NULL) return 0; for (c = (unsigned char *) msg; *c; c++) { /* not a control code */ if (*c > 31) continue; /* ctcp */ if (*c == 1) continue; /* escape */ if (*c == 27) { /* ISO 2022 charset shift sequence */ if (c[1] == '$' || c[1] == '(') { c++; continue; } } /* control code */ break; } if(*c) return 1; return 0; } /* * find_chasing * Find the client structure for a nick name (user) using history * mechanism if necessary. If the client is not found, an error message * (NO SUCH NICK) is generated. If the client was found through the * history, chasing will be 1 and otherwise 0. */ aClient *find_chasing(aClient *sptr, char *user, int *chasing) { aClient *who = find_client(user, (aClient *) NULL); if (chasing) *chasing = 0; if (who) return who; if (!(who = get_history(user, (long) KILLCHASETIMELIMIT))) { sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, sptr->name, user); return ((aClient *) NULL); } if (chasing) *chasing = 1; return who; } /* * Fixes a string so that the first white space found becomes an end of * string marker (`\-`). returns the 'fixed' string or "*" if the * string was NULL length or a NULL pointer. */ static char * check_string(char *s) { static char star[2] = "*"; char *str = s; if (BadPtr(s)) return star; for (; *s; s++) if (IsSpace(*s)) { *s = '\0'; break; } return (BadPtr(str)) ? star : str; } /* * create a string of form "foo!bar@fubar" given foo, bar and fubar as * the parameters. If NULL, they become "*". */ static char *make_nick_user_host(char *nick, char *name, char *host) { static char namebuf[NICKLEN + USERLEN + HOSTLEN + 6]; int n; char *ptr1, *ptr2; ptr1 = namebuf; for (ptr2 = check_string(nick), n = NICKLEN; *ptr2 && n--;) *ptr1++ = *ptr2++; *ptr1++ = '!'; for (ptr2 = check_string(name), n = USERLEN; *ptr2 && n--;) *ptr1++ = *ptr2++; *ptr1++ = '@'; for (ptr2 = check_string(host), n = HOSTLEN; *ptr2 && n--;) *ptr1++ = *ptr2++; *ptr1 = '\0'; return (namebuf); } #ifdef EXEMPT_LISTS /* Exempt list functions (+e) */ int add_exempt_id(aClient* cptr, aChannel* chptr, char* exempt_id) { aBanExempt* exempt = NULL; int cnt = 0; for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) { if (MyClient(cptr)) { if (++cnt >= MAXEXEMPTLIST) { sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name, chptr->chname, exempt_id, "exempt"); return -1; } if (!match(exempt->banstr, exempt_id)) return -1; } else if (!mycmp(exempt->banstr, exempt_id)) return -1; } exempt = (aBanExempt*)MyMalloc(sizeof(aBanExempt)); exempt->banstr = (char*)MyMalloc(strlen(exempt_id)+1); strcpy(exempt->banstr, exempt_id); exempt->when = timeofday; exempt->next = chptr->banexempt_list; chptr->banexempt_list = exempt; chptr->banserial++; if (IsPerson(cptr)) { exempt->who = (char *) MyMalloc(strlen(cptr->name) + strlen(cptr->user->username) + strlen(cptr->user->host) + 3); (void) ircsprintf(exempt->who, "%s!%s@%s", cptr->name, cptr->user->username, cptr->user->host); } else { exempt->who = (char *) MyMalloc(strlen(cptr->name) + 1); (void) strcpy(exempt->who, cptr->name); } /* determine type for less matching later */ if(exempt_id[0] == '*' && exempt_id[1] == '!') { if(exempt_id[2] == '*' && exempt_id[3] == '@') exempt->type = MTYP_HOST; else exempt->type = MTYP_USERHOST; } else exempt->type = MTYP_FULL; return 0; } int del_exempt_id(aChannel* chptr, char* exempt_id) { aBanExempt** exempt; aBanExempt* tmp; if (!exempt_id) return -1; for (exempt = &chptr->banexempt_list; *exempt; exempt = &((*exempt)->next)) { if (mycmp(exempt_id, (*exempt)->banstr) == 0) { tmp = *exempt; *exempt = tmp->next; chptr->banserial++; MyFree(tmp->banstr); MyFree(tmp->who); MyFree(tmp); break; } } return 0; } #endif #ifdef INVITE_LISTS /* Invite list functions (+I) */ int add_invite_id(aClient* cptr, aChannel* chptr, char* invite_id) { anInvite* invite; int cnt = 0; for (invite = chptr->invite_list; invite; invite = invite->next) { if (MyClient(cptr)) { if (++cnt >= MAXINVITELIST) { sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name, chptr->chname, invite_id, "invite"); return -1; } if (!match(invite->invstr, invite_id)) return -1; } else if (!mycmp(invite->invstr, invite_id)) return -1; } invite = (anInvite*)MyMalloc(sizeof(anInvite)); invite->invstr = (char*)MyMalloc(strlen(invite_id)+1); strcpy(invite->invstr, invite_id); invite->when = timeofday; invite->next = chptr->invite_list; chptr->invite_list = invite; if (IsPerson(cptr)) { invite->who = (char *) MyMalloc(strlen(cptr->name) + strlen(cptr->user->username) + strlen(cptr->user->host) + 3); (void) ircsprintf(invite->who, "%s!%s@%s", cptr->name, cptr->user->username, cptr->user->host); } else { invite->who = (char *) MyMalloc(strlen(cptr->name) + 1); (void) strcpy(invite->who, cptr->name); } return 0; } int del_invite_id(aChannel* chptr, char* invite_id) { anInvite** invite; anInvite* tmp; if (!invite_id) return -1; for (invite = &chptr->invite_list; *invite; invite = &((*invite)->next)) { if (mycmp(invite_id, (*invite)->invstr) == 0) { tmp = *invite; *invite = tmp->next; MyFree(tmp->invstr); MyFree(tmp->who); MyFree(tmp); break; } } return 0; } anInvite* is_invited(aClient* cptr, aChannel* chptr) { char s[NICKLEN + USERLEN + HOSTLEN + 6]; char *s2; anInvite* invite; strcpy(s, make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host)); s2 = make_nick_user_host(cptr->name, cptr->user->username, cptr->hostip); for (invite = chptr->invite_list; invite; invite = invite->next) { if (!match(invite->invstr, s) || !match(invite->invstr, s2)) break; } return invite; } #endif /* Ban functions to work with mode +b */ /* add_banid - add an id to be banned to the channel (belongs to cptr) */ static int add_banid(aClient *cptr, aChannel *chptr, char *banid) { aBan *ban; int cnt = 0; for (ban = chptr->banlist; ban; ban = ban->next) { /* Begin unbreaking redundant ban checking. First step is to allow * ALL non-duplicates from remote servers. Local clients are still * subject to the flawed redundancy check for compatibility with * older servers. This check can be corrected later. -Quension */ if (MyClient(cptr)) { if (++cnt >= MAXBANS) { sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name, chptr->chname, banid, "ban"); return -1; } if (!match(ban->banstr, banid)) return -1; } else if (!mycmp(ban->banstr, banid)) return -1; } ban = (aBan *) MyMalloc(sizeof(aBan)); ban->banstr = (char *) MyMalloc(strlen(banid) + 1); (void) strcpy(ban->banstr, banid); ban->next = chptr->banlist; if (IsPerson(cptr)) { ban->who = (char *) MyMalloc(strlen(cptr->name) + strlen(cptr->user->username) + strlen(cptr->user->host) + 3); (void) ircsprintf(ban->who, "%s!%s@%s", cptr->name, cptr->user->username, cptr->user->host); } else { ban->who = (char *) MyMalloc(strlen(cptr->name) + 1); (void) strcpy(ban->who, cptr->name); } /* determine what 'type' of mask this is, for less matching later */ if(banid[0] == '*' && banid[1] == '!') { if(banid[2] == '*' && banid[3] == '@') ban->type = MTYP_HOST; else ban->type = MTYP_USERHOST; } else ban->type = MTYP_FULL; ban->when = timeofday; chptr->banlist = ban; chptr->banserial++; return 0; } /* * del_banid - delete an id belonging to cptr if banid is null, * deleteall banids belonging to cptr. */ static int del_banid(aChannel *chptr, char *banid) { aBan **ban; aBan *tmp; if (!banid) return -1; for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next)) if (mycmp(banid, (*ban)->banstr) == 0) { tmp = *ban; *ban = tmp->next; chptr->banserial++; MyFree(tmp->banstr); MyFree(tmp->who); MyFree(tmp); break; } return 0; } /* * is_banned - returns CHFL_BANNED if banned else 0 * * caches banned status in chanMember for can_send() * -Quension [Jun 2004] */ static int is_banned(aClient *cptr, aChannel *chptr, chanMember *cm) { aBan *ban; #ifdef EXEMPT_LISTS aBanExempt *exempt; #endif char s[NICKLEN + USERLEN + HOSTLEN + 6]; char *s2; if (!IsPerson(cptr)) return 0; /* if cache is valid, use it */ if (cm) { if (cm->banserial == chptr->banserial) return (cm->flags & CHFL_BANNED); cm->banserial = chptr->banserial; cm->flags &= ~CHFL_BANNED; } strcpy(s, make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host)); s2 = make_nick_user_host(cptr->name, cptr->user->username, cptr->hostip); #ifdef EXEMPT_LISTS for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) if (!match(exempt->banstr, s) || !match(exempt->banstr, s2)) return 0; #endif for (ban = chptr->banlist; ban; ban = ban->next) if ((match(ban->banstr, s) == 0) || (match(ban->banstr, s2) == 0)) break; if (ban) { if (cm) cm->flags |= CHFL_BANNED; return CHFL_BANNED; } return 0; } aBan *nick_is_banned(aChannel *chptr, char *nick, aClient *cptr) { aBan *ban; #ifdef EXEMPT_LISTS aBanExempt *exempt; #endif char *s, s2[NICKLEN+USERLEN+HOSTLEN+6]; if (!IsPerson(cptr)) return NULL; strcpy(s2, make_nick_user_host(nick, cptr->user->username, cptr->user->host)); s = make_nick_user_host(nick, cptr->user->username, cptr->hostip); #ifdef EXEMPT_LISTS for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) if (exempt->type == MTYP_FULL && ((match(exempt->banstr, s2) == 0) || (match(exempt->banstr, s) == 0))) return NULL; #endif for (ban = chptr->banlist; ban; ban = ban->next) if (ban->type == MTYP_FULL && /* only check applicable bans */ ((match(ban->banstr, s2) == 0) || /* check host before IP */ (match(ban->banstr, s) == 0))) break; return (ban); } void remove_matching_bans(aChannel *chptr, aClient *cptr, aClient *from) { aBan *ban, *bnext; char targhost[NICKLEN+USERLEN+HOSTLEN+6]; char targip[NICKLEN+USERLEN+HOSTLEN+6]; char *m; int count = 0, send = 0; if (!IsPerson(cptr)) return; strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host)); strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username, cptr->hostip)); m = modebuf; *m++ = '-'; *m = '\0'; *parabuf = '\0'; ban = chptr->banlist; while(ban) { bnext = ban->next; if((match(ban->banstr, targhost) == 0) || (match(ban->banstr, targip) == 0)) { if (strlen(parabuf) + strlen(ban->banstr) + 10 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, ban->banstr); count++; *m++ = 'b'; *m = '\0'; } else if(*parabuf) send = 1; if(count == MAXTSMODEPARAMS) send = 1; if(send) { sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf); sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name, chptr->chname, chptr->channelts, modebuf, parabuf); send = 0; *parabuf = '\0'; m = modebuf; *m++ = '-'; if(count != MAXTSMODEPARAMS) { strcpy(parabuf, ban->banstr); *m++ = 'b'; count = 1; } else count = 0; *m = '\0'; } del_banid(chptr, ban->banstr); } ban = bnext; } if(*parabuf) { sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf); sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name, chptr->chname, chptr->channelts, modebuf, parabuf); } return; } #ifdef EXEMPT_LISTS void remove_matching_exempts(aChannel *chptr, aClient *cptr, aClient *from) { aBanExempt *ex, *enext; char targhost[NICKLEN+USERLEN+HOSTLEN+6]; char targip[NICKLEN+USERLEN+HOSTLEN+6]; char *m; int count = 0, send = 0; if (!IsPerson(cptr)) return; strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host)); strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username, cptr->hostip)); m = modebuf; *m++ = '-'; *m = '\0'; *parabuf = '\0'; ex = chptr->banexempt_list; while(ex) { enext = ex->next; if((match(ex->banstr, targhost) == 0) || (match(ex->banstr, targip) == 0)) { if (strlen(parabuf) + strlen(ex->banstr) + 10 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, ex->banstr); count++; *m++ = 'e'; *m = '\0'; } else if(*parabuf) send = 1; if(count == MAXTSMODEPARAMS) send = 1; if(send) { sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf); sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name, chptr->chname, chptr->channelts, modebuf, parabuf); send = 0; *parabuf = '\0'; m = modebuf; *m++ = '-'; if(count != MAXTSMODEPARAMS) { strcpy(parabuf, ex->banstr); *m++ = 'e'; count = 1; } else count = 0; *m = '\0'; } del_exempt_id(chptr, ex->banstr); } ex = enext; } if(*parabuf) { sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf); sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name, chptr->chname, chptr->channelts, modebuf, parabuf); } return; } #endif #ifdef INVITE_LISTS void remove_matching_invites(aChannel *chptr, aClient *cptr, aClient *from) { anInvite *inv, *inext; char targhost[NICKLEN+USERLEN+HOSTLEN+6]; char targip[NICKLEN+USERLEN+HOSTLEN+6]; char *m; int count = 0, send = 0; if (!IsPerson(cptr)) return; strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host)); strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username, cptr->hostip)); m = modebuf; *m++ = '-'; *m = '\0'; *parabuf = '\0'; inv = chptr->invite_list; while(inv) { inext = inv->next; if((match(inv->invstr, targhost) == 0) || (match(inv->invstr, targip) == 0)) { if (strlen(parabuf) + strlen(inv->invstr) + 10 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, inv->invstr); count++; *m++ = 'I'; *m = '\0'; } else if(*parabuf) send = 1; if(count == MAXTSMODEPARAMS) send = 1; if(send) { sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf); sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name, chptr->chname, chptr->channelts, modebuf, parabuf); send = 0; *parabuf = '\0'; m = modebuf; *m++ = '-'; if(count != MAXTSMODEPARAMS) { strcpy(parabuf, inv->invstr); *m++ = 'I'; count = 1; } else count = 0; *m = '\0'; } del_invite_id(chptr, inv->invstr); } inv = inext; } if(*parabuf) { sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name, chptr->chname, modebuf, parabuf); sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name, chptr->chname, chptr->channelts, modebuf, parabuf); } return; } #endif int check_joinrate(aChannel *chptr, time_t ts, int local, aClient *cptr) { int join_num = DEFAULT_JOIN_NUM; int join_time = DEFAULT_JOIN_TIME; if(!local && (NOW - ts) > 60) return 1; /* attempt to compensate for lag */ /* This first section checks the defaults, the second * checks the channels +j settings */ /* Has the join_time period elapsed? */ if((NOW - chptr->default_join_start) > join_time) { chptr->default_join_start = NOW; chptr->default_join_count = 0; } /* update the count here for attempts, in case a lower throttle blocks */ chptr->default_join_count++; /* If it's local and we've filled the join count, complain * to ops so they can take the appropriate measures */ if(local && chptr->default_join_count > join_num) sendto_realops_lev(DEBUG_LEV, "Join rate warning on %s for %s!%s@%s" " (%d in %d)", chptr->chname, cptr->name, cptr->user->username, cptr->hostip, chptr->default_join_count, NOW - chptr->default_join_start); /* Has the channel set their own custom settings? */ if(chptr->mode.mode & MODE_JOINRATE) { if(chptr->mode.join_num == 0 || chptr->mode.join_time == 0) return 1; /* channel has turned this off */ join_time = chptr->mode.join_time; join_num = chptr->mode.join_num; } /* Has the join_time period elapsed? */ if((NOW - chptr->join_start) > join_time) { chptr->join_start = NOW; chptr->join_count = 0; return 1; } /* If it's local and we've filled the join count, say no */ if(local && chptr->join_count >= join_num) { sendto_realops_lev(DEBUG_LEV, "Join rate throttling on %s for %s!%s@%s" " (%d/%d in %d/%d)", chptr->chname, cptr->name, cptr->user->username, cptr->hostip, chptr->join_count, join_num, NOW - chptr->join_start, join_time); return 0; } return 1; } /* * adds a user to a channel by adding another link to the channels * member chain. */ static void add_user_to_channel(aChannel *chptr, aClient *who, int flags) { Link *ptr; chanMember *cm; #ifdef DUMP_DEBUG fprintf(dumpfp,"Add to channel %s: %p:%s\n",chptr->chname,who,who->name); #endif if (who->user) { cm = make_chanmember(); cm->flags = flags; cm->cptr = who; cm->next = chptr->members; cm->banserial = chptr->banserial; chptr->members = cm; chptr->users++; ptr = make_link(); ptr->value.chptr = chptr; ptr->next = who->user->channel; who->user->channel = ptr; who->user->joined++; } } void remove_user_from_channel(aClient *sptr, aChannel *chptr) { chanMember **curr, *tmp; Link **lcurr, *ltmp; for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next) if (tmp->cptr == sptr) { *curr = tmp->next; free_chanmember(tmp); break; } for (lcurr = &sptr->user->channel; (ltmp = *lcurr); lcurr = <mp->next) if (ltmp->value.chptr == chptr) { *lcurr = ltmp->next; free_link(ltmp); break; } sptr->user->joined--; sub1_from_channel(chptr); } int is_chan_op(aClient *cptr, aChannel *chptr) { chanMember *cm; if (chptr) if ((cm = find_user_member(chptr->members, cptr))) return (cm->flags & CHFL_CHANOP); return 0; } int is_deopped(aClient *cptr, aChannel *chptr) { chanMember *cm; if (chptr) if ((cm = find_user_member(chptr->members, cptr))) return (cm->flags & CHFL_DEOPPED); return 0; } int is_only_halfop(aClient *cptr, aChannel *chptr) { chanMember *cm; if (chptr) if ((cm = find_user_member(chptr->members, cptr))) if (!(cm->flags & CHFL_CHANOP)) return (cm->flags & CHFL_HALFOP); return 0; } int is_halfop(aClient *cptr, aChannel *chptr) { chanMember *cm; if (chptr) if ((cm = find_user_member(chptr->members, cptr))) return (cm->flags & CHFL_HALFOP); return 0; } int has_voice(aClient *cptr, aChannel *chptr) { chanMember *cm; if (chptr) if ((cm = find_user_member(chptr->members, cptr))) return (cm->flags & CHFL_VOICE); return 0; } int can_send(aClient *cptr, aChannel *chptr, char *msg) { chanMember *cm; if (IsServer(cptr) || IsULine(cptr)) return 0; cm = find_user_member(chptr->members, cptr); if(!cm) { if (chptr->mode.mode & MODE_MODERATED) return (MODE_MODERATED); if(chptr->mode.mode & MODE_NOPRIVMSGS) return (MODE_NOPRIVMSGS); if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(cptr)) return (ERR_NEEDREGGEDNICK); if ((chptr->mode.mode & MODE_NOCTRL) && msg_has_ctrls(msg)) return (ERR_NOCTRLSONCHAN); if (MyClient(cptr) && is_banned(cptr, chptr, NULL)) return (MODE_BAN); /* * channel is -n and user is not there; * we need to bquiet them if we can */ } else { /* ops and voices can talk through everything except NOCTRL */ if (!(cm->flags & (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE))) { if (chptr->mode.mode & MODE_MODERATED) return (MODE_MODERATED); if (is_banned(cptr, chptr, cm)) return (MODE_BAN); if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(cptr)) return (ERR_NEEDREGGEDNICK); } if ((chptr->mode.mode & MODE_NOCTRL) && msg_has_ctrls(msg)) return (ERR_NOCTRLSONCHAN); } return 0; } /* Can a user change his nick on this channel? - TheSphere 08/01/04*/ int can_changenick(aClient *cptr, aChannel *chptr) { chanMember *cm; if (IsServer(cptr) || IsULine(cptr)) return 0; if ((cm = find_user_member(chptr->members, cptr))) { if (!(cm->flags & (CHFL_CHANOP | CHFL_HALFOP))) /* Let halfops change their name aswell - Tux */ { if (chptr->mode.mode & MODE_NONICK) return (MODE_MODERATED); } } return 0; } /* * write the "simple" list of channel modes for channel chptr onto * buffer mbuf with the parameters in pbuf. */ static void channel_modes(aClient *cptr, char *mbuf, char *pbuf, aChannel *chptr) { pbuf[0] = '\0'; *mbuf++ = '+'; if (chptr->mode.mode & MODE_SECRET) *mbuf++ = 's'; if (chptr->mode.mode & MODE_PRIVATE) *mbuf++ = 'p'; if (chptr->mode.mode & MODE_MODERATED) *mbuf++ = 'm'; if (chptr->mode.mode & MODE_TOPICLIMIT) *mbuf++ = 't'; if (chptr->mode.mode & MODE_INVITEONLY) *mbuf++ = 'i'; if (chptr->mode.mode & MODE_NOPRIVMSGS) *mbuf++ = 'n'; if (chptr->mode.mode & MODE_REGISTERED) *mbuf++ = 'r'; if (chptr->mode.mode & MODE_REGONLY) *mbuf++ = 'R'; if (chptr->mode.mode & MODE_NOCTRL) *mbuf++ = 'c'; if (chptr->mode.mode & MODE_OPERONLY) *mbuf++ = 'O'; #ifdef HAVE_SSL if (chptr->mode.mode & MODE_SSL) *mbuf++ = 'S'; #endif #ifdef ENABLE_CHANNEL_MODE_D if (chptr->mode.mode & MODE_RSL) *mbuf++ = 'D'; #endif if (chptr->mode.mode & MODE_NONICK) *mbuf++ = 'N'; if (chptr->mode.mode & MODE_MODREG) *mbuf++ = 'M'; #ifdef USE_CHANMODE_L if (chptr->mode.mode & MODE_LISTED) *mbuf++ = 'L'; #endif if (chptr->mode.limit) { *mbuf++ = 'l'; if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr)) ircsprintf(pbuf, "%d", chptr->mode.limit); } if (*chptr->mode.key) { *mbuf++ = 'k'; if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr)) { if(pbuf[0] != '\0') strcat(pbuf, " "); strcat(pbuf, chptr->mode.key); } } if (chptr->mode.mode & MODE_JOINRATE) { *mbuf++ = 'j'; if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr)) { char tmp[128]; if(pbuf[0] != '\0') strcat(pbuf, " "); if(chptr->mode.join_num == 0 || chptr->mode.join_time == 0) ircsprintf(tmp, "0"); else ircsprintf(tmp, "%d:%d", chptr->mode.join_num, chptr->mode.join_time); strcat(pbuf, tmp); } } *mbuf++ = '\0'; return; } static void send_channel_lists(aClient *cptr, aChannel *chptr) { aBan *bp; #ifdef EXEMPT_LISTS aBanExempt *exempt; #endif #ifdef INVITE_LISTS anInvite *inv; #endif char *cp; int count = 0, send = 0; cp = modebuf + strlen(modebuf); if (*parabuf) /* mode +l or +k xx */ count = 1; for (bp = chptr->banlist; bp; bp = bp->next) { if (strlen(parabuf) + strlen(bp->banstr) + 20 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, bp->banstr); count++; *cp++ = 'b'; *cp = '\0'; } else if (*parabuf) send = 1; if (count == MAXTSMODEPARAMS) send = 1; if (send) { sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname, chptr->channelts, modebuf, parabuf); send = 0; *parabuf = '\0'; cp = modebuf; *cp++ = '+'; if (count != MAXTSMODEPARAMS) { strcpy(parabuf, bp->banstr); *cp++ = 'b'; count = 1; } else count = 0; *cp = '\0'; } } #ifdef EXEMPT_LISTS for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) { if (strlen(parabuf) + strlen(exempt->banstr) + 20 < (size_t)MODEBUFLEN) { if (*parabuf) strcat(parabuf, " "); strcat(parabuf, exempt->banstr); count++; *cp++ = 'e'; *cp = 0; } else if (*parabuf) send = 1; if (count == MAXTSMODEPARAMS) send = 1; if (send) { sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname, chptr->channelts, modebuf, parabuf); send = 0; *parabuf = 0; cp = modebuf; *cp++ = '+'; if (count != MAXTSMODEPARAMS) { strcpy(parabuf, exempt->banstr); *cp++ = 'e'; count = 1; } else count = 0; *cp = 0; } } #endif #ifdef INVITE_LISTS for (inv = chptr->invite_list; inv; inv = inv->next) { if (strlen(parabuf) + strlen(inv->invstr) + 20 < (size_t)MODEBUFLEN) { if (*parabuf) strcat(parabuf, " "); strcat(parabuf, inv->invstr); count++; *cp++ = 'I'; *cp = 0; } else if (*parabuf) send = 1; if (count == MAXTSMODEPARAMS) send = 1; if (send) { sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname, chptr->channelts, modebuf, parabuf); send = 0; *parabuf = 0; cp = modebuf; *cp++ = '+'; if (count != MAXTSMODEPARAMS) { strcpy(parabuf, inv->invstr); *cp++ = 'I'; count = 1; } else count = 0; *cp = 0; } } #endif } /* send "cptr" a full list of the modes for channel chptr. */ void send_channel_modes(aClient *cptr, aChannel *chptr) { chanMember *l, *anop = NULL, *skip = NULL; int n = 0; char *t; if (*chptr->chname != '#') return; *modebuf = *parabuf = '\0'; channel_modes(cptr, modebuf, parabuf, chptr); ircsprintf(buf, ":%s SJOIN %ld %s %s %s :", me.name, chptr->channelts, chptr->chname, modebuf, parabuf); t = buf + strlen(buf); for (l = chptr->members; l; l = l->next) if (l->flags & (MODE_CHANOP|MODE_HALFOP)) { anop = l; break; } /* * follow the channel, but doing anop first if it's defined * * -orabidoo */ l = NULL; for (;;) { if (anop) { l = skip = anop; anop = NULL; } else { if (l == NULL || l == skip) l = chptr->members; else l = l->next; if (l && l == skip) l = l->next; if (l == NULL) break; } if (l->flags & MODE_CHANOP) *t++ = '@'; if (l->flags & MODE_HALFOP) *t++ = '%'; if (l->flags & MODE_VOICE) *t++ = '+'; strcpy(t, l->cptr->name); t += strlen(t); *t++ = ' '; n++; if (t - buf > BUFSIZE - 80) { *t++ = '\0'; if (t[-1] == ' ') t[-1] = '\0'; sendto_one(cptr, "%s", buf); sprintf(buf, ":%s SJOIN %ld %s 0 :", me.name, chptr->channelts, chptr->chname); t = buf + strlen(buf); n = 0; } } if (n) { *t++ = '\0'; if (t[-1] == ' ') t[-1] = '\0'; sendto_one(cptr, "%s", buf); } *parabuf = '\0'; *modebuf = '+'; modebuf[1] = '\0'; send_channel_lists(cptr, chptr); if (modebuf[1] || *parabuf) sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname, chptr->channelts, modebuf, parabuf); } /* This opermode function needs a rewrite -Sheik 17/05/2005 */ static void do_opermode(aChannel *chptr, aClient *sptr, int i) { static int sent = 0; if (!i) { if (sent==0) { sendto_ops("OperMode on Channel (%s), by %s. Modes: %s%s%s", chptr->chname, sptr->name, modebuf, (*parabuf!=0 ? " " : ""), parabuf); sent=1; } } else sent = 0; } int check_level(int level, int rlevel, aChannel *chptr, aClient *sptr) { if (level < rlevel && !IsSAdmin(sptr)) return 1; else if (MyClient(sptr) && (level < rlevel) && IsSAdmin(sptr)) do_opermode(chptr,sptr,0); return 0; } /* m_mode parv[0] - sender parv[1] - channel */ int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[]) { int mcount = 0, chanop=0; aChannel *chptr; int subparc = 2; /* Now, try to find the channel in question */ if (parc > 1) { chptr = find_channel(parv[1], NullChn); if (chptr == NullChn) return m_umode(cptr, sptr, parc, parv); } else { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "MODE"); return 0; } if (!check_channelname(sptr, (unsigned char *) parv[1])) return 0; if (IsULine(sptr) && !MyClient(sptr)) chanop=4; /* extra speshul access */ else if (IsSAdmin(sptr) && !is_chan_op(sptr, chptr) && !is_halfop(sptr,chptr) && !IsULine(sptr)) chanop=3; else if (is_chan_op(sptr, chptr) || (IsServer(sptr) && chptr->channelts!=0)) chanop=2; else if (is_only_halfop(sptr,chptr)) chanop=1; if (parc < 3) { *modebuf = *parabuf = '\0'; modebuf[1] = '\0'; channel_modes(sptr, modebuf, parabuf, chptr); sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0], chptr->chname, modebuf, parabuf); sendto_one(sptr, rpl_str(RPL_CREATIONTIME), me.name, parv[0], chptr->chname, chptr->channelts); return 0; } if(IsServer(cptr) && IsDigit(parv[2][0])) { ts_val modets = atol(parv[2]); if(modets != 0 && (modets > chptr->channelts)) return 0; subparc++; } mcount = set_mode(cptr, sptr, chptr, chanop, parc - subparc, parv + subparc, modebuf, parabuf); if (strlen(modebuf) > (size_t) 1) switch (mcount) { case 0: break; case -1: if (MyClient(sptr)) sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0], chptr->chname); else ircstp->is_fake++; break; default: /* Les give this oper mode another try using the old code.. 17/05/2005 -Sheik * This old format seems to be 95% corect -Sheik June 05 2005 */ if (chanop == 3 && MyClient(sptr)) sendto_ops("OperMode on Channel (%s), by %s. Modes: %s%s%s", chptr->chname, parv[0], modebuf, (*parabuf!=0 ? " " : ""), parabuf); sendto_channel_butserv_me(chptr, sptr, ":%s MODE %s %s %s", parv[0], chptr->chname, modebuf, parabuf); sendto_serv_butone(cptr, ":%s MODE %s %ld %s %s", parv[0], chptr->chname, chptr->channelts, modebuf, parabuf); } return 0; } /* the old set_mode was pissing me off with it's disgusting * hackery, so I rewrote it. Hope this works. }:> --wd * Corrected a 4-year-old mistake: the max modes limit applies to * the number of parameters, not mode changes. -Quension [Apr 2004] */ static int set_mode(aClient *cptr, aClient *sptr, aChannel *chptr, int level, int parc, char *parv[], char *mbuf, char *pbuf) { #define SM_ERR_NOPRIVS 0x0001 /* is not an op */ #define SM_ERR_MOREPARMS 0x0002 /* needs more parameters */ #define SM_ERR_RESTRICTED 0x0004 /* not allowed to op others or be op'd */ #define SM_ERR_NOTOPER 0x0008 /* not an irc op */ #ifdef HAVE_SSL #define SM_ERR_SSL 0x0010 /* SSL Only*/ #endif #ifdef ENABLE_CHANNEL_MODE_D #define SM_ERR_RSL 0x0020 /* RSL(Resolvoled Clients) Only*/ #endif #define SM_MAXMODES MAXMODEPARAMSUSER /* this macro appends to pbuf */ #define ADD_PARA(p) pptr = p; if(pidx) pbuf[pidx++] = ' '; while(*pptr) \ pbuf[pidx++] = *pptr++; static int flags[] = { MODE_PRIVATE, 'p', MODE_SECRET, 's', MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n', MODE_TOPICLIMIT, 't', MODE_REGONLY, 'R', MODE_INVITEONLY, 'i', MODE_NOCTRL, 'c', MODE_OPERONLY, 'O', MODE_MODREG, 'M', MODE_NONICK, 'N', #ifdef ENABLE_CHANNEL_MODE_D MODE_RSL, 'D', #endif #ifdef HAVE_SSL MODE_SSL, 'S', #endif #ifdef USE_CHANMODE_L MODE_LISTED, 'L', #endif 0x0, 0x0 }; Link *lp; /* for walking lists */ chanMember *cm; /* for walking channel member lists */ aBan *bp; /* for walking banlists */ char *modes=parv[0]; /* user's idea of mode changes */ int args; /* counter for what argument we're on */ int anylistsent = IsServer(sptr) ? 1 : 0; /* Only send 1 list and not to servers */ char change='+'; /* by default we + things... */ int errors=0; /* * errors returned, set with bitflags * so we only return them once */ /* from remote servers, ungodly numbers of modes can be sent, but * from local users only SM_MAXMODES are allowed */ int maxparams=((IsServer(sptr) || IsULine(sptr)) ? 512 : SM_MAXMODES); int nmodes=0; /* how many modes we've set so far */ int nparams=0; /* how many modes with parameters we've set so far */ aClient *who = NULL; /* who we're doing a mode for */ int chasing = 0; int i=0; char moreparmsstr[]="MODE "; char nuhbuf[NICKLEN + USERLEN + HOSTLEN + 6]; /* for bans */ char tmp[128]; /* temporary buffer */ int pidx = 0; /* index into pbuf */ char *pptr; /* temporary paramater pointer */ char *morig = mbuf; /* beginning of mbuf */ /* :cptr-name MODE chptr->chname [MBUF] [PBUF] (buflen - 3 max and NULL) */ /* added another 11 bytes to this, for TSMODE -epi */ int prelen = strlen(cptr->name) + strlen(chptr->chname) + 27; /* drop duplicates in the same mode change -- yeah, this is cheap, but real duplicate checking will have to wait for a protocol change to kill desyncs */ int seenalready = 0; args=1; if(parc<1) return 0; *mbuf++='+'; /* add the plus, even if they don't */ /* go through once to clean the user's mode string so we can * have a simple parser run through it...*/ while(*modes) { switch(*modes) { case '+': if(*(mbuf-1)=='-') { *(mbuf-1)='+'; /* change it around now */ change='+'; break; } else if(change=='+') /* we're still doing a +, we don't care */ break; change=*modes; *mbuf++='+'; break; case '-': if(*(mbuf-1)=='+') { *(mbuf-1)='-'; /* change it around now */ change='-'; break; } else if(change=='-') break; /* we're still doing a -, we don't care */ change=*modes; *mbuf++='-'; break; #ifdef HAVE_SSL case 'S': if (!IsULine(sptr) && (check_level(level,1,chptr,sptr) || !IsUmodez(sptr))) { errors |= SM_ERR_SSL; break; } else { if (change=='+') chptr->mode.mode|=MODE_SSL; else chptr->mode.mode&=~MODE_SSL; *mbuf++ = *modes; nmodes++; } break; #endif /* Added by Gabriel Baez On May 23 2005. * +D will prevent users who have been marke as unresolved clients, from joining the channel. * This is one of my favorite modes. -Sheik June 05 2005. */ #ifdef ENABLE_CHANNEL_MODE_D case 'D': if (!IsULine(sptr) && (check_level(level,2,chptr,sptr) && IsURSL(sptr) && !IsOper(sptr))) { errors |= SM_ERR_RSL; break; } else if (MyClient(sptr) && IsURSL(sptr) && !IsOper(sptr)) /* If you're not a oper, then stay out! -Sheik */ { errors |= SM_ERR_RSL; break; } else { if (change=='+') chptr->mode.mode|=MODE_RSL; else chptr->mode.mode&=~MODE_RSL; *mbuf++ = *modes; nmodes++; } break; #endif case 'O': if (!IsULine(sptr) && (level<2 || !IsOper(sptr))) { errors |= SM_ERR_NOTOPER; break; } else if (MyClient(sptr) && !IsOper(sptr)) { errors |= SM_ERR_NOTOPER; break; } else { if (change=='+') chptr->mode.mode|=MODE_OPERONLY; else chptr->mode.mode&=~MODE_OPERONLY; *mbuf++ = *modes; nmodes++; } break; case 'o': if (check_level(level,2,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } if(parv[args]==NULL) { /* silently drop the spare +o/v's */ break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } who = find_chasing(sptr, parv[args], &chasing); cm = find_user_member(chptr->members, who); if(cm == NULL) { sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL), me.name, cptr->name, parv[args], chptr->chname); /* swallow the arg */ args++; break; } /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) > REALMODEBUFLEN) { args++; break; } /* if we have the user, set them +/-[vo] */ if(change=='+') cm->flags|=CHFL_CHANOP; else cm->flags&=~CHFL_CHANOP; /* we've decided their mode was okay, cool */ *mbuf++ = *modes; ADD_PARA(cm->cptr->name) args++; nmodes++; if (IsServer(sptr) && change=='+') { chptr->channelts = 0; sendto_ops("Server %s setting +o and blasting TS on %s", sptr->name, chptr->chname); } break; case 'v': if (check_level(level,1,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } if(parv[args]==NULL) { /* silently drop the spare +o/v's */ break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } who = find_chasing(sptr, parv[args], &chasing); cm = find_user_member(chptr->members, who); if(cm == NULL) { sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL), me.name, cptr->name, parv[args], chptr->chname); /* swallow the arg */ args++; break; } /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) > REALMODEBUFLEN) { args++; break; } /* if we have the user, set them +/-[vo] */ if(change=='+') cm->flags|=CHFL_VOICE; else cm->flags&=~CHFL_VOICE; /* we've decided their mode was okay, cool */ *mbuf++ = *modes; ADD_PARA(cm->cptr->name) args++; nmodes++; break; case 'h': if(check_level(level,2,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } if(parv[args]==NULL) { /* silently drop the spare +o/v's */ break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } who = find_chasing(sptr, parv[args], &chasing); cm = find_user_member(chptr->members, who); if(cm == NULL) { sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL), me.name, cptr->name, parv[args], chptr->chname); /* swallow the arg */ args++; break; } /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) > REALMODEBUFLEN) { args++; break; } /* if we have the user, set them +/-[vo] */ if(change=='+') cm->flags |= (CHFL_HALFOP); else cm->flags &=~(CHFL_HALFOP); /* we've decided their mode was okay, cool */ *mbuf++ = *modes; ADD_PARA(cm->cptr->name) args++; nmodes++; if (IsServer(sptr) && *modes == 'h' && change=='+') { chptr->channelts = 0; sendto_ops("Server %s setting +h and blasting TS on %s", sptr->name, chptr->chname); } break; #ifdef INVITE_LISTS case 'I': if (check_level(level,1,chptr,sptr) && parv[args] != NULL) { errors |= SM_ERR_NOPRIVS; break; } else if (parv[args] == NULL) { anInvite *invite; if (anylistsent) /* don't send the list if they have received one */ break; for (invite = chptr->invite_list; invite; invite = invite->next) sendto_one(sptr, rpl_str(RPL_INVITELIST), me.name, cptr->name, chptr->chname, invite->invstr, invite->who, invite->when); sendto_one(cptr, rpl_str(RPL_ENDOFINVITELIST), me.name, cptr->name, chptr->chname); anylistsent = 1; break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } if (*parv[args] == ':' || *parv[args] == '\0') { args++; break; } strcpy(nuhbuf, collapse(pretty_mask(parv[args]))); parv[args] = nuhbuf; /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) > REALMODEBUFLEN) { args++; break; } /* if we can't add or delete (depending) the ban, change is * worthless anyhow */ if(!(change=='+' && !add_invite_id(sptr, chptr, parv[args])) && !(change=='-' && !del_invite_id(chptr, parv[args]))) { args++; break; } *mbuf++ = 'I'; ADD_PARA(parv[args]) args++; nmodes++; break; #endif #ifdef EXEMPT_LISTS case 'e': if (check_level(level,1,chptr,sptr) && parv[args] != NULL) { errors |= SM_ERR_NOPRIVS; break; } else if (parv[args] == NULL) { aBanExempt* exempt; if (anylistsent) /* don't send the list if they have received one */ break; for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) sendto_one(sptr, rpl_str(RPL_EXEMPTLIST), me.name, cptr->name, chptr->chname, exempt->banstr, exempt->who, exempt->when); sendto_one(cptr, rpl_str(RPL_ENDOFEXEMPTLIST), me.name, cptr->name, chptr->chname); anylistsent = 1; break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } if (*parv[args] == ':' || *parv[args] == '\0') { args++; break; } strcpy(nuhbuf, collapse(pretty_mask(parv[args]))); parv[args] = nuhbuf; /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) > REALMODEBUFLEN) { args++; break; } /* if we can't add or delete (depending) the exempt, change is * worthless anyhow */ if(!(change=='+' && !add_exempt_id(sptr, chptr, parv[args])) && !(change=='-' && !del_exempt_id(chptr, parv[args]))) { args++; break; } *mbuf++ = 'e'; ADD_PARA(parv[args]) args++; nmodes++; break; #endif case 'b': /* if the user has no more arguments, then they just want * to see the bans, okay, cool. */ if(check_level(level,1,chptr,sptr) && parv[args] != NULL) { errors |= SM_ERR_NOPRIVS; break; } /* show them the bans, woowoo */ if(parv[args]==NULL) { if (anylistsent) break; for(bp=chptr->banlist;bp;bp=bp->next) sendto_one(sptr, rpl_str(RPL_BANLIST), me.name, cptr->name, chptr->chname, bp->banstr, bp->who, bp->when); sendto_one(cptr, rpl_str(RPL_ENDOFBANLIST), me.name, cptr->name, chptr->chname); anylistsent = 1; break; /* we don't pass this along, either.. */ } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } /* do not allow : in bans, or a null ban */ if(*parv[args]==':' || *parv[args] == '\0') { args++; break; } /* make a 'pretty' ban mask here, then try and set it */ /* okay kids, let's do this again. * the buffer returned by pretty_mask is from * make_nick_user_host. This buffer is eaten by add/del banid. * Thus, some poor schmuck gets himself on the banlist. * Fixed. - lucas */ strcpy(nuhbuf, collapse(pretty_mask(parv[args]))); parv[args] = nuhbuf; /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) > REALMODEBUFLEN) { args++; break; } /* if we can't add or delete (depending) the ban, change is * worthless anyhow */ if(!(change=='+' && !add_banid(sptr, chptr, parv[args])) && !(change=='-' && !del_banid(chptr, parv[args]))) { args++; break; } *mbuf++ = 'b'; ADD_PARA(parv[args]) args++; nmodes++; break; case 'j': #ifdef JOINRATE_SERVER_ONLY if (MyClient(sptr)) { sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE), me.name, cptr->name, chptr->chname); break; } #endif if(check_level(level,1,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } /* if it's a -, just change the flag, we have no arguments */ if(change=='-') { if (MyClient(sptr) && (seenalready & MODE_JOINRATE)) break; seenalready |= MODE_JOINRATE; if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) break; *mbuf++ = 'j'; chptr->mode.mode &= ~MODE_JOINRATE; chptr->mode.join_num = 0; chptr->mode.join_time = 0; chptr->join_start = 0; chptr->join_count = 0; nmodes++; break; } else { char *tmpa, *tmperr; int j_num, j_time, tval; if(parv[args] == NULL) { errors|=SM_ERR_MOREPARMS; break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } if (MyClient(sptr) && (seenalready & MODE_JOINRATE)) { args++; break; } seenalready |= MODE_JOINRATE; tmpa = strchr(parv[args], ':'); if(tmpa) { *tmpa = '\0'; tmpa++; j_time = strtol(tmpa, &tmperr, 10); if(*tmperr != '\0' || j_time < 0) { /* error, user specified something * invalid, just bail. */ args++; break; } } else j_time = 0; j_num = strtol(parv[args], &tmperr, 10); if(*tmperr != '\0' || j_num < 0) { args++; break; } /* range limit for local non-samodes */ if (MyClient(sptr) && level < 2) { /* static limits: time <= 60, 4 <= num <= 60 */ if (j_time > 60) j_time = 60; if (j_num > 60) j_num = 60; if (j_num < 4) j_num = 4; /* adjust number to time using min rate 1/8 */ tval = (j_time-1)/8+1; if (j_num < tval) j_num = tval; /* adjust time to number using max rate 2/1 */ tval = j_num/2; if (j_time < tval) j_time = tval; } if(j_num == 0 || j_time == 0) { j_num = j_time = 0; ircsprintf(tmp, "0"); } else ircsprintf(tmp, "%d:%d", j_num, j_time); /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + strlen(tmp)) > REALMODEBUFLEN) { args++; break; } chptr->mode.mode |= MODE_JOINRATE; chptr->mode.join_num = j_num; chptr->mode.join_time = j_time; chptr->join_start = 0; chptr->join_count = 0; *mbuf++ = 'j'; ADD_PARA(tmp); args++; nmodes++; break; } case 'l': if(check_level(level,1,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } /* if it's a -, just change the flag, we have no arguments */ if(change=='-') { if (MyClient(sptr) && (seenalready & MODE_LIMIT)) break; seenalready |= MODE_LIMIT; if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) break; *mbuf++ = 'l'; chptr->mode.mode &= ~MODE_LIMIT; chptr->mode.limit = 0; nmodes++; break; } else { if(parv[args] == NULL) { errors|=SM_ERR_MOREPARMS; break; } if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } if (MyClient(sptr) && (seenalready & MODE_LIMIT)) { args++; break; } seenalready |= MODE_LIMIT; /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + 16) > REALMODEBUFLEN) { args++; break; } i = atoi(parv[args]); /* toss out invalid modes */ if(i < 1) { args++; break; } ircsprintf(tmp, "%d", i); chptr->mode.limit = i; chptr->mode.mode |= MODE_LIMIT; *mbuf++ = 'l'; ADD_PARA(tmp); args++; nmodes++; break; } case 'k': if(check_level(level,1,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } if(parv[args]==NULL) break; if(++nparams > maxparams) { /* too many modes with params, eat this one */ args++; break; } if (MyClient(sptr) && (seenalready & MODE_KEY)) { args++; break; } seenalready |= MODE_KEY; /* do not allow keys to start with :! ack! - lucas */ /* another ack: don't let people set null keys! */ /* and yet a third ack: no spaces in keys -epi */ if(*parv[args]==':' || *parv[args] == '\0' || strchr(parv[args], ' ')) { args++; break; } /* Do not let *'s in keys in preperation for key hiding - Raist * Also take out ",", which makes a channel unjoinable - lucas */ if (strchr(parv[args], '*') != NULL || strchr(parv[args], ',') != NULL) { args++; break; } /* if we're going to overflow our mode buffer, * drop the change instead */ if((prelen + (mbuf - morig) + pidx + KEYLEN+2) > REALMODEBUFLEN) { args++; break; } /* if they're an op, they can futz with the key in * any manner they like, we're not picky */ if(change=='+') { strncpy(chptr->mode.key,parv[args],KEYLEN); ADD_PARA(chptr->mode.key) } else { char *sendkey = chptr->mode.key; if (!*sendkey) sendkey = parv[args]; ADD_PARA(sendkey) *chptr->mode.key = '\0'; } *mbuf++='k'; args++; nmodes++; break; case 'r': if (MyClient(sptr) && (seenalready & MODE_REGISTERED)) break; seenalready |= MODE_REGISTERED; if (!IsServer(sptr) && !IsULine(sptr)) { sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE), me.name, cptr->name, chptr->chname); break; } else { if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) break; if(change=='+') chptr->mode.mode|=MODE_REGISTERED; else chptr->mode.mode&=~MODE_REGISTERED; } *mbuf++='r'; nmodes++; break; case 'L': if (MyClient(sptr) && (seenalready & MODE_LISTED)) break; seenalready |= MODE_LISTED; if (MyClient(sptr)) { sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE), me.name, cptr->name, chptr->chname); break; } else { if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) break; if(change=='+') chptr->mode.mode|=MODE_LISTED; else chptr->mode.mode&=~MODE_LISTED; } *mbuf++='L'; nmodes++; break; case 'i': if(check_level(level,2,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } if(change=='-') while ((lp=chptr->invites)) del_invite(lp->value.cptr, chptr); /* fall through to default case */ default: /* phew, no more tough modes. }:>, the rest are all * covered in one step * with the above array */ if(check_level(level,1,chptr,sptr)) { errors |= SM_ERR_NOPRIVS; break; } for(i=1;flags[i]!=0x0;i+=2) { if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) break; if(*modes==(char)flags[i]) { if (MyClient(sptr) && (seenalready & flags[i-1])) break; seenalready |= flags[i-1]; if(change=='+') chptr->mode.mode |= flags[i-1]; else chptr->mode.mode &= ~flags[i-1]; *mbuf++=*modes; nmodes++; break; } } /* unknown mode.. */ if(flags[i]==0x0) { /* we still spew lots of unknown mode bits...*/ /* but only to our own clients, silently ignore bogosity * from other servers... */ if(MyClient(sptr)) sendto_one(sptr, err_str(ERR_UNKNOWNMODE), me.name, sptr->name, *modes); } break; } /* spit out more parameters error here */ if(errors & SM_ERR_MOREPARMS && MyClient(sptr)) { moreparmsstr[5]=change; moreparmsstr[6]=*modes; sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, sptr->name, moreparmsstr); errors &= ~SM_ERR_MOREPARMS; /* oops, kill it in this case */ } modes++; } /* clean up the end of the string... */ if(*(mbuf-1) == '+' || *(mbuf-1) == '-') *(mbuf-1) = '\0'; else *mbuf = '\0'; pbuf[pidx] = '\0'; if(MyClient(sptr)) { if(errors & SM_ERR_NOPRIVS) sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, sptr->name, chptr->chname); if(errors & SM_ERR_NOTOPER) sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name); if(errors & SM_ERR_RESTRICTED) sendto_one(sptr,":%s NOTICE %s :*** Notice -- You are " "restricted and cannot chanop others", me.name, sptr->name); #ifdef HAVE_SSL if(errors & SM_ERR_SSL) sendto_one(sptr,":%s NOTICE %s :*** Notice -- This channel has (+S) set, which means that is" "restricted to SSL Connections only.", me.name, sptr->name); #endif #ifdef ENABLE_CHANNEL_MODE_D if(errors & SM_ERR_RSL) sendto_one(sptr,":%s NOTICE %s :*** Notice -- Permission denied you may not set (+D) or join a (+D)" "channel since you have been marked has an unresolved client.", me.name, sptr->name); #endif } /* all done! */ return nmodes; #undef ADD_PARA } /* This function allows Admins & IRCops to walk bans etc... -Sheik 04/14/03 */ static int can_walk(aClient *sptr, aChannel *chptr) { if (IsSAdmin(sptr)) { *modebuf = *parabuf = '\0'; modebuf[1] = '\0'; channel_modes(sptr, modebuf, parabuf, chptr); sendto_ops("OperWalk on Channel: (%s), by %s. Modes: (%s%s%s).", chptr->chname, sptr->name, modebuf, (*parabuf!=0 ? " " : ""), parabuf); return 1; } return 0; } static int can_join(aClient *sptr, aChannel *chptr, char *key) { Link *lp; int invited = 0; int error = 0; char *r = NULL; for(lp = sptr->user->invited; lp; lp = lp->next) { if(lp->value.chptr == chptr) { invited = 1; break; } } if (invited || IsULine(sptr)) return 1; if (check_joinrate(chptr, NOW, 1, sptr) == 0) { r = "+j"; error = ERR_CHANNELISFULL; } else if (chptr->mode.mode & MODE_INVITEONLY) { r = "+i"; error = ERR_INVITEONLYCHAN; } else if (chptr->mode.mode & MODE_OPERONLY && !IsOper(sptr)) { r = "+O"; error = ERR_INVITEONLYCHAN; } else if (chptr->mode.limit && chptr->users >= chptr->mode.limit) { r = "+l"; error = ERR_CHANNELISFULL; } else if (chptr->mode.mode & MODE_REGONLY && !IsRegNick(sptr)) error = ERR_NEEDREGGEDNICK; else if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key))) error = ERR_BADCHANNELKEY; #ifdef HAVE_SSL if (chptr->mode.mode & MODE_SSL && !IsUmodez(sptr)) error = ERR_SSL; #endif #ifdef ENABLE_CHANNEL_MODE_D if (chptr->mode.mode & MODE_RSL && IsURSL(sptr)) error = ERR_RSL; #endif #ifdef INVITE_LISTS if (error && is_invited(sptr, chptr)) error = 0; #endif if (!error && is_banned(sptr, chptr, NULL)) error = ERR_BANNEDFROMCHAN; if (error && can_walk(sptr, chptr)) error = 0; if (error) { if (error==ERR_NEEDREGGEDNICK) sendto_one(sptr, getreply(ERR_NEEDREGGEDNICK), me.name, sptr->name, chptr->chname, "join", aliastab[AII_NS].nick, aliastab[AII_NS].server, NS_Register_URL); else sendto_one(sptr, getreply(error), me.name, sptr->name, chptr->chname, r); return 0; } return 1; } /* * can_join_whynot: * puts a list of the modes preventing us from joining in reasonbuf * ret is number of matched modes */ static int can_join_whynot(aClient *sptr, aChannel *chptr, char *key, char *reasonbuf) { Link *lp; int invited = 0; int rbufpos = 0; for(lp = sptr->user->invited; lp; lp = lp->next) { if(lp->value.chptr == chptr) { invited = 1; break; } } if (invited || IsULine(sptr)) return 0; if (chptr->mode.mode & MODE_INVITEONLY) reasonbuf[rbufpos++] = 'i'; if (chptr->mode.mode & MODE_REGONLY && !IsRegNick(sptr)) reasonbuf[rbufpos++] = 'R'; if (chptr->mode.mode & MODE_OPERONLY && !IsOper(sptr)) reasonbuf[rbufpos++] = 'O'; if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key))) reasonbuf[rbufpos++] = 'k'; if (chptr->mode.limit && chptr->users >= chptr->mode.limit) reasonbuf[rbufpos++] = 'l'; if (check_joinrate(chptr, NOW, 1, sptr) == 0) reasonbuf[rbufpos++] = 'j'; #ifdef INVITE_LISTS if (rbufpos && is_invited(sptr, chptr)) rbufpos = 0; #endif if (is_banned(sptr, chptr, NULL)) reasonbuf[rbufpos++] = 'b'; reasonbuf[rbufpos] = '\0'; return rbufpos; } /* * Remove bells and commas from channel name */ void clean_channelname(unsigned char *cn) { for (; *cn; cn++) /* * All characters >33 are allowed, except commas, and the weird * fake-space character mIRCers whine about -wd */ if (*cn < 33 || *cn == ',' || (*cn == 160)) { *cn = '\0'; return; } return; } /* we also tell the client if the channel is invalid. */ int check_channelname(aClient *cptr, unsigned char *cn) { if(!MyClient(cptr)) return 1; for(;*cn;cn++) { if(*cn<33 || *cn == ',' || *cn==160) { sendto_one(cptr, getreply(ERR_BADCHANNAME), me.name, cptr->name, cn); return 0; } } return 1; } /* * * Get Channel block for chname (and allocate a new channel * * block, if it didn't exist before). */ static aChannel * get_channel(aClient *cptr, char *chname, int flag, int *created) { aChannel *chptr; int len; if(created) *created = 0; if (BadPtr(chname)) return NULL; len = strlen(chname); if (MyClient(cptr) && len > CHANNELLEN) { len = CHANNELLEN; *(chname + CHANNELLEN) = '\0'; } if ((chptr = find_channel(chname, (aChannel *) NULL))) return (chptr); if (flag == CREATE) { chptr = make_channel(); if(created) *created = 1; strncpyzt(chptr->chname, chname, len + 1); if (channel) channel->prevch = chptr; chptr->prevch = NULL; chptr->nextch = channel; channel = chptr; chptr->channelts = timeofday; (void) add_to_channel_hash_table(chname, chptr); Count.chan++; } return chptr; } static void add_invite(aClient *cptr, aChannel *chptr) { Link *inv, **tmp; del_invite(cptr, chptr); /* * delete last link in chain if the list is max length */ if (list_length(cptr->user->invited) >= maxchannelsperuser) { /* * This forgets the channel side of invitation -Vesa inv = * cptr->user->invited; cptr->user->invited = inv->next; * free_link(inv); */ del_invite(cptr, cptr->user->invited->value.chptr); } /* * add client to channel invite list */ inv = make_link(); inv->value.cptr = cptr; inv->next = chptr->invites; chptr->invites = inv; /* * add channel to the end of the client invite list */ for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next)); inv = make_link(); inv->value.chptr = chptr; inv->next = NULL; (*tmp) = inv; } /* * Delete Invite block from channel invite list and client invite list */ void del_invite(aClient *cptr, aChannel *chptr) { Link **inv, *tmp; for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next) if (tmp->value.cptr == cptr) { *inv = tmp->next; free_link(tmp); break; } for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next) if (tmp->value.chptr == chptr) { *inv = tmp->next; free_link(tmp); break; } } /* * * Subtract one user from channel i (and free channel * block, if * channel became empty). */ static void sub1_from_channel(aChannel *chptr) { Link *tmp; aBan *bp, *bprem; #ifdef INVITE_LISTS anInvite *invite, *invrem; #endif #ifdef EXEMPT_LISTS aBanExempt *exempt, *exrem; #endif if (--chptr->users <= 0) { /* * Now, find all invite links from channel structure */ while ((tmp = chptr->invites)) del_invite(tmp->value.cptr, chptr); bp = chptr->banlist; while (bp) { bprem = bp; bp = bp->next; MyFree(bprem->banstr); MyFree(bprem->who); MyFree(bprem); } #ifdef INVITE_LISTS invite = chptr->invite_list; while (invite) { invrem = invite; invite = invite->next; MyFree(invrem->invstr); MyFree(invrem->who); MyFree(invrem); } #endif #ifdef EXEMPT_LISTS exempt = chptr->banexempt_list; while (exempt) { exrem = exempt; exempt = exempt->next; MyFree(exrem->banstr); MyFree(exrem->who); MyFree(exrem); } #endif 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); #ifdef FLUD free_fluders(NULL, chptr); #endif free_channel(chptr); Count.chan--; } } /* * m_join * parv[0] = sender prefix * parv[1] = channel * parv[2] = channel password (key) */ int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[]) { static char jbuf[BUFSIZE]; Link *lp; struct simBan *ban; aChannel *chptr; char *name, *key = NULL; int i, flags = 0, chanlen=0; int allow_op = YES; char *p = NULL, *p2 = NULL; #ifdef ANTI_SPAMBOT int successful_join_count = 0; /* Number of channels successfully joined */ #endif /* Stop freezed and shunned users from joining channels 19/03/2006 -Sheik */ if (cptr->user && cptr->user->special_mode) { switch (cptr->user->special_mode) { case 3: #ifdef ERROR_FREEZE_NOTICE sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "JOIN"); #endif return 0; } } if IsShunned(sptr) { #ifdef ERROR_FREEZE_NOTICE sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "JOIN"); #endif return 0; } if (!(sptr->user)) { /* something is *fucked* - bail */ return 0; } if (parc < 2 || *parv[1] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "JOIN"); return 0; } if (MyClient(sptr)) parv[1] = canonize(parv[1]); *jbuf = '\0'; /* * * Rebuild list of channels joined to be the actual result of the * * JOIN. Note that "JOIN 0" is the destructive problem. */ for (i = 0, name = strtoken(&p, parv[1], ","); name; name = strtoken(&p, (char *) NULL, ",")) { /* * pathological case only on longest channel name. * If not dealt * with here, causes desynced channel ops * since ChannelExists() * doesn't see the same channel * as one being joined. cute bug. * Oct 11 1997, Dianora/comstud */ if(!check_channelname(sptr, (unsigned char *) name)) continue; chanlen=strlen(name); if (chanlen > CHANNELLEN) /* same thing is done in get_channel() */ { name[CHANNELLEN] = '\0'; chanlen=CHANNELLEN; } if (*name == '0' && !atoi(name)) *jbuf = '\0'; else if (!IsChannelName(name)) { if (MyClient(sptr)) sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); continue; } if (*jbuf) (void) strcat(jbuf, ","); (void) strncat(jbuf, name, sizeof(jbuf) - i - 1); i += chanlen + 1; } p = NULL; if (parv[2]) key = strtoken(&p2, parv[2], ","); parv[2] = NULL; /* * for m_names call later, parv[parc] * * must == NULL */ for (name = strtoken(&p, jbuf, ","); name; key = (key) ? strtoken(&p2, NULL, ",") : NULL, name = strtoken(&p, NULL, ",")) { /* * JOIN 0 sends out a part for all channels a user * has * joined. */ if (*name == '0' && !atoi(name)) { if (sptr->user->channel == NULL) continue; while ((lp = sptr->user->channel)) { chptr = lp->value.chptr; sendto_channel_butserv(chptr, sptr, PartFmt, parv[0], chptr->chname); remove_user_from_channel(sptr, chptr); } /* * Added /quote set for SPAMBOT * * int spam_time = MIN_JOIN_LEAVE_TIME; int spam_num = * MAX_JOIN_LEAVE_COUNT; */ #ifdef ANTI_SPAMBOT /* Dianora */ if (MyConnect(sptr) && !IsAnOper(sptr)) { if (sptr->join_leave_count >= spam_num) { sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a " "possible spambot", sptr->name, sptr->user->username, sptr->user->host); sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN; } else { int t_delta; if ((t_delta = (NOW - sptr->last_leave_time)) > JOIN_LEAVE_COUNT_EXPIRE_TIME) { int decrement_count; decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME); if (decrement_count > sptr->join_leave_count) sptr->join_leave_count = 0; else sptr->join_leave_count -= decrement_count; } else { if ((NOW - (sptr->last_join_time)) < spam_time) { /* oh, its a possible spambot */ sptr->join_leave_count++; } } sptr->last_leave_time = NOW; } } #endif sendto_serv_butone(cptr, ":%s JOIN 0", parv[0]); continue; } if (MyConnect(sptr)) { /* have we quarantined this channel? */ if(!IsOper(sptr) && (ban = check_mask_simbanned(name, SBAN_CHAN))) { sendto_one(sptr, getreply(ERR_CHANBANREASON), me.name, parv[0], name, BadPtr(ban->reason) ? "Reserved channel" : ban->reason); sendto_realops_lev(REJ_LEV, "Forbidding restricted channel %s from %s.", name, get_client_name(cptr, TRUE)); continue; } /* * local client is first to enter previously nonexistent * * channel so make them (rightfully) the Channel * Operator. */ flags = (ChannelExists(name)) ? 0 : CHFL_CHANOP; if (!IsAnOper(sptr) && server_was_split && !(confopts & FLAGS_SPLITOPOK)) allow_op = NO; if ((sptr->user->joined >= maxchannelsperuser) && (!IsAnOper(sptr) || (sptr->user->joined >= maxchannelsperuser * 3))) { sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS), me.name, parv[0], name); #ifdef ANTI_SPAMBOT if (successful_join_count) sptr->last_join_time = NOW; #endif return 0; } #ifdef ANTI_SPAMBOT /* * Dianora */ if (flags == 0) /* if channel doesn't exist, don't penalize */ successful_join_count++; if (sptr->join_leave_count >= spam_num) { /* Its already known as a possible spambot */ if (sptr->oper_warn_count_down > 0) /* my general paranoia */ sptr->oper_warn_count_down--; else sptr->oper_warn_count_down = 0; if (sptr->oper_warn_count_down == 0) { sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) trying to " "join %s is a possible spambot", sptr->name, sptr->user->username, sptr->user->host, name); sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN; } # ifndef ANTI_SPAMBOT_WARN_ONLY return 0; /* Don't actually JOIN anything, but * don't let spambot know that */ # endif } #endif } else { /* * complain for remote JOINs to existing channels * (they * should be SJOINs) -orabidoo */ if (!ChannelExists(name)) ts_warn("User on %s remotely JOINing new channel", sptr->user->server); } chptr = get_channel(sptr, name, CREATE, NULL); if (chptr && IsMember(sptr, chptr)) continue; if (!chptr || (MyConnect(sptr) && !can_join(sptr, chptr, key))) { #ifdef ANTI_SPAMBOT if (successful_join_count > 0) successful_join_count--; #endif continue; } /* only complain when the user can join the channel, the channel is * being created by this user, and this user is not allowed to be an op. * - lucas */ if (flags && !allow_op) sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Due to a network " "split, you can not obtain channel operator status in " "a new channel at this time.", me.name, sptr->name); /* Complete user entry to the new channel (if any) */ if (allow_op) add_user_to_channel(chptr, sptr, flags); else add_user_to_channel(chptr, sptr, 0); chptr->join_count++; /* Set timestamp if appropriate, and propagate */ if (MyClient(sptr) && flags == CHFL_CHANOP) { chptr->channelts = timeofday; /* we keep channel "creations" to the server sjoin format, so we can bounce modes and stuff if our ts is older. */ if (allow_op) sendto_serv_butone(cptr, ":%s SJOIN %ld %s + :@%s", me.name, chptr->channelts, name, parv[0]); else sendto_serv_butone(cptr, ":%s SJOIN %ld %s + :%s", me.name, chptr->channelts, name, parv[0]); } else if (MyClient(sptr)) sendto_serv_butone(cptr, CliSJOINFmt, parv[0], chptr->channelts, name); else sendto_serv_butone(cptr, ":%s JOIN :%s", parv[0], name); /* notify all other users on the new channel */ sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name); if (MyClient(sptr)) { del_invite(sptr, chptr); if (chptr->topic[0] != '\0') { sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0], name, chptr->topic); sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0], name, chptr->topic_nick, chptr->topic_time); } parv[1] = name; (void) m_names(cptr, sptr, 2, parv); } } #ifdef ANTI_SPAMBOT if (MyConnect(sptr) && successful_join_count) sptr->last_join_time = NOW; #endif return 0; } /* m_sajoin * join a channel regardless of modes. */ int m_sajoin(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr; char *name; int i; char errmodebuf[128]; /* Remote sajoin? nope. */ if(!MyClient(sptr)) return 0; if(!IsSAdmin(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (parc < 2 || *parv[1] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SAJOIN"); return 0; } name = parv[1]; chptr = find_channel(name, NULL); if(!chptr) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); return 0; } /* bail if they're already in the channel */ if(IsMember(sptr, chptr)) return 0; if((i = can_join_whynot(sptr, chptr, NULL, errmodebuf))) { send_globops("from %s: %s used SAJOIN (%s +%s)", me.name, sptr->name, chptr->chname, errmodebuf); sendto_serv_butone(NULL, ":%s GLOBOPS :%s used SAJOIN (%s +%s)", me.name, sptr->name, chptr->chname, errmodebuf); } else sendto_one(sptr, ":%s NOTICE %s :You didn't need to use" " /SAJOIN for %s", me.name, parv[0], chptr->chname); add_user_to_channel(chptr, sptr, 0); sendto_serv_butone(cptr, CliSJOINFmt, parv[0], chptr->channelts, name); sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name); if(MyClient(sptr)) { if(chptr->topic[0] != '\0') { sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0], name, chptr->topic); sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0], name, chptr->topic_nick, chptr->topic_time); } parv[1] = name; parv[2] = NULL; m_names(cptr, sptr, 2, parv); } return 0; } /* * m_part * parv[0] = sender prefix * parv[1] = channel * parv[2] = Optional part reason */ int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr; char *p, *name; char *reason = (parc > 2 && parv[2]) ? parv[2] : NULL; if (parc < 2 || parv[1][0] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PART"); return 0; } name = strtoken(&p, parv[1], ","); /* Stop freezed & Shunned users from parting channels. 19/03/2006 -Sheik */ if (cptr->user && cptr->user->special_mode) { switch (cptr->user->special_mode) { case 3: #ifdef ERROR_FREEZE_NOTICE sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PART"); #endif return 0; } } if IsShunned(sptr) { #ifdef ERROR_FREEZE_NOTICE sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PART"); #endif return 0; } #ifdef ANTI_SPAMBOT /* Dianora */ /* if its my client, and isn't an oper */ if (name && MyConnect(sptr) && !IsAnOper(sptr)) { if (sptr->join_leave_count >= spam_num) { sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a possible" " spambot", sptr->name, sptr->user->username, sptr->user->host); sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN; } else { int t_delta; if ((t_delta = (NOW - sptr->last_leave_time)) > JOIN_LEAVE_COUNT_EXPIRE_TIME) { int decrement_count; decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME); if (decrement_count > sptr->join_leave_count) sptr->join_leave_count = 0; else sptr->join_leave_count -= decrement_count; } else { if ((NOW - (sptr->last_join_time)) < spam_time) { /* oh, its a possible spambot */ sptr->join_leave_count++; } } sptr->last_leave_time = NOW; } } #endif while (name) { chptr = get_channel(sptr, name, 0, NULL); if (!chptr) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); name = strtoken(&p, (char *) NULL, ","); continue; } if (!IsMember(sptr, chptr)) { sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], name); name = strtoken(&p, (char *) NULL, ","); continue; } /* Remove user from the old channel (if any) */ if (parc < 3 || can_send(sptr,chptr,reason)) sendto_serv_butone(cptr, PartFmt, parv[0], name); else sendto_serv_butone(cptr, PartFmt2, parv[0], name, reason); if (parc < 3 || can_send(sptr,chptr,reason)) sendto_channel_butserv(chptr, sptr, PartFmt, parv[0], name); else sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], name, reason); remove_user_from_channel(sptr, chptr); name = strtoken(&p, (char *) NULL, ","); } return 0; } /* * m_kick * parv[0] = sender prefix * parv[1] = channel * parv[2] = client to kick * parv[3] = kick comment */ int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *who; aChannel *chptr; int chasing = 0; int user_count; /* count nicks being kicked, only allow 4 */ char *comment, *name, *p = NULL, *user, *p2 = NULL; if (parc < 3 || *parv[1] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KICK"); return 0; } if (IsServer(sptr) && !IsULine(sptr)) sendto_ops("KICK from %s for %s %s", parv[0], parv[1], parv[2]); comment = (BadPtr(parv[3])) ? parv[0] : parv[3]; if (strlen(comment) > (size_t) TOPICLEN) comment[TOPICLEN] = '\0'; *nickbuf = *buf = '\0'; name = strtoken(&p, parv[1], ","); while (name) { chptr = get_channel(sptr, name, !CREATE, NULL); if (!chptr) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); name = strtoken(&p, (char *) NULL, ","); continue; } /* * You either have chan op privs, or you don't -Dianora * * orabidoo and I discussed this one for a while... I hope he * approves of this code, users can get quite confused... * -Dianora */ if (!IsServer(sptr) && !is_chan_op(sptr, chptr) && !is_halfop(sptr, chptr) && !IsULine(sptr)) { /* was a user, not a server and user isn't seen as a chanop here */ if (MyConnect(sptr)) { /* user on _my_ server, with no chanops.. so go away */ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0], chptr->chname); name = strtoken(&p, (char *) NULL, ","); continue; } if (chptr->channelts == 0) { /* If its a TS 0 channel, do it the old way */ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0], chptr->chname); name = strtoken(&p, (char *) NULL, ","); continue; } /* * Its a user doing a kick, but is not showing as chanop * locally its also not a user ON -my- server, and the channel * has a TS. There are two cases we can get to this point * then... * * 1) connect burst is happening, and for some reason a legit op * has sent a KICK, but the SJOIN hasn't happened yet or been * seen. (who knows.. due to lag...) * * 2) The channel is desynced. That can STILL happen with TS * * Now, the old code roger wrote, would allow the KICK to go * through. Thats quite legit, but lets weird things like * KICKS by users who appear not to be chanopped happen, or * even neater, they appear not to be on the channel. This * fits every definition of a desync, doesn't it? ;-) So I * will allow the KICK, otherwise, things are MUCH worse. But * I will warn it as a possible desync. * * -Dianora * * sendto_one(sptr, err_str(ERR_DESYNC), me.name, parv[0], * chptr->chname); * * After more discussion with orabidoo... * * The code was sound, however, what happens if we have +h (TS4) * and some servers don't understand it yet? we will be seeing * servers with users who appear to have no chanops at all, * merrily kicking users.... -Dianora * */ } user = strtoken(&p2, parv[2], ","); user_count = 4; while (user && user_count) { user_count--; if (!(who = find_chasing(sptr, user, &chasing))) { user = strtoken(&p2, (char *) NULL, ","); continue; /* No such user left! */ } if (IsMember(who, chptr)) { if (is_only_halfop(sptr, chptr) && (is_chan_op(who,chptr) || is_halfop(who,chptr))) { sendto_one (who, ":%s NOTICE %s :*** Notice -- %s attempted to kick you, however you are a channel operator", me.name, who->name, sptr->name); // this will help out paranoid channel operators -Sheik. sendto_one(sptr, ":%s NOTICE %s :*** Notice -- You cant kick %s, due to insufficient privileges", me.name, parv[0], who->name); } else { sendto_channel_butserv(chptr, sptr, ":%s KICK %s %s :%s", parv[0], name, who->name, comment); sendto_serv_butone(cptr, ":%s KICK %s %s :%s", parv[0], name, who->name, comment); remove_user_from_channel(who, chptr); } } else sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL), me.name, parv[0], user, name); user = strtoken(&p2, (char *) NULL, ","); } /* loop on parv[2] */ name = strtoken(&p, (char *) NULL, ","); } /* loop on parv[1] */ return (0); } int count_channels(aClient *sptr) { aChannel *chptr; int count = 0; for (chptr = channel; chptr; chptr = chptr->nextch) count++; return (count); } void send_topic_burst(aClient *cptr) { aChannel *chptr; aClient *acptr; if (!(confopts & FLAGS_SERVHUB) || !(cptr->serv->uflags & ULF_NOBTOPIC)) for (chptr = channel; chptr; chptr = chptr->nextch) { if(chptr->topic[0] != '\0') sendto_one(cptr, ":%s TOPIC %s %s %ld :%s", me.name, chptr->chname, chptr->topic_nick, chptr->topic_time, chptr->topic); } if (!(confopts & FLAGS_SERVHUB) || !(cptr->serv->uflags & ULF_NOAWAY)) for (acptr = client; acptr; acptr = acptr->next) { if(!IsPerson(acptr) || acptr->from == cptr) continue; if(acptr->user->away) sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away); } } /* * m_topic * parv[0] = sender prefix * parv[1] = topic text */ int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr = NullChn; char *topic = NULL, *name, *tnick = sptr->name; time_t ts = timeofday; int member; if (parc < 2) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "TOPIC"); return 0; } name = parv[1]; chptr = find_channel(name, NullChn); if(!chptr) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name); return 0; } member = IsMember(sptr, chptr); if (parc == 2) /* user is requesting a topic */ { char *namep = chptr->chname; char tempchname[CHANNELLEN + 2]; if(!member && !(ShowChannel(sptr, chptr))) { if(IsAdmin(sptr)) { tempchname[0] = '%'; strcpy(&tempchname[1], chptr->chname); namep = tempchname; } else { sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], name); return 0; } } if (chptr->topic[0] == '\0') sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], namep); else { sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0], namep, chptr->topic); sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0], namep, chptr->topic_nick, chptr->topic_time); } return 0; } topic = parv[2]; if (MyClient(sptr)) { if (!member) { sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],name); return 0; } if ((chptr->mode.mode & MODE_TOPICLIMIT) && !is_chan_op(sptr, chptr)) { sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0], chptr->chname); return 0; } } else { /* extended info */ if (parc > 3) { topic = (parc > 4 ? parv[4] : ""); tnick = parv[2]; ts = atoi(parv[3]); } /* ignore old topics during burst/race */ if (!IsULine(sptr) && chptr->topic[0] && chptr->topic_time >= ts) return 0; } strncpyzt(chptr->topic, topic, TOPICLEN + 1); strcpy(chptr->topic_nick, tnick); chptr->topic_time = ts; /* in this case I think it's better that we send all the info that df * sends with the topic, so I changed everything to work like that. * -wd */ sendto_serv_butone(cptr, ":%s TOPIC %s %s %lu :%s", parv[0], chptr->chname, chptr->topic_nick, chptr->topic_time, chptr->topic); sendto_channel_butserv_me(chptr, sptr, ":%s TOPIC %s :%s", parv[0], chptr->chname, chptr->topic); return 0; } /* * m_invite * parv[0] - sender prefix * parv[1] - user to invite * parv[2] - channel name */ int m_invite(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; aChannel *chptr = NULL; if (parc < 3 || *parv[1] == 0) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "INVITE"); return -1; } if (!(acptr = find_person(parv[1], NULL))) { sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]); return 0; } if (MyClient(sptr)) { if (!(chptr = find_channel(parv[2], NULL))) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[2]); return 0; } if (!IsMember(sptr, chptr)) { sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], parv[2]); return 0; } if (IsMember(acptr, chptr)) { sendto_one(sptr, err_str(ERR_USERONCHANNEL), me.name, parv[0], parv[1], chptr->chname); return 0; } if (!is_chan_op(sptr, chptr)) { sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0], chptr->chname); return 0; } sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0], acptr->name, chptr->chname); if (acptr->user->away) sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0], acptr->name, acptr->user->away); } if (MyClient(acptr)) { /* stuff already done above */ if (!MyClient(sptr)) { if (!(chptr = find_channel(parv[2], NullChn))) return 0; if (IsMember(acptr, chptr)) return 0; } add_invite(acptr, chptr); sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", parv[0], acptr->name, chptr->chname); sendto_channelflags_butone(NULL, &me, chptr, CHFL_CHANOP, ":%s NOTICE @%s :%s invited %s into " "channel %s", me.name, chptr->chname, parv[0], acptr->name, chptr->chname); return 0; } sendto_one(acptr, ":%s INVITE %s :%s", parv[0], parv[1], parv[2]); return 0; } /* * The function which sends the actual channel list back to the user. * Operates by stepping through the hashtable, sending the entries back if * they match the criteria. * cptr = Local client to send the output back to. * numsend = Number (roughly) of lines to send back. Once this number has * been exceeded, send_list will finish with the current hash bucket, * and record that number as the number to start next time send_list * is called for this user. So, this function will almost always send * back more lines than specified by numsend (though not by much, * assuming CH_MAX is was well picked). So be conservative in your choice * of numsend. -Rak */ void send_list(aClient *cptr, int numsend) { aChannel *chptr; LOpts *lopt = cptr->user->lopt; int hashnum; char tempbuff[KEYLEN+8+3+1+3+6]; char modestuff[TOPICLEN+3+KEYLEN+8+3+1+6]; for (hashnum = lopt->starthash; hashnum < CH_MAX; hashnum++) { if (numsend > 0) { for (chptr = (aChannel *)hash_get_chan_bucket(hashnum); chptr; chptr = chptr->hnextch) { if (SecretChannel(chptr) && !IsAnOper(cptr) && !IsMember(cptr, chptr)) continue; #ifdef USE_CHANMODE_L if (lopt->only_listed && !(chptr->mode.mode & MODE_LISTED)) continue; #endif if ((!lopt->showall) && ((chptr->users < lopt->usermin) || ((lopt->usermax >= 0) && (chptr->users > lopt->usermax)) || ((chptr->channelts) < lopt->chantimemin) || (chptr->topic_time < lopt->topictimemin) || (chptr->channelts > lopt->chantimemax) || (chptr->topic_time > lopt->topictimemax) || (lopt->nolist && find_str_link(lopt->nolist, chptr->chname)) || (lopt->yeslist && !find_str_link(lopt->yeslist, chptr->chname)))) continue; /* Seem'd more efficent to seperate into two commands * then adding an or to the inline. -- Doc. */ if (IsAnOper(cptr)) { *modebuf = *parabuf = '\0'; modebuf[1] = '\0'; channel_modes(cptr, modebuf, parabuf, chptr); if (SecretChannel(chptr) || HiddenChannel(chptr)) ircsprintf(tempbuff, "\002[%s %s]\002", modebuf, parabuf); else ircsprintf(tempbuff, "[%s %s]", modebuf, parabuf); ircsprintf(modestuff, "%-20s %s", tempbuff, chptr->topic); sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name, chptr->chname, chptr->users, modestuff); } else { sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name, ShowChannel(cptr, chptr) ? chptr->chname : "*", chptr->users, ShowChannel(cptr, chptr) ? chptr->topic : ""); } numsend--; } } else break; } /* All done */ if (hashnum == CH_MAX) { Link *lp, *next; sendto_one(cptr, rpl_str(RPL_LISTEND), me.name, cptr->name); for (lp = lopt->yeslist; lp; lp = next) { next = lp->next; MyFree(lp->value.cp); free_link(lp); } for (lp = lopt->nolist; lp; lp = next) { next = lp->next; MyFree(lp->value.cp); free_link(lp); } MyFree(cptr->user->lopt); cptr->user->lopt = NULL; remove_from_list(&listing_clients, cptr, NULL); return; } /* * We've exceeded the limit on the number of channels to send back * at once. */ lopt->starthash = hashnum; return; } /* This code is by the fqircd team ( http://code.freequest.net/) credit goes to them. * It has been modified slightly by Sheik 4/16/05 * m_dolist * This function works as a wrapper for /LIST * Previous versions of this code had two functions, one for /QLIST and one for /LIST * Now we just call m_list directly from MSG_QLIST and MSG_LIST gets thrown into this wrapper */ #ifdef STRICT_LIST int m_dolist(aClient *cptr, aClient *sptr, int parc, char *parv[]) { /* If you're not in the bots class or using a registered nick, you'll get a message to use /QLIST instead */ char *class; if(sptr->class) class = sptr->class->name; else class = "NONE"; if(!bot_class) bot_class = "unconfigured"; if(!StrEq(class, bot_class) && !IsOper(sptr) && !IsRegNick(sptr)) { sendto_one(sptr, ":%s NOTICE %s :Usage of /LIST is restricted, identify for your nickname or use /QLIST instead.", me.name, parv[0]); sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); return 0; } m_list(cptr,sptr,parc,parv); return 0; } #endif /* * m_list * parv[0] = sender prefix * parv[1] = channel */ int m_list(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr; time_t currenttime = time(NULL); char *name, *p = NULL; LOpts *lopt = NULL; Link *lp, *next; int usermax, usermin, error = 0, doall = 0, only_listed = 1; int x; time_t chantimemin, chantimemax; ts_val topictimemin, topictimemax; Link *yeslist = NULL, *nolist = NULL; static char *usage[] = { "Usage: /raw LIST options (on mirc) or /quote LIST options (ircII)", "", "If you don't include any options, the default is to send you the", "entire unfiltered list of channels. Below are the options you can", "use, and what channels LIST will return when you use them.", ">number List channels with more than people.", " people.", "C>number List channels created between now and minutes ago.", "C minutes ago.", "T>number List channels whose topics are older than minutes", " (Ie, they have not changed in the last minutes.", "T " "minutes.", "*mask* List channels that match *mask*", "!*mask* List channels that do not match *mask*", NULL }; /* Some starting san checks -- No interserver lists allowed. */ if (cptr != sptr || !sptr->user) return 0; if (IsSquelch(sptr)) { sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); return 0; } /* If a /list is in progress, then another one will cancel it */ if ((lopt = sptr->user->lopt)!=NULL) { sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); for (lp = lopt->yeslist; lp; lp = next) { next = lp->next; free_link(lp); } for (lp = lopt->nolist; lp; lp = next) { next = lp->next; free_link(lp); } MyFree(sptr->user->lopt); sptr->user->lopt = NULL; remove_from_list(&listing_clients, sptr, NULL); return 0; } if (parc < 2 || BadPtr(parv[1])) { sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]); lopt = sptr->user->lopt = (LOpts *) MyMalloc(sizeof(LOpts)); memset(lopt, '\0', sizeof(LOpts)); lopt->showall = 1; #ifdef USE_CHANMODE_L lopt->only_listed = 1; #endif add_to_list(&listing_clients, sptr); if (SBufLength(&cptr->sendQ) < 2048) send_list(cptr, 64); return 0; } if ((parc == 2) && (parv[1][0] == '?') && (parv[1][1] == '\0')) { char **ptr = usage; for (; *ptr; ptr++) sendto_one(sptr, rpl_str(RPL_COMMANDSYNTAX), me.name, cptr->name, *ptr); return 0; } sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]); chantimemax = topictimemax = currenttime + 86400; chantimemin = topictimemin = 0; usermin = 0; usermax = -1; /* No maximum */ for (name = strtoken(&p, parv[1], ","); name && !error; name = strtoken(&p, (char *) NULL, ",")) { switch (*name) { case '<': usermax = atoi(name+1) - 1; doall = 1; break; case '>': usermin = atoi(name+1) + 1; doall = 1; break; #ifdef USE_CHANMODE_L case '-': if(!strcasecmp(++name,"all")) { only_listed = 0; doall = 1; } break; #endif case 'C': case 'c': /* Channel TS time -- creation time? */ ++name; switch (*name++) { case '<': chantimemax = currenttime - 60 * atoi(name); doall = 1; break; case '>': chantimemin = currenttime - 60 * atoi(name); doall = 1; break; default: sendto_one(sptr, err_str(ERR_LISTSYNTAX), me.name, cptr->name); error = 1; } break; case 'T': case 't': ++name; switch (*name++) { case '<': topictimemax = currenttime - 60 * atoi(name); doall = 1; break; case '>': topictimemin = currenttime - 60 * atoi(name); doall = 1; break; default: sendto_one(sptr, err_str(ERR_LISTSYNTAX), me.name, cptr->name); error = 1; } break; default: /* A channel, possibly with wildcards. * Thought for the future: Consider turning wildcard * processing on the fly. * new syntax: !channelmask will tell ircd to ignore * any channels matching that mask, and then * channelmask will tell ircd to send us a list of * channels only masking channelmask. Note: Specifying * a channel without wildcards will return that * channel even if any of the !channelmask masks * matches it. */ if (*name == '!') { doall = 1; lp = make_link(); lp->next = nolist; nolist = lp; DupString(lp->value.cp, name+1); } else if (strchr(name, '*') || strchr(name, '?')) { doall = 1; lp = make_link(); lp->next = yeslist; yeslist = lp; DupString(lp->value.cp, name); } else /* Just a normal channel */ { chptr = find_channel(name, NullChn); if (chptr && ((x = ShowChannel(sptr, chptr)) || IsAdmin(sptr))) { char *nameptr = name; char channame[CHANNELLEN + 2]; if(!x && IsAdmin(sptr)) { channame[0] = '%'; strcpy(&channame[1], chptr->chname); nameptr = channame; } sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0], nameptr, chptr->users, chptr->topic); } } } /* switch */ } /* while */ if (doall) { lopt = sptr->user->lopt = (LOpts *) MyMalloc(sizeof(LOpts)); memset(lopt, '\0', sizeof(LOpts)); lopt->usermin = usermin; lopt->usermax = usermax; lopt->topictimemax = topictimemax; lopt->topictimemin = topictimemin; lopt->chantimemax = chantimemax; lopt->chantimemin = chantimemin; lopt->nolist = nolist; lopt->yeslist = yeslist; lopt->only_listed = only_listed; add_to_list(&listing_clients, sptr); if (SBufLength(&cptr->sendQ) < 2048) send_list(cptr, 64); return 0; } sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); return 0; } /************************************************************************ * m_names() - Added by Jto 27 Apr 1989 * 12 Feb 2000 - geesh, time for a rewrite -lucas ************************************************************************/ /* * m_names * parv[0] = sender prefix * parv[1] = channel */ /* maximum names para to show to opers when abuse occurs */ #define TRUNCATED_NAMES 64 int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[]) { int mlen = strlen(me.name) + NICKLEN + 7; aChannel *chptr; aClient *acptr; int member; chanMember *cm; int idx, flag = 1, spos; char *s, *para = parv[1]; if (parc < 2 || !MyConnect(sptr)) { sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], "*"); return 0; } for(s = para; *s; s++) { if(*s == ',') { if(strlen(para) > TRUNCATED_NAMES) para[TRUNCATED_NAMES] = '\0'; sendto_realops("names abuser %s %s", get_client_name(sptr, FALSE), para); sendto_one(sptr, err_str(ERR_TOOMANYTARGETS), me.name, sptr->name, "NAMES"); return 0; } } if(!check_channelname(sptr, (unsigned char *)para)) return 0; chptr = find_channel(para, (aChannel *) NULL); if (!chptr || !ShowChannel(sptr, chptr)) { sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para); return 0; } /* cache whether this user is a member of this channel or not */ member = IsMember(sptr, chptr); if(PubChannel(chptr)) buf[0] = '='; else if(SecretChannel(chptr)) buf[0] = '@'; else buf[0] = '*'; idx = 1; buf[idx++] = ' '; for(s = chptr->chname; *s; s++) buf[idx++] = *s; buf[idx++] = ' '; buf[idx++] = ':'; /* If we go through the following loop and never add anything, we need this to be empty, otherwise spurious things from the LAST /names call get stuck in there.. - lucas */ buf[idx] = '\0'; spos = idx; /* starting point in buffer for names!*/ for (cm = chptr->members; cm; cm = cm->next) { acptr = cm->cptr; if(IsInvisible(acptr) && !member) continue; if(cm->flags & CHFL_CHANOP) buf[idx++] = '@'; else if(cm->flags & CHFL_HALFOP) buf[idx++] = '%'; else if(cm->flags & CHFL_VOICE) buf[idx++] = '+'; for(s = acptr->name; *s; s++) buf[idx++] = *s; buf[idx++] = ' '; buf[idx] = '\0'; flag = 1; if(mlen + idx + NICKLEN > BUFSIZE - 3) { sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); idx = spos; flag = 0; } } if (flag) sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para); return 0; } void send_user_joins(aClient *cptr, aClient *user) { Link *lp; aChannel *chptr; int cnt = 0, len = 0, clen; char *mask; *buf = ':'; (void) strcpy(buf + 1, user->name); (void) strcat(buf, " JOIN "); len = strlen(user->name) + 7; for (lp = user->user->channel; lp; lp = lp->next) { chptr = lp->value.chptr; if (*chptr->chname == '&') continue; if ((mask = strchr(chptr->chname, ':'))) if (match(++mask, cptr->name)) continue; clen = strlen(chptr->chname); if (clen > (size_t) BUFSIZE - 7 - len) { if (cnt) sendto_one(cptr, "%s", buf); *buf = ':'; (void) strcpy(buf + 1, user->name); (void) strcat(buf, " JOIN "); len = strlen(user->name) + 7; cnt = 0; } (void) strcpy(buf + len, chptr->chname); cnt++; len += clen; if (lp->next) { len++; (void) strcat(buf, ","); } } if (*buf && cnt) sendto_one(cptr, "%s", buf); return; } void kill_ban_list(aClient *cptr, aChannel *chptr) { void *pnx; aBan *bp; #ifdef EXEMPT_LISTS aBanExempt *ep; #endif #ifdef INVITE_LISTS anInvite *ip; #endif char *cp; int count = 0, send = 0; cp = modebuf; *cp++ = '-'; *cp = '\0'; *parabuf = '\0'; for (bp = chptr->banlist; bp; bp = bp->next) { if (strlen(parabuf) + strlen(bp->banstr) + 10 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, bp->banstr); count++; *cp++ = 'b'; *cp = '\0'; } else if (*parabuf) send = 1; if (count == MAXMODEPARAMS) send = 1; if (send) { sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s", cptr->name, chptr->chname, modebuf, parabuf); send = 0; *parabuf = '\0'; cp = modebuf; *cp++ = '-'; if (count != MAXMODEPARAMS) { strcpy(parabuf, bp->banstr); *cp++ = 'b'; count = 1; } else count = 0; *cp = '\0'; } } #ifdef EXEMPT_LISTS for (ep = chptr->banexempt_list; ep; ep = ep->next) { if (strlen(parabuf) + strlen(ep->banstr) + 10 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, ep->banstr); count++; *cp++ = 'e'; *cp = '\0'; } else if (*parabuf) send = 1; if (count == MAXMODEPARAMS) send = 1; if (send) { sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s", cptr->name, chptr->chname, modebuf, parabuf); send = 0; *parabuf = '\0'; cp = modebuf; *cp++ = '-'; if (count != MAXMODEPARAMS) { strcpy(parabuf, ep->banstr); *cp++ = 'e'; count = 1; } else count = 0; *cp = '\0'; } } #endif #ifdef INVITE_LISTS for (ip = chptr->invite_list; ip; ip = ip->next) { if (strlen(parabuf) + strlen(ip->invstr) + 10 < (size_t) MODEBUFLEN) { if(*parabuf) strcat(parabuf, " "); strcat(parabuf, ip->invstr); count++; *cp++ = 'I'; *cp = '\0'; } else if (*parabuf) send = 1; if (count == MAXMODEPARAMS) send = 1; if (send) { sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s", cptr->name, chptr->chname, modebuf, parabuf); send = 0; *parabuf = '\0'; cp = modebuf; *cp++ = '-'; if (count != MAXMODEPARAMS) { strcpy(parabuf, ip->invstr); *cp++ = 'I'; count = 1; } else count = 0; *cp = '\0'; } } #endif if(*parabuf) { sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s", cptr->name, chptr->chname, modebuf, parabuf); } /* physically destroy channel ban list */ bp = chptr->banlist; while(bp) { pnx = bp->next; MyFree(bp->banstr); MyFree(bp->who); MyFree(bp); bp = pnx; } chptr->banlist = NULL; #ifdef EXEMPT_LISTS ep = chptr->banexempt_list; while(ep) { pnx = ep->next; MyFree(ep->banstr); MyFree(ep->who); MyFree(ep); ep = pnx; } chptr->banexempt_list = NULL; #endif #ifdef INVITE_LISTS ip = chptr->invite_list; while(ip) { pnx = ip->next; MyFree(ip->invstr); MyFree(ip->who); MyFree(ip); ip = pnx; } chptr->invite_list = NULL; #endif /* reset bquiet cache */ chptr->banserial++; } static inline void sjoin_sendit(aClient *cptr, aClient *sptr, aChannel *chptr, char *from) { sendto_channel_butserv_me(chptr, sptr, ":%s MODE %s %s %s", from, chptr->chname, modebuf, parabuf); } /* m_resynch * * parv[0] = sender * parv[1] = #channel * * Sent from a server I am directly connected to that is requesting I resend * EVERYTHING I know about #channel. */ int m_resynch(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr; if(!MyConnect(sptr) || !IsServer(sptr) || parc < 2) return 0; chptr = find_channel(parv[1], NullChn); sendto_realops_lev(DEBUG_LEV, "%s is requesting a resynch of %s%s", parv[0], parv[1], (chptr == NullChn) ? " [failed]" : ""); if (chptr != NullChn) send_channel_modes(sptr, chptr); return 0; } /* * m_sjoin * parv[0] - sender * parv[1] - TS * parv[2] - channel * parv[3] - modes + n arguments (key and/or limit) * parv[4+n] - flags+nick list (all in one parameter) * * process a SJOIN, taking the TS's into account to either ignore the * incoming modes or undo the existing ones or merge them, and JOIN all * the specified users while sending JOIN/MODEs to non-TS servers and * to clients */ #define INSERTSIGN(x,y) \ if (what != x) { \ *mbuf++=y; \ what = x; \ } #define SJ_MODEPLUS(x, y) \ if(((y) & mode.mode) && !((y) & oldmode->mode)) \ { \ INSERTSIGN(1, '+') \ *mbuf++ = (x); \ } #define SJ_MODEMINUS(x, y) \ if(((y) & oldmode->mode) && !((y) & mode.mode)) \ { \ INSERTSIGN(-1, '-') \ *mbuf++ = (x); \ } #define SJ_MODEADD(x, y) case (x): mode.mode |= (y); break #define ADD_PARA(p) para = p; if(pbpos) parabuf[pbpos++] = ' '; \ while(*para) parabuf[pbpos++] = *para++; #define ADD_SJBUF(p) para = p; if(sjbufpos) sjbuf[sjbufpos++] = ' '; \ while(*para) sjbuf[sjbufpos++] = *para++; int m_sjoin(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr; aClient *acptr; ts_val newts, oldts, tstosend; static Mode mode, *oldmode; chanMember *cm; int args = 0, haveops = 0, keepourmodes = 1, keepnewmodes = 1, doesop = 0, what = 0, pargs = 0, fl, people = 0, isnew, clientjoin = 0, pbpos, sjbufpos, created = 0; char *s, *s0, *para; static char numeric[16], sjbuf[BUFSIZE]; char keep_modebuf[REALMODEBUFLEN], keep_parabuf[REALMODEBUFLEN]; char *mbuf = modebuf, *p; /* if my client is SJOINing, it's just a local user being a dufus. * Ignore him. * parc >= 5 (new serv<->serv SJOIN format) * parc >= 6 (old serv<->serv SJOIN format) * parc == 3 (new serv<->serv cliSJOIN format) */ if (MyClient(sptr) || (parc < 5 && IsServer(sptr)) || (parc < 3 && IsPerson(sptr))) return 0; if(parc == 3 && IsPerson(sptr)) clientjoin = 1; else if(IsDigit(parv[2][0])) { int i; if(parc < 6) return 0; for(i = 2; i < (parc - 1); i++) parv[i] = parv[i+1]; parc--; } if (!IsChannelName(parv[2])) return 0; newts = atol(parv[1]); isnew = ChannelExists(parv[2]) ? 0 : 1; chptr = get_channel(sptr, parv[2], CREATE, &created); oldts = chptr->channelts; for (cm = chptr->members; cm; cm = cm->next) if (cm->flags & MODE_CHANOP) { haveops++; break; } if(clientjoin) /* we have a good old client sjoin, with timestamp */ { if (isnew) chptr->channelts = tstosend = newts; else if (newts == 0 || oldts == 0) chptr->channelts = tstosend = 0; else if (newts == oldts) tstosend = oldts; else if (newts < oldts) { #ifdef OLD_WEIRD_CHANOP_NEGOTIATION if (haveops) tstosend = oldts; else chptr->channelts = tstosend = newts; #else chptr->channelts = tstosend = newts; if (!IsULine(sptr)) sendto_realops_lev(DEBUG_LEV, "Changing TS for %s from %d to %d on" " client SJOIN", chptr->chname, oldts, newts); #endif } else tstosend = oldts; /* parv[0] is the client that is joining. parv[0] == sptr->name */ if (!IsMember(sptr, chptr)) { add_user_to_channel(chptr, sptr, 0); chptr->join_count++; chptr->default_join_count++; sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], parv[2]); } sendto_serv_butone(cptr, CliSJOINFmt, parv[0], tstosend, parv[2]); /* if the channel is created in client sjoin, * we lost some channel modes. */ if(created) { sendto_realops_lev(DEBUG_LEV, "Requesting resynch of %s from " "%s (%s!%s@%s[%s] created)", chptr->chname, cptr->name, sptr->name, sptr->user->username, sptr->user->host, sptr->hostip); sendto_one(cptr, "RESYNCH %s", chptr->chname); } return 0; } memset((char *) &mode, '\0', sizeof(mode)); s = parv[3]; while (*s) { switch (*(s++)) { SJ_MODEADD('i', MODE_INVITEONLY); SJ_MODEADD('n', MODE_NOPRIVMSGS); SJ_MODEADD('p', MODE_PRIVATE); SJ_MODEADD('s', MODE_SECRET); SJ_MODEADD('m', MODE_MODERATED); SJ_MODEADD('t', MODE_TOPICLIMIT); SJ_MODEADD('r', MODE_REGISTERED); SJ_MODEADD('R', MODE_REGONLY); SJ_MODEADD('M', MODE_MODREG); SJ_MODEADD('c', MODE_NOCTRL); SJ_MODEADD('O', MODE_OPERONLY); #ifdef HAVE_SSL SJ_MODEADD('S', MODE_SSL); #endif #ifdef ENABLE_CHANNEL_MODE_D SJ_MODEADD('D', MODE_RSL); #endif SJ_MODEADD('N', MODE_NONICK); #ifdef USE_CHANMODE_L SJ_MODEADD('L', MODE_LISTED); #endif case 'k': strncpyzt(mode.key, parv[4 + args], KEYLEN + 1); args++; if (parc < 5 + args) return 0; break; case 'j': { char *tmpa, *tmpb; mode.mode |= MODE_JOINRATE; tmpa = parv[4 + args]; tmpb = strchr(tmpa, ':'); if(tmpb) { *tmpb = '\0'; tmpb++; mode.join_time = atoi(tmpb); } else mode.join_time = 0; mode.join_num = atoi(tmpa); args++; if (parc < 5 + args) return 0; } break; case 'l': mode.limit = atoi(parv[4 + args]); args++; if (parc < 5 + args) return 0; break; } } doesop = (parv[4 + args][0] == '@' || parv[4 + args][1] == '@'); oldmode = &chptr->mode; /* newts is the ts the remote server is providing */ /* oldts is our channel TS */ /* whichever TS is smaller wins. */ if (isnew) chptr->channelts = tstosend = newts; else if (newts == 0 || oldts == 0) chptr->channelts = tstosend = 0; else if (newts == oldts) tstosend = oldts; #ifdef OLD_WEIRD_CHANOP_NEGOTIATION else if (newts < oldts) { /* if remote ts is older, and they have ops, don't keep our modes. */ if (doesop) { kill_ban_list(sptr, chptr); keepourmodes = 0; } if (haveops && !doesop) tstosend = oldts; else chptr->channelts = tstosend = newts; } else /* if our TS is older, and we have ops, don't keep their modes */ { if (haveops) keepnewmodes = 0; if (doesop && !haveops) { chptr->channelts = tstosend = newts; if (MyConnect(sptr) && !IsULine(sptr)) ts_warn("Hacked ops on opless channel: %s", chptr->chname); } else tstosend = oldts; } #else else if (newts < oldts) { /* if remote ts is older, don't keep our modes. */ kill_ban_list(sptr, chptr); keepourmodes = 0; chptr->channelts = tstosend = newts; } else /* if our TS is older, don't keep their modes */ { keepnewmodes = 0; tstosend = oldts; } #endif if (!keepnewmodes) mode = *oldmode; else if (keepourmodes) { /* check overriding modes first */ if (oldmode->limit > mode.limit) mode.limit = oldmode->limit; if(*oldmode->key && *mode.key && strcmp(mode.key, oldmode->key) > 0) strcpy(mode.key, oldmode->key); else if(*oldmode->key && *mode.key == '\0') strcpy(mode.key, oldmode->key); if (oldmode->mode & MODE_JOINRATE) { if ((mode.mode & MODE_JOINRATE) && !mode.join_num) /* 0 wins */ ; else if (oldmode->join_num && mode.join_num > oldmode->join_num) /* more joins wins */ ; else if (mode.join_num == oldmode->join_num && mode.join_time < oldmode->join_time) /* same joins in less time wins */ ; else { /* our settings win */ mode.join_num = oldmode->join_num; mode.join_time = oldmode->join_time; } } /* now merge */ mode.mode |= oldmode->mode; } pbpos = 0; /* * since the most common case is that the modes are exactly the same, * this if will skip over the most common case... :) * * this would look prettier in a for loop, but it's unrolled here * so it's a bit faster. - lucas * * pass +: go through and add new modes that are in mode and not oldmode * pass -: go through and delete old modes that are in oldmode and not mode */ if(mode.mode != oldmode->mode) { SJ_MODEPLUS('p', MODE_PRIVATE); SJ_MODEPLUS('s', MODE_SECRET); SJ_MODEPLUS('m', MODE_MODERATED); SJ_MODEPLUS('n', MODE_NOPRIVMSGS); SJ_MODEPLUS('t', MODE_TOPICLIMIT); SJ_MODEPLUS('i', MODE_INVITEONLY); SJ_MODEPLUS('r', MODE_REGISTERED); SJ_MODEPLUS('R', MODE_REGONLY); SJ_MODEPLUS('M', MODE_MODREG); SJ_MODEPLUS('c', MODE_NOCTRL); SJ_MODEPLUS('O', MODE_OPERONLY); #ifdef HAVE_SSL SJ_MODEPLUS('S', MODE_SSL); #endif #ifdef ENABLE_CHANNEL_MODE_D SJ_MODEPLUS('D', MODE_RSL); #endif SJ_MODEPLUS('N', MODE_NONICK); #ifdef USE_CHANMODE_L SJ_MODEPLUS('L', MODE_LISTED); #endif SJ_MODEMINUS('p', MODE_PRIVATE); SJ_MODEMINUS('s', MODE_SECRET); SJ_MODEMINUS('m', MODE_MODERATED); SJ_MODEMINUS('n', MODE_NOPRIVMSGS); SJ_MODEMINUS('t', MODE_TOPICLIMIT); SJ_MODEMINUS('i', MODE_INVITEONLY); SJ_MODEMINUS('r', MODE_REGISTERED); SJ_MODEMINUS('R', MODE_REGONLY); SJ_MODEMINUS('M', MODE_MODREG); SJ_MODEMINUS('c', MODE_NOCTRL); SJ_MODEMINUS('O', MODE_OPERONLY); #ifdef HAVE_SSL SJ_MODEMINUS('S', MODE_SSL); #endif #ifdef ENABLE_CHANNEL_MODE_D SJ_MODEMINUS('D', MODE_RSL); #endif SJ_MODEMINUS('N', MODE_NONICK); #ifdef USE_CHANMODE_L SJ_MODEMINUS('L', MODE_LISTED); #endif } if ((oldmode->mode & MODE_JOINRATE) && !(mode.mode & MODE_JOINRATE)) { INSERTSIGN(-1,'-') *mbuf++ = 'j'; } if ((mode.mode & MODE_JOINRATE) && (!(oldmode->mode & MODE_JOINRATE) || (oldmode->join_num != mode.join_num || oldmode->join_time != mode.join_time))) { char tmp[128]; INSERTSIGN(1,'+') *mbuf++ = 'j'; if(mode.join_num == 0 || mode.join_time == 0) ircsprintf(tmp, "0"); else ircsprintf(tmp, "%d:%d", mode.join_num, mode.join_time); ADD_PARA(tmp) pargs++; } if (oldmode->limit && !mode.limit) { INSERTSIGN(-1,'-') *mbuf++ = 'l'; } if (mode.limit && oldmode->limit != mode.limit) { INSERTSIGN(1,'+') *mbuf++ = 'l'; sprintf(numeric, "%-15d", mode.limit); if ((s = strchr(numeric, ' '))) *s = '\0'; ADD_PARA(numeric); pargs++; } if (oldmode->key[0] && !mode.key[0]) { INSERTSIGN(-1,'-') *mbuf++ = 'k'; ADD_PARA(oldmode->key) pargs++; } if (mode.key[0] && strcmp(oldmode->key, mode.key)) { INSERTSIGN(1,'+') *mbuf++ = 'k'; ADD_PARA(mode.key) pargs++; } chptr->mode = mode; if (!keepourmodes) /* deop and devoice everyone! */ { what = 0; for (cm = chptr->members; cm; cm = cm->next) { if (cm->flags & MODE_CHANOP) { INSERTSIGN(-1,'-') *mbuf++ = 'o'; ADD_PARA(cm->cptr->name) pargs++; if (pargs >= MAXMODEPARAMS) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); mbuf = modebuf; *mbuf = '\0'; pargs = pbpos = what = 0; } cm->flags &= ~MODE_CHANOP; } if (cm->flags & MODE_HALFOP) { INSERTSIGN(-1,'-') *mbuf++ = 'h'; ADD_PARA(cm->cptr->name) pargs++; if (pargs >= MAXMODEPARAMS) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); mbuf = modebuf; *mbuf = '\0'; pargs = pbpos = what = 0; } cm->flags &= ~MODE_HALFOP; } if (cm->flags & MODE_VOICE) { INSERTSIGN(-1,'-') *mbuf++ = 'v'; ADD_PARA(cm->cptr->name) pargs++; if (pargs >= MAXMODEPARAMS) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); mbuf = modebuf; *mbuf = '\0'; pargs = pbpos = what = 0; } cm->flags &= ~MODE_VOICE; } } /* * We know if we get here, and we're in a sync, we haven't sent our topic * to sptr yet. (since topic burst is sent after sjoin burst finishes) */ if(chptr->topic[0]) { chptr->topic[0] = '\0'; sendto_channel_butserv_me(chptr, sptr, ":%s TOPIC %s :%s", sptr->name, chptr->chname, chptr->topic); } sendto_channel_butserv(chptr, &me, ":%s NOTICE %s :*** Notice -- TS for %s " "changed from %ld to %ld", me.name, chptr->chname, chptr->chname, oldts, newts); } if (mbuf != modebuf) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); } *modebuf = '\0'; parabuf[0] = '\0'; if (parv[3][0] != '0' && keepnewmodes) channel_modes(sptr, modebuf, parabuf, chptr); else { modebuf[0] = '0'; modebuf[1] = '\0'; } /* We do this down below now, so we can send out for two sjoin formats. * sprintf(t, ":%s SJOIN %ld %ld %s %s %s :", parv[0], tstosend, tstosend, * parv[2], modebuf, parabuf); * t += strlen(t); * the pointer "t" has been removed and is now replaced with an * index into sjbuf for faster appending */ strcpy(keep_modebuf, modebuf); strcpy(keep_parabuf, parabuf); sjbufpos = 0; mbuf = modebuf; pbpos = 0; pargs = 0; *mbuf++ = '+'; for (s = s0 = strtoken(&p, parv[args + 4], " "); s; s = s0 = strtoken(&p, (char *) NULL, " ")) { fl = 0; if (*s == '@' || s[1] == '@') fl |= MODE_CHANOP; if (*s == '%' || s[1] == '%') fl |= MODE_HALFOP; if (*s == '+' || s[1] == '+') fl |= MODE_VOICE; if (!keepnewmodes) { if (fl & MODE_CHANOP) fl = MODE_DEOPPED; else fl = 0; } while (*s == '@' || *s == '%' || *s == '+') s++; if (!(acptr = find_chasing(sptr, s, NULL))) continue; if (acptr->from != cptr) continue; people++; if (!IsMember(acptr, chptr)) { add_user_to_channel(chptr, acptr, fl); sendto_channel_butserv(chptr, acptr, ":%s JOIN :%s", s, parv[2]); } if (keepnewmodes) { ADD_SJBUF(s0) } else { ADD_SJBUF(s) } if (fl & MODE_CHANOP) { *mbuf++ = 'o'; ADD_PARA(s) pargs++; if (pargs >= MAXMODEPARAMS) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); mbuf = modebuf; *mbuf++ = '+'; pargs = pbpos = 0; } } if (fl & MODE_HALFOP) { *mbuf++ = 'h'; ADD_PARA(s) pargs++; if (pargs >= MAXMODEPARAMS) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); mbuf = modebuf; *mbuf++ = '+'; pargs = pbpos = 0; } } if (fl & MODE_VOICE) { *mbuf++ = 'v'; ADD_PARA(s) pargs++; if (pargs >= MAXMODEPARAMS) { *mbuf = '\0'; parabuf[pbpos] = '\0'; sjoin_sendit(cptr, sptr, chptr, parv[0]); mbuf = modebuf; *mbuf++ = '+'; pargs = pbpos = 0; } } } parabuf[pbpos] = '\0'; *mbuf = '\0'; if (pargs) sjoin_sendit(cptr, sptr, chptr, parv[0]); if (people) { sjbuf[sjbufpos] = '\0'; if(keep_parabuf[0] != '\0') sendto_serv_butone(cptr, SJOINFmt, parv[0], tstosend, parv[2], keep_modebuf, keep_parabuf, sjbuf); else sendto_serv_butone(cptr, SJOINFmtNP, parv[0], tstosend, parv[2], keep_modebuf, sjbuf); } else if(created && chptr->users == 0) sub1_from_channel(chptr); return 0; } #undef INSERTSIGN #undef ADD_PARA #undef ADD_SJBUF /* m_samode - Just bout the same as df * - Raistlin * parv[0] = sender * parv[1] = channel * parv[2] = modes */ int m_samode(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aChannel *chptr; if (!MyClient(sptr)) return 0; if (!IsAnOper(sptr) || !IsSAdmin(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if(parc < 3) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SAMODE"); return 0; } if((chptr = find_channel(parv[1], NullChn)) == NullChn) return 0; if(!check_channelname(sptr, (unsigned char *)parv[1])) return 0; set_mode(cptr, sptr, chptr, 2, parc - 2, parv + 2, modebuf, parabuf); if (strlen(modebuf) > (size_t)1) { sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", parv[0], chptr->chname, modebuf, parabuf); sendto_serv_butone(cptr, ":%s MODE %s 0 %s %s", parv[0], chptr->chname, modebuf, parabuf); if(MyClient(sptr)) { sendto_serv_butone(NULL, ":%s GLOBOPS :%s used SAMODE (%s %s%s%s)", me.name, sptr->name, chptr->chname, modebuf, (*parabuf!=0 ? " " : ""), parabuf); send_globops("from %s: %s used SAMODE (%s %s%s%s)", me.name, sptr->name, chptr->chname, modebuf, (*parabuf!=0 ? " " : ""), parabuf); } } return 0; } char *pretty_mask(char *mask) { char *cp, *user, *host; if ((user = strchr((cp = mask), '!'))) *user++ = '\0'; if ((host = strrchr(user ? user : cp, '@'))) { *host++ = '\0'; if (!user) return make_nick_user_host(NULL, cp, host); } else if (!user && strchr(cp, '.')) return make_nick_user_host(NULL, NULL, cp); return make_nick_user_host(cp, user, host); } u_long memcount_channel(MCchannel *mc) { aChannel *chptr; aBan *ban; #ifdef EXEMPT_LISTS aBanExempt *exempt; #endif #ifdef INVITE_LISTS anInvite *invite; #endif DLink *lp; Link *lp2; chanMember *cm; #ifdef FLUD struct fludbot *fb; #endif mc->file = __FILE__; for (chptr = channel; chptr; chptr = chptr->nextch) { mc->e_channels++; for (ban = chptr->banlist; ban; ban = ban->next) { mc->bans.c++; mc->bans.m += sizeof(*ban); mc->bans.m += strlen(ban->banstr) + 1; mc->bans.m += strlen(ban->who) + 1; } #ifdef EXEMPT_LISTS for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) { mc->exempts.c++; mc->exempts.m += sizeof(*exempt); mc->exempts.m += strlen(exempt->banstr) + 1; mc->exempts.m += strlen(exempt->who) + 1; } #endif #ifdef INVITE_LISTS for (invite = chptr->invite_list; invite; invite = invite->next) { mc->invites.c++; mc->invites.m += sizeof(*invite); mc->invites.m += strlen(invite->invstr) + 1; mc->invites.m += strlen(invite->who) + 1; } #endif for (cm = chptr->members; cm; cm = cm->next) mc->e_chanmembers++; #ifdef FLUD for (fb = chptr->fluders; fb; fb = fb->next) mc->e_fludbots++; #endif mc->e_inv_links += mc_links(chptr->invites); } for (lp = listing_clients; lp; lp = lp->next) { mc->lopts.c++; mc->lopts.m += sizeof(LOpts); mc->e_dlinks++; for (lp2 = lp->value.cptr->user->lopt->yeslist; lp2; lp2 = lp2->next) { mc->lopts.m += strlen(lp2->value.cp) + 1; mc->e_lopt_links++; } for (lp2 = lp->value.cptr->user->lopt->nolist; lp2; lp2 = lp2->next) { mc->lopts.m += strlen(lp2->value.cp) + 1; mc->e_lopt_links++; } } mc->total.c = mc->bans.c; mc->total.m = mc->bans.m; #ifdef EXEMPT_LISTS mc->total.c += mc->exempts.c; mc->total.m += mc->exempts.m; #endif #ifdef INVITE_LISTS mc->total.c += mc->invites.c; mc->total.m += mc->invites.m; #endif mc->total.c += mc->lopts.c; mc->total.m += mc->lopts.m; mc->s_scratch.c++; mc->s_scratch.m += sizeof(nickbuf); mc->s_scratch.c++; mc->s_scratch.m += sizeof(buf); mc->s_scratch.c++; mc->s_scratch.m += sizeof(modebuf); mc->s_scratch.c++; mc->s_scratch.m += sizeof(parabuf); return mc->total.m; }