]>
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; | |
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 | } |