]>
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
);
47 void cfhook_lostnick(int hook
, void *arg
);
49 /* helper functions */
50 regop
*cf_createregop(nick
*np
, chanindex
*cip
);
51 void cf_deleteregop(chanindex
*cip
, regop
*ro
);
52 unsigned long cf_gethash(nick
*np
, int type
);
54 int cf_storechanfix(void);
55 int cf_loadchanfix(void);
58 #define min(a,b) ((a > b) ? b : a)
61 cfext
= registerchanext("chanfix");
62 cfnext
= registernickext("chanfix");
64 if (cfext
< 0 || cfnext
< 0) {
65 Error("chanfix", ERR_ERROR
, "Couldn't register channel and/or nick extension");
68 schedulerecurring(time(NULL
), 0, CFSAMPLEINTERVAL
, &cfsched_dosample
, NULL
);
69 schedulerecurring(time(NULL
), 0, CFEXPIREINTERVAL
, &cfsched_doexpire
, NULL
);
70 schedulerecurring(time(NULL
), 0, CFAUTOSAVEINTERVAL
, &cfsched_dosave
, NULL
);
72 registercontrolcmd("cfdebug", 10, 1, &cfcmd_debug
);
73 registercontrolcmd("cfhistogram", 10, 1, &cfcmd_debughistogram
);
75 registercontrolcmd("cfsample", 10, 1, &cfcmd_debugsample
);
76 registercontrolcmd("cfexpire", 10, 1, &cfcmd_debugexpire
);
78 registercontrolcmd("chanopstat", 10, 1, &cfcmd_chanopstat
);
79 registercontrolcmd("chanoplist", 10, 1, &cfcmd_chanoplist
);
81 registercontrolcmd("chanfix", 10, 1, &cfcmd_chanfix
);
82 registercontrolcmd("showregs", 10, 1, &cfcmd_showregs
);
84 /* should we disable this in the 'final' build? */
85 // registercontrolcmd("requestop", 0, 2, &cfcmd_requestop);
87 registercontrolcmd("cfsave", 10, 0, &cfcmd_save
);
88 registercontrolcmd("cfload", 10, 0, &cfcmd_load
);
91 registerhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
92 registerhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
93 registerhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
94 registerhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
97 registerhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
98 registerhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
99 registerhook(HOOK_NICK_LOSTNICK
, &cfhook_lostnick
);
111 if (cffailedinit
== 0) {
112 deleteschedule(NULL
, &cfsched_dosample
, NULL
);
113 deleteschedule(NULL
, &cfsched_doexpire
, NULL
);
114 deleteschedule(NULL
, &cfsched_dosave
, NULL
);
120 for (i
=0; i
<NICKHASHSIZE
; i
++)
121 for (nip
=nicktable
[i
]; nip
; nip
=nip
->next
)
122 free(nip
->exts
[cfnext
]);
124 releasechanext(cfext
);
125 releasenickext(cfnext
);
127 deregistercontrolcmd("cfdebug", &cfcmd_debug
);
128 deregistercontrolcmd("cfhistogram", &cfcmd_debughistogram
);
130 deregistercontrolcmd("cfsample", &cfcmd_debugsample
);
131 deregistercontrolcmd("cfexpire", &cfcmd_debugexpire
);
133 deregistercontrolcmd("chanopstat", &cfcmd_chanopstat
);
134 deregistercontrolcmd("chanoplist", &cfcmd_chanoplist
);
135 deregistercontrolcmd("chanfix", &cfcmd_chanfix
);
136 deregistercontrolcmd("showregs", &cfcmd_showregs
);
138 // deregistercontrolcmd("requestop", &cfcmd_requestop);
140 deregistercontrolcmd("cfsave", &cfcmd_save
);
141 deregistercontrolcmd("cfload", &cfcmd_load
);
144 deregisterhook(HOOK_CHANNEL_DEOPPED
, &cfhook_autofix
);
145 deregisterhook(HOOK_CHANNEL_PART
, &cfhook_autofix
);
146 deregisterhook(HOOK_CHANNEL_KICK
, &cfhook_autofix
);
147 deregisterhook(HOOK_CHANNEL_JOIN
, &cfhook_autofix
);
150 deregisterhook(HOOK_CORE_STATSREQUEST
, &cfhook_statsreport
);
151 deregisterhook(HOOK_NICK_ACCOUNT
, &cfhook_auth
);
152 deregisterhook(HOOK_NICK_LOSTNICK
, &cfhook_lostnick
);
156 int cfcmd_debug(void *source
, int cargc
, char **cargv
) {
157 nick
*np
= (nick
*)source
;
164 controlreply(np
, "Syntax: cfdebug <#channel>");
169 cip
= findchanindex(cargv
[0]);
172 controlreply(np
, "No such channel.");
176 controlreply(np
, "Found channel %s. Retrieving chanfix information...", cargv
[0]);
178 cf
= cip
->exts
[cfext
];
181 controlreply(np
, "No chanfix information for %s", cargv
[0]);
185 controlreply(np
, "Found chanfix information. Dumping...");
187 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
188 ro
= ((regop
**)cf
->regops
.content
)[i
];
190 controlreply(np
, "%d. type: %s hash: 0x%x lastopped: %d uh: %s score: %d",
191 i
+ 1, ro
->type
== CFACCOUNT
? "CFACCOUNT" : "CFHOST", ro
->hash
,
192 ro
->lastopped
, ro
->uh
? ro
->uh
->content
: "(unknown)", ro
->score
);
195 controlreply(np
, "Done.");
200 int cfcmd_debughistogram(void *source
, int cargc
, char **cargv
) {
201 nick
*np
= (nick
*)source
;
206 int histogram
[10001]; /* 625 (lines) * 16 (columns/line) + 1 (for histogram[0]) */
208 for (i
= 0; i
< 10001; i
++)
211 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
212 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
213 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
214 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
215 score
= ((regop
**)cf
->regops
.content
)[a
]->score
;
224 controlreply(np
, "--- Histogram of chanfix scores");
226 for (i
= 1; i
< 10001; i
+= 16) {
229 for (a
= 0; a
< 16; a
++) {
233 sprintf(buf
+strlen(buf
),"%d", histogram
[i
+a
]);
236 controlreply(np
, "%6d: %s", i
, buf
);
239 controlreply(np
, "--- End of histogram");
244 int cfcmd_debugsample(void *source
, int cargc
, char **cargv
) {
245 cfsched_dosample(NULL
);
247 controlreply((nick
*)source
, "Done.");
252 int cfcmd_debugexpire(void *source
, int cargc
, char **cargv
) {
253 cfsched_doexpire(NULL
);
255 controlreply((nick
*)source
, "Done.");
260 /* used for qsorting int arrays */
261 int cmpint(const void *a
, const void *b
) {
273 /* used for qsorting regop* arrays */
274 int cmpregop(const void *a
, const void *b
) {
275 regop
*p
= *(regop
**)a
;
276 regop
*q
= *(regop
**)b
;
278 if (p
->score
> q
->score
)
280 else if (p
->score
< q
->score
)
286 int cf_getsortedregops(chanfix
*cf
, int max
, regop
**list
) {
292 qsort(cf
->regops
.content
, cf
->regops
.cursi
, sizeof(regop
*), cmpregop
);
294 for (i
= 0; i
< min(max
, cf
->regops
.cursi
); i
++) {
295 list
[i
] = ((regop
**)cf
->regops
.content
)[i
];
301 int cfcmd_chanopstat(void *source
, int cargc
, char **cargv
) {
302 nick
*np
= (nick
*)source
;
313 controlreply(np
, "Syntax: chanopstat <#channel>");
318 cp
= findchannel(cargv
[0]);
321 controlreply(np
, "No such channel.");
326 cf
= cp
->index
->exts
[cfext
];
329 controlreply(np
, "No chanfix information for %s", cargv
[0]);
335 count
= cf_getsortedregops(cf
, 10, rolist
);
339 for (i
=0;i
<count
;i
++) {
343 sprintf(buf
+strlen(buf
),"%d", rolist
[i
]->score
);
346 controlreply(np
, "Scores of \"best ops\" on %s are: %s", cargv
[0], buf
);
349 scores
= (int*)malloc(sizeof(int) * cp
->users
->hashsize
);
353 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
354 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
355 np2
= getnickbynumeric(cp
->users
->content
[a
]);
357 ro
= cf_findregop(np2
, cp
->index
, CFACCOUNT
| CFHOST
);
360 scores
[i
++] = ro
->score
;
364 qsort(scores
, i
, sizeof(int), &cmpint
);
368 for (a
=0;a
<min(i
,20);a
++) {
375 sprintf(buf
+strlen(buf
),"%d", scores
[a
]);
380 controlreply(np
, "Scores of current ops on %s are: %s", cargv
[0], buf
);
382 controlreply(np
, "Done.");
387 int cfcmd_chanoplist(void *source
, int cargc
, char **cargv
) {
388 nick
*np
= (nick
*)source
;
400 controlreply(np
, "Syntax: chanoplist <#channel>");
405 cip
= findchanindex(cargv
[0]);
408 controlreply(np
, "No such channel.");
413 cf
= cip
->exts
[cfext
];
416 controlreply(np
, "No chanfix information for %s", cargv
[0]);
421 count
= cf_getsortedregops(cf
, 50, rolist
);
423 controlreply(np
, "Pos Score Type User/Last seen");
425 for (i
=0;i
<count
;i
++) {
426 np2
= cf_findnick(rolist
[i
], cip
);
429 hand
= getnumerichandlefromchanhash(cip
->channel
->users
, np2
->numeric
);
431 /* hand should be non-null in all cases */
433 controlreply(np
, "%3d %5d %-7s %1s%-16s %s@%s (%s)", i
+ 1, rolist
[i
]->score
,
434 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host",
435 (*hand
& CUMODE_OP
) ? "@" : "", np2
->nick
, np2
->ident
,
436 np2
->host
->name
->content
, np2
->realname
->name
->content
);
438 ct
= rolist
[i
]->lastopped
;
442 date
= (char*)malloc(strlen(cdate
) + 1);
445 for (a
=0;a
<strlen(date
);a
++) {
446 if (date
[a
] == '\n') {
452 controlreply(np
, "%3d %5d %-7s %1s%-16s %s Last seen: %s", i
+ 1, rolist
[i
]->score
,
453 (rolist
[i
]->type
== CFACCOUNT
) ? "account" : "host",
455 rolist
[i
]->uh
? rolist
[i
]->uh
->content
: "!UNKNOWN!", date
);
461 controlreply(np
, "Done.");
466 int cfcmd_chanfix(void *source
, int cargc
, char **cargv
) {
467 nick
*np
= (nick
*)source
;
472 controlreply(np
, "Syntax: chanfix <#channel>");
477 cp
= findchannel(cargv
[0]);
480 controlreply(np
, "No such channel.");
485 if (sp_countsplitservers() > 0) {
486 controlreply(np
, "Chanfix cannot be used during a netsplit.");
491 ret
= cf_fixchannel(cp
);
495 controlreply(np
, "Channel was fixed.");
498 controlreply(np
, "Channel cannot be fixed: no chanfix information");
500 case CFX_FIXEDFEWOPS
:
501 controlreply(np
, "Channel was fixed but only a few ops could be found and reopped.");
511 int cfcmd_showregs(void *source
, int cargc
, char **cargv
) {
512 nick
*np
= (nick
*)source
;
516 int a
, i
, count
, ops
;
520 controlreply(np
, "Syntax: showregs <#channel>");
525 cp
= findchannel(cargv
[0]);
528 controlreply(np
, "No such channel.");
533 cf
= cp
->index
->exts
[cfext
];
536 controlreply(np
, "No chanfix information for %s", cargv
[0]);
543 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
544 if(cp
->users
->content
[a
] != nouser
) {
545 np2
= getnickbynumeric(cp
->users
->content
[a
]);
547 if (IsService(np2
)) {
548 controlreply(np
, "%s (service)", np2
->nick
);
554 /* now get a sorted list of regops */
555 ops
= cf_getsortedregops(cf
, 50, rolist
);
557 /* and show some of them */
558 for (i
=0;i
<ops
;i
++) {
559 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< rolist
[0]->score
/ 2 || rolist
[i
]->score
< CFMINSCORE
)
562 np2
= cf_findnick(rolist
[i
], cp
->index
);
565 controlreply(np
, "%s (%s)", np2
->nick
, rolist
[i
]->type
== CFACCOUNT
? "account" : "host");
571 controlreply((nick
*)source
, "--- End of list: %d users listed", count
);
576 int cfcmd_requestop(void *source
, int cargc
, char **cargv
) {
577 nick
*np
= (nick
*)source
;
585 controlreply(np
, "Syntax: requestop <#channel> [nick]");
590 cp
= findchannel(cargv
[0]);
593 controlreply(np
, "No such channel.");
599 user
= getnickbynick(cargv
[1]);
602 controlreply(np
, "No such user.");
608 hand
= getnumerichandlefromchanhash(cp
->users
, user
->numeric
);
611 controlreply(np
, "User %s is not on channel %s.", user
->nick
, cargv
[0]);
616 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
617 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
618 controlreply(np
, "There are ops on channel %s. This command can only be"
619 " used if there are no ops.", cargv
[0]);
625 if (sp_countsplitservers() > 0) {
626 controlreply(np
, "One or more servers are currently split. Wait until the"
627 " netsplit is over and try again.");
632 if (cf_wouldreop(user
, cp
)) {
633 localsetmodeinit(&changes
, cp
, mynick
);
634 localdosetmode_nick(&changes
, user
, MC_OP
);
635 localsetmodeflush(&changes
, 1);
637 controlreply(np
, "Chanfix opped you on the specified channel.");
639 ret
= cf_fixchannel(cp
);
641 if (ret
== CFX_NOUSERSAVAILABLE
)
642 controlreply(np
, "Chanfix knows regular ops for that channel. They will"
643 " be opped when they return.");
645 controlreply(np
, "Chanfix has opped known ops for that channel.");
651 int cfcmd_save(void *source
, int cargc
, char **cargv
) {
652 nick
*np
= (nick
*)source
;
655 count
= cf_storechanfix();
657 controlreply(np
, "%d chanfix records saved.", count
);
668 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
669 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
670 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
671 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
672 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
673 free(((regop
**)cf
->regops
.content
)[a
]);
676 array_free(&(((chanfix
*)cip
->exts
[cfext
])->regops
));
679 free(cip
->exts
[cfext
]);
680 cip
->exts
[cfext
] = NULL
;
685 int cfcmd_load(void *source
, int cargc
, char **cargv
) {
686 nick
*np
= (nick
*)source
;
689 count
= cf_loadchanfix();
691 controlreply(np
, "%d chanfix records loaded.", count
);
696 int cf_hasauthedcloneonchan(nick
*np
, channel
*cp
) {
700 for (jp
= np
->host
->nicks
; jp
; jp
= jp
->nextbyhost
) {
701 if (!IsAccount(jp
) || jp
->numeric
== np
->numeric
)
704 hand
= getnumerichandlefromchanhash(cp
->users
, jp
->numeric
);
706 if (hand
&& (*hand
& CUMODE_OP
) && strcmp(np
->ident
, jp
->ident
) == 0)
713 void cfsched_dosample(void *arg
) {
714 int i
,a
,now
,cfscore
,cfnewro
,cfuhost
,diff
;
720 struct timeval start
;
722 char buf
[USERLEN
+1+HOSTLEN
+1];
726 cfuhost
= cfscore
= cfnewro
= 0;
728 if (sp_countsplitservers() > CFMAXSPLITSERVERS
)
731 gettimeofday(&start
, NULL
);
733 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
734 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
735 cf
= (chanfix
*)cip
->exts
[cfext
];
738 if (!cp
|| cp
->users
->totalusers
< CFMINUSERS
)
741 for (a
=0;a
<cp
->users
->hashsize
;a
++) {
742 if ((cp
->users
->content
[a
] != nouser
) && (cp
->users
->content
[a
] & CUMODE_OP
)) {
743 np
= getnickbynumeric(cp
->users
->content
[a
]);
750 roh
= ro
= cf_findregop(np
, cip
, CFACCOUNT
| CFHOST
);
752 if ((ro
== NULL
|| (IsAccount(np
) && ro
->type
== CFHOST
)) &&
753 !cf_hasauthedcloneonchan(np
, cp
)) {
754 ro
= cf_createregop(np
, cip
);
758 /* lastopped == now if the user has clones, we obviously
759 * don't want to give them points in this case */
760 if (ro
&& ro
->lastopped
!= now
) {
761 if (ro
->type
!= CFHOST
|| !cf_hasauthedcloneonchan(np
, cp
)) {
766 /* merge any matching CFHOST records */
767 if (roh
&& roh
->type
== CFHOST
&& ro
->type
== CFACCOUNT
) {
769 ro
->score
+= roh
->score
;
771 cf_deleteregop(cip
, roh
);
774 /* store the user's account/host if we have to */
775 if (ro
->uh
== NULL
&& ro
->score
>= CFMINSCOREUH
) {
776 if (ro
->type
== CFACCOUNT
)
777 ro
->uh
= getsstring(np
->authname
, ACCOUNTLEN
);
779 strcpy(buf
, np
->ident
);
781 strcat(buf
, np
->host
->name
->content
);
783 roh
->uh
= getsstring(buf
, USERLEN
+1+HOSTLEN
);
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
, "sampled chanfix scores, assigned %d new"
805 " points, %d new regops, %d user@hosts added, deltaT: %dms", cfscore
,
806 cfnewro
, cfuhost
, diff
);
810 void cfsched_doexpire(void *arg
) {
815 int i
,a
,cfscore
,cfregop
,cffreeuh
,diff
;
818 struct timeval start
;
822 cffreeuh
= cfscore
= cfregop
= 0;
824 gettimeofday(&start
, NULL
);
825 currenttime
=getnettime();
827 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
828 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
829 cf
= (chanfix
*)cip
->exts
[cfext
];
832 rolist
= (regop
**)cf
->regops
.content
;
834 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
837 if ((currenttime
- ro
->lastopped
> 2 * CFSAMPLEINTERVAL
) && ro
->score
) {
842 if (ro
->score
< CFMINSCOREUH
&& ro
->uh
) {
849 if (ro
->score
== 0 || ro
->lastopped
< currenttime
- CFREMEMBEROPS
) {
850 cf_deleteregop(cip
, ro
);
858 /* stolen from channel/channelindex.c */
859 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
860 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
861 /* CAREFUL: deleting items from chains you're walking is bad */
864 /* try to delete it if there's no chanfix* pointer */
865 if (cip
->exts
[cfext
] == NULL
)
866 releasechanindex(cip
);
870 cp
= findchannel("#qnet.chanfix");
873 gettimeofday(&end
, NULL
);
875 diff
= (end
.tv_sec
* 1000 + end
.tv_usec
/ 1000) -
876 (start
.tv_sec
* 1000 + start
.tv_usec
/ 1000);
878 sendmessagetochannel(mynick
, cp
, "expired chanfix scores, purged %d points,"
879 " scrapped %6d regops, %d user@hosts freed, deltaT: %dms", cfscore
,
880 cfregop
, cffreeuh
, diff
);
885 void cfsched_dosave(void *arg
) {
890 void cfhook_autofix(int hook
, void *arg
) {
892 void **args
= (void**)arg
;
895 if (hook
== HOOK_CHANNEL_DEOPPED
|| hook
== HOOK_CHANNEL_PART
||
896 hook
== HOOK_CHANNEL_KICK
|| hook
== HOOK_CHANNEL_JOIN
) {
899 if (sp_countsplitservers() > 0)
902 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
903 if (cp
->users
->content
[a
] != nouser
) {
906 if (cp
->users
->content
[a
] & CUMODE_OP
)
911 /* don't fix small channels.. it's inaccurate and
912 * they could just cycle the channel */
916 cf_fixchannel((channel
*)args
[0]);
921 void cfhook_statsreport(int hook
, void *arg
) {
923 int i
,a
,rc
,mc
,memory
;
929 memory
= rc
= mc
= 0;
931 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
932 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
933 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
934 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
935 memory
+= sizeof(regop
) + sizeof(regop
*);
937 if (((regop
**)cf
->regops
.content
)[a
]->uh
)
938 memory
+= sizeof(sstring
) + strlen(((regop
**)cf
->regops
.content
)[a
]->uh
->content
) + 1;
943 memory
+= sizeof(chanfix
);
950 for (i
=0; i
<NICKHASHSIZE
; i
++) {
951 for (nip
=nicktable
[i
]; nip
; nip
=nip
->next
) {
952 if (nip
->exts
[cfnext
])
953 memory
+= sizeof(int);
957 sprintf(buf
, "Chanfix : %6d registered ops, %9d monitored channels. %9d"
958 " kbytes of memory used", rc
, mc
, memory
/ 1024);
959 triggerhook(HOOK_CORE_STATSREPLY
, buf
);
963 void cfhook_auth(int hook
, void *arg
) {
964 nick
*np
= (nick
*)arg
;
966 /* Invalidate the user's hash */
967 free(np
->exts
[cfnext
]);
969 np
->exts
[cfnext
] = NULL
;
971 /* Calculate the new hash */
972 cf_gethash(np
, CFACCOUNT
);
975 void cfhook_lostnick(int hook
, void *arg
) {
976 nick
*np
= (nick
*)arg
;
978 free(np
->exts
[cfnext
]);
981 /* Returns the hash of a specific user (np), type can be either CFACCOUNT,
982 CFHOST or both (binary or'd values). cf_gethash will also cache the user's
983 hash in a nick extension */
984 unsigned long cf_gethash(nick
*np
, int type
) {
985 char buf
[USERLEN
+1+HOSTLEN
+1];
988 if (IsAccount(np
) && type
& CFACCOUNT
) {
989 if (np
->exts
[cfnext
] == NULL
) {
990 np
->exts
[cfnext
] = (int*)malloc(sizeof(int));
991 *(int*)np
->exts
[cfnext
] = crc32(np
->authname
);
994 return *(int*)np
->exts
[cfnext
];
995 } else if (type
== CFACCOUNT
)
996 return 0; /* this should not happen */
999 if (!IsAccount(np
) && np
->exts
[cfnext
])
1000 return *(int*)np
->exts
[cfnext
];
1002 strcpy(buf
, np
->ident
);
1004 strcat(buf
, np
->host
->name
->content
);
1007 /* if the user is not authed, update the hash */
1008 if (!IsAccount(np
)) {
1009 np
->exts
[cfnext
] = (int*)malloc(sizeof(int));
1011 *(int*)np
->exts
[cfnext
] = hash
;
1018 return 0; /* should not happen */
1021 /* This seems to be a lot faster than using sprintf */
1022 int cf_equhost(const char *uhost
, const char *user
, const char *host
) {
1023 char *p
= strchr(uhost
, '@');
1025 /* We assume the uhost contains a @ - which it should do in all cases */
1029 if (ircd_strncmp(uhost
, user
, p
- uhost
) == 0 && ircd_strcmp(p
+ 1, host
) == 0)
1035 /* Why do we actually store the users' real hosts/accounts instead of hashes?
1036 * - it allows operators to see the hosts/accounts in 'chanoplist' even if the
1037 * users are not online
1038 * - it avoids hash collisions (could be avoided with md5/sha1/etc.)
1040 int cf_cmpregopnick(regop
*ro
, nick
*np
) {
1041 if (ro
->uh
!= NULL
) {
1042 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
1043 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
) &&
1044 strcmp(ro
->uh
->content
, np
->authname
) == 0);
1046 return (ro
->hash
== cf_gethash(np
, CFHOST
) &&
1047 cf_equhost(ro
->uh
->content
, np
->ident
, np
->host
->name
->content
));
1050 if (ro
->type
== CFACCOUNT
&& IsAccount(np
))
1051 return (ro
->hash
== cf_gethash(np
, CFACCOUNT
));
1053 return (ro
->hash
== cf_gethash(np
, CFHOST
));
1057 nick
*cf_findnick(regop
*ro
, chanindex
*cip
) {
1058 chanfix
*cf
= cip
->exts
[cfext
];
1059 channel
*cp
= cip
->channel
;
1063 if (cf
== NULL
|| cp
== NULL
)
1066 if (ro
->type
== CFACCOUNT
) {
1067 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1068 if(cp
->users
->content
[a
] != nouser
) {
1069 np2
= getnickbynumeric(cp
->users
->content
[a
]);
1071 if (!IsAccount(np2
))
1074 if (cf_cmpregopnick(ro
, np2
))
1080 if (ro
->type
== CFHOST
) {
1081 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1082 if(cp
->users
->content
[a
] != nouser
) {
1083 np2
= getnickbynumeric(cp
->users
->content
[a
]);
1085 if (cf_cmpregopnick(ro
, np2
))
1094 regop
*cf_findregop(nick
*np
, chanindex
*cip
, int type
) {
1095 chanfix
*cf
= cip
->exts
[cfext
];
1102 if (IsAccount(np
) && type
& CFACCOUNT
)
1107 for (i
=0;i
<cf
->regops
.cursi
;i
++) {
1108 ro
= ((regop
**)cf
->regops
.content
)[i
];
1110 if (ro
->type
== ty
&& cf_cmpregopnick(ro
, np
))
1114 /* try using the uhost if we didn't find a user with the right account */
1115 if (ty
== CFACCOUNT
&& type
& CFHOST
)
1116 return cf_findregop(np
, cip
, CFHOST
);
1123 regop
*cf_createregop(nick
*np
, chanindex
*cip
) {
1124 chanfix
*cf
= cip
->exts
[cfext
];
1129 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1132 array_init(&(cf
->regops
), sizeof(regop
*));
1134 cip
->exts
[cfext
] = cf
;
1137 slot
= array_getfreeslot(&(cf
->regops
));
1139 rolist
= (regop
**)cf
->regops
.content
;
1141 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1148 rolist
[slot
]->type
= type
;
1149 rolist
[slot
]->hash
= cf_gethash(np
, type
);
1150 rolist
[slot
]->uh
= NULL
;
1151 rolist
[slot
]->lastopped
= 0;
1152 rolist
[slot
]->score
= 0;
1154 return rolist
[slot
];
1157 void cf_deleteregop(chanindex
*cip
, regop
*ro
) {
1158 chanfix
*cf
= cip
->exts
[cfext
];
1164 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1165 if (((regop
**)cf
->regops
.content
)[a
] == ro
) {
1166 freesstring(((regop
**)cf
->regops
.content
)[a
]->uh
);
1167 free(((regop
**)cf
->regops
.content
)[a
]);
1168 array_delslot(&(cf
->regops
), a
);
1172 /* get rid of chanfix* if there are no more regops */
1173 if (cf
->regops
.cursi
== 0) {
1174 array_free(&(cf
->regops
));
1176 cip
->exts
[cfext
] = NULL
;
1178 /* we could try to free the chanindex* here
1179 but that would make cfsched_dosample a lot more
1184 int cf_fixchannel(channel
*cp
) {
1187 modechanges changes
;
1190 chanfix
*cf
= cp
->index
->exts
[cfext
];
1193 return CFX_NOCHANFIX
;
1195 localsetmodeinit(&changes
, cp
, mynick
);
1199 /* reop services first and deop other users */
1200 for(a
=0;a
<cp
->users
->hashsize
;a
++) {
1201 if(cp
->users
->content
[a
] != nouser
) {
1202 np
= getnickbynumeric(cp
->users
->content
[a
]);
1204 if (IsService(np
) && (np
->nick
[1] == '\0')) {
1205 localdosetmode_nick(&changes
, np
, MC_OP
);
1208 localdosetmode_nick(&changes
, np
, MC_DEOP
);
1212 /* don't reop users if we've already opped some services */
1215 localsetmodeflush(&changes
, 1);
1220 /* now get a sorted list of regops */
1221 ops
= cf_getsortedregops(cf
, 50, rolist
);
1223 /* and op some of them */
1224 for (i
=0;i
<ops
;i
++) {
1225 if (count
>= CFMAXOPS
|| rolist
[i
]->score
< rolist
[0]->score
/ 2)
1228 if (rolist
[i
]->score
< CFMINSCORE
&& i
!= 0 )
1231 np
= cf_findnick(rolist
[i
], cp
->index
);
1233 /* only if it's not a service, so we don't screw up 'count' */
1234 if (np
&& !(IsService(np
) && np
->nick
[1] == '\0')) {
1235 localdosetmode_nick(&changes
, np
, MC_OP
);
1241 localsetmodeflush(&changes
, 1);
1243 if (count
== CFMAXOPS
)
1245 else if (count
== 0)
1246 return CFX_NOUSERSAVAILABLE
;
1248 return CFX_FIXEDFEWOPS
;
1251 int cf_storechanfix(void) {
1258 int a
, i
, count
= 0;
1260 snprintf(dstfile
, 300, "%s.%d", CFSTORAGE
, CFSAVEFILES
);
1263 for (i
= CFSAVEFILES
; i
> 0; i
--) {
1264 snprintf(srcfile
, 300, "%s.%i", CFSTORAGE
, i
- 1);
1265 snprintf(dstfile
, 300, "%s.%i", CFSTORAGE
, i
);
1266 rename(srcfile
, dstfile
);
1269 snprintf(srcfile
, 300, "%s.0", CFSTORAGE
);
1270 cfdata
= fopen(srcfile
, "w");
1275 for (i
=0; i
<CHANNELHASHSIZE
; i
++) {
1276 for (cip
=chantable
[i
]; cip
; cip
=cip
->next
) {
1277 if ((cf
= cip
->exts
[cfext
]) != NULL
) {
1278 for (a
=0;a
<cf
->regops
.cursi
;a
++) {
1279 ro
= ((regop
**)cf
->regops
.content
)[a
];
1282 fprintf(cfdata
, "%s %lu %lu %lu %lu %s\n", cip
->name
->content
,
1283 (unsigned long)ro
->type
, (unsigned long)ro
->hash
,
1284 (unsigned long)ro
->lastopped
, (unsigned long)ro
->score
,
1287 fprintf(cfdata
, "%s %lu %lu %lu %lu\n", cip
->name
->content
,
1288 (unsigned long)ro
->type
, (unsigned long)ro
->hash
,
1289 (unsigned long)ro
->lastopped
, (unsigned long)ro
->score
);
1301 /* channel type hash lastopped score host */
1302 int cf_parseline(char *line
) {
1307 char chan
[CHANNELLEN
+1];
1308 char host
[USERLEN
+1+HOSTLEN
+1];
1309 unsigned long type
,hash
,lastopped
,score
;
1312 count
= sscanf(line
, "%s %lu %lu %lu %lu %s", chan
, &type
, &hash
, &lastopped
, &score
, host
);
1315 return 0; /* invalid chanfix record */
1317 cip
= findorcreatechanindex(chan
);
1319 cf
= cip
->exts
[cfext
];
1322 cf
= (chanfix
*)malloc(sizeof(chanfix
));
1325 array_init(&(cf
->regops
), sizeof(regop
*));
1327 cip
->exts
[cfext
] = cf
;
1330 slot
= array_getfreeslot(&(cf
->regops
));
1332 rolist
= (regop
**)cf
->regops
.content
;
1334 rolist
[slot
] = (regop
*)malloc(sizeof(regop
));
1336 rolist
[slot
]->type
= type
;
1337 rolist
[slot
]->hash
= hash
;
1338 rolist
[slot
]->lastopped
= lastopped
;
1339 rolist
[slot
]->score
= score
;
1341 if (count
>= 6 && strchr(host
, '@') != NULL
)
1342 rolist
[slot
]->uh
= getsstring(host
, USERLEN
+1+HOSTLEN
);
1344 rolist
[slot
]->uh
= NULL
;
1349 int cf_loadchanfix(void) {
1357 snprintf(srcfile
, 300, "%s.0", CFSTORAGE
);
1358 cfdata
= fopen(srcfile
, "r");
1365 while (!feof(cfdata
)) {
1366 if (fgets(line
, sizeof(line
), cfdata
) == NULL
)
1369 if (line
[strlen(line
) - 1] == '\n')
1370 line
[strlen(line
) - 1] = '\0';
1372 if (line
[strlen(line
) - 1] == '\r')
1373 line
[strlen(line
) - 1] = '\0';
1375 if (line
[0] != '\0') {
1376 if (cf_parseline(line
))
1386 /* functions for users of this module */
1387 chanfix
*cf_findchanfix(chanindex
*cip
) {
1388 return cip
->exts
[cfext
];
1391 int cf_wouldreop(nick
*np
, channel
*cp
) {
1395 chanfix
*cf
= cp
->index
->exts
[cfext
];
1398 return 1; /* too bad, we can't do anything about it */
1400 ro
= cf_findregop(np
, cp
->index
, CFACCOUNT
| CFHOST
);
1405 rolist
= (regop
**)cf
->regops
.content
;
1407 for (i
=0; i
<cf
->regops
.cursi
; i
++)
1408 if (rolist
[i
]->score
> topscore
)
1409 topscore
= rolist
[i
]->score
;
1411 if (ro
->score
> topscore
/ 2 && ro
->score
> CFMINSCORE
)