]>
Commit | Line | Data |
---|---|---|
1 | #include <string.h> | |
2 | #include <stdio.h> | |
3 | #include <stdarg.h> | |
4 | #include "../core/hooks.h" | |
5 | #include "../lib/splitline.h" | |
6 | #include "../core/config.h" | |
7 | #include "../irc/irc.h" | |
8 | #include "../lib/array.h" | |
9 | #include "../lib/base64.h" | |
10 | #include "../lib/irc_string.h" | |
11 | #include "../core/error.h" | |
12 | #include "../localuser/localuser.h" | |
13 | #include "../lib/strlfunc.h" | |
14 | #include "../lib/sha1.h" | |
15 | #include "../irc/irc.h" | |
16 | #include "../lib/hmac.h" | |
17 | #include "../core/schedule.h" | |
18 | #include "xsb.h" | |
19 | ||
20 | static const char *DEFAULT_SERVICE_MASKS[] = { "services*.*.quakenet.org", (char *)0 }; | |
21 | ||
22 | static array defaultservicemasks_a; | |
23 | static sstring **defaultservicemasks; | |
24 | ||
25 | static void handlemaskprivmsg(int, void *); | |
26 | static CommandTree *cmds; | |
27 | ||
28 | struct messagequeue { | |
29 | struct messagequeue *next; | |
30 | long numeric; | |
31 | char buf[]; | |
32 | }; | |
33 | ||
34 | struct messagequeue *head, *tail; | |
35 | ||
36 | static nick *xsbnick; | |
37 | static void *xsbnicksched; | |
38 | ||
39 | static void handler(nick *target, int messagetype, void **args); | |
40 | static void flushqueue(void); | |
41 | ||
42 | static char *servicenick(char *server) { | |
43 | static char nickbuf[NICKLEN+1]; | |
44 | char digest[SHA1_DIGESTSIZE*2+1]; | |
45 | unsigned char digestbuf[SHA1_DIGESTSIZE]; | |
46 | SHA1_CTX s; | |
47 | ||
48 | SHA1Init(&s); | |
49 | SHA1Update(&s, (unsigned char *)server, strlen(server)); | |
50 | SHA1Final(digestbuf, &s); | |
51 | ||
52 | snprintf(nickbuf, sizeof(nickbuf), "XSB1-%s", hmac_printhex(digestbuf, digest, SHA1_DIGESTSIZE)); | |
53 | ||
54 | return nickbuf; | |
55 | } | |
56 | ||
57 | static void setuplocaluser(void *arg) { | |
58 | xsbnicksched = NULL; | |
59 | ||
60 | xsbnick = registerlocaluser(servicenick(myserver->content), "xsb", "xsb.quakenet.org", "eXtended service broadcast localuser (v1)", NULL, UMODE_OPER | UMODE_SERVICE | UMODE_INV, handler); | |
61 | if(!xsbnick) { | |
62 | xsbnicksched = scheduleoneshot(time(NULL)+1, setuplocaluser, NULL); | |
63 | } else { | |
64 | flushqueue(); | |
65 | } | |
66 | } | |
67 | ||
68 | void _init(void) { | |
69 | const char **p; | |
70 | array *servicemasks; | |
71 | ||
72 | cmds = newcommandtree(); | |
73 | if(!cmds) | |
74 | return; | |
75 | ||
76 | xsbnicksched = scheduleoneshot(time(NULL)+1, setuplocaluser, NULL); | |
77 | ||
78 | registerhook(HOOK_NICK_MASKPRIVMSG, &handlemaskprivmsg); | |
79 | ||
80 | array_init(&defaultservicemasks_a, sizeof(sstring *)); | |
81 | ||
82 | for(p=DEFAULT_SERVICE_MASKS;*p;p++) { | |
83 | int i = array_getfreeslot(&defaultservicemasks_a); | |
84 | defaultservicemasks = (sstring **)defaultservicemasks_a.content; | |
85 | ||
86 | defaultservicemasks[i] = getsstring(*p, strlen(*p)); | |
87 | } | |
88 | ||
89 | servicemasks = getconfigitems("xsb", "servicemask"); | |
90 | if(!servicemasks || !servicemasks->cursi) | |
91 | Error("xsb", ERR_WARNING, "No service masks in config file (xsb/servicemask), using defaults."); | |
92 | } | |
93 | ||
94 | void _fini(void) { | |
95 | int i; | |
96 | struct messagequeue *q, *nq; | |
97 | ||
98 | if(xsbnick) | |
99 | deregisterlocaluser(xsbnick, NULL); | |
100 | ||
101 | if(xsbnicksched) | |
102 | deleteschedule(xsbnicksched, setuplocaluser, NULL); | |
103 | ||
104 | if(!cmds) | |
105 | return; | |
106 | ||
107 | destroycommandtree(cmds); | |
108 | ||
109 | deregisterhook(HOOK_NICK_MASKPRIVMSG, &handlemaskprivmsg); | |
110 | ||
111 | for(i=0;i<defaultservicemasks_a.cursi;i++) | |
112 | freesstring(defaultservicemasks[i]); | |
113 | array_free(&defaultservicemasks_a); | |
114 | ||
115 | for(q=head;q;q=nq) { | |
116 | nq = q->next; | |
117 | free(q); | |
118 | } | |
119 | ||
120 | head = NULL; | |
121 | tail = NULL; | |
122 | } | |
123 | ||
124 | void xsb_addcommand(const char *name, const int maxparams, CommandHandler handler) { | |
125 | addcommandtotree(cmds, name, 0, maxparams, handler); | |
126 | } | |
127 | ||
128 | void xsb_delcommand(const char *name, CommandHandler handler) { | |
129 | deletecommandfromtree(cmds, name, handler); | |
130 | } | |
131 | ||
132 | static void handlemaskprivmsg(int hooknum, void *args) { | |
133 | void **hargs = (void **)args; | |
134 | nick *source = hargs[0]; | |
135 | char *message = hargs[2]; | |
136 | Command *cmd; | |
137 | int cargc; | |
138 | char *cargv[50]; | |
139 | server *s; | |
140 | ||
141 | if(!source) | |
142 | return; | |
143 | ||
144 | if(!IsOper(source) || !IsService(source)) | |
145 | return; | |
146 | ||
147 | cargc = splitline(message, cargv, 50, 0); | |
148 | if(cargc < 2) | |
149 | return; | |
150 | ||
151 | if(strcmp(cargv[0], "XSB1")) | |
152 | return; | |
153 | ||
154 | s = &serverlist[homeserver(source->numeric)]; | |
155 | if(s->linkstate != LS_LINKED) { | |
156 | Error("xsb", ERR_WARNING, "Got XSB message from unlinked server (%s): %s", s->name->content, source->nick); | |
157 | return; | |
158 | } | |
159 | ||
160 | #ifndef XSB_DEBUG | |
161 | if(!(s->flags & SMODE_SERVICE)) { | |
162 | Error("xsb", ERR_WARNING, "Got XSB message from non-service server (%s): %s", s->name->content, source->nick); | |
163 | return; | |
164 | } | |
165 | #endif | |
166 | ||
167 | cmd = findcommandintree(cmds, cargv[1], 1); | |
168 | if(!cmd) | |
169 | return; | |
170 | ||
171 | if(cmd->maxparams < (cargc - 2)) { | |
172 | rejoinline(cargv[cmd->maxparams], cargc - (cmd->maxparams)); | |
173 | cargc = (cmd->maxparams) + 2; | |
174 | } | |
175 | ||
176 | (cmd->handler)(source, cargc - 2, &cargv[2]); | |
177 | } | |
178 | ||
179 | static void directsend(struct messagequeue *q) { | |
180 | nick *np = getnickbynumeric(q->numeric); | |
181 | if(!np || !IsOper(np) || !IsService(np)) | |
182 | return; | |
183 | ||
184 | #ifndef XSB_DEBUG | |
185 | if(!(serverlist[homeserver(np->numeric)].flags & SMODE_SERVICE)) | |
186 | return; | |
187 | #endif | |
188 | ||
189 | sendmessagetouser(xsbnick, np, "XSB1 %s", q->buf); | |
190 | } | |
191 | ||
192 | static void flushqueue(void) { | |
193 | struct messagequeue *q, *nq; | |
194 | ||
195 | for(q=head;q;q=nq) { | |
196 | nq = q->next; | |
197 | ||
198 | directsend(q); | |
199 | free(q); | |
200 | } | |
201 | head = NULL; | |
202 | tail = NULL; | |
203 | } | |
204 | ||
205 | static int getservicemasks(sstring ***masks) { | |
206 | array *aservicemasks = getconfigitems("xsb", "servicemask"); | |
207 | if(!aservicemasks || !aservicemasks->cursi) | |
208 | aservicemasks = &defaultservicemasks_a; | |
209 | ||
210 | *masks = (sstring **)aservicemasks->content; | |
211 | ||
212 | return aservicemasks->cursi; | |
213 | } | |
214 | ||
215 | static void xsb_send(nick *target, const char *format, ...) { | |
216 | char buf[512]; | |
217 | va_list va; | |
218 | size_t len; | |
219 | struct messagequeue *q; | |
220 | ||
221 | va_start(va, format); | |
222 | len = vsnprintf(buf, sizeof(buf), format, va); | |
223 | va_end(va); | |
224 | if(len >= sizeof(buf)) | |
225 | len = sizeof(buf); | |
226 | ||
227 | /* sucks */ | |
228 | q = (struct messagequeue *)malloc(sizeof(struct messagequeue) + len + 5); | |
229 | q->numeric = target->numeric; | |
230 | strlcpy(q->buf, buf, len + 2); | |
231 | ||
232 | if(xsbnick) { | |
233 | directsend(q); | |
234 | free(q); | |
235 | } else { | |
236 | q->next = NULL; | |
237 | if(tail) { | |
238 | tail->next = q; | |
239 | } else { | |
240 | head = q; | |
241 | } | |
242 | tail = q; | |
243 | } | |
244 | } | |
245 | ||
246 | void xsb_broadcast(const char *command, server *service, const char *format, ...) { | |
247 | char buf[512]; | |
248 | va_list va; | |
249 | sstring **servicemasks; | |
250 | int i, j; | |
251 | nick *np; | |
252 | unsigned int marker; | |
253 | ||
254 | va_start(va, format); | |
255 | vsnprintf(buf, sizeof(buf), format, va); | |
256 | va_end(va); | |
257 | ||
258 | if(service) { | |
259 | np = getnickbynick(servicenick(service->name->content)); | |
260 | if(!np) | |
261 | return; | |
262 | ||
263 | xsb_send(np, "%s %s", command, buf); | |
264 | return; | |
265 | } | |
266 | ||
267 | marker = nextservermarker(); | |
268 | ||
269 | for(i=getservicemasks(&servicemasks)-1;i>=0;i--) { | |
270 | char *mask = servicemasks[i]->content; | |
271 | for(j=0;j<MAXSERVERS;j++) { | |
272 | if(!serverlist[j].name || serverlist[j].marker == marker) | |
273 | continue; | |
274 | ||
275 | if(!match(mask, serverlist[j].name->content)) { | |
276 | serverlist[j].marker = marker; | |
277 | ||
278 | np = getnickbynick(servicenick(serverlist[j].name->content)); | |
279 | if(np) | |
280 | xsb_send(np, "%s %s", command, buf); | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
285 | ||
286 | void xsb_unicast(const char *command, nick *np, const char *format, ...) { | |
287 | char buf[512]; | |
288 | va_list va; | |
289 | ||
290 | va_start(va, format); | |
291 | vsnprintf(buf, sizeof(buf), format, va); | |
292 | va_end(va); | |
293 | ||
294 | xsb_send(np, "%s %s", command, buf); | |
295 | } | |
296 | ||
297 | int xsb_isservice(server *service) { | |
298 | int i; | |
299 | sstring **servicemasks; | |
300 | char *name = service->name->content; | |
301 | ||
302 | if(!(service->flags & SMODE_SERVICE)) | |
303 | return 0; | |
304 | ||
305 | for(i=getservicemasks(&servicemasks)-1;i>=0;i--) | |
306 | if(match2strings(servicemasks[i]->content, name)) | |
307 | return 1; | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | static void handler(nick *target, int messagetype, void **args) { | |
313 | switch(messagetype) { | |
314 | case LU_PRIVMSG: { | |
315 | void *newargs[3]; | |
316 | nick *sender = args[0]; | |
317 | char *cargv = args[1]; | |
318 | ||
319 | newargs[0] = sender; | |
320 | newargs[1] = NULL; | |
321 | newargs[2] = cargv; | |
322 | ||
323 | handlemaskprivmsg(0, newargs); | |
324 | break; | |
325 | } | |
326 | case LU_KILLED: | |
327 | xsbnick = NULL; | |
328 | xsbnicksched = scheduleoneshot(time(NULL)+1, setuplocaluser, NULL); | |
329 | break; | |
330 | } | |
331 | } |