]> jfr.im git - irc/quakenet/newserv.git/blob - chanserv/chancmds/chanlev.c
559e05145b632cde2c809cd81d5882c0a06532af
[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
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 |
119 QCUFLAG_AUTOOP | QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAG_KNOWN);
120
121 /* masters and above can see everything except personal flags */
122 if (rcup && CUHasMasterPriv(rcup)) {
123 flagmask = QCUFLAG_ALL & ~QCUFLAGS_PERSONAL;
124 showtimes=1;
125 }
126
127 /* Staff access, show everything */
128 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
129 flagmask = QCUFLAG_ALL;
130 showtimes=1;
131 }
132
133 if (cargc==1) {
134 /* One arg: list chanlev */
135 int ncnt=0,mcnt=0,ocnt=0,vcnt=0,kcnt=0,bcnt=0;
136
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
148 if (CIsSuspended(rcp) && cs_privcheck(QPRIV_VIEWCHANSUSPENSION, sender)) {
149 char *bywhom;
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
162 chanservstdmessage(sender, QM_CHANLEV_SUSPENDREASON, rcp->suspendreason?rcp->suspendreason->content:"(no reason)");
163 chanservstdmessage(sender, QM_CHANLEV_SUSPENDBY, bywhom);
164 chanservstdmessage(sender, QM_CHANLEV_SUSPENDSINCE, rcp->suspendtime);
165 }
166
167 if (rcp->comment && (cs_privcheck(QPRIV_VIEWCOMMENTS, sender)))
168 chanservstdmessage(sender, QM_SHORT_COMMENT, rcp->comment->content);
169
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
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
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
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 {
226 q9strftime(time1,sizeof(time1),rcuplist->usetime);
227 }
228 if (!rcuplist->changetime) {
229 strcpy(time2, "Unknown");
230 } else {
231 q9strftime(time2,sizeof(time2),rcuplist->changetime);
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);
241 chanservstdmessage(sender, QM_CHANLEVSUMMARY, j, ncnt, mcnt, ocnt, vcnt, kcnt, bcnt);
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) {
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
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
294 /* Everyone can change their own flags (except +dqb), and control (and see) personal flags */
295 if (rcup==rcuplist) {
296 changemask = (rcup->flags | QCUFLAGS_PERSONAL) & ~(QCUFLAGS_PUNISH);
297 flagmask |= QCUFLAGS_PERSONAL;
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 |
303 QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAGS_PUNISH);
304
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 */
309 if (CUIsOwner(rcup))
310 changemask |= ( QCUFLAG_MASTER | QCUFLAG_OWNER );
311 }
312
313 oldflags=rcuplist->flags;
314 if (setflags(&(rcuplist->flags), changemask, cargv[2], rcuflags, REJECT_UNKNOWN | REJECT_DISALLOWED)) {
315 chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE);
316 if (newuser)
317 freeregchanuser(rcuplist);
318 return CMD_ERROR;
319 }
320
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)) {
323 rcuplist->flags=oldflags;
324 chanservstdmessage(sender, QM_USEGIVEOWNER);
325 if (newuser)
326 freeregchanuser(rcuplist);
327 return CMD_ERROR;
328 }
329
330 /* Fix up impossible combinations */
331 rcuplist->flags = cs_sanitisechanlev(rcuplist->flags);
332
333 /* Check if anything "significant" has changed */
334 if ((oldflags ^ rcuplist->flags) & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP))
335 rcuplist->changetime=time(NULL);
336
337 if(rcuplist->flags == oldflags) {
338 chanservstdmessage(sender, QM_CHANLEVNOCHANGE);
339 if (newuser)
340 freeregchanuser(rcuplist);
341 return CMD_OK;
342 }
343
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));
347 csdb_chanlevhistory_insert(rcp, sender, rcuplist->user, oldflags, rcuplist->flags);
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 }
358 chanservstdmessage(sender, QM_CHANLEVCHANGED, cargv[1], cip->name->content,
359 printflags(rcuplist->flags & flagmask, rcuflags));
360 } else {
361 /* User has no flags: delete */
362 if (!newuser) {
363 chanservstdmessage(sender, QM_CHANLEVREMOVED, cargv[1], cip->name->content);
364 csdb_deletechanuser(rcuplist);
365 delreguserfromchannel(rcp, target);
366 }
367 freeregchanuser(rcuplist);
368 rcuplist=NULL;
369 if (cs_removechannelifempty(sender, rcp)) {
370 chanservstdmessage(sender, QM_CHANLEVEMPTIEDCHANNEL);
371 return CMD_OK;
372 }
373 }
374
375 /* Update the channel if needed */
376 rcp->status |= QCSTAT_OPCHECK;
377 cs_timerfunc(cip);
378 } else {
379 if (rcuplist == rcup)
380 flagmask |= QCUFLAGS_PERSONAL;
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 }
387 }
388 }
389
390 return CMD_OK;
391 }