]>
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"
25 /* user accessible commands */
26 int cfcmd_debug(void *source
, int cargc
, char **cargv
);
27 int cfcmd_debughistogram(void *source
, int cargc
, char **cargv
);
28 int cfcmd_debugsample(void *source
, int cargc
, char **cargv
);
29 int cfcmd_debugexpire(void *source
, int cargc
, char **cargv
);
30 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
);
31 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
);
32 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
);
33 int cfcmd_showregs(void *source
, int cargc
, char **cargv
);
34 int cfcmd_requestop(void *source
, int cargc
, char **cargv
);
35 int cfcmd_save(void *source
, int cargc
, char **cargv
);
36 int cfcmd_load(void *source
, int cargc
, char **cargv
);
38 /* scheduled events */
39 void cfsched_dosample(void *arg
);
40 void cfsched_doexpire(void *arg
);
41 void cfsched_dosave(void *arg
);
44 void cfhook_autofix(int hook
, void *arg
);
45 void cfhook_statsreport(int hook
, void *arg
);
46 void cfhook_auth(int hook
, void *arg
);
48 /* helper functions */
49 regop
*cf_createregop(nick
*np
, chanindex
*cip
);
50 void cf_deleteregop(chanindex
*cip
, regop
*ro
);
51 unsigned long cf_gethash(nick
*np
, int type
);
53 int cf_storechanfix(void);
54 int cf_loadchanfix(void);
57 #define min(a,b) ((a > b) ? b : a)
60 cfext
= registerchanext("chanfix");
61 cfnext
= registernickext("chanfix");
63 if (cfext
< 0 || cfnext
< 0) {
64 Error("chanfix", ERR_ERROR
, "Couldn't register channel and/or nick extension");
67 schedulerecurring(time(NULL
), 0, CFSAMPLEINTERVAL
, &cfsched_dosample
, NULL
);
68 schedulerecurring(time(NULL
), 0, CFEXPIREINTERVAL
, &cfsched_doexpire
, NULL
);
69 schedulerecurring(time(NULL
), 0, CFAUTOSAVEINTERVAL
, &cfsched_dosave
, NULL
);
71 registercontrolhelpcmd("cfdebug", NO_DEVELOPER
, 1, &cfcmd_debug
, "Display Debug Information on chanfix data for channel");
72 registercontrolhelpcmd("cfhistogram", NO_DEVELOPER
, 1, &cfcmd_debughistogram
, "Display Debug Histogram of chanfix data for channel");
74 registercontrolhelpcmd("cfsample", NO_DEVELOPER
, 1, &cfcmd_debugsample
, "DEBUG Command - must not be loaded on live instances");
75 registercontrolhelpcmd("cfexpire", NO_DEVELOPER
, 1, &cfcmd_debugexpire
, "DEBUG Command - must not be loaded on live instances");
77 registercontrolhelpcmd("chanopstat", NO_OPER
, 1, &cfcmd_chanopstat
, "Shows chanop statistics for a given channel");
78 registercontrolhelpcmd("chanoplist", NO_OPER
, 1, &cfcmd_chanoplist
, "Shows lists of known chanops, including scores");
80 registercontrolhelpcmd("chanfix", NO_OPER
, 1, &cfcmd_chanfix
, "Perform a chanfix on a channel to op known users only");
81 registercontrolhelpcmd("showregs", NO_OPER
, 1, &cfcmd_showregs
, "Show regular ops known on a channel (including services)");
83 /* should we disable this in the 'final' build? */
84 /* registercontrolcmd("requestop", 0, 2, &cfcmd_requestop); */
86 registercontrolhelpcmd("cfsave", NO_DEVELOPER
, 0, &cfcmd_save
, "Force save of chanfix data");
87 registercontrolhelpcmd("cfload", NO_DEVELOPER
, 0, &cfcmd_load
, "Force load of chanfix data");
90 registerhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
91 registerhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
92 registerhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
93 registerhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
96 registerhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
97 registerhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
106 if (cffailedinit
== 0) {
107 deleteschedule(NULL
, &cfsched_dosample
, NULL
);
108 deleteschedule(NULL
, &cfsched_doexpire
, NULL
);
109 deleteschedule(NULL
, &cfsched_dosave
, NULL
);
115 deregistercontrolcmd("cfdebug", &cfcmd_debug
);
116 deregistercontrolcmd("cfhistogram", &cfcmd_debughistogram
);
118 deregistercontrolcmd("cfsample", &cfcmd_debugsample
);
119 deregistercontrolcmd("cfexpire", &cfcmd_debugexpire
);
121 deregistercontrolcmd("chanopstat", &cfcmd_chanopstat
);
122 deregistercontrolcmd("chanoplist", &cfcmd_chanoplist
);
123 deregistercontrolcmd("chanfix", &cfcmd_chanfix
);
124 deregistercontrolcmd("showregs", &cfcmd_showregs
);
126 // deregistercontrolcmd("requestop", &cfcmd_requestop);
128 deregistercontrolcmd("cfsave", &cfcmd_save
);
129 deregistercontrolcmd("cfload", &cfcmd_load
);
132 deregisterhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
133 deregisterhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
134 deregisterhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
135 deregisterhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
138 deregisterhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
139 deregisterhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
143 releasechanext(cfext
);
147 releasenickext(cfnext
);
151 int cfcmd_debug(void *source
, int cargc
, char **cargv
) {
152 nick
*np
= (nick
*)source
;
159 controlreply(np
, "Syntax: cfdebug <#channel>");
164 cip
= findchanindex(cargv
[0]);
167 controlreply(np
, "No such channel.");
171 controlreply(np
, "Found channel %s. Retrieving chanfix information...", cargv
[0]);
173 cf
= cip
->exts
[cfext
];
176 controlreply(np
, "No chanfix information for %s", cargv
[0]);
180 controlreply(np
, "Found chanfix information. Dumping...");
182 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
183 ro
= ((regop
**)cf
->regops
.content
)[i
];
185 controlreply(np
, "%d. type: %s hash: 0x%lx lastopped: %lu uh: %s score: %d",
186 i
+ 1, ro
->type
== CFACCOUNT
? "CFACCOUNT" : "CFHOST", ro
->hash
,
187 ro
->lastopped
, ro
->uh
? ro
->uh
->content
: "(unknown)", ro
->score
);
190 controlreply(np
, "Done.");
195 int cfcmd_debughistogram(void *source
, int cargc
, char **cargv
) {
196 nick
*np
= (nick
*)source
;
200 int histogram
[10001]; /* 625 (lines) * 16 (columns/line) + 1 (for histogram[0]) */
202 for (i
= 0; i
< 10001; i
++)
205 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
206 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
207 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
208 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
209 score
= ((regop
**)cf
->regops
.content
)[a
]->score
;
218 controlreply(np
, "--- Histogram of chanfix scores");
220 for (i
= 1; i
< 10001; i
+= 16) {
221 controlreply(np
, "%6d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
222 i
, histogram
[i
], histogram
[i
+1], histogram
[i
+2], histogram
[i
+3], histogram
[i
+4],
223 histogram
[i
+5], histogram
[i
+6], histogram
[i
+7], histogram
[i
+8], histogram
[i
+9],
224 histogram
[i
+10], histogram
[i
+11], histogram
[i
+12], histogram
[i
+13], histogram
[i
+14],
228 controlreply(np
, "--- End of histogram");
233 int cfcmd_debugsample(void *source
, int cargc
, char **cargv
) {
234 cfsched_dosample(NULL
);
236 controlreply((nick
*)source
, "Done.");
241 int cfcmd_debugexpire(void *source
, int cargc
, char **cargv
) {
242 cfsched_doexpire(NULL
);
244 controlreply((nick
*)source
, "Done.");
249 /* used for qsorting int arrays */
250 int cmpint(const void *a
, const void *b
) {
262 /* used for qsorting regop* arrays */
263 int cmpregop(const void *a
, const void *b
) {
264 regop
*p
= *(regop
**)a
;
265 regop
*q
= *(regop
**)b
;
267 if (p
->score
> q
->score
)
269 else if (p
->score
< q
->score
)
275 int cf_getsortedregops(chanfix
*cf
, int max
, regop
**list
) {
281 qsort(cf
->regops
.content
, cf
->regops
.cursi
, sizeof(regop
*), cmpregop
);
283 for (i
= 0; i
< min(max
, cf
->regops
.cursi
); i
++) {
284 list
[i
] = ((regop
**)cf
->regops
.content
)[i
];
290 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
) {
291 nick
*np
= (nick
*)source
;
301 controlreply(np
, "Syntax: chanopstat <#channel>");
306 cp
= findchannel(cargv
[0]);
309 controlreply(np
, "No such channel.");
314 cf
= cp
->index
->exts
[cfext
];
317 controlreply(np
, "No chanfix information for %s", cargv
[0]);
323 count
= cf_getsortedregops(cf
, 10, rolist
);
324 controlreply(np
, "Scores of \"best ops\" on %s are:", cargv
[0]);
326 for (i
=0;i
<count
;i
++) {
327 controlreply(np
, " %d", rolist
[i
]->score
);
331 scores
= (int*)malloc(sizeof(int) * cp
->users
->hashsize
);
335 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
336 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
337 np2
= getnickbynumeric(cp
->users
->content
[a
]);
339 ro
= cf_findregop(np2
, cp
->index
, CFACCOUNT
| CFHOST
);
342 scores
[i
++] = ro
->score
;
346 qsort(scores
, i
, sizeof(int), &cmpint
);
347 controlreply(np
, "Scores of current ops on %s are:", cargv
[0]);
349 for (a
=0;a
<min(i
,20);a
++) {
353 controlreply(np
, " %d", scores
[a
]);
357 controlreply(np
, "Done.");
362 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
) {
363 nick
*np
= (nick
*)source
;
375 controlreply(np
, "Syntax: chanoplist <#channel>");
380 cip
= findchanindex(cargv
[0]);
383 controlreply(np
, "No such channel.");
388 cf
= cip
->exts
[cfext
];
391 controlreply(np
, "No chanfix information for %s", cargv
[0]);
396 count
= cf_getsortedregops(cf
, 50, rolist
);
398 controlreply(np
, "Pos Score Type User/Last seen");
400 for (i
=0;i
<count
;i
++) {
401 np2
= cf_findnick(rolist
[i
], cip
);
404 hand
= getnumerichandlefromchanhash(cip
->channel
->users
, np2
->numeric
);
406 /* hand should be non-null in all cases */
408 controlreply(np
, "%3d %5d %-7s %1s%-16s %s@%s (%s)", i
+ 1, rolist
[i
]->score
,
409 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host",
410 (*hand
& CUMODE_OP
) ? "@" : "", np2
->nick
, np2
->ident
,
411 np2
->host
->name
->content
, np2
->realname
->name
->content
);
413 ct
= rolist
[i
]->lastopped
;
415 strftime(date
,sizeof(date
),"%d-%m-%Y %H:%M:%S",tm
);
416 controlreply(np
, "%3d %5d %-7s %1s%-16s %s Last seen: %s", i
+ 1, rolist
[i
]->score
,
417 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host", "", "!NONE!",
418 rolist
[i
]->uh
? rolist
[i
]->uh
->content
: "!UNKNOWN!", date
);
422 controlreply(np
, "Done.");
427 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
) {
428 nick
*np
= (nick
*)source
;
433 controlreply(np
, "Syntax: chanfix <#channel>");
438 cp
= findchannel(cargv
[0]);
441 controlreply(np
, "No such channel.");
446 if (sp_countsplitservers() > 0) {
447 controlreply(np
, "Chanfix cannot be used during a netsplit.");
452 ret
= cf_fixchannel(cp
);
456 controlreply(np
, "Channel was fixed.");
459 controlreply(np
, "Channel cannot be fixed: no chanfix information");
461 case CFX_FIXEDFEWOPS
:
462 controlreply(np
, "Channel was fixed but only a few ops could be found and reopped.");
472 int cfcmd_showregs(void *source
, int cargc
, char **cargv
) {
473 nick
*np
= (nick
*)source
;
481 controlreply(np
, "Syntax: showregs <#channel>");
486 cp
= findchannel(cargv
[0]);
489 controlreply(np
, "No such channel.");
494 cf
= cp
->index
->exts
[cfext
];
497 controlreply(np
, "No chanfix information for %s", cargv
[0]);
504 for(i
=0;i
<cp
->users
->hashsize
;i
++) {
505 if(cp
->users
->content
[i
] != nouser
) {
506 np2
= getnickbynumeric(cp
->users
->content
[i
]);
508 if (IsService(np2
)) {
509 controlreply(np
, "%s (service)", np2
->nick
);
515 /* now get a sorted list of regops */
516 ops
= cf_getsortedregops(cf
, 50, rolist
);
518 /* and show some of them */
519 for (i
=0;i
<ops
;i
++) {
520 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< rolist
[0]->score
/ 2 || rolist
[i
]->score
< CFMINSCORE
)
523 np2
= cf_findnick(rolist
[i
], cp
->index
);
526 controlreply(np
, "%s (%s)", np2
->nick
, rolist
[i
]->type
== CFACCOUNT
? "account" : "host");
532 controlreply((nick
*)source
, "--- End of list: %d users listed", count
);
537 int cfcmd_requestop(void *source
, int cargc
, char **cargv
) {
538 nick
*np
= (nick
*)source
;
546 controlreply(np
, "Syntax: requestop <#channel> [nick]");
551 cp
= findchannel(cargv
[0]);
554 controlreply(np
, "No such channel.");
560 user
= getnickbynick(cargv
[1]);
563 controlreply(np
, "No such user.");
569 hand
= getnumerichandlefromchanhash(cp
->users
, user
->numeric
);
572 controlreply(np
, "User %s is not on channel %s.", user
->nick
, cargv
[0]);
577 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
578 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
579 controlreply(np
, "There are ops on channel %s. This command can only be"
580 " used if there are no ops.", cargv
[0]);
586 if (sp_countsplitservers() > 0) {
587 controlreply(np
, "One or more servers are currently split. Wait until the"
588 " netsplit is over and try again.");
593 if (cf_wouldreop(user
, cp
)) {
594 localsetmodeinit(&changes
, cp
, mynick
);
595 localdosetmode_nick(&changes
, user
, MC_OP
);
596 localsetmodeflush(&changes
, 1);
598 controlreply(np
, "Chanfix opped you on the specified channel.");
600 ret
= cf_fixchannel(cp
);
602 if (ret
== CFX_NOUSERSAVAILABLE
)
603 controlreply(np
, "Chanfix knows regular ops for that channel. They will"
604 " be opped when they return.");
606 controlreply(np
, "Chanfix has opped known ops for that channel.");
612 int cfcmd_save(void *source
, int cargc
, char **cargv
) {
613 nick
*np
= (nick
*)source
;
616 count
= cf_storechanfix();
618 controlreply(np
, "%d chanfix records saved.", count
);
629 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
630 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
631 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
632 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
633 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
634 free(((regop
**)cf
->regops
.content
)[a
]);
637 array_free(&(((chanfix
*)cip
->exts
[cfext
])->regops
));
640 free(cip
->exts
[cfext
]);
641 cip
->exts
[cfext
] = NULL
;
646 int cfcmd_load(void *source
, int cargc
, char **cargv
) {
647 nick
*np
= (nick
*)source
;
650 count
= cf_loadchanfix();
652 controlreply(np
, "%d chanfix records loaded.", count
);
657 int cf_hasauthedcloneonchan(nick
*np
, channel
*cp
) {
661 for (jp
= np
->host
->nicks
; jp
; jp
= jp
->nextbyhost
) {
662 if (!IsAccount(jp
) || jp
->numeric
== np
->numeric
)
665 hand
= getnumerichandlefromchanhash(cp
->users
, jp
->numeric
);
667 if (hand
&& (*hand
& CUMODE_OP
) && strcmp(np
->ident
, jp
->ident
) == 0)
674 void cfsched_dosample(void *arg
) {
675 int i
,a
,now
,cfscore
,cfnewro
,cfuhost
,diff
;
681 struct timeval start
;
683 char buf
[USERLEN
+1+HOSTLEN
+1];
687 cfuhost
= cfscore
= cfnewro
= 0;
689 if (sp_countsplitservers() > CFMAXSPLITSERVERS
)
692 gettimeofday(&start
, NULL
);
694 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
695 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
696 cf
= (chanfix
*)cip
->exts
[cfext
];
699 if (!cp
|| cp
->users
->totalusers
< CFMINUSERS
)
702 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
703 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
704 np
= getnickbynumeric(cp
->users
->content
[a
]);
711 roh
= ro
= cf_findregop(np
, cip
, CFACCOUNT
| CFHOST
);
713 if ((ro
== NULL
|| (IsAccount(np
) && ro
->type
== CFHOST
)) &&
714 !cf_hasauthedcloneonchan(np
, cp
)) {
715 ro
= cf_createregop(np
, cip
);
719 /* lastopped == now if the user has clones, we obviously
720 * don't want to give them points in this case */
721 if (ro
&& ro
->lastopped
!= now
) {
722 if (ro
->type
!= CFHOST
|| !cf_hasauthedcloneonchan(np
, cp
)) {
727 /* merge any matching CFHOST records */
728 if (roh
&& roh
->type
== CFHOST
&& ro
->type
== CFACCOUNT
) {
730 ro
->score
+= roh
->score
;
732 cf_deleteregop(cip
, roh
);
735 /* store the user's account/host if we have to */
736 if (ro
->uh
== NULL
&& ro
->score
>= CFMINSCOREUH
) {
737 if (ro
->type
== CFACCOUNT
)
738 ro
->uh
= getsstring(np
->authname
, ACCOUNTLEN
);
740 snprintf(buf
, sizeof(buf
), "%s@%s", np
->ident
, np
->host
->name
->content
);
741 roh
->uh
= getsstring(buf
, USERLEN
+1+HOSTLEN
);
754 cp
= findchannel("#qnet.chanfix");
757 gettimeofday(&end
, NULL
);
759 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
760 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
762 sendmessagetochannel(mynick
, cp
, "sampled chanfix scores, assigned %d new"
763 " points, %d new regops, %d user@hosts added, deltaT: %dms", cfscore
,
764 cfnewro
, cfuhost
, diff
);
768 void cfsched_doexpire(void *arg
) {
773 int i
,a
,cfscore
,cfregop
,cffreeuh
,diff
;
776 struct timeval start
;
780 cffreeuh
= cfscore
= cfregop
= 0;
782 gettimeofday(&start
, NULL
);
783 currenttime
=getnettime();
785 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
786 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
787 cf
= (chanfix
*)cip
->exts
[cfext
];
790 rolist
= (regop
**)cf
->regops
.content
;
792 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
795 if (((currenttime
- ro
->lastopped
) > (2 * CFSAMPLEINTERVAL
)) && ro
->score
) {
800 if ((ro
->score
< CFMINSCOREUH
) && ro
->uh
) {
807 if (ro
->score
== 0 || ro
->lastopped
< (currenttime
- CFREMEMBEROPS
)) {
808 cf_deleteregop(cip
, ro
);
816 /* stolen from channel/channelindex.c */
817 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
818 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
819 /* CAREFUL: deleting items from chains you're walking is bad */
822 /* try to delete it if there's no chanfix* pointer */
823 if (cip
->exts
[cfext
] == NULL
)
824 releasechanindex(cip
);
828 cp
= findchannel("#qnet.chanfix");
831 gettimeofday(&end
, NULL
);
833 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
834 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
836 sendmessagetochannel(mynick
, cp
, "expired chanfix scores, purged %d points,"
837 " scrapped %6d regops, %d user@hosts freed, deltaT: %dms", cfscore
,
838 cfregop
, cffreeuh
, diff
);
843 void cfsched_dosave(void *arg
) {
848 void cfhook_autofix(int hook
, void *arg
) {
850 void **args
= (void**)arg
;
853 if (hook
== HOOK_CHANNEL_DEOPPED
|| hook
== HOOK_CHANNEL_PART
||
854 hook
== HOOK_CHANNEL_KICK
|| hook
== HOOK_CHANNEL_JOIN
) {
857 if (sp_countsplitservers() > 0)
860 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
861 if (cp
->users
->content
[a
] != nouser
) {
864 if (cp
->users
->content
[a
] & CUMODE_OP
)
869 /* don't fix small channels.. it's inaccurate and
870 * they could just cycle the channel */
874 cf_fixchannel((channel
*)args
[0]);
879 void cfhook_statsreport(int hook
, void *arg
) {
881 int i
,a
,rc
,mc
,memory
;
886 memory
= rc
= mc
= 0;
888 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
889 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
890 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
891 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
892 memory
+= sizeof(regop
) + sizeof(regop
*);
894 if (((regop
**)cf
->regops
.content
)[a
]->uh
)
895 memory
+= sizeof(sstring
) + strlen(((regop
**)cf
->regops
.content
)[a
]->uh
->content
) + 1;
900 memory
+= sizeof(chanfix
);
907 snprintf(buf
, sizeof(buf
), "Chanfix : %6d registered ops, %9d monitored channels. %9d"
908 " kbytes of memory used", rc
, mc
, (memory
/ 1024));
909 triggerhook(HOOK_CORE_STATSREPLY
, buf
);
913 void cfhook_auth(int hook
, void *arg
) {
914 nick
*np
= (nick
*)arg
;
916 /* Invalidate the user's hash */
917 np
->exts
[cfnext
] = NULL
;
919 /* Calculate the new hash */
920 cf_gethash(np
, CFACCOUNT
);
923 /* Returns the hash of a specific user (np), type can be either CFACCOUNT,
924 CFHOST or both (binary or'd values). cf_gethash will also cache the user's
925 hash in a nick extension */
926 unsigned long cf_gethash(nick
*np
, int type
) {
927 char buf
[USERLEN
+1+HOSTLEN
+1];
930 if (IsAccount(np
) && (type
& CFACCOUNT
)) {
931 if (np
->exts
[cfnext
] == NULL
) {
932 np
->exts
[cfnext
] = (void *)crc32(np
->authname
);
935 return (unsigned long)np
->exts
[cfnext
];
936 } else if (type
== CFACCOUNT
)
937 return 0; /* this should not happen */
940 if (!IsAccount(np
) && np
->exts
[cfnext
])
941 return (unsigned long)np
->exts
[cfnext
];
943 snprintf(buf
, sizeof(buf
), "%s@%s", np
->ident
, np
->host
->name
->content
);
946 /* if the user is not authed, update the hash */
947 if (!IsAccount(np
)) {
948 np
->exts
[cfnext
] = (void *)hash
;
955 return 0; /* should not happen */
958 /* This seems to be a lot faster than using sprintf */
959 int cf_equhost(const char *uhost
, const char *user
, const char *host
) {
960 char *p
= strchr(uhost
, '@');
962 /* We assume the uhost contains a @ - which it should do in all cases */
966 if (ircd_strncmp(uhost
, user
, p
- uhost
) == 0 && ircd_strcmp(p
+ 1, host
) == 0)
972 /* Why do we actually store the users' real hosts/accounts instead of hashes?
973 * - it allows operators to see the hosts/accounts in 'chanoplist' even if the
974 * users are not online
975 * - it avoids hash collisions (could be avoided with md5/sha1/etc.)
977 int cf_cmpregopnick(regop
*ro
, nick
*np
) {
978 if (ro
->uh
!= NULL
) {
979 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
980 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
) &&
981 strcmp(ro
->uh
->content
, np
->authname
) == 0);
983 return (ro
->hash
== cf_gethash(np
, CFHOST
) &&
984 cf_equhost(ro
->uh
->content
, np
->ident
, np
->host
->name
->content
));
987 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
988 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
));
990 return (ro
->hash
== cf_gethash(np
, CFHOST
));
994 nick
*cf_findnick(regop
*ro
, chanindex
*cip
) {
995 chanfix
*cf
= cip
->exts
[cfext
];
996 channel
*cp
= cip
->channel
;
1000 if (cf
== NULL
|| cp
== NULL
)
1003 if (ro
->type
== CFACCOUNT
) {
1004 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1005 if(cp
->users
->content
[a
] != nouser
) {
1006 np2
= getnickbynumeric(cp
->users
->content
[a
]);
1008 if (!IsAccount(np2
))
1011 if (cf_cmpregopnick(ro
, np2
))
1017 if (ro
->type
== CFHOST
) {
1018 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1019 if(cp
->users
->content
[a
] != nouser
) {
1020 np2
= getnickbynumeric(cp
->users
->content
[a
]);
1022 if (cf_cmpregopnick(ro
, np2
))
1031 regop
*cf_findregop(nick
*np
, chanindex
*cip
, int type
) {
1032 chanfix
*cf
= cip
->exts
[cfext
];
1039 if (IsAccount(np
) && (type
& CFACCOUNT
))
1044 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
1045 ro
= ((regop
**)cf
->regops
.content
)[i
];
1047 if (ro
->type
== ty
&& cf_cmpregopnick(ro
, np
))
1051 /* try using the uhost if we didn't find a user with the right account */
1052 if (ty
== CFACCOUNT
&& (type
& CFHOST
))
1053 return cf_findregop(np
, cip
, CFHOST
);
1060 regop
*cf_createregop(nick
*np
, chanindex
*cip
) {
1061 chanfix
*cf
= cip
->exts
[cfext
];
1066 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1069 array_init(&(cf
->regops
), sizeof(regop
*));
1071 cip
->exts
[cfext
] = cf
;
1074 slot
= array_getfreeslot(&(cf
->regops
));
1076 rolist
= (regop
**)cf
->regops
.content
;
1078 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1085 rolist
[slot
]->type
= type
;
1086 rolist
[slot
]->hash
= cf_gethash(np
, type
);
1087 rolist
[slot
]->uh
= NULL
;
1088 rolist
[slot
]->lastopped
= 0;
1089 rolist
[slot
]->score
= 0;
1091 return rolist
[slot
];
1094 void cf_deleteregop(chanindex
*cip
, regop
*ro
) {
1095 chanfix
*cf
= cip
->exts
[cfext
];
1101 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1102 if (((regop
**)cf
->regops
.content
)[a
] == ro
) {
1103 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
1104 free(((regop
**)cf
->regops
.content
)[a
]);
1105 array_delslot(&(cf
->regops
), a
);
1109 /* get rid of chanfix* if there are no more regops */
1110 if (cf
->regops
.cursi
== 0) {
1111 array_free(&(cf
->regops
));
1113 cip
->exts
[cfext
] = NULL
;
1115 /* we could try to free the chanindex* here
1116 but that would make cfsched_dosample a lot more
1121 int cf_fixchannel(channel
*cp
) {
1124 modechanges changes
;
1127 chanfix
*cf
= cp
->index
->exts
[cfext
];
1130 return CFX_NOCHANFIX
;
1132 localsetmodeinit(&changes
, cp
, mynick
);
1136 /* reop services first and deop other users */
1137 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1138 if(cp
->users
->content
[a
] != nouser
) {
1139 np
= getnickbynumeric(cp
->users
->content
[a
]);
1141 if (IsService(np
) && (np
->nick
[1] == '\0')) {
1142 localdosetmode_nick(&changes
, np
, MC_OP
);
1145 localdosetmode_nick(&changes
, np
, MC_DEOP
);
1149 /* don't reop users if we've already opped some services */
1152 localsetmodeflush(&changes
, 1);
1157 /* now get a sorted list of regops */
1158 ops
= cf_getsortedregops(cf
, 50, rolist
);
1160 /* and op some of them */
1161 for (i
=0;i
<ops
;i
++) {
1162 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< (rolist
[0]->score
/ 2))
1165 if (rolist
[i
]->score
< CFMINSCORE
&& i
!= 0 )
1168 np
= cf_findnick(rolist
[i
], cp
->index
);
1170 /* only if it's not a service, so we don't screw up 'count' */
1171 if (np
&& !(IsService(np
) && np
->nick
[1] == '\0')) {
1172 localdosetmode_nick(&changes
, np
, MC_OP
);
1178 localsetmodeflush(&changes
, 1);
1180 if (count
== CFMAXOPS
)
1182 else if (count
== 0)
1183 return CFX_NOUSERSAVAILABLE
;
1185 return CFX_FIXEDFEWOPS
;
1188 int cf_storechanfix(void) {
1195 int a
, i
, count
= 0;
1197 snprintf(dstfile
, sizeof(dstfile
), "%s.%d", CFSTORAGE
, CFSAVEFILES
);
1200 for (i
= CFSAVEFILES
; i
> 0; i
--) {
1201 snprintf(srcfile
, sizeof(srcfile
), "%s.%i", CFSTORAGE
, i
- 1);
1202 snprintf(dstfile
, sizeof(dstfile
), "%s.%i", CFSTORAGE
, i
);
1203 rename(srcfile
, dstfile
);
1206 snprintf(srcfile
, sizeof(srcfile
), "%s.0", CFSTORAGE
);
1207 cfdata
= fopen(srcfile
, "w");
1212 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
1213 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
1214 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
1215 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1216 ro
= ((regop
**)cf
->regops
.content
)[a
];
1219 fprintf(cfdata
, "%s %d %lu %lu %d %s\n", cip
->name
->content
,
1220 ro
->type
, ro
->hash
, ro
->lastopped
, ro
->score
, ro
->uh
->content
);
1222 fprintf(cfdata
, "%s %d %lu %lu %d\n", cip
->name
->content
,
1223 ro
->type
, ro
->hash
, ro
->lastopped
, ro
->score
);
1235 /* channel type hash lastopped score host */
1236 int cf_parseline(char *line
) {
1241 char chan
[CHANNELLEN
+1];
1245 char host
[USERLEN
+1+HOSTLEN
+1];
1248 count
= sscanf(line
, "%s %d %lu %lu %d %s", chan
, &type
, &hash
, &lastopped
, &score
, host
);
1251 return 0; /* invalid chanfix record */
1253 cip
= findorcreatechanindex(chan
);
1255 cf
= cip
->exts
[cfext
];
1258 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1261 array_init(&(cf
->regops
), sizeof(regop
*));
1263 cip
->exts
[cfext
] = cf
;
1266 slot
= array_getfreeslot(&(cf
->regops
));
1268 rolist
= (regop
**)cf
->regops
.content
;
1270 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1272 rolist
[slot
]->type
= type
;
1273 rolist
[slot
]->hash
= hash
;
1274 rolist
[slot
]->lastopped
= lastopped
;
1275 rolist
[slot
]->score
= score
;
1278 rolist
[slot
]->uh
= getsstring(host
, USERLEN
+1+HOSTLEN
);
1280 rolist
[slot
]->uh
= NULL
;
1285 int cf_loadchanfix(void) {
1293 snprintf(srcfile
, sizeof(srcfile
), "%s.0", CFSTORAGE
);
1294 cfdata
= fopen(srcfile
, "r");
1301 while (!feof(cfdata
)) {
1302 if (fgets(line
, sizeof(line
), cfdata
) == NULL
)
1305 if (line
[strlen(line
) - 1] == '\n')
1306 line
[strlen(line
) - 1] = '\0';
1308 if (line
[strlen(line
) - 1] == '\r')
1309 line
[strlen(line
) - 1] = '\0';
1311 if (line
[0] != '\0') {
1312 if (cf_parseline(line
))
1322 /* functions for users of this module */
1323 chanfix
*cf_findchanfix(chanindex
*cip
) {
1324 return cip
->exts
[cfext
];
1327 int cf_wouldreop(nick
*np
, channel
*cp
) {
1331 chanfix
*cf
= cp
->index
->exts
[cfext
];
1334 return 1; /* too bad, we can't do anything about it */
1336 ro
= cf_findregop(np
, cp
->index
, CFACCOUNT
| CFHOST
);
1341 rolist
= (regop
**)cf
->regops
.content
;
1343 for (i
=0; i
<cf
->regops
.cursi
; i
++)
1344 if (rolist
[i
]->score
> topscore
)
1345 topscore
= rolist
[i
]->score
;
1347 if (ro
->score
> topscore
/ 2 && ro
->score
> CFMINSCORE
)