]>
Commit | Line | Data |
---|---|---|
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 | ||
33 | struct chanuserrec { | |
5a2e0cb7 | 34 | unsigned int flags; |
0ab1dd8c | 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; | |
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 | 209 | out: |
0ab1dd8c | 210 | free(theusers); |
211 | return CMD_ERROR; | |
212 | } |