X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/blobdiff_plain/df3bf97067f8f12eb2076ecaf37a89c30d56d9be..f651cddfa1748d9624e3a8b2ec2335dcde246a5c:/localuser/localuserchannel.c diff --git a/localuser/localuserchannel.c b/localuser/localuserchannel.c index f9e87c63..d770f877 100644 --- a/localuser/localuserchannel.c +++ b/localuser/localuserchannel.c @@ -12,6 +12,7 @@ #include #include #include +#include MODULE_VERSION(""); @@ -94,7 +95,7 @@ int handleinvitecmd(void *source, int cargc, char **cargv) { } if (!(sender=getnickbynumericstr(source))) { - Error("localuserchannel",ERR_WARNING,"Got invite from unknown numeric %s.",source); + Error("localuserchannel",ERR_WARNING,"Got invite from unknown numeric %s.",(char *)source); return CMD_OK; } @@ -129,7 +130,8 @@ int handleinvitecmd(void *source, int cargc, char **cargv) { return CMD_OK; } -int handlechannelmsgcmd(void *source, int cargc, char **cargv) { +/* PRIVMSG/NOTICE to channel handling is identical up to the point where the hook is called. */ +static int handlechannelmsgornotice(void *source, int cargc, char **cargv, int isnotice) { void *nargs[3]; nick *sender; channel *target; @@ -148,12 +150,12 @@ int handlechannelmsgcmd(void *source, int cargc, char **cargv) { } if ((sender=getnickbynumericstr((char *)source))==NULL) { - Error("localuserchannel",ERR_DEBUG,"PRIVMSG from non existant user %s",(char *)source); + Error("localuserchannel",ERR_DEBUG,"PRIVMSG/NOTICE from non existant user %s",(char *)source); return CMD_OK; } if ((target=findchannel(cargv[0]))==NULL) { - Error("localuserchannel",ERR_DEBUG,"PRIVMSG to non existant channel %s",cargv[0]); + Error("localuserchannel",ERR_DEBUG,"PRIVMSG/NOTICE to non existant channel %s",cargv[0]); return CMD_OK; } @@ -176,11 +178,11 @@ int handlechannelmsgcmd(void *source, int cargc, char **cargv) { found++; if (umhandlers[numeric&MAXLOCALUSER]) { if ((np=getnickbynumeric(numeric))==NULL) { - Error("localuserchannel",ERR_ERROR,"PRIVMSG to channel user who doesn't exist (?) on %s",cargv[0]); + Error("localuserchannel",ERR_ERROR,"PRIVMSG/NOTICE to channel user who doesn't exist (?) on %s",cargv[0]); continue; } if (!IsDeaf(np)) { - (umhandlers[numeric&MAXLOCALUSER])(np,LU_CHANMSG,nargs); + (umhandlers[numeric&MAXLOCALUSER])(np,(isnotice?LU_CHANNOTICE:LU_CHANMSG),nargs); } else { found--; } @@ -189,80 +191,128 @@ int handlechannelmsgcmd(void *source, int cargc, char **cargv) { } if (!found) { - Error("localuserchannel",ERR_DEBUG,"Couldn't find any local targets for PRIVMSG to %s",cargv[0]); + Error("localuserchannel",ERR_DEBUG,"Couldn't find any local targets for PRIVMSG/NOTICE to %s",cargv[0]); } return CMD_OK; } -/* - * Added by Cruicky so S2 can receive channel notices - * Shameless rip of the above with s/privmsg/notice - */ +/* Wrapper functions to call the above code */ +int handlechannelmsgcmd(void *source, int cargc, char **cargv) { + return handlechannelmsgornotice(source, cargc, cargv, 0); +} + int handlechannelnoticecmd(void *source, int cargc, char **cargv) { - void *nargs[3]; - nick *sender; - channel *target; - nick *np; - unsigned long numeric; - int i; - int found=0; + return handlechannelmsgornotice(source, cargc, cargv, 1); +} - if (cargc<2) { - return CMD_OK; - } +/* Burst onto channel. This replaces the timestamp and modes + * with the provided ones. Keys and limits use the provided ones + * if needed. nick * is optional, but joins the channel opped if + * provided. + * + * Due to the way ircu works, this only works if the provided timestamp is + * older than the one currently on the channel. If the timestamps are + * equal, the modes are ignored, but the user (if any) is still allowed to + * join with op. If the provided timestamp is newer than the exsting one we + * just do a join instead - if you try to replace an old timestamp with a + * newer one ircu will just laugh at you (and you will be desynced). + */ +int localburstontochannel(channel *cp, nick *np, time_t timestamp, flag_t modes, unsigned int limit, char *key) { + unsigned int i; + char extramodebuf[512]; + char nickbuf[512]; - if (cargv[0][0]!='#' && cargv[0][0]!='+') { - /* Not a channel notice */ - return CMD_OK; + if (cp==NULL) + return 1; + + if (timestamp > cp->timestamp) { + return localjoinchannel(np, cp); } - if ((sender=getnickbynumericstr((char *)source))==NULL) { - Error("localuserchannel",ERR_DEBUG,"NOTICE from non existant user %s",(char *)source); - return CMD_OK; - } - - if ((target=findchannel(cargv[0]))==NULL) { - Error("localuserchannel",ERR_DEBUG,"NOTICE to non existant channel %s",cargv[0]); - return CMD_OK; - } - - /* OK, we have a valid channel the notice was sent to. Let's look to see - * if we have any local users on there. Set up the arguments first as they are - * always going to be the same. */ - - nargs[0]=(void *)sender; - nargs[1]=(void *)target; - nargs[2]=(void *)cargv[1]; - - for (found=0,i=0;iusers->hashsize;i++) { - numeric=target->users->content[i]; - if (numeric!=nouser && homeserver(numeric&CU_NUMERICMASK)==mylongnum) { - /* OK, it's one of our users.. we need to deal with it */ - if (found && ((getnickbynumericstr((char *)source)==NULL) || ((findchannel(cargv[0]))==NULL) || !(getnumerichandlefromchanhash(target->users, sender->numeric)))) { - Error("localuserchannel", ERR_INFO, "Nick or channel lost, or user no longer on channel in LU_CHANNOTICE"); - break; + if (timestamp < cp->timestamp) { + cp->timestamp=timestamp; + cp->flags=modes; + + /* deal with key - if we need one use the provided one if set, otherwise + * the existing one, but if there is no existing one clear +k */ + if (IsKey(cp)) { + /* Sanitise the provided key - this might invalidate it.. */ + if (key) + clean_key(key); + + if (key && *key) { + /* Free old key, if any */ + if (cp->key) + freesstring(cp->key); + + cp->key=getsstring(key,KEYLEN); + } else { + if (!cp->key) + ClearKey(cp); } - found++; - if (umhandlers[numeric&MAXLOCALUSER]) { - if ((np=getnickbynumeric(numeric))==NULL) { - Error("localuserchannel",ERR_ERROR,"NOTICE to channel user who doesn't exist (?) on %s",cargv[0]); - continue; - } - if (!IsDeaf(np)) { - (umhandlers[numeric&MAXLOCALUSER])(np,LU_CHANNOTICE,nargs); - } else { - found--; - } + } else { + /* Not +k - free the existing key, if any */ + freesstring(cp->key); + cp->key=NULL; + } + + if (IsLimit(cp)) { + if (limit) { + cp->limit=limit; + } else { + if (!cp->limit) + ClearLimit(cp); + } + } else { + if (cp->limit) + cp->limit=0; + } + + /* We also need to blow away all other op/voice and bans on the + * channel. This is the same code we use when someone else does + * it to us. */ + clearallbans(cp); + for (i=0;iusers->hashsize;i++) { + if (cp->users->content[i]!=nouser) { + cp->users->content[i]&=CU_NUMERICMASK; } } } - if (!found) { - Error("localuserchannel",ERR_DEBUG,"Couldn't find any local targets for NOTICE to %s",cargv[0]); + /* Actually add the nick to the channel. Make sure it's a local nick and actually exists first. */ + if (np && (homeserver(np->numeric) == mylongnum) && + !(getnumerichandlefromchanhash(cp->users,np->numeric))) { + addnicktochannel(cp,(np->numeric)|CUMODE_OP); + } else { + np=NULL; /* If we're not adding it here, don't send it later in the burst msg either */ + } + + if (connected) { + /* actual burst message */ + if (np) { + sprintf(nickbuf," %s:o", longtonumeric(np->numeric,5)); + } else { + nickbuf[0]='\0'; + } + + if (IsLimit(cp)) { + sprintf(extramodebuf," %d",cp->limit); + } else { + extramodebuf[0]='\0'; + } + + /* XX B #channel +modes */ + irc_send("%s B %s %lu %s%s%s%s%s", + mynumeric->content,cp->index->name->content,cp->timestamp, + printflags(cp->flags,cmodeflags),extramodebuf, + IsKey(cp)?" ":"",IsKey(cp)?cp->key->content:"", nickbuf); } - return CMD_OK; + /* Tell the world something happened... */ + triggerhook(HOOK_CHANNEL_BURST,cp); + + return 0; } int localjoinchannel(nick *np, channel *cp) { @@ -289,20 +339,21 @@ int localjoinchannel(nick *np, channel *cp) { harg[0]=cp; harg[1]=np; - triggerhook(HOOK_CHANNEL_JOIN, harg); - if (connected) { irc_send("%s J %s %lu",longtonumeric(np->numeric,5),cp->index->name->content,cp->timestamp); } + + triggerhook(HOOK_CHANNEL_JOIN, harg); + return 0; } -int localpartchannel(nick *np, channel *cp) { +int localpartchannel(nick *np, channel *cp, char *reason) { void *harg[3]; /* Check pointers are valid.. */ if (cp==NULL || np==NULL) { - Error("localuserchannel",ERR_WARNING,"Trying to part NULL channel or NULL nick (cp=%x,np=%x)",cp,np); + Error("localuserchannel",ERR_WARNING,"Trying to part NULL channel or NULL nick (cp=%p,np=%p)",cp,np); return 1; } @@ -319,7 +370,10 @@ int localpartchannel(nick *np, channel *cp) { } if (connected) { - irc_send("%s L %s",longtonumeric(np->numeric,5),cp->index->name->content); + if (reason != NULL) + irc_send("%s L %s :%s",longtonumeric(np->numeric,5),cp->index->name->content, reason); + else + irc_send("%s L %s",longtonumeric(np->numeric,5),cp->index->name->content); } harg[0]=cp; @@ -438,6 +492,11 @@ void localdosetmode_ban (modechanges *changes, const char *ban, short dir) { /* If we're told to clear a ban that isn't here, do nothing. */ if (dir==MCB_DEL && !clearban(changes->cp, bansstr->content, 1)) return; + + /* Similarly if someone is trying to add a completely overlapped ban, do + * nothing */ + if (dir==MCB_ADD && !setban(changes->cp, bansstr->content)) + return; if (changes->changecount >= MAXMODEARGS) localsetmodeflush(changes, 0); @@ -456,7 +515,7 @@ void localdosetmode_ban (modechanges *changes, const char *ban, short dir) { * Set or clear a key on the channel */ -void localdosetmode_key (modechanges *changes, const char *key, short dir) { +void localdosetmode_key (modechanges *changes, char *key, short dir) { int i,j; sstring *keysstr; @@ -465,6 +524,11 @@ void localdosetmode_key (modechanges *changes, const char *key, short dir) { localsetmodeflush(changes,0); if (dir==MCB_ADD) { + /* Sanitise the key. If this nullifies it then we give up */ + clean_key(key); + if (!*key) + return; + /* Get a copy of the key for use later */ keysstr=getsstring(key, KEYLEN); @@ -481,6 +545,7 @@ void localdosetmode_key (modechanges *changes, const char *key, short dir) { freesstring(changes->cp->key); changes->cp->key=getsstring(key, KEYLEN); /* That's it, we're done */ + freesstring(keysstr); return; } else { /* There was a command to delete key.. we need to flush @@ -644,6 +709,11 @@ void localdosetmode_nick (modechanges *changes, nick *target, short modes) { return; } + if (IsCloaked(target)) { + /* Target is cloaked, never set channel modes for cloaked users */ + return; + } + if ((modes & MC_DEOP) && (*lp & CUMODE_OP)) { (*lp) &= ~CUMODE_OP; if (changes->changecount >= MAXMODEARGS) @@ -823,6 +893,7 @@ void localusermodechange(nick *np, channel *cp, char *modes) { void localsettopic(nick *np, channel *cp, char *topic) { unsigned long *lp; char source[10]; + time_t now=getnettime(); if (np==NULL || (lp=getnumerichandlefromchanhash(cp->users,np->numeric))==NULL) { /* User isn't on channel, hack mode */ @@ -843,10 +914,16 @@ void localsettopic(nick *np, channel *cp, char *topic) { } cp->topic=getsstring(topic,TOPICLEN); - cp->topictime=getnettime(); + + /* Update the topic time iff the old time was in the past. This + * means if we are bouncing a topic with a TS 1sec newer than ours + * we won't use an old timestamp */ + if (cp->topictime < now) { + cp->topictime=now; + } if (connected) { - irc_send("%s T %s %u %u :%s",source,cp->index->name->content,cp->timestamp,cp->topictime,(cp->topic)?cp->topic->content:""); + irc_send("%s T %s %jd %jd :%s",source,cp->index->name->content,(intmax_t)cp->timestamp,(intmax_t)cp->topictime,(cp->topic)?cp->topic->content:""); } } @@ -858,7 +935,7 @@ void localkickuser(nick *np, channel *cp, nick *target, const char *message) { if (pk->target == target && pk->chan == cp) return; - Error("localuserchannel", ERR_INFO, "Adding pending kick for %s on %s", target->nick, cp->index->name->content); + Error("localuserchannel", ERR_DEBUG, "Adding pending kick for %s on %s", target->nick, cp->index->name->content); pk = (pendingkick *)malloc(sizeof(pendingkick)); pk->source = np; pk->chan = cp; @@ -910,7 +987,7 @@ void clearpendingkicks(int hooknum, void *arg) { pendingkicklist = pk->next; if (pk->target && pk->chan) { - Error("localuserchannel", ERR_INFO, "Processing pending kick for %s on %s", pk->target->nick, pk->chan->index->name->content); + Error("localuserchannel", ERR_DEBUG, "Processing pending kick for %s on %s", pk->target->nick, pk->chan->index->name->content); _localkickuser(pk->source, pk->chan, pk->target, pk->reason->content); } @@ -971,7 +1048,30 @@ void sendmessagetochannel(nick *source, channel *cp, char *format, ... ) { } } -void localinvite(nick *source, channel *cp, nick *target) { +void sendopnoticetochannel(nick *source, channel *cp, char *format, ... ) { + char buf[BUFSIZE]; + char senderstr[6]; + va_list va; + + if (!source) + return; + + longtonumeric2(source->numeric,5,senderstr); + + va_start(va,format); + /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */ + /* So max sendable message is 495 bytes. Of course, a client won't be able + * to receive this.. */ + + vsnprintf(buf,BUFSIZE-17,format,va); + va_end(va); + + if (connected) { + irc_send("%s WC %s :%s",senderstr,cp->index->name->content,buf); + } +} + +void localinvite(nick *source, chanindex *cip, nick *target) { /* Servers can't send invites */ if (!source) @@ -983,7 +1083,7 @@ void localinvite(nick *source, channel *cp, nick *target) { * argument */ if (connected) { irc_send("%s I %s :%s",longtonumeric(source->numeric,5), - target->nick, cp->index->name->content); + target->nick, cip->name->content); } }