X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/blobdiff_plain/70b0a4e57a15fcdd2af466de4c38dc6c32dce469..40814391c7b017da05d4daa2895093e5099801c7:/localuser/localuserchannel.c diff --git a/localuser/localuserchannel.c b/localuser/localuserchannel.c index b6a5715a..d770f877 100644 --- a/localuser/localuserchannel.c +++ b/localuser/localuserchannel.c @@ -6,31 +6,61 @@ #include "../channel/channel.h" #include "../irc/irc.h" #include "../lib/version.h" +#include "../lib/sstring.h" #include #include #include #include +#include MODULE_VERSION(""); +typedef struct pendingkick { + nick *source, *target; + channel *chan; + sstring *reason; + struct pendingkick *next; +} pendingkick; + +pendingkick *pendingkicklist; + int handlechannelmsgcmd(void *source, int cargc, char **cargv); int handlechannelnoticecmd(void *source, int cargc, char **cargv); int handleinvitecmd(void *source, int cargc, char **cargv); +void clearpendingkicks(int hooknum, void *arg); +void checkpendingkicknicks(int hooknum, void *arg); +void checkpendingkickchannels(int hooknum, void *arg); +void _localkickuser(nick *np, channel *cp, nick *target, const char *message); void luc_handlekick(int hooknum, void *arg); void _init() { + pendingkicklist=NULL; registerserverhandler("P",&handlechannelmsgcmd,2); registerserverhandler("O",&handlechannelnoticecmd,2); registerserverhandler("I",&handleinvitecmd,2); registerhook(HOOK_CHANNEL_KICK, luc_handlekick); + registerhook(HOOK_NICK_LOSTNICK, checkpendingkicknicks); + registerhook(HOOK_CHANNEL_LOSTCHANNEL, checkpendingkickchannels); + registerhook(HOOK_CORE_ENDOFHOOKSQUEUE,&clearpendingkicks); } void _fini() { + pendingkick *pk; + deregisterserverhandler("P",&handlechannelmsgcmd); deregisterserverhandler("O",&handlechannelnoticecmd); deregisterserverhandler("I",&handleinvitecmd); deregisterhook(HOOK_CHANNEL_KICK, luc_handlekick); + deregisterhook(HOOK_NICK_LOSTNICK, checkpendingkicknicks); + deregisterhook(HOOK_CHANNEL_LOSTCHANNEL, checkpendingkickchannels); + deregisterhook(HOOK_CORE_ENDOFHOOKSQUEUE,&clearpendingkicks); + + for (pk=pendingkicklist;pk;pk=pendingkicklist) { + pendingkicklist = pk->next; + freesstring(pk->reason); + free(pk); + } } void luc_handlekick(int hooknum, void *arg) { @@ -65,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; } @@ -100,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; @@ -119,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; } @@ -140,14 +171,18 @@ int handlechannelmsgcmd(void *source, int cargc, char **cargv) { 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_CHANMSG"); + break; + } 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--; } @@ -156,76 +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 (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); + } + } 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 ((target=findchannel(cargv[0]))==NULL) { - Error("localuserchannel",ERR_DEBUG,"NOTICE to non existant channel %s",cargv[0]); - return CMD_OK; + /* 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 */ } - - /* 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 */ - 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--; - } - } + + 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); } - if (!found) { - Error("localuserchannel",ERR_DEBUG,"Couldn't find any local targets for NOTICE to %s",cargv[0]); - } + /* Tell the world something happened... */ + triggerhook(HOOK_CHANNEL_BURST,cp); - return CMD_OK; + return 0; } int localjoinchannel(nick *np, channel *cp) { @@ -252,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; } @@ -282,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; @@ -401,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); @@ -419,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; @@ -428,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); @@ -444,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 @@ -607,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) @@ -786,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 */ @@ -806,14 +914,41 @@ 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:""); } } void localkickuser(nick *np, channel *cp, nick *target, const char *message) { + pendingkick *pk; + + if (hookqueuelength) { + for (pk = pendingkicklist; pk; pk = pk->next) + if (pk->target == target && pk->chan == cp) + return; + + 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; + pk->target = target; + pk->reason = getsstring(message, BUFSIZE); + pk->next = pendingkicklist; + pendingkicklist = pk; + } else { + _localkickuser(np, cp, target, message); + } +} + +void _localkickuser(nick *np, channel *cp, nick *target, const char *message) { unsigned long *lp; char source[10]; @@ -825,7 +960,7 @@ void localkickuser(nick *np, channel *cp, nick *target, const char *message) { if (homeserver(np->numeric)!=mylongnum) { return; } - if ((*lp&CUMODE_OP)==0 && IsTopicLimit(cp)) { + if ((*lp&CUMODE_OP)==0) { localgetops(np,cp); } strcpy(source,longtonumeric(np->numeric,5)); @@ -844,6 +979,52 @@ void localkickuser(nick *np, channel *cp, nick *target, const char *message) { delnickfromchannel(cp, target->numeric, 1); } +void clearpendingkicks(int hooknum, void *arg) { + pendingkick *pk; + + pk = pendingkicklist; + while (pk) { + pendingkicklist = pk->next; + + if (pk->target && pk->chan) { + 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); + } + + freesstring(pk->reason); + free(pk); + pk = pendingkicklist; + } +} + +void checkpendingkicknicks(int hooknum, void *arg) { + nick *np = (nick *)arg; + pendingkick *pk; + + for (pk=pendingkicklist; pk; pk = pk->next) { + if (pk->source == np) { + Error("localuserchannel", ERR_INFO, "Pending kick source %s got deleted, NULL'ing source for pending kick", np->nick); + pk->source = NULL; + } + if (pk->target == np) { + Error("localuserchannel", ERR_INFO, "Pending kick target %s got deleted, NULL'ing target for pending kick", np->nick); + pk->target = NULL; + } + } +} + +void checkpendingkickchannels(int hooknum, void *arg) { + channel *cp = (channel *)arg; + pendingkick *pk; + + for (pk=pendingkicklist; pk; pk = pk->next) { + if (pk->chan == cp) { + Error("localuserchannel", ERR_INFO, "Pending kick channel %s got deleted, NULL'ing channel for pending kick", cp->index->name->content); + pk->chan = NULL; + } + } +} + void sendmessagetochannel(nick *source, channel *cp, char *format, ... ) { char buf[BUFSIZE]; char senderstr[6]; @@ -867,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) @@ -879,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); } }