]>
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: | |
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 | ||
32 | struct chanuserrec { | |
5a2e0cb7 | 33 | unsigned int flags; |
0ab1dd8c | 34 | regchanuser *rcup; |
35 | reguser *rup; | |
36 | nick *np; | |
37 | }; | |
38 | ||
39 | static 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 | ||
45 | int 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 | 208 | out: |
0ab1dd8c | 209 | free(theusers); |
210 | return CMD_ERROR; | |
211 | } |