]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - check.patch
use FindUser instead of Findclient/server to find a user
[irc/quakenet/snircd-patchqueue.git] / check.patch
CommitLineData
edb26b39 1# HG changeset patch
3be24488 2# Parent 4e198b121c286785bbdc97fefed95f6d7b95c9ab
edb26b39 3
3be24488 4diff -r 4e198b121c28 include/check.h
edb26b39 5--- /dev/null Thu Jan 01 00:00:00 1970 +0000
3be24488 6+++ b/include/check.h Fri Jul 26 19:55:24 2013 +0100
edb26b39
P
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 */
3be24488
P
57diff -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 @@
edb26b39
P
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);
3be24488
P
70diff -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
edb26b39
P
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*[]);
3be24488
P
93diff -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
edb26b39
P
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,
3be24488
P
104diff -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 @@
edb26b39
P
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
3be24488
P
118diff -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
edb26b39
P
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.
3be24488
P
134diff -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
edb26b39
P
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 }
3be24488
P
146diff -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
edb26b39
P
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 \
3be24488
P
157diff -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
edb26b39
P
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),
3be24488 168diff -r 4e198b121c28 ircd/m_check.c
edb26b39 169--- /dev/null Thu Jan 01 00:00:00 1970 +0000
3be24488
P
170+++ b/ircd/m_check.c Fri Jul 26 19:55:24 2013 +0100
171@@ -0,0 +1,804 @@
edb26b39
P
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+ }
3be24488 328+ else if ((acptr = FindUser(parv[1]))) {
edb26b39
P
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+}
3be24488
P
976diff -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 @@
edb26b39
P
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
3be24488
P
1003diff -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
edb26b39
P
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 */
3be24488
P
1040diff -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 @@
edb26b39
P
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);