]> jfr.im git - irc/quakenet/newserv.git/blame - localuser/localuser.c
patricia trie changes
[irc/quakenet/newserv.git] / localuser / localuser.c
CommitLineData
c86edd1d
Q
1/* localuser.c */
2
3#include "../nick/nick.h"
4#include "../lib/base64.h"
526e7c1d 5#include "../lib/sstring.h"
c86edd1d
Q
6#include "../irc/irc.h"
7#include "../irc/irc_config.h"
8#include "../core/hooks.h"
9#include "../core/error.h"
87698d77 10#include "../lib/version.h"
c86edd1d
Q
11#include "localuser.h"
12
13#include <string.h>
14#include <stdarg.h>
15#include <stdio.h>
16
70b0a4e5 17MODULE_VERSION("");
87698d77 18
c86edd1d
Q
19int currentlocalunum;
20UserMessageHandler umhandlers[MAXLOCALUSER+1];
21
526e7c1d
P
22typedef struct pendingkill {
23 nick *source, *target;
24 sstring *reason;
25 struct pendingkill *next;
26} pendingkill;
27
28pendingkill *pendingkilllist;
29
c86edd1d 30void checklocalkill(int hooknum, void *nick);
526e7c1d
P
31void clearpendingkills(int hooknum, void *arg);
32void checkpendingkills(int hooknum, void *arg);
33void _killuser(nick *source, nick *target, char *reason);
c86edd1d
Q
34
35void _init() {
36 int i;
37
38 for (i=0;i<=MAXLOCALUSER;i++) {
39 umhandlers[i]=NULL;
40 }
41 currentlocalunum=1;
526e7c1d 42 pendingkilllist=NULL;
c86edd1d 43 registerhook(HOOK_IRC_SENDBURSTNICKS,&sendnickburst);
acd438c7 44 registerhook(HOOK_NICK_KILL,&checklocalkill);
ff99b5c0 45 registerhook(HOOK_NICK_LOSTNICK,&checkpendingkills); /* CHECK ME -> should this hook KILL or LOSTNICK or BOTH */
526e7c1d 46 registerhook(HOOK_CORE_ENDOFHOOKSQUEUE,&clearpendingkills);
c86edd1d
Q
47 registerserverhandler("P",&handleprivatemsgcmd,2);
48 registerserverhandler("O",&handleprivatenoticecmd, 2);
49}
50
df3bf970
CP
51void _fini() {
52 pendingkill *pk;
53
54 for (pk=pendingkilllist;pk;pk=pendingkilllist) {
55 pendingkilllist = pk->next;
56 freesstring(pk->reason);
57 free(pk);
58 }
65f2c6a3 59
60 deregisterhook(HOOK_IRC_SENDBURSTNICKS,&sendnickburst);
61 deregisterhook(HOOK_NICK_KILL,&checklocalkill);
62 deregisterhook(HOOK_NICK_LOSTNICK,&checkpendingkills); /* CHECK ME -> should this hook KILL or LOSTNICK or BOTH */
63 deregisterhook(HOOK_CORE_ENDOFHOOKSQUEUE,&clearpendingkills);
646b8161 64
65 deregisterserverhandler("P",&handleprivatemsgcmd);
66 deregisterserverhandler("O",&handleprivatenoticecmd);
df3bf970
CP
67}
68
c86edd1d 69/*
c153c0dc 70 * registerlocaluserwithuseridflags:
c86edd1d
Q
71 * This function creates a local user, and broadcasts it's existence to the net (if connected).
72 */
73
c153c0dc 74nick *registerlocaluserwithuseridflags(char *nickname, char *ident, char *host, char *realname, char *authname, unsigned long authid, flag_t umodes, flag_t accountflags, UserMessageHandler handler) {
c86edd1d
Q
75 int i;
76 nick *newuser,*np;
526e7c1d
P
77 struct irc_in_addr ipaddress;
78
c86edd1d 79 i=0;
95ee3dac 80 currentlocalunum=(currentlocalunum+1)%262142;
c86edd1d
Q
81 while (servernicks[numerictolong(mynumeric->content,2)][currentlocalunum&MAXLOCALUSER]!=NULL) {
82 /* Numeric 262143 on our server is used for "nouser" by the channels module, so cannot be allocated */
83 currentlocalunum=(currentlocalunum+1)%262142;
84 if (++i>MAXLOCALUSER) {
85 return NULL;
86 }
87 }
88
89 /* This code is very similar to stuff in nick.c... */
90 newuser=newnick();
91 newuser->numeric=(numerictolong(mynumeric->content,2)<<18)|(currentlocalunum);
92 strncpy(newuser->nick,nickname,NICKLEN);
93 newuser->nick[NICKLEN]='\0';
94 strncpy(newuser->ident,ident,USERLEN);
95 newuser->ident[USERLEN]='\0';
96 newuser->host=findorcreatehost(host);
97 newuser->realname=findorcreaterealname(realname);
98 newuser->nextbyhost=newuser->host->nicks;
99 newuser->host->nicks=newuser;
100 newuser->nextbyrealname=newuser->realname->nicks;
101 newuser->realname->nicks=newuser;
102 newuser->umodes=umodes;
c153c0dc 103 newuser->accountflags=accountflags;
526e7c1d 104
526e7c1d 105 memset(&ipaddress, 0, sizeof(ipaddress));
c426783d 106 ((unsigned short *)(ipaddress.in6_16))[5] = 65535;
4f077f54
P
107 ((unsigned short *)(ipaddress.in6_16))[6] = 127;
108 ((unsigned char *)(ipaddress.in6_16))[14] = 1;
109 ((unsigned char *)(ipaddress.in6_16))[15] = (currentlocalunum%253)+1;
526e7c1d
P
110
111 newuser->ipnode = refnode(iptree, &ipaddress, PATRICIA_MAXBITS);
96644df6 112 node_increment_usercount(newuser->ipnode);
c426783d 113
c86edd1d
Q
114 newuser->timestamp=getnettime();
115 newuser->shident=NULL;
116 newuser->sethost=NULL;
117 newuser->marker=0;
118 memset(newuser->exts, 0, MAXNICKEXTS * sizeof(void *));
119
120 if (IsAccount(newuser)) {
121 strncpy(newuser->authname,authname,ACCOUNTLEN);
47657339
C
122 newuser->accountts=newuser->timestamp;
123 if (authid) {
124 newuser->auth=findorcreateauthname(authid);
125 newuser->auth->usercount++;
126 newuser->nextbyauthname=newuser->auth->nicks;
127 newuser->auth->nicks=newuser;
128 } else {
129 newuser->auth=NULL;
130 }
c86edd1d
Q
131 } else {
132 newuser->authname[0]='\0';
47657339
C
133 newuser->accountts=0;
134 newuser->auth=NULL;
c86edd1d
Q
135 }
136
137 if (connected) {
138 /* Check for nick collision */
139 if ((np=getnickbynick(nickname))!=NULL) {
140 /* Make sure we will win the collision */
141 newuser->timestamp=(np->timestamp-1);
142 killuser(NULL, np, "Nick collision");
143 }
144 sendnickmsg(newuser);
145 }
146
147 if (handler!=NULL) {
148 umhandlers[(currentlocalunum&MAXLOCALUSER)]=handler;
149 }
150
151 *(gethandlebynumeric(newuser->numeric))=newuser;
152 addnicktohash(newuser);
153 triggerhook(HOOK_NICK_NEWNICK,newuser);
154
155 return newuser;
156}
157
158/*
159 * renamelocaluser:
160 * This function changes the name of a given local user
161 */
162
163int renamelocaluser(nick *np, char *newnick) {
164 nick *np2;
526e7c1d 165 char ipbuf[25];
c86edd1d
Q
166 time_t timestamp=getnettime();
167
168 if (!np)
169 return -1;
170
171 if (strlen(newnick) > NICKLEN)
172 return -1;
173
174 if (homeserver(np->numeric)!=mylongnum)
175 return -1;
176
177 if ((np2=getnickbynick(newnick))) {
178 if (np2==np) {
179 /* Case only name change */
180 strncpy(np->nick,newnick,NICKLEN);
181 np->nick[NICKLEN]='\0';
526e7c1d
P
182 irc_send("%s N %s %d",iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),np->nick,np->timestamp);
183 triggerhook(HOOK_NICK_RENAME,np);
c86edd1d
Q
184 return 0;
185 } else {
186 /* Kill other user and drop through */
187 timestamp=np2->timestamp-1;
188 killuser(NULL, np2, "Nick collision");
189 }
190 }
191
192 np->timestamp=timestamp;
193 removenickfromhash(np);
194 strncpy(np->nick,newnick,NICKLEN);
195 np->nick[NICKLEN]='\0';
196 addnicktohash(np);
197 irc_send("%s N %s %d",longtonumeric(np->numeric,5),np->nick,np->timestamp);
198 triggerhook(HOOK_NICK_RENAME,np);
199
200 return 0;
201}
202
203/*
204 * deregisterlocaluser:
205 * This function removes the given local user from the network
206 */
207
208int deregisterlocaluser(nick *np, char *reason) {
c86edd1d 209 long numeric;
73ab573f 210 char reasonstr[512];
211 void *harg[2];
c86edd1d
Q
212
213 if (np==NULL || (homeserver(np->numeric)!=mylongnum)) {
214 /* Non-existent user, or user not on this server */
215 return -1;
216 }
217
218 if (reason==NULL || *reason=='\0') {
73ab573f 219 sprintf(reasonstr,"Quit");
220 } else {
221 snprintf(reasonstr,510,"Quit: %s",reason);
c86edd1d
Q
222 }
223
73ab573f 224 harg[0]=np;
225 harg[1]=reasonstr;
226
227 triggerhook(HOOK_NICK_QUIT, harg);
228
c86edd1d
Q
229 numeric=np->numeric;
230 umhandlers[np->numeric&MAXLOCALUSER]=NULL;
231 deletenick(np);
232 if (connected) {
73ab573f 233 irc_send("%s Q :%s",longtonumeric(numeric,5),reasonstr);
c86edd1d
Q
234 }
235
236 return 0;
237}
238
c5f1f827
CP
239/*
240 * hooklocaluserhandler:
241 * This function adds a new handler to the hook chain for a local user
242 * THIS RELIES ON MODULES BEING UNLOADED IN THE CORRECT ORDER.
243 */
244UserMessageHandler hooklocaluserhandler(nick *np, UserMessageHandler newhandler) {
245 UserMessageHandler oldhandler = NULL;
246 if (np==NULL || (homeserver(np->numeric)!=mylongnum)) {
247 /* Non-existent user, or user not on this server */
248 return NULL;
249 }
250 oldhandler = umhandlers[np->numeric&MAXLOCALUSER];
251 umhandlers[np->numeric&MAXLOCALUSER]=newhandler;
252 return oldhandler;
253}
254
c86edd1d
Q
255/*
256 * sendnickmsg:
257 * Sends details of a given local nick to the network.
258 */
259
260void sendnickmsg(nick *np) {
261 char numericbuf[6];
526e7c1d 262 char ipbuf[25];
c86edd1d
Q
263
264 strncpy(numericbuf,longtonumeric(np->numeric,5),5);
265 numericbuf[5]='\0';
266
47657339 267 if (IsAccount(np)) {
c153c0dc
CP
268 if(np->accountflags) {
269 irc_send("%s N %s 1 %ld %s %s %s %s:%ld:%lu:" FLAG_T_SPECIFIER " %s %s :%s",
47657339 270 mynumeric->content,np->nick,np->timestamp,np->ident,np->host->name->content,
c153c0dc 271 printflags(np->umodes,umodeflags),np->authname,np->accountts,np->auth?np->auth->userid:0,np->accountflags,
27ce0548 272 iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),numericbuf,np->realname->name->content);
c153c0dc
CP
273 } else if (np->auth) {
274 irc_send("%s N %s 1 %ld %s %s %s %s:%ld:%lu %s %s :%s",
275 mynumeric->content,np->nick,np->timestamp,np->ident,np->host->name->content,
276 printflags(np->umodes,umodeflags),np->authname,np->accountts,np->auth->userid,
277 iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),numericbuf,np->realname->name->content);
47657339
C
278 } else if (np->accountts) {
279 irc_send("%s N %s 1 %ld %s %s %s %s:%ld %s %s :%s",
280 mynumeric->content,np->nick,np->timestamp,np->ident,np->host->name->content,
281 printflags(np->umodes,umodeflags),np->authname,np->accountts,
27ce0548 282 iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),numericbuf,np->realname->name->content);
47657339
C
283 } else {
284 irc_send("%s N %s 1 %ld %s %s %s %s %s %s :%s",
285 mynumeric->content,np->nick,np->timestamp,np->ident,np->host->name->content,
286 printflags(np->umodes,umodeflags),np->authname,
27ce0548 287 iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),numericbuf,np->realname->name->content);
47657339
C
288 }
289 } else {
290 irc_send("%s N %s 1 %ld %s %s %s %s %s :%s",
291 mynumeric->content,np->nick,np->timestamp,np->ident,np->host->name->content,
27ce0548 292 printflags(np->umodes,umodeflags),iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),
47657339
C
293 numericbuf,np->realname->name->content);
294 }
c86edd1d
Q
295}
296
297void sendnickburst(int hooknum, void *arg) {
298 /* Send nick messages for all local users */
299 nick **nh;
300 int i;
301
302 nh=servernicks[numerictolong(mynumeric->content,2)];
303 for (i=0;i<=MAXLOCALUSER;i++) {
304 if (nh[i]!=NULL) {
305 sendnickmsg(nh[i]);
306 }
307 }
308}
309
310/* Check for a kill of a local user */
acd438c7
CP
311void checklocalkill(int hooknum, void *arg) {
312 void **args=arg;
313 nick *target=args[0];
314 char *reason=args[1];
c86edd1d 315 long numeric;
acd438c7
CP
316
317 void *myargs[1];
318 myargs[0]=reason;
319
c86edd1d
Q
320
321 numeric=((nick *)target)->numeric;
322
323 if (homeserver(numeric)==mylongnum) {
324 if (umhandlers[(numeric)&MAXLOCALUSER]!=NULL) {
acd438c7 325 (umhandlers[(numeric)&MAXLOCALUSER])((nick *)target,LU_KILLED,myargs);
c86edd1d
Q
326 }
327 }
328}
329
330int handleprivatemsgcmd(void *source, int cargc, char **cargv) {
331 return handlemessageornotice(source, cargc, cargv, 0);
332}
333
334int handleprivatenoticecmd(void *source, int cargc, char **cargv) {
335 return handlemessageornotice(source, cargc, cargv, 1);
336}
337
338/* Handle privmsg or notice command addressed to user */
339int handlemessageornotice(void *source, int cargc, char **cargv, int isnotice) {
340 nick *sender;
341 nick *target;
342 char *ch;
343 char targetnick[NICKLEN+1];
344 int foundat;
345 void *nargs[2];
346 int i;
347
348 /* Should have target and message */
349 if (cargc<2) {
350 return CMD_OK;
351 }
352
353 if (cargv[0][0]=='#' || cargv[0][0]=='+') {
354 /* Channel message/notice */
355 return CMD_OK;
356 }
357
358 if ((sender=getnickbynumericstr((char *)source))==NULL) {
359 Error("localuser",ERR_WARNING,"PRIVMSG from non existant user %s",(char *)source);
360 return CMD_OK;
361 }
362
363 /* Check for a "secure" message (foo@bar) */
364 foundat=0;
365 for (i=0,ch=cargv[0];(i<=NICKLEN) && (*ch);i++,ch++) {
366 if (*ch=='@') {
367 targetnick[i]='\0';
368 foundat=1;
369 break;
370 } else {
371 targetnick[i]=*ch;
372 }
373 }
374
375 if (!foundat) { /* Didn't find an @ sign, assume it's a numeric */
376 if ((target=getnickbynumericstr(cargv[0]))==NULL) {
377 Error("localuser",ERR_DEBUG,"Couldn't find target for %s",cargv[0]);
378 return CMD_OK;
379 }
380 } else { /* Did find @, do a lookup by nick */
381 if ((target=getnickbynick(targetnick))==NULL) {
382 Error("localuser",ERR_DEBUG,"Couldn't find target for %s",cargv[0]);
bc81aa36 383 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[0]);
c86edd1d
Q
384 return CMD_OK;
385 }
386 }
387
388 if (homeserver(target->numeric)!=mylongnum) {
389 Error("localuser",ERR_WARNING,"Got message/notice for someone not on my server");
bc81aa36
C
390 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[0]);
391 return CMD_OK;
392 }
393
394 if (foundat && !IsService(target)) {
395 Error("localuser",ERR_DEBUG,"Received secure message for %s, but user is not a service",cargv[0]);
396 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[0]);
c86edd1d
Q
397 return CMD_OK;
398 }
399
400 if (umhandlers[(target->numeric)&MAXLOCALUSER]==NULL) {
401 /* No handler anyhow.. */
402 return CMD_OK;
403 }
404
405 /* Dispatch the message. */
406 nargs[0]=(void *)sender;
407 nargs[1]=(void *)cargv[1];
408 (umhandlers[(target->numeric)&MAXLOCALUSER])(target,isnotice?LU_PRIVNOTICE:(foundat?LU_SECUREMSG:LU_PRIVMSG),nargs);
409
410 return CMD_OK;
411}
412
413/* Send message to user */
414void sendmessagetouser(nick *source, nick *target, char *format, ... ) {
415 char buf[BUFSIZE];
416 char senderstr[6];
417 va_list va;
418
419 longtonumeric2(source->numeric,5,senderstr);
420
421 va_start(va,format);
422 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
423 /* So max sendable message is 495 bytes. Of course, a client won't be able
424 * to receive this.. */
425
426 vsnprintf(buf,BUFSIZE-17,format,va);
427 va_end(va);
428
429 if (homeserver(target->numeric)!=mylongnum)
430 irc_send("%s P %s :%s",senderstr,longtonumeric(target->numeric,5),buf);
431}
432
433/* Send messageto server, we don't check, but noones going to want to put a server pointer in anyway... */
434void sendsecuremessagetouser(nick *source, nick *target, char *servername, char *format, ... ) {
435 char buf[BUFSIZE];
436 char senderstr[6];
437 va_list va;
438
439 longtonumeric2(source->numeric,5,senderstr);
440
441 va_start(va,format);
442 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
443 /* So max sendable message is 495 bytes. Of course, a client won't be able
444 * to receive this.. */
445
446 vsnprintf(buf,BUFSIZE-17,format,va);
447 va_end(va);
448
449 if (homeserver(target->numeric)!=mylongnum)
450 irc_send("%s P %s@%s :%s",senderstr,target->nick,servername,buf);
451}
452
453/* Send notice to user */
454void sendnoticetouser(nick *source, nick *target, char *format, ... ) {
455 char buf[BUFSIZE];
456 char senderstr[6];
457 va_list va;
458
459 longtonumeric2(source->numeric,5,senderstr);
460
461 va_start(va,format);
462 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
463 /* So max sendable message is 495 bytes. Of course, a client won't be able
464 * to receive this.. */
465
466 vsnprintf(buf,BUFSIZE-17,format,va);
467 va_end(va);
468
469 if (homeserver(target->numeric)!=mylongnum)
470 irc_send("%s O %s :%s",senderstr,longtonumeric(target->numeric,5),buf);
471}
472
473/* Kill user */
474void killuser(nick *source, nick *target, char *format, ... ) {
475 char buf[BUFSIZE];
526e7c1d
P
476 va_list va;
477 pendingkill *pk;
478
479 va_start(va, format);
480 vsnprintf(buf, BUFSIZE-17, format, va);
481 va_end (va);
482
483 if (hookqueuelength) {
484 for (pk = pendingkilllist; pk; pk = pk->next)
485 if (pk->target == target)
486 return;
487
a77bd764 488 Error("localuser", ERR_DEBUG, "Adding pending kill for %s", target->nick);
526e7c1d
P
489 pk = (pendingkill *)malloc(sizeof(pendingkill));
490 pk->source = source;
491 pk->target = target;
492 pk->reason = getsstring(buf, BUFSIZE);
493 pk->next = pendingkilllist;
494 pendingkilllist = pk;
495 } else {
496 _killuser(source, target, buf);
497 }
498}
499
ab2c2b20
CP
500void sethostuser(nick *target, char *ident, char *host) {
501 irc_send("%s SH %s %s %s", mynumeric->content, longtonumeric(target->numeric, 5), ident, host);
502}
503
526e7c1d 504void _killuser(nick *source, nick *target, char *reason) {
c86edd1d 505 char senderstr[6];
616eddc9 506 char sourcestring[HOSTLEN+NICKLEN+3];
c86edd1d
Q
507
508 if (!source) {
509 /* If we have a null nick, use the server.. */
510 strcpy(senderstr, mynumeric->content);
511 strcpy(sourcestring, myserver->content);
512 } else {
513 strcpy(senderstr, longtonumeric(source->numeric,5));
514 sprintf(sourcestring,"%s!%s",source->host->name->content, source->nick);
515 }
516
526e7c1d 517 irc_send("%s D %s :%s (%s)",senderstr,longtonumeric(target->numeric,5),sourcestring,reason);
c86edd1d
Q
518 deletenick(target);
519}
520
526e7c1d
P
521void clearpendingkills(int hooknum, void *arg) {
522 pendingkill *pk;
523
524 pk = pendingkilllist;
525 while (pk) {
526 pendingkilllist = pk->next;
527
528 if (pk->target) {
a77bd764 529 Error("localuser", ERR_DEBUG, "Processing pending kill for %s", pk->target->nick);
526e7c1d
P
530 _killuser(pk->source, pk->target, pk->reason->content);
531 }
532
533 freesstring(pk->reason);
534 free(pk);
535 pk = pendingkilllist;
536 }
537}
538
539void checkpendingkills(int hooknum, void *arg) {
540 nick *np = (nick *)arg;
541 pendingkill *pk;
542
543 for (pk=pendingkilllist; pk; pk = pk->next) {
544 if (pk->source == np) {
545 Error("localuser", ERR_INFO, "Pending kill source %s got deleted, NULL'ing source for pending kill", np->nick);
546 pk->source = NULL;
547 }
548 if (pk->target == np) {
549 Error("localuser", ERR_INFO, "Pending kill target %s got deleted, NULL'ing target for pending kill", np->nick);
550 pk->target = NULL;
551 }
552 }
553}
554
c86edd1d 555/* Auth user */
b17a30ed 556void localusersetaccountwithuseridflagsts(nick *np, char *accname, unsigned long accid, flag_t accountflags, time_t authTS) {
c86edd1d
Q
557 if (IsAccount(np)) {
558 Error("localuser",ERR_WARNING,"Tried to set account on user %s already authed", np->nick);
559 return;
560 }
561
562 SetAccount(np);
563 strncpy(np->authname, accname, ACCOUNTLEN);
564 np->authname[ACCOUNTLEN]='\0';
b17a30ed 565 np->accountts=authTS?authTS:getnettime();
c153c0dc 566 np->accountflags=accountflags;
47657339
C
567
568 if (accid) {
569 np->auth=findorcreateauthname(accid);
570 np->auth->usercount++;
571 np->nextbyauthname=np->auth->nicks;
572 np->auth->nicks=np;
573 } else {
574 np->auth=NULL;
575 }
c86edd1d
Q
576
577 if (connected) {
c153c0dc
CP
578 if (np->accountflags) {
579 irc_send("%s AC %s %s %ld %lu %lu",mynumeric->content, longtonumeric(np->numeric,5), np->authname, np->accountts, np->auth?np->auth->userid:0, np->accountflags);
580 } else if (np->auth) {
47657339
C
581 irc_send("%s AC %s %s %ld %lu",mynumeric->content, longtonumeric(np->numeric,5), np->authname, np->accountts, np->auth->userid);
582 } else {
583 irc_send("%s AC %s %s %ld",mynumeric->content, longtonumeric(np->numeric,5), np->authname, np->accountts);
584 }
c86edd1d
Q
585 }
586
587 triggerhook(HOOK_NICK_ACCOUNT, np);
588}
589
eac66732
CP
590void localusersetumodes(nick *np, flag_t newmodes) {
591 if (connected) {
592 irc_send("%s M %s %s", longtonumeric(np->numeric,5), np->nick, printflagdiff(np->umodes, newmodes, umodeflags));
593 }
594
595 np->umodes = newmodes;
596}