]> jfr.im git - irc/quakenet/newserv.git/blob - signontracker/signontracker.c
A4STATS: remove E style escapes and switch to createtable for indices
[irc/quakenet/newserv.git] / signontracker / signontracker.c
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 }