]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - check
nickgline: include nick! bit in gline loggin
[irc/quakenet/snircd-patchqueue.git] / check
CommitLineData
edb26b39
P
1# HG changeset patch
2# Parent 3303f23758b0f0bc35a9d786a027aba155488e62
3
4diff -r 3303f23758b0 include/check.h
5--- /dev/null Thu Jan 01 00:00:00 1970 +0000
6+++ b/include/check.h Wed Jul 17 21:38:16 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 */
57diff -r 3303f23758b0 include/client.h
58--- a/include/client.h Wed Jul 17 21:30:44 2013 +0100
59+++ b/include/client.h Wed Jul 17 21:38:16 2013 +0100
60@@ -774,6 +774,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);
70diff -r 3303f23758b0 include/handlers.h
71--- a/include/handlers.h Wed Jul 17 21:30:44 2013 +0100
72+++ b/include/handlers.h Wed Jul 17 21:38:16 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*[]);
93diff -r 3303f23758b0 include/ircd_features.h
94--- a/include/ircd_features.h Wed Jul 17 21:30:44 2013 +0100
95+++ b/include/ircd_features.h Wed Jul 17 21:38:16 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,
104diff -r 3303f23758b0 include/msg.h
105--- a/include/msg.h Wed Jul 17 21:30:44 2013 +0100
106+++ b/include/msg.h Wed Jul 17 21:38:16 2013 +0100
107@@ -260,6 +260,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
118diff -r 3303f23758b0 include/s_user.h
119--- a/include/s_user.h Wed Jul 17 21:30:44 2013 +0100
120+++ b/include/s_user.h Wed Jul 17 21:38:16 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.
134diff -r 3303f23758b0 ircd/IPcheck.c
135--- a/ircd/IPcheck.c Wed Jul 17 21:30:44 2013 +0100
136+++ b/ircd/IPcheck.c Wed Jul 17 21:38:16 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 }
146diff -r 3303f23758b0 ircd/Makefile.in
147--- a/ircd/Makefile.in Wed Jul 17 21:30:44 2013 +0100
148+++ b/ircd/Makefile.in Wed Jul 17 21:38:16 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 \
157diff -r 3303f23758b0 ircd/ircd_features.c
158--- a/ircd/ircd_features.c Wed Jul 17 21:30:44 2013 +0100
159+++ b/ircd/ircd_features.c Wed Jul 17 21:38:16 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),
168diff -r 3303f23758b0 ircd/m_check.c
169--- /dev/null Thu Jan 01 00:00:00 1970 +0000
170+++ b/ircd/m_check.c Wed Jul 17 21:38:16 2013 +0100
171@@ -0,0 +1,809 @@
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 = FindClient(parv[1])) && !(FindServer(parv[1]))) { /* client and not a server */
329+ if (!IsRegistered(acptr)) {
330+ send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
331+ return 0;
332+ }
333+
334+ checkClient(sptr, acptr);
335+ }
336+ else if ((acptr = FindServer(parv[1]))) { /* server */
337+ checkServer(sptr, acptr);
338+ }
339+ else if (checkHostmask(sptr, parv[1], flags) > 0) /* hostmask */
340+ return 1;
341+ else /* no match */
342+ send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
343+
344+ return 1;
345+}
346+
347+
348+
349+/* return number of clients from same IP on the channel */
350+static int checkClones(struct Channel *chptr, struct Client *cptr) {
351+ int clones = 0, count = 0;
352+ struct Membership *lp;
353+ struct Client *acptr;
354+
355+ for (lp = chptr->members; lp; lp = lp->next_member) {
356+ acptr = lp->user;
357+ if (are_ips_clones(&cli_ip(cptr),&cli_ip(acptr))) {
358+ clones++;
359+ }
360+ }
361+
362+ /* Optimise only if we will actually save CPU time */
363+ if (clones >= 2) {
364+ for (lp = chptr->members; lp; lp = lp->next_member) {
365+ acptr = lp->user;
366+ if (are_ips_clones(&cli_ip(cptr),&cli_ip(acptr))) {
367+ cli_marker(acptr) = clones;
368+ count++;
369+ if (clones == count) {
370+ break;
371+ }
372+ }
373+ }
374+ }
375+
376+ return clones;
377+}
378+
379+
380+/* compare IPs from clients and return 1 when they are clones
381+ * same IPv4 IP
382+ * IPv4 and IPv6 IPs, but IPv4 over IPv6 etc cases
383+ * IPv6 IPs from the same /64 block
384+ */
385+int are_ips_clones(const struct irc_in_addr *ip1, const struct irc_in_addr *ip2) {
386+ int ipv4ip1 = has_ipv4_addr(ip1);
387+
388+ /* are both ip addresses ipv4 or ipv6? if not, no clones */
389+ if (ipv4ip1 != has_ipv4_addr(ip2)) return 0;
390+
391+ if (ipv4ip1) /* check ipv4 */
392+ return (get_ipv4_addr(ip1) == get_ipv4_addr(ip2)) ? 1 : 0;
393+
394+ /* check ipv6 */
395+ return ipmask_check(ip1, ip2, IPV6USERBITS) ? 1 : 0;
396+}
397+
398+
399+void checkUsers(struct Client *sptr, struct Channel *chptr, int flags) {
400+ struct Membership *lp;
401+ struct Ban *ban;
402+ struct Client *acptr;
403+
404+ char outbuf[BUFSIZE], outbuf2[BUFSIZE], ustat[64];
405+ int cntr = 0, opcntr = 0, vcntr = 0, clones = 0, bans = 0, authed = 0, delayedjoin = 0;
406+ char *zombie, *showlevel;
407+
408+ if (flags & CHECK_SHOWUSERS) {
409+ send_reply(sptr, RPL_DATASTR, "Users (@ = op, + = voice)");
410+ }
411+
412+ if (flags & CHECK_CLONES) {
413+ for (lp = chptr->members; lp; lp = lp->next_member) {
414+ cli_marker(lp->user) = 0;
415+ }
416+ }
417+
418+ for (lp = chptr->members; lp; lp = lp->next_member) {
419+ int opped = 0, c = 0;
420+
421+ acptr = lp->user;
422+ zombie = IsZombie(lp) ? "!" : "";
423+ showlevel = (flags & CHECK_OPLEVELS) ? " " : "";
424+
425+ if (flags & CHECK_CLONES) {
426+ if (!cli_marker(acptr)) {
427+ c = checkClones(chptr, acptr);
428+ } else {
429+ c = cli_marker(acptr);
430+ }
431+
432+ if (c != 1) {
433+ clones++;
434+ }
435+ }
436+
437+ if (IsChanOp(lp)) {
438+ if (flags & CHECK_OPLEVELS) {
439+ if (c) {
440+ ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%3hu@", c, zombie, OpLevel(lp));
441+ } else {
442+ ircd_snprintf(0, ustat, sizeof(ustat), "%s%3hu@", zombie, OpLevel(lp));
443+ }
444+ } else {
445+ if (c) {
446+ ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s@", c, zombie);
447+ } else {
448+ ircd_snprintf(0, ustat, sizeof(ustat), "%s@", zombie);
449+ }
450+ }
451+ opcntr++;
452+ opped = 1;
453+ }
454+ else if (HasVoice(lp)) {
455+ if (c) {
456+ ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%s+", c, showlevel, zombie);
457+ } else {
458+ ircd_snprintf(0, ustat, sizeof(ustat), "%s%s+", showlevel, zombie);
459+ }
460+ vcntr++;
461+ }
462+ else if (IsDelayedJoin(lp)) {
463+ if (c) {
464+ ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%s<", c, showlevel, zombie);
465+ } else {
466+ ircd_snprintf(0, ustat, sizeof(ustat), "%s%s<", showlevel, zombie);
467+ }
468+ delayedjoin++;
469+ }
470+ else {
471+ if (c) {
472+ ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s%s", c, showlevel, zombie);
473+ } else {
474+ ircd_snprintf(0, ustat, sizeof(ustat), " %s%s", showlevel, zombie);
475+ }
476+ }
477+
478+ if ((c = IsAccount(acptr))) {
479+ authed++;
480+ }
481+
482+ if ((flags & CHECK_SHOWUSERS) || ((flags & CHECK_OPSONLY) && opped)) {
483+ ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF);
484+ if (flags & CHECK_SHOWHOSTIP) {
485+ ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]", ircd_ntoa(&(cli_ip(acptr))));
486+ }
487+ send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->realusername,
488+ ((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,
489+ (c ? cli_user(acptr)->account : ""));
490+ }
491+
492+ cntr++;
493+ }
494+
495+ send_reply(sptr, RPL_DATASTR, " ");
496+
497+ if (flags & CHECK_CLONES) {
498+ ircd_snprintf(0, outbuf, sizeof(outbuf),
499+ "Total users:: %d (%d ops, %d voiced, %d clones, %d authed, %d hidden)",
500+ cntr, opcntr, vcntr, clones, authed, delayedjoin);
501+
502+ for (lp = chptr->members; lp; lp = lp->next_member) {
503+ cli_marker(lp->user) = 0;
504+ }
505+ } else {
506+ ircd_snprintf(0, outbuf, sizeof(outbuf),
507+ "Total users:: %d (%d ops, %d voiced, %d authed, %d hidden)",
508+ cntr, opcntr, vcntr, authed, delayedjoin);
509+ }
510+
511+ send_reply(sptr, RPL_DATASTR, outbuf);
512+ send_reply(sptr, RPL_DATASTR, " ");
513+
514+ /* Do not display bans if ! flags & CHECK_SHOWUSERS */
515+ if (flags & CHECK_SHOWUSERS) {
516+ send_reply(sptr, RPL_DATASTR, "Bans on channel::");
517+
518+ for (ban = chptr->banlist; ban; ban = ban->next) {
519+ ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s - Set by %s, on %s (%Tu)",
520+ ++bans, ban->banstr, ban->who, myctime(ban->when), ban->when);
521+ send_reply(sptr, RPL_DATASTR, outbuf);
522+ }
523+
524+ if (bans == 0)
525+ send_reply(sptr, RPL_DATASTR, "<none>");
526+ }
527+
528+ send_reply(sptr, RPL_ENDOFCHECK, " ");
529+}
530+
531+void checkChannel(struct Client *sptr, struct Channel *chptr) {
532+ char outbuf[TOPICLEN + MODEBUFLEN + 64], modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
533+
534+ /* Header */
535+ send_reply(sptr, RPL_DATASTR, " ");
536+ send_reply(sptr, RPL_CHKHEAD, "channel", chptr->chname);
537+ send_reply(sptr, RPL_DATASTR, " ");
538+
539+ /* Creation Time */
540+ ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Creation time:: %s (%Tu)", myctime(chptr->creationtime), chptr->creationtime);
541+ send_reply(sptr, RPL_DATASTR, outbuf);
542+
543+ /* Topic */
544+ if (strlen(chptr->topic) <= 0)
545+ send_reply(sptr, RPL_DATASTR, " Topic:: <none>");
546+ else {
547+ ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic:: %s", chptr->topic);
548+ send_reply(sptr, RPL_DATASTR, outbuf);
549+
550+ /* ..set by */
551+ ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set by:: %s", chptr->topic_nick);
552+ send_reply(sptr, RPL_DATASTR, outbuf);
553+
554+ ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set at:: %s (%Tu)", myctime(chptr->topic_time), chptr->topic_time);
555+ send_reply(sptr, RPL_DATASTR, outbuf);
556+ }
557+
558+ /* Channel Modes */
559+
560+ strcpy(outbuf, "Channel mode(s):: ");
561+
562+ modebuf[0] = '\0';
563+ parabuf[0] = '\0';
564+
565+ channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr, NULL);
566+
567+ if(modebuf[1] == '\0')
568+ strcat(outbuf, "<none>");
569+ else if(*parabuf) {
570+ strcat(outbuf, modebuf);
571+ strcat(outbuf, " ");
572+ strcat(outbuf, parabuf);
573+ }
574+ else
575+ strcat(outbuf, modebuf);
576+
577+ send_reply(sptr, RPL_DATASTR, outbuf);
578+
579+ /* Don't send 'END OF CHECK' message, it's sent in checkUsers, which is called after this. */
580+}
581+
582+void checkClient(struct Client *sptr, struct Client *acptr) {
583+ struct Channel *chptr;
584+ struct Membership *lp;
585+ struct irc_sockaddr sin;
586+ char outbuf[BUFSIZE];
587+ time_t nowr;
588+
589+ /* Header */
590+ send_reply(sptr, RPL_DATASTR, " ");
591+ send_reply(sptr, RPL_CHKHEAD, "user", cli_name(acptr));
592+ send_reply(sptr, RPL_DATASTR, " ");
593+
594+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Nick:: %s (%s%s)", cli_name(acptr), NumNick(acptr));
595+ send_reply(sptr, RPL_DATASTR, outbuf);
596+
597+ if (MyUser(acptr)) {
598+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Signed on:: %s (%Tu)", myctime(acptr->cli_firsttime), acptr->cli_firsttime);
599+ send_reply(sptr, RPL_DATASTR, outbuf);
600+ }
601+
602+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Timestamp:: %s (%d)", myctime(acptr->cli_lastnick), acptr->cli_lastnick);
603+ send_reply(sptr, RPL_DATASTR, outbuf);
604+
605+ ircd_snprintf(0, outbuf, sizeof(outbuf), " User/Hostmask:: %s@%s [%s] (Clients: %hu)", cli_user(acptr)->username, cli_user(acptr)->host,
606+ ircd_ntoa(&(cli_ip(acptr))), IPcheck_nr(acptr));
607+ send_reply(sptr, RPL_DATASTR, outbuf);
608+
609+ if (IsSetHost(acptr) || HasHiddenHost(acptr)) {
610+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Real User/Host:: %s@%s", cli_user(acptr)->realusername, cli_user(acptr)->realhost);
611+ send_reply(sptr, RPL_DATASTR, outbuf);
612+ }
613+
614+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Name:: %s%c", cli_info(acptr), COLOR_OFF);
615+ send_reply(sptr, RPL_DATASTR, outbuf);
616+
617+ if( IsService(cli_user(acptr)->server)) {
618+ if (IsChannelService(acptr))
619+ send_reply(sptr, RPL_DATASTR, " Status:: Network Service");
620+ else if (IsAnOper(acptr))
621+ send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator (service) (ID: %s)", cli_user(acptr)->opername ? cli_user(acptr)->opername : "<unknown>");
622+ else
623+ send_reply(sptr, RPL_DATASTR, " Status:: Client (service)");
624+ } else if (IsAnOper(acptr)) {
625+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Status:: IRC Operator (ID: %s)", cli_user(acptr)->opername ? cli_user(acptr)->opername : "<unknown>");
626+ send_reply(sptr, RPL_DATASTR, outbuf);
627+ } else
628+ send_reply(sptr, RPL_DATASTR, " Status:: Client");
629+
630+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to:: %s (Hops: %d)", cli_name(cli_user(acptr)->server), cli_hopcount(acptr));
631+ send_reply(sptr, RPL_DATASTR, outbuf);
632+
633+ /* +s (SERV_NOTICE) is not relayed to us from remote servers,
634+ * so we cannot tell if a remote client has that mode set.
635+ * And hacking it onto the end of the output of umode_str is EVIL BAD AND WRONG
636+ * (and breaks if the user is +r) so we won't do that either.
637+ */
638+
639+ /* show the usermodes and account info (but not OperID and sethost) */
640+ umodes = umode_str(acptr, UMODE_AND_ACCOUNT);
641+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Usermode(s):: %s%s", *umodes ? "+" : "<none>", umodes);
642+ send_reply(sptr, RPL_DATASTR, outbuf);
643+
644+ if (cli_user(acptr)->joined == 0)
645+ send_reply(sptr, RPL_DATASTR, " Channel(s):: <none>");
646+ else if (cli_user(acptr)->joined > 50) {
647+
648+ /* NB. As a sanity check, we DO NOT show the individual channels the
649+ * client is on if it is on > 50 channels. This is to prevent the ircd
650+ * barfing ala Uworld when someone does /quote check Q :).. (I shouldn't imagine
651+ * an Oper would want to see every single channel 'x' client is on anyway if
652+ * they are on *that* many).
653+ */
654+
655+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Channel(s):: - (total: %u)", cli_user(acptr)->joined);
656+ send_reply(sptr, RPL_DATASTR, outbuf);
657+ }
658+ else {
659+ char chntext[BUFSIZE];
660+ int len = strlen(" Channel(s):: ");
661+ int mlen = strlen(me.cli_name) + len + strlen(cli_name(sptr));
662+ *chntext = '\0';
663+
664+ strcpy(chntext, " Channel(s):: ");
665+ for (lp = cli_user(acptr)->channel; lp; lp = lp->next_channel) {
666+ chptr = lp->channel;
667+ if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
668+ send_reply(sptr, RPL_DATASTR, chntext);
669+ *chntext = '\0';
670+ strcpy(chntext, " Channel(s):: ");
671+ len = strlen(chntext);
672+ }
673+ if (IsDeaf(acptr))
674+ *(chntext + len++) = '-';
675+ if (!PubChannel(chptr))
676+ *(chntext + len++) = '*';
677+ if (IsZombie(lp))
678+ *(chntext + len++) = '!';
679+ if (IsChanOp(lp))
680+ *(chntext + len++) = '@';
681+ else if (HasVoice(lp))
682+ *(chntext + len++) = '+';
683+ else if (IsDelayedJoin(lp))
684+ *(chntext + len++) = '<';
685+ if (len)
686+ *(chntext + len) = '\0';
687+
688+ strcpy(chntext + len, chptr->chname);
689+ len += strlen(chptr->chname);
690+ strcat(chntext + len, " ");
691+ len++;
692+ }
693+
694+ if (chntext[0] != '\0')
695+ send_reply(sptr, RPL_DATASTR, chntext);
696+ }
697+
698+ if (MyUser(acptr)) {
699+ nowr = CurrentTime - cli_user(acptr)->last;
700+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Idle for:: %d days, %02ld:%02ld:%02ld",
701+ nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
702+ send_reply(sptr, RPL_DATASTR, outbuf);
703+ }
704+
705+ /* Away message (if applicable) */
706+ if (cli_user(acptr)->away) {
707+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Away message:: %s", cli_user(acptr)->away);
708+ send_reply(sptr, RPL_DATASTR, outbuf);
709+ }
710+
711+ /* If local user.. */
712+ if (MyUser(acptr)) {
713+ os_get_peername(con_fd(cli_connect(sptr)), &sin);
714+
715+ send_reply(sptr, RPL_DATASTR, " ");
716+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Ports:: %d -> %d (client -> server)",
717+ sin.port, cli_listener(acptr)->addr.port);
718+ send_reply(sptr, RPL_DATASTR, outbuf);
719+ if (feature_bool(FEAT_EXTENDED_CHECKCMD)) {
720+ /* Note: sendq = receiveq for a client (it makes sense really) */
721+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Data sent:: %lu.%0.3u Kb (%u protocol messages)",
722+ (unsigned long)cli_receiveB(acptr) / 1024, (unsigned long)cli_receiveB(acptr) % 1024, cli_receiveM(acptr));
723+ send_reply(sptr, RPL_DATASTR, outbuf);
724+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Data received:: %lu.%0.3lu Kb (%u protocol messages)",
725+ (unsigned long)cli_sendB(acptr) / 1024, (unsigned long)cli_sendB(acptr) % 1024, cli_sendM(acptr));
726+ send_reply(sptr, RPL_DATASTR, outbuf);
727+ ircd_snprintf(0, outbuf, sizeof(outbuf), " receiveQ size:: %d bytes (max. %d bytes)",
728+ DBufLength(&(cli_recvQ(acptr))), feature_int(FEAT_CLIENT_FLOOD));
729+ send_reply(sptr, RPL_DATASTR, outbuf);
730+ ircd_snprintf(0, outbuf, sizeof(outbuf), " sendQ size:: %d bytes (max. %d bytes)",
731+ DBufLength(&(cli_sendQ(acptr))), get_sendq(acptr));
732+ send_reply(sptr, RPL_DATASTR, outbuf);
733+ }
734+ }
735+
736+ /* Send 'END OF CHECK' message */
737+ send_reply(sptr, RPL_ENDOFCHECK, " ");
738+}
739+
740+void checkServer(struct Client *sptr, struct Client *acptr) {
741+ char outbuf[BUFSIZE];
742+
743+ /* Header */
744+ send_reply(sptr, RPL_DATASTR, " ");
745+ send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name);
746+ send_reply(sptr, RPL_DATASTR, " ");
747+
748+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at:: %s (%Tu)", myctime(acptr->cli_serv->timestamp), acptr->cli_serv->timestamp);
749+ send_reply(sptr, RPL_DATASTR, outbuf);
750+
751+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name:: %s", acptr->cli_name);
752+ send_reply(sptr, RPL_DATASTR, outbuf);
753+
754+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric:: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx));
755+ send_reply(sptr, RPL_DATASTR, outbuf);
756+
757+ ircd_snprintf(0, outbuf, sizeof(outbuf), " Users:: %d / %d", (acptr == &me) ? UserStats.local_clients : cli_serv(acptr)->clients,
758+ base64toint(cli_serv(acptr)->nn_capacity));
759+ send_reply(sptr, RPL_DATASTR, outbuf);
760+
761+ if (IsBurst(acptr))
762+ send_reply(sptr, RPL_DATASTR, " Status:: Bursting");
763+ else if (IsBurstAck(acptr))
764+ send_reply(sptr, RPL_DATASTR, " Status:: Awaiting EOB Ack");
765+ else if (IsService(acptr))
766+ send_reply(sptr, RPL_DATASTR, " Status:: Network Service");
767+ else if (IsHub(acptr))
768+ send_reply(sptr, RPL_DATASTR, " Status:: Network Hub");
769+
770+ if (feature_bool(FEAT_EXTENDED_CHECKCMD)) {
771+ int dlinkc = 0;
772+ struct DLink* slink = NULL;
773+
774+ send_reply(sptr, RPL_DATASTR, " ");
775+ send_reply(sptr, RPL_DATASTR, "Downlinks::");
776+ for (slink = cli_serv(acptr)->down; slink; slink = slink->next) {
777+ ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc,
778+ IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" : IsService(slink->value.cptr) ? "=" : IsHub(slink->value.cptr) ? "+" : " ",
779+ cli_name(slink->value.cptr));
780+ send_reply(sptr, RPL_DATASTR, outbuf);
781+ }
782+
783+ if (!dlinkc)
784+ send_reply(sptr, RPL_DATASTR, "<none>");
785+ }
786+
787+ /* Send 'END OF CHECK' message */
788+ send_reply(sptr, RPL_ENDOFCHECK, " ");
789+}
790+
791+signed int checkHostmask(struct Client *sptr, char *orighoststr, int flags) {
792+ struct Client *acptr;
793+ struct Channel *chptr;
794+ struct Membership *lp;
795+ int count = 0, found = 0;
796+ char outbuf[BUFSIZE];
797+ char targhost[NICKLEN + USERLEN + HOSTLEN + 3], curhost[NICKLEN + USERLEN + HOSTLEN + 3];
798+ char hoststr[NICKLEN + USERLEN + HOSTLEN + 3];
799+ char nickm[NICKLEN + 1], userm[USERLEN + 1], hostm[HOSTLEN + 1];
800+ char *p = NULL;
801+ char *umodes;
802+ struct irc_in_addr cidr_check;
803+ unsigned char cidr_check_bits;
804+
805+ ircd_strncpy(hoststr, orighoststr, NICKLEN + USERLEN + HOSTLEN + 3);
806+ strcpy(nickm,"*");
807+ strcpy(userm,"*");
808+ strcpy(hostm,"*");
809+
810+ if (!strchr(hoststr, '!') && !strchr(hoststr, '@'))
811+ ircd_strncpy(hostm,hoststr,HOSTLEN);
812+ else {
813+ if ((p = strchr(hoststr, '@'))) {
814+ *p++ = '\0';
815+ if (*p) ircd_strncpy(hostm,p, HOSTLEN);
816+ }
817+
818+ /* Get the nick!user mask */
819+ if ((p = strchr(hoststr, '!'))) {
820+ *p++ = '\0';
821+ if (*p) ircd_strncpy(userm,p,USERLEN);
822+ if (*hoststr) ircd_strncpy(nickm,hoststr,NICKLEN);
823+ }
824+ else if (*hoststr) {
825+ /* Durz: We should only do the following *IF* the hoststr has not already been
826+ * copied into hostm (ie. neither ! or @ specified).. otherwise, when we do
827+ * /quote check *.barrysworld.com - we end up with targhost as: *!*.barryswo@*.barrysworld.com
828+ */
829+ ircd_strncpy(userm,hoststr,USERLEN);
830+ }
831+ }
832+
833+ if (ipmask_parse(hostm, &cidr_check, &cidr_check_bits) != 0) {
834+ flags |= CHECK_CIDRMASK;
835+ }
836+
837+ /* Copy formatted string into "targhost" buffer */
838+ ircd_snprintf(0, targhost, sizeof(targhost), "%s!%s@%s", nickm, userm, hostm);
839+
840+ targhost[sizeof(targhost) - 1] = '\0';
841+
842+ /* Note: we have to exclude the last client struct as it is not a real client
843+ * structure, and therefore any attempt to access elements in it would cause
844+ * a segfault.
845+ */
846+
847+ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
848+ /* Dont process if acptr is a unregistered client, a server or a ping */
849+ if (!IsRegistered(acptr) || IsServer(acptr))
850+ continue;
851+
852+ if (IsMe(acptr)) /* Always the last acptr record */
853+ break;
854+
855+ if(count >= 500) { /* sanity stuff */
856+ ircd_snprintf(0, outbuf, sizeof(outbuf), "More than %d results, truncating...", count);
857+ send_reply(sptr, RPL_DATASTR, outbuf);
858+ break;
859+ }
860+
861+ /* Copy host info into buffer */
862+ curhost[0] = '\0';
863+ ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", cli_name(acptr), cli_user(acptr)->realusername, cli_user(acptr)->realhost);
864+
865+ if (flags & CHECK_CIDRMASK) {
866+ if (ipmask_check(&cli_ip(acptr), &cidr_check, cidr_check_bits) && !match(nickm, acptr->cli_name)
867+ && (!match(userm, cli_user(acptr)->realusername) || !match(userm, cli_user(acptr)->username)))
868+ found = 1;
869+ }
870+ else {
871+ if(match((const char*)targhost,(const char*)curhost) == 0)
872+ found = 1;
873+ else {
874+ curhost[0] = '\0';
875+ ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, cli_user(acptr)->username, cli_user(acptr)->host);
876+
877+ if(match((const char*)targhost,(const char*)curhost) == 0)
878+ found = 1;
879+ }
880+ }
881+
882+ if (found == 1) {
883+ found = 0; /* reset that so it doesn't get crazy go nuts */
884+
885+ /* Show header if we've found at least 1 record */
886+ if (count == 0) {
887+ /* Output header */
888+ send_reply(sptr, RPL_DATASTR, " ");
889+ send_reply(sptr, RPL_CHKHEAD, "host", targhost);
890+
891+ send_reply(sptr, RPL_DATASTR, " ");
892+ if (flags & CHECK_SHOWMORE)
893+ ircd_snprintf(0, outbuf, sizeof(outbuf), "No. %s nick user@host [IP] (usermodes) :realname", (flags & CHECK_CLONES) ? "[clients]" : "");
894+ else
895+ ircd_snprintf(0, outbuf, sizeof(outbuf), "%s %-*s%-*s%s", "No.", (NICKLEN + 2), "Nick",
896+ (USERLEN + 2), "User", "Host");
897+ send_reply(sptr, RPL_DATASTR, outbuf);
898+ }
899+
900+ if (flags & CHECK_SHOWMORE) {
901+ /* show more information */
902+ umodes = umode_str(acptr, UMODE_AND_ACCOUNT_SHORT);
903+ ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d ", (count+1));
904+ if (flags & CHECK_CLONES)
905+ ircd_snprintf(0, outbuf, sizeof(outbuf), "%s[%+3hu] ", outbuf, IPcheck_nr(acptr));
906+ ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%s %s@%s [%s] (%s%s) :%s", outbuf,
907+ acptr->cli_name,
908+ cli_user(acptr)->realusername, cli_user(acptr)->realhost,
909+ ircd_ntoa(&(cli_ip(acptr))),
910+ *umodes ? "+" : "<none>", umodes,
911+ (flags & CHECK_SHOWSERVER) ? cli_name(cli_user(acptr)->server) : cli_info(acptr));
912+ } else {
913+ /* default output */
914+ ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d %-*s%-*s%s", (count+1), (NICKLEN + 2),
915+ acptr->cli_name, (USERLEN + 2), cli_user(acptr)->realusername,
916+ (flags & CHECK_SHOWIPS) ? ircd_ntoa(&(cli_ip(acptr))) : cli_user(acptr)->realhost);
917+ }
918+ send_reply(sptr, RPL_DATASTR, outbuf);
919+
920+ /* Show channel output (if applicable) - the 50 channel limit sanity check
921+ * is specifically to prevent coredumping when someone lamely tries to /check
922+ * Q or some other channel service...
923+ */
924+ if (flags & CHECK_CHECKCHAN) {
925+ if (cli_user(acptr)->joined > 0 && cli_user(acptr)->joined <= 50) {
926+ char chntext[BUFSIZE];
927+ int len = strlen(" on channels: ");
928+ int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name);
929+ *chntext = '\0';
930+
931+ strcpy(chntext, " on channels: ");
932+ for (lp = cli_user(acptr)->channel; lp; lp = lp->next_channel) {
933+ chptr = lp->channel;
934+ if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
935+ send_reply(sptr, RPL_DATASTR, chntext);
936+ *chntext = '\0';
937+ strcpy(chntext, " on channels: ");
938+ len = strlen(chntext);
939+ }
940+ if (IsDeaf(acptr))
941+ *(chntext + len++) = '-';
942+ if (!PubChannel(chptr))
943+ *(chntext + len++) = '*';
944+ if (IsZombie(lp))
945+ *(chntext + len++) = '!';
946+ if (IsChanOp(lp))
947+ *(chntext + len++) = '@';
948+ else if (HasVoice(lp))
949+ *(chntext + len++) = '+';
950+ else if (IsDelayedJoin(lp))
951+ *(chntext + len++) = '<';
952+ if (len)
953+ *(chntext + len) = '\0';
954+
955+ strcpy(chntext + len, chptr->chname);
956+ len += strlen(chptr->chname);
957+ strcat(chntext + len, " ");
958+ len++;
959+ }
960+ if (chntext[0] != '\0')
961+ send_reply(sptr, RPL_DATASTR, chntext);
962+
963+ send_reply(sptr, RPL_DATASTR, " ");
964+ }
965+ }
966+ count++;
967+ }
968+ }
969+
970+ if (count > 0) {
971+ send_reply(sptr, RPL_DATASTR, " ");
972+
973+ ircd_snprintf(0, outbuf, sizeof(outbuf), "Matching records found:: %d", count);
974+ send_reply(sptr, RPL_DATASTR, outbuf);
975+
976+ send_reply(sptr, RPL_ENDOFCHECK, " ");
977+ }
978+
979+ return count;
980+}
981diff -r 3303f23758b0 ircd/parse.c
982--- a/ircd/parse.c Wed Jul 17 21:30:44 2013 +0100
983+++ b/ircd/parse.c Wed Jul 17 21:38:16 2013 +0100
984@@ -647,6 +647,23 @@
985 { m_cap, m_cap, m_ignore, m_cap, m_ignore }
986 },
987 #endif
988+
989+ /*
990+ * - ASUKA ---------------------------------------------------------------------
991+ * Add the command for CHECK.
992+ * This was adapted from Lain for use in Asuka.
993+ * Original code by Durzel (durzel@quakenet.org).
994+ *
995+ * qoreQ (qoreQ@quakenet.org) - 08/14/2002
996+ * -----------------------------------------------------------------------------
997+ */
998+ {
999+ MSG_CHECK,
1000+ TOK_CHECK,
1001+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
1002+ { m_unregistered, m_not_oper, m_check, m_check, m_ignore }
1003+ },
1004+
1005 /* This command is an alias for QUIT during the unregistered part of
1006 * of the server. This is because someone jumping via a broken web
1007 * proxy will send a 'POST' as their first command - which we will
1008diff -r 3303f23758b0 ircd/s_err.c
1009--- a/ircd/s_err.c Wed Jul 17 21:30:44 2013 +0100
1010+++ b/ircd/s_err.c Wed Jul 17 21:38:16 2013 +0100
1011@@ -602,19 +602,19 @@
1012 /* 284 */
1013 { RPL_FEATURE, 0, "284" },
1014 /* 285 */
1015- { 0 },
1016+ { RPL_NEWHOSTIS, "%s: %s host %s - [%s@%s]" },
1017 /* 286 */
1018- { 0 },
1019+ { RPL_CHKHEAD, ":Information for %s %s", "286" },
1020 /* 287 */
1021- { 0 },
1022+ { RPL_CHANUSER, ": %s%s (%s@%s) %s (%s) %s", "287" },
1023 /* 288 */
1024 { 0 },
1025 /* 289 */
1026 { 0 },
1027 /* 290 */
1028- { 0 },
1029+ { RPL_DATASTR, ":%s", "290" },
1030 /* 291 */
1031- { 0 },
1032+ { RPL_ENDOFCHECK, ":%s", "291" },
1033 /* 292 */
1034 { 0 },
1035 /* 293 */
1036@@ -848,7 +848,7 @@
1037 /* 407 */
1038 { ERR_TOOMANYTARGETS, "%s :Duplicate recipients. No message delivered", "407" },
1039 /* 408 */
1040- { 0 },
1041+ { ERR_SEARCHNOMATCH, ":%s %s No matching record(s) found", "408" },
1042 /* 409 */
1043 { ERR_NOORIGIN, ":No origin specified", "409" },
1044 /* 410 */
1045diff -r 3303f23758b0 ircd/s_user.c
1046--- a/ircd/s_user.c Wed Jul 17 21:30:44 2013 +0100
1047+++ b/ircd/s_user.c Wed Jul 17 21:38:16 2013 +0100
1048@@ -1485,7 +1485,8 @@
1049 m--; /* Step back over the '\0' */
1050 }
1051
1052- if (IsSetHost(cptr)) {
1053+ /* sethost parameter is wanted */
1054+ if (type != UMODE_AND_ACCOUNT && IsSetHost(cptr)) {
1055 *m++ = ' ';
1056 ircd_snprintf(0, m, USERLEN + HOSTLEN + 2, "%s@%s", cli_user(cptr)->username,
1057 cli_user(cptr)->host);