]>
Commit | Line | Data |
---|---|---|
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 | ||
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; | |
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, | |
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 | | |
72ca906c | 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) | |
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 | ||
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) { |
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 { | |
b7eebdcc | 227 | q9strftime(time1,sizeof(time1),rcuplist->usetime); |
1dd6d55d | 228 | } |
229 | if (!rcuplist->changetime) { | |
230 | strcpy(time2, "Unknown"); | |
231 | } else { | |
b7eebdcc | 232 | q9strftime(time2,sizeof(time2),rcuplist->changetime); |
1dd6d55d | 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); | |
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 */ | |
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) { | |
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 | ||
285 | if(channelcount >= MAXCHANNELS) { | |
286 | chanservstdmessage(sender, QM_TOOMANYCHANNELS); | |
287 | return CMD_ERROR; | |
288 | } | |
289 | ||
1dd6d55d | 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 | ||
b2bc2ffe | 306 | /* Everyone can change their own flags (except +dqb), and control (and see) personal flags */ |
1dd6d55d | 307 | if (rcup==rcuplist) { |
50ec21f3 | 308 | changemask = (rcup->flags | QCUFLAGS_PERSONAL) & ~(QCUFLAGS_PUNISH); |
156f6bad | 309 | flagmask |= QCUFLAGS_PERSONAL; |
1dd6d55d | 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 | | |
50ec21f3 | 315 | QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAGS_PUNISH); |
1dd6d55d | 316 | |
8fab5f64 CP |
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 | */ | |
1dd6d55d | 321 | if (CUIsOwner(rcup)) |
8fab5f64 | 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)) { | |
327 | chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE); | |
5413876f CP |
328 | if (newuser) |
329 | freeregchanuser(rcuplist); | |
1dd6d55d | 330 | return CMD_ERROR; |
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 CP |
335 | rcuplist->flags=oldflags; |
336 | chanservstdmessage(sender, QM_USEGIVEOWNER); | |
5413876f CP |
337 | if (newuser) |
338 | freeregchanuser(rcuplist); | |
8fab5f64 CP |
339 | return CMD_ERROR; |
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)) | |
347 | rcuplist->changetime=time(NULL); | |
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], | |
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) { | |
375 | /* User still valid: update or create */ | |
376 | if (newuser) { | |
1dd6d55d | 377 | csdb_createchanuser(rcuplist); |
378 | } else { | |
379 | csdb_updatechanuser(rcuplist); | |
380 | } | |
1bd2b810 | 381 | chanservstdmessage(sender, QM_CHANLEVCHANGED, cargv[1], cip->name->content, |
382 | printflags(rcuplist->flags & flagmask, rcuflags)); | |
1dd6d55d | 383 | } else { |
384 | /* User has no flags: delete */ | |
385 | if (!newuser) { | |
1bd2b810 | 386 | chanservstdmessage(sender, QM_CHANLEVREMOVED, cargv[1], cip->name->content); |
1dd6d55d | 387 | csdb_deletechanuser(rcuplist); |
388 | delreguserfromchannel(rcp, target); | |
389 | } | |
390 | freeregchanuser(rcuplist); | |
391 | rcuplist=NULL; | |
5f7881b2 | 392 | if (cs_removechannelifempty(sender, rcp)) { |
393 | chanservstdmessage(sender, QM_CHANLEVEMPTIEDCHANNEL); | |
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 | } |