]>
jfr.im git - irc/quakenet/newserv.git/blob - chanfix/chanfix.c
10 #include "../splitlist/splitlist.h"
11 #include "../localuser/localuserchannel.h"
12 #include "../core/schedule.h"
13 #include "../core/error.h"
14 #include "../nick/nick.h"
15 #include "../lib/irc_string.h"
16 #include "../control/control.h"
17 #include "../lib/version.h"
28 /* user accessible commands */
29 int cfcmd_debug(void *source
, int cargc
, char **cargv
);
30 int cfcmd_debughistogram(void *source
, int cargc
, char **cargv
);
31 int cfcmd_debugsample(void *source
, int cargc
, char **cargv
);
32 int cfcmd_debugexpire(void *source
, int cargc
, char **cargv
);
33 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
);
34 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
);
35 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
);
36 int cfcmd_showregs(void *source
, int cargc
, char **cargv
);
37 int cfcmd_requestop(void *source
, int cargc
, char **cargv
);
38 int cfcmd_save(void *source
, int cargc
, char **cargv
);
39 int cfcmd_load(void *source
, int cargc
, char **cargv
);
41 /* scheduled events */
42 void cfsched_dosample(void *arg
);
43 void cfsched_doexpire(void *arg
);
44 void cfsched_dosave(void *arg
);
47 void cfhook_autofix(int hook
, void *arg
);
48 void cfhook_statsreport(int hook
, void *arg
);
49 void cfhook_auth(int hook
, void *arg
);
51 /* helper functions */
52 regop
*cf_createregop(nick
*np
, chanindex
*cip
);
53 void cf_deleteregop(chanindex
*cip
, regop
*ro
);
54 unsigned long cf_gethash(nick
*np
, int type
);
56 int cf_storechanfix(void);
57 int cf_loadchanfix(void);
60 #define min(a,b) ((a > b) ? b : a)
63 cfext
= registerchanext("chanfix");
64 cfnext
= registernickext("chanfix");
66 if (cfext
< 0 || cfnext
< 0) {
67 Error("chanfix", ERR_ERROR
, "Couldn't register channel and/or nick extension");
70 schedulerecurring(time(NULL
), 0, CFSAMPLEINTERVAL
, &cfsched_dosample
, NULL
);
71 schedulerecurring(time(NULL
), 0, CFEXPIREINTERVAL
, &cfsched_doexpire
, NULL
);
72 schedulerecurring(time(NULL
), 0, CFAUTOSAVEINTERVAL
, &cfsched_dosave
, NULL
);
74 registercontrolhelpcmd("cfdebug", NO_DEVELOPER
, 1, &cfcmd_debug
, "Display Debug Information on chanfix data for channel");
75 registercontrolhelpcmd("cfhistogram", NO_DEVELOPER
, 1, &cfcmd_debughistogram
, "Display Debug Histogram of chanfix data for channel");
77 registercontrolhelpcmd("cfsample", NO_DEVELOPER
, 1, &cfcmd_debugsample
, "DEBUG Command - must not be loaded on live instances");
78 registercontrolhelpcmd("cfexpire", NO_DEVELOPER
, 1, &cfcmd_debugexpire
, "DEBUG Command - must not be loaded on live instances");
80 registercontrolhelpcmd("chanopstat", NO_OPER
, 1, &cfcmd_chanopstat
, "Shows chanop statistics for a given channel");
81 registercontrolhelpcmd("chanoplist", NO_OPER
, 1, &cfcmd_chanoplist
, "Shows lists of known chanops, including scores");
83 registercontrolhelpcmd("chanfix", NO_OPER
, 1, &cfcmd_chanfix
, "Perform a chanfix on a channel to op known users only");
84 registercontrolhelpcmd("showregs", NO_OPER
, 1, &cfcmd_showregs
, "Show regular ops known on a channel (including services)");
86 /* should we disable this in the 'final' build? */
87 /* registercontrolcmd("requestop", 0, 2, &cfcmd_requestop); */
89 registercontrolhelpcmd("cfsave", NO_DEVELOPER
, 0, &cfcmd_save
, "Force save of chanfix data");
90 registercontrolhelpcmd("cfload", NO_DEVELOPER
, 0, &cfcmd_load
, "Force load of chanfix data");
93 registerhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
94 registerhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
95 registerhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
96 registerhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
99 registerhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
100 registerhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
109 if (cffailedinit
== 0) {
110 deleteschedule(NULL
, &cfsched_dosample
, NULL
);
111 deleteschedule(NULL
, &cfsched_doexpire
, NULL
);
112 deleteschedule(NULL
, &cfsched_dosave
, NULL
);
118 deregistercontrolcmd("cfdebug", &cfcmd_debug
);
119 deregistercontrolcmd("cfhistogram", &cfcmd_debughistogram
);
121 deregistercontrolcmd("cfsample", &cfcmd_debugsample
);
122 deregistercontrolcmd("cfexpire", &cfcmd_debugexpire
);
124 deregistercontrolcmd("chanopstat", &cfcmd_chanopstat
);
125 deregistercontrolcmd("chanoplist", &cfcmd_chanoplist
);
126 deregistercontrolcmd("chanfix", &cfcmd_chanfix
);
127 deregistercontrolcmd("showregs", &cfcmd_showregs
);
129 // deregistercontrolcmd("requestop", &cfcmd_requestop);
131 deregistercontrolcmd("cfsave", &cfcmd_save
);
132 deregistercontrolcmd("cfload", &cfcmd_load
);
135 deregisterhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
136 deregisterhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
137 deregisterhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
138 deregisterhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
141 deregisterhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
142 deregisterhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
146 releasechanext(cfext
);
150 releasenickext(cfnext
);
154 int cfcmd_debug(void *source
, int cargc
, char **cargv
) {
155 nick
*np
= (nick
*)source
;
162 controlreply(np
, "Syntax: cfdebug <#channel>");
167 cip
= findchanindex(cargv
[0]);
170 controlreply(np
, "No such channel.");
174 controlreply(np
, "Found channel %s. Retrieving chanfix information...", cargv
[0]);
176 cf
= cip
->exts
[cfext
];
179 controlreply(np
, "No chanfix information for %s", cargv
[0]);
183 controlreply(np
, "Found chanfix information. Dumping...");
185 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
186 ro
= ((regop
**)cf
->regops
.content
)[i
];
188 controlreply(np
, "%d. type: %s hash: 0x%lx lastopped: %lu uh: %s score: %d",
189 i
+ 1, ro
->type
== CFACCOUNT
? "CFACCOUNT" : "CFHOST", ro
->hash
,
190 ro
->lastopped
, ro
->uh
? ro
->uh
->content
: "(unknown)", ro
->score
);
193 controlreply(np
, "Done.");
198 int cfcmd_debughistogram(void *source
, int cargc
, char **cargv
) {
199 nick
*np
= (nick
*)source
;
203 int histogram
[10001]; /* 625 (lines) * 16 (columns/line) + 1 (for histogram[0]) */
205 for (i
= 0; i
< 10001; i
++)
208 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
209 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
210 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
211 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
212 score
= ((regop
**)cf
->regops
.content
)[a
]->score
;
221 controlreply(np
, "--- Histogram of chanfix scores");
223 for (i
= 1; i
< 10001; i
+= 16) {
224 controlreply(np
, "%6d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
225 i
, histogram
[i
], histogram
[i
+1], histogram
[i
+2], histogram
[i
+3], histogram
[i
+4],
226 histogram
[i
+5], histogram
[i
+6], histogram
[i
+7], histogram
[i
+8], histogram
[i
+9],
227 histogram
[i
+10], histogram
[i
+11], histogram
[i
+12], histogram
[i
+13], histogram
[i
+14],
231 controlreply(np
, "--- End of histogram");
236 int cfcmd_debugsample(void *source
, int cargc
, char **cargv
) {
237 cfsched_dosample(NULL
);
239 controlreply((nick
*)source
, "Done.");
244 int cfcmd_debugexpire(void *source
, int cargc
, char **cargv
) {
245 cfsched_doexpire(NULL
);
247 controlreply((nick
*)source
, "Done.");
252 /* used for qsorting int arrays */
253 int cmpint(const void *a
, const void *b
) {
265 /* used for qsorting regop* arrays */
266 int cmpregop(const void *a
, const void *b
) {
267 regop
*p
= *(regop
**)a
;
268 regop
*q
= *(regop
**)b
;
270 if (p
->score
> q
->score
)
272 else if (p
->score
< q
->score
)
278 int cf_getsortedregops(chanfix
*cf
, int max
, regop
**list
) {
284 qsort(cf
->regops
.content
, cf
->regops
.cursi
, sizeof(regop
*), cmpregop
);
286 for (i
= 0; i
< min(max
, cf
->regops
.cursi
); i
++) {
287 list
[i
] = ((regop
**)cf
->regops
.content
)[i
];
293 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
) {
294 nick
*np
= (nick
*)source
;
304 controlreply(np
, "Syntax: chanopstat <#channel>");
309 cp
= findchannel(cargv
[0]);
312 controlreply(np
, "No such channel.");
317 cf
= cp
->index
->exts
[cfext
];
320 controlreply(np
, "No chanfix information for %s", cargv
[0]);
326 count
= cf_getsortedregops(cf
, 10, rolist
);
327 controlreply(np
, "Scores of \"best ops\" on %s are:", cargv
[0]);
329 for (i
=0;i
<count
;i
++) {
330 controlreply(np
, " %d", rolist
[i
]->score
);
334 scores
= (int*)malloc(sizeof(int) * cp
->users
->hashsize
);
338 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
339 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
340 np2
= getnickbynumeric(cp
->users
->content
[a
]);
342 ro
= cf_findregop(np2
, cp
->index
, CFACCOUNT
| CFHOST
);
345 scores
[i
++] = ro
->score
;
349 qsort(scores
, i
, sizeof(int), &cmpint
);
350 controlreply(np
, "Scores of current ops on %s are:", cargv
[0]);
352 for (a
=0;a
<min(i
,20);a
++) {
356 controlreply(np
, " %d", scores
[a
]);
360 controlreply(np
, "Done.");
365 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
) {
366 nick
*np
= (nick
*)source
;
378 controlreply(np
, "Syntax: chanoplist <#channel>");
383 cip
= findchanindex(cargv
[0]);
386 controlreply(np
, "No such channel.");
391 cf
= cip
->exts
[cfext
];
394 controlreply(np
, "No chanfix information for %s", cargv
[0]);
399 count
= cf_getsortedregops(cf
, 50, rolist
);
401 controlreply(np
, "Pos Score Type User/Last seen");
403 for (i
=0;i
<count
;i
++) {
404 np2
= cf_findnick(rolist
[i
], cip
);
407 hand
= getnumerichandlefromchanhash(cip
->channel
->users
, np2
->numeric
);
409 /* hand should be non-null in all cases */
411 controlreply(np
, "%3d %5d %-7s %1s%-16s %s@%s (%s)", i
+ 1, rolist
[i
]->score
,
412 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host",
413 (*hand
& CUMODE_OP
) ? "@" : "", np2
->nick
, np2
->ident
,
414 np2
->host
->name
->content
, np2
->realname
->name
->content
);
416 ct
= rolist
[i
]->lastopped
;
418 strftime(date
,sizeof(date
),"%d-%m-%Y %H:%M:%S",tm
);
419 controlreply(np
, "%3d %5d %-7s %1s%-16s %s Last seen: %s", i
+ 1, rolist
[i
]->score
,
420 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host", "", "!NONE!",
421 rolist
[i
]->uh
? rolist
[i
]->uh
->content
: "!UNKNOWN!", date
);
425 controlreply(np
, "Done.");
430 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
) {
431 nick
*np
= (nick
*)source
;
436 controlreply(np
, "Syntax: chanfix <#channel>");
441 cp
= findchannel(cargv
[0]);
444 controlreply(np
, "No such channel.");
449 if (sp_countsplitservers() > 0) {
450 controlreply(np
, "Chanfix cannot be used during a netsplit.");
455 ret
= cf_fixchannel(cp
);
459 controlreply(np
, "Channel was fixed.");
462 controlreply(np
, "Channel cannot be fixed: no chanfix information");
464 case CFX_FIXEDFEWOPS
:
465 controlreply(np
, "Channel was fixed but only a few ops could be found and reopped.");
475 int cfcmd_showregs(void *source
, int cargc
, char **cargv
) {
476 nick
*np
= (nick
*)source
;
484 controlreply(np
, "Syntax: showregs <#channel>");
489 cp
= findchannel(cargv
[0]);
492 controlreply(np
, "No such channel.");
497 cf
= cp
->index
->exts
[cfext
];
500 controlreply(np
, "No chanfix information for %s", cargv
[0]);
507 for(i
=0;i
<cp
->users
->hashsize
;i
++) {
508 if(cp
->users
->content
[i
] != nouser
) {
509 np2
= getnickbynumeric(cp
->users
->content
[i
]);
511 if (IsService(np2
)) {
512 controlreply(np
, "%s (service)", np2
->nick
);
518 /* now get a sorted list of regops */
519 ops
= cf_getsortedregops(cf
, 50, rolist
);
521 /* and show some of them */
522 for (i
=0;i
<ops
;i
++) {
523 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< rolist
[0]->score
/ 2 || rolist
[i
]->score
< CFMINSCORE
)
526 np2
= cf_findnick(rolist
[i
], cp
->index
);
529 controlreply(np
, "%s (%s)", np2
->nick
, rolist
[i
]->type
== CFACCOUNT
? "account" : "host");
535 controlreply((nick
*)source
, "--- End of list: %d users listed", count
);
540 int cfcmd_requestop(void *source
, int cargc
, char **cargv
) {
541 nick
*np
= (nick
*)source
;
549 controlreply(np
, "Syntax: requestop <#channel> [nick]");
554 cp
= findchannel(cargv
[0]);
557 controlreply(np
, "No such channel.");
563 user
= getnickbynick(cargv
[1]);
566 controlreply(np
, "No such user.");
572 hand
= getnumerichandlefromchanhash(cp
->users
, user
->numeric
);
575 controlreply(np
, "User %s is not on channel %s.", user
->nick
, cargv
[0]);
580 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
581 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
582 controlreply(np
, "There are ops on channel %s. This command can only be"
583 " used if there are no ops.", cargv
[0]);
589 if (sp_countsplitservers() > 0) {
590 controlreply(np
, "One or more servers are currently split. Wait until the"
591 " netsplit is over and try again.");
596 if (cf_wouldreop(user
, cp
)) {
597 localsetmodeinit(&changes
, cp
, mynick
);
598 localdosetmode_nick(&changes
, user
, MC_OP
);
599 localsetmodeflush(&changes
, 1);
601 controlreply(np
, "Chanfix opped you on the specified channel.");
603 ret
= cf_fixchannel(cp
);
605 if (ret
== CFX_NOUSERSAVAILABLE
)
606 controlreply(np
, "Chanfix knows regular ops for that channel. They will"
607 " be opped when they return.");
609 controlreply(np
, "Chanfix has opped known ops for that channel.");
615 int cfcmd_save(void *source
, int cargc
, char **cargv
) {
616 nick
*np
= (nick
*)source
;
619 count
= cf_storechanfix();
621 controlreply(np
, "%d chanfix records saved.", count
);
632 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
633 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
634 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
635 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
636 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
637 free(((regop
**)cf
->regops
.content
)[a
]);
640 array_free(&(((chanfix
*)cip
->exts
[cfext
])->regops
));
643 free(cip
->exts
[cfext
]);
644 cip
->exts
[cfext
] = NULL
;
649 int cfcmd_load(void *source
, int cargc
, char **cargv
) {
650 nick
*np
= (nick
*)source
;
653 count
= cf_loadchanfix();
655 controlreply(np
, "%d chanfix records loaded.", count
);
660 int cf_hasauthedcloneonchan(nick
*np
, channel
*cp
) {
664 for (jp
= np
->host
->nicks
; jp
; jp
= jp
->nextbyhost
) {
665 if (!IsAccount(jp
) || jp
->numeric
== np
->numeric
)
668 hand
= getnumerichandlefromchanhash(cp
->users
, jp
->numeric
);
670 if (hand
&& (*hand
& CUMODE_OP
) && strcmp(np
->ident
, jp
->ident
) == 0)
677 void cfsched_dosample(void *arg
) {
678 int i
,a
,now
,cfscore
,cfnewro
,cfuhost
,diff
;
684 struct timeval start
;
686 char buf
[USERLEN
+1+HOSTLEN
+1];
690 cfuhost
= cfscore
= cfnewro
= 0;
692 if (sp_countsplitservers() > CFMAXSPLITSERVERS
)
695 gettimeofday(&start
, NULL
);
697 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
698 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
699 cf
= (chanfix
*)cip
->exts
[cfext
];
702 if (!cp
|| cp
->users
->totalusers
< CFMINUSERS
)
705 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
706 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
707 np
= getnickbynumeric(cp
->users
->content
[a
]);
717 roh
= ro
= cf_findregop(np
, cip
, CFACCOUNT
| CFHOST
);
719 if ((ro
== NULL
|| (IsAccount(np
) && ro
->type
== CFHOST
)) &&
720 !cf_hasauthedcloneonchan(np
, cp
)) {
721 ro
= cf_createregop(np
, cip
);
725 /* lastopped == now if the user has clones, we obviously
726 * don't want to give them points in this case */
727 if (ro
&& ro
->lastopped
!= now
) {
728 if (ro
->type
!= CFHOST
|| !cf_hasauthedcloneonchan(np
, cp
)) {
733 /* merge any matching CFHOST records */
734 if (roh
&& roh
->type
== CFHOST
&& ro
->type
== CFACCOUNT
) {
736 ro
->score
+= roh
->score
;
738 cf_deleteregop(cip
, roh
);
741 /* store the user's account/host if we have to */
742 if (ro
->uh
== NULL
&& ro
->score
>= CFMINSCOREUH
) {
743 if (ro
->type
== CFACCOUNT
)
744 ro
->uh
= getsstring(np
->authname
, ACCOUNTLEN
);
746 snprintf(buf
, sizeof(buf
), "%s@%s", np
->ident
, np
->host
->name
->content
);
747 roh
->uh
= getsstring(buf
, USERLEN
+1+HOSTLEN
);
760 cp
= findchannel("#qnet.chanfix");
763 gettimeofday(&end
, NULL
);
765 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
766 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
768 sendmessagetochannel(mynick
, cp
, "sampled chanfix scores, assigned %d new"
769 " points, %d new regops, %d user@hosts added, deltaT: %dms", cfscore
,
770 cfnewro
, cfuhost
, diff
);
774 void cfsched_doexpire(void *arg
) {
779 int i
,a
,cfscore
,cfregop
,cffreeuh
,diff
;
782 struct timeval start
;
786 cffreeuh
= cfscore
= cfregop
= 0;
788 gettimeofday(&start
, NULL
);
789 currenttime
=getnettime();
791 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
792 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
793 cf
= (chanfix
*)cip
->exts
[cfext
];
796 rolist
= (regop
**)cf
->regops
.content
;
798 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
801 if (((currenttime
- ro
->lastopped
) > (2 * CFSAMPLEINTERVAL
)) && ro
->score
) {
806 if ((ro
->score
< CFMINSCOREUH
) && ro
->uh
) {
813 if (ro
->score
== 0 || ro
->lastopped
< (currenttime
- CFREMEMBEROPS
)) {
814 cf_deleteregop(cip
, ro
);
822 /* stolen from channel/channelindex.c */
823 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
824 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
825 /* CAREFUL: deleting items from chains you're walking is bad */
828 /* try to delete it if there's no chanfix* pointer */
829 if (cip
->exts
[cfext
] == NULL
)
830 releasechanindex(cip
);
834 cp
= findchannel("#qnet.chanfix");
837 gettimeofday(&end
, NULL
);
839 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
840 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
842 sendmessagetochannel(mynick
, cp
, "expired chanfix scores, purged %d points,"
843 " scrapped %6d regops, %d user@hosts freed, deltaT: %dms", cfscore
,
844 cfregop
, cffreeuh
, diff
);
849 void cfsched_dosave(void *arg
) {
854 void cfhook_autofix(int hook
, void *arg
) {
856 void **args
= (void**)arg
;
859 if (hook
== HOOK_CHANNEL_DEOPPED
|| hook
== HOOK_CHANNEL_PART
||
860 hook
== HOOK_CHANNEL_KICK
|| hook
== HOOK_CHANNEL_JOIN
) {
863 if (sp_countsplitservers() > 0)
866 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
867 if (cp
->users
->content
[a
] != nouser
) {
870 if (cp
->users
->content
[a
] & CUMODE_OP
)
875 /* don't fix small channels.. it's inaccurate and
876 * they could just cycle the channel */
880 cf_fixchannel((channel
*)args
[0]);
885 void cfhook_statsreport(int hook
, void *arg
) {
887 int i
,a
,rc
,mc
,memory
;
892 memory
= rc
= mc
= 0;
894 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
895 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
896 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
897 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
898 memory
+= sizeof(regop
) + sizeof(regop
*);
900 if (((regop
**)cf
->regops
.content
)[a
]->uh
)
901 memory
+= sizeof(sstring
) + strlen(((regop
**)cf
->regops
.content
)[a
]->uh
->content
) + 1;
906 memory
+= sizeof(chanfix
);
913 snprintf(buf
, sizeof(buf
), "Chanfix : %6d registered ops, %9d monitored channels. %9d"
914 " kbytes of memory used", rc
, mc
, (memory
/ 1024));
915 triggerhook(HOOK_CORE_STATSREPLY
, buf
);
919 void cfhook_auth(int hook
, void *arg
) {
920 nick
*np
= (nick
*)arg
;
922 /* Invalidate the user's hash */
923 np
->exts
[cfnext
] = NULL
;
925 /* Calculate the new hash */
926 cf_gethash(np
, CFACCOUNT
);
929 /* Returns the hash of a specific user (np), type can be either CFACCOUNT,
930 CFHOST or both (binary or'd values). cf_gethash will also cache the user's
931 hash in a nick extension */
932 unsigned long cf_gethash(nick
*np
, int type
) {
933 char buf
[USERLEN
+1+HOSTLEN
+1];
936 if (IsAccount(np
) && (type
& CFACCOUNT
)) {
937 if (np
->exts
[cfnext
] == NULL
) {
938 np
->exts
[cfnext
] = (void *)crc32(np
->authname
);
941 return (unsigned long)np
->exts
[cfnext
];
942 } else if (type
== CFACCOUNT
)
943 return 0; /* this should not happen */
946 if (!IsAccount(np
) && np
->exts
[cfnext
])
947 return (unsigned long)np
->exts
[cfnext
];
949 snprintf(buf
, sizeof(buf
), "%s@%s", np
->ident
, np
->host
->name
->content
);
952 /* if the user is not authed, update the hash */
953 if (!IsAccount(np
)) {
954 np
->exts
[cfnext
] = (void *)hash
;
961 return 0; /* should not happen */
964 /* This seems to be a lot faster than using sprintf */
965 int cf_equhost(const char *uhost
, const char *user
, const char *host
) {
966 char *p
= strchr(uhost
, '@');
968 /* We assume the uhost contains a @ - which it should do in all cases */
972 if (ircd_strncmp(uhost
, user
, p
- uhost
) == 0 && ircd_strcmp(p
+ 1, host
) == 0)
978 /* Why do we actually store the users' real hosts/accounts instead of hashes?
979 * - it allows operators to see the hosts/accounts in 'chanoplist' even if the
980 * users are not online
981 * - it avoids hash collisions (could be avoided with md5/sha1/etc.)
983 int cf_cmpregopnick(regop
*ro
, nick
*np
) {
984 if (ro
->uh
!= NULL
) {
985 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
986 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
) &&
987 strcmp(ro
->uh
->content
, np
->authname
) == 0);
989 return (ro
->hash
== cf_gethash(np
, CFHOST
) &&
990 cf_equhost(ro
->uh
->content
, np
->ident
, np
->host
->name
->content
));
993 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
994 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
));
996 return (ro
->hash
== cf_gethash(np
, CFHOST
));
1000 nick
*cf_findnick(regop
*ro
, chanindex
*cip
) {
1001 chanfix
*cf
= cip
->exts
[cfext
];
1002 channel
*cp
= cip
->channel
;
1006 if (cf
== NULL
|| cp
== NULL
)
1009 if (ro
->type
== CFACCOUNT
) {
1010 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1011 if(cp
->users
->content
[a
] != nouser
) {
1012 np2
= getnickbynumeric(cp
->users
->content
[a
]);
1014 if (!IsAccount(np2
))
1017 if (cf_cmpregopnick(ro
, np2
))
1023 if (ro
->type
== CFHOST
) {
1024 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1025 if(cp
->users
->content
[a
] != nouser
) {
1026 np2
= getnickbynumeric(cp
->users
->content
[a
]);
1028 if (cf_cmpregopnick(ro
, np2
))
1037 regop
*cf_findregop(nick
*np
, chanindex
*cip
, int type
) {
1038 chanfix
*cf
= cip
->exts
[cfext
];
1045 if (IsAccount(np
) && (type
& CFACCOUNT
))
1050 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
1051 ro
= ((regop
**)cf
->regops
.content
)[i
];
1053 if (ro
->type
== ty
&& cf_cmpregopnick(ro
, np
))
1057 /* try using the uhost if we didn't find a user with the right account */
1058 if (ty
== CFACCOUNT
&& (type
& CFHOST
))
1059 return cf_findregop(np
, cip
, CFHOST
);
1066 regop
*cf_createregop(nick
*np
, chanindex
*cip
) {
1067 chanfix
*cf
= cip
->exts
[cfext
];
1072 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1075 array_init(&(cf
->regops
), sizeof(regop
*));
1077 cip
->exts
[cfext
] = cf
;
1080 slot
= array_getfreeslot(&(cf
->regops
));
1082 rolist
= (regop
**)cf
->regops
.content
;
1084 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1091 rolist
[slot
]->type
= type
;
1092 rolist
[slot
]->hash
= cf_gethash(np
, type
);
1093 rolist
[slot
]->uh
= NULL
;
1094 rolist
[slot
]->lastopped
= 0;
1095 rolist
[slot
]->score
= 0;
1097 return rolist
[slot
];
1100 void cf_deleteregop(chanindex
*cip
, regop
*ro
) {
1101 chanfix
*cf
= cip
->exts
[cfext
];
1107 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1108 if (((regop
**)cf
->regops
.content
)[a
] == ro
) {
1109 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
1110 free(((regop
**)cf
->regops
.content
)[a
]);
1111 array_delslot(&(cf
->regops
), a
);
1115 /* get rid of chanfix* if there are no more regops */
1116 if (cf
->regops
.cursi
== 0) {
1117 array_free(&(cf
->regops
));
1119 cip
->exts
[cfext
] = NULL
;
1121 /* we could try to free the chanindex* here
1122 but that would make cfsched_dosample a lot more
1127 int cf_fixchannel(channel
*cp
) {
1130 modechanges changes
;
1133 chanfix
*cf
= cp
->index
->exts
[cfext
];
1136 return CFX_NOCHANFIX
;
1138 localsetmodeinit(&changes
, cp
, mynick
);
1142 /* reop services first and deop other users */
1143 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1144 if(cp
->users
->content
[a
] != nouser
) {
1145 np
= getnickbynumeric(cp
->users
->content
[a
]);
1147 if (IsService(np
) && (np
->nick
[1] == '\0')) {
1148 localdosetmode_nick(&changes
, np
, MC_OP
);
1151 localdosetmode_nick(&changes
, np
, MC_DEOP
);
1155 /* don't reop users if we've already opped some services */
1158 localsetmodeflush(&changes
, 1);
1163 /* now get a sorted list of regops */
1164 ops
= cf_getsortedregops(cf
, 50, rolist
);
1166 /* and op some of them */
1167 for (i
=0;i
<ops
;i
++) {
1168 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< (rolist
[0]->score
/ 2))
1171 if (rolist
[i
]->score
< CFMINSCORE
&& i
!= 0 )
1174 np
= cf_findnick(rolist
[i
], cp
->index
);
1176 /* only if it's not a service, so we don't screw up 'count' */
1177 if (np
&& !(IsService(np
) && np
->nick
[1] == '\0')) {
1178 localdosetmode_nick(&changes
, np
, MC_OP
);
1184 localsetmodeflush(&changes
, 1);
1186 if (count
== CFMAXOPS
)
1188 else if (count
== 0)
1189 return CFX_NOUSERSAVAILABLE
;
1191 return CFX_FIXEDFEWOPS
;
1194 int cf_storechanfix(void) {
1201 int a
, i
, count
= 0;
1203 snprintf(dstfile
, sizeof(dstfile
), "%s.%d", CFSTORAGE
, CFSAVEFILES
);
1206 for (i
= CFSAVEFILES
; i
> 0; i
--) {
1207 snprintf(srcfile
, sizeof(srcfile
), "%s.%i", CFSTORAGE
, i
- 1);
1208 snprintf(dstfile
, sizeof(dstfile
), "%s.%i", CFSTORAGE
, i
);
1209 rename(srcfile
, dstfile
);
1212 snprintf(srcfile
, sizeof(srcfile
), "%s.0", CFSTORAGE
);
1213 cfdata
= fopen(srcfile
, "w");
1218 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
1219 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
1220 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
1221 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1222 ro
= ((regop
**)cf
->regops
.content
)[a
];
1225 fprintf(cfdata
, "%s %d %lu %lu %d %s\n", cip
->name
->content
,
1226 ro
->type
, ro
->hash
, ro
->lastopped
, ro
->score
, ro
->uh
->content
);
1228 fprintf(cfdata
, "%s %d %lu %lu %d\n", cip
->name
->content
,
1229 ro
->type
, ro
->hash
, ro
->lastopped
, ro
->score
);
1241 /* channel type hash lastopped score host */
1242 int cf_parseline(char *line
) {
1247 char chan
[CHANNELLEN
+1];
1251 char host
[USERLEN
+1+HOSTLEN
+1];
1254 count
= sscanf(line
, "%s %d %lu %lu %d %s", chan
, &type
, &hash
, &lastopped
, &score
, host
);
1257 return 0; /* invalid chanfix record */
1259 cip
= findorcreatechanindex(chan
);
1261 cf
= cip
->exts
[cfext
];
1264 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1267 array_init(&(cf
->regops
), sizeof(regop
*));
1269 cip
->exts
[cfext
] = cf
;
1272 slot
= array_getfreeslot(&(cf
->regops
));
1274 rolist
= (regop
**)cf
->regops
.content
;
1276 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1278 rolist
[slot
]->type
= type
;
1279 rolist
[slot
]->hash
= hash
;
1280 rolist
[slot
]->lastopped
= lastopped
;
1281 rolist
[slot
]->score
= score
;
1284 rolist
[slot
]->uh
= getsstring(host
, USERLEN
+1+HOSTLEN
);
1286 rolist
[slot
]->uh
= NULL
;
1291 int cf_loadchanfix(void) {
1299 snprintf(srcfile
, sizeof(srcfile
), "%s.0", CFSTORAGE
);
1300 cfdata
= fopen(srcfile
, "r");
1307 while (!feof(cfdata
)) {
1308 if (fgets(line
, sizeof(line
), cfdata
) == NULL
)
1311 if (line
[strlen(line
) - 1] == '\n')
1312 line
[strlen(line
) - 1] = '\0';
1314 if (line
[strlen(line
) - 1] == '\r')
1315 line
[strlen(line
) - 1] = '\0';
1317 if (line
[0] != '\0') {
1318 if (cf_parseline(line
))
1328 /* functions for users of this module */
1329 chanfix
*cf_findchanfix(chanindex
*cip
) {
1330 return cip
->exts
[cfext
];
1333 int cf_wouldreop(nick
*np
, channel
*cp
) {
1337 chanfix
*cf
= cp
->index
->exts
[cfext
];
1340 return 1; /* too bad, we can't do anything about it */
1342 ro
= cf_findregop(np
, cp
->index
, CFACCOUNT
| CFHOST
);
1347 rolist
= (regop
**)cf
->regops
.content
;
1349 for (i
=0; i
<cf
->regops
.cursi
; i
++)
1350 if (rolist
[i
]->score
> topscore
)
1351 topscore
= rolist
[i
]->score
;
1353 if (ro
->score
> topscore
/ 2 && ro
->score
> CFMINSCORE
)