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