]> jfr.im git - irc/quakenet/newserv.git/blame - chanserv/chancmds/chanlev.c
fix bug in G stats
[irc/quakenet/newserv.git] / chanserv / chancmds / chanlev.c
CommitLineData
1dd6d55d 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);
cabd7271 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.
1e32d528 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.
b2bc2ffe 32 * CMDHELP: Valid flags are:
1e32d528 33 * CMDHELP: Access level flags - these control the user's overall privilege level on the channel:
b2bc2ffe 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
1e32d528 39 * CMDHELP: Punishment flags - these restrict the user on the channel in some way:
b2bc2ffe 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
cabd7271 52 * CMDHELP: by the user concerned. They are not visible to other users.
b2bc2ffe 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.
cabd7271 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.
1dd6d55d 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
70int 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
89int 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;
b7eebdcc 96 char time1[TIMELEN],time2[TIMELEN];
1dd6d55d 97 char flagbuf[30];
1dd6d55d 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
105 if (cargc<1) {
106 chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "chanlev");
107 return CMD_ERROR;
108 }
109
110 if (!(cip=cs_checkaccess(sender, cargv[0], CA_KNOWN,
111 NULL, "chanlev", QPRIV_VIEWFULLCHANLEV, 0)))
112 return CMD_ERROR;
113
114 rcp=cip->exts[chanservext];
115 rcup=findreguseronchannel(rcp, rup);
116
117 /* Set flagmask for +v/+o users (can't see bans etc.) */
118 flagmask = (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_AUTOVOICE |
72ca906c 119 QCUFLAG_AUTOOP | QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAG_KNOWN);
1dd6d55d 120
b2bc2ffe 121 /* masters and above can see everything except personal flags */
cabd7271 122 if (rcup && CUHasMasterPriv(rcup)) {
b2bc2ffe 123 flagmask = QCUFLAG_ALL & ~QCUFLAGS_PERSONAL;
124 showtimes=1;
125 }
126
127 /* Staff access, show everything */
128 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
1dd6d55d 129 flagmask = QCUFLAG_ALL;
130 showtimes=1;
131 }
132
133 if (cargc==1) {
134 /* One arg: list chanlev */
96249709 135 int ncnt=0,mcnt=0,ocnt=0,vcnt=0,kcnt=0,bcnt=0;
136
1dd6d55d 137 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
138 reguser *founder=NULL, *addedby=NULL;
139 addedby=findreguserbyID(rcp->addedby);
140 chanservstdmessage(sender, QM_ADDEDBY, addedby ? addedby->username : "(unknown)");
141 founder=findreguserbyID(rcp->founder);
142 chanservstdmessage(sender, QM_FOUNDER, founder ? founder->username : "(unknown)");
143 if (rcp->chantype) {
144 chanservstdmessage(sender, QM_CHANTYPE, chantypes[rcp->chantype]->content);
145 }
146 }
147
e086f1b5 148 if (CIsSuspended(rcp) && cs_privcheck(QPRIV_VIEWCHANSUSPENSION, sender)) {
79313d98 149 char *bywhom;
e086f1b5
CP
150
151 if(cs_privcheck(QPRIV_VIEWSUSPENDEDBY, sender)) {
152 reguser *trup = findreguserbyID(rcp->suspendby);
153 if(trup) {
154 bywhom = trup->username;
155 } else {
156 bywhom = "(unknown)";
157 }
158 } else {
159 bywhom = "(hidden)";
160 }
161
e086f1b5
CP
162 chanservstdmessage(sender, QM_CHANLEV_SUSPENDREASON, rcp->suspendreason?rcp->suspendreason->content:"(no reason)");
163 chanservstdmessage(sender, QM_CHANLEV_SUSPENDBY, bywhom);
79313d98 164 chanservstdmessage(sender, QM_CHANLEV_SUSPENDSINCE, rcp->suspendtime);
e086f1b5
CP
165 }
166
baafd54b
CP
167 if (rcp->comment && (cs_privcheck(QPRIV_VIEWCOMMENTS, sender)))
168 chanservstdmessage(sender, QM_SHORT_COMMENT, rcp->comment->content);
169
1dd6d55d 170 /* Count users */
171 for (i=0,usercount=0;i<REGCHANUSERHASHSIZE;i++)
172 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
173 usercount++;
174
175 /* Allocate array */
176 rusers=(regchanuser **)malloc(usercount * sizeof(regchanuser *));
177
178 /* Fill array */
179 for (j=i=0;i<REGCHANUSERHASHSIZE;i++) {
180 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan) {
181 if (!(flags=rcuplist->flags & flagmask))
182 continue;
183
184 rusers[j++]=rcuplist;
185 }
186 }
187
188 /* Sort */
189 qsort(rusers, j, sizeof(regchanuser *), compareflags);
190
191 /* List */
192 for (i=0;i<j;i++) {
193 rcuplist=rusers[i];
194
195 if (!(flags=rcuplist->flags & flagmask))
196 continue;
197
156f6bad 198 /* If you're listing yourself, we should show personal flags too */
199 if (rcuplist==rcup) {
200 flags=rcuplist->flags & (flagmask | QCUFLAGS_PERSONAL);
201 }
202
96249709 203 /* Do the count here; note that +n's aren't counted as +m (and so on). We're not
204 * using the IsX() macros because the displayed count needs to match up with
205 * the displayed flags... */
206 if (flags & QCUFLAG_OWNER) ncnt++; else
207 if (flags & QCUFLAG_MASTER) mcnt++; else
208 if (flags & QCUFLAG_OP) ocnt++; else
209 if (flags & QCUFLAG_VOICE) vcnt++; else
210 if (flags & QCUFLAG_KNOWN) kcnt++;
211 if (flags & QCUFLAG_BANNED) bcnt++;
212
1dd6d55d 213 if (!donehead) {
214 chanservstdmessage(sender, QM_CHANLEVHEADER, cip->name->content);
215 if (showtimes)
216 chanservstdmessage(sender, QM_CHANLEVCOLFULL);
217 else
218 chanservstdmessage(sender, QM_CHANLEVCOLSHORT);
219 donehead=1;
220 }
221
222 if (showtimes) {
223 if (!rcuplist->usetime) {
224 strcpy(time1,"Never");
225 } else {
b7eebdcc 226 q9strftime(time1,sizeof(time1),rcuplist->usetime);
1dd6d55d 227 }
228 if (!rcuplist->changetime) {
229 strcpy(time2, "Unknown");
230 } else {
b7eebdcc 231 q9strftime(time2,sizeof(time2),rcuplist->changetime);
1dd6d55d 232 }
233 chanservsendmessage(sender, " %-15s %-13s %-14s %-14s %s", rcuplist->user->username,
234 printflags(flags, rcuflags), time1, time2, rcuplist->info?rcuplist->info->content:"");
235 } else
236 chanservsendmessage(sender, " %-15s %s", rcuplist->user->username, printflags(flags, rcuflags));
237 }
238
239 if (donehead) {
240 chanservstdmessage(sender, QM_ENDOFLIST);
96249709 241 chanservstdmessage(sender, QM_CHANLEVSUMMARY, j, ncnt, mcnt, ocnt, vcnt, kcnt, bcnt);
1dd6d55d 242 } else {
243 chanservstdmessage(sender, QM_NOUSERSONCHANLEV, cip->name->content);
244 }
245
246 free(rusers);
247 } else {
248 /* 2 or more args.. relates to one specific user */
249 if (!(target=findreguser(sender, cargv[1])))
250 return CMD_ERROR; /* If there was an error, findreguser will have sent a message saying why.. */
251
252 rcuplist=findreguseronchannel(rcp, target);
253
254 if (cargc>2) {
255 /* To change chanlev you have to either.. */
256 if (!( cs_privcheck(QPRIV_CHANGECHANLEV, sender) || /* Have override privilege */
257 (rcup && rcuplist && (rcup==rcuplist) && CUKnown(rcup)) || /* Be manipulting yourself (oo er..) */
258 (rcup && CUHasMasterPriv(rcup) && /* Have +m or +n on the channel */
259 !(rcuplist && CUIsOwner(rcuplist) && !CUIsOwner(rcup))) /* masters can't screw with owners */
260 )) {
261 chanservstdmessage(sender, QM_NOACCESSONCHAN, cip->name->content, "chanlev");
262 return CMD_ERROR;
263 }
264
265 if (!rcuplist) {
705542fa
CP
266 /* new user, we could store a count instead... that's probably better... */
267 unsigned int count;
268
269 for (count=i=0;i<REGCHANUSERHASHSIZE;i++)
270 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
271 count++;
272
273 if(count >= MAXCHANLEVS) {
274 chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
275 return CMD_ERROR;
276 }
277
1dd6d55d 278 rcuplist=getregchanuser();
279 rcuplist->user=target;
280 rcuplist->chan=rcp;
281 rcuplist->flags=0;
282 rcuplist->changetime=time(NULL);
283 rcuplist->usetime=0;
284 rcuplist->info=NULL;
285 newuser=1;
286 }
287
288 if (cs_privcheck(QPRIV_CHANGECHANLEV, sender)) {
289 /* Opers are allowed to change everything */
290 changemask = QCUFLAG_ALL;
291 } else {
292 changemask=0;
293
b2bc2ffe 294 /* Everyone can change their own flags (except +dqb), and control (and see) personal flags */
1dd6d55d 295 if (rcup==rcuplist) {
50ec21f3 296 changemask = (rcup->flags | QCUFLAGS_PERSONAL) & ~(QCUFLAGS_PUNISH);
156f6bad 297 flagmask |= QCUFLAGS_PERSONAL;
1dd6d55d 298 }
299
300 /* Masters are allowed to manipulate +ovagtbqdpk */
301 if (CUHasMasterPriv(rcup))
302 changemask |= ( QCUFLAG_KNOWN | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_AUTOOP | QCUFLAG_AUTOVOICE |
50ec21f3 303 QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAGS_PUNISH);
1dd6d55d 304
8fab5f64
CP
305 /* Owners are allowed to manipulate +ms as well.
306 * We allow +n to be given initially, but we check later to see if the flag has been added.
307 * if it has, abort and say "use giveowner"
308 */
1dd6d55d 309 if (CUIsOwner(rcup))
8fab5f64 310 changemask |= ( QCUFLAG_MASTER | QCUFLAG_OWNER );
1dd6d55d 311 }
312
313 oldflags=rcuplist->flags;
314 if (setflags(&(rcuplist->flags), changemask, cargv[2], rcuflags, REJECT_UNKNOWN | REJECT_DISALLOWED)) {
315 chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE);
5413876f
CP
316 if (newuser)
317 freeregchanuser(rcuplist);
1dd6d55d 318 return CMD_ERROR;
319 }
320
2ca43b1b 321 /* check to see if +n has been given. Opers can bypass this. */
322 if (!cs_privcheck(QPRIV_CHANGECHANLEV, sender) && !(oldflags & QCUFLAG_OWNER) && (rcuplist->flags & QCUFLAG_OWNER)) {
8fab5f64
CP
323 rcuplist->flags=oldflags;
324 chanservstdmessage(sender, QM_USEGIVEOWNER);
5413876f
CP
325 if (newuser)
326 freeregchanuser(rcuplist);
8fab5f64
CP
327 return CMD_ERROR;
328 }
329
95332d7b 330 /* Fix up impossible combinations */
331 rcuplist->flags = cs_sanitisechanlev(rcuplist->flags);
156f6bad 332
1dd6d55d 333 /* Check if anything "significant" has changed */
334 if ((oldflags ^ rcuplist->flags) & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP))
335 rcuplist->changetime=time(NULL);
336
183b8e2f 337 if(rcuplist->flags == oldflags) {
1bd2b810 338 chanservstdmessage(sender, QM_CHANLEVNOCHANGE);
5413876f 339 if (newuser)
1bd2b810 340 freeregchanuser(rcuplist);
183b8e2f
P
341 return CMD_OK;
342 }
343
1dd6d55d 344 strcpy(flagbuf,printflags(oldflags,rcuflags));
345 cs_log(sender,"CHANLEV %s #%s %s (%s -> %s)",cip->name->content,rcuplist->user->username,cargv[2],
346 flagbuf,printflags(rcuplist->flags,rcuflags));
183b8e2f 347 csdb_chanlevhistory_insert(rcp, sender, rcuplist->user, oldflags, rcuplist->flags);
1dd6d55d 348
349 /* Now see what we do next */
350 if (rcuplist->flags) {
351 /* User still valid: update or create */
352 if (newuser) {
353 addregusertochannel(rcuplist);
354 csdb_createchanuser(rcuplist);
355 } else {
356 csdb_updatechanuser(rcuplist);
357 }
1bd2b810 358 chanservstdmessage(sender, QM_CHANLEVCHANGED, cargv[1], cip->name->content,
359 printflags(rcuplist->flags & flagmask, rcuflags));
1dd6d55d 360 } else {
361 /* User has no flags: delete */
362 if (!newuser) {
1bd2b810 363 chanservstdmessage(sender, QM_CHANLEVREMOVED, cargv[1], cip->name->content);
1dd6d55d 364 csdb_deletechanuser(rcuplist);
365 delreguserfromchannel(rcp, target);
366 }
367 freeregchanuser(rcuplist);
368 rcuplist=NULL;
5f7881b2 369 if (cs_removechannelifempty(sender, rcp)) {
370 chanservstdmessage(sender, QM_CHANLEVEMPTIEDCHANNEL);
371 return CMD_OK;
372 }
1dd6d55d 373 }
1bd2b810 374
375 /* Update the channel if needed */
1dd6d55d 376 rcp->status |= QCSTAT_OPCHECK;
377 cs_timerfunc(cip);
1dd6d55d 378 } else {
50ec21f3 379 if (rcuplist == rcup)
380 flagmask |= QCUFLAGS_PERSONAL;
1bd2b810 381 if (rcuplist && (rcuplist->flags & flagmask)) {
382 chanservstdmessage(sender, QM_CHANUSERFLAGS, cargv[1], cip->name->content,
383 printflags(rcuplist->flags & flagmask, rcuflags));
384 } else {
385 chanservstdmessage(sender, QM_CHANUSERUNKNOWN, cargv[1], cip->name->content);
386 }
1dd6d55d 387 }
388 }
389
390 return CMD_OK;
391}