]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blob - check.patch
use FindUser instead of Findclient/server to find a user
[irc/quakenet/snircd-patchqueue.git] / check.patch
1 # HG changeset patch
2 # Parent 4e198b121c286785bbdc97fefed95f6d7b95c9ab
3
4 diff -r 4e198b121c28 include/check.h
5 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6 +++ b/include/check.h Fri Jul 26 19:55:24 2013 +0100
7 @@ -0,0 +1,49 @@
8 +/*
9 + * IRC - Internet Relay Chat, ircd/check.h
10 + * Copyright (C) 1990 University of Oulu, Computing Center
11 + *
12 + * This program is free software; you can redistribute it and/or modify
13 + * it under the terms of the GNU General Public License as published by
14 + * the Free Software Foundation; either version 1, or (at your option)
15 + * any later version.
16 + *
17 + * This program is distributed in the hope that it will be useful,
18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 + * GNU General Public License for more details.
21 + *
22 + * You should have received a copy of the GNU General Public License
23 + * along with this program; if not, write to the Free Software
24 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 + */
26 +
27 +/*
28 + * - ASUKA ---------------------------------------------------------------------
29 + * These are the declarations of the CHECK functions for Asuka.
30 + * Some of this code is from previous QuakeNet ircds, and some of it is my own.
31 + * The old code was written by Durzel (durzel@quakenet.org).
32 + *
33 + * qoreQ (qoreQ@quakenet.org) - 08/14/2002
34 + * -----------------------------------------------------------------------------
35 + */
36 +
37 +#ifndef INCLUDED_check_h
38 +#define INCLUDED_check_h
39 +
40 +#define HEADERLINE "--------------------------------------------------------------------"
41 +#define COLOR_OFF '\017'
42 +
43 +/* IP is IPv4, or IPv4 over IPv6 (2002::/16 range) */
44 +#define has_ipv4_addr(x) (irc_in_addr_is_ipv4(x) || (x)->in6_16[0] == htons(0x2002))
45 +/* Return IP in IPv4 notation, also when IP is IPv4 over IPv6 */
46 +#define get_ipv4_addr(x) (irc_in_addr_is_ipv4(x) ? \
47 + (ntohs((x)->in6_16[6]) << 16) | ntohs((x)->in6_16[7]) : \
48 + (ntohs((x)->in6_16[1]) << 16) | ntohs((x)->in6_16[2]))
49 +
50 +extern void checkChannel(struct Client *sptr, struct Channel *chptr);
51 +extern void checkUsers(struct Client *sptr, struct Channel *chptr, int flags);
52 +extern void checkClient(struct Client *sptr, struct Client *acptr);
53 +extern void checkServer(struct Client *sptr, struct Client *acptr);
54 +extern signed int checkHostmask(struct Client *sptr, char *hoststr, int flags);
55 +
56 +#endif /* INCLUDED_check_h */
57 diff -r 4e198b121c28 include/client.h
58 --- a/include/client.h Thu Jul 25 22:43:11 2013 +0100
59 +++ b/include/client.h Fri Jul 26 19:55:24 2013 +0100
60 @@ -776,6 +776,9 @@
61 #define HIDE_IP 0 /**< Do not show IP address in get_client_name() */
62 #define SHOW_IP 1 /**< Show ident and IP address in get_client_name() */
63
64 +/** Number of bits unique per user under IPv6, used for clone checks */
65 +#define IPV6USERBITS 64
66 +
67 extern const char* get_client_name(const struct Client* sptr, int showip);
68 extern const char* client_get_default_umode(const struct Client* sptr);
69 extern int client_get_ping(const struct Client* local_client);
70 diff -r 4e198b121c28 include/handlers.h
71 --- a/include/handlers.h Thu Jul 25 22:43:11 2013 +0100
72 +++ b/include/handlers.h Fri Jul 26 19:55:24 2013 +0100
73 @@ -88,6 +88,19 @@
74
75 extern int m_admin(struct Client*, struct Client*, int, char*[]);
76 extern int m_away(struct Client*, struct Client*, int, char*[]);
77 +
78 +/*
79 + * - ASUKA ---------------------------------------------------------------------
80 + * Add the command for CHECK.
81 + * This was adapted from Lain for use in Asuka.
82 + * Original code by Durzel (durzel@quakenet.org).
83 + *
84 + * qoreQ (qoreQ@quakenet.org) - 08/30/2002
85 + * -----------------------------------------------------------------------------
86 + */
87 +
88 +extern int m_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]);
89 +
90 extern int m_cap(struct Client*, struct Client*, int, char*[]);
91 extern int m_cnotice(struct Client*, struct Client*, int, char*[]);
92 extern int m_cprivmsg(struct Client*, struct Client*, int, char*[]);
93 diff -r 4e198b121c28 include/ircd_features.h
94 --- a/include/ircd_features.h Thu Jul 25 22:43:11 2013 +0100
95 +++ b/include/ircd_features.h Fri Jul 26 19:55:24 2013 +0100
96 @@ -102,6 +102,7 @@
97 FEAT_ANNOUNCE_INVITES,
98
99 /* features that affect all operators */
100 + FEAT_EXTENDED_CHECKCMD,
101 FEAT_CONFIG_OPERCMDS,
102 FEAT_SETHOST,
103 FEAT_SETHOST_USER,
104 diff -r 4e198b121c28 include/msg.h
105 --- a/include/msg.h Thu Jul 25 22:43:11 2013 +0100
106 +++ b/include/msg.h Fri Jul 26 19:55:24 2013 +0100
107 @@ -265,6 +265,10 @@
108 #define TOK_SERVSET "SERVSET"
109 #define CMD_SERVSET MSG_SERVSET, TOK_SERVSET
110
111 +#define MSG_CHECK "CHECK"
112 +#define TOK_CHECK "CC"
113 +#define CMD_CHECK MSG_CHECK, TOK_CHECK
114 +
115 #define MSG_REHASH "REHASH" /* REHA */
116 #define TOK_REHASH "REHASH"
117 #define CMD_REHASH MSG_REHASH, TOK_REHASH
118 diff -r 4e198b121c28 include/s_user.h
119 --- a/include/s_user.h Thu Jul 25 22:43:11 2013 +0100
120 +++ b/include/s_user.h Fri Jul 26 19:55:24 2013 +0100
121 @@ -54,6 +54,12 @@
122 #define ALLOWMODES_ANY 0 /**< Allow any user mode */
123 #define ALLOWMODES_DEFAULT 1 /**< Only allow the subset of modes that are legit defaults */
124
125 +/* return sets for umode_str() */
126 +#define UMODE_ALL_PARAMS 0 /**< return the user modes and all parameters */
127 +#define UMODE_ALL_PARAMS_BUT_OPERID 1 /**< return the user modes and all parameters except OperID */
128 +#define UMODE_AND_ACCOUNT 2 /**< return the user modes and account parameter */
129 +#define UMODE_AND_ACCOUNT_SHORT 3 /**< return the user modes and account (but no account timestamp, ID or flags) */
130 +
131 /** Formatter function for send_user_info().
132 * @param who Client being displayed.
133 * @param sptr Client requesting information.
134 diff -r 4e198b121c28 ircd/IPcheck.c
135 --- a/ircd/IPcheck.c Thu Jul 25 22:43:11 2013 +0100
136 +++ b/ircd/IPcheck.c Fri Jul 26 19:55:24 2013 +0100
137 @@ -120,7 +120,7 @@
138 ip_registry_canonicalize(&canon, ip);
139 entry = hashTable[ip_registry_hash(&canon)];
140 for ( ; entry; entry = entry->next) {
141 - int bits = (canon.in6_16[0] == htons(0x2002)) ? 48 : 64;
142 + int bits = (canon.in6_16[0] == htons(0x2002)) ? 48 : IPV6USERBITS;
143 if (ipmask_check(&canon, &entry->addr, bits))
144 break;
145 }
146 diff -r 4e198b121c28 ircd/Makefile.in
147 --- a/ircd/Makefile.in Thu Jul 25 22:43:11 2013 +0100
148 +++ b/ircd/Makefile.in Fri Jul 26 19:55:24 2013 +0100
149 @@ -119,6 +119,7 @@
150 m_away.c \
151 m_burst.c \
152 m_cap.c \
153 + m_check.c \
154 m_clearmode.c \
155 m_close.c \
156 m_connect.c \
157 diff -r 4e198b121c28 ircd/ircd_features.c
158 --- a/ircd/ircd_features.c Thu Jul 25 22:43:11 2013 +0100
159 +++ b/ircd/ircd_features.c Fri Jul 26 19:55:24 2013 +0100
160 @@ -367,6 +367,7 @@
161 F_B(ANNOUNCE_INVITES, 0, 0, 0),
162
163 /* features that affect all operators */
164 + F_B(EXTENDED_CHECKCMD, 0, 0, 0),
165 F_B(CONFIG_OPERCMDS, 0, 0, 0),
166 F_B(SETHOST, 0, 0, 0),
167 F_B(SETHOST_USER, 0, 0, 0),
168 diff -r 4e198b121c28 ircd/m_check.c
169 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
170 +++ b/ircd/m_check.c Fri Jul 26 19:55:24 2013 +0100
171 @@ -0,0 +1,804 @@
172 +/*
173 + * IRC - Internet Relay Chat, ircd/m_check.c
174 + * Copyright (C) 1990 Jarkko Oikarinen and
175 + * University of Oulu, Computing Center
176 + *
177 + * See file AUTHORS in IRC package for additional names of
178 + * the programmers.
179 + *
180 + * This program is free software; you can redistribute it and/or modify
181 + * it under the terms of the GNU General Public License as published by
182 + * the Free Software Foundation; either version 1, or (at your option)
183 + * any later version.
184 + *
185 + * This program is distributed in the hope that it will be useful,
186 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
187 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
188 + * GNU General Public License for more details.
189 + *
190 + * You should have received a copy of the GNU General Public License
191 + * along with this program; if not, write to the Free Software
192 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
193 + */
194 +
195 +#include "channel.h"
196 +#include "check.h"
197 +#include "class.h"
198 +#include "client.h"
199 +#include "hash.h"
200 +#include "IPcheck.h"
201 +#include "ircd.h"
202 +#include "ircd_alloc.h"
203 +#include "ircd_defs.h"
204 +#include "ircd_features.h"
205 +#include "ircd_reply.h"
206 +#include "ircd_string.h"
207 +#include "ircd_snprintf.h"
208 +#include "ircd_osdep.h"
209 +#include "list.h"
210 +#include "listener.h"
211 +#include "match.h"
212 +#include "msg.h"
213 +#include "numeric.h"
214 +#include "numnicks.h"
215 +#include "querycmds.h"
216 +#include "send.h"
217 +#include "s_user.h"
218 +#include "s_debug.h"
219 +#include "s_misc.h"
220 +
221 +#include <string.h>
222 +
223 +#define CHECK_CHECKCHAN 0x01 /* -c */
224 +#define CHECK_SHOWUSERS 0x02 /* ! -u */
225 +#define CHECK_OPSONLY 0x04 /* -o */
226 +#define CHECK_SHOWIPS 0x08 /* -i */
227 +#define CHECK_CIDRMASK 0x10 /* automatically detected when performing a hostmask /CHECK */
228 +#define CHECK_OPLEVELS 0x20 /* -l */
229 +#define CHECK_CLONES 0x40 /* -C */
230 +#define CHECK_SHOWSERVER 0x80 /* -s */
231 +#define CHECK_SHOWHOSTIP 0x100 /* -I */
232 +#define CHECK_SHOWMORE 0x200 /* -e */
233 +
234 +/*
235 + * - ASUKA ---------------------------------------------------------------------
236 + * This is the implimentation of the CHECK function for Asuka.
237 + * Some of this code is from previous QuakeNet ircds, but most of it is mine..
238 + * The old code was written by Durzel (durzel@quakenet.org).
239 + *
240 + * qoreQ (qoreQ@quakenet.org) - 08/14/2002
241 + * -----------------------------------------------------------------------------
242 + */
243 +
244 +/*
245 + * Syntax: CHECK <channel|nick|server|hostmask> [-flags]
246 + *
247 + * Where valid flags are:
248 + * -c: Show channels when checking a hostmask.
249 + * -e: show more inform when checking a mask.
250 + * -i: Show IPs instead of hostnames when displaying results.
251 + * -l: Show oplevels when checking a channel.
252 + * -o: Only show channel operators when checking a channel.
253 + * -s: show server user is on when checking a channel (or on a mask when combined with -e).
254 + * -u: Hide users when checking a channel.
255 + * -C: Perform clone count when checking a channel.
256 + * -I: show hostnames and IPs when checking a channel.
257 + *
258 + * <hostmask> can be of the form host, user@host, nick!user@host,
259 + * with host being host.domain.cc, 127.0.0.1 or 127.0.0.0/24.
260 + * Wildcards are supported.
261 + */
262 +
263 +int m_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) {
264 + struct Channel *chptr;
265 + struct Client *acptr;
266 + int flags = CHECK_SHOWUSERS, i;
267 +
268 + if (parc < 2) {
269 + send_reply(sptr, ERR_NEEDMOREPARAMS, "CHECK");
270 + return 0;
271 + }
272 +
273 + if ( parc>=4 ||
274 + (parc==3 && parv[2][0] != '-')) {
275 + /* remote query */
276 + if (hunt_server_cmd(sptr, CMD_CHECK, cptr, 0, parc==4 ? "%C %s %s" : "%C %s", 1, parc, parv) != HUNTED_ISME)
277 + return 0;
278 + parv++; parc--;
279 + }
280 +
281 + /* This checks to see if any flags have been supplied */
282 + if ((parc >= 3) && (parv[2][0] == '-')) {
283 + for (i = 0; parv[2][i]; i++) {
284 + switch (parv[2][i]) {
285 + case 'c':
286 + flags |= CHECK_CHECKCHAN;
287 + break;
288 +
289 + case 'o':
290 + flags |= CHECK_OPSONLY; /* fall through */
291 + case 'u':
292 + flags &= ~(CHECK_SHOWUSERS);
293 + break;
294 +
295 + case 'i':
296 + flags |= CHECK_SHOWIPS;
297 + break;
298 + case 'l':
299 + flags |= CHECK_OPLEVELS;
300 + break;
301 + case 'C':
302 + flags |= CHECK_CLONES;
303 + break;
304 + case 's':
305 + flags |= CHECK_SHOWSERVER;
306 + break;
307 + case 'I':
308 + flags |= CHECK_SHOWHOSTIP;
309 + break;
310 + case 'e':
311 + flags |= CHECK_SHOWMORE;
312 + break;
313 + default:
314 + /* might want to raise some sort of error here? */
315 + break;
316 + }
317 + }
318 + }
319 +
320 + if (IsChannelName(parv[1])) { /* channel */
321 + if ((chptr = FindChannel(parv[1]))) {
322 + checkChannel(sptr, chptr);
323 + checkUsers(sptr, chptr, flags);
324 + }
325 + else
326 + send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
327 + }
328 + else if ((acptr = FindUser(parv[1]))) {
329 + checkClient(sptr, acptr);
330 + }
331 + else if ((acptr = FindServer(parv[1]))) { /* server */
332 + checkServer(sptr, acptr);
333 + }
334 + else if (checkHostmask(sptr, parv[1], flags) > 0) /* hostmask */
335 + return 1;
336 + else /* no match */
337 + send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
338 +
339 + return 1;
340 +}
341 +
342 +
343 +
344 +/* return number of clients from same IP on the channel */
345 +static int checkClones(struct Channel *chptr, struct Client *cptr) {
346 + int clones = 0, count = 0;
347 + struct Membership *lp;
348 + struct Client *acptr;
349 +
350 + for (lp = chptr->members; lp; lp = lp->next_member) {
351 + acptr = lp->user;
352 + if (are_ips_clones(&cli_ip(cptr),&cli_ip(acptr))) {
353 + clones++;
354 + }
355 + }
356 +
357 + /* Optimise only if we will actually save CPU time */
358 + if (clones >= 2) {
359 + for (lp = chptr->members; lp; lp = lp->next_member) {
360 + acptr = lp->user;
361 + if (are_ips_clones(&cli_ip(cptr),&cli_ip(acptr))) {
362 + cli_marker(acptr) = clones;
363 + count++;
364 + if (clones == count) {
365 + break;
366 + }
367 + }
368 + }
369 + }
370 +
371 + return clones;
372 +}
373 +
374 +
375 +/* compare IPs from clients and return 1 when they are clones
376 + * same IPv4 IP
377 + * IPv4 and IPv6 IPs, but IPv4 over IPv6 etc cases
378 + * IPv6 IPs from the same /64 block
379 + */
380 +int are_ips_clones(const struct irc_in_addr *ip1, const struct irc_in_addr *ip2) {
381 + int ipv4ip1 = has_ipv4_addr(ip1);
382 +
383 + /* are both ip addresses ipv4 or ipv6? if not, no clones */
384 + if (ipv4ip1 != has_ipv4_addr(ip2)) return 0;
385 +
386 + if (ipv4ip1) /* check ipv4 */
387 + return (get_ipv4_addr(ip1) == get_ipv4_addr(ip2)) ? 1 : 0;
388 +
389 + /* check ipv6 */
390 + return ipmask_check(ip1, ip2, IPV6USERBITS) ? 1 : 0;
391 +}
392 +
393 +
394 +void checkUsers(struct Client *sptr, struct Channel *chptr, int flags) {
395 + struct Membership *lp;
396 + struct Ban *ban;
397 + struct Client *acptr;
398 +
399 + char outbuf[BUFSIZE], outbuf2[BUFSIZE], ustat[64];
400 + int cntr = 0, opcntr = 0, vcntr = 0, clones = 0, bans = 0, authed = 0, delayedjoin = 0;
401 + char *zombie, *showlevel;
402 +
403 + if (flags & CHECK_SHOWUSERS) {
404 + send_reply(sptr, RPL_DATASTR, "Users (@ = op, + = voice)");
405 + }
406 +
407 + if (flags & CHECK_CLONES) {
408 + for (lp = chptr->members; lp; lp = lp->next_member) {
409 + cli_marker(lp->user) = 0;
410 + }
411 + }
412 +
413 + for (lp = chptr->members; lp; lp = lp->next_member) {
414 + int opped = 0, c = 0;
415 +
416 + acptr = lp->user;
417 + zombie = IsZombie(lp) ? "!" : "";
418 + showlevel = (flags & CHECK_OPLEVELS) ? " " : "";
419 +
420 + if (flags & CHECK_CLONES) {
421 + if (!cli_marker(acptr)) {
422 + c = checkClones(chptr, acptr);
423 + } else {
424 + c = cli_marker(acptr);
425 + }
426 +
427 + if (c != 1) {
428 + clones++;
429 + }
430 + }
431 +
432 + if (IsChanOp(lp)) {
433 + if (flags & CHECK_OPLEVELS) {
434 + if (c) {
435 + ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%3hu@", c, zombie, OpLevel(lp));
436 + } else {
437 + ircd_snprintf(0, ustat, sizeof(ustat), "%s%3hu@", zombie, OpLevel(lp));
438 + }
439 + } else {
440 + if (c) {
441 + ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s@", c, zombie);
442 + } else {
443 + ircd_snprintf(0, ustat, sizeof(ustat), "%s@", zombie);
444 + }
445 + }
446 + opcntr++;
447 + opped = 1;
448 + }
449 + else if (HasVoice(lp)) {
450 + if (c) {
451 + ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%s+", c, showlevel, zombie);
452 + } else {
453 + ircd_snprintf(0, ustat, sizeof(ustat), "%s%s+", showlevel, zombie);
454 + }
455 + vcntr++;
456 + }
457 + else if (IsDelayedJoin(lp)) {
458 + if (c) {
459 + ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%s<", c, showlevel, zombie);
460 + } else {
461 + ircd_snprintf(0, ustat, sizeof(ustat), "%s%s<", showlevel, zombie);
462 + }
463 + delayedjoin++;
464 + }
465 + else {
466 + if (c) {
467 + ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%s", c, showlevel, zombie);
468 + } else {
469 + ircd_snprintf(0, ustat, sizeof(ustat), " %s%s", showlevel, zombie);
470 + }
471 + }
472 +
473 + if ((c = IsAccount(acptr))) {
474 + authed++;
475 + }
476 +
477 + if ((flags & CHECK_SHOWUSERS) || ((flags & CHECK_OPSONLY) && opped)) {
478 + ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF);
479 + if (flags & CHECK_SHOWHOSTIP) {
480 + ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]", ircd_ntoa(&(cli_ip(acptr))));
481 + }
482 + send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->realusername,
483 + ((flags & CHECK_SHOWIPS) ? ircd_ntoa(&(cli_ip(acptr))) : cli_user(acptr)->realhost), (flags & CHECK_SHOWHOSTIP) ? outbuf2 : "", (flags & CHECK_SHOWSERVER) ? cli_name(cli_user(acptr)->server) : outbuf,
484 + (c ? cli_user(acptr)->account : ""));
485 + }
486 +
487 + cntr++;
488 + }
489 +
490 + send_reply(sptr, RPL_DATASTR, " ");
491 +
492 + if (flags & CHECK_CLONES) {
493 + ircd_snprintf(0, outbuf, sizeof(outbuf),
494 + "Total users:: %d (%d ops, %d voiced, %d clones, %d authed, %d hidden)",
495 + cntr, opcntr, vcntr, clones, authed, delayedjoin);
496 +
497 + for (lp = chptr->members; lp; lp = lp->next_member) {
498 + cli_marker(lp->user) = 0;
499 + }
500 + } else {
501 + ircd_snprintf(0, outbuf, sizeof(outbuf),
502 + "Total users:: %d (%d ops, %d voiced, %d authed, %d hidden)",
503 + cntr, opcntr, vcntr, authed, delayedjoin);
504 + }
505 +
506 + send_reply(sptr, RPL_DATASTR, outbuf);
507 + send_reply(sptr, RPL_DATASTR, " ");
508 +
509 + /* Do not display bans if ! flags & CHECK_SHOWUSERS */
510 + if (flags & CHECK_SHOWUSERS) {
511 + send_reply(sptr, RPL_DATASTR, "Bans on channel::");
512 +
513 + for (ban = chptr->banlist; ban; ban = ban->next) {
514 + ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s - Set by %s, on %s (%Tu)",
515 + ++bans, ban->banstr, ban->who, myctime(ban->when), ban->when);
516 + send_reply(sptr, RPL_DATASTR, outbuf);
517 + }
518 +
519 + if (bans == 0)
520 + send_reply(sptr, RPL_DATASTR, "<none>");
521 + }
522 +
523 + send_reply(sptr, RPL_ENDOFCHECK, " ");
524 +}
525 +
526 +void checkChannel(struct Client *sptr, struct Channel *chptr) {
527 + char outbuf[TOPICLEN + MODEBUFLEN + 64], modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
528 +
529 + /* Header */
530 + send_reply(sptr, RPL_DATASTR, " ");
531 + send_reply(sptr, RPL_CHKHEAD, "channel", chptr->chname);
532 + send_reply(sptr, RPL_DATASTR, " ");
533 +
534 + /* Creation Time */
535 + ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Creation time:: %s (%Tu)", myctime(chptr->creationtime), chptr->creationtime);
536 + send_reply(sptr, RPL_DATASTR, outbuf);
537 +
538 + /* Topic */
539 + if (strlen(chptr->topic) <= 0)
540 + send_reply(sptr, RPL_DATASTR, " Topic:: <none>");
541 + else {
542 + ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic:: %s", chptr->topic);
543 + send_reply(sptr, RPL_DATASTR, outbuf);
544 +
545 + /* ..set by */
546 + ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set by:: %s", chptr->topic_nick);
547 + send_reply(sptr, RPL_DATASTR, outbuf);
548 +
549 + ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set at:: %s (%Tu)", myctime(chptr->topic_time), chptr->topic_time);
550 + send_reply(sptr, RPL_DATASTR, outbuf);
551 + }
552 +
553 + /* Channel Modes */
554 +
555 + strcpy(outbuf, "Channel mode(s):: ");
556 +
557 + modebuf[0] = '\0';
558 + parabuf[0] = '\0';
559 +
560 + channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr, NULL);
561 +
562 + if(modebuf[1] == '\0')
563 + strcat(outbuf, "<none>");
564 + else if(*parabuf) {
565 + strcat(outbuf, modebuf);
566 + strcat(outbuf, " ");
567 + strcat(outbuf, parabuf);
568 + }
569 + else
570 + strcat(outbuf, modebuf);
571 +
572 + send_reply(sptr, RPL_DATASTR, outbuf);
573 +
574 + /* Don't send 'END OF CHECK' message, it's sent in checkUsers, which is called after this. */
575 +}
576 +
577 +void checkClient(struct Client *sptr, struct Client *acptr) {
578 + struct Channel *chptr;
579 + struct Membership *lp;
580 + struct irc_sockaddr sin;
581 + char outbuf[BUFSIZE];
582 + time_t nowr;
583 +
584 + /* Header */
585 + send_reply(sptr, RPL_DATASTR, " ");
586 + send_reply(sptr, RPL_CHKHEAD, "user", cli_name(acptr));
587 + send_reply(sptr, RPL_DATASTR, " ");
588 +
589 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Nick:: %s (%s%s)", cli_name(acptr), NumNick(acptr));
590 + send_reply(sptr, RPL_DATASTR, outbuf);
591 +
592 + if (MyUser(acptr)) {
593 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Signed on:: %s (%Tu)", myctime(acptr->cli_firsttime), acptr->cli_firsttime);
594 + send_reply(sptr, RPL_DATASTR, outbuf);
595 + }
596 +
597 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Timestamp:: %s (%d)", myctime(acptr->cli_lastnick), acptr->cli_lastnick);
598 + send_reply(sptr, RPL_DATASTR, outbuf);
599 +
600 + ircd_snprintf(0, outbuf, sizeof(outbuf), " User/Hostmask:: %s@%s [%s] (Clients: %hu)", cli_user(acptr)->username, cli_user(acptr)->host,
601 + ircd_ntoa(&(cli_ip(acptr))), IPcheck_nr(acptr));
602 + send_reply(sptr, RPL_DATASTR, outbuf);
603 +
604 + if (IsSetHost(acptr) || HasHiddenHost(acptr)) {
605 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Real User/Host:: %s@%s", cli_user(acptr)->realusername, cli_user(acptr)->realhost);
606 + send_reply(sptr, RPL_DATASTR, outbuf);
607 + }
608 +
609 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Name:: %s%c", cli_info(acptr), COLOR_OFF);
610 + send_reply(sptr, RPL_DATASTR, outbuf);
611 +
612 + if( IsService(cli_user(acptr)->server)) {
613 + if (IsChannelService(acptr))
614 + send_reply(sptr, RPL_DATASTR, " Status:: Network Service");
615 + else if (IsAnOper(acptr))
616 + send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator (service) (ID: %s)", cli_user(acptr)->opername ? cli_user(acptr)->opername : "<unknown>");
617 + else
618 + send_reply(sptr, RPL_DATASTR, " Status:: Client (service)");
619 + } else if (IsAnOper(acptr)) {
620 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Status:: IRC Operator (ID: %s)", cli_user(acptr)->opername ? cli_user(acptr)->opername : "<unknown>");
621 + send_reply(sptr, RPL_DATASTR, outbuf);
622 + } else
623 + send_reply(sptr, RPL_DATASTR, " Status:: Client");
624 +
625 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to:: %s (Hops: %d)", cli_name(cli_user(acptr)->server), cli_hopcount(acptr));
626 + send_reply(sptr, RPL_DATASTR, outbuf);
627 +
628 + /* +s (SERV_NOTICE) is not relayed to us from remote servers,
629 + * so we cannot tell if a remote client has that mode set.
630 + * And hacking it onto the end of the output of umode_str is EVIL BAD AND WRONG
631 + * (and breaks if the user is +r) so we won't do that either.
632 + */
633 +
634 + /* show the usermodes and account info (but not OperID and sethost) */
635 + umodes = umode_str(acptr, UMODE_AND_ACCOUNT);
636 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Usermode(s):: %s%s", *umodes ? "+" : "<none>", umodes);
637 + send_reply(sptr, RPL_DATASTR, outbuf);
638 +
639 + if (cli_user(acptr)->joined == 0)
640 + send_reply(sptr, RPL_DATASTR, " Channel(s):: <none>");
641 + else if (cli_user(acptr)->joined > 50) {
642 +
643 + /* NB. As a sanity check, we DO NOT show the individual channels the
644 + * client is on if it is on > 50 channels. This is to prevent the ircd
645 + * barfing ala Uworld when someone does /quote check Q :).. (I shouldn't imagine
646 + * an Oper would want to see every single channel 'x' client is on anyway if
647 + * they are on *that* many).
648 + */
649 +
650 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Channel(s):: - (total: %u)", cli_user(acptr)->joined);
651 + send_reply(sptr, RPL_DATASTR, outbuf);
652 + }
653 + else {
654 + char chntext[BUFSIZE];
655 + int len = strlen(" Channel(s):: ");
656 + int mlen = strlen(me.cli_name) + len + strlen(cli_name(sptr));
657 + *chntext = '\0';
658 +
659 + strcpy(chntext, " Channel(s):: ");
660 + for (lp = cli_user(acptr)->channel; lp; lp = lp->next_channel) {
661 + chptr = lp->channel;
662 + if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
663 + send_reply(sptr, RPL_DATASTR, chntext);
664 + *chntext = '\0';
665 + strcpy(chntext, " Channel(s):: ");
666 + len = strlen(chntext);
667 + }
668 + if (IsDeaf(acptr))
669 + *(chntext + len++) = '-';
670 + if (!PubChannel(chptr))
671 + *(chntext + len++) = '*';
672 + if (IsZombie(lp))
673 + *(chntext + len++) = '!';
674 + if (IsChanOp(lp))
675 + *(chntext + len++) = '@';
676 + else if (HasVoice(lp))
677 + *(chntext + len++) = '+';
678 + else if (IsDelayedJoin(lp))
679 + *(chntext + len++) = '<';
680 + if (len)
681 + *(chntext + len) = '\0';
682 +
683 + strcpy(chntext + len, chptr->chname);
684 + len += strlen(chptr->chname);
685 + strcat(chntext + len, " ");
686 + len++;
687 + }
688 +
689 + if (chntext[0] != '\0')
690 + send_reply(sptr, RPL_DATASTR, chntext);
691 + }
692 +
693 + if (MyUser(acptr)) {
694 + nowr = CurrentTime - cli_user(acptr)->last;
695 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Idle for:: %d days, %02ld:%02ld:%02ld",
696 + nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
697 + send_reply(sptr, RPL_DATASTR, outbuf);
698 + }
699 +
700 + /* Away message (if applicable) */
701 + if (cli_user(acptr)->away) {
702 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Away message:: %s", cli_user(acptr)->away);
703 + send_reply(sptr, RPL_DATASTR, outbuf);
704 + }
705 +
706 + /* If local user.. */
707 + if (MyUser(acptr)) {
708 + os_get_peername(con_fd(cli_connect(sptr)), &sin);
709 +
710 + send_reply(sptr, RPL_DATASTR, " ");
711 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Ports:: %d -> %d (client -> server)",
712 + sin.port, cli_listener(acptr)->addr.port);
713 + send_reply(sptr, RPL_DATASTR, outbuf);
714 + if (feature_bool(FEAT_EXTENDED_CHECKCMD)) {
715 + /* Note: sendq = receiveq for a client (it makes sense really) */
716 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Data sent:: %lu.%0.3u Kb (%u protocol messages)",
717 + (unsigned long)cli_receiveB(acptr) / 1024, (unsigned long)cli_receiveB(acptr) % 1024, cli_receiveM(acptr));
718 + send_reply(sptr, RPL_DATASTR, outbuf);
719 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Data received:: %lu.%0.3lu Kb (%u protocol messages)",
720 + (unsigned long)cli_sendB(acptr) / 1024, (unsigned long)cli_sendB(acptr) % 1024, cli_sendM(acptr));
721 + send_reply(sptr, RPL_DATASTR, outbuf);
722 + ircd_snprintf(0, outbuf, sizeof(outbuf), " receiveQ size:: %d bytes (max. %d bytes)",
723 + DBufLength(&(cli_recvQ(acptr))), feature_int(FEAT_CLIENT_FLOOD));
724 + send_reply(sptr, RPL_DATASTR, outbuf);
725 + ircd_snprintf(0, outbuf, sizeof(outbuf), " sendQ size:: %d bytes (max. %d bytes)",
726 + DBufLength(&(cli_sendQ(acptr))), get_sendq(acptr));
727 + send_reply(sptr, RPL_DATASTR, outbuf);
728 + }
729 + }
730 +
731 + /* Send 'END OF CHECK' message */
732 + send_reply(sptr, RPL_ENDOFCHECK, " ");
733 +}
734 +
735 +void checkServer(struct Client *sptr, struct Client *acptr) {
736 + char outbuf[BUFSIZE];
737 +
738 + /* Header */
739 + send_reply(sptr, RPL_DATASTR, " ");
740 + send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name);
741 + send_reply(sptr, RPL_DATASTR, " ");
742 +
743 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at:: %s (%Tu)", myctime(acptr->cli_serv->timestamp), acptr->cli_serv->timestamp);
744 + send_reply(sptr, RPL_DATASTR, outbuf);
745 +
746 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name:: %s", acptr->cli_name);
747 + send_reply(sptr, RPL_DATASTR, outbuf);
748 +
749 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric:: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx));
750 + send_reply(sptr, RPL_DATASTR, outbuf);
751 +
752 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Users:: %d / %d", (acptr == &me) ? UserStats.local_clients : cli_serv(acptr)->clients,
753 + base64toint(cli_serv(acptr)->nn_capacity));
754 + send_reply(sptr, RPL_DATASTR, outbuf);
755 +
756 + if (IsBurst(acptr))
757 + send_reply(sptr, RPL_DATASTR, " Status:: Bursting");
758 + else if (IsBurstAck(acptr))
759 + send_reply(sptr, RPL_DATASTR, " Status:: Awaiting EOB Ack");
760 + else if (IsService(acptr))
761 + send_reply(sptr, RPL_DATASTR, " Status:: Network Service");
762 + else if (IsHub(acptr))
763 + send_reply(sptr, RPL_DATASTR, " Status:: Network Hub");
764 +
765 + if (feature_bool(FEAT_EXTENDED_CHECKCMD)) {
766 + int dlinkc = 0;
767 + struct DLink* slink = NULL;
768 +
769 + send_reply(sptr, RPL_DATASTR, " ");
770 + send_reply(sptr, RPL_DATASTR, "Downlinks::");
771 + for (slink = cli_serv(acptr)->down; slink; slink = slink->next) {
772 + ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc,
773 + IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" : IsService(slink->value.cptr) ? "=" : IsHub(slink->value.cptr) ? "+" : " ",
774 + cli_name(slink->value.cptr));
775 + send_reply(sptr, RPL_DATASTR, outbuf);
776 + }
777 +
778 + if (!dlinkc)
779 + send_reply(sptr, RPL_DATASTR, "<none>");
780 + }
781 +
782 + /* Send 'END OF CHECK' message */
783 + send_reply(sptr, RPL_ENDOFCHECK, " ");
784 +}
785 +
786 +signed int checkHostmask(struct Client *sptr, char *orighoststr, int flags) {
787 + struct Client *acptr;
788 + struct Channel *chptr;
789 + struct Membership *lp;
790 + int count = 0, found = 0;
791 + char outbuf[BUFSIZE];
792 + char targhost[NICKLEN + USERLEN + HOSTLEN + 3], curhost[NICKLEN + USERLEN + HOSTLEN + 3];
793 + char hoststr[NICKLEN + USERLEN + HOSTLEN + 3];
794 + char nickm[NICKLEN + 1], userm[USERLEN + 1], hostm[HOSTLEN + 1];
795 + char *p = NULL;
796 + char *umodes;
797 + struct irc_in_addr cidr_check;
798 + unsigned char cidr_check_bits;
799 +
800 + ircd_strncpy(hoststr, orighoststr, NICKLEN + USERLEN + HOSTLEN + 3);
801 + strcpy(nickm,"*");
802 + strcpy(userm,"*");
803 + strcpy(hostm,"*");
804 +
805 + if (!strchr(hoststr, '!') && !strchr(hoststr, '@'))
806 + ircd_strncpy(hostm,hoststr,HOSTLEN);
807 + else {
808 + if ((p = strchr(hoststr, '@'))) {
809 + *p++ = '\0';
810 + if (*p) ircd_strncpy(hostm,p, HOSTLEN);
811 + }
812 +
813 + /* Get the nick!user mask */
814 + if ((p = strchr(hoststr, '!'))) {
815 + *p++ = '\0';
816 + if (*p) ircd_strncpy(userm,p,USERLEN);
817 + if (*hoststr) ircd_strncpy(nickm,hoststr,NICKLEN);
818 + }
819 + else if (*hoststr) {
820 + /* Durz: We should only do the following *IF* the hoststr has not already been
821 + * copied into hostm (ie. neither ! or @ specified).. otherwise, when we do
822 + * /quote check *.barrysworld.com - we end up with targhost as: *!*.barryswo@*.barrysworld.com
823 + */
824 + ircd_strncpy(userm,hoststr,USERLEN);
825 + }
826 + }
827 +
828 + if (ipmask_parse(hostm, &cidr_check, &cidr_check_bits) != 0) {
829 + flags |= CHECK_CIDRMASK;
830 + }
831 +
832 + /* Copy formatted string into "targhost" buffer */
833 + ircd_snprintf(0, targhost, sizeof(targhost), "%s!%s@%s", nickm, userm, hostm);
834 +
835 + targhost[sizeof(targhost) - 1] = '\0';
836 +
837 + /* Note: we have to exclude the last client struct as it is not a real client
838 + * structure, and therefore any attempt to access elements in it would cause
839 + * a segfault.
840 + */
841 +
842 + for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
843 + /* Dont process if acptr is a unregistered client, a server or a ping */
844 + if (!IsRegistered(acptr) || IsServer(acptr))
845 + continue;
846 +
847 + if (IsMe(acptr)) /* Always the last acptr record */
848 + break;
849 +
850 + if(count >= 500) { /* sanity stuff */
851 + ircd_snprintf(0, outbuf, sizeof(outbuf), "More than %d results, truncating...", count);
852 + send_reply(sptr, RPL_DATASTR, outbuf);
853 + break;
854 + }
855 +
856 + /* Copy host info into buffer */
857 + curhost[0] = '\0';
858 + ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", cli_name(acptr), cli_user(acptr)->realusername, cli_user(acptr)->realhost);
859 +
860 + if (flags & CHECK_CIDRMASK) {
861 + if (ipmask_check(&cli_ip(acptr), &cidr_check, cidr_check_bits) && !match(nickm, acptr->cli_name)
862 + && (!match(userm, cli_user(acptr)->realusername) || !match(userm, cli_user(acptr)->username)))
863 + found = 1;
864 + }
865 + else {
866 + if(match((const char*)targhost,(const char*)curhost) == 0)
867 + found = 1;
868 + else {
869 + curhost[0] = '\0';
870 + ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, cli_user(acptr)->username, cli_user(acptr)->host);
871 +
872 + if(match((const char*)targhost,(const char*)curhost) == 0)
873 + found = 1;
874 + }
875 + }
876 +
877 + if (found == 1) {
878 + found = 0; /* reset that so it doesn't get crazy go nuts */
879 +
880 + /* Show header if we've found at least 1 record */
881 + if (count == 0) {
882 + /* Output header */
883 + send_reply(sptr, RPL_DATASTR, " ");
884 + send_reply(sptr, RPL_CHKHEAD, "host", targhost);
885 +
886 + send_reply(sptr, RPL_DATASTR, " ");
887 + if (flags & CHECK_SHOWMORE)
888 + ircd_snprintf(0, outbuf, sizeof(outbuf), "No. %s nick user@host [IP] (usermodes) :realname", (flags & CHECK_CLONES) ? "[clients]" : "");
889 + else
890 + ircd_snprintf(0, outbuf, sizeof(outbuf), "%s %-*s%-*s%s", "No.", (NICKLEN + 2), "Nick",
891 + (USERLEN + 2), "User", "Host");
892 + send_reply(sptr, RPL_DATASTR, outbuf);
893 + }
894 +
895 + if (flags & CHECK_SHOWMORE) {
896 + /* show more information */
897 + umodes = umode_str(acptr, UMODE_AND_ACCOUNT_SHORT);
898 + ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d ", (count+1));
899 + if (flags & CHECK_CLONES)
900 + ircd_snprintf(0, outbuf, sizeof(outbuf), "%s[%+3hu] ", outbuf, IPcheck_nr(acptr));
901 + ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%s %s@%s [%s] (%s%s) :%s", outbuf,
902 + acptr->cli_name,
903 + cli_user(acptr)->realusername, cli_user(acptr)->realhost,
904 + ircd_ntoa(&(cli_ip(acptr))),
905 + *umodes ? "+" : "<none>", umodes,
906 + (flags & CHECK_SHOWSERVER) ? cli_name(cli_user(acptr)->server) : cli_info(acptr));
907 + } else {
908 + /* default output */
909 + ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d %-*s%-*s%s", (count+1), (NICKLEN + 2),
910 + acptr->cli_name, (USERLEN + 2), cli_user(acptr)->realusername,
911 + (flags & CHECK_SHOWIPS) ? ircd_ntoa(&(cli_ip(acptr))) : cli_user(acptr)->realhost);
912 + }
913 + send_reply(sptr, RPL_DATASTR, outbuf);
914 +
915 + /* Show channel output (if applicable) - the 50 channel limit sanity check
916 + * is specifically to prevent coredumping when someone lamely tries to /check
917 + * Q or some other channel service...
918 + */
919 + if (flags & CHECK_CHECKCHAN) {
920 + if (cli_user(acptr)->joined > 0 && cli_user(acptr)->joined <= 50) {
921 + char chntext[BUFSIZE];
922 + int len = strlen(" on channels: ");
923 + int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name);
924 + *chntext = '\0';
925 +
926 + strcpy(chntext, " on channels: ");
927 + for (lp = cli_user(acptr)->channel; lp; lp = lp->next_channel) {
928 + chptr = lp->channel;
929 + if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
930 + send_reply(sptr, RPL_DATASTR, chntext);
931 + *chntext = '\0';
932 + strcpy(chntext, " on channels: ");
933 + len = strlen(chntext);
934 + }
935 + if (IsDeaf(acptr))
936 + *(chntext + len++) = '-';
937 + if (!PubChannel(chptr))
938 + *(chntext + len++) = '*';
939 + if (IsZombie(lp))
940 + *(chntext + len++) = '!';
941 + if (IsChanOp(lp))
942 + *(chntext + len++) = '@';
943 + else if (HasVoice(lp))
944 + *(chntext + len++) = '+';
945 + else if (IsDelayedJoin(lp))
946 + *(chntext + len++) = '<';
947 + if (len)
948 + *(chntext + len) = '\0';
949 +
950 + strcpy(chntext + len, chptr->chname);
951 + len += strlen(chptr->chname);
952 + strcat(chntext + len, " ");
953 + len++;
954 + }
955 + if (chntext[0] != '\0')
956 + send_reply(sptr, RPL_DATASTR, chntext);
957 +
958 + send_reply(sptr, RPL_DATASTR, " ");
959 + }
960 + }
961 + count++;
962 + }
963 + }
964 +
965 + if (count > 0) {
966 + send_reply(sptr, RPL_DATASTR, " ");
967 +
968 + ircd_snprintf(0, outbuf, sizeof(outbuf), "Matching records found:: %d", count);
969 + send_reply(sptr, RPL_DATASTR, outbuf);
970 +
971 + send_reply(sptr, RPL_ENDOFCHECK, " ");
972 + }
973 +
974 + return count;
975 +}
976 diff -r 4e198b121c28 ircd/parse.c
977 --- a/ircd/parse.c Thu Jul 25 22:43:11 2013 +0100
978 +++ b/ircd/parse.c Fri Jul 26 19:55:24 2013 +0100
979 @@ -654,6 +654,23 @@
980 { m_cap, m_cap, m_ignore, m_cap, m_ignore }
981 },
982 #endif
983 +
984 + /*
985 + * - ASUKA ---------------------------------------------------------------------
986 + * Add the command for CHECK.
987 + * This was adapted from Lain for use in Asuka.
988 + * Original code by Durzel (durzel@quakenet.org).
989 + *
990 + * qoreQ (qoreQ@quakenet.org) - 08/14/2002
991 + * -----------------------------------------------------------------------------
992 + */
993 + {
994 + MSG_CHECK,
995 + TOK_CHECK,
996 + 0, MAXPARA, MFLG_SLOW, 0, NULL,
997 + { m_unregistered, m_not_oper, m_check, m_check, m_ignore }
998 + },
999 +
1000 /* This command is an alias for QUIT during the unregistered part of
1001 * of the server. This is because someone jumping via a broken web
1002 * proxy will send a 'POST' as their first command - which we will
1003 diff -r 4e198b121c28 ircd/s_err.c
1004 --- a/ircd/s_err.c Thu Jul 25 22:43:11 2013 +0100
1005 +++ b/ircd/s_err.c Fri Jul 26 19:55:24 2013 +0100
1006 @@ -602,19 +602,19 @@
1007 /* 284 */
1008 { RPL_FEATURE, 0, "284" },
1009 /* 285 */
1010 - { 0 },
1011 + { RPL_NEWHOSTIS, "%s: %s host %s - [%s@%s]" },
1012 /* 286 */
1013 - { 0 },
1014 + { RPL_CHKHEAD, ":Information for %s %s", "286" },
1015 /* 287 */
1016 - { 0 },
1017 + { RPL_CHANUSER, ": %s%s (%s@%s) %s (%s) %s", "287" },
1018 /* 288 */
1019 { 0 },
1020 /* 289 */
1021 { 0 },
1022 /* 290 */
1023 - { 0 },
1024 + { RPL_DATASTR, ":%s", "290" },
1025 /* 291 */
1026 - { 0 },
1027 + { RPL_ENDOFCHECK, ":%s", "291" },
1028 /* 292 */
1029 { 0 },
1030 /* 293 */
1031 @@ -848,7 +848,7 @@
1032 /* 407 */
1033 { ERR_TOOMANYTARGETS, "%s :Duplicate recipients. No message delivered", "407" },
1034 /* 408 */
1035 - { 0 },
1036 + { ERR_SEARCHNOMATCH, ":%s %s No matching record(s) found", "408" },
1037 /* 409 */
1038 { ERR_NOORIGIN, ":No origin specified", "409" },
1039 /* 410 */
1040 diff -r 4e198b121c28 ircd/s_user.c
1041 --- a/ircd/s_user.c Thu Jul 25 22:43:11 2013 +0100
1042 +++ b/ircd/s_user.c Fri Jul 26 19:55:24 2013 +0100
1043 @@ -1507,7 +1507,8 @@
1044 m--; /* Step back over the '\0' */
1045 }
1046
1047 - if (IsSetHost(cptr)) {
1048 + /* sethost parameter is wanted */
1049 + if (type != UMODE_AND_ACCOUNT && IsSetHost(cptr)) {
1050 *m++ = ' ';
1051 ircd_snprintf(0, m, USERLEN + HOSTLEN + 2, "%s@%s", cli_user(cptr)->username,
1052 cli_user(cptr)->host);