]> jfr.im git - irc/quakenet/newserv.git/blame - noperserv/noperserv.c
Implement notice flag +G for automated gline messages.
[irc/quakenet/newserv.git] / noperserv / noperserv.c
CommitLineData
3e3692bf
CP
1/*
2 * NOperserv v0.01
3 *
4 * A replacement for Germania's ageing Operservice2
5 *
6 * Copyright (C) 2005 Chris Porter.
7 */
8
9#include "../localuser/localuser.h"
10#include "../lib/irc_string.h"
d7a70850 11#include "../lib/strlfunc.h"
87698d77 12#include "../lib/version.h"
fb17e427 13#include "../authext/authext.h"
3e3692bf
CP
14#include "noperserv.h"
15#include "noperserv_db.h"
16#include "noperserv_hooks.h"
17#include "noperserv_policy.h"
18
19#include <stdio.h>
20#include <string.h>
21#include <stdarg.h>
22
70b0a4e5 23MODULE_VERSION("");
87698d77 24
3e3692bf
CP
25#define FLAGBUFLEN 100
26
27#define NO_FOUND_NICKNAME 1
28#define NO_FOUND_AUTHNAME 2
29
7b26c20e
GB
30int noperserv_ext;
31
3e3692bf
CP
32const flag no_commandflags[] = {
33 { 'o', __NO_OPER },
34 { 't', __NO_TRUST },
35 { 's', __NO_STAFF },
36 { 'S', __NO_SEC },
37 { 'd', __NO_DEVELOPER },
38 { 'L', __NO_LEGACY },
39 { 'O', __NO_OPERED },
40 { 'r', __NO_AUTHED },
41 { 'R', __NO_ACCOUNT },
3f5ead93 42 { 'Y', __NO_RELAY },
3e3692bf
CP
43 { '\0', 0 }
44 };
45
46const flag no_userflags[] = {
47 { 'o', __NO_OPER },
48 { 't', __NO_TRUST },
49 { 's', __NO_STAFF },
50 { 'S', __NO_SEC },
51 { 'd', __NO_DEVELOPER },
3f5ead93 52 { 'Y', __NO_RELAY },
3e3692bf
CP
53 { '\0', 0 }
54 };
55
56const flag no_noticeflags[] = {
57 { 'm', NL_MANAGEMENT }, /* hello, password, userflags, noticeflags */
58 { 't', NL_TRUSTS }, /* trust stuff... */
a50e1bf1
CP
59 { 'k', NL_KICKKILLS }, /* KICK/KILL commands */
60 { 'I', NL_MISC }, /* misc commands */
3e3692bf 61 { 'g', NL_GLINES }, /* GLINE commands */
4e52c61d 62 { 'G', NL_GLINES_AUTO }, /* automated gline messages */
3e3692bf
CP
63 { 'h', NL_HITS }, /* Where a gline or kill is set automatically by the bot */
64 { 'c', NL_CLONING }, /* Clone detection */
65 { 'C', NL_CLEARCHAN }, /* When someone clearchans */
66 { 'f', NL_FAKEUSERS }, /* Fakeuser addition */
67 { 'b', NL_BROADCASTS }, /* Broadcast/mbroadcast/sbroadcast */
68 { 'o', NL_OPERATIONS }, /* insmod/rmmod/etc */
69 { 'O', NL_OPERING }, /* when someone opers */
70 { 'n', NL_NOTICES }, /* turn off to receive notices instead of privmsgs */
71 { 'A', NL_ALL_COMMANDS }, /* all commands sent */
72 { '\0', 0 }
73 };
74
75int noperserv_hello(void *sender, int cargc, char **cargv);
76int noperserv_noticeflags(void *sender, int cargc, char **cargv);
77int noperserv_userflags(void *sender, int cargc, char **cargv);
78int noperserv_deluser(void *sender, int cargc, char **cargv);
79void noperserv_oper_detection(int hooknum, void *arg);
bb4b25ee 80void noperserv_reply(nick *np, char *format, ...) __attribute__ ((format (printf, 2, 3)));
3e3692bf 81
7a32ca6e
CP
82int init = 0;
83
3e3692bf 84void _init() {
7a32ca6e
CP
85 if(!noperserv_load_db())
86 return;
3e3692bf 87
7a32ca6e 88 noperserv_ext = registernickext("noperserv");
3e3692bf
CP
89
90 noperserv_setup_hooks();
91
92 registercontrolhelpcmd("hello", NO_OPERED | NO_AUTHED, 1, &noperserv_hello, "Syntax: HELLO ?nickname|#authname?\nCreates an account on the service for the specified nick, or if one isn't supplied, your nickname.");
cc3768c4
CP
93 registercontrolhelpcmd("userflags", NO_ACCOUNT, 2, &noperserv_userflags,
94 "Syntax: USERFLAGS <nickname|#authname> ?modifications?\n"
95 " Views and modifies user permissions.\n"
96 " If no nickname or authname is supplied, you are substituted for it.\n"
97 " If no flags are supplied, flags are just displayed instead of modified."
98 " Flags:\n"
99 " +o: Operator\n"
100 " +s: Staff member\n"
101 " +S: Security team member\n"
102 " +d: NOperserv developer\n"
103 " +t: Trust queue worker\n"
3f5ead93 104 " +Y: Relay\n"
cc3768c4
CP
105 " Additional flags may show up in SHOWCOMMANDS but are not userflags as such:\n"
106 " +r: Authed user\n"
a50e1bf1
CP
107 " +R: Registered NOperserv user\n"
108 " +O: Must be /OPER'ed\n"
cc3768c4
CP
109 " +L: Legacy command\n"
110 );
3e3692bf
CP
111 registercontrolhelpcmd("noticeflags", NO_ACCOUNT, 1, &noperserv_noticeflags,
112 "Syntax: NOTICEFLAGS ?(nickname|#authname)|flags?\n"
113 " This command can view and modify your own notice flags, and view that of other users.\n"
114 " Flags:\n"
115 " +m: Management (hello, password, userflags, noticeflags)\n"
116 " +t: Trusts\n"
a50e1bf1 117 " +k: KICK/KILL commands\n"
3e3692bf 118 " +g: GLINE commands\n"
4e52c61d 119 " +G: automated gline messages\n"
35070c19 120 " +h: Shows when glines are set by code (hits)\n"
3e3692bf
CP
121 " +c: Clone information\n"
122 " +C: CLEARCHAN command\n"
cc3768c4 123 " +f: FAKEUSER commands\n"
3e3692bf
CP
124 " +b: BROADCAST commands\n"
125 " +o: Operation commands, such as insmod, rmmod, die, etc\n"
126 " +O: /OPER\n"
9aacd544 127 " +I: Misc commands (resync, etc)\n"
3e3692bf 128 " +n: Sends notices instead of privmsgs\n"
cc3768c4 129 " +A: Every single command sent to the service (spammy)\n"
3e3692bf
CP
130 );
131
132 registercontrolhelpcmd("deluser", NO_OPERED | NO_ACCOUNT, 2, &noperserv_deluser, "Syntax: DELUSER <nickname|#authname>\nDeletes the specified user.");
133 registerhook(HOOK_NICK_MODEOPER, &noperserv_oper_detection);
7a32ca6e
CP
134
135 init = 1;
3e3692bf
CP
136}
137
138#ifdef BROKEN_DLCLOSE
139void __fini() {
140#else
141void _fini() {
142#endif
7a32ca6e
CP
143 if(!init)
144 return;
145
3e3692bf
CP
146 deregisterhook(HOOK_NICK_MODEOPER, &noperserv_oper_detection);
147
148 deregistercontrolcmd("noticeflags", &noperserv_noticeflags);
149 deregistercontrolcmd("userflags", &noperserv_userflags);
150 deregistercontrolcmd("noticeflags", &noperserv_noticeflags);
7821bd5e
CP
151 deregistercontrolcmd("hello", &noperserv_hello);
152 deregistercontrolcmd("deluser", &noperserv_deluser);
3e3692bf
CP
153
154 noperserv_cleanup_hooks();
155
156 noperserv_cleanup_db();
157
158 releasenickext(noperserv_ext);
159}
160
161/* @test */
162int noperserv_hello(void *sender, int cargc, char **cargv) {
1fbb1306 163 char *newaccount = NULL;
3e3692bf
CP
164 no_autheduser *au;
165 int i;
166 nick *np = (nick *)sender, *np2, *target = NULL;
167
168 if(cargc == 0) {
169 newaccount = np->authname;
170 } else {
171 if(cargv[0][0] == '#') {
7b1a4a73 172 authname *a = getauthbyname(cargv[0] + 1);
fb17e427 173 if(!a) {
3e3692bf
CP
174 controlreply(np, "Cannot find anyone with that authname on the network.");
175 return CMD_ERROR;
176 }
ef49043d 177 newaccount = a->name;
3e3692bf
CP
178 } else {
179 target = getnickbynick(cargv[0]);
180 if(!target) {
181 controlreply(np, "Supplied nickname is not on the network.");
182 return CMD_ERROR;
183 }
184 if(!IsAccount(target)) {
185 controlreply(np, "Supplied user is not authed with the network.");
186 return CMD_ERROR;
187 }
188 newaccount = target->authname;
189 }
190 }
191 au = noperserv_get_autheduser(newaccount);
192 if(au) {
193 controlreply(np, "Authname already registered.");
194 return CMD_ERROR;
195 }
196
197 au = noperserv_new_autheduser(newaccount);
198 if(!au) {
199 controlreply(np, "Memory allocation error.");
200 return CMD_ERROR;
201 }
202
203 if(noperserv_get_autheduser_count() == 1) {
204 au->authlevel = NO_FIRST_USER_LEVEL;
205 au->noticelevel = NO_FIRST_USER_DEFAULT_NOTICELEVEL;
206 } else {
207 au->authlevel = NO_DEFAULT_LEVEL;
208 au->noticelevel = NO_DEFAULT_NOTICELEVEL;
209 }
210
211 au->id = noperserv_next_autheduser_id();
212 noperserv_update_autheduser(au);
213
214 for(i=0;i<NICKHASHSIZE;i++)
215 for(np2=nicktable[i];np2;np2=np2->next)
216 if(IsAccount(np2) && !ircd_strcmp(newaccount, np2->authname)) {
217 noperserv_add_to_autheduser(np2, au);
218 controlreply(np2, "An account has been created for you (auth %s).", au->authname->content);
219 if(NOGetAuthLevel(au))
220 controlreply(np2, "User flags: %s", printflags(NOGetAuthLevel(au), no_userflags));
221 controlreply(np2, "Notice flags: %s", printflags(NOGetNoticeLevel(au), no_noticeflags));
222 }
223
224 if(ircd_strcmp(np->authname, newaccount)) { /* send a message to the person who HELLO'ed if we haven't already been told */
225 controlreply(np, "Account created for auth %s.", au->authname->content);
226 if(NOGetAuthLevel(au))
227 controlreply(np, "User flags: %s", printflags(NOGetAuthLevel(au), no_userflags));
228 controlreply(np, "Notice flags: %s", printflags(NOGetNoticeLevel(au), no_noticeflags));
229 controlreply(np, "Instructions sent to all authed users.");
230 } else if(au->nick && au->nick->next) { /* if we have already been told, tell the user it was sent to more than themselves */
231 controlreply(np, "Instructions sent to all authed users.");
232 }
233
234 controlwall(NO_OPERED, NL_MANAGEMENT, "%s/%s just HELLO'ed: %s", np->nick, np->authname, au->authname->content);
235 return CMD_OK;
236}
237
238no_autheduser *noperserv_autheduser_from_command(nick *np, char *command, int *typefound, char **returned) {
239 no_autheduser *au;
240 if(command[0] == '#') {
241 au = noperserv_get_autheduser(command + 1);
242 if(!au) {
243 controlreply(np, "Authname not found.");
244 } else {
245 *typefound = NO_FOUND_AUTHNAME;
246 *returned = au->authname->content;
247 return au;
248 }
249 } else {
250 nick *np2 = getnickbynick(command);
251 if(!np2) {
252 controlreply(np, "Nickname not on the network.");
253 return CMD_OK;
254 }
255 if(!IsAccount(np2)) {
256 controlreply(np, "User is not authed with the network.");
257 return CMD_OK;
258 }
259 au = NOGetAuthedUser(np2);
260 if(!au) {
261 controlreply(np, "User does not have an account.");
262 } else {
263 *typefound = NO_FOUND_NICKNAME;
264 *returned = np2->nick;
265 return au;
266 }
267 }
268
269 return NULL;
270}
271
272int noperserv_noticeflags(void *sender, int cargc, char **cargv) {
273 nick *np = (nick *)sender;
274 no_autheduser *au;
275
276 if(cargc == 1) {
277 if((cargv[0][0] == '+') || (cargv[0][0] == '-')) {
278 int ret;
279 au = NOGetAuthedUser(np);
280 flag_t fwas = NOGetNoticeLevel(au), permittedchanges = noperserv_policy_permitted_noticeflags(au);
281
282 ret = setflags(&au->noticelevel, permittedchanges, cargv[0], no_noticeflags, REJECT_DISALLOWED | REJECT_UNKNOWN);
283 if(ret != REJECT_UNKNOWN) {
284 if(ret == REJECT_DISALLOWED) {
285 flag_t fnow = fwas;
286 setflags(&fnow, NL_ALL, cargv[0], no_noticeflags, REJECT_NONE);
287 if(fnow == fwas) {
288 controlreply(np, "No changes made to existing flags.");
289 } else {
290 char ourflags[FLAGBUFLEN], ournoticeflags[FLAGBUFLEN];
291 controlreply(np, "Flag alterations denied.");
292
293 strlcpy(ourflags, printflags(NOGetAuthLevel(au), no_userflags), sizeof(ourflags));
294 strlcpy(ournoticeflags, printflags(NOGetNoticeLevel(au), no_noticeflags), sizeof(ournoticeflags));
295 controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) attempted to NOTICEFLAGS (%s): %s", np->nick, np->authname, ourflags, ournoticeflags, printflagdiff(fwas, fnow, no_noticeflags));
296 return CMD_ERROR;
297 }
298 } else if(ret == REJECT_NONE) {
299 if(NOGetNoticeLevel(au) == fwas) {
300 controlreply(np, "No changes made to existing flags.");
301 } else {
302 char ourflags[FLAGBUFLEN], ournoticeflags[FLAGBUFLEN], diff[FLAGBUFLEN * 2 + 1], finalflags[FLAGBUFLEN];
303 no_nicklist *nl = au->nick;
304 noperserv_update_autheduser(au);
305 controlreply(np, "Flag alterations complete.");
306
307 strlcpy(ourflags, printflags(NOGetAuthLevel(au), no_userflags), sizeof(ourflags));
308 strlcpy(ournoticeflags, printflags(fwas, no_noticeflags), sizeof(ournoticeflags));
309 strlcpy(diff, printflagdiff(fwas, NOGetNoticeLevel(au), no_noticeflags), sizeof(diff));
310 controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) successfully used NOTICEFLAGS (%s): %s", np->nick, np->authname, ourflags, ournoticeflags, diff);
311
312 strlcpy(finalflags, printflags(NOGetNoticeLevel(au), no_noticeflags), sizeof(finalflags));
313 for(;nl;nl=nl->next)
314 if(nl->nick != np) {
315 controlreply(nl->nick, "!!! %s just used NOTICEFLAGS (%s): %s", np->nick, ournoticeflags, diff);
316 controlreply(nl->nick, "Your notice flags are %s", finalflags);
317 }
318 }
319 }
320 } else {
321 controlreply(np, "Unknown flag(s) supplied.");
322 return CMD_ERROR;
323 }
324 } else {
325 int typefound;
326 char *itemfound;
327 au = noperserv_autheduser_from_command(np, cargv[0], &typefound, &itemfound);
328 if(!au)
329 return CMD_ERROR;
330
331 if(au != NOGetAuthedUser(np)) {
332 controlreply(np, "Notice flags for %s %s are: %s", typefound==NO_FOUND_NICKNAME?"user":"authname", itemfound, printflags(NOGetNoticeLevel(au), no_noticeflags));
333 return CMD_OK;
334 }
335 }
336 } else {
337 au = NOGetAuthedUser(np);
338 }
339
340 if(!au) /* shouldn't happen */
341 return CMD_ERROR;
342
343 controlreply(np, "Your notice flags are: %s", printflags(NOGetNoticeLevel(au), no_noticeflags));
344
345 return CMD_OK;
346}
347
348/* @test */
349int noperserv_deluser(void *sender, int cargc, char **cargv) {
350 nick *np = (nick *)sender;
351 no_autheduser *target /* target user */, *au = NOGetAuthedUser(np); /* user executing command */
352 char *userreturned = NULL; /* nickname or authname of the target, pulled from the db */
353 int typefound; /* whether it was an authname or a username */
354 no_nicklist *nl;
355 char targetflags[FLAGBUFLEN], ourflags[FLAGBUFLEN], deleteduser[NOMax(ACCOUNTLEN, NICKLEN) + 1];
356
357 if(cargc != 1)
358 return CMD_USAGE;
359
360 target = noperserv_autheduser_from_command(np, cargv[0], &typefound, &userreturned);
361 if(!target)
362 return CMD_ERROR;
363
364 strlcpy(targetflags, printflags(NOGetAuthLevel(target), no_userflags), sizeof(targetflags));
365 strlcpy(ourflags, printflags(NOGetAuthLevel(au), no_userflags), sizeof(ourflags));
366
367 /* we have to copy it as it might point to an autheduser, which we're about to delete */
368 strlcpy(deleteduser, userreturned, sizeof(deleteduser));
369
370 /* we have to check if target != au, because if successful policy_modification_permitted just returns the flags we're allowed
371 to modify, if we have no flags we won't be able to delete ourselves */
372 if((target != au) && !noperserv_policy_permitted_modifications(au, target)) {
373 controlreply(np, "Deletion denied.");
374 controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) attempted to DELUSER %s (%s)", np->nick, np->authname, ourflags, target->authname->content, targetflags);
375
376 return CMD_ERROR;
377 }
378
379 for(nl=target->nick;nl;nl=nl->next)
380 if(nl->nick != np)
381 controlreply(nl->nick, "!!! %s/%s (%s) just DELUSERed you.", np->nick, np->authname, ourflags);
382
383 noperserv_delete_autheduser(target);
384
385 controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) successfully used DELUSER on %s (%s)", np->nick, np->authname, ourflags, target->authname->content, targetflags);
386
387 if(target == au) {
388 controlreply(np, "You have been deleted.");
389 } else {
390 controlreply(np, "%s %s deleted.", typefound==NO_FOUND_AUTHNAME?"Auth":"User", deleteduser);
391 }
392
393 return CMD_OK;
394}
395
396/* @test */
397/* this command needs LOTS of checking */
398int noperserv_userflags(void *sender, int cargc, char **cargv) {
399 nick *np = (nick *)sender;
400 no_autheduser *au = NOGetAuthedUser(np), *target = NULL;
401 char *flags = NULL, *nicktarget = NULL;
402 int typefound;
403
404 if(cargc == 0) {
405 target = au;
406 } else if(cargc == 1) {
407 if((cargv[0][0] == '+') || (cargv[0][0] == '-')) { /* modify our own */
408 flags = cargv[0];
409 target = au;
410 } else { /* viewing someone elses */
411 nicktarget = cargv[0];
412 }
413 } else if(cargc == 2) {
414 nicktarget = cargv[0];
415 flags = cargv[1];
416 } else {
417 return CMD_USAGE;
418 }
419
420 if(nicktarget) {
421 target = noperserv_autheduser_from_command(np, nicktarget, &typefound, &nicktarget);
422 if(!target)
423 return CMD_ERROR;
424 }
425
426 if(flags) {
427 int ret;
428 flag_t permitted = noperserv_policy_permitted_modifications(au, target), fwas = NOGetAuthLevel(target), fours = NOGetAuthLevel(au);
429
430 ret = setflags(&target->authlevel, permitted, flags, no_userflags, REJECT_DISALLOWED | REJECT_UNKNOWN);
431 if(ret != REJECT_UNKNOWN) {
432 if(ret == REJECT_DISALLOWED) {
433 flag_t fnow = fwas;
434 setflags(&fnow, NO_ALL_FLAGS, flags, no_userflags, REJECT_NONE);
435 if(fnow == fwas) {
436 controlreply(np, "No changes made to existing flags.");
437 } else {
438 char targetflags[FLAGBUFLEN], ourflags[FLAGBUFLEN];
439 controlreply(np, "Flag alterations denied.");
440
441 strlcpy(targetflags, printflags(fwas, no_userflags), sizeof(targetflags));
442 strlcpy(ourflags, printflags(fours, no_userflags), sizeof(ourflags));
443
444 controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) attempted to use USERFLAGS on %s (%s): %s", np->nick, np->authname, ourflags, target->authname->content, targetflags, printflagdiff(fwas, fnow, no_userflags));
445 return CMD_ERROR;
446 }
447 } else if(ret == REJECT_NONE) {
448 if(NOGetAuthLevel(target) == fwas) {
449 controlreply(np, "No changes made to existing flags.");
450 } else {
451 char targetflags[FLAGBUFLEN], ourflags[FLAGBUFLEN], finalflags[FLAGBUFLEN];
452 no_nicklist *nl = target->nick;
453
454 noperserv_policy_update_noticeflags(fwas, target);
455 noperserv_update_autheduser(target);
456
457 controlreply(np, "Flag alterations complete.");
458
459 strlcpy(targetflags, printflags(fwas, no_userflags), sizeof(targetflags));
460 strlcpy(ourflags, printflags(fours, no_userflags), sizeof(ourflags));
461
462 controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) successfully used USERFLAGS on %s (%s): %s", np->nick, np->authname, ourflags, target->authname->content, targetflags, printflagdiff(fwas, NOGetAuthLevel(target), no_userflags));
463
464 strlcpy(finalflags, printflags(NOGetAuthLevel(target), no_userflags), sizeof(finalflags));
465 for(;nl;nl=nl->next)
466 if(nl->nick != np) {
467 controlreply(nl->nick, "!!! %s/%s (%s) just used USERFLAGS on you (%s): %s", np->nick, np->authname, ourflags, targetflags, printflagdiff(fwas, NOGetAuthLevel(target), no_userflags));
468 controlreply(nl->nick, "Your user flags are now: %s", finalflags);
469 controlreply(nl->nick, "Your notice flags are now: %s", printflags(target->noticelevel, no_noticeflags));
470 }
471 }
472 }
473 } else {
474 controlreply(np, "Unknown flag(s) supplied.");
475 return CMD_ERROR;
476 }
477 }
478
479 if(target != au) {
480 controlreply(np, "User flags for %s %s: %s", typefound==NO_FOUND_AUTHNAME?"auth":"user", nicktarget, printflags(NOGetAuthLevel(target), no_userflags));
481 controlreply(np, "Notice flags for %s %s: %s", typefound==NO_FOUND_AUTHNAME?"auth":"user", nicktarget, printflags(target->noticelevel, no_noticeflags));
482 } else {
483 controlreply(np, "Your user flags are: %s", printflags(NOGetAuthLevel(target), no_userflags));
484 controlreply(np, "Your notice flags are: %s", printflags(target->noticelevel, no_noticeflags));
485 }
486
487 return CMD_OK;
488}
489
490void noperserv_oper_detection(int hooknum, void *arg) {
510b754a 491 nick *np = (nick *)arg;
3e3692bf 492
3e3692bf 493 if(np->umodes & UMODE_OPER) {
510b754a
CP
494 if(np->opername && strcmp(np->opername->content, "-")) {
495 controlwall(NO_OPER, NL_OPERING, "%s!%s@%s%s%s just OPERed as %s", np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:"", np->opername->content);
496 } else {
3e3692bf 497 controlwall(NO_OPER, NL_OPERING, "%s!%s@%s%s%s just OPERed", np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:"");
510b754a
CP
498 }
499 } else {
500 controlwall(NO_OPER, NL_OPERING, "%s!%s@%s%s%s just DEOPERed", np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:"");
3e3692bf
CP
501 }
502}