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