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"
27 static int cffailedinit
;
29 /* user accessible commands */
30 int cfcmd_debug(void *source
, int cargc
, char **cargv
);
31 int cfcmd_debughistogram(void *source
, int cargc
, char **cargv
);
32 int cfcmd_debugsample(void *source
, int cargc
, char **cargv
);
33 int cfcmd_debugexpire(void *source
, int cargc
, char **cargv
);
34 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
);
35 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
);
36 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
);
37 int cfcmd_showregs(void *source
, int cargc
, char **cargv
);
38 int cfcmd_requestop(void *source
, int cargc
, char **cargv
);
39 int cfcmd_save(void *source
, int cargc
, char **cargv
);
40 int cfcmd_load(void *source
, int cargc
, char **cargv
);
42 /* scheduled events */
43 void cfsched_dosample(void *arg
);
44 void cfsched_doexpire(void *arg
);
45 void cfsched_dosave(void *arg
);
48 void cfhook_autofix(int hook
, void *arg
);
49 void cfhook_statsreport(int hook
, void *arg
);
50 void cfhook_auth(int hook
, void *arg
);
52 /* helper functions */
53 regop
*cf_createregop(nick
*np
, chanindex
*cip
);
54 void cf_deleteregop(chanindex
*cip
, regop
*ro
);
55 unsigned long cf_gethash(nick
*np
, int type
);
57 int cf_storechanfix(void);
58 int cf_loadchanfix(void);
61 #define min(a,b) ((a > b) ? b : a)
64 cfext
= registerchanext("chanfix");
65 cfnext
= registernickext("chanfix");
67 if (cfext
< 0 || cfnext
< 0) {
68 Error("chanfix", ERR_ERROR
, "Couldn't register channel and/or nick extension");
73 schedulerecurring(time(NULL
), 0, CFSAMPLEINTERVAL
, &cfsched_dosample
, NULL
);
74 schedulerecurring(time(NULL
), 0, CFEXPIREINTERVAL
, &cfsched_doexpire
, NULL
);
75 schedulerecurring(time(NULL
), 0, CFAUTOSAVEINTERVAL
, &cfsched_dosave
, NULL
);
77 registercontrolhelpcmd("cfdebug", NO_DEVELOPER
, 1, &cfcmd_debug
, "Display Debug Information on chanfix data for channel");
78 registercontrolhelpcmd("cfhistogram", NO_DEVELOPER
, 1, &cfcmd_debughistogram
, "Display Debug Histogram of chanfix data for channel");
80 registercontrolhelpcmd("cfsample", NO_DEVELOPER
, 1, &cfcmd_debugsample
, "DEBUG Command - must not be loaded on live instances");
81 registercontrolhelpcmd("cfexpire", NO_DEVELOPER
, 1, &cfcmd_debugexpire
, "DEBUG Command - must not be loaded on live instances");
83 registercontrolhelpcmd("chanopstat", NO_OPER
, 1, &cfcmd_chanopstat
, "Shows chanop statistics for a given channel");
84 registercontrolhelpcmd("chanoplist", NO_OPER
, 1, &cfcmd_chanoplist
, "Shows lists of known chanops, including scores");
86 registercontrolhelpcmd("chanfix", NO_OPER
, 1, &cfcmd_chanfix
, "Perform a chanfix on a channel to op known users only");
87 registercontrolhelpcmd("showregs", NO_OPER
, 1, &cfcmd_showregs
, "Show regular ops known on a channel (including services)");
89 /* should we disable this in the 'final' build? */
90 /* registercontrolcmd("requestop", 0, 2, &cfcmd_requestop); */
92 registercontrolhelpcmd("cfsave", NO_DEVELOPER
, 0, &cfcmd_save
, "Force save of chanfix data");
93 registercontrolhelpcmd("cfload", NO_DEVELOPER
, 0, &cfcmd_load
, "Force load of chanfix data");
96 registerhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
97 registerhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
98 registerhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
99 registerhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
102 registerhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
103 registerhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
111 if (cffailedinit
== 0)
114 deleteschedule(NULL
, &cfsched_dosample
, NULL
);
115 deleteschedule(NULL
, &cfsched_doexpire
, NULL
);
116 deleteschedule(NULL
, &cfsched_dosave
, NULL
);
122 deregistercontrolcmd("cfdebug", &cfcmd_debug
);
123 deregistercontrolcmd("cfhistogram", &cfcmd_debughistogram
);
125 deregistercontrolcmd("cfsample", &cfcmd_debugsample
);
126 deregistercontrolcmd("cfexpire", &cfcmd_debugexpire
);
128 deregistercontrolcmd("chanopstat", &cfcmd_chanopstat
);
129 deregistercontrolcmd("chanoplist", &cfcmd_chanoplist
);
130 deregistercontrolcmd("chanfix", &cfcmd_chanfix
);
131 deregistercontrolcmd("showregs", &cfcmd_showregs
);
133 // deregistercontrolcmd("requestop", &cfcmd_requestop);
135 deregistercontrolcmd("cfsave", &cfcmd_save
);
136 deregistercontrolcmd("cfload", &cfcmd_load
);
139 deregisterhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
140 deregisterhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
141 deregisterhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
142 deregisterhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
145 deregisterhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
146 deregisterhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
149 releasechanext(cfext
);
152 releasenickext(cfnext
);
155 int cfcmd_debug(void *source
, int cargc
, char **cargv
) {
156 nick
*np
= (nick
*)source
;
165 cip
= findchanindex(cargv
[0]);
168 controlreply(np
, "No such channel.");
173 controlreply(np
, "Found channel %s. Retrieving chanfix information...", cargv
[0]);
175 cf
= cip
->exts
[cfext
];
178 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
];
292 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
) {
293 nick
*np
= (nick
*)source
;
305 cp
= findchannel(cargv
[0]);
308 controlreply(np
, "No such channel.");
313 cf
= cp
->index
->exts
[cfext
];
316 controlreply(np
, "No chanfix information for %s", cargv
[0]);
322 count
= cf_getsortedregops(cf
, 10, rolist
);
323 controlreply(np
, "Scores of \"best ops\" on %s are:", cargv
[0]);
325 for (i
=0;i
<count
;i
++)
326 controlreply(np
, " %d", rolist
[i
]->score
);
329 scores
= (int*)malloc(sizeof(int) * cp
->users
->hashsize
);
333 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
334 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
335 np2
= getnickbynumeric(cp
->users
->content
[a
]);
337 ro
= cf_findregop(np2
, cp
->index
, CFACCOUNT
| CFHOST
);
340 scores
[i
++] = ro
->score
;
344 qsort(scores
, i
, sizeof(int), &cmpint
);
345 controlreply(np
, "Scores of current ops on %s are:", cargv
[0]);
347 for (a
=0;a
<min(i
,20);a
++) {
351 controlreply(np
, " %d", scores
[a
]);
355 controlreply(np
, "Done.");
360 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
) {
361 nick
*np
= (nick
*)source
;
375 cip
= findchanindex(cargv
[0]);
378 controlreply(np
, "No such channel.");
383 cf
= cip
->exts
[cfext
];
386 controlreply(np
, "No chanfix information for %s", cargv
[0]);
391 count
= cf_getsortedregops(cf
, 50, rolist
);
393 controlreply(np
, "Pos Score Type User/Last seen");
395 for (i
=0;i
<count
;i
++) {
396 np2
= cf_findnick(rolist
[i
], cip
);
399 hand
= getnumerichandlefromchanhash(cip
->channel
->users
, np2
->numeric
);
401 /* hand should be non-null in all cases */
403 controlreply(np
, "%3d %5d %-7s %1s%-16s %s@%s (%s)", i
+ 1, rolist
[i
]->score
,
404 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host",
405 (*hand
& CUMODE_OP
) ? "@" : "", np2
->nick
, np2
->ident
,
406 np2
->host
->name
->content
, np2
->realname
->name
->content
);
408 ct
= rolist
[i
]->lastopped
;
410 strftime(date
,sizeof(date
),"%d-%m-%Y %H:%M:%S",tm
);
411 controlreply(np
, "%3d %5d %-7s %1s%-16s %s Last seen: %s", i
+ 1, rolist
[i
]->score
,
412 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host", "", "!NONE!",
413 rolist
[i
]->uh
? rolist
[i
]->uh
->content
: "!UNKNOWN!", date
);
417 controlreply(np
, "Done.");
422 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
) {
423 nick
*np
= (nick
*)source
;
430 cp
= findchannel(cargv
[0]);
433 controlreply(np
, "No such channel.");
438 if (sp_countsplitservers(SERVERTYPEFLAG_USER_STATE
) > 0) {
439 controlreply(np
, "Chanfix cannot be used during a netsplit.");
444 ret
= cf_fixchannel(cp
);
448 controlreply(np
, "Channel was fixed.");
451 controlreply(np
, "Channel cannot be fixed: no chanfix information");
453 case CFX_FIXEDFEWOPS
:
454 controlreply(np
, "Channel was fixed but only a few ops could be found and reopped.");
464 int cfcmd_showregs(void *source
, int cargc
, char **cargv
) {
465 nick
*np
= (nick
*)source
;
475 cp
= findchannel(cargv
[0]);
478 controlreply(np
, "No such channel.");
483 cf
= cp
->index
->exts
[cfext
];
486 controlreply(np
, "No chanfix information for %s", cargv
[0]);
493 for(i
=0;i
<cp
->users
->hashsize
;i
++) {
494 if(cp
->users
->content
[i
] != nouser
) {
495 np2
= getnickbynumeric(cp
->users
->content
[i
]);
497 if (IsService(np2
)) {
498 controlreply(np
, "%s (service)", np2
->nick
);
504 /* now get a sorted list of regops */
505 ops
= cf_getsortedregops(cf
, 50, rolist
);
507 /* and show some of them */
508 for (i
=0;i
<ops
;i
++) {
509 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< rolist
[0]->score
/ 2 || rolist
[i
]->score
< CFMINSCORE
)
512 np2
= cf_findnick(rolist
[i
], cp
->index
);
515 controlreply(np
, "%s (%s)", np2
->nick
, rolist
[i
]->type
== CFACCOUNT
? "account" : "host");
521 controlreply((nick
*)source
, "--- End of list: %d users listed", count
);
526 int cfcmd_requestop(void *source
, int cargc
, char **cargv
) {
527 nick
*np
= (nick
*)source
;
537 cp
= findchannel(cargv
[0]);
540 controlreply(np
, "No such channel.");
546 user
= getnickbynick(cargv
[1]);
549 controlreply(np
, "No such user.");
555 hand
= getnumerichandlefromchanhash(cp
->users
, user
->numeric
);
558 controlreply(np
, "User %s is not on channel %s.", user
->nick
, cargv
[0]);
563 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
564 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
565 controlreply(np
, "There are ops on channel %s. This command can only be"
566 " used if there are no ops.", cargv
[0]);
572 if (sp_countsplitservers(SERVERTYPEFLAG_USER_STATE
) > 0) {
573 controlreply(np
, "One or more servers are currently split. Wait until the"
574 " netsplit is over and try again.");
579 if (cf_wouldreop(user
, cp
)) {
580 localsetmodeinit(&changes
, cp
, mynick
);
581 localdosetmode_nick(&changes
, user
, MC_OP
);
582 localsetmodeflush(&changes
, 1);
584 controlreply(np
, "Chanfix opped you on the specified channel.");
586 ret
= cf_fixchannel(cp
);
588 if (ret
== CFX_NOUSERSAVAILABLE
)
589 controlreply(np
, "Chanfix knows regular ops for that channel. They will"
590 " be opped when they return.");
592 controlreply(np
, "Chanfix has opped known ops for that channel.");
598 int cfcmd_save(void *source
, int cargc
, char **cargv
) {
599 nick
*np
= (nick
*)source
;
602 count
= cf_storechanfix();
604 controlreply(np
, "%d chanfix records saved.", count
);
615 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
616 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
617 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
618 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
619 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
620 free(((regop
**)cf
->regops
.content
)[a
]);
623 array_free(&(((chanfix
*)cip
->exts
[cfext
])->regops
));
626 free(cip
->exts
[cfext
]);
627 cip
->exts
[cfext
] = NULL
;
632 int cfcmd_load(void *source
, int cargc
, char **cargv
) {
633 nick
*np
= (nick
*)source
;
636 count
= cf_loadchanfix();
638 controlreply(np
, "%d chanfix records loaded.", count
);
643 int cf_hasauthedcloneonchan(nick
*np
, channel
*cp
) {
647 for (jp
= np
->host
->nicks
; jp
; jp
= jp
->nextbyhost
) {
648 if (!IsAccount(jp
) || jp
->numeric
== np
->numeric
)
651 hand
= getnumerichandlefromchanhash(cp
->users
, jp
->numeric
);
653 if (hand
&& (*hand
& CUMODE_OP
) && strcmp(np
->ident
, jp
->ident
) == 0)
660 void cfsched_dosample(void *arg
) {
661 int i
,a
,now
,cfscore
,cfnewro
,diff
;
666 struct timeval start
;
671 cfscore
= cfnewro
= 0;
673 if (sp_countsplitservers(SERVERTYPEFLAG_USER_STATE
) > CFMAXSPLITSERVERS
)
676 gettimeofday(&start
, NULL
);
678 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
679 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
682 if (!cp
|| cp
->users
->totalusers
< CFMINUSERS
)
685 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
686 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
687 np
= getnickbynumeric(cp
->users
->content
[a
]);
697 roh
= ro
= cf_findregop(np
, cip
, CFACCOUNT
| CFHOST
);
699 if ((ro
== NULL
|| (IsAccount(np
) && ro
->type
== CFHOST
)) &&
700 !cf_hasauthedcloneonchan(np
, cp
)) {
701 ro
= cf_createregop(np
, cip
);
705 /* lastopped == now if the user has clones, we obviously
706 * don't want to give them points in this case */
707 if (!ro
|| ro
->lastopped
== now
)
710 if (ro
->type
!= CFHOST
|| !cf_hasauthedcloneonchan(np
, cp
)) {
715 /* merge any matching CFHOST records */
716 if (roh
&& roh
->type
== CFHOST
&& ro
->type
== CFACCOUNT
) {
718 ro
->score
+= roh
->score
;
720 cf_deleteregop(cip
, roh
);
729 cp
= findchannel("#qnet.chanfix");
732 gettimeofday(&end
, NULL
);
734 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
735 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
737 sendmessagetochannel(mynick
, cp
, "sampled chanfix scores, assigned %d new"
738 " points, %d new regops, deltaT: %dms", cfscore
,
743 void cfsched_doexpire(void *arg
) {
748 int i
,a
,cfscore
,cfregop
,diff
;
751 struct timeval start
;
755 cfscore
= cfregop
= 0;
757 gettimeofday(&start
, NULL
);
758 currenttime
=getnettime();
760 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
761 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
762 cf
= (chanfix
*)cip
->exts
[cfext
];
765 rolist
= (regop
**)cf
->regops
.content
;
767 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
770 if (((currenttime
- ro
->lastopped
) > (2 * CFSAMPLEINTERVAL
)) && ro
->score
) {
775 if (ro
->score
== 0 || ro
->lastopped
< (currenttime
- CFREMEMBEROPS
)) {
776 cf_deleteregop(cip
, ro
);
784 /* stolen from channel/channelindex.c */
785 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
786 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
787 /* CAREFUL: deleting items from chains you're walking is bad */
790 /* try to delete it if there's no chanfix* pointer */
791 if (cip
->exts
[cfext
] == NULL
)
792 releasechanindex(cip
);
796 cp
= findchannel("#qnet.chanfix");
799 gettimeofday(&end
, NULL
);
801 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
802 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
804 sendmessagetochannel(mynick
, cp
, "expired chanfix scores, purged %d points,"
805 " scrapped %6d regops, deltaT: %dms", cfscore
,
811 void cfsched_dosave(void *arg
) {
816 void cfhook_autofix(int hook
, void *arg
) {
818 void **args
= (void**)arg
;
821 if (hook
== HOOK_CHANNEL_DEOPPED
|| hook
== HOOK_CHANNEL_PART
||
822 hook
== HOOK_CHANNEL_KICK
|| hook
== HOOK_CHANNEL_JOIN
) {
825 if (sp_countsplitservers(SERVERTYPEFLAG_USER_STATE
) > 0)
828 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
829 if (cp
->users
->content
[a
] != nouser
) {
832 if (cp
->users
->content
[a
] & CUMODE_OP
)
837 /* don't fix small channels.. it's inaccurate and
838 * they could just cycle the channel */
842 cf_fixchannel((channel
*)args
[0]);
847 void cfhook_statsreport(int hook
, void *arg
) {
849 int i
,a
,rc
,mc
,memory
;
854 memory
= rc
= mc
= 0;
856 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
857 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
858 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
859 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
860 memory
+= sizeof(regop
) + sizeof(regop
*);
862 if (((regop
**)cf
->regops
.content
)[a
]->uh
)
863 memory
+= sizeof(sstring
) + strlen(((regop
**)cf
->regops
.content
)[a
]->uh
->content
) + 1;
868 memory
+= sizeof(chanfix
);
875 snprintf(buf
, sizeof(buf
), "Chanfix : %6d registered ops, %9d monitored channels. %9d"
876 " kbytes of memory used", rc
, mc
, (memory
/ 1024));
877 triggerhook(HOOK_CORE_STATSREPLY
, buf
);
881 void cfhook_auth(int hook
, void *arg
) {
882 nick
*np
= (nick
*)arg
;
884 /* Invalidate the user's hash */
885 np
->exts
[cfnext
] = NULL
;
887 /* Calculate the new hash */
888 cf_gethash(np
, CFACCOUNT
);
891 /* Returns the hash of a specific user (np), type can be either CFACCOUNT,
892 CFHOST or both (binary or'd values). cf_gethash will also cache the user's
893 hash in a nick extension */
894 unsigned long cf_gethash(nick
*np
, int type
) {
895 char buf
[USERLEN
+1+HOSTLEN
+1];
898 if (IsAccount(np
) && (type
& CFACCOUNT
)) {
899 if (np
->exts
[cfnext
] == NULL
) {
900 np
->exts
[cfnext
] = (void *)irc_crc32(np
->authname
);
903 return (unsigned long)np
->exts
[cfnext
];
904 } else if (type
== CFACCOUNT
)
905 return 0; /* this should not happen */
908 if (!IsAccount(np
) && np
->exts
[cfnext
])
909 return (unsigned long)np
->exts
[cfnext
];
911 snprintf(buf
, sizeof(buf
), "%s@%s", np
->ident
, np
->host
->name
->content
);
912 hash
= irc_crc32(buf
);
914 /* if the user is not authed, update the hash */
915 if (!IsAccount(np
)) {
916 np
->exts
[cfnext
] = (void *)hash
;
923 return 0; /* should not happen */
926 /* This seems to be a lot faster than using sprintf */
927 int cf_equhost(const char *uhost
, const char *user
, const char *host
) {
928 char *p
= strchr(uhost
, '@');
930 /* We assume the uhost contains a @ - which it should do in all cases */
934 if (ircd_strncmp(uhost
, user
, p
- uhost
) == 0 && ircd_strcmp(p
+ 1, host
) == 0)
940 /* Why do we actually store the users' real hosts/accounts instead of hashes?
941 * - it allows operators to see the hosts/accounts in 'chanoplist' even if the
942 * users are not online
943 * - it avoids hash collisions (could be avoided with md5/sha1/etc.)
945 int cf_cmpregopnick(regop
*ro
, nick
*np
) {
946 if (ro
->uh
!= NULL
) {
947 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
948 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
) &&
949 strcmp(ro
->uh
->content
, np
->authname
) == 0);
951 return (ro
->hash
== cf_gethash(np
, CFHOST
) &&
952 cf_equhost(ro
->uh
->content
, np
->ident
, np
->host
->name
->content
));
955 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
956 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
));
958 return (ro
->hash
== cf_gethash(np
, CFHOST
));
962 nick
*cf_findnick(regop
*ro
, chanindex
*cip
) {
963 chanfix
*cf
= cip
->exts
[cfext
];
964 channel
*cp
= cip
->channel
;
968 if (cf
== NULL
|| cp
== NULL
)
971 if (ro
->type
== CFACCOUNT
) {
972 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
973 if(cp
->users
->content
[a
] != nouser
) {
974 np2
= getnickbynumeric(cp
->users
->content
[a
]);
979 if (cf_cmpregopnick(ro
, np2
))
985 if (ro
->type
== CFHOST
) {
986 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
987 if(cp
->users
->content
[a
] != nouser
) {
988 np2
= getnickbynumeric(cp
->users
->content
[a
]);
990 if (cf_cmpregopnick(ro
, np2
))
999 regop
*cf_findregop(nick
*np
, chanindex
*cip
, int type
) {
1000 chanfix
*cf
= cip
->exts
[cfext
];
1007 if (IsAccount(np
) && (type
& CFACCOUNT
))
1012 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
1013 ro
= ((regop
**)cf
->regops
.content
)[i
];
1015 if (ro
->type
== ty
&& cf_cmpregopnick(ro
, np
))
1019 /* try using the uhost if we didn't find a user with the right account */
1020 if (ty
== CFACCOUNT
&& (type
& CFHOST
))
1021 return cf_findregop(np
, cip
, CFHOST
);
1028 regop
*cf_createregop(nick
*np
, chanindex
*cip
) {
1029 chanfix
*cf
= cip
->exts
[cfext
];
1032 char buf
[USERLEN
+1+HOSTLEN
+1];
1035 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1038 array_init(&(cf
->regops
), sizeof(regop
*));
1040 cip
->exts
[cfext
] = cf
;
1043 slot
= array_getfreeslot(&(cf
->regops
));
1045 rolist
= (regop
**)cf
->regops
.content
;
1047 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1049 if (IsAccount(np
)) {
1051 rolist
[slot
]->uh
= getsstring(np
->authname
, ACCOUNTLEN
);
1055 snprintf(buf
, sizeof(buf
), "%s@%s", np
->ident
, np
->host
->name
->content
);
1056 rolist
[slot
]->uh
= getsstring(buf
, USERLEN
+1+HOSTLEN
);
1059 rolist
[slot
]->type
= type
;
1060 rolist
[slot
]->hash
= cf_gethash(np
, type
);
1061 rolist
[slot
]->lastopped
= 0;
1062 rolist
[slot
]->score
= 0;
1064 return rolist
[slot
];
1067 void cf_deleteregop(chanindex
*cip
, regop
*ro
) {
1068 chanfix
*cf
= cip
->exts
[cfext
];
1074 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1075 if (((regop
**)cf
->regops
.content
)[a
] == ro
) {
1076 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
1077 free(((regop
**)cf
->regops
.content
)[a
]);
1078 array_delslot(&(cf
->regops
), a
);
1082 /* get rid of chanfix* if there are no more regops */
1083 if (cf
->regops
.cursi
== 0) {
1084 array_free(&(cf
->regops
));
1086 cip
->exts
[cfext
] = NULL
;
1088 /* we could try to free the chanindex* here
1089 but that would make cfsched_dosample a lot more
1094 int cf_fixchannel(channel
*cp
) {
1097 modechanges changes
;
1100 chanfix
*cf
= cp
->index
->exts
[cfext
];
1103 return CFX_NOCHANFIX
;
1105 localsetmodeinit(&changes
, cp
, mynick
);
1109 /* reop services first and deop other users */
1110 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1111 if(cp
->users
->content
[a
] != nouser
) {
1112 np
= getnickbynumeric(cp
->users
->content
[a
]);
1114 if (IsService(np
) && (np
->nick
[1] == '\0')) {
1115 localdosetmode_nick(&changes
, np
, MC_OP
);
1118 localdosetmode_nick(&changes
, np
, MC_DEOP
);
1122 /* don't reop users if we've already opped some services */
1125 localsetmodeflush(&changes
, 1);
1130 /* now get a sorted list of regops */
1131 ops
= cf_getsortedregops(cf
, 50, rolist
);
1133 /* and op some of them */
1134 for (i
=0;i
<ops
;i
++) {
1135 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< (rolist
[0]->score
/ 2))
1138 if (rolist
[i
]->score
< CFMINSCORE
&& i
!= 0 )
1141 np
= cf_findnick(rolist
[i
], cp
->index
);
1143 /* only if it's not a service, so we don't screw up 'count' */
1144 if (np
&& !(IsService(np
) && np
->nick
[1] == '\0')) {
1145 localdosetmode_nick(&changes
, np
, MC_OP
);
1151 localsetmodeflush(&changes
, 1);
1153 if (count
== CFMAXOPS
)
1155 else if (count
== 0)
1156 return CFX_NOUSERSAVAILABLE
;
1158 return CFX_FIXEDFEWOPS
;
1161 int cf_storechanfix(void) {
1168 int a
, i
, count
= 0;
1170 snprintf(dstfile
, sizeof(dstfile
), "%s.%d", CFSTORAGE
, CFSAVEFILES
);
1173 for (i
= CFSAVEFILES
; i
> 0; i
--) {
1174 snprintf(srcfile
, sizeof(srcfile
), "%s.%i", CFSTORAGE
, i
- 1);
1175 snprintf(dstfile
, sizeof(dstfile
), "%s.%i", CFSTORAGE
, i
);
1176 rename(srcfile
, dstfile
);
1179 snprintf(srcfile
, sizeof(srcfile
), "%s.0", CFSTORAGE
);
1180 cfdata
= fopen(srcfile
, "w");
1185 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
1186 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
1187 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
1188 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1189 ro
= ((regop
**)cf
->regops
.content
)[a
];
1192 fprintf(cfdata
, "%s %d %lu %lu %d %s\n", cip
->name
->content
,
1193 ro
->type
, ro
->hash
, ro
->lastopped
, ro
->score
, ro
->uh
->content
);
1195 fprintf(cfdata
, "%s %d %lu %lu %d\n", cip
->name
->content
,
1196 ro
->type
, ro
->hash
, ro
->lastopped
, ro
->score
);
1208 /* channel type hash lastopped score host */
1209 int cf_parseline(char *line
) {
1214 char chan
[CHANNELLEN
+1];
1218 char host
[USERLEN
+1+HOSTLEN
+1];
1221 count
= sscanf(line
, "%s %d %lu %lu %d %s", chan
, &type
, &hash
, &lastopped
, &score
, host
);
1224 return 0; /* invalid chanfix record */
1226 cip
= findorcreatechanindex(chan
);
1228 cf
= cip
->exts
[cfext
];
1231 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1234 array_init(&(cf
->regops
), sizeof(regop
*));
1236 cip
->exts
[cfext
] = cf
;
1239 slot
= array_getfreeslot(&(cf
->regops
));
1241 rolist
= (regop
**)cf
->regops
.content
;
1243 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1245 rolist
[slot
]->type
= type
;
1246 rolist
[slot
]->hash
= hash
;
1247 rolist
[slot
]->lastopped
= lastopped
;
1248 rolist
[slot
]->score
= score
;
1249 rolist
[slot
]->uh
= getsstring(host
, USERLEN
+1+HOSTLEN
);
1254 int cf_loadchanfix(void) {
1262 snprintf(srcfile
, sizeof(srcfile
), "%s.0", CFSTORAGE
);
1263 cfdata
= fopen(srcfile
, "r");
1270 while (!feof(cfdata
)) {
1271 if (fgets(line
, sizeof(line
), cfdata
) == NULL
)
1274 if (line
[strlen(line
) - 1] == '\n')
1275 line
[strlen(line
) - 1] = '\0';
1277 if (line
[strlen(line
) - 1] == '\r')
1278 line
[strlen(line
) - 1] = '\0';
1280 if (line
[0] != '\0') {
1281 if (cf_parseline(line
))
1291 /* functions for users of this module */
1292 chanfix
*cf_findchanfix(chanindex
*cip
) {
1293 return cip
->exts
[cfext
];
1296 int cf_wouldreop(nick
*np
, channel
*cp
) {
1300 chanfix
*cf
= cp
->index
->exts
[cfext
];
1303 return 1; /* too bad, we can't do anything about it */
1305 ro
= cf_findregop(np
, cp
->index
, CFACCOUNT
| CFHOST
);
1310 rolist
= (regop
**)cf
->regops
.content
;
1312 for (i
=0; i
<cf
->regops
.cursi
; i
++)
1313 if (rolist
[i
]->score
> topscore
)
1314 topscore
= rolist
[i
]->score
;
1316 if (ro
->score
> topscore
/ 2 && ro
->score
> CFMINSCORE
)