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