unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

who_old.c (22238B)

      1 /*   src/modules/who_old.c
      2  *   Copyright (C) 1990 Jarkko Oikarinen and
      3  *                      University of Oulu, Computing Center
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers.
      7  *
      8  *   This program is free softwmare; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 /* rewritten 06/02 by larne, the old one was unreadable. */
     24 /* changed indentation + some parts rewritten by Syzop. */
     25 
     26 #include "unrealircd.h"
     27 
     28 CMD_FUNC(cmd_who);
     29 
     30 /* Place includes here */
     31 #define MSG_WHO 	"WHO"
     32 
     33 ModuleHeader MOD_HEADER
     34   = {
     35 	"who_old",	/* Name of module */
     36 	"5.0", /* Version */
     37 	"command /who (old version)", /* Short description of module */
     38 	"UnrealIRCd Team",
     39 	"unrealircd-6",
     40     };
     41 
     42 /* This is called on module init, before Server Ready */
     43 MOD_INIT()
     44 {
     45 	if (!CommandAdd(modinfo->handle, MSG_WHO, cmd_who, MAXPARA, CMD_USER))
     46 	{
     47 		config_warn("You cannot load both the cmd_whox and cmd_who module. You should ONLY load the cmd_whox module.");
     48 		return MOD_FAILED;
     49 	}
     50 	MARK_AS_OFFICIAL_MODULE(modinfo);
     51 	return MOD_SUCCESS;
     52 }
     53 
     54 /* Is first run when server is 100% ready */
     55 MOD_LOAD()
     56 {
     57 	return MOD_SUCCESS;
     58 }
     59 
     60 
     61 /* Called when module is unloaded */
     62 MOD_UNLOAD()
     63 {
     64 	return MOD_SUCCESS;
     65 }
     66 
     67 static void do_channel_who(Client *client, Channel *channel, const char *mask);
     68 static void make_who_status(Client *, Client *, Channel *, Member *, char *, int);
     69 static void do_other_who(Client *client, const char *mask);
     70 static void send_who_reply(Client *, Client *, const char *, const char *, const char *);
     71 static const char *first_visible_channel(Client *, Client *, int *);
     72 static int parse_who_options(Client *, int, const char **);
     73 static void who_sendhelp(Client *);
     74 
     75 #define WF_OPERONLY  0x01 /**< only show opers */
     76 #define WF_ONCHANNEL 0x02 /**< we're on the channel we're /who'ing */
     77 #define WF_WILDCARD  0x04 /**< a wildcard /who */
     78 #define WF_REALHOST  0x08 /**< want real hostnames */
     79 #define WF_IP	     0x10 /**< want IP addresses */
     80 
     81 static int who_flags;
     82 
     83 #define WHO_CANTSEE 0x01 /**< set if we can't see them */
     84 #define WHO_CANSEE  0x02 /**< set if we can */
     85 #define WHO_OPERSEE 0x04 /**< set if we only saw them because we're an oper */
     86 
     87 #define FVC_HIDDEN  0x01
     88 
     89 #define WHO_WANT 1
     90 #define WHO_DONTWANT 2
     91 #define WHO_DONTCARE 0
     92 
     93 struct {
     94 	int want_away;
     95 	int want_channel;
     96 	const char *channel; /**< if they want one */
     97 	int want_gecos;
     98 	const char *gecos;
     99 	int want_server;
    100 	const char *server;
    101 	int want_host;
    102 	const char *host;
    103 	int want_nick;
    104 	const char *nick;
    105 	int want_user;
    106 	const char *user;
    107 	int want_ip;
    108 	const char *ip;
    109 	int want_port;
    110 	int port;
    111 	int want_umode;
    112 	int umodes_dontwant;
    113 	int umodes_want;
    114 	int common_channels_only;
    115 } wfl;
    116 
    117 /** The /who command: retrieves information from users. */
    118 CMD_FUNC(cmd_who)
    119 {
    120 	Channel *target_channel;
    121 	const char *mask = parv[1];
    122 	char maskbuf[512];
    123 	int i = 0;
    124 
    125 	if (!MyUser(client))
    126 		return;
    127 
    128 	who_flags = 0;
    129 	memset(&wfl, 0, sizeof(wfl));
    130 
    131 	if (parc > 1)
    132 	{
    133 		i = parse_who_options(client, parc - 1, parv + 1);
    134 		if (i < 0)
    135 		{
    136 			sendnumeric(client, RPL_ENDOFWHO, mask);
    137 			return;
    138 		}
    139 	}
    140 
    141 	if (parc-i < 2 || strcmp(parv[1 + i], "0") == 0)
    142 		mask = "*";
    143 	else
    144 		mask = parv[1 + i];
    145 
    146 	if (!i && parc > 2 && *parv[2] == 'o')
    147 		who_flags |= WF_OPERONLY;
    148 
    149 	/* Pfff... collapse... hate it! */
    150 	strlcpy(maskbuf, mask, sizeof(maskbuf));
    151 	collapse(maskbuf);
    152 	mask = maskbuf;
    153 
    154 	if (*mask == '\0')
    155 	{
    156 		/* no mask given */
    157 		sendnumeric(client, RPL_ENDOFWHO, "*");
    158 		return;
    159 	}
    160 
    161 	if ((target_channel = find_channel(mask)) != NULL)
    162 	{
    163 		do_channel_who(client, target_channel, mask);
    164 		sendnumeric(client, RPL_ENDOFWHO, mask);
    165 		return;
    166 	}
    167 
    168 	if (wfl.channel && wfl.want_channel == WHO_WANT && 
    169 	    (target_channel = find_channel(wfl.channel)) != NULL)
    170 	{
    171 		do_channel_who(client, target_channel, mask);
    172 		sendnumeric(client, RPL_ENDOFWHO, mask);
    173 		return;
    174 	}
    175 	else
    176 	{
    177 		do_other_who(client, mask);
    178 		sendnumeric(client, RPL_ENDOFWHO, mask);
    179 		return;
    180 	}
    181 
    182 	return;
    183 }
    184 
    185 static void who_sendhelp(Client *client)
    186 {
    187   char *who_help[] = {
    188     "/WHO [+|-][achmnsuM] [args]",
    189     "Flags are specified like channel modes, the flags chmnsu all have arguments",
    190     "Flags are set to a positive check by +, a negative check by -",
    191     "The flags work as follows:",
    192     "Flag a: user is away",
    193     "Flag c <channel>:       user is on <channel>,",
    194     "                        no wildcards accepted",
    195     "Flag h <host>:          user has string <host> in their hostname,",
    196     "                        wildcards accepted",
    197     "Flag m <usermodes>:     user has <usermodes> set, only",
    198     "                        O/o/C/A/a/N/B are allowed",
    199     "Flag n <nick>:          user has string <nick> in their nickname,",
    200     "                        wildcards accepted",
    201     "Flag s <server>:        user is on server <server>,",
    202     "                        wildcards not accepted",
    203     "Flag u <user>:          user has string <user> in their username,",
    204     "                        wildcards accepted",
    205     "Behavior flags:",
    206     "Flag M: check for user in channels I am a member of",
    207     NULL
    208   };
    209 
    210   char *who_oper_help[] = {
    211     "/WHO [+|-][acghimnsuMRI] [args]",
    212     "Flags are specified like channel modes, the flags chigmnsu all have arguments",
    213     "Flags are set to a positive check by +, a negative check by -",
    214     "The flags work as follows:",
    215     "Flag a: user is away",
    216     "Flag c <channel>:       user is on <channel>,",
    217     "                        no wildcards accepted",
    218     "Flag g <gcos/realname>: user has string <gcos> in their GCOS,",
    219     "                        wildcards accepted",
    220     "Flag h <host>:          user has string <host> in their hostname,",
    221     "                        wildcards accepted",
    222     "Flag i <ip>:            user has string <ip> in their IP address,",
    223     "                        wildcards accepted",
    224     "Flag p <port>:          user is connecting on port <port>,",
    225     "                        local connections only",
    226     "Flag m <usermodes>:     user has <usermodes> set",
    227     "Flag n <nick>:          user has string <nick> in their nickname,",
    228     "                        wildcards accepted",
    229     "Flag s <server>:        user is on server <server>,",
    230     "                        wildcards not accepted",
    231     "Flag u <user>:          user has string <user> in their username,",
    232     "                        wildcards accepted",
    233     "Behavior flags:",
    234     "Flag M: check for user in channels I am a member of",
    235     "Flag R: show users' real hostnames",
    236     "Flag I: show users' IP addresses",
    237     NULL
    238   };
    239   char **s;
    240 
    241 	if (IsOper(client))
    242 		s = who_oper_help;
    243 	else
    244 		s = who_help;
    245 
    246 	for (; *s; s++)
    247 		sendnumeric(client, RPL_LISTSYNTAX, *s);
    248 }
    249 
    250 #define WHO_ADD 1
    251 #define WHO_DEL 2
    252 
    253 static int parse_who_options(Client *client, int argc, const char **argv)
    254 {
    255 	const char *s = argv[0];
    256 	int what = WHO_ADD;
    257 	int i = 1;
    258 
    259 /* A few helper macro's because this is used a lot, added during recode by Syzop. */
    260 
    261 /** function requiress a parameter: check if there's one, if not: return -1. */
    262 #define REQUIRE_PARAM() { if (i >= argc) { \
    263                            who_sendhelp(client); \
    264                            return -1; \
    265                       } } while(0);
    266 /** set option 'x' depending on 'what' (add/want or del/dontwant) */
    267 #define SET_OPTION(x) { if (what == WHO_ADD) \
    268                            x = WHO_WANT; \
    269                       else \
    270                            x = WHO_DONTWANT; \
    271                       } while(0);
    272 /** Eat a param, set the param in memory and set the option to want or dontwant */
    273 #define DOIT(x,y) { REQUIRE_PARAM(); x = argv[i]; SET_OPTION(y); i++; } while(0);
    274 
    275 	if (*s != '-' && *s != '+')
    276 		return 0;
    277 
    278 	while (*s)
    279  	{
    280 		switch (*s)
    281 		{
    282 			case '+':
    283 	  			what = WHO_ADD;
    284 	  			break;
    285 			case '-':
    286 				what = WHO_DEL;
    287 				break;
    288 			case 'a':
    289 				SET_OPTION(wfl.want_away);
    290 				break;
    291 			case 'c':
    292 				DOIT(wfl.channel, wfl.want_channel);
    293 				break;
    294 			case 'g':
    295 				REQUIRE_PARAM()
    296 				if (!IsOper(client))
    297 					break; /* oper-only */
    298 				wfl.gecos = argv[i];
    299 				SET_OPTION(wfl.want_gecos);
    300 				i++;
    301 				break;
    302 			case 's':
    303 				DOIT(wfl.server, wfl.want_server);
    304 				break;
    305 			case 'h':
    306 				DOIT(wfl.host, wfl.want_host);
    307 				break;
    308 			case 'i':
    309 				REQUIRE_PARAM()
    310 				if (!IsOper(client))
    311 					break; /* oper-only */
    312 				wfl.ip = argv[i];
    313 				SET_OPTION(wfl.want_ip);
    314 				i++;
    315 				break;
    316 			case 'n':
    317 				DOIT(wfl.nick, wfl.want_nick);
    318 				break;
    319 			case 'u':
    320 				DOIT(wfl.user, wfl.want_user);
    321 				break;
    322 			case 'm':
    323 				REQUIRE_PARAM()
    324 				{
    325 					const char *s = argv[i];
    326 					int *umodes;
    327 
    328 					if (what == WHO_ADD)
    329 						umodes = &wfl.umodes_want;
    330 					else
    331 						umodes = &wfl.umodes_dontwant;
    332 
    333 					*umodes = set_usermode(s);
    334 
    335 					if (!IsOper(client))
    336 						*umodes = *umodes & UMODE_OPER; /* these are usermodes regular users may search for. just oper now. */
    337 					if (*umodes == 0)
    338 						return -1;
    339 				}
    340 				i++;
    341 				break;
    342 			case 'p':
    343 				REQUIRE_PARAM()
    344 				if (!IsOper(client))
    345 					break; /* oper-only */
    346 				wfl.port = atoi(argv[i]);
    347 				SET_OPTION(wfl.want_port);
    348 				i++;
    349 				break;
    350 			case 'M':
    351 				SET_OPTION(wfl.common_channels_only);
    352 				break;
    353 			case 'R':
    354 				if (!IsOper(client))
    355 					break;
    356 				if (what == WHO_ADD)
    357 					who_flags |= WF_REALHOST;
    358 				else
    359 					who_flags &= ~WF_REALHOST;
    360 				break;
    361 			case 'I':
    362 				if (!IsOper(client))
    363 					break;
    364 				if (what == WHO_ADD)
    365 					who_flags |= WF_IP;
    366 				
    367 				else
    368 					who_flags &= ~WF_IP;
    369 				break;
    370 			default:
    371 				who_sendhelp(client);
    372 				return -1;
    373 		}
    374 		s++;
    375     }
    376 
    377   return i;
    378 #undef REQUIRE_PARAM
    379 #undef SET_OPTION
    380 #undef DOIT
    381 }
    382 
    383 static int can_see(Client *requester, Client *target, Channel *channel)
    384 {
    385 	int ret = 0;
    386 	char has_common_chan = 0;
    387 
    388 	do {
    389 		/* can only see people */
    390 		if (!IsUser(target))
    391 			return WHO_CANTSEE;
    392 
    393 		/* can only see opers if thats what they want */
    394 		if (who_flags & WF_OPERONLY)
    395 		{
    396 			if (!IsOper(target))
    397 				return ret | WHO_CANTSEE;
    398 			if (IsHideOper(target)) {
    399 				if (IsOper(requester))
    400 					ret |= WHO_OPERSEE;
    401 				else
    402 					return ret | WHO_CANTSEE;
    403 			}
    404 		}
    405 
    406 		/* if they only want people who are away */
    407 		if ((wfl.want_away == WHO_WANT && !target->user->away) ||
    408 		    (wfl.want_away == WHO_DONTWANT && target->user->away))
    409 			return WHO_CANTSEE;
    410 
    411 		/* if they only want people on a certain channel. */
    412 		if (wfl.want_channel != WHO_DONTCARE)
    413  		{
    414 			Channel *chan = find_channel(wfl.channel);
    415 			if (!chan && wfl.want_channel == WHO_WANT)
    416 				return WHO_CANTSEE;
    417 			if ((wfl.want_channel == WHO_WANT) && !IsMember(target, chan))
    418 				return WHO_CANTSEE;
    419 			if ((wfl.want_channel == WHO_DONTWANT) && IsMember(target, chan))
    420 				return WHO_CANTSEE;
    421 		}
    422 
    423 		/* if they only want people with a certain gecos */
    424 		if (wfl.want_gecos != WHO_DONTCARE)
    425 		{
    426 			if (((wfl.want_gecos == WHO_WANT) && !match_simple(wfl.gecos, target->info)) ||
    427 			    ((wfl.want_gecos == WHO_DONTWANT) && match_simple(wfl.gecos, target->info)))
    428 			{
    429 				return WHO_CANTSEE;
    430 			}
    431 		}
    432 
    433 		/* if they only want people with a certain server */
    434 		if (wfl.want_server != WHO_DONTCARE)
    435 		{
    436 			if (((wfl.want_server == WHO_WANT) && strcasecmp(wfl.server, target->user->server)) ||
    437 			    ((wfl.want_server == WHO_DONTWANT) && !strcasecmp(wfl.server, target->user->server)))
    438 			{
    439 				return WHO_CANTSEE;
    440 			}
    441 		}
    442 
    443 		/* if they only want people with a certain host */
    444 		if (wfl.want_host != WHO_DONTCARE)
    445 		{
    446 			char *host;
    447 
    448 			if (IsOper(requester))
    449 				host = target->user->realhost;
    450 			else
    451 				host = GetHost(target);
    452 
    453 			if (((wfl.want_host == WHO_WANT) && !match_simple(wfl.host, host)) ||
    454 			    ((wfl.want_host == WHO_DONTWANT) && match_simple(wfl.host, host)))
    455 			{
    456 				return WHO_CANTSEE;
    457 			}
    458 		}
    459 
    460 		/* if they only want people with a certain IP */
    461 		if (wfl.want_ip != WHO_DONTCARE)
    462 		{
    463 			char *ip;
    464 
    465 			ip = target->ip;
    466 			if (!ip)
    467 				return WHO_CANTSEE;
    468 
    469 			if (((wfl.want_ip == WHO_WANT) && !match_simple(wfl.ip, ip)) ||
    470 			    ((wfl.want_ip == WHO_DONTWANT) && match_simple(wfl.ip, ip)))
    471 			{
    472 				return WHO_CANTSEE;
    473 			}
    474 		}
    475 
    476 		/* if they only want people connecting on a certain port */
    477 		if (wfl.want_port != WHO_DONTCARE)
    478 		{
    479 			int port;
    480 			
    481 			if (!MyUser(target))
    482 				return WHO_CANTSEE;
    483 
    484 			port = target->local->listener->port;
    485 
    486 			if (((wfl.want_port == WHO_WANT) && wfl.port != port) ||
    487 			    ((wfl.want_port == WHO_DONTWANT) && wfl.port == port))
    488 			{
    489 				return WHO_CANTSEE;
    490 			}
    491 		}
    492 
    493 		/* if they only want people with a certain nick.. */
    494 		if (wfl.want_nick != WHO_DONTCARE)
    495 		{
    496 			if (((wfl.want_nick == WHO_WANT) && !match_simple(wfl.nick, target->name)) ||
    497 			    ((wfl.want_nick == WHO_DONTWANT) && match_simple(wfl.nick, target->name)))
    498 			{
    499 				return WHO_CANTSEE;
    500 			}
    501 		}
    502 
    503 		/* if they only want people with a certain username */
    504 		if (wfl.want_user != WHO_DONTCARE)
    505 		{
    506 			if (((wfl.want_user == WHO_WANT) && !match_simple(wfl.user, target->user->username)) ||
    507 			    ((wfl.want_user == WHO_DONTWANT) && match_simple(wfl.user, target->user->username)))
    508 			{
    509 				return WHO_CANTSEE;
    510 			}
    511 		}
    512 
    513 		/* if they only want people with a certain umode */
    514 		if (wfl.umodes_want)
    515 		{
    516 			if (!(target->umodes & wfl.umodes_want) || (!IsOper(requester) && (target->umodes & UMODE_HIDEOPER)))
    517 				return WHO_CANTSEE;
    518 		}
    519 
    520 		if (wfl.umodes_dontwant)
    521 		{
    522 			if ((target->umodes & wfl.umodes_dontwant) && (!(target->umodes & UMODE_HIDEOPER) || IsOper(requester)))
    523 				return WHO_CANTSEE;
    524 		}
    525 
    526 		/* if they only want common channels */
    527 		if (wfl.common_channels_only)
    528 		{
    529 			if (!has_common_channels(requester, target))
    530 				return WHO_CANTSEE;
    531 			has_common_chan = 1;
    532 		}
    533 
    534 		if (channel)
    535 		{
    536 			int member = who_flags & WF_ONCHANNEL;
    537 
    538 			if (SecretChannel(channel) || HiddenChannel(channel))
    539 			{
    540 				/* if they aren't on it.. they can't see it */
    541 				if (!(who_flags & WF_ONCHANNEL))
    542 					break;
    543 			}
    544 			if (IsInvisible(target) && !member)
    545 				break;
    546 
    547 			if (!user_can_see_member(requester, target, channel))
    548 				break; /* invisible (eg: due to delayjoin) */
    549 		}
    550 		else
    551 		{
    552 			/* a user/mask who */
    553 
    554 			/* If the common channel info hasn't been set, set it now */
    555 			if (!wfl.common_channels_only)
    556 				has_common_chan = has_common_channels(requester, target);
    557 
    558 			if (IsInvisible(target) && !has_common_chan)
    559 			{
    560 				/* don't show them unless it's an exact match 
    561 				   or it is the user requesting the /who */
    562 				if ((who_flags & WF_WILDCARD) && requester != target)
    563 					break;
    564 			}
    565 		}
    566 
    567 		/* phew.. show them. */
    568 		return WHO_CANSEE;
    569 	} while (0);
    570 
    571 	/* if we get here, it's oper-dependant. */
    572 	if (IsOper(requester))
    573 		return ret | WHO_OPERSEE | WHO_CANSEE;
    574 	else
    575 	{
    576 		if (requester == target)
    577 			return ret | WHO_CANSEE;
    578 		else
    579 			return ret | WHO_CANTSEE;
    580 	}
    581 }
    582 
    583 static void do_channel_who(Client *client, Channel *channel, const char *mask)
    584 {
    585 	Member *cm = channel->members;
    586 	if (IsMember(client, channel) || ValidatePermissionsForPath("channel:see:who:onchannel",client,NULL,channel,NULL))
    587 		who_flags |= WF_ONCHANNEL;
    588 
    589 	for (cm = channel->members; cm; cm = cm->next)
    590 	{
    591 		Client *acptr = cm->client;
    592 		char status[32];
    593 		int cansee;
    594 		if ((cansee = can_see(client, acptr, channel)) & WHO_CANTSEE)
    595 			continue;
    596 
    597 		make_who_status(client, acptr, channel, cm, status, cansee);
    598 		send_who_reply(client, acptr, channel->name, status, "");
    599     }
    600 }
    601 
    602 static void make_who_status(Client *client, Client *acptr, Channel *channel, 
    603 			    Member *cm, char *status, int cansee)
    604 {
    605 	int i = 0;
    606 	Hook *h;
    607 
    608 	if (acptr->user->away)
    609 		status[i++] = 'G';
    610 	else
    611 		status[i++] = 'H';
    612 
    613 	if (IsRegNick(acptr))
    614 		status[i++] = 'r';
    615 
    616 	if (IsSecureConnect(acptr))
    617 		status[i++] = 's';
    618 
    619 	for (h = Hooks[HOOKTYPE_WHO_STATUS]; h; h = h->next)
    620 	{
    621 		int ret = (*(h->func.intfunc))(client, acptr, channel, cm, status, cansee);
    622 		if (ret != 0)
    623 			status[i++] = (char)ret;
    624 	}
    625 	
    626 	if (IsOper(acptr) && (!IsHideOper(acptr) || client == acptr || IsOper(client)))
    627 		status[i++] = '*';
    628 
    629 	if (IsOper(acptr) && (IsHideOper(acptr) && client != acptr && IsOper(client)))
    630 		status[i++] = '!';
    631   
    632 	if (cansee & WHO_OPERSEE)
    633 		status[i++] = '?';
    634 
    635 	if (cm)
    636 	{
    637 		if (HasCapability(client, "multi-prefix"))
    638 		{
    639 			/* Standard NAMES reply (single character) */
    640 			char c = mode_to_prefix(*cm->member_modes);
    641 			if (c)
    642 				status[i++] = c;
    643 		}
    644 		else
    645 		{
    646 			/* NAMES reply with all rights included (multi-prefix / NAMESX) */
    647 			strcpy(&status[i], modes_to_prefix(cm->member_modes));
    648 			i += strlen(&status[i]);
    649 		}
    650 	}
    651 
    652 	status[i] = '\0';
    653 }
    654 
    655 static void do_other_who(Client *client, const char *mask)
    656 {
    657 int oper = IsOper(client);
    658 
    659 	if (strchr(mask, '*') || strchr(mask, '?'))
    660 	{
    661 		int i = 0;
    662 		/* go through all users.. */
    663 		Client *acptr;
    664 		who_flags |= WF_WILDCARD;
    665 
    666 		list_for_each_entry(acptr, &client_list, client_node)
    667 		{
    668 		int cansee;
    669 		char status[20];
    670 		const char *channel;
    671 		int flg;
    672 
    673 			if (!IsUser(acptr))
    674 				continue;
    675 			if (!oper) {
    676 				/* non-opers can only search on nick here */
    677 				if (!match_simple(mask, acptr->name))
    678 					continue;
    679 			} else {
    680 				/* opers can search on name, ident, virthost, ip and realhost.
    681 				 * Yes, I like readable if's -- Syzop.
    682 				 */
    683 				if (match_simple(mask, acptr->name) || match_simple(mask, acptr->user->realhost) ||
    684 				    match_simple(mask, acptr->user->username))
    685 					goto matchok;
    686 				if (IsHidden(acptr) && match_simple(mask, acptr->user->virthost))
    687 					goto matchok;
    688 				if (acptr->ip && match_simple(mask, acptr->ip))
    689 					goto matchok;
    690 				/* nothing matched... */
    691 				continue;
    692 			}
    693 matchok:
    694 			if ((cansee = can_see(client, acptr, NULL)) & WHO_CANTSEE)
    695 				continue;
    696 			if (WHOLIMIT && !IsOper(client) && ++i > WHOLIMIT)
    697 			{
    698 				sendnumeric(client, ERR_WHOLIMEXCEED, WHOLIMIT);
    699 				return;
    700 			}
    701 
    702 			channel = first_visible_channel(client, acptr, &flg);
    703 			make_who_status(client, acptr, NULL, NULL, status, cansee);
    704 			send_who_reply(client, acptr, channel, status, (flg & FVC_HIDDEN) ? "~" : "");
    705 		}
    706 	}
    707 	else
    708 	{
    709 		/* just a single client (no wildcards detected) */
    710 		Client *acptr = find_client(mask, NULL);
    711 		int cansee;
    712 		char status[20];
    713 		const char *channel;
    714 		int flg;
    715 
    716 		if (!acptr)
    717 			return;
    718 
    719 		if ((cansee = can_see(client, acptr, NULL)) == WHO_CANTSEE)
    720 			return;
    721 
    722 		channel = first_visible_channel(client, acptr, &flg);
    723 		make_who_status(client, acptr, NULL, NULL, status, cansee);
    724 		send_who_reply(client, acptr, channel, status, (flg & FVC_HIDDEN) ? "~" : "");
    725 	}
    726 }
    727 
    728 static void send_who_reply(Client *client, Client *acptr, 
    729 			   const char *channel, const char *status, const char *xstat)
    730 {
    731 	char *stat;
    732 	const char *host;
    733 	int flat = (FLAT_MAP && !IsOper(client)) ? 1 : 0;
    734 
    735 	stat = safe_alloc(strlen(status) + strlen(xstat) + 1);
    736 	sprintf(stat, "%s%s", status, xstat);
    737 
    738 	if (IsOper(client))
    739 	{
    740 		if (who_flags & WF_REALHOST)
    741 			host = acptr->user->realhost;
    742 		else if (who_flags & WF_IP)
    743 			host = (acptr->ip ? acptr->ip : acptr->user->realhost);
    744 		else
    745 			host = GetHost(acptr);
    746 	}
    747 	else
    748 		host = GetHost(acptr);
    749 					
    750 
    751 	if (IsULine(acptr) && !IsOper(client) && !ValidatePermissionsForPath("server:info:map:ulines",client,acptr,NULL,NULL) && HIDE_ULINES)
    752 	{
    753 	        sendnumeric(client, RPL_WHOREPLY,
    754         	     channel,       /* channel name */
    755 	             acptr->user->username, /* user name */
    756         	     host,		    /* hostname */
    757 	             "hidden",              /* let's hide the server from normal users if the server is a uline and HIDE_ULINES is on */
    758         	     acptr->name,           /* nick */
    759 	             stat,                  /* status */
    760         	     0,                     /* hops (hidden) */
    761 	             acptr->info            /* realname */
    762              	);
    763 
    764 	} else {
    765 		sendnumeric(client, RPL_WHOREPLY,
    766 		     channel,       /* channel name */
    767 		     acptr->user->username,      /* user name */
    768 		     host,		         /* hostname */
    769 		     acptr->user->server,        /* server name */
    770 		     acptr->name,                /* nick */
    771 		     stat,                       /* status */
    772 		     flat ? 0 : acptr->hopcount, /* hops */ 
    773 		     acptr->info                 /* realname */
    774 		     );
    775 	}
    776 	safe_free(stat);
    777 }
    778 
    779 static const char *first_visible_channel(Client *client, Client *acptr, int *flg)
    780 {
    781 	Membership *lp;
    782 
    783 	*flg = 0;
    784 
    785 	for (lp = acptr->user->channel; lp; lp = lp->next)
    786 	{
    787 		Channel *channel = lp->channel;
    788 		Hook *h;
    789 		int ret = EX_ALLOW;
    790 		int operoverride = 0;
    791 		int showchannel = 0;
    792 		
    793 		/* Note that the code below is almost identical to the one in /WHOIS */
    794 
    795 		if (ShowChannel(client, channel))
    796 			showchannel = 1;
    797 
    798 		for (h = Hooks[HOOKTYPE_SEE_CHANNEL_IN_WHOIS]; h; h = h->next)
    799 		{
    800 			int n = (*(h->func.intfunc))(client, acptr, channel);
    801 			/* Hook return values:
    802 			 * EX_ALLOW means 'yes is ok, as far as modules are concerned'
    803 			 * EX_DENY means 'hide this channel, unless oper overriding'
    804 			 * EX_ALWAYS_DENY means 'hide this channel, always'
    805 			 * ... with the exception that we always show the channel if you /WHOIS yourself
    806 			 */
    807 			if (n == EX_DENY)
    808 			{
    809 				ret = EX_DENY;
    810 			}
    811 			else if (n == EX_ALWAYS_DENY)
    812 			{
    813 				ret = EX_ALWAYS_DENY;
    814 				break;
    815 			}
    816 		}
    817 		
    818 		if (ret == EX_DENY)
    819 			showchannel = 0;
    820 		
    821 		if (!showchannel && (ValidatePermissionsForPath("channel:see:who:secret",client,NULL,channel,NULL) || ValidatePermissionsForPath("channel:see:whois",client,NULL,channel,NULL)))
    822 		{
    823 			showchannel = 1; /* OperOverride */
    824 			operoverride = 1;
    825 		}
    826 		
    827 		if ((ret == EX_ALWAYS_DENY) && (acptr != client))
    828 			continue; /* a module asked us to really not expose this channel, so we don't (except target==ourselves). */
    829 
    830 		if (acptr == client)
    831 			showchannel = 1;
    832 
    833 		if (operoverride)
    834 			*flg |= FVC_HIDDEN;
    835 
    836 		if (showchannel)
    837 			return channel->name;
    838 	}
    839 
    840 	/* no channels that they can see */
    841 	return "*";
    842 }