]> jfr.im git - irc/quakenet/newserv.git/blame - chanserv/chancmds/chanlev.c
CHANSERV: better batcher error handling for expired accounts/accounts with no email.
[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;
3c6cc37d 93 regchanuser *rcup, *trcup, *rcuplist;
1dd6d55d 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;
0555113a 104 void *args[3];
1dd6d55d 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,
f24e6935 112 NULL, "chanlev", QPRIV_VIEWFULLCHANLEV, 0)))
1dd6d55d 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 |
f24e6935 120 QCUFLAG_AUTOOP | QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAG_KNOWN);
1dd6d55d 121
b2bc2ffe 122 /* masters and above can see everything except personal flags */
cabd7271 123 if (rcup && CUHasMasterPriv(rcup)) {
b2bc2ffe 124 flagmask = QCUFLAG_ALL & ~QCUFLAGS_PERSONAL;
125 showtimes=1;
126 }
127
128 /* Staff access, show everything */
129 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
1dd6d55d 130 flagmask = QCUFLAG_ALL;
131 showtimes=1;
132 }
133
134 if (cargc==1) {
135 /* One arg: list chanlev */
96249709 136 int ncnt=0,mcnt=0,ocnt=0,vcnt=0,kcnt=0,bcnt=0;
137
1dd6d55d 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
e086f1b5 149 if (CIsSuspended(rcp) && cs_privcheck(QPRIV_VIEWCHANSUSPENSION, sender)) {
79313d98 150 char *bywhom;
e086f1b5
CP
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
e086f1b5
CP
163 chanservstdmessage(sender, QM_CHANLEV_SUSPENDREASON, rcp->suspendreason?rcp->suspendreason->content:"(no reason)");
164 chanservstdmessage(sender, QM_CHANLEV_SUSPENDBY, bywhom);
79313d98 165 chanservstdmessage(sender, QM_CHANLEV_SUSPENDSINCE, rcp->suspendtime);
e086f1b5
CP
166 }
167
baafd54b
CP
168 if (rcp->comment && (cs_privcheck(QPRIV_VIEWCOMMENTS, sender)))
169 chanservstdmessage(sender, QM_SHORT_COMMENT, rcp->comment->content);
170
1dd6d55d 171 /* Count users */
172 for (i=0,usercount=0;i<REGCHANUSERHASHSIZE;i++)
173 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
f24e6935 174 usercount++;
1dd6d55d 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) {
f24e6935
GB
182 if (!(flags=rcuplist->flags & flagmask))
183 continue;
184
185 rusers[j++]=rcuplist;
1dd6d55d 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))
f24e6935 197 continue;
1dd6d55d 198
156f6bad 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
96249709 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
1dd6d55d 214 if (!donehead) {
f24e6935
GB
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;
1dd6d55d 221 }
222
223 if (showtimes) {
f24e6935
GB
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:"");
1dd6d55d 236 } else
f24e6935 237 chanservsendmessage(sender, " %-15s %s", rcuplist->user->username, printflags(flags, rcuflags));
1dd6d55d 238 }
239
240 if (donehead) {
241 chanservstdmessage(sender, QM_ENDOFLIST);
96249709 242 chanservstdmessage(sender, QM_CHANLEVSUMMARY, j, ncnt, mcnt, ocnt, vcnt, kcnt, bcnt);
1dd6d55d 243 } else {
244 chanservstdmessage(sender, QM_NOUSERSONCHANLEV, cip->name->content);
245 }
d72584f9 246
247 triggerhook(HOOK_CHANSERV_CHANLEVDUMP, sender);
1dd6d55d 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 */
f24e6935
GB
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;
1dd6d55d 266 }
267
268 if (!rcuplist) {
705542fa 269 /* new user, we could store a count instead... that's probably better... */
3c6cc37d 270 unsigned int chanlevcount, channelcount;
705542fa 271
3c6cc37d 272 for (chanlevcount=i=0;i<REGCHANUSERHASHSIZE;i++)
705542fa 273 for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
3c6cc37d 274 chanlevcount++;
705542fa 275
3c6cc37d 276 if(chanlevcount >= MAXCHANLEVS) {
705542fa
CP
277 chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
278 return CMD_ERROR;
279 }
3c6cc37d
GB
280
281 channelcount=0;
282 for (trcup=target->knownon;trcup;trcup=trcup->nextbyuser)
283 channelcount++;
284
09aa0b0a 285 if(channelcount >= MAXCHANNELS) {
3c6cc37d
GB
286 chanservstdmessage(sender, QM_TOOMANYCHANNELS);
287 return CMD_ERROR;
288 }
289
f24e6935
GB
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;
1dd6d55d 298 }
299
300 if (cs_privcheck(QPRIV_CHANGECHANLEV, sender)) {
f24e6935
GB
301 /* Opers are allowed to change everything */
302 changemask = QCUFLAG_ALL;
1dd6d55d 303 } else {
f24e6935
GB
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 );
1dd6d55d 323 }
324
325 oldflags=rcuplist->flags;
326 if (setflags(&(rcuplist->flags), changemask, cargv[2], rcuflags, REJECT_UNKNOWN | REJECT_DISALLOWED)) {
f24e6935 327 chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE);
5413876f
CP
328 if (newuser)
329 freeregchanuser(rcuplist);
f24e6935 330 return CMD_ERROR;
1dd6d55d 331 }
332
2ca43b1b 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)) {
8fab5f64 335 rcuplist->flags=oldflags;
f24e6935 336 chanservstdmessage(sender, QM_USEGIVEOWNER);
5413876f
CP
337 if (newuser)
338 freeregchanuser(rcuplist);
f24e6935 339 return CMD_ERROR;
8fab5f64
CP
340 }
341
95332d7b 342 /* Fix up impossible combinations */
343 rcuplist->flags = cs_sanitisechanlev(rcuplist->flags);
156f6bad 344
1dd6d55d 345 /* Check if anything "significant" has changed */
346 if ((oldflags ^ rcuplist->flags) & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP))
f24e6935 347 rcuplist->changetime=time(NULL);
1dd6d55d 348
183b8e2f 349 if(rcuplist->flags == oldflags) {
1bd2b810 350 chanservstdmessage(sender, QM_CHANLEVNOCHANGE);
5413876f 351 if (newuser)
1bd2b810 352 freeregchanuser(rcuplist);
183b8e2f
P
353 return CMD_OK;
354 }
355
1dd6d55d 356 strcpy(flagbuf,printflags(oldflags,rcuflags));
357 cs_log(sender,"CHANLEV %s #%s %s (%s -> %s)",cip->name->content,rcuplist->user->username,cargv[2],
f24e6935 358 flagbuf,printflags(rcuplist->flags,rcuflags));
183b8e2f 359 csdb_chanlevhistory_insert(rcp, sender, rcuplist->user, oldflags, rcuplist->flags);
1dd6d55d 360
515a27ad 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
d72584f9 367 args[0]=sender;
368 args[1]=rcuplist;
369 args[2]=(void *)oldflags;
515a27ad 370
d72584f9 371 triggerhook(HOOK_CHANSERV_CHANLEVMOD, args);
372
1dd6d55d 373 /* Now see what we do next */
374 if (rcuplist->flags) {
f24e6935
GB
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,
1bd2b810 382 printflags(rcuplist->flags & flagmask, rcuflags));
1dd6d55d 383 } else {
f24e6935
GB
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);
5f7881b2 394 return CMD_OK;
395 }
1dd6d55d 396 }
1bd2b810 397
398 /* Update the channel if needed */
1dd6d55d 399 rcp->status |= QCSTAT_OPCHECK;
400 cs_timerfunc(cip);
1dd6d55d 401 } else {
50ec21f3 402 if (rcuplist == rcup)
403 flagmask |= QCUFLAGS_PERSONAL;
1bd2b810 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 }
1dd6d55d 410 }
411 }
515a27ad 412
1dd6d55d 413
414 return CMD_OK;
415}