/* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2002 Eggheads Development Team * Copyright (C) 2002 - 2008 Bryan Drewery * * 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 2 * of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * tclchan.c -- part of channels.mod * */ static int FindElement(char *resultBuf, const char *list, size_t listLength, const char **elementPtr, const char **nextPtr, size_t *sizePtr, int *bracePtr) { const char *p = list; const char *elemStart = NULL; /* Points to first byte of first element. */ const char *limit = (list + listLength); /* Points just after list's last byte. */ int openBraces = 0; /* Brace nesting level during parse. */ int inQuotes = 0; size_t size = 0; /* lint. */ const char *p2 = NULL; /* * Skim off leading white space and check for an opening brace or * quote. We treat embedded NULLs in the list as bytes belonging to * a list element. */ while ((p < limit) && egg_isspace(*p)) { /* INTL: ISO space. */ p++; } if (p == limit) { /* no element found */ elemStart = limit; goto done; } if (*p == '{') { openBraces = 1; p++; } else if (*p == '"') { inQuotes = 1; p++; } elemStart = p; if (bracePtr != 0) { *bracePtr = openBraces; } /* * Find element's end (a space, close brace, or the end of the string). */ while (p < limit) { switch (*p) { /* * Open brace: don't treat specially unless the element is in * braces. In this case, keep a nesting count. */ case '{': if (openBraces != 0) { openBraces++; } break; /* * Close brace: if element is in braces, keep nesting count and * quit when the last close brace is seen. */ case '}': if (openBraces > 1) { openBraces--; } else if (openBraces == 1) { size = (p - elemStart); p++; if ((p >= limit) || egg_isspace(*p)) { /* INTL: ISO space. */ goto done; } /* * Garbage after the closing brace; return an error. */ if (resultBuf) { p2 = p; while ((p2 < limit) && (!egg_isspace(*p2)) /* INTL: ISO space. */ && (p2 < p+20)) { p2++; } // snprintf(resultBuf, RESULT_LEN, "list element in braces followed by \"%.*s\" instead of space", (int) (p2-p), p); } return ERROR; } break; /* * Backslash: skip over everything up to the end of the * backslash sequence. */ /* case '\\': { Tcl_UtfBackslash(p, &numChars, NULL); p += (numChars - 1); break; } */ /* * Space: ignore if element is in braces or quotes; otherwise * terminate element. */ case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': if ((openBraces == 0) && !inQuotes) { size = (p - elemStart); goto done; } break; /* * Double-quote: if element is in quotes then terminate it. */ case '"': if (inQuotes) { size = (p - elemStart); p++; if ((p >= limit) || egg_isspace(*p)) { /* INTL: ISO space */ goto done; } /* * Garbage after the closing quote; return an error. */ if (resultBuf) { p2 = p; while ((p2 < limit) && (!egg_isspace(*p2)) /* INTL: ISO space */ && (p2 < p+20)) { p2++; } // sprintf(resultBuf, "list element in quotes followed by \"%.*s\" %s", (int) (p2-p), p, "instead of space"); } return ERROR; } break; } p++; } /* * End of list: terminate element. */ if (p == limit) { if (openBraces != 0) { if (resultBuf) { simple_snprintf(resultBuf, RESULT_LEN, "unmatched open brace in list"); } return ERROR; } else if (inQuotes) { if (resultBuf) { simple_snprintf(resultBuf, RESULT_LEN, "unmatched open quote in list"); } return ERROR; } size = (p - elemStart); } done: while ((p < limit) && (egg_isspace(*p))) { /* INTL: ISO space. */ p++; } *elementPtr = elemStart; *nextPtr = p; if (sizePtr != 0) { *sizePtr = size; } return OK; } /* unneeded? int CopyAndCollapse(int count, const char *src, char *dst) { register char c; int numRead; int newCount = 0; int backslashCount; for (c = *src; count > 0; src++, c = *src, count--) { if (c == '\\') { backslashCount = Tcl_UtfBackslash(src, &numRead, dst); dst += backslashCount; newCount += backslashCount; src += numRead-1; count -= numRead-1; } else { *dst = c; dst++; newCount++; } } *dst = 0; return newCount; } */ int SplitList(char *resultBuf, const char *list, int *argcPtr, const char ***argvPtr) { const char **argv = NULL; const char *l = NULL; const char *element = NULL; char *p = NULL; int result, brace = 0; size_t size = 2; /* initialized to 1 for NULL pointer */ size_t length, elSize, i = 0; /* * Figure out how much space to allocate. There must be enough * space for both the array of pointers and also for a copy of * the list. To estimate the number of pointers needed, count * the number of space characters in the list. */ for (l = list; *l != 0; l++) { if (egg_isspace(*l)) { /* INTL: ISO space. */ size++; } } argv = (const char **) my_calloc(1, (unsigned) ((size * sizeof(char *)) + (l - list) + 1 + 15)); /* 15 cuz the tcl src is hard to follow */ length = strlen(list); for (p = ((char *) argv) + size*sizeof(char *); *list != 0; i++) { const char *prevList = list; result = FindElement(resultBuf, list, length, &element, &list, &elSize, &brace); length -= (list - prevList); if (result != OK) { free((char *) argv); return result; } if (*element == 0) { break; } if (i >= size) { free((char *) argv); if (resultBuf) strlcpy(resultBuf, "internal error in SplitList", RESULT_LEN); return ERROR; } argv[i] = p; if (brace) { memcpy(p, element, elSize); p += elSize; *p = 0; p++; } else { /* CopyAndCollapse(elSize, element, p); */ memcpy(p, element, elSize); p += elSize + 1; } } argv[i] = NULL; *argvPtr = argv; *argcPtr = i; return OK; } /* Parse options for a channel. */ int channel_modify(char *result, struct chanset_t *chan, int items, char **item, bool cmd) { bool error = 0; int old_status = chan->status, old_mode_mns_prot = chan->mode_mns_prot, old_mode_pls_prot = chan->mode_pls_prot; char s[121] = "", result_extra[RESULT_LEN / 2] = ""; if (result) result[0] = 0; for (register int i = 0; i < items; i++) { /* Chanchar template } else if (!strcmp(item[i], "temp")) { i++; if (i >= items) { if (result) strlcpy(result, "channel temp needs argument", RESULT_LEN); return ERROR; } strlcpy(chan->temp, item[i], sizeof(chan->temp)); check_temp(chan); */ if (!strcmp(item[i], "chanmode")) { i++; if (i >= items) { if (result) strlcpy(result, "channel chanmode needs argument", RESULT_LEN); return ERROR; } strlcpy(s, item[i], sizeof(s)); set_mode_protect(chan, s); } else if (!strcmp(item[i], "topic")) { char *p = NULL; i++; if (i >= items) { if (result) strlcpy(result, "topic needs argument", RESULT_LEN); return ERROR; } p = replace(item[i], "{", "["); p = replace(p, "}", "]"); strlcpy(chan->topic, p, sizeof(chan->topic)); } else if (!cmd && !strcmp(item[i], "addedby")) { i++; if (i >= items) { if (result) strlcpy(result, "addedby needs argument", RESULT_LEN); return ERROR; } strlcpy(chan->added_by, item[i], sizeof(chan->added_by)); } else if (!cmd && !strcmp(item[i], "addedts")) { i++; if (i >= items) { if (result) strlcpy(result, "addedts needs argument", RESULT_LEN); return ERROR; } chan->added_ts = atoi(item[i]); } else if (!strcmp(item[i], "limit")) { i++; if (i >= items) { if (result) strlcpy(result, "channel limit needs argument", RESULT_LEN); return ERROR; } chan->limitraise = atoi(item[i]); chan->limit_prot = 0; /* } else if (!strcmp(item[i], "revenge-mode")) { i++; if (i >= items) { if (result) strlcpy(result, "channel revenge-mode needs argument", RESULT_LEN); return ERROR; } chan->revenge_mode = atoi(item[i]); */ } else if (!strcmp(item[i], "ban-time")) { i++; if (i >= items) { if (result) strlcpy(result, "channel ban-time needs argument", RESULT_LEN); return ERROR; } chan->ban_time = atoi(item[i]); } else if (!strcmp(item[i], "exempt-time")) { i++; if (i >= items) { if (result) strlcpy(result, "channel exempt-time needs argument", RESULT_LEN); return ERROR; } chan->exempt_time = atoi(item[i]); } else if (!strcmp(item[i], "invite-time")) { i++; if (i >= items) { if (result) strlcpy(result, "channel invite-time needs argument", RESULT_LEN); return ERROR; } chan->invite_time = atoi(item[i]); } else if (!strcmp(item[i], "closed-ban")) { i++; if (i >= items) { if (result) strlcpy(result, "channel closed-ban needs argument", RESULT_LEN); return ERROR; } chan->closed_ban = atoi(item[i]); } else if (!strcmp(item[i], "closed-invite")) { i++; if (i >= items) { if (result) strlcpy(result, "channel closed-invite needs argument", RESULT_LEN); return ERROR; } chan->closed_invite = atoi(item[i]); if (chan->mode_mns_prot & CHANINV && chan->closed_invite) chan->closed_invite = 0; } else if (!strcmp(item[i], "closed-private")) { i++; if (i >= items) { if (result) strlcpy(result, "channel closed-private needs argument", RESULT_LEN); return ERROR; } chan->closed_private = atoi(item[i]); if (chan->mode_mns_prot & CHANPRIV && chan->closed_private) chan->closed_private = 0; } else if (!strcmp(item[i], "voice-non-ident")) { i++; if (i >= items) { if (result) strlcpy(result, "channel voice-ident needs argument", RESULT_LEN); return ERROR; } chan->voice_non_ident = atoi(item[i]); } else if (!strcmp(item[i], "bad-cookie")) { i++; if (i >= items) { if (result) strlcpy(result, "channel bad-cookie needs argument", RESULT_LEN); return ERROR; } chan->bad_cookie = deflag_translate(item[i]); } else if (!strcmp(item[i], "manop")) { i++; if (i >= items) { if (result) strlcpy(result, "channel manop needs argument", RESULT_LEN); return ERROR; } chan->manop = deflag_translate(item[i]); } else if (!strcmp(item[i], "mdop")) { i++; if (i >= items) { if (result) strlcpy(result, "channel mdop needs argument", RESULT_LEN); return ERROR; } chan->mdop = deflag_translate(item[i]); } else if (!strcmp(item[i], "mop")) { i++; if (i >= items) { if (result) strlcpy(result, "channel mop needs argument", RESULT_LEN); return ERROR; } chan->mop = deflag_translate(item[i]); } else if (!strcmp(item[i], "knock")) { i++; if (i >= items) { if (result) strlcpy(result, "channel knock needs argument", RESULT_LEN); return ERROR; } if (!str_isdigit(item[i])) { if (!strcasecmp("Op", item[i])) chan->knock_flags = CHAN_FLAG_OP; else if (!strcasecmp("Voice", item[i])) chan->knock_flags = CHAN_FLAG_VOICE; else if (!strcasecmp("User", item[i])) chan->knock_flags = CHAN_FLAG_USER; else if (!strcasecmp("None", item[i])) chan->knock_flags = 0; else { if (result) strlcpy(result, "channel knock only accepts Op|Voice|User|None", RESULT_LEN); return ERROR; } } else chan->knock_flags = atoi(item[i]); } else if (!strcmp(item[i], "flood-exempt")) { i++; if (i >= items) { if (result) strlcpy(result, "channel flood-exempt needs argument", RESULT_LEN); return ERROR; } if (!str_isdigit(item[i])) { if (!strcasecmp("Op", item[i])) chan->flood_exempt_mode = CHAN_FLAG_OP; else if (!strcasecmp("Voice", item[i])) chan->flood_exempt_mode = CHAN_FLAG_VOICE; else if (!strcasecmp("None", item[i])) chan->flood_exempt_mode = 0; else { if (result) strlcpy(result, "channel flood-exempt only accepts Op|Voice|None", RESULT_LEN); return ERROR; } } else chan->flood_exempt_mode = atoi(item[i]); } else if (!strcmp(item[i], "flood-lock-time")) { i++; if (i >= items) { if (result) strlcpy(result, "channel flood-lock-time needs argument", RESULT_LEN); return ERROR; } chan->flood_lock_time = atoi(item[i]); } else if (!strcmp(item[i], "auto-delay")) { i++; if (i >= items) { if (result) strlcpy(result, "channel auto-delay needs argument", RESULT_LEN); return ERROR; } chan->auto_delay = atoi(item[i]); } else if (!strcmp(item[i], "ban-type")) { i++; if (i >= items) { if (result) strlcpy(result, "channel ban-type needs argument", RESULT_LEN); return ERROR; } chan->ban_type = atoi(item[i]); /* Chanint template * } else if (!strcmp(item[i], "temp")) { * i++; * if (i >= items) { * if (result) * strlcpy(result, "channel temp needs argument", RESULT_LEN); * return ERROR; * } * chan->temp = atoi(item[i]); */ } else if (!strcmp(item[i], "+enforcebans")) chan->status |= CHAN_ENFORCEBANS; else if (!strcmp(item[i], "-enforcebans")) chan->status &= ~CHAN_ENFORCEBANS; else if (!strcmp(item[i], "+dynamicbans")) chan->status |= CHAN_DYNAMICBANS; else if (!strcmp(item[i], "-dynamicbans")) chan->status &= ~CHAN_DYNAMICBANS; else if (!strcmp(item[i], "-userbans")) chan->status |= CHAN_NOUSERBANS; else if (!strcmp(item[i], "+userbans")) chan->status &= ~CHAN_NOUSERBANS; else if (!strcmp(item[i], "+bitch")) chan->status |= CHAN_BITCH; else if (!strcmp(item[i], "-bitch")) chan->status &= ~CHAN_BITCH; else if (!strcmp(item[i], "+nodesynch")) chan->status |= CHAN_NODESYNCH; else if (!strcmp(item[i], "-nodesynch")) chan->status &= ~CHAN_NODESYNCH; else if (!strcmp(item[i], "+inactive")) chan->status |= CHAN_INACTIVE; else if (!strcmp(item[i], "-inactive")) chan->status&= ~CHAN_INACTIVE; else if (!strcmp(item[i], "+secret")) chan->status |= CHAN_SECRET; else if (!strcmp(item[i], "-secret")) chan->status &= ~CHAN_SECRET; else if (!strcmp(item[i], "+cycle")) chan->status |= CHAN_CYCLE; else if (!strcmp(item[i], "-cycle")) chan->status &= ~CHAN_CYCLE; else if (!strcmp(item[i], "+dynamicexempts")) chan->status |= CHAN_DYNAMICEXEMPTS; else if (!strcmp(item[i], "-dynamicexempts")) chan->status &= ~CHAN_DYNAMICEXEMPTS; else if (!strcmp(item[i], "-userexempts")) chan->status |= CHAN_NOUSEREXEMPTS; else if (!strcmp(item[i], "+userexempts")) chan->status &= ~CHAN_NOUSEREXEMPTS; else if (!strcmp(item[i], "+dynamicinvites")) chan->status |= CHAN_DYNAMICINVITES; else if (!strcmp(item[i], "-dynamicinvites")) chan->status &= ~CHAN_DYNAMICINVITES; else if (!strcmp(item[i], "-userinvites")) chan->status |= CHAN_NOUSERINVITES; else if (!strcmp(item[i], "+userinvites")) chan->status &= ~CHAN_NOUSERINVITES; else if (!strcmp(item[i], "+closed")) chan->status |= CHAN_CLOSED; else if (!strcmp(item[i], "-closed")) chan->status &= ~CHAN_CLOSED; else if (HAVE_TAKE && !strcmp(item[i], "+take")) chan->status |= CHAN_TAKE; else if (HAVE_TAKE && !strcmp(item[i], "-take")) chan->status &= ~CHAN_TAKE; else if (!HAVE_TAKE && !strcmp(item[i], "+take")) chan->status |= CHAN_BITCH; else if (!HAVE_TAKE && !strcmp(item[i], "-take")) chan->status &= ~CHAN_BITCH; else if (!strcmp(item[i], "+voice")) chan->status |= CHAN_VOICE; else if (!strcmp(item[i], "-voice")) chan->status &= ~CHAN_VOICE; else if (!strcmp(item[i], "+autoop")) chan->status |= CHAN_AUTOOP; else if (!strcmp(item[i], "-autoop")) chan->status &= ~CHAN_AUTOOP; else if (!strcmp(item[i], "+botbitch")) chan->status |= CHAN_BOTBITCH; else if (!strcmp(item[i], "-botbitch")) chan->status &= ~CHAN_BOTBITCH; else if (!strcmp(item[i], "+nomassjoin")) chan->status |= CHAN_NOMASSJOIN; else if (!strcmp(item[i], "-nomassjoin")) chan->status &= ~CHAN_NOMASSJOIN; else if (!strcmp(item[i], "+meankicks")) chan->status |= CHAN_MEANKICKS; else if (!strcmp(item[i], "-meankicks")) chan->status &= ~CHAN_MEANKICKS; else if (!strcmp(item[i], "+rbl")) chan->status |= CHAN_RBL; else if (!strcmp(item[i], "-rbl")) chan->status &= ~CHAN_RBL; else if (!strcmp(item[i], "+voicebitch")) chan->status |= CHAN_VOICEBITCH; else if (!strcmp(item[i], "-voicebitch")) chan->status &= ~CHAN_VOICEBITCH; /* Chanflag template * else if (!strcmp(item[i], "+temp")) * chan->status |= CHAN_TEMP; * else if (!strcmp(item[i], "-temp")) * chan->status &= ~CHAN_TEMP; */ else if (!strcmp(item[i], "+fastop")) chan->status |= CHAN_FASTOP; else if (!strcmp(item[i], "-fastop")) chan->status &= ~CHAN_FASTOP; else if (!strcmp(item[i], "+private")) chan->status |= CHAN_PRIVATE; else if (!strcmp(item[i], "-private")) chan->status &= ~CHAN_PRIVATE; else if (!strcmp(item[i], "+backup")) chan->status |= CHAN_BACKUP; else if (!strcmp(item[i], "-backup")) chan->status &= ~CHAN_BACKUP; /* ignore wasoptest, stopnethack and clearbans in chanfile, remove this later */ else if (!cmd && !HAVE_TAKE && !strcmp(item[i], "+take")) ; else if (!cmd && !HAVE_TAKE && !strcmp(item[i], "-take")) ; else if (!cmd && !strcmp(item[i], "stopnethack-mode")) ; else if (!cmd && !strcmp(item[i], "revenge-mode")) ; else if (!cmd && !strcmp(item[i], "+revenge")) ; else if (!cmd && !strcmp(item[i], "-revenge")) ; else if (!cmd && !strcmp(item[i], "+revengebot")) ; else if (!cmd && !strcmp(item[i], "-revengebot")) ; else if (!cmd && !strcmp(item[i], "+manop")) ; else if (!cmd && !strcmp(item[i], "-manop")) ; else if (!cmd && !strcmp(item[i], "+dontkickops")) ; else if (!cmd && !strcmp(item[i], "-dontkickops")) ; else if (!cmd && !strcmp(item[i], "+nomdop")) ; else if (!cmd && !strcmp(item[i], "-nomdop")) ; else if (!cmd && !strcmp(item[i], "+protectfriends")) ; else if (!cmd && !strcmp(item[i], "-protectfriends")) ; else if (!cmd && !strcmp(item[i], "+protectops")) ; else if (!cmd && !strcmp(item[i], "-protectops")) ; else if (!cmd && !strcmp(item[i], "+punish")) ; else if (!cmd && !strcmp(item[i], "-punish")) ; else if (!cmd && !strcmp(item[i], "+seen")) ; else if (!cmd && !strcmp(item[i], "-seen")) ; else if (!cmd && !strcmp(item[i], "+secret")) ; else if (!cmd && !strcmp(item[i], "-secret")) ; else if (!cmd && !strcmp(item[i], "-stopnethack")) ; else if (!cmd && !strcmp(item[i], "+stopnethack")) ; else if (!cmd && !strcmp(item[i], "-wasoptest")) ; else if (!cmd && !strcmp(item[i], "+wasoptest")) ; /* Eule 01.2000 */ else if (!cmd && !strcmp(item[i], "+clearbans")) ; else if (!cmd && !strcmp(item[i], "-clearbans")) ; else if (!cmd && !strncmp(item[i], "need-", 5)) ; else if (!strncmp(item[i], "flood-", 6)) { int *pthr = NULL; interval_t *ptime = NULL; char *p = NULL; if (!strcmp(item[i] + 6, "*")) { i++; if (i >= items) { if (result) simple_snprintf(result, RESULT_LEN, "%s needs argument", item[i - 1]); return ERROR; } p = strchr(item[i], ':'); int thr = 0; interval_t time = 0; if (p) { *p++ = 0; thr = atoi(item[i]); time = atoi(p); *--p = ':'; } else { thr = atoi(item[i]); time = 1; } chan->flood_pub_thr = thr; chan->flood_pub_time = time; chan->flood_join_thr = thr; chan->flood_join_time = time; chan->flood_ctcp_thr = thr; chan->flood_ctcp_time = time; chan->flood_kick_thr = thr; chan->flood_kick_time = time; chan->flood_deop_thr = thr; chan->flood_deop_time = time; chan->flood_nick_thr = thr; chan->flood_nick_time = time; chan->flood_mjoin_thr = thr; chan->flood_mjoin_time = time; } else if (!strcmp(item[i] + 6, "chan")) { pthr = &chan->flood_pub_thr; ptime = &chan->flood_pub_time; } else if (!strcmp(item[i] + 6, "join")) { pthr = &chan->flood_join_thr; ptime = &chan->flood_join_time; } else if (!strcmp(item[i] + 6, "ctcp")) { pthr = &chan->flood_ctcp_thr; ptime = &chan->flood_ctcp_time; } else if (!strcmp(item[i] + 6, "kick")) { pthr = &chan->flood_kick_thr; ptime = &chan->flood_kick_time; } else if (!strcmp(item[i] + 6, "deop")) { pthr = &chan->flood_deop_thr; ptime = &chan->flood_deop_time; } else if (!strcmp(item[i] + 6, "nick")) { pthr = &chan->flood_nick_thr; ptime = &chan->flood_nick_time; } else if (!strcmp(item[i] + 6, "mjoin")) { pthr = &chan->flood_mjoin_thr; ptime = &chan->flood_mjoin_time; } else { /* Ignore for optimal forward compatibility */ i++; continue; } if (pthr && ptime) { //Ignore flood-* i++; if (i >= items) { if (result) simple_snprintf(result, RESULT_LEN, "%s needs argument", item[i - 1]); return ERROR; } p = strchr(item[i], ':'); if (p) { *p++ = 0; *pthr = atoi(item[i]); *ptime = atoi(p); *--p = ':'; } else { *pthr = atoi(item[i]); *ptime = 1; } } } else { if (result && item[i][0]) { /* ignore "" */ if (!result[0]) strlcpy(result, "illegal channel option: ", RESULT_LEN); if (result_extra[0]) strlcat(result_extra, " ", sizeof(result_extra)); strlcat(result_extra, item[i], sizeof(result_extra)); } error = 1; } } if ((chan->status ^ old_status) & CHAN_TAKE) chan->status |= (CHAN_FASTOP|CHAN_BITCH); // to avoid bots still mass opping from +take from not using cookies if (!conf.bot->hub && (chan != chanset_default)) { if ((old_status ^ chan->status) & (CHAN_INACTIVE | CHAN_BACKUP)) { if (!shouldjoin(chan) && (chan->ircnet_status & (CHAN_ACTIVE | CHAN_PEND))) dprintf(DP_SERVER, "PART %s\n", chan->name[0] ? chan->name : chan->dname); if (shouldjoin(chan) && !(chan->ircnet_status & (CHAN_ACTIVE | CHAN_PEND | CHAN_JOINING))) { dprintf(DP_SERVER, "JOIN %s %s\n", (chan->name[0]) ? chan->name : chan->dname, chan->channel.key[0] ? chan->channel.key : chan->key_prot); chan->ircnet_status |= CHAN_JOINING; } } if (me_op(chan)) { if ((old_status ^ chan->status) & (CHAN_ENFORCEBANS|CHAN_NOUSERBANS|CHAN_DYNAMICBANS|CHAN_NOUSEREXEMPTS|CHAN_NOUSERINVITES|CHAN_DYNAMICEXEMPTS|CHAN_DYNAMICINVITES)) { recheck_channel(chan, 1); /* Does a change in channel flag affect client status in the channel? */ } else if ((chan->status ^ old_status) & (CHAN_BITCH | CHAN_BOTBITCH | CHAN_PRIVATE | CHAN_CLOSED | CHAN_VOICE | CHAN_VOICEBITCH )) { recheck_channel(chan, 0); } else if ((old_status & CHAN_TAKE) && !(chan->status & CHAN_TAKE)) { //Set -take, fetch bans/exempts/invites recheck_channel(chan, 2); } else if (old_mode_pls_prot != chan->mode_pls_prot || old_mode_mns_prot != chan->mode_mns_prot) { recheck_channel_modes(chan); } } } if (result && result[0] && result_extra[0]) strlcat(result, result_extra, RESULT_LEN); if (error) return ERROR; return OK; } static void init_masklist(masklist *m) { m->mask = (char *) my_calloc(1, 1); m->who = NULL; m->next = NULL; } /* Initialize out the channel record. */ static void init_channel(struct chanset_t *chan, bool reset) { chan->channel.maxmembers = 0; chan->channel.mode = 0; chan->channel.members = 0; if (!reset) chan->channel.key = (char *) my_calloc(1, 1); chan->channel.ban = (masklist *) my_calloc(1, sizeof(masklist)); init_masklist(chan->channel.ban); chan->channel.exempt = (masklist *) my_calloc(1, sizeof(masklist)); init_masklist(chan->channel.exempt); chan->channel.invite = (masklist *) my_calloc(1, sizeof(masklist)); init_masklist(chan->channel.invite); chan->channel.member = (memberlist *) my_calloc(1, sizeof(memberlist)); chan->channel.member->nick[0] = 0; chan->channel.member->next = NULL; chan->channel.topic = NULL; } static void clear_masklist(masklist *m) { masklist *temp = NULL; for (; m; m = temp) { temp = m->next; if (m->mask) free(m->mask); if (m->who) free(m->who); free(m); } } /* Clear out channel data from memory. */ void clear_channel(struct chanset_t *chan, bool reset) { memberlist *m = NULL, *m1 = NULL; if (chan->channel.topic) free(chan->channel.topic); for (m = chan->channel.member; m; m = m1) { m1 = m->next; free(m); } clear_masklist(chan->channel.ban); chan->channel.ban = NULL; clear_masklist(chan->channel.exempt); chan->channel.exempt = NULL; clear_masklist(chan->channel.invite); chan->channel.invite = NULL; chan->channel.last_eI = 0; chan->ircnet_status &= ~CHAN_HAVEBANS; if (reset) init_channel(chan, 1); } /* Create new channel and parse commands. */ int channel_add(char *result, char *newname, char *options, bool isdefault) { /* When loading userfile */ if (newname && newname[0] && loading && !strcmp(newname, "default")) isdefault = 1; if (!newname || !newname[0] || (!isdefault && !strchr(CHANMETA, newname[0]))) { if (result) strlcpy(result, "invalid channel prefix", RESULT_LEN); return ERROR; } if (strchr(newname, ',') != NULL) { if (result) strlcpy(result, "invalid channel name", RESULT_LEN); return ERROR; } const char **item = NULL; int items = 0; char buf[3001] = ""; simple_snprintf(buf, sizeof(buf), "chanmode { %s }", glob_chanmode); if (!isdefault) { // These are passed in in 'options' strlcat(buf, " ", sizeof(buf)); strlcat(buf, def_chanset, sizeof(buf)); } // Add in 'default' channel options if (!isdefault && chanset_default) { bd::String default_chan_options = channel_to_string(chanset_default); strlcat(buf, " ", sizeof(buf)); strlcat(buf, default_chan_options.c_str(), sizeof(buf)); } if (options && options[0]) { strlcat(buf, " ", sizeof(buf)); strlcat(buf, options, sizeof(buf)); } if (SplitList(result, buf, &items, &item) != OK) return ERROR; bool join = 0; struct chanset_t *chan = NULL; int ret = OK; if ((chan = findchan_by_dname(newname))) { /* Already existing channel, maybe a reload of the channel file */ chan->status &= ~CHAN_FLAGGED; /* don't delete me! :) */ } else { chan = (struct chanset_t *) my_calloc(1, sizeof(struct chanset_t)); /* These are defaults, bzero already set them 0, but we set them for future reference */ chan->limit_prot = 0; chan->limit = 0; chan->closed_ban = 0; chan->closed_private = 1; chan->closed_invite = 1; chan->voice_non_ident = 1; chan->auto_delay = 5; chan->ban_type = 3; /* Chanint template * chan->temp = 0; */ chan->knock_flags = 0; chan->flood_lock_time = 120; chan->flood_exempt_mode = 0; chan->flood_pub_thr = gfld_chan_thr; chan->flood_pub_time = gfld_chan_time; chan->flood_ctcp_thr = gfld_ctcp_thr; chan->flood_ctcp_time = gfld_ctcp_time; chan->flood_join_thr = gfld_join_thr; chan->flood_join_time = gfld_join_time; chan->flood_deop_thr = gfld_deop_thr; chan->flood_deop_time = gfld_deop_time; chan->flood_kick_thr = gfld_kick_thr; chan->flood_kick_time = gfld_kick_time; chan->flood_nick_thr = gfld_nick_thr; chan->flood_nick_time = gfld_nick_time; chan->flood_mjoin_thr = 6; chan->flood_mjoin_time = 1; // chan->revenge_mode = global_revenge_mode; chan->limitraise = 20; chan->ban_time = global_ban_time; chan->exempt_time = global_exempt_time; chan->invite_time = global_invite_time; chan->channel.jointime = 0; chan->channel.parttime = 0; chan->channel.fighting = 0; chan->channel.drone_set_mode = 0; chan->channel.drone_jointime = 0; chan->channel.drone_joins = 0; chan->channel.last_eI = 0; /* We _only_ put the dname (display name) in here so as not to confuse * any code later on. chan->name gets updated with the channel name as * the server knows it, when we join the channel. */ strlcpy(chan->dname, newname, 81); /* Initialize chan->channel info */ if (isdefault) { if (chanset_default) remove_channel(chanset_default); chanset_default = chan; } else { init_channel(chan, 0); list_append((struct list_type **) &chanset, (struct list_type *) chan); /* Channel name is stored in xtra field for sharebot stuff */ if (!conf.bot->hub && !isdefault) join = 1; } } /* If loading is set, we're loading the userfile. Ignore errors while * reading userfile and just return OK. This is for compatability * if a user goes back to an eggdrop that no-longer supports certain * (channel) options. */ if ((channel_modify(result, chan, items, (char **) item, 0) != OK)) { putlog(LOG_ERROR, "*", "Error parsing channel options for %s: %s", chan->dname, result); if (!loading) ret = ERROR; } free(item); if (join && shouldjoin(chan)) dprintf(DP_SERVER, "JOIN %s %s\n", chan->dname, chan->key_prot); return ret; }