]> jfr.im git - irc/quakenet/newserv.git/blob - chanserv/chancmds/chanlev.c
chanserv: Revert unintended change to the chanlev command.
[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, *trcup, *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 chanlevcount, channelcount;
271
272 for (chanlevcount=i=0;i<REGCHANUSERHASHSIZE;i++)
273 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
274 chanlevcount++;
275
276 if(chanlevcount >= MAXCHANLEVS) {
277 chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
278 return CMD_ERROR;
279 }
280
281 channelcount=0;
282 for (trcup=target->knownon;trcup;trcup=trcup->nextbyuser)
283 channelcount++;
284
285 if(channelcount >= MAXCHANNELS) {
286 chanservstdmessage(sender, QM_TOOMANYCHANNELS);
287 return CMD_ERROR;
288 }
289
290 rcuplist=getregchanuser();
291 rcuplist->user=target;
292 rcuplist->chan=rcp;
293 rcuplist->flags=0;
294 rcuplist->changetime=time(NULL);
295 rcuplist->usetime=0;
296 rcuplist->info=NULL;
297 newuser=1;
298 }
299
300 if (cs_privcheck(QPRIV_CHANGECHANLEV, sender)) {
301 /* Opers are allowed to change everything */
302 changemask = QCUFLAG_ALL;
303 } else {
304 changemask=0;
305
306 /* Everyone can change their own flags (except +dqb), and control (and see) personal flags */
307 if (rcup==rcuplist) {
308 changemask = (rcup->flags | QCUFLAGS_PERSONAL) & ~(QCUFLAGS_PUNISH);
309 flagmask |= QCUFLAGS_PERSONAL;
310 }
311
312 /* Masters are allowed to manipulate +ovagtbqdpk */
313 if (CUHasMasterPriv(rcup))
314 changemask |= ( QCUFLAG_KNOWN | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_AUTOOP | QCUFLAG_AUTOVOICE |
315 QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAGS_PUNISH);
316
317 /* Owners are allowed to manipulate +ms as well.
318 * We allow +n to be given initially, but we check later to see if the flag has been added.
319 * if it has, abort and say "use giveowner"
320 */
321 if (CUIsOwner(rcup))
322 changemask |= ( QCUFLAG_MASTER | QCUFLAG_OWNER );
323 }
324
325 oldflags=rcuplist->flags;
326 if (setflags(&(rcuplist->flags), changemask, cargv[2], rcuflags, REJECT_UNKNOWN | REJECT_DISALLOWED)) {
327 chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE);
328 if (newuser)
329 freeregchanuser(rcuplist);
330 return CMD_ERROR;
331 }
332
333 /* check to see if +n has been given. Opers can bypass this. */
334 if (!cs_privcheck(QPRIV_CHANGECHANLEV, sender) && !(oldflags & QCUFLAG_OWNER) && (rcuplist->flags & QCUFLAG_OWNER)) {
335 rcuplist->flags=oldflags;
336 chanservstdmessage(sender, QM_USEGIVEOWNER);
337 if (newuser)
338 freeregchanuser(rcuplist);
339 return CMD_ERROR;
340 }
341
342 /* Fix up impossible combinations */
343 rcuplist->flags = cs_sanitisechanlev(rcuplist->flags);
344
345 /* Check if anything "significant" has changed */
346 if ((oldflags ^ rcuplist->flags) & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP))
347 rcuplist->changetime=time(NULL);
348
349 if(rcuplist->flags == oldflags) {
350 chanservstdmessage(sender, QM_CHANLEVNOCHANGE);
351 if (newuser)
352 freeregchanuser(rcuplist);
353 return CMD_OK;
354 }
355
356 strcpy(flagbuf,printflags(oldflags,rcuflags));
357 cs_log(sender,"CHANLEV %s #%s %s (%s -> %s)",cip->name->content,rcuplist->user->username,cargv[2],
358 flagbuf,printflags(rcuplist->flags,rcuflags));
359 csdb_chanlevhistory_insert(rcp, sender, rcuplist->user, oldflags, rcuplist->flags);
360
361 /* The user has to be on the relevant chanlev list before we trigger the hook.
362 * So that enlisting has been hoisted to here. */
363 if (newuser && rcuplist->flags) {
364 addregusertochannel(rcuplist);
365 }
366
367 args[0]=sender;
368 args[1]=rcuplist;
369 args[2]=(void *)oldflags;
370
371 triggerhook(HOOK_CHANSERV_CHANLEVMOD, args);
372
373 /* Now see what we do next */
374 if (rcuplist->flags) {
375 /* User still valid: update or create */
376 if (newuser) {
377 csdb_createchanuser(rcuplist);
378 } else {
379 csdb_updatechanuser(rcuplist);
380 }
381 chanservstdmessage(sender, QM_CHANLEVCHANGED, cargv[1], cip->name->content,
382 printflags(rcuplist->flags & flagmask, rcuflags));
383 } else {
384 /* User has no flags: delete */
385 if (!newuser) {
386 chanservstdmessage(sender, QM_CHANLEVREMOVED, cargv[1], cip->name->content);
387 csdb_deletechanuser(rcuplist);
388 delreguserfromchannel(rcp, target);
389 }
390 freeregchanuser(rcuplist);
391 rcuplist=NULL;
392 if (cs_removechannelifempty(sender, rcp)) {
393 chanservstdmessage(sender, QM_CHANLEVEMPTIEDCHANNEL);
394 return CMD_OK;
395 }
396 }
397
398 /* Update the channel if needed */
399 rcp->status |= QCSTAT_OPCHECK;
400 cs_timerfunc(cip);
401 } else {
402 if (rcuplist == rcup)
403 flagmask |= QCUFLAGS_PERSONAL;
404 if (rcuplist && (rcuplist->flags & flagmask)) {
405 chanservstdmessage(sender, QM_CHANUSERFLAGS, cargv[1], cip->name->content,
406 printflags(rcuplist->flags & flagmask, rcuflags));
407 } else {
408 chanservstdmessage(sender, QM_CHANUSERUNKNOWN, cargv[1], cip->name->content);
409 }
410 }
411 }
412
413
414 return CMD_OK;
415 }