]> jfr.im git - irc/quakenet/newserv.git/blame - chanserv/chancmds/users.c
Update USERS command to have silly restriction on "helpers" as per helpmod2.
[irc/quakenet/newserv.git] / chanserv / chancmds / users.c
CommitLineData
0ab1dd8c 1/*
2 * CMDNAME: users
3 * CMDLEVEL: QCMD_AUTHED
4 * CMDARGS: 1
5 * CMDDESC: Displays a list of users on the channel.
6 * CMDFUNC: csc_dousers
7 * CMDPROTO: int csc_dousers(void *source, int cargc, char **cargv);
8 * CMDHELP: Usage: USERS <channel>
9 * CMDHELP: Displays a list of users on the named channel along with their usernames and flags
10 * CMDHELP: on the channel, where:
11 * CMDHELP: channel - channel to use
12 * CMDHELP: USERS requires operator (+o) access on the named channel.
13 */
14
15#include "../chanserv.h"
16#include "../../nick/nick.h"
17#include "../../lib/flags.h"
18#include "../../lib/irc_string.h"
19#include "../../channel/channel.h"
20#include "../../parser/parser.h"
21#include "../../irc/irc.h"
22#include "../../localuser/localuserchannel.h"
23#include <string.h>
24#include <stdio.h>
25
5a2e0cb7 26/* Random flags and structs and sort functions which will come in handly later */
27
0ab1dd8c 28#define ISQ 0x40000000
29#define ISOP 0x20000000
30#define ISV 0x10000000
31
32struct chanuserrec {
5a2e0cb7 33 unsigned int flags;
0ab1dd8c 34 regchanuser *rcup;
35 reguser *rup;
36 nick *np;
37};
38
39static int comparetheflags(const void *a, const void *b) {
40 const struct chanuserrec *ra=a, *rb=b;
41
42 return rb->flags-ra->flags;
43}
44
45int csc_dousers(void *source, int cargc, char **cargv) {
46 nick *sender=source, *np;
5a2e0cb7 47 reguser *rup=getreguserfromnick(sender);
0ab1dd8c 48 chanindex *cip;
49 regchan *rcp;
50 struct chanuserrec *theusers;
51 regchanuser *rcup;
52 channel *cp;
53 unsigned int i,j;
54 char *flagbuf, *unbuf, modechar;
55 char uhbuf[USERLEN+HOSTLEN+2];
56 flag_t flagmask;
57 unsigned int ops,voices,users,flags,qops,masters;
5a2e0cb7 58 unsigned int notonchan=0;
0ab1dd8c 59
60 if (cargc<1) {
61 chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "users");
62 return CMD_ERROR;
63 }
64
fc823b95 65 if (!(cip=cs_checkaccess(sender, cargv[0], CA_KNOWN,
0ab1dd8c 66 NULL, "users", QPRIV_VIEWFULLCHANLEV, 0)))
67 return CMD_ERROR;
68
69 rcp=cip->exts[chanservext];
70
71 if (!(cp=cip->channel)) {
72 chanservstdmessage(sender,QM_EMPTYCHAN,cip->name->content);
73 return CMD_ERROR;
74 }
75
5a2e0cb7 76 /* Two part logic here. Firstly, only staff are allowed to look at
77 * channels they are not on. But later we will prevent them looking at
78 * channels with opers on them, but clearly it's ok to do this on any
79 * channel you are actually on regardless of whether there is an oper or
80 * not. So extract this "notonchan" status here and use it in the later
81 * checks. We set "notonchan" if you are not an oper and not on the
82 * channel. Non-staff with "notonchan" set are then immediately rebuffed.
83 */
84 if (!getnumerichandlefromchanhash(cip->channel->users,sender->numeric) &&
85 !UHasOperPriv(rup))
86 notonchan=1;
87
88 if (!cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender) && notonchan) {
0ab1dd8c 89 chanservstdmessage(sender,QM_NOTONCHAN,cip->name->content);
90 return CMD_ERROR;
91 }
5a2e0cb7 92
93 /* malloc() time: if we abort after here we should goto out; rather than
94 * return; */
0ab1dd8c 95 theusers=malloc(cp->users->totalusers * sizeof(struct chanuserrec));
96 memset(theusers,0,cp->users->totalusers * sizeof(struct chanuserrec));
5a2e0cb7 97
98 /* OK, fill in our custom struct with useful info about each actual user */
0ab1dd8c 99 for (i=0,j=0;i<cp->users->hashsize;i++) {
100 if (cp->users->content[i]==nouser)
101 continue;
102
5a2e0cb7 103 /* shouldn't happen */
0ab1dd8c 104 if (!(np=getnickbynumeric(cp->users->content[i])))
105 goto out;
106
5a2e0cb7 107 /* Don't let non-opers look at channels with opers in. How paranoid are we?
108 * Obviously if you are on the channel yourself, no such silliness is necessary.
109 * Making the arbitrary assumption that one letter nicks are OK as per helpmod2.
110 */
111 if (notonchan && IsOper(np) && np->nick[1] &&
112 (IsSecret(cp) || IsPrivate(cp) || IsKey(cp) || IsInviteOnly(cp))) {
113 chanservstdmessage(sender,QM_OPERONCHAN,"users",cip->name->content);
114 goto out;
115 }
116
0ab1dd8c 117 theusers[j].np=np;
118
5a2e0cb7 119 /* Copy op and voice bits from the chanuser */
0ab1dd8c 120 theusers[j].flags=(cp->users->content[i]>>2) & 0x30000000;
121
5a2e0cb7 122 /* Make sure we come out at the top of the list */
0ab1dd8c 123 if (np==chanservnick) {
124 theusers[j].flags|=0x40000000;
125 }
126
127 theusers[j].rup=getreguserfromnick(np);
128 if (theusers[j].rup) {
129 theusers[j].rcup=findreguseronchannel(rcp, theusers[j].rup);
5a2e0cb7 130 /* OR in the chanlev flags at the bottom if they are known */
0ab1dd8c 131 if (theusers[j].rcup)
132 theusers[j].flags |= theusers[j].rcup->flags;
133 }
134 j++;
135 }
136
137 qsort(theusers, j, sizeof(struct chanuserrec), comparetheflags);
138
fc823b95 139 chanservstdmessage(sender, QM_USERSHEADER, cip->name->content);
5a2e0cb7 140
141 /* Determine what flags to show. Public flags always OK, punishment flags
142 * for masters only, personal flags for yourself only except staff who can
143 * see all. Obviously we have to do the bit about personal flags later on...
144 */
0ab1dd8c 145 flagmask=QCUFLAGS_PUBLIC;
146 ops=voices=users=flags=qops=masters=0;
147 if (cs_checkaccess(sender, NULL, CA_MASTERPRIV, cip, "users", 0, 1))
148 flagmask |= QCUFLAGS_PUNISH;
149
150 if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender))
151 flagmask = QCUFLAG_ALL;
152
153 for (i=0;i<j;i++) {
154 if (theusers[i].flags & ISOP) {
155 ops++;
156 modechar='@';
157 } else if (theusers[i].flags & ISV) {
158 voices++;
159 modechar='+';
160 } else {
161 users++;
162 modechar=' ';
163 }
164
165 if (theusers[i].flags & ISQ) {
5a2e0cb7 166 /* OK so in reality we may actually have a username and even some
167 * flags. But those don't really matter so let's sub in a cute message. */
0ab1dd8c 168 unbuf="It's me!";
169 flagbuf="";
170 } else {
171 if (theusers[i].rup) {
172 unbuf=theusers[i].rup->username;
173 } else {
174 unbuf="";
175 }
176
177 rcup=theusers[i].rcup;
178
179 if (rcup) {
180 flags++;
181 if (CUHasOpPriv(rcup))
182 qops++;
183
184 if (CUIsMaster(rcup) || CUIsOwner(rcup))
185 masters++;
186 }
187
5a2e0cb7 188 /* Subtlety here: we conditionally OR in the personal flags for
189 * display but not for the check. This is because if you have
190 * personal flags you must have some public flag too by definition.
191 * Compare based on reguser so you can see personal flags for all
192 * clones on your account. */
0ab1dd8c 193 if (theusers[i].rcup && (theusers[i].rcup->flags & flagmask)) {
5a2e0cb7 194 flagbuf=printflags((flagmask | (theusers[i].rup==rup?QCUFLAGS_PERSONAL:0)) & theusers[i].rcup->flags, rcuflags);
0ab1dd8c 195 } else {
196 flagbuf="";
197 }
198 }
199 np=theusers[i].np;
200 chanservsendmessage(sender, "%c%-15s %-15s %-12s (%s)",modechar,np->nick,unbuf,flagbuf,visibleuserhost(np, uhbuf));
201 }
202 free(theusers);
203
204 chanservstdmessage(sender, QM_ENDOFLIST);
205 chanservstdmessage(sender, QM_USERSSUMMARY, j, ops, voices, users, flags, qops, masters);
206 return CMD_OK;
207
5a2e0cb7 208out:
0ab1dd8c 209 free(theusers);
210 return CMD_ERROR;
211}