]>
Commit | Line | Data |
---|---|---|
189935b1 | 1 | /* |
2 | * IRC - Internet Relay Chat, ircd/m_names.c | |
3 | * Copyright (C) 1990 Jarkko Oikarinen and | |
4 | * University of Oulu, Computing Center | |
5 | * | |
6 | * See file AUTHORS in IRC package for additional names of | |
7 | * the programmers. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 1, or (at your option) | |
12 | * any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
d8e74551 | 23 | * $Id: m_names.c,v 1.22.2.1 2005/10/06 04:00:26 entrope Exp $ |
189935b1 | 24 | */ |
25 | ||
26 | /* | |
27 | * m_functions execute protocol messages on this server: | |
28 | * | |
29 | * cptr is always NON-NULL, pointing to a *LOCAL* client | |
30 | * structure (with an open socket connected!). This | |
31 | * identifies the physical socket where the message | |
32 | * originated (or which caused the m_function to be | |
33 | * executed--some m_functions may call others...). | |
34 | * | |
35 | * sptr is the source of the message, defined by the | |
36 | * prefix part of the message if present. If not | |
37 | * or prefix not found, then sptr==cptr. | |
38 | * | |
39 | * (!IsServer(cptr)) => (cptr == sptr), because | |
40 | * prefixes are taken *only* from servers... | |
41 | * | |
42 | * (IsServer(cptr)) | |
43 | * (sptr == cptr) => the message didn't | |
44 | * have the prefix. | |
45 | * | |
46 | * (sptr != cptr && IsServer(sptr) means | |
47 | * the prefix specified servername. (?) | |
48 | * | |
49 | * (sptr != cptr && !IsServer(sptr) means | |
50 | * that message originated from a remote | |
51 | * user (not local). | |
52 | * | |
53 | * combining | |
54 | * | |
55 | * (!IsServer(sptr)) means that, sptr can safely | |
56 | * taken as defining the target structure of the | |
57 | * message in this server. | |
58 | * | |
59 | * *Always* true (if 'parse' and others are working correct): | |
60 | * | |
61 | * 1) sptr->from == cptr (note: cptr->from == cptr) | |
62 | * | |
63 | * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr | |
64 | * *cannot* be a local connection, unless it's | |
65 | * actually cptr!). [MyConnect(x) should probably | |
66 | * be defined as (x == x->from) --msa ] | |
67 | * | |
68 | * parc number of variable parameter strings (if zero, | |
69 | * parv is allowed to be NULL) | |
70 | * | |
71 | * parv a NULL terminated list of parameter pointers, | |
72 | * | |
73 | * parv[0], sender (prefix string), if not present | |
74 | * this points to an empty string. | |
75 | * parv[1]...parv[parc-1] | |
76 | * pointers to additional parameters | |
77 | * parv[parc] == NULL, *always* | |
78 | * | |
79 | * note: it is guaranteed that parv[0]..parv[parc-1] are all | |
80 | * non-NULL pointers. | |
81 | */ | |
82 | #include "config.h" | |
83 | ||
84 | #include "channel.h" | |
85 | #include "client.h" | |
86 | #include "hash.h" | |
87 | #include "ircd.h" | |
88 | #include "ircd_log.h" | |
89 | #include "ircd_reply.h" | |
90 | #include "ircd_string.h" | |
91 | #include "msg.h" | |
92 | #include "numeric.h" | |
93 | #include "numnicks.h" | |
94 | #include "s_user.h" | |
95 | #include "send.h" | |
96 | ||
97 | /* #include <assert.h> -- Now using assert in ircd_log.h */ | |
98 | #include <string.h> | |
99 | ||
100 | /* | |
101 | * Sends a suitably formatted 'names' reply to 'sptr' consisting of nicks within | |
102 | * 'chptr', depending on 'filter'. | |
103 | * | |
104 | * NAMES_ALL - Lists all users on channel. | |
105 | * NAMES_VIS - Only list visible (-i) users. --Gte (04/06/2000). | |
106 | * NAMES_EON - When OR'd with the other two, adds an 'End of Names' numeric | |
107 | * used by m_join | |
108 | * | |
109 | */ | |
110 | ||
111 | void do_names(struct Client* sptr, struct Channel* chptr, int filter) | |
112 | { | |
113 | int mlen; | |
114 | int idx; | |
115 | int flag; | |
116 | int needs_space; | |
117 | int len; | |
118 | char buf[BUFSIZE]; | |
119 | struct Client *c2ptr; | |
120 | struct Membership* member; | |
121 | ||
122 | assert(chptr); | |
123 | assert(sptr); | |
124 | assert((filter&NAMES_ALL) != (filter&NAMES_VIS)); | |
125 | ||
126 | /* Tag Pub/Secret channels accordingly. */ | |
127 | ||
128 | strcpy(buf, "* "); | |
129 | if (PubChannel(chptr)) | |
130 | *buf = '='; | |
131 | else if (SecretChannel(chptr)) | |
132 | *buf = '@'; | |
133 | ||
134 | len = strlen(chptr->chname); | |
135 | strcpy(buf + 2, chptr->chname); | |
136 | strcpy(buf + 2 + len, " :"); | |
137 | ||
138 | idx = len + 4; | |
139 | flag = 1; | |
140 | needs_space = 0; | |
141 | ||
142 | if (!ShowChannel(sptr, chptr)) /* Don't list private channels unless we are on them. */ | |
143 | return; | |
144 | ||
145 | /* Iterate over all channel members, and build up the list. */ | |
146 | ||
147 | mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); | |
148 | ||
149 | for (member = chptr->members; member; member = member->next_member) | |
150 | { | |
151 | c2ptr = member->user; | |
152 | ||
153 | if (((filter&NAMES_VIS)!=0) && IsInvisible(c2ptr)) | |
154 | continue; | |
155 | ||
156 | if (IsZombie(member) && member->user != sptr) | |
157 | continue; | |
158 | ||
159 | if (IsDelayedJoin(member) && (member->user != sptr) && !(filter & NAMES_DEL)) | |
160 | continue; | |
161 | ||
162 | if ((!IsDelayedJoin(member) || (member->user == sptr)) && (filter & NAMES_DEL)) | |
163 | continue; | |
164 | ||
165 | if (needs_space) { | |
166 | strcat(buf, " "); | |
167 | idx++; | |
168 | } | |
169 | needs_space=1; | |
170 | if (IsZombie(member)) | |
171 | { | |
172 | strcat(buf, "!"); | |
173 | idx++; | |
174 | } | |
175 | else if (IsChanOp(member)) | |
176 | { | |
177 | strcat(buf, "@"); | |
178 | idx++; | |
179 | } | |
180 | else if (HasVoice(member)) | |
181 | { | |
182 | strcat(buf, "+"); | |
183 | idx++; | |
184 | } | |
185 | strcat(buf, cli_name(c2ptr)); | |
186 | idx += strlen(cli_name(c2ptr)); | |
187 | flag = 1; | |
188 | if (mlen + idx + NICKLEN + 5 > BUFSIZE) | |
189 | /* space, modifier, nick, \r \n \0 */ | |
190 | { | |
191 | send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); | |
192 | strcpy(buf, "* "); | |
193 | ircd_strncpy(buf + 2, chptr->chname, len + 1); | |
194 | buf[len + 2] = 0; | |
195 | strcat(buf, " :"); | |
196 | if (PubChannel(chptr)) | |
197 | *buf = '='; | |
198 | else if (SecretChannel(chptr)) | |
199 | *buf = '@'; | |
200 | idx = len + 4; | |
201 | flag = 0; | |
202 | needs_space=0; | |
203 | } | |
204 | } | |
205 | if (flag) | |
206 | send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); | |
207 | if (filter&NAMES_EON) | |
208 | send_reply(sptr, RPL_ENDOFNAMES, chptr->chname); | |
209 | } | |
210 | ||
211 | /* | |
212 | * m_names - generic message handler | |
213 | * | |
214 | * parv[0] = sender prefix | |
215 | * parv[1] = channel | |
216 | */ | |
217 | ||
218 | int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) | |
219 | { | |
220 | struct Channel *chptr; | |
221 | struct Channel *ch2ptr; | |
222 | struct Client *c2ptr; | |
223 | struct Membership* member; | |
224 | char* s; | |
225 | char* para = parc > 1 ? parv[1] : 0; | |
226 | int showingdelayed = 0; | |
227 | ||
228 | if (parc > 1 && !ircd_strcmp(parv[1], "-D")) { | |
229 | para = (parc > 2) ? parv[2] : 0; | |
230 | showingdelayed = 1; | |
231 | if (parc > 3 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %s %C", 3, parc, parv)) | |
232 | return 0; | |
233 | } else if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv)) | |
234 | return 0; | |
235 | ||
236 | if (EmptyString(para)) { | |
237 | send_reply(sptr, RPL_ENDOFNAMES, "*"); | |
238 | return 0; | |
239 | } | |
240 | else if (*para == '0') | |
241 | *para = '\0'; | |
242 | ||
243 | s = strchr(para, ','); /* Recursively call m_names for each comma-separated channel. Eww. */ | |
244 | if (s) { | |
d8e74551 | 245 | parv[1+showingdelayed] = ++s; |
189935b1 | 246 | m_names(cptr, sptr, parc, parv); |
247 | } | |
248 | ||
249 | /* | |
250 | * Special Case 1: "/names 0". | |
251 | * Full list as per RFC. | |
252 | */ | |
253 | ||
254 | if (!*para) { | |
255 | int idx; | |
256 | int mlen; | |
257 | int flag; | |
258 | struct Channel *ch3ptr; | |
259 | char buf[BUFSIZE]; | |
260 | ||
261 | mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); | |
262 | ||
263 | /* List all visible channels/visible members */ | |
264 | ||
265 | for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next) | |
266 | { | |
267 | if (!ShowChannel(sptr, ch2ptr)) | |
268 | continue; /* Don't show secret chans. */ | |
269 | ||
270 | if (find_channel_member(sptr, ch2ptr)) | |
271 | { | |
272 | do_names(sptr, ch2ptr, (showingdelayed?NAMES_DEL:0)|NAMES_ALL); /* Full list if we're in this chan. */ | |
273 | } else { | |
274 | do_names(sptr, ch2ptr, (showingdelayed?NAMES_DEL:0)|NAMES_VIS); | |
275 | } | |
276 | } | |
277 | ||
278 | /* List all remaining users on channel '*' */ | |
279 | ||
280 | strcpy(buf, "* * :"); | |
281 | idx = 5; | |
282 | flag = 0; | |
283 | ||
284 | for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr)) | |
285 | { | |
286 | int showflag = 0; | |
287 | ||
288 | if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr))) | |
289 | continue; | |
290 | ||
291 | member = cli_user(c2ptr)->channel; | |
292 | ||
293 | while (member) | |
294 | { | |
295 | ch3ptr = member->channel; | |
296 | ||
297 | if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr)) | |
298 | showflag = 1; | |
299 | ||
300 | member = member->next_channel; | |
301 | } | |
302 | ||
303 | if (showflag) /* Have we already shown them? */ | |
304 | continue; | |
305 | ||
306 | strcat(buf, cli_name(c2ptr)); | |
307 | strcat(buf, " "); | |
308 | idx += strlen(cli_name(c2ptr)) + 1; | |
309 | flag = 1; | |
310 | ||
311 | if (mlen + idx + NICKLEN + 3 > BUFSIZE) /* space, \r\n\0 */ | |
312 | { | |
313 | send_reply(sptr, RPL_NAMREPLY, buf); | |
314 | strcpy(buf, "* * :"); | |
315 | idx = 5; | |
316 | flag = 0; | |
317 | } | |
318 | } | |
319 | if (flag) | |
320 | send_reply(sptr, RPL_NAMREPLY, buf); | |
321 | send_reply(sptr, RPL_ENDOFNAMES, "*"); | |
322 | return 1; | |
323 | } | |
324 | ||
325 | /* | |
326 | * Special Case 2: User is on this channel, requesting full names list. | |
327 | * (As performed with each /join) - ** High frequency usage ** | |
328 | */ | |
329 | ||
330 | chptr = FindChannel(para); | |
331 | ||
332 | if (chptr) { | |
333 | member = find_member_link(chptr, sptr); | |
334 | if (member) | |
335 | { | |
336 | do_names(sptr, chptr, (showingdelayed?NAMES_DEL:0)|NAMES_ALL); | |
337 | if (!EmptyString(para)) | |
338 | { | |
339 | send_reply(sptr, RPL_ENDOFNAMES, chptr ? chptr->chname : para); | |
340 | return 1; | |
341 | } | |
342 | } | |
343 | else | |
344 | { | |
345 | /* | |
346 | * Special Case 3: User isn't on this channel, show all visible users, in | |
347 | * non secret channels. | |
348 | */ | |
349 | do_names(sptr, chptr, (showingdelayed?NAMES_DEL:0)|NAMES_VIS); | |
350 | send_reply(sptr, RPL_ENDOFNAMES, para); | |
351 | } | |
352 | } else { /* Channel doesn't exist. */ | |
353 | send_reply(sptr, RPL_ENDOFNAMES, para); | |
354 | } | |
355 | return 1; | |
356 | } | |
357 | ||
358 | ||
359 | /* | |
360 | * ms_names - server message handler | |
361 | * | |
362 | * parv[0] = sender prefix | |
363 | * parv[1] = channel | |
364 | */ | |
365 | int ms_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) | |
366 | { | |
367 | struct Channel *chptr; | |
368 | struct Channel *ch2ptr; | |
369 | struct Client *c2ptr; | |
370 | struct Membership* member; | |
371 | char* s; | |
372 | char* para = parc > 1 ? parv[1] : 0; | |
373 | int showingdelayed = 0; | |
374 | ||
375 | if (parc > 1 && !ircd_strcmp(parv[1], "-D")) { | |
376 | para = (parc > 2) ? parv[2] : 0; | |
377 | showingdelayed = NAMES_DEL; | |
378 | if (parc > 3 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %s %C", 3, parc, parv)) | |
379 | return 0; | |
380 | } else if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv)) | |
381 | return 0; | |
382 | ||
383 | if (EmptyString(para)) { | |
384 | send_reply(sptr, RPL_ENDOFNAMES, "*"); | |
385 | return 0; | |
386 | } | |
387 | else if (*para == '0') | |
388 | *para = '\0'; | |
389 | ||
390 | s = strchr(para, ','); /* Recursively call m_names for each comma-separated channel. */ | |
391 | if (s) { | |
d8e74551 | 392 | parv[1+!!showingdelayed] = ++s; |
189935b1 | 393 | m_names(cptr, sptr, parc, parv); |
394 | } | |
395 | ||
396 | /* | |
397 | * Special Case 1: "/names 0". | |
398 | * Full list as per RFC. | |
399 | */ | |
400 | ||
401 | if (!*para) { | |
402 | int idx; | |
403 | int mlen; | |
404 | int flag; | |
405 | struct Channel *ch3ptr; | |
406 | char buf[BUFSIZE]; | |
407 | ||
408 | mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); | |
409 | ||
410 | /* List all visible channels/visible members */ | |
411 | ||
412 | for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next) | |
413 | { | |
414 | if (!ShowChannel(sptr, ch2ptr)) | |
415 | continue; /* Don't show secret chans. */ | |
416 | ||
417 | if (find_channel_member(sptr, ch2ptr)) | |
418 | { | |
419 | do_names(sptr, ch2ptr, showingdelayed|NAMES_ALL); /* Full list if we're in this chan. */ | |
420 | } else { | |
421 | do_names(sptr, ch2ptr, showingdelayed|NAMES_VIS); | |
422 | } | |
423 | } | |
424 | ||
425 | /* List all remaining users on channel '*' */ | |
426 | ||
427 | strcpy(buf, "* * :"); | |
428 | idx = 5; | |
429 | flag = 0; | |
430 | ||
431 | for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr)) | |
432 | { | |
433 | int showflag = 0; | |
434 | ||
435 | if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr))) | |
436 | continue; | |
437 | ||
438 | member = cli_user(c2ptr)->channel; | |
439 | ||
440 | while (member) | |
441 | { | |
442 | ch3ptr = member->channel; | |
443 | ||
444 | if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr)) | |
445 | showflag = 1; | |
446 | ||
447 | member = member->next_channel; | |
448 | } | |
449 | ||
450 | if (showflag) /* Have we already shown them? */ | |
451 | continue; | |
452 | ||
453 | strcat(buf, cli_name(c2ptr)); | |
454 | strcat(buf, " "); | |
455 | idx += strlen(cli_name(c2ptr)) + 1; | |
456 | flag = 1; | |
457 | ||
458 | if (mlen + idx + NICKLEN + 3 > BUFSIZE) /* space, \r\n\0 */ | |
459 | { | |
460 | send_reply(sptr, RPL_NAMREPLY, buf); | |
461 | strcpy(buf, "* * :"); | |
462 | idx = 5; | |
463 | flag = 0; | |
464 | } | |
465 | } | |
466 | if (flag) | |
467 | send_reply(sptr, RPL_NAMREPLY, buf); | |
468 | send_reply(sptr, RPL_ENDOFNAMES, "*"); | |
469 | return 1; | |
470 | } | |
471 | ||
472 | /* | |
473 | * Special Case 2: User is on this channel, requesting full names list. | |
474 | * (As performed with each /join) - ** High frequency usage ** | |
475 | */ | |
476 | ||
477 | chptr = FindChannel(para); | |
478 | ||
479 | if (chptr) { | |
480 | member = find_member_link(chptr, sptr); | |
481 | if (member) | |
482 | { | |
483 | do_names(sptr, chptr, showingdelayed|NAMES_ALL); | |
484 | if (!EmptyString(para)) | |
485 | { | |
486 | send_reply(sptr, RPL_ENDOFNAMES, chptr ? chptr->chname : para); | |
487 | return 1; | |
488 | } | |
489 | } | |
490 | else | |
491 | { | |
492 | /* | |
493 | * Special Case 3: User isn't on this channel, show all visible users, in | |
494 | * non secret channels. | |
495 | */ | |
496 | do_names(sptr, chptr, showingdelayed|NAMES_VIS); | |
497 | } | |
498 | } else { /* Channel doesn't exist. */ | |
499 | send_reply(sptr, RPL_ENDOFNAMES, para); | |
500 | } | |
501 | return 1; | |
502 | } |