]> jfr.im git - irc/quakenet/newserv.git/blame - chanserv/chancmds/users.c
CHANSERV: better batcher error handling for expired accounts/accounts with no email.
[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:
d75b8aac 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.
0ab1dd8c 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
5a2e0cb7 27/* Random flags and structs and sort functions which will come in handly later */
28
0ab1dd8c 29#define ISQ 0x40000000
30#define ISOP 0x20000000
31#define ISV 0x10000000
32
33struct chanuserrec {
5a2e0cb7 34 unsigned int flags;
0ab1dd8c 35 regchanuser *rcup;
36 reguser *rup;
37 nick *np;
38};
39
40static 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
46int csc_dousers(void *source, int cargc, char **cargv) {
47 nick *sender=source, *np;
5a2e0cb7 48 reguser *rup=getreguserfromnick(sender);
0ab1dd8c 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;
5a2e0cb7 59 unsigned int notonchan=0;
0ab1dd8c 60
61 if (cargc<1) {
62 chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "users");
63 return CMD_ERROR;
64 }
65
fc823b95 66 if (!(cip=cs_checkaccess(sender, cargv[0], CA_KNOWN,
0ab1dd8c 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
5a2e0cb7 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) {
0ab1dd8c 90 chanservstdmessage(sender,QM_NOTONCHAN,cip->name->content);
91 return CMD_ERROR;
92 }
5a2e0cb7 93
94 /* malloc() time: if we abort after here we should goto out; rather than
95 * return; */
0ab1dd8c 96 theusers=malloc(cp->users->totalusers * sizeof(struct chanuserrec));
97 memset(theusers,0,cp->users->totalusers * sizeof(struct chanuserrec));
5a2e0cb7 98
99 /* OK, fill in our custom struct with useful info about each actual user */
0ab1dd8c 100 for (i=0,j=0;i<cp->users->hashsize;i++) {
101 if (cp->users->content[i]==nouser)
102 continue;
103
5a2e0cb7 104 /* shouldn't happen */
0ab1dd8c 105 if (!(np=getnickbynumeric(cp->users->content[i])))
106 goto out;
107
5a2e0cb7 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
0ab1dd8c 118 theusers[j].np=np;
119
5a2e0cb7 120 /* Copy op and voice bits from the chanuser */
0ab1dd8c 121 theusers[j].flags=(cp->users->content[i]>>2) & 0x30000000;
122
5a2e0cb7 123 /* Make sure we come out at the top of the list */
0ab1dd8c 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);
5a2e0cb7 131 /* OR in the chanlev flags at the bottom if they are known */
0ab1dd8c 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
fc823b95 140 chanservstdmessage(sender, QM_USERSHEADER, cip->name->content);
5a2e0cb7 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 */
0ab1dd8c 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) {
5a2e0cb7 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. */
0ab1dd8c 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
5a2e0cb7 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. */
0ab1dd8c 194 if (theusers[i].rcup && (theusers[i].rcup->flags & flagmask)) {
5a2e0cb7 195 flagbuf=printflags((flagmask | (theusers[i].rup==rup?QCUFLAGS_PERSONAL:0)) & theusers[i].rcup->flags, rcuflags);
0ab1dd8c 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
5a2e0cb7 209out:
0ab1dd8c 210 free(theusers);
211 return CMD_ERROR;
212}