unrealircd

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

svsmode.c (17906B)

      1 /*
      2  *   IRC - Internet Relay Chat, src/modules/svsmode.c
      3  *   (C) 2001 The UnrealIRCd Team
      4  *
      5  *   SVSMODE and SVS2MODE commands
      6  *
      7  *   See file AUTHORS in IRC package for additional names of
      8  *   the programmers.
      9  *
     10  *   This program is free software; you can redistribute it and/or modify
     11  *   it under the terms of the GNU General Public License as published by
     12  *   the Free Software Foundation; either version 1, or (at your option)
     13  *   any later version.
     14  *
     15  *   This program is distributed in the hope that it will be useful,
     16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  *   GNU General Public License for more details.
     19  *
     20  *   You should have received a copy of the GNU General Public License
     21  *   along with this program; if not, write to the Free Software
     22  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     23  */
     24 
     25 /* FIXME: this one needs a lot more mtag work !! with _special... */
     26 
     27 #include "unrealircd.h"
     28 
     29 void add_send_mode_param(Channel *channel, Client *from, char what, char mode, char *param);
     30 CMD_FUNC(cmd_svsmode);
     31 CMD_FUNC(cmd_svs2mode);
     32 
     33 #define MSG_SVSMODE 	"SVSMODE"	
     34 #define MSG_SVS2MODE    "SVS2MODE"
     35 
     36 ModuleHeader MOD_HEADER
     37   = {
     38 	"svsmode",
     39 	"5.0",
     40 	"command /svsmode and svs2mode", 
     41 	"UnrealIRCd Team",
     42 	"unrealircd-6",
     43     };
     44 
     45 char modebuf[BUFSIZE], parabuf[BUFSIZE];
     46 
     47 MOD_INIT()
     48 {
     49 	CommandAdd(modinfo->handle, MSG_SVSMODE, cmd_svsmode, MAXPARA, CMD_SERVER|CMD_USER);
     50 	CommandAdd(modinfo->handle, MSG_SVS2MODE, cmd_svs2mode, MAXPARA, CMD_SERVER|CMD_USER);
     51 	MARK_AS_OFFICIAL_MODULE(modinfo);
     52 	return MOD_SUCCESS;
     53 }
     54 
     55 MOD_LOAD()
     56 {
     57 	return MOD_SUCCESS;
     58 }
     59 
     60 MOD_UNLOAD()
     61 {
     62 	return MOD_SUCCESS;
     63 }
     64 
     65 void unban_user(Client *client, Channel *channel, Client *acptr, char chmode)
     66 {
     67 	Extban *extban;
     68 	const char *nextbanstr;
     69 	Ban *ban, *bnext;
     70 	Ban **banlist;
     71 	BanContext *b;
     72 	char uhost[NICKLEN+USERLEN+HOSTLEN+6], vhost[NICKLEN+USERLEN+HOSTLEN+6];
     73 	char ihost[NICKLEN+USERLEN+HOSTLEN+6], chost[NICKLEN+USERLEN+HOSTLEN+6];
     74 
     75 	/* BUILD HOSTS */
     76 
     77 	*uhost = *vhost = *ihost = *chost = '\0';
     78 
     79 	strlcpy(uhost, make_nick_user_host(acptr->name, 
     80 		acptr->user->username, acptr->user->realhost),
     81 		sizeof uhost);
     82 
     83 	if (GetIP(acptr)) /* only if we actually have an IP */
     84 		strlcpy(ihost, make_nick_user_host(acptr->name,
     85 			acptr->user->username, GetIP(acptr)),
     86 			sizeof ihost);
     87 
     88 	/* The next could have been an IsSetHost(), but I'm playing it safe with regards to backward compat. */
     89 	if (IsHidden(acptr) &&
     90 	    !(*acptr->user->cloakedhost && !strcasecmp(acptr->user->virthost, acptr->user->cloakedhost))) 
     91 	{
     92 		strlcpy(vhost, make_nick_user_host(acptr->name,
     93 			acptr->user->username, acptr->user->virthost),
     94 			sizeof vhost);
     95 	}
     96 
     97 	if (*acptr->user->cloakedhost) /* only if we know the cloaked host */
     98 		strlcpy(chost, make_nick_user_host(acptr->name, 
     99 			acptr->user->username, acptr->user->cloakedhost),
    100 			sizeof chost);
    101 
    102 	/* SELECT BANLIST */
    103 
    104 	switch (chmode)
    105 	{
    106 		case 'b':
    107 			banlist = &channel->banlist;
    108 			break;
    109 		case 'e':
    110 			banlist = &channel->exlist;
    111 			break;
    112 		case 'I':
    113 			banlist = &channel->invexlist;
    114 			break;
    115 		default:
    116 			abort();
    117 	}
    118 
    119 	/* DO THE ACTUAL WORK */
    120 
    121 	b = safe_alloc(sizeof(BanContext));
    122 	b->client = acptr;
    123 	b->channel = channel;
    124 	b->ban_check_types = BANCHK_JOIN;
    125 
    126 	for (ban = *banlist; ban; ban = bnext)
    127 	{
    128 		bnext = ban->next;
    129 		if (match_simple(ban->banstr, uhost) ||
    130 		    (*vhost && match_simple(ban->banstr, vhost)) ||
    131 		    (*ihost && match_simple(ban->banstr, ihost)) ||
    132 		    (*chost && match_simple(ban->banstr, chost)))
    133 		{
    134 			add_send_mode_param(channel, client, '-',  chmode, ban->banstr);
    135 			del_listmode(banlist, channel, ban->banstr);
    136 		}
    137 		else if (chmode != 'I' && *ban->banstr == '~' && (extban = findmod_by_bantype(ban->banstr, &nextbanstr)))
    138 		{
    139 			if (extban->is_banned_events & b->ban_check_types)
    140 			{
    141 				b->banstr = nextbanstr;
    142 				if (extban->is_banned(b))
    143 				{
    144 					add_send_mode_param(channel, acptr, '-', chmode, ban->banstr);
    145 					del_listmode(banlist, channel, ban->banstr);
    146 				}
    147 			}
    148 		}
    149 	}
    150 	safe_free(b);
    151 }
    152 
    153 void clear_bans(Client *client, Channel *channel, char chmode)
    154 {
    155 	Extban *extban;
    156 	Ban *ban, *bnext;
    157 	Ban **banlist;
    158 
    159 	switch (chmode)
    160 	{
    161 		case 'b':
    162 			banlist = &channel->banlist;
    163 			break;
    164 		case 'e':
    165 			banlist = &channel->exlist;
    166 			break;
    167 		case 'I':
    168 			banlist = &channel->invexlist;
    169 			break;
    170 		default:
    171 			abort();
    172 	}
    173 	
    174 	for (ban = *banlist; ban; ban = bnext)
    175 	{
    176 		bnext = ban->next;
    177 		if (chmode != 'I' && (*ban->banstr == '~') && (extban = findmod_by_bantype(ban->banstr, NULL)))
    178 		{
    179 			if (!(extban->is_banned_events & BANCHK_JOIN))
    180 				continue;
    181 		}
    182 		add_send_mode_param(channel, client, '-',  chmode, ban->banstr);
    183 		del_listmode(banlist, channel, ban->banstr);
    184 	}
    185 }
    186 
    187 /** Special Channel MODE command for services, used by SVSMODE and SVS2MODE.
    188  * @note
    189  * This SVSMODE/SVS2MODE for channels is not simply the regular MODE "but for
    190  * services". No, it does different things.
    191  *
    192  *  Syntax: SVSMODE <channel> <mode and params>
    193  *
    194  * There are three variants (do NOT mix them!):
    195  * 1) SVSMODE #chan -[b|e|I]
    196  *    This will remove all bans/exempts/invex in the channel.
    197  * 2) SVSMODE #chan -[b|e|I] nickname
    198  *    This will remove all bans/exempts/invex that affect nickname.
    199  *    Eg: -b nick may remove bans places on IP, host, cloaked host, vhost,
    200  *    basically anything that matches this nickname. Note that the
    201  *    user with the specified nickname needs to be online for this to work.
    202  * 3) SVSMODE #chan -[v|h|o|a|q]
    203  *    This will remove the specified mode(s) from all users in the channel.
    204  *    Eg: -o will result in a MODE -o for every channel operator.
    205  *
    206  * OLD syntax had a 'ts' parameter. No services are known to use this.
    207  */
    208 void channel_svsmode(Client *client, int parc, const char *parv[]) 
    209 {
    210 	Channel *channel;
    211 	Client *target;
    212 	const char *m;
    213 	int what = MODE_ADD;
    214 	int i = 4; // wtf is this
    215 	Member *member;
    216 	int channel_flags;
    217 
    218 	*parabuf = *modebuf = '\0';
    219 
    220 	if ((parc < 3) || BadPtr(parv[2]))
    221 		return;
    222 
    223 	if (!(channel = find_channel(parv[1])))
    224 		return;
    225 
    226 	for (m = parv[2]; *m; m++)
    227 	{
    228 		if (*m == '+') 
    229 		{
    230 			what = MODE_ADD;
    231 		} else
    232 		if (*m == '-')
    233 		{
    234 			what = MODE_DEL;
    235 		} else
    236 		if ((*m == 'b') || (*m == 'e') || (*m == 'I'))
    237 		{
    238 			if (parc >= i)
    239 			{
    240 				if (!(target = find_user(parv[i-1], NULL)))
    241 				{
    242 					i++;
    243 					break;
    244 				}
    245 				i++;
    246 
    247 				unban_user(client, channel, target, *m);
    248 			}
    249 			else {
    250 				clear_bans(client, channel, *m);
    251 			}
    252 		} else
    253 		{
    254 			/* Find member mode handler (vhoaq) */
    255 			Cmode *cm = find_channel_mode_handler(*m);
    256 			if (!cm || (cm->type != CMODE_MEMBER))
    257 			{
    258 				unreal_log(ULOG_WARNING, "svsmode", "INVALID_SVSMODE", client,
    259 				           "Invalid SVSMODE for mode '$mode_character' in channel $channel from $client.",
    260 				           log_data_char("mode_character", *m),
    261 				           log_data_channel("channel", channel));
    262 				continue;
    263 			}
    264 			if (what != MODE_DEL)
    265 			{
    266 				unreal_log(ULOG_WARNING, "svsmode", "INVALID_SVSMODE", client,
    267 				           "Invalid SVSMODE from $client trying to add '$mode_character' in $channel.",
    268 				           log_data_char("mode_character", *m),
    269 				           log_data_channel("channel", channel));
    270 				continue;
    271 			}
    272 			for (member = channel->members; member; member = member->next)
    273 			{
    274 				if (check_channel_access_letter(member->member_modes, *m))
    275 				{
    276 					Membership *mb = find_membership_link(member->client->user->channel, channel);
    277 					if (!mb)
    278 						continue; /* bug */
    279 					
    280 					/* Send the -x out */
    281 					add_send_mode_param(channel, client, '-', *m, member->client->name);
    282 					
    283 					/* And remove from memory */
    284 					del_member_mode_fast(member, mb, *m);
    285 				}
    286 			}
    287 		}
    288 	}
    289 
    290 	/* only send message if modes have changed */
    291 	if (*parabuf)
    292 	{
    293 		MessageTag *mtags = NULL;
    294 		int destroy_channel = 0;
    295 		/* NOTE: cannot use 'recv_mtag' here because MODE could be rewrapped. Not ideal :( */
    296 		new_message(client, NULL, &mtags);
    297 
    298 		sendto_channel(channel, client, client, 0, 0, SEND_LOCAL, mtags,
    299 		               ":%s MODE %s %s %s",
    300 		               client->name, channel->name,  modebuf, parabuf);
    301 		sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s%s", client->id, channel->name, modebuf, parabuf, IsServer(client)?" 0":"");
    302 
    303 		/* Activate this hook just like cmd_mode.c */
    304 		RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0, &destroy_channel);
    305 
    306 		free_message_tags(mtags);
    307 
    308 		*parabuf = 0;
    309 	}
    310 }
    311 
    312 /** Special User MODE command for Services.
    313  * @note
    314  * This is used by both SVSMODE and SVS2MODE, when dealing with users (not channels).
    315  * parv[1] - nick to change mode for
    316  * parv[2] - modes to change
    317  * parv[3] - account name (if mode contains 'd')
    318  *
    319  * show_change can be 0 (for svsmode) or 1 (for svs2mode).
    320  */
    321 void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], int show_change)
    322 {
    323 	Umode *um;
    324 	const char *m;
    325 	Client *target;
    326 	int  what;
    327 	long oldumodes = 0;
    328 
    329 	if (!IsSvsCmdOk(client))
    330 		return;
    331 
    332 	what = MODE_ADD;
    333 
    334 	if (parc < 3)
    335 		return;
    336 
    337 	if (parv[1][0] == '#') 
    338 	{
    339 		channel_svsmode(client, parc, parv);
    340 		return;
    341 	}
    342 
    343 	if (!(target = find_user(parv[1], NULL)))
    344 		return;
    345 
    346 	userhost_save_current(target);
    347 
    348 	oldumodes = target->umodes;
    349 
    350 	/* parse mode change string(s) */
    351 	for (m = parv[2]; *m; m++)
    352 		switch (*m)
    353 		{
    354 			case '+':
    355 				what = MODE_ADD;
    356 				break;
    357 			case '-':
    358 				what = MODE_DEL;
    359 				break;
    360 
    361 			/* we may not get these, but they shouldnt be in default */
    362 			case ' ':
    363 			case '\n':
    364 			case '\r':
    365 			case '\t':
    366 				break;
    367 			case 'i':
    368 				if ((what == MODE_ADD) && !(target->umodes & UMODE_INVISIBLE))
    369 					irccounts.invisible++;
    370 				if ((what == MODE_DEL) && (target->umodes & UMODE_INVISIBLE))
    371 					irccounts.invisible--;
    372 				goto setmodex;
    373 			case 'o':
    374 				if ((what == MODE_ADD) && !(target->umodes & UMODE_OPER))
    375 				{
    376 					if (!IsOper(target) && MyUser(target))
    377 						list_add(&target->special_node, &oper_list);
    378 
    379 					irccounts.operators++;
    380 				}
    381 				if ((what == MODE_DEL) && (target->umodes & UMODE_OPER))
    382 				{
    383 					if (target->umodes & UMODE_HIDEOPER)
    384 					{
    385 						/* clear 'H' too, and opercount stays the same.. */
    386 						target->umodes &= ~UMODE_HIDEOPER;
    387 					} else {
    388 						irccounts.operators--;
    389 					}
    390 
    391 					if (MyUser(target) && !list_empty(&target->special_node))
    392 						list_del(&target->special_node);
    393 					
    394 					/* User is no longer oper (after the goto below, anyway)...
    395 					 * so remove all oper-only modes and snomasks.
    396 					 */
    397 					if (MyUser(client))
    398 						RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL);
    399 					remove_oper_privileges(target, 0);
    400 				}
    401 				goto setmodex;
    402 			case 'H':
    403 				if (what == MODE_ADD && !(target->umodes & UMODE_HIDEOPER))
    404 				{
    405 					if (!IsOper(target) && !strchr(parv[2], 'o')) /* (ofcoz this strchr() is flawed) */
    406 					{
    407 						/* isn't an oper, and would not become one either.. abort! */
    408 						unreal_log(ULOG_WARNING, "svsmode", "SVSMODE_INVALID", client,
    409 						           "[BUG] Server $client tried to set user mode +H (hidden ircop) "
    410 						           "on a user that is not +o (not ircop)! "
    411 						           "Please fix your services, or if you think it is our fault, then "
    412 						           "report at https://bugs.unrealircd.org/. "
    413 						           "Parameters: $para1 $para2. Target: $target.",
    414 						           log_data_string("para1", parv[1]),
    415 						           log_data_string("para2", parv[2]),
    416 						           log_data_client("target", target));
    417 						break; /* abort! */
    418 					}
    419 					irccounts.operators--;
    420 				}
    421 				if (what == MODE_DEL && (target->umodes & UMODE_HIDEOPER))
    422 					irccounts.operators++;
    423 				goto setmodex;
    424 			case 'd':
    425 				if (parv[3])
    426 				{
    427 					int was_logged_in = IsLoggedIn(target) ? 1 : 0;
    428 					strlcpy(target->user->account, parv[3], sizeof(target->user->account));
    429 					if (!was_logged_in && !IsLoggedIn(target))
    430 					{
    431 						/* We don't care about users going from not logged in
    432 						 * to not logged in, which is something that can happen
    433 						 * from 0 to 123456, eg from no account to unconfirmed account.
    434 						 */
    435 					} else {
    436 						/* LOGIN or LOGOUT (or account change) */
    437 						user_account_login(recv_mtags, target);
    438 					}
    439 					if (MyConnect(target) && IsDead(target))
    440 						return; /* was killed due to *LINE on ~a probably */
    441 				}
    442 				else
    443 				{
    444 					/* setting deaf */
    445 					goto setmodex;
    446 				}
    447 				break;
    448 			case 'x':
    449 				if (what == MODE_DEL)
    450 				{
    451 					/* -x */
    452 					if (target->user->virthost)
    453 					{
    454 						/* Removing mode +x and virthost set... recalculate host then (but don't activate it!) */
    455 						safe_strdup(target->user->virthost, target->user->cloakedhost);
    456 					}
    457 				} else
    458 				{
    459 					/* +x */
    460 					if (!target->user->virthost)
    461 					{
    462 						/* Hmm... +x but no virthost set, that's bad... use cloakedhost.
    463 						 * Not sure if this could ever happen, but just in case... -- Syzop
    464 						 */
    465 						safe_strdup(target->user->virthost, target->user->cloakedhost);
    466 					}
    467 					/* Announce the new host to VHP servers if we're setting the virthost to the cloakedhost.
    468 					 * In other cases, we can assume that the host has been broadcasted already (after all,
    469 					 * how else could it have been changed...?).
    470 					 * NOTES: we're doing a strcasecmp here instead of simply checking if it's a "+x but
    471 					 * not -t"-case. The reason for this is that the 't' might follow ("+xt" instead of "+tx"),
    472 					 * in which case we would have needlessly announced it. Ok I didn't test it but that's
    473 					 * the idea behind it :P. -- Syzop
    474 					 */
    475 					if (MyUser(target) && !strcasecmp(target->user->virthost, target->user->cloakedhost))
    476 						sendto_server(NULL, PROTO_VHP, 0, NULL, ":%s SETHOST :%s", target->id,
    477 							target->user->virthost);
    478 				}
    479 				goto setmodex;
    480 			case 't':
    481 				/* We support -t nowadays, which means we remove the vhost and set the cloaked host
    482 				 * (note that +t is a NOOP, that code is in +x)
    483 				 */
    484 				if (what == MODE_DEL)
    485 				{
    486 					/* First, check if there's a change at all. Perhaps it's a -t on someone
    487 					 * with no virthost or virthost being cloakedhost?
    488 					 * Also, check to make sure user->cloakedhost exists at all.
    489 					 * This so we won't crash in weird cases like non-conformant servers.
    490 					 */
    491 					if (target->user->virthost && *target->user->cloakedhost && strcasecmp(target->user->cloakedhost, GetHost(target)))
    492 					{
    493 						/* Make the change effective: */
    494 						safe_strdup(target->user->virthost, target->user->cloakedhost);
    495 						/* And broadcast the change to VHP servers */
    496 						if (MyUser(target))
    497 							sendto_server(NULL, PROTO_VHP, 0, NULL, ":%s SETHOST :%s", target->id,
    498 								target->user->virthost);
    499 					}
    500 					goto setmodex;
    501 				}
    502 				break;
    503 			case 'z':
    504 				/* Setting and unsetting user mode 'z' remotely is not supported */
    505 				break;
    506 			default:
    507 				setmodex:
    508 				for (um = usermodes; um; um = um->next)
    509 				{
    510 					if (um->letter == *m)
    511 					{
    512 						if (what == MODE_ADD)
    513 							target->umodes |= um->mode;
    514 						else
    515 							target->umodes &= ~um->mode;
    516 						break;
    517 					}
    518 				}
    519 				break;
    520 		} /*switch*/
    521 
    522 	if (parc > 3)
    523 		sendto_server(client, 0, 0, recv_mtags, ":%s %s %s %s %s",
    524 		    client->id, show_change ? "SVS2MODE" : "SVSMODE",
    525 		    parv[1], parv[2], parv[3]);
    526 	else
    527 		sendto_server(client, 0, 0, recv_mtags, ":%s %s %s %s",
    528 		    client->id, show_change ? "SVS2MODE" : "SVSMODE",
    529 		    parv[1], parv[2]);
    530 
    531 	/* Here we trigger the same hooks that cmd_mode does and, likewise,
    532 	   only if the old flags (oldumodes) are different than the newly-
    533 	   set ones */
    534 	if (oldumodes != target->umodes)
    535 		RunHook(HOOKTYPE_UMODE_CHANGE, target, oldumodes, target->umodes);
    536 
    537 	if (show_change)
    538 	{
    539 		char buf[BUFSIZE];
    540 		build_umode_string(target, oldumodes, ALL_UMODES, buf);
    541 		if (MyUser(target) && *buf)
    542 		{
    543 			MessageTag *mtags = NULL;
    544 			new_message(client, recv_mtags, &mtags);
    545 			sendto_one(target, mtags, ":%s MODE %s :%s", client->name, target->name, buf);
    546 			safe_free_message_tags(mtags);
    547 		}
    548 	}
    549 
    550 	userhost_changed(target); /* we can safely call this, even if nothing changed */
    551 
    552 	VERIFY_OPERCOUNT(target, "svsmodeX");
    553 }
    554 
    555 /*
    556  * cmd_svsmode() added by taz
    557  * parv[1] - username to change mode for
    558  * parv[2] - modes to change
    559  * parv[3] - account name (if mode contains 'd')
    560  */
    561 CMD_FUNC(cmd_svsmode)
    562 {
    563 	do_svsmode(client, recv_mtags, parc, parv, 0);
    564 }
    565 
    566 /*
    567  * cmd_svs2mode() added by Potvin
    568  * parv[1] - username to change mode for
    569  * parv[2] - modes to change
    570  * parv[3] - account name (if mode contains 'd')
    571  */
    572 CMD_FUNC(cmd_svs2mode)
    573 {
    574 	do_svsmode(client, recv_mtags, parc, parv, 1);
    575 }
    576 
    577 void add_send_mode_param(Channel *channel, Client *from, char what, char mode, char *param)
    578 {
    579 	static char *modes = NULL, lastwhat;
    580 	static short count = 0;
    581 	short send = 0;
    582 	
    583 	if (!modes) modes = modebuf;
    584 	
    585 	if (!modebuf[0])
    586 	{
    587 		modes = modebuf;
    588 		*modes++ = what;
    589 		*modes = 0;
    590 		lastwhat = what;
    591 		*parabuf = 0;
    592 		count = 0;
    593 	}
    594 	if (lastwhat != what)
    595 	{
    596 		*modes++ = what;
    597 		*modes = 0;
    598 		lastwhat = what;
    599 	}
    600 	if (strlen(parabuf) + strlen(param) + 11 < MODEBUFLEN)
    601 	{
    602 		if (*parabuf) 
    603 			strcat(parabuf, " ");
    604 		strcat(parabuf, param);
    605 		*modes++ = mode;
    606 		*modes = 0;
    607 		count++;
    608 	}
    609 	else if (*parabuf) 
    610 		send = 1;
    611 
    612 	if (count == MAXMODEPARAMS)
    613 		send = 1;
    614 
    615 	if (send)
    616 	{
    617 		MessageTag *mtags = NULL;
    618 		/* NOTE: cannot use 'recv_mtag' here because MODE could be rewrapped. Not ideal :( */
    619 		new_message(from, NULL, &mtags);
    620 		sendto_channel(channel, from, from, 0, 0, SEND_LOCAL, mtags,
    621 		               ":%s MODE %s %s %s",
    622 		               from->name, channel->name, modebuf, parabuf);
    623 		sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s%s", from->id, channel->name, modebuf, parabuf, IsServer(from)?" 0":"");
    624 		free_message_tags(mtags);
    625 		send = 0;
    626 		*parabuf = 0;
    627 		modes = modebuf;
    628 		*modes++ = what;
    629 		lastwhat = what;
    630 		if (count != MAXMODEPARAMS)
    631 		{
    632 			strcpy(parabuf, param);
    633 			*modes++ = mode;
    634 			count = 1;
    635 		}
    636 		else 
    637 			count = 0;
    638 		*modes = 0;
    639 	}
    640 }