]> jfr.im git - irc/quakenet/newserv.git/blob - signontracker/signontracker.c
e0b076379b5a8d22713b7283727203124a164a48
[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 "../lib/version.h"
9 #include "../lib/ccassert.h"
10 #include "../lib/strlfunc.h"
11 #include "../core/error.h"
12 #include "../core/hooks.h"
13 #include "../core/schedule.h"
14 #include "../irc/irc.h"
15 #include "../nick/nick.h"
16 #include "../control/control.h"
17 #include "signontracker.h"
18
19 #define MAXWHOISLINES 50 /* from the ircd */
20
21 MODULE_VERSION("");
22
23 static void hook_newnick(int hook, void *arg);
24 static void hook_rename(int hook, void *arg);
25 static void queue_scan_nick(nick *np);
26 static void flush_queue_sched(void *arg);
27 static int whois_reply_handler(void *source, int cargc, char **cargv);
28
29 static int nickext = -1;
30
31 #define QUEUEBUFLEN (MAXWHOISLINES*(NICKLEN+1)+1)
32
33 static struct {
34 char buf[QUEUEBUFLEN];
35 int len;
36 void *sched;
37 } queue[MAXSERVERS];
38
39 void _init(void) {
40 nick *np;
41 int i;
42
43 CCASSERT(sizeof(time_t) <= sizeof(long));
44
45 nickext = registernickext("signontracker");
46 if(nickext < 0)
47 return;
48
49 registerhook(HOOK_NICK_NEWNICK, &hook_newnick);
50 registerhook(HOOK_NICK_RENAME, &hook_rename);
51 registernumerichandler(317, whois_reply_handler, 7);
52
53 for(i=0;i<NICKHASHSIZE;i++)
54 for(np=nicktable[i];np;np=np->next)
55 queue_scan_nick(np);
56 }
57
58 void _fini(void) {
59 int i;
60
61 if(nickext < 0)
62 return;
63
64 deregisterhook(HOOK_NICK_NEWNICK, &hook_newnick);
65 deregisterhook(HOOK_NICK_RENAME, &hook_rename);
66 deregisternumerichandler(317, whois_reply_handler);
67
68 for(i=0;i<MAXSERVERS;i++)
69 deleteschedule(queue[i].sched, flush_queue_sched, (void *)(long)i);
70
71 releasenickext(nickext);
72 }
73
74 static int whois_reply_handler(void *source, int cargc, char **cargv) {
75 time_t connected;
76 nick *np;
77
78 if(cargc < 5)
79 return CMD_OK;
80
81 np = getnickbynick(cargv[3]);
82 if(!np)
83 return CMD_OK;
84
85 connected = strtoul(cargv[5], NULL, 10);
86 np->exts[nickext] = (void *)connected;
87
88 triggerhook(HOOK_SIGNONTRACKER_HAVETIME, np);
89
90 return CMD_OK;
91 }
92
93 static void flush_queue(int server) {
94 char control_numeric[6];
95
96 if(!queue[server].len)
97 return;
98
99 if(!mynick) {
100 /* TODO: fix */
101 Error("signontracker", ERR_STOP, "Unable to get control nick.");
102 return;
103 }
104 longtonumeric2(mynick->numeric, 5, control_numeric);
105
106 irc_send("%s W %s %s", control_numeric, longtonumeric(server, 2), queue[server].buf);
107 queue[server].len = 0;
108 queue[server].buf[0] = '\0';
109 }
110
111 static void queue_scan_nick(nick *np) {
112 int server;
113
114 if(NickOnServiceServer(np)) /* TODO: fix */
115 return;
116
117 server = homeserver(np->numeric);
118
119 if(queue[server].len)
120 strlcat(queue[server].buf, ",", QUEUEBUFLEN);
121
122 queue[server].len++;
123
124 strlcat(queue[server].buf, np->nick, QUEUEBUFLEN);
125
126 if(queue[server].len == MAXWHOISLINES) {
127 flush_queue(server);
128 } else if(queue[server].sched == NULL) {
129 queue[server].sched = scheduleoneshot(time(NULL), flush_queue_sched, (void *)(long)server);
130 }
131 }
132
133 static void flush_queue_sched(void *arg) {
134 int server = (long)arg;
135
136 flush_queue(server);
137 queue[server].sched = NULL;
138 }
139
140 static void hook_newnick(int hook, void *arg) {
141 nick *np = (nick *)arg;
142
143 queue_scan_nick(np);
144 }
145
146 static void hook_rename(int hook, void *arg) {
147 nick *np = (nick *)arg;
148
149 if(getnicksignon(np))
150 return;
151
152 queue_scan_nick(np);
153 }
154
155 time_t getnicksignon(nick *np) {
156 if(nickext < 0)
157 return 0;
158
159 return (time_t)np->exts[nickext];
160 }