]> jfr.im git - irc/quakenet/newserv.git/blob - chanserv/chancmds/chanlev.c
ee0588e74662996afacfcc6bb078da6d7c0660e6
[irc/quakenet/newserv.git] / chanserv / chancmds / chanlev.c
1 /* Automatically generated by refactor.pl.
2 *
3 *
4 * CMDNAME: chanlev
5 * CMDLEVEL: QCMD_AUTHED
6 * CMDARGS: 3
7 * CMDDESC: Shows or modifies user access on a channel.
8 * CMDFUNC: csc_dochanlev
9 * CMDPROTO: int csc_dochanlev(void *source, int cargc, char **cargv);
10 * CMDHELP: Usage: CHANLEV <channel> [<user> [<change>]]
11 * CMDHELP: Displays or alters the access of known users on a channel, where:
12 * CMDHELP: channel - the channel to use
13 * CMDHELP: user - the user to list or modify. user can be specified as either an active
14 * CMDHELP: nickname on the network or #accountname. If user is not specified then
15 * CMDHELP: all known users are listed.
16 * CMDHELP: change - lists the flags to add or remove, with + to add or - to remove. For
17 * CMDHELP: example, +ao to add a and o flags, or -gv to remove g and v flags. This
18 * CMDHELP: can be used to add or remove users from the channel. If change is not
19 * CMDHELP: specified then the current access of the named user is displayed.
20 * CMDHELP: Displaying known user information requires you to be known (+k) on the named channel.
21 * CMDHELP: Adjusting flags for other users requires master (+m) access on the named channel.
22 * CMDHELP: Adding or removing the +m flag for other users requires owner (+n) access on the
23 * CMDHELP: named channel.
24 * CMDHELP: You may always remove your own flags, except +qdb flags (which are not visible to you).
25 * CMDHELP: Adding or removing personal flags requires you to be known (+k) on the named channel.
26 * CMDHELP: Note that channel owners (+n) can grant +n to channel masters but they must use
27 * CMDHELP: the GIVEOWNER command for this.
28 * CMDHELP: The access level flags determine which commands a user is allowed to use on a channel.
29 * CMDHELP: Holding an access flag also grants access to any action requiring a lesser flag (e.g.
30 * CMDHELP: +m users can perform actions requiring operator (+o) status even if they do not
31 * CMDHELP: actually have +o set). The access flags are listed in descending order.
32 * CMDHELP: Valid flags are:
33 * CMDHELP: Access level flags - these control the user's overall privilege level on the channel:
34 * CMDHELP: +n OWNER Can add or remove masters and all other flags (except personal flags)
35 * CMDHELP: +m MASTER Can add or remove all access except master or owner
36 * CMDHELP: +o OP Can get ops on the channel
37 * CMDHELP: +v VOICE Can get voice on the channel
38 * CMDHELP: +k KNOWN Known on the channel - can get invites to the channel via INVITE
39 * CMDHELP: Punishment flags - these restrict the user on the channel in some way:
40 * CMDHELP: +q DEVOICE Not allowed to be voiced on the channel
41 * CMDHELP: +d DEOP Not allowed to be opped on the channel
42 * CMDHELP: +b BANNED Banned from the channel
43 * CMDHELP: Extra flags - these control specific behaviour on the channel:
44 * CMDHELP: +a AUTOOP Ops the user automatically when they join the channel (the user
45 * CMDHELP: must also hold +o in order to have this flag)
46 * CMDHELP: +g AUTOVOICE Voices the user automatically when they join the channel (the
47 * CMDHELP: user must also hold +v in order to have this flag)
48 * CMDHELP: +p PROTECT If the user has +o or +v, this makes sure they will always have
49 * CMDHELP: that status, they will be reopped/voiced if deopped/voiced
50 * CMDHELP: +t TOPIC Can use SETTOPIC to alter the topic on the channel
51 * CMDHELP: Personal flags - these control user personal preferences and can only be changed
52 * CMDHELP: by the user concerned. They are not visible to other users.
53 * CMDHELP: +w NOWELCOME Prevents the welcome message being sent when you join the channel.
54 * CMDHELP: +j AUTOINV Invites you to the channel automatically when you authenticate.
55 * CMDHELP: Note that non-sensible combinations of flags are not allowed. After making a
56 * CMDHELP: change the current status of the named user on the channel will be confirmed.
57 */
58
59 #include "../chanserv.h"
60 #include "../../nick/nick.h"
61 #include "../../lib/flags.h"
62 #include "../../lib/irc_string.h"
63 #include "../../channel/channel.h"
64 #include "../../parser/parser.h"
65 #include "../../irc/irc.h"
66 #include "../../localuser/localuserchannel.h"
67 #include <string.h>
68 #include <stdio.h>
69
70 int compareflags(const void *u1, const void *u2) {
71 const regchanuser *r1=*(void **)u1, *r2=*(void **)u2;
72 flag_t f1,f2;
73
74 for (f1=QCUFLAG_OWNER;f1;f1>>=1)
75 if (r1->flags & f1)
76 break;
77
78 for (f2=QCUFLAG_OWNER;f2;f2>>=1)
79 if (r2->flags & f2)
80 break;
81
82 if (f1==f2) {
83 return ircd_strcmp(r1->user->username, r2->user->username);
84 } else {
85 return f2-f1;
86 }
87 }
88
89 int csc_dochanlev(void *source, int cargc, char **cargv) {
90 nick *sender=source;
91 chanindex *cip;
92 regchan *rcp;
93 regchanuser *rcup, *rcuplist;
94 regchanuser **rusers;
95 reguser *rup=getreguserfromnick(sender), *target;
96 char time1[TIMELEN],time2[TIMELEN];
97 char flagbuf[30];
98 flag_t flagmask, changemask, flags, oldflags;
99 int showtimes=0;
100 int donehead=0;
101 int i,j;
102 int newuser=0;
103 int usercount;
104 void *args[3];
105
106 if (cargc<1) {
107 chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "chanlev");
108 return CMD_ERROR;
109 }
110
111 if (!(cip=cs_checkaccess(sender, cargv[0], CA_KNOWN,
112 NULL, "chanlev", QPRIV_VIEWFULLCHANLEV, 0)))
113 return CMD_ERROR;
114
115 rcp=cip->exts[chanservext];
116 rcup=findreguseronchannel(rcp, rup);
117
118 /* Set flagmask for +v/+o users (can't see bans etc.) */
119 flagmask = (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_AUTOVOICE |
120 QCUFLAG_AUTOOP | QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAG_KNOWN);
121
122 /* masters and above can see everything except personal flags */
123 if (rcup && CUHasMasterPriv(rcup)) {
124 flagmask = QCUFLAG_ALL & ~QCUFLAGS_PERSONAL;
125 showtimes=1;
126 }
127
128 /* Staff access, show everything */
129 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
130 flagmask = QCUFLAG_ALL;
131 showtimes=1;
132 }
133
134 if (cargc==1) {
135 /* One arg: list chanlev */
136 int ncnt=0,mcnt=0,ocnt=0,vcnt=0,kcnt=0,bcnt=0;
137
138 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
139 reguser *founder=NULL, *addedby=NULL;
140 addedby=findreguserbyID(rcp->addedby);
141 chanservstdmessage(sender, QM_ADDEDBY, addedby ? addedby->username : "(unknown)");
142 founder=findreguserbyID(rcp->founder);
143 chanservstdmessage(sender, QM_FOUNDER, founder ? founder->username : "(unknown)");
144 if (rcp->chantype) {
145 chanservstdmessage(sender, QM_CHANTYPE, chantypes[rcp->chantype]->content);
146 }
147 }
148
149 if (CIsSuspended(rcp) && cs_privcheck(QPRIV_VIEWCHANSUSPENSION, sender)) {
150 char *bywhom;
151
152 if(cs_privcheck(QPRIV_VIEWSUSPENDEDBY, sender)) {
153 reguser *trup = findreguserbyID(rcp->suspendby);
154 if(trup) {
155 bywhom = trup->username;
156 } else {
157 bywhom = "(unknown)";
158 }
159 } else {
160 bywhom = "(hidden)";
161 }
162
163 chanservstdmessage(sender, QM_CHANLEV_SUSPENDREASON, rcp->suspendreason?rcp->suspendreason->content:"(no reason)");
164 chanservstdmessage(sender, QM_CHANLEV_SUSPENDBY, bywhom);
165 chanservstdmessage(sender, QM_CHANLEV_SUSPENDSINCE, rcp->suspendtime);
166 }
167
168 if (rcp->comment && (cs_privcheck(QPRIV_VIEWCOMMENTS, sender)))
169 chanservstdmessage(sender, QM_SHORT_COMMENT, rcp->comment->content);
170
171 /* Count users */
172 for (i=0,usercount=0;i<REGCHANUSERHASHSIZE;i++)
173 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
174 usercount++;
175
176 /* Allocate array */
177 rusers=(regchanuser **)malloc(usercount * sizeof(regchanuser *));
178
179 /* Fill array */
180 for (j=i=0;i<REGCHANUSERHASHSIZE;i++) {
181 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan) {
182 if (!(flags=rcuplist->flags & flagmask))
183 continue;
184
185 rusers[j++]=rcuplist;
186 }
187 }
188
189 /* Sort */
190 qsort(rusers, j, sizeof(regchanuser *), compareflags);
191
192 /* List */
193 for (i=0;i<j;i++) {
194 rcuplist=rusers[i];
195
196 if (!(flags=rcuplist->flags & flagmask))
197 continue;
198
199 /* If you're listing yourself, we should show personal flags too */
200 if (rcuplist==rcup) {
201 flags=rcuplist->flags & (flagmask | QCUFLAGS_PERSONAL);
202 }
203
204 /* Do the count here; note that +n's aren't counted as +m (and so on). We're not
205 * using the IsX() macros because the displayed count needs to match up with
206 * the displayed flags... */
207 if (flags & QCUFLAG_OWNER) ncnt++; else
208 if (flags & QCUFLAG_MASTER) mcnt++; else
209 if (flags & QCUFLAG_OP) ocnt++; else
210 if (flags & QCUFLAG_VOICE) vcnt++; else
211 if (flags & QCUFLAG_KNOWN) kcnt++;
212 if (flags & QCUFLAG_BANNED) bcnt++;
213
214 if (!donehead) {
215 chanservstdmessage(sender, QM_CHANLEVHEADER, cip->name->content);
216 if (showtimes)
217 chanservstdmessage(sender, QM_CHANLEVCOLFULL);
218 else
219 chanservstdmessage(sender, QM_CHANLEVCOLSHORT);
220 donehead=1;
221 }
222
223 if (showtimes) {
224 if (!rcuplist->usetime) {
225 strcpy(time1,"Never");
226 } else {
227 q9strftime(time1,sizeof(time1),rcuplist->usetime);
228 }
229 if (!rcuplist->changetime) {
230 strcpy(time2, "Unknown");
231 } else {
232 q9strftime(time2,sizeof(time2),rcuplist->changetime);
233 }
234 chanservsendmessage(sender, " %-15s %-13s %-14s %-14s %s", rcuplist->user->username,
235 printflags(flags, rcuflags), time1, time2, rcuplist->info?rcuplist->info->content:"");
236 } else
237 chanservsendmessage(sender, " %-15s %s", rcuplist->user->username, printflags(flags, rcuflags));
238 }
239
240 if (donehead) {
241 chanservstdmessage(sender, QM_ENDOFLIST);
242 chanservstdmessage(sender, QM_CHANLEVSUMMARY, j, ncnt, mcnt, ocnt, vcnt, kcnt, bcnt);
243 } else {
244 chanservstdmessage(sender, QM_NOUSERSONCHANLEV, cip->name->content);
245 }
246
247 triggerhook(HOOK_CHANSERV_CHANLEVDUMP, sender);
248
249 free(rusers);
250 } else {
251 /* 2 or more args.. relates to one specific user */
252 if (!(target=findreguser(sender, cargv[1])))
253 return CMD_ERROR; /* If there was an error, findreguser will have sent a message saying why.. */
254
255 rcuplist=findreguseronchannel(rcp, target);
256
257 if (cargc>2) {
258 /* To change chanlev you have to either.. */
259 if (!( cs_privcheck(QPRIV_CHANGECHANLEV, sender) || /* Have override privilege */
260 (rcup && rcuplist && (rcup==rcuplist) && CUKnown(rcup)) || /* Be manipulting yourself (oo er..) */
261 (rcup && CUHasMasterPriv(rcup) && /* Have +m or +n on the channel */
262 !(rcuplist && CUIsOwner(rcuplist) && !CUIsOwner(rcup))) /* masters can't screw with owners */
263 )) {
264 chanservstdmessage(sender, QM_NOACCESSONCHAN, cip->name->content, "chanlev");
265 return CMD_ERROR;
266 }
267
268 if (!rcuplist) {
269 /* new user, we could store a count instead... that's probably better... */
270 unsigned int count;
271
272 for (count=i=0;i<REGCHANUSERHASHSIZE;i++)
273 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
274 count++;
275
276 if(count >= MAXCHANLEVS) {
277 chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
278 return CMD_ERROR;
279 }
280
281 rcuplist=getregchanuser();
282 rcuplist->user=target;
283 rcuplist->chan=rcp;
284 rcuplist->flags=0;
285 rcuplist->changetime=time(NULL);
286 rcuplist->usetime=0;
287 rcuplist->info=NULL;
288 newuser=1;
289 }
290
291 if (cs_privcheck(QPRIV_CHANGECHANLEV, sender)) {
292 /* Opers are allowed to change everything */
293 changemask = QCUFLAG_ALL;
294 } else {
295 changemask=0;
296
297 /* Everyone can change their own flags (except +dqb), and control (and see) personal flags */
298 if (rcup==rcuplist) {
299 changemask = (rcup->flags | QCUFLAGS_PERSONAL) & ~(QCUFLAGS_PUNISH);
300 flagmask |= QCUFLAGS_PERSONAL;
301 }
302
303 /* Masters are allowed to manipulate +ovagtbqdpk */
304 if (CUHasMasterPriv(rcup))
305 changemask |= ( QCUFLAG_KNOWN | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_AUTOOP | QCUFLAG_AUTOVOICE |
306 QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAGS_PUNISH);
307
308 /* Owners are allowed to manipulate +ms as well.
309 * We allow +n to be given initially, but we check later to see if the flag has been added.
310 * if it has, abort and say "use giveowner"
311 */
312 if (CUIsOwner(rcup))
313 changemask |= ( QCUFLAG_MASTER | QCUFLAG_OWNER );
314 }
315
316 oldflags=rcuplist->flags;
317 if (setflags(&(rcuplist->flags), changemask, cargv[2], rcuflags, REJECT_UNKNOWN | REJECT_DISALLOWED)) {
318 chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE);
319 if (newuser)
320 freeregchanuser(rcuplist);
321 return CMD_ERROR;
322 }
323
324 /* check to see if +n has been given. Opers can bypass this. */
325 if (!cs_privcheck(QPRIV_CHANGECHANLEV, sender) && !(oldflags & QCUFLAG_OWNER) && (rcuplist->flags & QCUFLAG_OWNER)) {
326 rcuplist->flags=oldflags;
327 chanservstdmessage(sender, QM_USEGIVEOWNER);
328 if (newuser)
329 freeregchanuser(rcuplist);
330 return CMD_ERROR;
331 }
332
333 /* Fix up impossible combinations */
334 rcuplist->flags = cs_sanitisechanlev(rcuplist->flags);
335
336 /* Check if anything "significant" has changed */
337 if ((oldflags ^ rcuplist->flags) & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP))
338 rcuplist->changetime=time(NULL);
339
340 if(rcuplist->flags == oldflags) {
341 chanservstdmessage(sender, QM_CHANLEVNOCHANGE);
342 if (newuser)
343 freeregchanuser(rcuplist);
344 return CMD_OK;
345 }
346
347 strcpy(flagbuf,printflags(oldflags,rcuflags));
348 cs_log(sender,"CHANLEV %s #%s %s (%s -> %s)",cip->name->content,rcuplist->user->username,cargv[2],
349 flagbuf,printflags(rcuplist->flags,rcuflags));
350 csdb_chanlevhistory_insert(rcp, sender, rcuplist->user, oldflags, rcuplist->flags);
351
352 /* The user has to be on the relevant chanlev list before we trigger the hook.
353 * So that enlisting has been hoisted to here. */
354 if (newuser && rcuplist->flags) {
355 addregusertochannel(rcuplist);
356 }
357
358 args[0]=sender;
359 args[1]=rcuplist;
360 args[2]=(void *)oldflags;
361
362 triggerhook(HOOK_CHANSERV_CHANLEVMOD, args);
363
364 /* Now see what we do next */
365 if (rcuplist->flags) {
366 /* User still valid: update or create */
367 if (newuser) {
368 csdb_createchanuser(rcuplist);
369 } else {
370 csdb_updatechanuser(rcuplist);
371 }
372 chanservstdmessage(sender, QM_CHANLEVCHANGED, cargv[1], cip->name->content,
373 printflags(rcuplist->flags & flagmask, rcuflags));
374 } else {
375 /* User has no flags: delete */
376 if (!newuser) {
377 chanservstdmessage(sender, QM_CHANLEVREMOVED, cargv[1], cip->name->content);
378 csdb_deletechanuser(rcuplist);
379 delreguserfromchannel(rcp, target);
380 }
381 freeregchanuser(rcuplist);
382 rcuplist=NULL;
383 if (cs_removechannelifempty(sender, rcp)) {
384 chanservstdmessage(sender, QM_CHANLEVEMPTIEDCHANNEL);
385 return CMD_OK;
386 }
387 }
388
389 /* Update the channel if needed */
390 rcp->status |= QCSTAT_OPCHECK;
391 cs_timerfunc(cip);
392 } else {
393 if (rcuplist == rcup)
394 flagmask |= QCUFLAGS_PERSONAL;
395 if (rcuplist && (rcuplist->flags & flagmask)) {
396 chanservstdmessage(sender, QM_CHANUSERFLAGS, cargv[1], cip->name->content,
397 printflags(rcuplist->flags & flagmask, rcuflags));
398 } else {
399 chanservstdmessage(sender, QM_CHANUSERUNKNOWN, cargv[1], cip->name->content);
400 }
401 }
402 }
403
404
405 return CMD_OK;
406 }