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