int csc_dorecover(void *source, int cargc, char **cargv) { nick *sender=source,*np; reguser *rup; regchanuser *rcup; regchan *rcp; chanindex *cip; unsigned long *lp; int i; modechanges changes; if (cargc<1) { chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "recover"); return CMD_ERROR; } if (!(cip=cs_checkaccess(sender, cargv[0], CA_MASTERPRIV, NULL, "recover",0, 0))) return CMD_ERROR; rcp=cip->exts[chanservext]; if (cip->channel) { localsetmodeinit(&changes, cip->channel, chanservnick); /* clearchan */ localdosetmode_key(&changes, NULL, MCB_DEL); localdosetmode_simple(&changes, 0, cip->channel->flags); cs_docheckchanmodes(cip->channel, &changes); /* unbanall */ while (cip->channel->bans) { localdosetmode_ban(&changes, bantostring(cip->channel->bans), MCB_DEL); } /* remove the registered bans that match on me */ cs_unbanfn(sender, cip, nickmatchban_peerthroughhidehost, sender, 1, 0); /* deopall */ for (i=0,lp=cip->channel->users->content; i<cip->channel->users->hashsize;i++,lp++) { if (*lp!=nouser && (*lp & CUMODE_OP)) { if (!(np=getnickbynumeric(*lp)) || (!IsService(np) && (!(rup=getreguserfromnick(np)) || !(rcup=findreguseronchannel(rcp, rup)) || !(CUHasOpPriv(rcup)) || !(CUIsProtect(rcup) || CIsProtect(rcp))))) { localdosetmode_nick(&changes, np, MC_DEOP); } } } localsetmodeflush(&changes, 1); } cs_log(sender,"RECOVER %s",cip->name->content); chanservstdmessage(sender, QM_DONE); return CMD_OK; }
void csdb_doaccounthistory_real(DBConn *dbconn, void *arg) { nick *np=getnickbynumeric((unsigned long)arg); reguser *rup; char *oldpass, *newpass, *oldemail, *newemail; time_t changetime; DBResult *pgres; int count=0; char tbuf[TIMELEN]; if(!dbconn) return; pgres=dbgetresult(dbconn); if (!dbquerysuccessful(pgres)) { Error("chanserv", ERR_ERROR, "Error loading account history data."); return; } if (dbnumfields(pgres) != 7) { Error("chanserv", ERR_ERROR, "Account history data format error."); dbclear(pgres); return; } if (!np) { dbclear(pgres); return; } if (!(rup=getreguserfromnick(np)) || !UHasOperPriv(rup)) { Error("chanserv", ERR_ERROR, "No reguser pointer or oper privs in account history."); dbclear(pgres); return; } /* @TIMELEN */ chanservsendmessage(np, "Number: Time: Old password: New password: Old email: New email:"); while(dbfetchrow(pgres)) { /*userID=strtoul(dbgetvalue(pgres, 0), NULL, 10);*/ changetime=strtoul(dbgetvalue(pgres, 1), NULL, 10); /*authtime=strtoul(dbgetvalue(pgres, 2), NULL, 10);*/ oldpass=dbgetvalue(pgres, 3); newpass=dbgetvalue(pgres, 4); oldemail=dbgetvalue(pgres, 5); newemail=dbgetvalue(pgres, 6); q9strftime(tbuf, sizeof(tbuf), changetime); chanservsendmessage(np, "#%-6d %-19s %-14s %-14s %-30s %s", ++count, tbuf, oldpass, newpass, oldemail, newemail); /* @TIMELEN */ } chanservstdmessage(np, QM_ENDOFLIST); dbclear(pgres); }
void notice_free(searchCtx *ctx, struct searchNode *thenode) { struct notice_localdata *localdata; nick *np, *nnp; chanindex *cip, *ncip; int i, j; unsigned int nickmarker; localdata = thenode->localdata; if (ctx->searchcmd == reg_chansearch) { nickmarker=nextnickmarker(); for (i=0;i<CHANNELHASHSIZE;i++) { for (cip=chantable[i];cip;cip=ncip) { ncip = cip->next; if (cip != NULL && cip->channel != NULL && cip->marker == localdata->marker) { for (j=0;j<cip->channel->users->hashsize;j++) { if (cip->channel->users->content[j]==nouser) continue; if ((np=getnickbynumeric(cip->channel->users->content[j]))) np->marker=nickmarker; } } } } for (i=0;i<NICKHASHSIZE;i++) { for(np=nicktable[i];np;np=nnp) { nnp = np->next; if (np->marker == nickmarker) controlnotice(np, "%s", localdata->message); } } } else { for (i=0;i<NICKHASHSIZE;i++) { for (np=nicktable[i];np;np=nnp) { nnp = np->next; if (np->marker == localdata->marker) controlnotice(np, "%s", localdata->message); } } } /* notify opers of the action */ ctx->wall(NL_BROADCASTS, "%s/%s sent the following message to %d %s: %s", senderNSExtern->nick, senderNSExtern->authname, localdata->count, localdata->count != 1 ? "users" : "user", localdata->message); free(localdata); free(thenode); }
void *authedpct_exe(searchCtx *ctx, struct searchNode *thenode, void *theinput) { int i; int j=0; nick *np; chanindex *cip = (chanindex *)theinput; if (!cip->channel) return (void *)0; for (i=0;i<cip->channel->users->hashsize;i++) { if (cip->channel->users->content[i]==nouser) continue; if ((np=getnickbynumeric(cip->channel->users->content[i])) && IsAccount(np)) j++; } return (void *)(long)((j * 100) / cip->channel->users->totalusers); }
int csc_dodeopall(void *source, int cargc, char **cargv) { nick *sender=source,*np; reguser *rup; regchanuser *rcup; regchan *rcp; chanindex *cip; unsigned long *lp; int i; modechanges changes; if (cargc<1) { chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "deopall"); return CMD_ERROR; } if (!(cip=cs_checkaccess(sender, cargv[0], CA_MASTERPRIV, NULL, "deopall",0, 0))) return CMD_ERROR; rcp=cip->exts[chanservext]; if (cip->channel) { localsetmodeinit(&changes, cip->channel, chanservnick); for (i=0,lp=cip->channel->users->content; i<cip->channel->users->hashsize;i++,lp++) { if (*lp!=nouser && (*lp & CUMODE_OP)) { if (!(np=getnickbynumeric(*lp)) || (!IsService(np) && (!(rup=getreguserfromnick(np)) || !(rcup=findreguseronchannel(rcp, rup)) || !(CUHasOpPriv(rcup)) || !(CUIsProtect(rcup) || CIsProtect(rcp))))) { localdosetmode_nick(&changes, np, MC_DEOP); } } } localsetmodeflush(&changes, 1); } cs_log(sender,"DEOPALL %s",cip->name->content); chanservstdmessage(sender, QM_DONE); return CMD_OK; }
int dofsck(void *source, int cargc, char **cargv) { int i,j; nick *sender=source, *np, *np2; host *hp; realname *rnp; unsigned int nickmarker; int errors=0; unsigned int nummask,mnum; /* Start off with strict nick consistency checks.. */ controlreply(sender,"- Performing nickname checks"); nickmarker=nextnickmarker(); for(i=0;i<HOSTHASHSIZE;i++) for(hp=hosttable[i];hp;hp=hp->next) hp->marker=0; for(i=0;i<REALNAMEHASHSIZE;i++) for(rnp=realnametable[i];rnp;rnp=rnp->next) rnp->marker=0; controlreply(sender," - Scanning nick hash table"); for (i=0;i<NICKHASHSIZE;i++) { for(np=nicktable[i];np;np=np->next) { if (np->marker==nickmarker) { controlreply(sender, "ERROR: bumped into the same nick %s/%s twice in hash table.",longtonumeric(np->numeric,5),np->nick); errors++; } /* Mark this nick so we can check we found them all */ np->marker=nickmarker; /* Check that we can find this nick with the lookup functions */ if (getnickbynick(np->nick) != np) { controlreply(sender, "ERROR: can't find %s/%s using getnickbynick().", longtonumeric(np->numeric,5),np->nick); errors++; } if (getnickbynumeric(np->numeric) != np) { controlreply(sender, "ERROR: can't find %s/%s using getnickbynumeric().", longtonumeric(np->numeric,5),np->nick); errors++; } /* Check that the nick appears in the list of nicks using its host */ if (findhost(np->host->name->content) != np->host) { controlreply(sender, "ERROR: can't find %s/%s's host (%s) using findhost().", longtonumeric(np->numeric,5),np->nick,np->host->name->content); errors++; } for(np2=np->host->nicks;np2;np2=np2->nextbyhost) if (np2==np) break; if (!np2) { controlreply(sender, "ERROR: can't find %s/%s (host=%s) on the host users list.", longtonumeric(np->numeric,5),np->nick,np->host->name->content); errors++; } np->host->marker++; /* Same for realnames */ if (findrealname(np->realname->name->content) != np->realname) { controlreply(sender, "ERROR: can't find %s/%s's realname (%s) using findrealname().", longtonumeric(np->numeric,5),np->nick,np->realname->name->content); errors++; } for(np2=np->realname->nicks;np2;np2=np2->nextbyrealname) if (np2==np) break; if (!np2) { controlreply(sender, "ERROR: can't find %s/%s (realname=%s) on the realname users list.", longtonumeric(np->numeric,5),np->nick,np->realname->name->content); errors++; } np->realname->marker++; if (IsAccount(np) && !np->authname[0]) { controlreply(sender, "ERROR: nick %s/%s is +r but no authname stored.", longtonumeric(np->numeric,5),np->nick); errors++; } /* if (!IsAccount(np) && np->authname[0]) { controlreply(sender, "ERROR: nick %s/%s is -r but carries authname '%s'.", longtonumeric(np->numeric,5),np->nick,np->authname); errors++; } */ if (IsSetHost(np) && (!np->sethost || !np->shident)) { controlreply(sender, "ERROR: nick %s/%s is +h but nick or hostname not set.", longtonumeric(np->numeric,5),np->nick); errors++; } if (!IsSetHost(np) && (np->sethost || np->shident)) { controlreply(sender, "ERROR: nick %s/%s is -h but has nick or hostname set.", longtonumeric(np->numeric,5),np->nick); errors++; } } } controlreply(sender," - Scanning server user tables"); for(i=0;i<MAXSERVERS;i++) { if (serverlist[i].linkstate != LS_INVALID) { nummask=((MAXSERVERS-1)<<18) | serverlist[i].maxusernum; for (j=0;j<=serverlist[i].maxusernum;j++) { if ((np=servernicks[i][j])) { mnum=(i<<18) | j; if ((np->numeric & nummask) != mnum) { controlreply(sender, "ERROR: nick %s/%s has wrong masked numeric.",longtonumeric(np->numeric,5),np->nick); errors++; } if (np->marker != nickmarker) { controlreply(sender, "ERROR: nick %s/%s in server user table but not hash!", longtonumeric(np->numeric,5),np->nick); errors++; } np->marker=0; } } } } controlreply(sender," - Scanning host and realname tables"); for (i=0;i<HOSTHASHSIZE;i++) { for (hp=hosttable[i];hp;hp=hp->next) { /* Check that the user counts match up */ if (hp->clonecount != hp->marker) { controlreply(sender, "ERROR: host %s has inconsistent clone count (stored=%d, measured=%u)", hp->name->content,hp->clonecount,hp->marker); errors++; } for (np=hp->nicks;np;np=np->nextbyhost) { if (np->host != hp) { controlreply(sender, "ERROR: nick %s/%s is in list for wrong host " "(in list for %s, actual host %s).", longtonumeric(np->numeric,5),np->nick,hp->name->content, np->host->name->content); errors++; } } hp->marker=0; } } for (i=0;i<REALNAMEHASHSIZE;i++) { for (rnp=realnametable[i];rnp;rnp=rnp->next) { if (rnp->usercount != rnp->marker) { controlreply(sender, "ERROR: realname '%s' has inconsistent clone count " "(stored=%d, measured=%d).", rnp->name->content,rnp->usercount,rnp->marker); errors++; } for (np=rnp->nicks;np;np=np->nextbyrealname) { if (np->realname != rnp) { controlreply(sender, "ERROR: nick %s/%s is in list for wrong realname " "(in list for '%s', actual realname '%s').", longtonumeric(np->numeric,5),np->nick, rnp->name->content, np->realname->name->content); errors++; } } rnp->marker=0; } } if (errors) controlreply(sender,"All checks complete. %d errors found.",errors); else controlreply(sender,"All checks complete. No errors found."); return CMD_OK; }
void csc_dorollbackchan_real(DBConn *dbconn, void *arg) { nick *np=getnickbynumeric((unsigned long)arg); reguser *rup, *crup1, *crup2; chanindex *cip = NULL; regchan *rcp=NULL; regchanuser *rcup; unsigned int userID, channelID, targetID; time_t changetime, authtime; flag_t oldflags, newflags; DBResult *pgres; int j, newuser; char fbuf[18]; if(!dbconn) return; pgres=dbgetresult(dbconn); if (!dbquerysuccessful(pgres)) { Error("chanserv", ERR_ERROR, "Error loading chanlev history data."); return; } if (dbnumfields(pgres) != 7) { Error("chanserv", ERR_ERROR, "Chanlev history data format error."); dbclear(pgres); return; } if (!np) { Error("chanserv", ERR_ERROR, "No nick pointer in rollback."); dbclear(pgres); return; } if (!(rup=getreguserfromnick(np)) || !UHasOperPriv(rup)) { Error("chanserv", ERR_ERROR, "No reguser pointer or oper privs in rollback."); dbclear(pgres); return; } while(dbfetchrow(pgres)) { userID=strtoul(dbgetvalue(pgres, 0), NULL, 10); channelID=strtoul(dbgetvalue(pgres, 1), NULL, 10); if (!rcp) { for (j=0; j<CHANNELHASHSIZE && !rcp; j++) { for (cip=chantable[j]; cip && !rcp; cip=cip->next) { if (!cip->exts[chanservext]) continue; if (((regchan*)cip->exts[chanservext])->ID == channelID) rcp=(regchan*)cip->exts[chanservext]; } } if (!rcp) { Error("chanserv", ERR_ERROR, "No regchan pointer or oper privs in rollback."); dbclear(pgres); return; } cip=rcp->index; chanservsendmessage(np, "Attempting to roll back %s:", cip->name->content); } targetID=strtoul(dbgetvalue(pgres, 2), NULL, 10); changetime=strtoul(dbgetvalue(pgres, 3), NULL, 10); authtime=strtoul(dbgetvalue(pgres, 4), NULL, 10); oldflags=strtoul(dbgetvalue(pgres, 5), NULL, 10); newflags=strtoul(dbgetvalue(pgres, 6), NULL, 10); strncpy(fbuf, printflags(newflags, rcuflags), 17); fbuf[17]='\0'; crup1=findreguserbyID(userID); crup2=findreguserbyID(targetID); if (!crup2) { chanservsendmessage(np, "Affected user (ID: %d) is no longer in database, continuing...", targetID); continue; } if (!(rcup=findreguseronchannel(rcp, crup2))) { rcup=getregchanuser(); rcup->user=crup2; rcup->chan=rcp; rcup->flags=0; rcup->changetime=time(NULL); rcup->usetime=0; rcup->info=NULL; newuser=1; } else newuser=0; csdb_chanlevhistory_insert(rcp, np, rcup->user, rcup->flags, oldflags); rcup->flags=oldflags; chanservsendmessage(np, "%s user flags for %s (%s -> %s)", newflags?oldflags?"Restoring":"Deleting":"Readding", crup2->username, fbuf, printflags(oldflags, rcuflags)); if (rcup->flags) { if (newuser) { addregusertochannel(rcup); csdb_createchanuser(rcup); } else csdb_updatechanuser(rcup); } else { if (!newuser) { csdb_deletechanuser(rcup); delreguserfromchannel(rcp, crup2); } freesstring(rcup->info); freeregchanuser(rcup); rcup=NULL; for (j=0; j<REGCHANUSERHASHSIZE; j++) if (rcp->regusers[j]) break; if (j==REGCHANUSERHASHSIZE) { cs_log(np, "DELCHAN %s (Cleared chanlev from rollback)", cip->name->content); chanservsendmessage(np, "Rollback cleared chanlev list, channel deleted."); rcp=NULL; } } } chanservstdmessage(np, QM_DONE); dbclear(pgres); }
void gline_free(searchCtx *ctx, struct searchNode *thenode) { struct gline_localdata *localdata; nick *np, *nnp; chanindex *cip, *ncip; whowas *ww; int i, j, hits, safe=0; time_t ti = time(NULL); glinebuf gbuf; localdata = thenode->localdata; if (localdata->count > NSMAX_GLINE_LIMIT) { /* need to warn the user that they have just tried to twat half the network ... */ ctx->reply(senderNSExtern, "Warning: your pattern matches too many users (%d) - nothing done.", localdata->count); free(localdata); free(thenode); return; } glinebufinit(&gbuf, 0); if (ctx->searchcmd == reg_chansearch) { for (i=0;i<CHANNELHASHSIZE;i++) { for (cip=chantable[i];cip;cip=ncip) { ncip = cip->next; if (cip != NULL && cip->channel != NULL && cip->marker == localdata->marker) { for (j=0;j<cip->channel->users->hashsize;j++) { if (cip->channel->users->content[j]==nouser) continue; if ((np=getnickbynumeric(cip->channel->users->content[j]))) { if(!glineuser(&gbuf, np, localdata, ti)) safe++; } } } } } } else if (ctx->searchcmd == reg_nicksearch) { for (i=0;i<NICKHASHSIZE;i++) { for (np=nicktable[i];np;np=nnp) { nnp = np->next; if (np->marker == localdata->marker) { if(!glineuser(&gbuf, np, localdata, ti)) safe++; } } } } else { for (i = whowasoffset; i < whowasoffset + whowasmax; i++) { ww = &whowasrecs[i % whowasmax]; if (ww->type == WHOWAS_UNUSED) continue; if (ww->marker == localdata->marker) { if(!glineuser(&gbuf, &ww->nick, localdata, ti)) safe++; } } } glinebufcounthits(&gbuf, &hits, NULL); glinebufcommit(&gbuf, 1); if (safe) ctx->reply(senderNSExtern, "Warning: your pattern matched privileged users (%d in total) - these have not been touched.", safe); /* notify opers of the action */ ctx->wall(NL_GLINES, "%s/%s glined %d %s via %s for %s [%d untouched].", senderNSExtern->nick, senderNSExtern->authname, (localdata->count - safe), (localdata->count - safe) != 1 ? "users" : "user", (ctx->searchcmd == reg_chansearch) ? "chansearch" : "nicksearch", longtoduration(localdata->duration, 1), safe); free(localdata); free(thenode); }
hchannel *hchannel_add(const char *cname) { channel *cp; hchannel *hchan; cp = findchannel((char*)cname); if (!cp) { localcreatechannel(helpmodnick, (char*)cname); cp = findchannel((char*)cname); } else { localjoinchannel(helpmodnick, cp); localgetops(helpmodnick, cp); } hchan = (hchannel*)malloc(sizeof(hchannel)); hchan->welcome[0] = '\0'; hchan->real_channel = cp; hchan->flags = H_CHANFLAGS_DEFAULT; hchan->channel_users = NULL; hchan->channel_hterms = NULL; hchan->max_idle = 5 * HDEF_m; hchan->topic = NULL; hchan->report_to = NULL; hchan->autoqueue = 0; hchan->jf_control = time(NULL); hchan->lc_profile = NULL; hchan->censor = NULL; hchan->htickets = NULL; hchan->ticket_message = NULL; hchan->last_activity = time(NULL); hchan->last_staff_activity = time(NULL); hchan->stats = get_hstat_channel(); hchan->next = hchannels; hchannels = hchan; { int i; nick *nck; huser *husr; huser_channel *tmp; for (i=0;i < hchan->real_channel->users->hashsize;i++) { nck = getnickbynumeric(hchan->real_channel->users->content[i]); if (!nck) /* it's a hash, not an array */ continue; if ((husr = huser_get(nck)) == NULL) husr = huser_add(nck); tmp = huser_add_channel(husr, hchan); hchannel_add_user(hchan, husr); if (hchan->real_channel->users->content[i] & CUMODE_OP) tmp->flags |= HCUMODE_OP; if (hchan->real_channel->users->content[i] & CUMODE_VOICE) tmp->flags |= HCUMODE_VOICE; } } return hchan; }
int handleclearmodemsg(void *source, int cargc, char **cargv) { channel *cp; void *harg[4]; nick *np, *target; char *mcp; unsigned long usermask=0; int i; int changes=0; if (cargc<2) { return CMD_OK; } if ((cp=findchannel(cargv[0]))==NULL) { /* No channel, abort */ Error("channel",ERR_WARNING,"Mode change on non-existent channel %s",cargv[0]); return CMD_OK; } if (((char *)source)[2]=='\0') { /* Server mode change? (I don't think servers are allowed to do CLEARMODE) */ np=NULL; } else if ((np=getnickbynumericstr((char *)source))==NULL) { /* No sender, continue but moan */ Error("channel",ERR_WARNING,"Mode change by non-existent user %s on channel %s",(char *)source,cp->index->name->content); } harg[0]=cp; harg[1]=np; harg[3]=(void *)(long)(cp->flags); for (mcp=cargv[1];*mcp;mcp++) { switch (*mcp) { case 'o': usermask |= CUMODE_OP; changes |= MODECHANGE_USERS; break; case 'v': usermask |= CUMODE_VOICE; changes |= MODECHANGE_USERS; break; case 'n': ClearNoExtMsg(cp); changes |= MODECHANGE_MODES; break; case 't': ClearTopicLimit(cp); changes |= MODECHANGE_MODES; break; case 's': ClearSecret(cp); changes |= MODECHANGE_MODES; break; case 'p': ClearPrivate(cp); changes |= MODECHANGE_MODES; break; case 'i': ClearInviteOnly(cp); changes |= MODECHANGE_MODES; break; case 'm': ClearModerated(cp); changes |= MODECHANGE_MODES; break; case 'c': ClearNoColour(cp); changes |= MODECHANGE_MODES; break; case 'C': ClearNoCTCP(cp); changes |= MODECHANGE_MODES; break; case 'r': ClearRegOnly(cp); changes |= MODECHANGE_MODES; break; case 'D': ClearDelJoins(cp); changes |= MODECHANGE_MODES; break; case 'u': ClearNoQuitMsg(cp); changes |= MODECHANGE_MODES; break; case 'N': ClearNoNotice(cp); changes |= MODECHANGE_MODES; break; case 'M': ClearModNoAuth(cp); changes |= MODECHANGE_MODES; break; case 'T': ClearSingleTarg(cp); changes |= MODECHANGE_MODES; break; case 'b': clearallbans(cp); changes |= MODECHANGE_BANS; break; case 'k': /* This is all safe even if there is no key atm */ freesstring(cp->key); cp->key=NULL; ClearKey(cp); changes |= MODECHANGE_MODES; break; case 'l': cp->limit=0; ClearLimit(cp); changes |= MODECHANGE_MODES; break; } } if (usermask) { /* We have to strip something off each user */ for (i=0;i<cp->users->hashsize;i++) { if (cp->users->content[i]!=nouser && (cp->users->content[i] & usermask)) { /* This user exists and has at least one of the modes we're clearing */ if ((target=getnickbynumeric(cp->users->content[i]))==NULL) { /* This really is a fuckup, we found them on the channel but there isn't a user with that numeric */ /* This means there's a serious bug in the nick/channel tracking code */ Error("channel",ERR_ERROR,"CLEARMODE failed: user on channel who doesn't exist?"); } else { harg[2]=target; /* Yes, these are deliberate three way bitwise ANDs.. */ if (cp->users->content[i] & usermask & CUMODE_OP) triggerhook(HOOK_CHANNEL_DEOPPED, harg); if (cp->users->content[i] & usermask & CUMODE_VOICE) triggerhook(HOOK_CHANNEL_DEVOICED, harg); cp->users->content[i] &= ~usermask; } } } } harg[2]=(void *)((long)changes); triggerhook(HOOK_CHANNEL_MODECHANGE, harg); return CMD_OK; }
int handlemodemsg(void *source, int cargc, char **cargv) { channel *cp; int dir=1; int arg=2; char *modestr; unsigned long *lp; void *harg[4]; nick *np, *target; int hooknum; int changes=0; if (cargc<2) { return CMD_OK; } if (cargv[0][0]!='#' && cargv[0][0]!='+') { /* Not a channel, ignore */ return CMD_OK; } if ((cp=findchannel(cargv[0]))==NULL) { /* No channel, abort */ Error("channel",ERR_WARNING,"Mode change on non-existent channel %s",cargv[0]); return CMD_OK; } if (((char *)source)[2]=='\0') { /* Server mode change, treat as divine intervention */ np=NULL; } else if ((np=getnickbynumericstr((char *)source))==NULL) { /* No sender, continue but moan */ Error("channel",ERR_WARNING,"Mode change by non-existent user %s on channel %s",(char *)source,cp->index->name->content); } /* Set up the hook data */ harg[0]=cp; harg[1]=np; harg[3]=(void *)(long)(cp->flags); /* Process the mode string one character at a time */ /* Maybe I'll write this more intelligently one day if I can comprehend the ircu code that does this */ for (modestr=cargv[1];*modestr;modestr++) { switch(*modestr) { /* Set whether we are adding or removing modes */ case '+': dir=1; break; case '-': dir=0; break; /* Simple modes: just set or clear based on value of dir */ case 'n': if (dir) { SetNoExtMsg(cp); } else { ClearNoExtMsg(cp); } changes |= MODECHANGE_MODES; break; case 't': if (dir) { SetTopicLimit(cp); } else { ClearTopicLimit(cp); } changes |= MODECHANGE_MODES; break; case 's': if (dir) { SetSecret(cp); ClearPrivate(cp); } else { ClearSecret(cp); } changes |= MODECHANGE_MODES; break; case 'p': if (dir) { SetPrivate(cp); ClearSecret(cp); } else { ClearPrivate(cp); } changes |= MODECHANGE_MODES; break; case 'i': if (dir) { SetInviteOnly(cp); } else { ClearInviteOnly(cp); } changes |= MODECHANGE_MODES; break; case 'm': if (dir) { SetModerated(cp); } else { ClearModerated(cp); } changes |= MODECHANGE_MODES; break; case 'c': if (dir) { SetNoColour(cp); } else { ClearNoColour(cp); } changes |= MODECHANGE_MODES; break; case 'C': if (dir) { SetNoCTCP(cp); } else { ClearNoCTCP(cp); } changes |= MODECHANGE_MODES; break; case 'r': if (dir) { SetRegOnly(cp); } else { ClearRegOnly(cp); } changes |= MODECHANGE_MODES; break; case 'D': if (dir) { SetDelJoins(cp); } else { ClearDelJoins(cp); } changes |= MODECHANGE_MODES; break; case 'u': if (dir) { SetNoQuitMsg(cp); } else { ClearNoQuitMsg(cp); } changes |= MODECHANGE_MODES; break; case 'N': if (dir) { SetNoNotice(cp); } else { ClearNoNotice(cp); } changes |= MODECHANGE_MODES; break; case 'M': if (dir) { SetModNoAuth(cp); } else { ClearModNoAuth(cp); } changes |= MODECHANGE_MODES; break; case 'T': if (dir) { SetSingleTarg(cp); } else { ClearSingleTarg(cp); } changes |= MODECHANGE_MODES; break; /* Parameter modes: advance parameter and possibly read it in */ case 'l': if (dir) { /* +l uses a parameter, but -l does not. * If there is no parameter, don't set the mode. * I guess we should moan too in that case, but * they might be even nastier to us if we do ;) */ if (arg<cargc) { cp->limit=strtol(cargv[arg++],NULL,10); SetLimit(cp); } } else { ClearLimit(cp); cp->limit=0; } changes |= MODECHANGE_MODES; break; case 'k': if (dir) { /* +k uses a parameter in both directions */ if (arg<cargc) { freesstring(cp->key); /* It's probably NULL, but be safe */ cp->key=getsstring(cargv[arg++],KEYLEN); SetKey(cp); } } else { freesstring(cp->key); cp->key=NULL; ClearKey(cp); arg++; /* Eat the arg without looking at it, even if it's not there */ } changes |= MODECHANGE_MODES; break; /* Op/Voice */ case 'o': case 'v': if (arg<cargc) { if((lp=getnumerichandlefromchanhash(cp->users,numerictolong(cargv[arg++],5)))==NULL) { /* They're not on the channel; MODE crossed with part/kill/kick/blah */ Error("channel",ERR_DEBUG,"Mode change for user %s not on channel %s",cargv[arg-1],cp->index->name->content); } else { if ((target=getnickbynumeric(*lp))==NULL) { /* This really is a fuckup, we found them on the channel but there isn't a user with that numeric */ /* This means there's a serious bug in the nick/channel tracking code */ Error("channel",ERR_ERROR,"Mode change for user %s on channel %s who doesn't exist",cargv[arg-1],cp->index->name->content); } else { /* Do the mode change whilst admiring the beautiful code layout */ harg[2]=target; if (*modestr=='o') { if (dir) { *lp |= CUMODE_OP; hooknum=HOOK_CHANNEL_OPPED; } else { *lp &= ~CUMODE_OP; hooknum=HOOK_CHANNEL_DEOPPED; } } else { if (dir) { *lp |= CUMODE_VOICE; hooknum=HOOK_CHANNEL_VOICED; } else { *lp &= ~CUMODE_VOICE; hooknum=HOOK_CHANNEL_DEVOICED; } } triggerhook(hooknum,harg); } } } changes |= MODECHANGE_USERS; break; case 'b': if (arg<cargc) { if (dir) { setban(cp,cargv[arg++]); triggerhook(HOOK_CHANNEL_BANSET,harg); } else { clearban(cp,cargv[arg++],0); triggerhook(HOOK_CHANNEL_BANCLEAR,harg); } } changes |= MODECHANGE_BANS; break; default: Error("channel",ERR_DEBUG,"Unknown mode char '%c' %s on %s",*modestr,dir?"set":"cleared",cp->index->name->content); break; } } harg[2]=(void *)((long)changes); triggerhook(HOOK_CHANNEL_MODECHANGE,(void *)harg); return CMD_OK; }