]>
Commit | Line | Data |
---|---|---|
1 | /* TODO: | |
2 | * - fix STOP error when we can't find control's nick | |
3 | * - deal with Q/fishbot/O/T clones | |
4 | * - deal with our own localusers | |
5 | */ | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <string.h> | |
9 | #include "../lib/version.h" | |
10 | #include "../lib/ccassert.h" | |
11 | #include "../lib/strlfunc.h" | |
12 | #include "../core/error.h" | |
13 | #include "../core/hooks.h" | |
14 | #include "../core/schedule.h" | |
15 | #include "../irc/irc.h" | |
16 | #include "../nick/nick.h" | |
17 | #include "../control/control.h" | |
18 | #include "signontracker.h" | |
19 | ||
20 | #define MAXWHOISLINES 50 /* from the ircd */ | |
21 | ||
22 | MODULE_VERSION(""); | |
23 | ||
24 | static void hook_newnick(int hook, void *arg); | |
25 | static void hook_rename(int hook, void *arg); | |
26 | static void queue_scan_nick(nick *np); | |
27 | static void flush_queue_sched(void *arg); | |
28 | static int whois_reply_handler(void *source, int cargc, char **cargv); | |
29 | static int cmd_listnosignonusers(void *source, int cargc, char **cargv); | |
30 | ||
31 | static int nickext = -1; | |
32 | ||
33 | #define QUEUEBUFLEN (MAXWHOISLINES*(NICKLEN+1)+1) | |
34 | ||
35 | static struct { | |
36 | char buf[QUEUEBUFLEN]; | |
37 | int len; | |
38 | void *sched; | |
39 | } queue[MAXSERVERS]; | |
40 | ||
41 | void _init(void) { | |
42 | nick *np; | |
43 | int i; | |
44 | ||
45 | CCASSERT(sizeof(time_t) <= sizeof(long)); | |
46 | ||
47 | nickext = registernickext("signontracker"); | |
48 | if(nickext < 0) | |
49 | return; | |
50 | ||
51 | registerhook(HOOK_NICK_NEWNICK, &hook_newnick); | |
52 | registerhook(HOOK_NICK_RENAME, &hook_rename); | |
53 | registernumerichandler(317, whois_reply_handler, 7); | |
54 | ||
55 | for(i=0;i<NICKHASHSIZE;i++) | |
56 | for(np=nicktable[i];np;np=np->next) | |
57 | queue_scan_nick(np); | |
58 | ||
59 | registercontrolhelpcmd("listnosignonusers", NO_DEVELOPER, 0, cmd_listnosignonusers, "Shows users without a signed on timestamp."); | |
60 | } | |
61 | ||
62 | void _fini(void) { | |
63 | int i; | |
64 | ||
65 | if(nickext < 0) | |
66 | return; | |
67 | ||
68 | deregistercontrolcmd("listnosignonusers", cmd_listnosignonusers); | |
69 | ||
70 | deregisterhook(HOOK_NICK_NEWNICK, &hook_newnick); | |
71 | deregisterhook(HOOK_NICK_RENAME, &hook_rename); | |
72 | deregisternumerichandler(317, whois_reply_handler); | |
73 | ||
74 | for(i=0;i<MAXSERVERS;i++) | |
75 | deleteschedule(queue[i].sched, flush_queue_sched, (void *)(long)i); | |
76 | ||
77 | releasenickext(nickext); | |
78 | } | |
79 | ||
80 | static int whois_reply_handler(void *source, int cargc, char **cargv) { | |
81 | time_t connected; | |
82 | nick *np; | |
83 | ||
84 | if(cargc < 5) | |
85 | return CMD_OK; | |
86 | ||
87 | np = getnickbynick(cargv[3]); | |
88 | if(!np) | |
89 | return CMD_OK; | |
90 | ||
91 | connected = strtoul(cargv[5], NULL, 10); | |
92 | np->exts[nickext] = (void *)connected; | |
93 | ||
94 | triggerhook(HOOK_SIGNONTRACKER_HAVETIME, np); | |
95 | ||
96 | return CMD_OK; | |
97 | } | |
98 | ||
99 | static void flush_queue(int server) { | |
100 | char control_numeric[6]; | |
101 | ||
102 | if(!queue[server].len) | |
103 | return; | |
104 | ||
105 | if(!mynick) { | |
106 | /* TODO: fix */ | |
107 | Error("signontracker", ERR_STOP, "Unable to get control nick."); | |
108 | return; | |
109 | } | |
110 | longtonumeric2(mynick->numeric, 5, control_numeric); | |
111 | ||
112 | irc_send("%s W %s %s", control_numeric, longtonumeric(server, 2), queue[server].buf); | |
113 | queue[server].len = 0; | |
114 | queue[server].buf[0] = '\0'; | |
115 | } | |
116 | ||
117 | static void queue_scan_nick(nick *np) { | |
118 | int server; | |
119 | ||
120 | if(NickOnServiceServer(np)) /* TODO: fix */ | |
121 | return; | |
122 | ||
123 | server = homeserver(np->numeric); | |
124 | ||
125 | if(queue[server].len++) | |
126 | strlcat(queue[server].buf, ",", QUEUEBUFLEN); | |
127 | ||
128 | strlcat(queue[server].buf, np->nick, QUEUEBUFLEN); | |
129 | ||
130 | if(queue[server].len == MAXWHOISLINES || strlen(queue[server].buf) > 480) { | |
131 | flush_queue(server); | |
132 | } else if(queue[server].sched == NULL) { | |
133 | queue[server].sched = scheduleoneshot(time(NULL), flush_queue_sched, (void *)(long)server); | |
134 | } | |
135 | } | |
136 | ||
137 | static void flush_queue_sched(void *arg) { | |
138 | int server = (long)arg; | |
139 | ||
140 | flush_queue(server); | |
141 | queue[server].sched = NULL; | |
142 | } | |
143 | ||
144 | static void hook_newnick(int hook, void *arg) { | |
145 | nick *np = (nick *)arg; | |
146 | ||
147 | queue_scan_nick(np); | |
148 | } | |
149 | ||
150 | static void hook_rename(int hook, void *arg) { | |
151 | void **harg = (void **)arg; | |
152 | nick *np = (nick *)harg[0]; | |
153 | ||
154 | if(getnicksignon(np)) | |
155 | return; | |
156 | ||
157 | queue_scan_nick(np); | |
158 | } | |
159 | ||
160 | time_t getnicksignon(nick *np) { | |
161 | if(nickext < 0) | |
162 | return 0; | |
163 | ||
164 | return (time_t)np->exts[nickext]; | |
165 | } | |
166 | ||
167 | static int cmd_listnosignonusers(void *source, int cargc, char **cargv) { | |
168 | nick *sender=(nick *)source; | |
169 | int i, found = 0, displayed = 0; | |
170 | nick *np; | |
171 | ||
172 | for(i=0;i<NICKHASHSIZE;i++) { | |
173 | for(np=nicktable[i];np;np=np->next) { | |
174 | if(!NickOnServiceServer(np) && !getnicksignon(np)) { | |
175 | if(found++ < 100) { | |
176 | controlreply(sender, "%s%s", np->nick, NickOnServiceServer(np) ? "(S)" : " "); | |
177 | displayed++; | |
178 | } | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | controlreply(sender, "Done, %d entries (%d displayed).", found, displayed); | |
184 | return CMD_OK; | |
185 | } |