1 |
/* PTlink protocol module for IRC Services. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
#include "services.h" |
11 |
#include "modules.h" |
12 |
#include "conffile.h" |
13 |
#include "language.h" |
14 |
#include "messages.h" |
15 |
#include "version.h" |
16 |
#include "modules/operserv/operserv.h" |
17 |
#include "modules/operserv/maskdata.h" |
18 |
#include "modules/nickserv/nickserv.h" |
19 |
#include "modules/chanserv/chanserv.h" |
20 |
|
21 |
#include "banexcept.c" |
22 |
#include "sjoin.c" |
23 |
#include "svsnick.c" |
24 |
|
25 |
/*************************************************************************/ |
26 |
|
27 |
/* "Nick" to use to indicate Services-set GLINEs */ |
28 |
#define GLINE_WHO "<ircservices>" |
29 |
|
30 |
|
31 |
static Module *module_operserv; |
32 |
|
33 |
static char *NetworkDomain = NULL; |
34 |
|
35 |
static int32 usermode_admin = 0; /* +aANT */ |
36 |
static int32 usermode_hiding = 0; /* +S */ |
37 |
static int32 chanmode_admins_only = 0; /* +A */ |
38 |
|
39 |
/*************************************************************************/ |
40 |
/***************** Local interface to external routines ******************/ |
41 |
/*************************************************************************/ |
42 |
|
43 |
static typeof(is_services_admin) *p_is_services_admin = NULL; |
44 |
|
45 |
static int local_is_services_admin(User *u) |
46 |
{ |
47 |
return p_is_services_admin && (*p_is_services_admin)(u); |
48 |
} |
49 |
#define is_services_admin local_is_services_admin |
50 |
|
51 |
/*************************************************************************/ |
52 |
/************************** User/channel modes ***************************/ |
53 |
/*************************************************************************/ |
54 |
|
55 |
struct modedata_init { |
56 |
uint8 mode; |
57 |
ModeData data; |
58 |
}; |
59 |
|
60 |
#define MI_ADMIN 0x01000000 /* Usermode given to admins */ |
61 |
#define MI_HIDING 0x02000000 /* Usermode for hiding users */ |
62 |
#define MI_ADMINS_ONLY 0x01000000 /* Chanmode for admin-only channels */ |
63 |
|
64 |
static const struct modedata_init new_usermodes[] = { |
65 |
{'g', {0x00000008}}, /* Receive globops */ |
66 |
{'h', {0x00000010}}, /* Helpop */ |
67 |
{'r', {0x00000020,0,0,0,MI_REGISTERED}}, |
68 |
/* Registered nick */ |
69 |
{'a', {0x00000040}}, /* Services admin */ |
70 |
{'A', {0x00000080}}, /* Server admin */ |
71 |
{'N', {0x00000100,0,0,0,MI_ADMIN}}, |
72 |
/* Network admin */ |
73 |
{'T', {0x00000200,0,0,0,MI_ADMIN}}, |
74 |
/* Technical admin */ |
75 |
/* Flags in this range are used by Unreal */ |
76 |
{'S', {0x00002000,0,0,0,MI_HIDING}}, |
77 |
/* Stealth mode (hides joins etc) */ |
78 |
{'B', {0x00004000}}, /* Is a bot ("deaf" in Unreal) */ |
79 |
{'R', {0x00008000}}, /* Allow PRIVMSGs from +r clients only */ |
80 |
{'p', {0x00010000}}, /* Private (don't show channels in /whois) */ |
81 |
{'v', {0x00020000}}, /* Don't allow DCCs */ |
82 |
}; |
83 |
|
84 |
static const struct modedata_init new_chanmodes[] = { |
85 |
{'R', {0x00000100,0,0,0,MI_REGNICKS_ONLY}}, |
86 |
/* Only identified users can join */ |
87 |
{'r', {0x00000200,0,0,0,MI_REGISTERED}}, |
88 |
/* Set for all registered channels */ |
89 |
{'c', {0x00000400,0,0}}, /* No ANSI colors in channel */ |
90 |
{'O', {0x00000800,0,0,0,MI_OPERS_ONLY}}, |
91 |
/* Only opers can join channel */ |
92 |
{'A', {0x00001000,0,0,0,MI_OPERS_ONLY|MI_ADMINS_ONLY}}, |
93 |
/* Only admins can join channel */ |
94 |
{'c', {0x00100000,0,0}}, /* Strip colors */ |
95 |
{'d', {0x00800000,0,0}}, /* No flooding */ |
96 |
{'S', {0x04000000,0,0}}, /* No spam */ |
97 |
{'q', {0x08000000,0,0}}, /* No quits */ |
98 |
{'K', {0x10000000,0,0}}, /* Send knock message on failed join */ |
99 |
{'e', {0x80000000,1,1,0,MI_MULTIPLE}}, |
100 |
/* Ban exceptions */ |
101 |
}; |
102 |
|
103 |
static const struct modedata_init new_chanusermodes[] = { |
104 |
{'a', {0x00000010,1,1,'.'}}, /* Channel owner */ |
105 |
}; |
106 |
|
107 |
static void init_modes(void) |
108 |
{ |
109 |
int i; |
110 |
|
111 |
for (i = 0; i < lenof(new_usermodes); i++) { |
112 |
usermodes[new_usermodes[i].mode] = new_usermodes[i].data; |
113 |
if (new_usermodes[i].data.info & MI_ADMIN) |
114 |
usermode_admin |= new_usermodes[i].data.flag; |
115 |
if (new_usermodes[i].data.info & MI_HIDING) |
116 |
usermode_hiding |= new_usermodes[i].data.flag; |
117 |
} |
118 |
for (i = 0; i < lenof(new_chanmodes); i++) { |
119 |
chanmodes[new_chanmodes[i].mode] = new_chanmodes[i].data; |
120 |
if (new_chanmodes[i].data.info & MI_ADMINS_ONLY) |
121 |
chanmode_admins_only |= new_chanmodes[i].data.flag; |
122 |
} |
123 |
for (i = 0; i < lenof(new_chanusermodes); i++) |
124 |
chanusermodes[new_chanusermodes[i].mode] = new_chanusermodes[i].data; |
125 |
|
126 |
mode_setup(); |
127 |
}; |
128 |
|
129 |
/*************************************************************************/ |
130 |
/************************* IRC message receiving *************************/ |
131 |
/*************************************************************************/ |
132 |
|
133 |
static void m_nick(char *source, int ac, char **av) |
134 |
{ |
135 |
char *newav[11]; |
136 |
|
137 |
if (*source) { |
138 |
/* Old user changing nicks. */ |
139 |
if (ac != 2) { |
140 |
module_log_debug(1, "NICK message: wrong number of parameters" |
141 |
" (%d) for source `%s'", ac, source); |
142 |
} else { |
143 |
do_nick(source, ac, av); |
144 |
} |
145 |
return; |
146 |
} |
147 |
|
148 |
/* New user. */ |
149 |
if (ac != 9) { |
150 |
module_log_debug(1, "NICK message: wrong number of parameters (%d)" |
151 |
" for new user", ac); |
152 |
return; |
153 |
} |
154 |
newav[ 0] = av[0]; /* Nick */ |
155 |
newav[ 1] = av[1]; /* Hop count */ |
156 |
newav[ 2] = av[2]; /* Timestamp */ |
157 |
newav[ 3] = av[4]; /* Username */ |
158 |
newav[ 4] = av[5]; /* Hostname */ |
159 |
newav[ 5] = av[7]; /* Server */ |
160 |
newav[ 6] = av[8]; /* Real name */ |
161 |
newav[ 7] = NULL; /* Services stamp */ |
162 |
newav[ 8] = NULL; /* IP address */ |
163 |
newav[ 9] = av[3]; /* Modes */ |
164 |
newav[10] = av[6]; /* User area (fake hostname) */ |
165 |
do_nick(source, 11, newav); |
166 |
} |
167 |
|
168 |
/*************************************************************************/ |
169 |
|
170 |
static void m_capab(char *source, int ac, char **av) |
171 |
{ |
172 |
char *s; |
173 |
int got_PTS4 = 0; |
174 |
int got_QS = 0; |
175 |
int got_EX = 0; |
176 |
|
177 |
if (ac < 1) { |
178 |
module_log("received CAPAB with no parameters--broken ircd?"); |
179 |
} else { |
180 |
for (s = strtok(av[0]," "); s; s = strtok(NULL," ")) { |
181 |
if (stricmp(s, "PTS4") == 0) |
182 |
got_PTS4 = 1; |
183 |
else if (stricmp(s, "QS") == 0) |
184 |
got_QS = 1; |
185 |
else if (stricmp(s, "EX") == 0) |
186 |
got_EX = 1; |
187 |
} |
188 |
} |
189 |
if (!got_PTS4 || !got_QS || !got_EX) { |
190 |
module_log("CAPAB: capabilities missing:%s%s%s", |
191 |
got_PTS4 ? "" : " PTS4", |
192 |
got_QS ? "" : " QS", |
193 |
got_EX ? "" : " EX"); |
194 |
send_error("Need PTS4/QS/EX capabilities"); |
195 |
strbcpy(quitmsg, "Remote server doesn't support all of PTS4/QS/EX"); |
196 |
quitting = 1; |
197 |
} |
198 |
} |
199 |
|
200 |
/*************************************************************************/ |
201 |
|
202 |
static void m_svinfo(char *source, int ac, char **av) |
203 |
{ |
204 |
if (ac < 2) { |
205 |
module_log("received SVINFO with <2 parameters--broken ircd?"); |
206 |
send_error("Invalid SVINFO received (at least 2 parameters needed)"); |
207 |
quitting = 1; |
208 |
} else { |
209 |
if (atoi(av[1]) > 6 || atoi(av[0]) < 6) { |
210 |
send_error("Need protocol version 6 support"); |
211 |
strbcpy(quitmsg, |
212 |
"Remote server doesn't support protocol version 6"); |
213 |
quitting = 1; |
214 |
} |
215 |
} |
216 |
} |
217 |
|
218 |
/*************************************************************************/ |
219 |
|
220 |
static void m_sjoin(char *source, int ac, char **av) |
221 |
{ |
222 |
if (ac < 4) { |
223 |
module_log("SJOIN: expected >=4 params, got %d (broken ircd?)", ac); |
224 |
return; |
225 |
} |
226 |
do_sjoin(source, ac, av); |
227 |
} |
228 |
|
229 |
/*************************************************************************/ |
230 |
|
231 |
static void m_newmask(char *source, int ac, char **av) |
232 |
{ |
233 |
char *newuser, *newhost; |
234 |
User *u; |
235 |
|
236 |
if (ac < 1) { |
237 |
module_log("NEWUSER: parameters missing--broken ircd?"); |
238 |
return; |
239 |
} |
240 |
if (!(u = get_user(source))) { |
241 |
module_log("got NEWUSER from nonexistent user %s", source); |
242 |
return; |
243 |
} |
244 |
newuser = av[0]; |
245 |
newhost = strchr(newuser, '@'); |
246 |
if (newhost) |
247 |
*newhost++ = 0; |
248 |
else |
249 |
newhost = ""; |
250 |
free(u->username); |
251 |
u->username = sstrdup(newuser); |
252 |
free(u->host); |
253 |
u->host = sstrdup(newhost); |
254 |
} |
255 |
|
256 |
/*************************************************************************/ |
257 |
|
258 |
/* GLINE/SGLINE/SQLINE handling: cancel any Services-set lines from remote |
259 |
* servers (see comments in unreal.c/m_tkl() for details). |
260 |
*/ |
261 |
|
262 |
static void m_gline(char *source, int ac, char **av) |
263 |
{ |
264 |
typeof(get_maskdata) *p_get_maskdata = NULL; |
265 |
typeof(put_maskdata) *p_put_maskdata = NULL; |
266 |
|
267 |
if (ac < 3 || strcmp(av[2], GLINE_WHO) != 0) |
268 |
return; |
269 |
if (!(p_get_maskdata = get_module_symbol(NULL, "get_maskdata"))) |
270 |
return; |
271 |
if (!(p_put_maskdata = get_module_symbol(NULL, "put_maskdata"))) |
272 |
return; |
273 |
if ((*p_put_maskdata)((*p_get_maskdata)(MD_AKILL, av[0]))) |
274 |
return; |
275 |
send_cmd(ServerName, "UNGLINE :%s", av[0]); |
276 |
} |
277 |
|
278 |
static void m_sgline(char *source, int ac, char **av) |
279 |
{ |
280 |
typeof(get_maskdata) *p_get_maskdata = NULL; |
281 |
typeof(put_maskdata) *p_put_maskdata = NULL; |
282 |
int masklen; |
283 |
|
284 |
if (ac < 3) |
285 |
return; |
286 |
masklen = atoi(av[1]); |
287 |
if (masklen < strlen(av[2])) |
288 |
av[2][masklen] = 0; |
289 |
if (!(p_get_maskdata = get_module_symbol(NULL, "get_maskdata"))) |
290 |
return; |
291 |
if (!(p_put_maskdata = get_module_symbol(NULL, "put_maskdata"))) |
292 |
return; |
293 |
if ((*p_put_maskdata)((*p_get_maskdata)(MD_SGLINE, av[2]))) |
294 |
return; |
295 |
send_cmd(ServerName, "UNSGLINE :%s", av[2]); |
296 |
} |
297 |
|
298 |
static void m_sqline(char *source, int ac, char **av) |
299 |
{ |
300 |
typeof(get_maskdata) *p_get_maskdata = NULL; |
301 |
typeof(put_maskdata) *p_put_maskdata = NULL; |
302 |
|
303 |
if (ac < 1) |
304 |
return; |
305 |
if (!(p_get_maskdata = get_module_symbol(NULL, "get_maskdata"))) |
306 |
return; |
307 |
if (!(p_put_maskdata = get_module_symbol(NULL, "put_maskdata"))) |
308 |
return; |
309 |
if ((*p_put_maskdata)((*p_get_maskdata)(MD_SQLINE, av[0]))) |
310 |
return; |
311 |
send_cmd(ServerName, "UNSQLINE :%s", av[0]); |
312 |
} |
313 |
|
314 |
/*************************************************************************/ |
315 |
|
316 |
static Message ptlink_messages[] = { |
317 |
{ "CAPAB", m_capab }, |
318 |
{ "GLINE", m_gline }, |
319 |
{ "GLOBOPS", NULL }, |
320 |
{ "GNOTICE", NULL }, |
321 |
{ "GOPER", NULL }, |
322 |
{ "NEWMASK", m_newmask }, |
323 |
{ "NICK", m_nick }, |
324 |
{ "SILENCE", NULL }, |
325 |
{ "SJOIN", m_sjoin }, |
326 |
{ "SGLINE", m_sgline }, |
327 |
{ "SQLINE", m_sqline }, |
328 |
{ "SVINFO", m_svinfo }, |
329 |
{ "SVLINE", NULL }, |
330 |
{ "UNGLINE", NULL }, |
331 |
{ "UNSGLINE", NULL }, |
332 |
{ "UNSQLINE", NULL }, |
333 |
{ "UNSVLINE", NULL }, |
334 |
{ "UNZOMBIE", NULL }, |
335 |
{ "ZOMBIE", NULL }, |
336 |
{ NULL } |
337 |
}; |
338 |
|
339 |
/*************************************************************************/ |
340 |
/************************** IRC message sending **************************/ |
341 |
/*************************************************************************/ |
342 |
|
343 |
/* Send a NICK command for a new user. */ |
344 |
|
345 |
static void do_send_nick(const char *nick, const char *user, const char *host, |
346 |
const char *server, const char *name, |
347 |
const char *modes) |
348 |
{ |
349 |
/* NICK <nick> <hops> <TS> <umode> <user> <host> <fakehost> <server> |
350 |
* :<ircname> */ |
351 |
send_cmd(NULL, "NICK %s 1 %ld +%s %s %s %s %s 0 :%s", nick, |
352 |
(long)time(NULL), modes, user, host, host, server, name); |
353 |
} |
354 |
|
355 |
/*************************************************************************/ |
356 |
|
357 |
/* Send a NICK command to change an existing user's nick. */ |
358 |
|
359 |
static void do_send_nickchange(const char *nick, const char *newnick) |
360 |
{ |
361 |
send_cmd(nick, "NICK %s %ld", newnick, (long)time(NULL)); |
362 |
} |
363 |
|
364 |
/*************************************************************************/ |
365 |
|
366 |
/* Send a command to change a user's "real name". */ |
367 |
|
368 |
static void do_send_namechange(const char *nick, const char *newname) |
369 |
{ |
370 |
/* Not supported by this protocol. */ |
371 |
} |
372 |
|
373 |
/*************************************************************************/ |
374 |
|
375 |
/* Send a SERVER command, and anything else needed at the beginning of the |
376 |
* connection. |
377 |
*/ |
378 |
|
379 |
static void do_send_server(void) |
380 |
{ |
381 |
Module *mod; |
382 |
int32 maxusercnt = 0; |
383 |
|
384 |
send_cmd(NULL, "PASS %s :TS", RemotePassword); |
385 |
send_cmd(NULL, "CAPAB :PTS4 QS EX"); |
386 |
send_cmd(NULL, "SERVER %s 1 ircservices-%s :%s", ServerName, |
387 |
version_number, ServerDesc); |
388 |
send_cmd(NULL, "SVINFO 6 6"); |
389 |
if ((mod = find_module("operserv/main")) != NULL) { |
390 |
typeof(get_operserv_data) *p_get_operserv_data; |
391 |
p_get_operserv_data = get_module_symbol(mod, "get_operserv_data"); |
392 |
if (p_get_operserv_data) |
393 |
p_get_operserv_data(OSDATA_MAXUSERCNT, &maxusercnt); |
394 |
} |
395 |
send_cmd(NULL, "SVSINFO %ld %d", (long)start_time, maxusercnt); |
396 |
} |
397 |
|
398 |
/*************************************************************************/ |
399 |
|
400 |
/* Send a SERVER command for a remote (juped) server. */ |
401 |
|
402 |
static void do_send_server_remote(const char *server, const char *reason) |
403 |
{ |
404 |
send_cmd(NULL, "SERVER %s 2 :%s", server, reason); |
405 |
} |
406 |
|
407 |
/*************************************************************************/ |
408 |
|
409 |
/* Send a WALLOPS (really a GLOBOPS). */ |
410 |
|
411 |
static void do_wallops(const char *source, const char *fmt, ...) |
412 |
{ |
413 |
va_list args; |
414 |
char buf[BUFSIZE]; |
415 |
|
416 |
va_start(args, fmt); |
417 |
vsnprintf(buf, sizeof(buf), fmt, args); |
418 |
va_end(args); |
419 |
send_cmd(source ? source : ServerName, "GLOBOPS :%s", buf); |
420 |
} |
421 |
|
422 |
/*************************************************************************/ |
423 |
|
424 |
/* Send a NOTICE to all users on the network. */ |
425 |
|
426 |
static void do_notice_all(const char *source, const char *fmt, ...) |
427 |
{ |
428 |
va_list args; |
429 |
char msgbuf[BUFSIZE]; |
430 |
|
431 |
va_start(args, fmt); |
432 |
vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); |
433 |
va_end(args); |
434 |
if (NetworkDomain) { |
435 |
send_cmd(source, "NOTICE $*.%s :%s", NetworkDomain, msgbuf); |
436 |
} else { |
437 |
/* Go through all common top-level domains. If you have others, |
438 |
* add them here. */ |
439 |
send_cmd(source, "NOTICE $*.com :%s", msgbuf); |
440 |
send_cmd(source, "NOTICE $*.net :%s", msgbuf); |
441 |
send_cmd(source, "NOTICE $*.org :%s", msgbuf); |
442 |
send_cmd(source, "NOTICE $*.edu :%s", msgbuf); |
443 |
} |
444 |
} |
445 |
|
446 |
/*************************************************************************/ |
447 |
|
448 |
/* Send a command which modifies channel status. */ |
449 |
|
450 |
static void do_send_channel_cmd(const char *source, const char *fmt, ...) |
451 |
{ |
452 |
va_list args; |
453 |
|
454 |
va_start(args, fmt); |
455 |
vsend_cmd(ServerName, fmt, args); |
456 |
va_end(args); |
457 |
} |
458 |
|
459 |
/*************************************************************************/ |
460 |
/******************************* Callbacks *******************************/ |
461 |
/*************************************************************************/ |
462 |
|
463 |
static int do_user_create(User *user, int ac, char **av) |
464 |
{ |
465 |
user->fakehost = sstrdup(av[9]); |
466 |
return 0; |
467 |
} |
468 |
|
469 |
/*************************************************************************/ |
470 |
|
471 |
static int do_user_mode(User *user, int modechar, int add, char **av) |
472 |
{ |
473 |
switch (modechar) { |
474 |
case 'o': |
475 |
if (add) { |
476 |
user->mode |= UMODE_o; |
477 |
if (add && user_identified(user) && is_services_admin(user)) |
478 |
send_cmd(ServerName, "SVSMODE %s +a", user->nick); |
479 |
user->mode &= ~UMODE_o; |
480 |
} |
481 |
return 0; |
482 |
|
483 |
case 'r': |
484 |
if (user_identified(user)) { |
485 |
if (!add) |
486 |
send_cmd(ServerName, "SVSMODE %s +r", user->nick); |
487 |
} else { |
488 |
if (add) |
489 |
send_cmd(ServerName, "SVSMODE %s -r", user->nick); |
490 |
} |
491 |
return 1; |
492 |
|
493 |
case 'a': |
494 |
if (is_oper(user)) { |
495 |
if (is_services_admin(user)) { |
496 |
if (!add) |
497 |
send_cmd(ServerName, "SVSMODE %s +a", user->nick); |
498 |
} else { |
499 |
if (add) |
500 |
send_cmd(ServerName, "SVSMODE %s -a", user->nick); |
501 |
} |
502 |
return 1; |
503 |
} |
504 |
} |
505 |
return 0; |
506 |
} |
507 |
|
508 |
/*************************************************************************/ |
509 |
|
510 |
static int do_nick_identified(User *u, int old_status) |
511 |
{ |
512 |
if (is_oper(u) && is_services_admin(u)) |
513 |
send_cmd(ServerName, "SVSMODE %s +a", u->nick); |
514 |
return 0; |
515 |
} |
516 |
|
517 |
/*************************************************************************/ |
518 |
|
519 |
static int do_set_topic(const char *source, Channel *c, const char *topic, |
520 |
const char *setter, time_t t) |
521 |
{ |
522 |
if (setter) |
523 |
return 0; |
524 |
c->topic_time = t; |
525 |
send_cmd(source, "TOPIC %s %s %ld :%s", c->name, c->topic_setter, |
526 |
(long)c->topic_time, c->topic ? c->topic : ""); |
527 |
return 1; |
528 |
} |
529 |
|
530 |
/*************************************************************************/ |
531 |
|
532 |
static int do_check_chan_user_modes(const char *source, User *user, |
533 |
Channel *c, int32 modes) |
534 |
{ |
535 |
/* Don't do anything to hiding users */ |
536 |
return ((user->mode & usermode_hiding) ? 1 : 0); |
537 |
} |
538 |
|
539 |
/*************************************************************************/ |
540 |
|
541 |
static int do_check_kick(User *user, const char *chan, ChannelInfo *ci, |
542 |
char **mask_ret, const char **reason_ret) |
543 |
{ |
544 |
Channel *c = get_channel(chan); |
545 |
|
546 |
/* Don't let plain opers into +A (admin only) channels */ |
547 |
if ((((c?c->mode:0) | (ci?ci->mlock.on:0)) & chanmode_admins_only) |
548 |
&& !(user->mode & usermode_admin) |
549 |
) { |
550 |
*mask_ret = create_mask(user, 1); |
551 |
*reason_ret = getstring(user->ngi, CHAN_NOT_ALLOWED_TO_JOIN); |
552 |
return 1; |
553 |
} |
554 |
return 0; |
555 |
} |
556 |
|
557 |
/*************************************************************************/ |
558 |
|
559 |
static int do_send_akill(const char *username, const char *host, |
560 |
time_t expires, const char *who, const char *reason) |
561 |
{ |
562 |
time_t now = time(NULL); |
563 |
|
564 |
send_cmd(ServerName, "GLINE %s@%s %ld %s :%s", username, host, |
565 |
(long)((expires && expires > now) ? expires - now : 0), |
566 |
GLINE_WHO, reason); |
567 |
return 1; |
568 |
} |
569 |
|
570 |
/*************************************************************************/ |
571 |
|
572 |
static int do_cancel_akill(const char *username, const char *host) |
573 |
{ |
574 |
send_cmd(ServerName, "UNGLINE %s@%s", username, host); |
575 |
return 1; |
576 |
} |
577 |
|
578 |
/*************************************************************************/ |
579 |
|
580 |
static int do_send_sgline(const char *mask, time_t expires, const char *who, |
581 |
const char *reason) |
582 |
{ |
583 |
send_cmd(ServerName, "SGLINE %d :%s:%s", (int)strlen(mask), mask, reason); |
584 |
return 1; |
585 |
} |
586 |
|
587 |
static int do_send_sqline(const char *mask, time_t expires, const char *who, |
588 |
const char *reason) |
589 |
{ |
590 |
send_cmd(ServerName, "SQLINE %s :%s", mask, reason); |
591 |
return 1; |
592 |
} |
593 |
|
594 |
/*************************************************************************/ |
595 |
|
596 |
static int do_cancel_sgline(const char *mask) |
597 |
{ |
598 |
send_cmd(ServerName, "UNSGLINE :%s", mask); |
599 |
return 1; |
600 |
} |
601 |
|
602 |
static int do_cancel_sqline(const char *mask) |
603 |
{ |
604 |
send_cmd(ServerName, "UNSQLINE %s", mask); |
605 |
return 1; |
606 |
} |
607 |
|
608 |
/*************************************************************************/ |
609 |
/***************************** Module stuff ******************************/ |
610 |
/*************************************************************************/ |
611 |
|
612 |
ConfigDirective module_config[] = { |
613 |
{ "NetworkDomain", { { CD_STRING, 0, &NetworkDomain } } }, |
614 |
SJOIN_CONFIG, |
615 |
{ NULL } |
616 |
}; |
617 |
|
618 |
/*************************************************************************/ |
619 |
|
620 |
static int do_load_module(Module *mod, const char *modname) |
621 |
{ |
622 |
if (strcmp(modname, "operserv/main") == 0) { |
623 |
module_operserv = mod; |
624 |
p_is_services_admin = get_module_symbol(mod, "is_services_admin"); |
625 |
if (!p_is_services_admin) { |
626 |
module_log("warning: unable to look up symbol `is_services_admin'" |
627 |
" in module `operserv/main'"); |
628 |
} |
629 |
} else if (strcmp(modname, "operserv/akill") == 0) { |
630 |
if (!add_callback(mod, "send_akill", do_send_akill)) |
631 |
module_log("Unable to add send_akill callback"); |
632 |
if (!add_callback(mod, "cancel_akill", do_cancel_akill)) |
633 |
module_log("Unable to add cancel_akill callback"); |
634 |
} else if (strcmp(modname, "operserv/sline") == 0) { |
635 |
if (!add_callback(mod, "send_sgline", do_send_sgline)) |
636 |
module_log("Unable to add send_sgline callback"); |
637 |
if (!add_callback(mod, "send_sqline", do_send_sqline)) |
638 |
module_log("Unable to add send_sqline callback"); |
639 |
if (!add_callback(mod, "cancel_sgline", do_cancel_sgline)) |
640 |
module_log("Unable to add cancel_sgline callback"); |
641 |
if (!add_callback(mod, "cancel_sqline", do_cancel_sqline)) |
642 |
module_log("Unable to add cancel_sqline callback"); |
643 |
} else if (strcmp(modname, "nickserv/main") == 0) { |
644 |
if (!add_callback(mod, "identified", do_nick_identified)) |
645 |
module_log("Unable to add NickServ identified callback"); |
646 |
} else if (strcmp(modname, "chanserv/main") == 0) { |
647 |
if (!add_callback(mod, "check_chan_user_modes", |
648 |
do_check_chan_user_modes)) |
649 |
module_log("Unable to add ChanServ check_chan_user_modes" |
650 |
" callback"); |
651 |
if (!add_callback(mod, "check_kick", do_check_kick)) |
652 |
module_log("Unable to add ChanServ check_kick callback"); |
653 |
} |
654 |
return 0; |
655 |
} |
656 |
|
657 |
/*************************************************************************/ |
658 |
|
659 |
static int do_unload_module(Module *mod) |
660 |
{ |
661 |
if (mod == module_operserv) { |
662 |
module_operserv = NULL; |
663 |
p_is_services_admin = NULL; |
664 |
} |
665 |
return 0; |
666 |
} |
667 |
|
668 |
/*************************************************************************/ |
669 |
|
670 |
int init_module(void) |
671 |
{ |
672 |
unsigned char c; |
673 |
|
674 |
|
675 |
protocol_name = "PTlink"; |
676 |
protocol_version = "6.x"; |
677 |
protocol_features = PF_BANEXCEPT | PF_NOQUIT; |
678 |
protocol_nickmax = 20; |
679 |
|
680 |
if (!register_messages(ptlink_messages)) { |
681 |
module_log("Unable to register messages"); |
682 |
exit_module(1); |
683 |
return 0; |
684 |
} |
685 |
|
686 |
if (!add_callback(NULL, "load module", do_load_module) |
687 |
|| !add_callback(NULL, "unload module", do_unload_module) |
688 |
|| !add_callback(NULL, "user create", do_user_create) |
689 |
|| !add_callback(NULL, "user MODE", do_user_mode) |
690 |
|| !add_callback(NULL, "set topic", do_set_topic) |
691 |
) { |
692 |
module_log("Unable to add callbacks"); |
693 |
exit_module(1); |
694 |
return 0; |
695 |
} |
696 |
|
697 |
if (!init_banexcept() || !init_sjoin() || !init_svsnick("SVSNICK")) { |
698 |
exit_module(1); |
699 |
return 0; |
700 |
} |
701 |
|
702 |
init_modes(); |
703 |
|
704 |
irc_lowertable['['] = '['; |
705 |
irc_lowertable['\\'] = '\\'; |
706 |
irc_lowertable[']'] = ']'; |
707 |
/* Note that PTlink extended character sets are not supported */ |
708 |
valid_nick_table['\\'] = 0; |
709 |
for (c = 0; c < 32; c++) |
710 |
valid_chan_table[c] = 0; |
711 |
valid_chan_table['\\'] = 0; |
712 |
valid_chan_table[160] = 0; |
713 |
|
714 |
send_nick = do_send_nick; |
715 |
send_nickchange = do_send_nickchange; |
716 |
send_namechange = do_send_namechange; |
717 |
send_server = do_send_server; |
718 |
send_server_remote = do_send_server_remote; |
719 |
wallops = do_wallops; |
720 |
notice_all = do_notice_all; |
721 |
send_channel_cmd = do_send_channel_cmd; |
722 |
pseudoclient_modes = ""; |
723 |
enforcer_modes = ""; |
724 |
pseudoclient_oper = 0; |
725 |
|
726 |
mapstring(OPER_BOUNCY_MODES, OPER_BOUNCY_MODES_U_LINE); |
727 |
|
728 |
return 1; |
729 |
} |
730 |
|
731 |
/*************************************************************************/ |
732 |
|
733 |
int exit_module(int shutdown) |
734 |
{ |
735 |
if (!shutdown) { |
736 |
/* Do not allow removal */ |
737 |
return 0; |
738 |
} |
739 |
|
740 |
exit_svsnick(); |
741 |
exit_sjoin(); |
742 |
exit_banexcept(); |
743 |
remove_callback(NULL, "set topic", do_set_topic); |
744 |
remove_callback(NULL, "user MODE", do_user_mode); |
745 |
remove_callback(NULL, "user create", do_user_create); |
746 |
remove_callback(NULL, "unload module", do_unload_module); |
747 |
remove_callback(NULL, "load module", do_load_module); |
748 |
unregister_messages(ptlink_messages); |
749 |
return 1; |
750 |
} |
751 |
|
752 |
/*************************************************************************/ |
753 |
|
754 |
/* |
755 |
* Local variables: |
756 |
* c-file-style: "stroustrup" |
757 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
758 |
* indent-tabs-mode: nil |
759 |
* End: |
760 |
* |
761 |
* vim: expandtab shiftwidth=4: |
762 |
*/ |