*** ../irc2.8.21/ircd/channel.c	Sun Nov  6 11:02:38 1994
--- ircd/channel.c	Fri Jun 13 10:49:11 1997
***************
*** 59,64 ****
--- 59,75 ----
  void	clean_channelname PROTO((char *));
  void	del_invite PROTO((aClient *, aChannel *));
  
+ #ifdef ORATIMING
+ struct timeval tsdnow, tsdthen; 
+ unsigned long tsdms;
+ #endif
+ 
+ /*
+ ** number of seconds to add to all readings of time() when making TS's
+ ** -orabidoo
+ */
+ ts_val	timedelta = 0;
+ 
  static	char	*PartFmt = ":%s PART %s";
  /*
   * some buffers for rebuilding channel/nick lists with ,'s
***************
*** 303,313 ****
--- 314,339 ----
  
  	if ((tmp = find_user_link(chptr->members, lp->value.cptr)))
  		if (lp->flags & MODE_ADD)
+ 		    {
  			tmp->flags |= lp->flags & MODE_FLAGS;
+ 			if (lp->flags & MODE_CHANOP)
+ 				tmp->flags &= ~MODE_DEOPPED;
+ 		    }
  		else
  			tmp->flags &= ~lp->flags & MODE_FLAGS;
  }
  
+ static	void	set_deopped(lp, chptr)
+ Link	*lp;
+ aChannel *chptr;
+ {
+ 	Reg1	Link	*tmp;
+ 
+ 	if ((tmp = find_user_link(chptr->members, lp->value.cptr)))
+ 		if ((tmp->flags & MODE_CHANOP) == 0)
+ 			tmp->flags |= MODE_DEOPPED;
+ }
+ 
  int	is_chan_op(cptr, chptr)
  aClient *cptr;
  aChannel *chptr;
***************
*** 321,326 ****
--- 347,365 ----
  	return 0;
  }
  
+ int	is_deopped(cptr, chptr)
+ aClient *cptr;
+ aChannel *chptr;
+ {
+ 	Reg1	Link	*lp;
+ 
+ 	if (chptr)
+ 		if ((lp = find_user_link(chptr->members, cptr)))
+ 			return (lp->flags & CHFL_DEOPPED);
+ 
+ 	return 0;
+ }
+ 
  int	has_voice(cptr, chptr)
  aClient *cptr;
  aChannel *chptr;
***************
*** 462,487 ****
  	*modebuf = *parabuf = '\0';
  	channel_modes(cptr, modebuf, parabuf, chptr);
  
! 	send_mode_list(cptr, chptr->chname, chptr->members, CHFL_CHANOP, 'o');
! 	if (modebuf[1] || *parabuf)
! 		sendto_one(cptr, ":%s MODE %s %s %s",
! 			   me.name, chptr->chname, modebuf, parabuf);
  
! 	*parabuf = '\0';
! 	*modebuf = '+';
! 	modebuf[1] = '\0';
! 	send_mode_list(cptr, chptr->chname, chptr->banlist, CHFL_BAN, 'b');
! 	if (modebuf[1] || *parabuf)
! 		sendto_one(cptr, ":%s MODE %s %s %s",
! 			   me.name, chptr->chname, modebuf, parabuf);
  
! 	*parabuf = '\0';
! 	*modebuf = '+';
! 	modebuf[1] = '\0';
! 	send_mode_list(cptr, chptr->chname, chptr->members, CHFL_VOICE, 'v');
! 	if (modebuf[1] || *parabuf)
! 		sendto_one(cptr, ":%s MODE %s %s %s",
! 			   me.name, chptr->chname, modebuf, parabuf);
  }
  
  /*
--- 501,607 ----
  	*modebuf = *parabuf = '\0';
  	channel_modes(cptr, modebuf, parabuf, chptr);
  
! 	if (DoesTS(cptr))
! 	    {
! 		Link	*l, *anop = NULL, *skip = NULL;
! 		int	n = 0;
! 		char	*t;
! 
! 		if (*parabuf)
! 			strcat(parabuf, " ");
! 		sprintf(buf, ":%s SJOIN %ld %s %s %s:", me.name,
! 			chptr->channelts, chptr->chname, modebuf, parabuf);
! 		t = buf + strlen(buf);
! 		for (l = chptr->members; l && l->value.cptr; l = l->next)
! 			if (l->flags & MODE_CHANOP)
! 			    {
! 				anop = l;
! 				break;
! 			    }
! 		/* follow the channel, but doing anop first if it's defined
! 		**  -orabidoo
! 		*/
! 		l = NULL;
! 		for (;;)
! 		    {
! 			if (anop)
! 			    {
! 				l = skip = anop;
! 				anop = NULL;
! 			    }
! 			else 
! 			    {
! 				if (l == NULL || l == skip)
! 					l = chptr->members;
! 				else
! 					l = l->next;
! 				if (l && l == skip)
! 					l = l->next;
! 				if (l == NULL)
! 					break;
! 			    }
! 			if (l->flags & MODE_CHANOP)
! 				*t++ = '@';
! 			if (l->flags & MODE_VOICE)
! 				*t++ = '+';
! 			strcpy(t, l->value.cptr->name);
! 			t += strlen(t);
! 			*t++ = ' ';
! 			n++;
! 			if (t - buf > BUFSIZE - 80)
! 			    {
! 				*t++ = '\0';
! 				if (t[-1] == ' ') t[-1] = '\0';
! 				sendto_one(cptr, "%s", buf);
! 				sprintf(buf, ":%s SJOIN %ld %s 0 :",
! 					me.name, chptr->channelts,
! 					chptr->chname);
! 				t = buf + strlen(buf);
! 				n = 0;
! 			    }
! 		    }
! 
! 		if (n)
! 		    {
! 			*t++ = '\0';
! 			if (t[-1] == ' ') t[-1] = '\0';
! 			sendto_one(cptr, "%s", buf);
! 		    }
! 		*parabuf = '\0';
! 		*modebuf = '+';
! 		modebuf[1] = '\0';
! 		send_mode_list(cptr, chptr->chname, chptr->banlist, CHFL_BAN,
! 				'b');
! 		if (modebuf[1] || *parabuf)
! 			sendto_one(cptr, ":%s MODE %s %s %s",
! 				   me.name, chptr->chname, modebuf, parabuf);
! 	    }
! 	else
! 	    {
! 		send_mode_list(cptr, chptr->chname, chptr->members, 
! 				CHFL_CHANOP, 'o');
! 		if (modebuf[1] || *parabuf)
! 			sendto_one(cptr, ":%s MODE %s %s %s",
! 				   me.name, chptr->chname, modebuf, parabuf);
  
! 		*parabuf = '\0';
! 		*modebuf = '+';
! 		modebuf[1] = '\0';
! 		send_mode_list(cptr, chptr->chname, chptr->banlist, CHFL_BAN,
! 				'b');
! 		if (modebuf[1] || *parabuf)
! 			sendto_one(cptr, ":%s MODE %s %s %s",
! 				   me.name, chptr->chname, modebuf, parabuf);
  
! 		*parabuf = '\0';
! 		*modebuf = '+';
! 		modebuf[1] = '\0';
! 		send_mode_list(cptr, chptr->chname, chptr->members, CHFL_VOICE,
! 				'v');
! 		if (modebuf[1] || *parabuf)
! 			sendto_one(cptr, ":%s MODE %s %s %s",
! 				   me.name, chptr->chname, modebuf, parabuf);
! 	   }
  }
  
  /*
***************
*** 528,561 ****
  		channel_modes(sptr, modebuf, parabuf, chptr);
  		sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
  			   chptr->chname, modebuf, parabuf);
  		return 0;
  	    }
  	mcount = set_mode(cptr, sptr, chptr, parc - 2, parv + 2,
  			  modebuf, parabuf);
  
- 	if ((mcount < 0) && MyConnect(sptr) && !IsServer(sptr))
- 	    {
- 		sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
- 			   me.name, parv[0], chptr->chname);
- 		return 0;
- 	    }
  	if (strlen(modebuf) > (size_t)1)
! 	    {
! 		if ((IsServer(cptr) && !IsServer(sptr) && !chanop) ||
! 		    (mcount < 0))
  		    {
! 			sendto_ops("Fake: %s MODE %s %s %s",
! 				   parv[0], parv[1], modebuf, parabuf);
! 			ircstp->is_fake++;
! 		    }
! 		else
! 			sendto_channel_butserv(chptr, sptr,
  						":%s MODE %s %s %s", parv[0],
  						chptr->chname, modebuf,
  						parabuf);
! 		sendto_match_servs(chptr, cptr, ":%s MODE %s %s %s",
! 				   parv[0], chptr->chname, modebuf, parabuf);
! 	    }
  	return 0;
  }
  
--- 648,687 ----
  		channel_modes(sptr, modebuf, parabuf, chptr);
  		sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
  			   chptr->chname, modebuf, parabuf);
+ 		sendto_one(sptr, rpl_str(RPL_CREATIONTIME), me.name, parv[0],
+ 			   chptr->chname, chptr->channelts);
  		return 0;
  	    }
  	mcount = set_mode(cptr, sptr, chptr, parc - 2, parv + 2,
  			  modebuf, parabuf);
  
  	if (strlen(modebuf) > (size_t)1)
! 		switch (mcount)
  		    {
! 			case 0:
! 			    break;
! 			case -1:
! 			    if (MyClient(sptr))
! 				    sendto_one(sptr, 
! 					       err_str(ERR_CHANOPRIVSNEEDED),
! 					       me.name, parv[0], chptr->chname);
! 			    else
! 				{
! 				    sendto_ops("Fake: %s MODE %s %s %s",
! 					    parv[0], parv[1], modebuf, parabuf);
! 				    ircstp->is_fake++;
! 				}
! 			    break;
! 			default:
! 			    sendto_channel_butserv(chptr, sptr,
  						":%s MODE %s %s %s", parv[0],
  						chptr->chname, modebuf,
  						parabuf);
! 			    sendto_match_servs(chptr, cptr,
! 			    			":%s MODE %s %s %s",
! 				   		parv[0], chptr->chname,
! 						modebuf, parabuf);
! 		    }
  	return 0;
  }
  
***************
*** 575,581 ****
  				MODE_PRIVATE,    'p', MODE_SECRET,     's',
  				MODE_MODERATED,  'm', MODE_NOPRIVMSGS, 'n',
  				MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i',
- 				MODE_VOICE,      'v', MODE_KEY,        'k',
  				0x0, 0x0 };
  
  	Reg1	Link	*lp;
--- 701,706 ----
***************
*** 583,589 ****
  	Reg3	int	*ip;
  	u_int	whatt = MODE_ADD;
  	int	limitset = 0, count = 0, chasing = 0;
! 	int	nusers, ischop, new, len, keychange = 0, opcnt = 0;
  	char	fm = '\0';
  	aClient *who;
  	Mode	*mode, oldm;
--- 708,715 ----
  	Reg3	int	*ip;
  	u_int	whatt = MODE_ADD;
  	int	limitset = 0, count = 0, chasing = 0;
! 	int	nusers, ischop, isok, isdeop, new, len;
! 	int	keychange = 0, opcnt = 0;
  	char	fm = '\0';
  	aClient *who;
  	Mode	*mode, oldm;
***************
*** 595,600 ****
--- 721,729 ----
  	mode = &(chptr->mode);
  	bcopy((char *)mode, (char *)&oldm, sizeof(Mode));
  	ischop = IsServer(sptr) || is_chan_op(sptr, chptr);
+ 	isdeop = !ischop && !IsServer(sptr) && is_deopped(sptr, chptr);
+ 	isok = ischop || (!isdeop && IsServer(cptr) && DoesTS(cptr) && 
+ 	       chptr->channelts);
  	new = mode->mode;
  
  	while (curr && *curr && count >= 0)
***************
*** 636,646 ****
  			** just some extra messages if nick appeared more than
  			** once in the MODE message... --msa
  			*/
! 			if (chasing && ischop)
! 				sendto_one(cptr, ":%s MODE %s %c%c %s",
! 					   me.name, chptr->chname,
! 					   whatt == MODE_ADD ? '+' : '-',
! 					   *curr, who->name);
  
  			if (who == cptr && whatt == MODE_ADD && *curr == 'o')
  				break;
--- 765,778 ----
  			** just some extra messages if nick appeared more than
  			** once in the MODE message... --msa
  			*/
! 			/*
! 			** This is actually useless - if we notice a nick
! 			** change it means the server the mode comes from       
! 			** dealt with the mode with the old nick, and already
! 			** applied it, and the nick change must be coming
! 			** from another server; besides, we don't want to
! 			** be resetting the channel's TS -orabidoo
! 			*/
  
  			if (who == cptr && whatt == MODE_ADD && *curr == 'o')
  				break;
***************
*** 679,688 ****
  				break;
  			*parv = check_string(*parv);
  			{
! 				u_char	*s;
  
! 				for (s = (u_char *)*parv; *s; s++)
  					*s &= 0x7f;
  			}
  			if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
  				break;
--- 811,833 ----
  				break;
  			*parv = check_string(*parv);
  			{
! 				u_char	*s, *t;
! 				int abuse = 0;
  
! 				for (s = t = (u_char *)*parv; *s; s++)
! 				    {
! 					if (*s > 0x7f && *s <= 0xa0)
! 						abuse++;
  					*s &= 0x7f;
+ 					if (*s > (u_char)' ' && *s != ':')
+ 						*t++ = *s;
+ 				    }
+ 				*t = '\0';
+ 				if (abuse)
+ 				sendto_ops("User %s trying to abuse +k bug", 
+ 					   sptr->name);
+ 				if (t == (u_char *)*parv)
+ 					break;
  			}
  			if (MyClient(sptr) && opcnt >= MAXMODEPARAMS)
  				break;
***************
*** 766,771 ****
--- 911,917 ----
  			    {
  				limitset = 1;
  				nusers = 0;
+ 				count++;
  				break;
  			    }
  			if (--parc > 0)
***************
*** 827,832 ****
--- 973,996 ----
  
  	whatt = 0;
  
+ 	/* if it's a non-deopped non-op on a TS channel from a TS
+ 	** server, allow only ops and deops and ignore everything
+ 	** else (to avoid having desynched ops) -orabidoo
+ 	*/
+ 	if (!ischop && isok)
+ 	    {
+ 		Reg1	int 	i;
+ 		Reg2	int	j;
+ 
+ 		new = oldm.mode;
+ 		for (i = 0, j = 0; i < opcnt; i++)
+ 			if (chops[i].flags & (MODE_CHANOP|MODE_VOICE))
+ 				chops[j++] = chops[i];
+ 			else
+ 				count--;
+ 		opcnt = j;
+ 	    }
+ 
  	for (ip = flags; *ip; ip += 2)
  		if ((*ip & new) && !(*ip & oldm.mode))
  		    {
***************
*** 974,981 ****
  				len += strlen(cp);
  				(void)strcat(pbuf, " ");
  				len++;
! 				if (ischop)
  					change_chan_flag(lp, chptr);
  				break;
  			case MODE_BAN :
  				if (ischop && ((whatt & MODE_ADD) &&
--- 1138,1157 ----
  				len += strlen(cp);
  				(void)strcat(pbuf, " ");
  				len++;
! 				if (isok)
  					change_chan_flag(lp, chptr);
+ 				if (IsServer(sptr) && c == 'o' && 
+ 				    whatt == MODE_ADD)
+ 				    {
+ 					chptr->channelts = 0;
+ #ifdef TS_WARNINGS
+ 	ts_warn("Server %s setting +o and blasting TS on %s", sptr->name,
+ 					chptr->chname);
+ #endif
+ 				    }
+ 				if (c == 'o' && whatt == MODE_ADD && isdeop &&
+ 				    !is_chan_op(lp->value.cptr, chptr))
+ 					set_deopped(lp, chptr);
  				break;
  			case MODE_BAN :
  				if (ischop && ((whatt & MODE_ADD) &&
***************
*** 996,1002 ****
  
  	*mbuf++ = '\0';
  
! 	return ischop ? count : -count;
  }
  
  static	int	can_join(sptr, chptr, key)
--- 1172,1190 ----
  
  	*mbuf++ = '\0';
  
! 	/* returns:
! 	** -1  = mode changes were generated but ignored, and a FAKE
! 	**       must be sent for remote clients and a CHANOPRIVSNEEDED
! 	**       for local ones
! 	**  0  = ignore whatever was generated, do not propagate anything
! 	** >0  = modes were accepted, propagate
! 	*/
! 	if (isok)
! 		return count;
! 	if (isdeop && !MyClient(sptr))
! 		return 0;
! 	else
! 		return -1;
  }
  
  static	int	can_join(sptr, chptr, key)
***************
*** 1304,1309 ****
--- 1492,1509 ----
  				return 0;
  			    }
  		    }
+ 		else
+ 		    {
+ #ifdef TS_WARNINGS
+ 			/*
+ 			** complain for remote JOINs to existing channels
+ 			** (they should be SJOINs) -orabidoo
+ 			*/
+ 			if (!ChannelExists(name) && sptr->user)
+ 		ts_warn("User on %s remotely JOINing new channel", 
+ 				sptr->user->server);
+ #endif
+ 		    }
  
  		chptr = get_channel(sptr, name, CREATE);
  
***************
*** 1323,1341 ****
  		*/
  		add_user_to_channel(chptr, sptr, flags);
  		/*
  		** notify all other users on the new channel
  		*/
  		sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s",
  					parv[0], name);
- 		sendto_match_servs(chptr, cptr, ":%s JOIN :%s", parv[0], name);
  
  		if (MyClient(sptr))
  		    {
  			del_invite(sptr, chptr);
  			if (flags == CHFL_CHANOP)
! 				sendto_match_servs(chptr, cptr,
  						   ":%s MODE %s +o %s",
  						   me.name, name, parv[0]);
  			if (chptr->topic[0] != '\0')
  				sendto_one(sptr, rpl_str(RPL_TOPIC), me.name,
  					   parv[0], name, chptr->topic);
--- 1523,1579 ----
  		*/
  		add_user_to_channel(chptr, sptr, flags);
  		/*
+ 		**  Set timestamp if appropriate, and propagate
+ 		*/
+ 		if (MyClient(sptr) && flags == CHFL_CHANOP)
+ 		    {
+ 			chptr->channelts = time(NULL) + timedelta;
+ #ifdef TS_ONLY
+ 			sendto_match_servs(chptr, cptr,
+ 				":%s SJOIN %ld %s + :@%s", me.name, 
+ 				chptr->channelts, name, parv[0]);
+ #else
+ 			sendto_match_TS_servs(0, chptr, cptr, ":%s JOIN :%s",
+ 						parv[0], name);
+ 			sendto_match_TS_servs(1, chptr, cptr,
+ 				":%s SJOIN %ld %s + :@%s", me.name, 
+ 				chptr->channelts, name, parv[0]);
+ #endif
+ 		    }
+ 		else if (MyClient(sptr))
+ 		    {
+ #ifdef TS_ONLY
+ 			sendto_match_servs(chptr, cptr, 
+ 				":%s SJOIN %ld %s + :%s", me.name, 
+ 				chptr->channelts, name, parv[0]);
+ #else
+ 			sendto_match_TS_servs(0, chptr, cptr, ":%s JOIN :%s", 
+ 						parv[0], name);
+ 			sendto_match_TS_servs(1, chptr, cptr, 
+ 				":%s SJOIN %ld %s + :%s", me.name, 
+ 				chptr->channelts, name, parv[0]);
+ #endif
+ 		    }
+ 		else
+ 		    sendto_match_servs(chptr, cptr, ":%s JOIN :%s", parv[0], 
+ 					name);
+ 
+ 			
+ 		/*
  		** notify all other users on the new channel
  		*/
  		sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s",
  					parv[0], name);
  
  		if (MyClient(sptr))
  		    {
  			del_invite(sptr, chptr);
+ #ifndef TS_ONLY
  			if (flags == CHFL_CHANOP)
! 				sendto_match_TS_servs(0, chptr, cptr,
  						   ":%s MODE %s +o %s",
  						   me.name, name, parv[0]);
+ #endif
  			if (chptr->topic[0] != '\0')
  				sendto_one(sptr, rpl_str(RPL_TOPIC), me.name,
  					   parv[0], name, chptr->topic);
***************
*** 1462,1468 ****
  		    }
  		if (check_channelmask(sptr, cptr, name))
  			continue;
! 		if (!IsServer(sptr) && !is_chan_op(sptr, chptr))
  		    {
  			sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
  				   me.name, parv[0], chptr->chname);
--- 1700,1707 ----
  		    }
  		if (check_channelmask(sptr, cptr, name))
  			continue;
! 		if (!IsServer(sptr) && !is_chan_op(sptr, chptr) &&
! 		    (MyConnect(sptr) || !DoesTS(cptr) || !chptr->channelts))
  		    {
  			sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
  				   me.name, parv[0], chptr->chname);
***************
*** 1966,1968 ****
--- 2205,2674 ----
  
  	return;
  }
+ 
+ static	void sjoin_sendit(cptr, sptr, chptr, from)
+ aClient *cptr;
+ aClient *sptr;
+ aChannel *chptr;
+ char	*from;
+ {
+ 	sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", from,
+ 				chptr->chname, modebuf, parabuf);
+ #ifndef TS_ONLY
+ 	sendto_match_TS_servs(0, chptr, cptr, ":%s MODE %s %s %s",
+ 				from, chptr->chname, modebuf, parabuf);
+ #endif
+ }
+ 
+ /*
+  * m_sjoin
+  * parv[0] - sender
+  * parv[1] - TS
+  * parv[2] - channel
+  * parv[3] - modes + n arguments (key and/or limit)
+  * parv[4+n] - flags+nick list (all in one parameter)
+ 
+  * 
+  * process a SJOIN, taking the TS's into account to either ignore the
+  * incomign modes or undo the existing ones or merge them, and JOIN
+  * all the specified users while sending JOIN/MODEs to non-TS servers
+  * and to clients
+  */
+ 
+ int	m_sjoin(cptr, sptr, parc, parv)
+ aClient *cptr;
+ aClient *sptr;
+ int	parc;
+ char	*parv[];
+ {
+ 	aChannel *chptr;
+ 	aClient	*acptr;
+ 	ts_val	newts, oldts, tstosend;
+ 	static	Mode mode, *oldmode;
+ 	Link	*l;
+ 	int	args = 0, haveops = 0, keepourmodes = 1, keepnewmodes = 1,
+ 		doesop = 0, what = 0, pargs = 0, *ip, fl, people = 0, isnew;
+ 	Reg1	char *s, *s0;
+ 	static	char numeric[16], sjbuf[BUFSIZE];
+ 	char	*mbuf = modebuf, *t = sjbuf, *p;
+ 
+ 	static	int	flags[] = {
+ 				MODE_PRIVATE,    'p', MODE_SECRET,     's',
+ 				MODE_MODERATED,  'm', MODE_NOPRIVMSGS, 'n',
+ 				MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i',
+ 				0x0, 0x0 };
+ 
+ 	if (check_registered(sptr) || IsClient(sptr) || parc < 5)
+ 		return 0;
+ 	if (!IsChannelName(parv[2]) ||
+ 	    check_channelmask(sptr, cptr, parv[2]) == -1)
+ 		return 0;
+ 	newts = atol(parv[1]);
+ 	bzero((char *)&mode, sizeof(mode));
+ 
+ 	s = parv[3];
+ 	while (*s)
+ 		switch(*(s++))
+ 		    {
+ 		    case 'i':
+ 			mode.mode |= MODE_INVITEONLY;
+ 			break;
+ 		    case 'n':
+ 			mode.mode |= MODE_NOPRIVMSGS;
+ 			break;
+ 		    case 'p':
+ 			mode.mode |= MODE_PRIVATE;
+ 			break;
+ 		    case 's':
+ 			mode.mode |= MODE_SECRET;
+ 			break;
+ 		    case 'm':
+ 			mode.mode |= MODE_MODERATED;
+ 			break;
+ 		    case 't':
+ 			mode.mode |= MODE_TOPICLIMIT;
+ 			break;
+ 		    case 'k':
+ 			strncpyzt(mode.key, parv[4+args], KEYLEN+1);
+ 			args++;
+ 			if (parc < 5+args) return 0;
+ 			break;
+ 		    case 'l':
+ 			mode.limit = atoi(parv[4+args]);
+ 			args++;
+ 			if (parc < 5+args) return 0;
+ 			break;
+ 		    }
+ 
+ 	*parabuf = '\0';
+ 
+ 	isnew = ChannelExists(parv[2]) ? 0 : 1;
+ 	chptr = get_channel(sptr, parv[2], CREATE);
+ 	oldts = chptr->channelts;
+ 
+ 	/* we only look if any ops are introduced if we'll actually need
+ 	 * to use the info
+ 	 */
+ 	if (!isnew && newts > 0 && oldts > 0 && newts != oldts)
+ 	    {
+ 		static char nicks[BUFSIZE];
+ 
+ 		strcpy(nicks, parv[4+args]);
+ 		for (s=strtoken(&p, nicks, " "); s; s=strtoken(&p, NULL, " "))
+ 			if (*s == '@' || s[1] == '@')
+ 			    {
+ 				while (*s == '@' || *s == '+')
+ 					s++;
+ 				if (!(acptr = find_chasing(sptr, s, NULL)))
+ 					continue;
+ 				if (acptr->from != cptr)
+ 					continue;
+ 			    	doesop = 1;
+ 				break;
+ 			    }
+ 	    }
+ 
+ 	for (l = chptr->members; l && l->value.cptr; l = l->next)
+ 		if (l->flags & MODE_CHANOP)
+ 		    {
+ 			haveops++;
+ 			break;
+ 		    }
+ 
+ 	oldmode = &chptr->mode;
+ 
+ 	if (isnew)
+ 		chptr->channelts = tstosend = newts;
+ 	else if (newts == 0 || oldts == 0)
+ 		chptr->channelts = tstosend = 0;
+ 	else if (newts == oldts)
+ 		tstosend = oldts;
+ 	else if (newts < oldts)
+ 	    {
+ 		if (doesop)
+ 			keepourmodes = 0;
+ 		if (haveops && !doesop)
+ 			tstosend = oldts;
+ 		else
+ 			chptr->channelts = tstosend = newts;
+ 	    }
+ 	else
+ 	    {
+ 		if (haveops)
+ 			keepnewmodes = 0;
+ 		if (doesop && !haveops)
+ 		    {
+ 			chptr->channelts = tstosend = newts;
+ 			if (MyConnect(sptr))
+ 				ts_warn("Hacked ops on opless channel: %s",
+ 					    chptr->chname);
+ 		    }
+ 		else
+ 			tstosend = oldts;
+ 	    }
+ 
+ 	if (!keepnewmodes)
+ 		mode = *oldmode;
+ 	else if (keepourmodes)
+ 	    {
+ 		mode.mode |= oldmode->mode;
+ 		if (oldmode->limit > mode.limit)
+ 			mode.limit = oldmode->limit;
+ 		if (strcmp(mode.key, oldmode->key) < 0)
+ 			strcpy(mode.key, oldmode->key);
+ 	    }
+ 
+ 	for (ip = flags; *ip; ip += 2)
+ 		if ((*ip & mode.mode) && !(*ip & oldmode->mode))
+ 		    {
+ 			if (what != 1)
+ 			    {
+ 				*mbuf++ = '+';
+ 				what = 1;
+ 			    }
+ 			*mbuf++ = *(ip+1);
+ 		    }
+ 	for (ip = flags; *ip; ip += 2)
+ 		if ((*ip & oldmode->mode) && !(*ip & mode.mode))
+ 		    {
+ 			if (what != -1)
+ 			    {
+ 				*mbuf++ = '-';
+ 				what = -1;
+ 			    }
+ 			*mbuf++ = *(ip+1);
+ 		    }
+ 	if (oldmode->limit && !mode.limit)
+ 	    {
+ 		if (what != -1)
+ 		    {
+ 			*mbuf++ = '-';
+ 			what = -1;
+ 		    }
+ 		*mbuf++ = 'l';
+ 	    }
+ 	if (oldmode->key[0] && !mode.key[0])
+ 	    {
+ 		if (what != -1)
+ 		    {
+ 			*mbuf++ = '-';
+ 			what = -1;
+ 		    }
+ 		*mbuf++ = 'k';
+ 		strcat(parabuf, oldmode->key);
+ 		strcat(parabuf, " ");
+ 		pargs++;
+ 	    }
+ 	if (mode.limit && oldmode->limit != mode.limit)
+ 	    {
+ 		if (what != 1)
+ 		    {
+ 			*mbuf++ = '+';
+ 			what = 1;
+ 		    }
+ 		*mbuf++ = 'l';
+ 		(void)sprintf(numeric, "%-15d", mode.limit);
+ 		if ((s = index(numeric, ' ')))
+ 			*s = '\0';
+ 		strcat(parabuf, numeric);
+ 		strcat(parabuf, " ");
+ 		pargs++;
+ 	    }
+ 	if (mode.key[0] && strcmp(oldmode->key, mode.key))
+ 	    {
+ 		if (what != 1)
+ 		    {
+ 			*mbuf++ = '+';
+ 			what = 1;
+ 		    }
+ 		*mbuf++ = 'k';
+ 		strcat(parabuf, mode.key);
+ 		strcat(parabuf, " ");
+ 		pargs++;
+ 	    }
+ 
+ 	chptr->mode = mode;
+ 
+ 	if (!keepourmodes)
+ 	    {
+ 	    	what = 0;
+ 		for (l = chptr->members; l && l->value.cptr; l = l->next)
+ 		    {
+ 			if (l->flags & MODE_CHANOP)
+ 			    {
+ 				if (what != -1)
+ 				    {
+ 					*mbuf++ = '-';
+ 					what = -1;
+ 				    }
+ 				*mbuf++ = 'o';
+ 				strcat(parabuf, l->value.cptr->name);
+ 				strcat(parabuf, " ");
+ 				pargs++;
+ 				if (pargs >= (MAXMODEPARAMS-2))
+ 				    {
+ 					*mbuf = '\0';
+ 					sjoin_sendit(cptr, sptr, chptr,
+ 						    parv[0]);
+ 					mbuf = modebuf;
+ 					*mbuf = parabuf[0] = '\0';
+ 					pargs = what = 0;
+ 				    }
+ 				l->flags &= ~MODE_CHANOP;
+ 			    }
+ 			if (l->flags & MODE_VOICE)
+ 			    {
+ 				if (what != -1)
+ 				    {
+ 					*mbuf++ = '-';
+ 					what = -1;
+ 				    }
+ 				*mbuf++ = 'v';
+ 				strcat(parabuf, l->value.cptr->name);
+ 				strcat(parabuf, " ");
+ 				pargs++;
+ 				if (pargs >= (MAXMODEPARAMS-2))
+ 				    {
+ 					*mbuf = '\0';
+ 					sjoin_sendit(cptr, sptr, chptr,
+ 						    parv[0]);
+ 					mbuf = modebuf;
+ 					*mbuf = parabuf[0] = '\0';
+ 					pargs = what = 0;
+ 				    }
+ 				l->flags &= ~MODE_VOICE;
+ 			    }
+ 		    }
+ 	    }
+ 	if (mbuf != modebuf)
+ 	    {
+ 	    	*mbuf = '\0';
+ 		sjoin_sendit(cptr, sptr, chptr, parv[0]);
+ 	    }
+ 
+ 	*modebuf = *parabuf = '\0';
+ 	if (parv[3][0] != '0' && keepnewmodes)
+ 		channel_modes(sptr, modebuf, parabuf, chptr);
+ 	else
+ 	    {
+ 		modebuf[0] = '0';
+ 		modebuf[1] = '\0';
+ 	    }
+ 
+ 	sprintf(t, ":%s SJOIN %ld %s %s %s :", parv[0], tstosend, parv[2],
+ 		modebuf, parabuf);
+ 	t += strlen(t);
+ 
+ 	mbuf = modebuf;
+ 	parabuf[0] = '\0';
+ 	pargs = 0;
+ 	*mbuf++ = '+';
+ 
+ 	for (s = s0 = strtoken(&p, parv[args+4], " "); s;
+ 	     s = s0 = strtoken(&p, NULL, " "))
+ 	    {
+ 		fl = 0;
+ 		if (*s == '@' || s[1] == '@')
+ 			fl |= MODE_CHANOP;
+ 		if (*s == '+' || s[1] == '+')
+ 			fl |= MODE_VOICE;
+ 		if (!keepnewmodes)
+ 			if (fl & MODE_CHANOP)
+ 				fl = MODE_DEOPPED;
+ 			else
+ 				fl = 0;
+ 		while (*s == '@' || *s == '+')
+ 			s++;
+ 		if (!(acptr = find_chasing(sptr, s, NULL)))
+ 			continue;
+ 		if (acptr->from != cptr)
+ 			continue;
+ 		people++;
+ 		if (!IsMember(acptr, chptr))
+ 		    {
+ 			add_user_to_channel(chptr, acptr, fl);
+ 			sendto_channel_butserv(chptr, acptr, ":%s JOIN :%s",
+ 						s, parv[2]);
+ #ifndef TS_ONLY
+ 			sendto_match_TS_servs(0, chptr, cptr, ":%s JOIN :%s",
+ 						s, parv[2]);
+ #endif
+ 		    }
+ 		if (keepnewmodes)
+ 			strcpy(t, s0);
+ 		else
+ 			strcpy(t, s);
+ 		t += strlen(t);
+ 		*t++ = ' ';
+ 		if (fl & MODE_CHANOP)
+ 		    {
+ 			*mbuf++ = 'o';
+ 			strcat(parabuf, s);
+ 			strcat(parabuf, " ");
+ 			pargs++;
+ 			if (pargs >= (MAXMODEPARAMS-2))
+ 			    {
+ 				*mbuf = '\0';
+ 				sjoin_sendit(cptr, sptr, chptr, parv[0]);
+ 				mbuf = modebuf;
+ 				*mbuf++ = '+';
+ 				parabuf[0] = '\0';
+ 				pargs = 0;
+ 			    }
+ 		    }
+ 		if (fl & MODE_VOICE)
+ 		    {
+ 			*mbuf++ = 'v';
+ 			strcat(parabuf, s);
+ 			strcat(parabuf, " ");
+ 			pargs++;
+ 			if (pargs >= (MAXMODEPARAMS-2))
+ 			    {
+ 				*mbuf = '\0';
+ 				sjoin_sendit(cptr, sptr, chptr, parv[0]);
+ 				mbuf = modebuf;
+ 				*mbuf++ = '+';
+ 				parabuf[0] = '\0';
+ 				pargs = 0;
+ 			    }
+ 		    }
+ 	    }
+ 
+ 	*mbuf = '\0';
+ 	if (pargs)
+ 		sjoin_sendit(cptr, sptr, chptr, parv[0]);
+ 	if (people)
+ 	    {
+ 		if (t[-1] == ' ')
+ 			t[-1] = '\0';
+ 		else
+ 			*t = '\0';
+ #ifdef TS_ONLY
+ 		sendto_match_servs(chptr, cptr, "%s", sjbuf);
+ #else
+ 		sendto_match_TS_servs(1, chptr, cptr, "%s", sjbuf);
+ #endif
+ 	    }
+ }
+ 
+ 
+ #ifdef	TSDEBUG
+ /* this code is for debugging purposes only and should be removed in the
+ ** definitive version of TSora... besides, it's illegible  -orabidoo
+ */
+ int	m_ts(cptr, sptr, parc, parv)
+ aClient *cptr, *sptr;
+ int	parc;
+ char	*parv[];
+ {
+ 	aClient	*acptr;
+ 	aChannel *chptr;
+ 
+ 	if (check_registered_user(sptr))
+ 		return 0;
+ 	sendto_one(sptr, ":%s 999 %s :Time delta is: %ld", me.name, sptr->name, timedelta);
+ 	if (parc > 1)
+ 	    {
+ 		if (*parv[1] == '#' || *parv[1] == '&') 
+ 		    {
+ 			chptr = get_channel(sptr, parv[1], 0);
+ 			if (!chptr)
+ 			    {
+ 				sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
+ 					    me.name, parv[0], parv[1]);
+ 				return 0;
+ 			    }
+ 			sendto_one(sptr, ":%s 999 %s :Channel - %s : %ld", 
+ 				   me.name, sptr->name, chptr->chname,
+ 				   chptr->channelts);
+ 		    }
+ 		else
+ 		    {
+ 			acptr = find_chasing(sptr, parv[1], NULL);
+ 			if (!acptr)
+ 				return 0;
+ 			sendto_one(sptr, ":%s 999 %s :User - %s : %ld",
+ 				   me.name, sptr->name, acptr->name,
+ 				   acptr->tsinfo);
+ 		   }
+ 	       return 0;
+ 	   }
+ 	for (acptr = client; acptr; acptr = acptr->next)
+ 	{
+ 	    if (IsPerson(acptr))
+ 	    	sendto_one(sptr, ":%s 999 %s :User - %s : %ld", me.name,
+ 			     sptr->name, acptr->name, acptr->tsinfo);
+ 	    else if (IsServer(acptr) && MyConnect(acptr))
+ 		sendto_one(sptr, ":%s 999 %s :Server - %s (%s)", me.name,
+ 			    sptr->name, acptr->name,
+ 			    (DoesTS(acptr) ? "does TS" : "no TS"));
+ 	}
+ 	for (chptr = channel; chptr; chptr = chptr->nextch)
+ 		sendto_one(sptr, ":%s 999 %s :Channel - %s : %ld", me.name,
+ 			     sptr->name, chptr->chname, chptr->channelts);
+ 	return 0;
+ }
+ 
+ #endif
+ 
+ 
*** ../irc2.8.21/ircd/s_bsd.c	Sun Nov  6 12:03:35 1994
--- ircd/s_bsd.c	Thu Dec 21 10:55:09 1995
***************
*** 92,97 ****
--- 92,99 ----
  #endif
  static	char	readbuf[8192];
  
+ extern	ts_val	timedelta;
+ 
  /*
   * Try and find the correct name to use with getrlimit() for setting the max.
   * number of files allowed to be open by this process.
***************
*** 910,916 ****
  		return -1;
  	    }
  	if (!BadPtr(aconf->passwd))
! 		sendto_one(cptr, "PASS :%s", aconf->passwd);
  
  	aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
  	if (!aconf)
--- 912,918 ----
  		return -1;
  	    }
  	if (!BadPtr(aconf->passwd))
! 		sendto_one(cptr, "PASS %s :TS", aconf->passwd);
  
  	aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
  	if (!aconf)
*** ../irc2.8.21/ircd/s_debug.c	Mon Oct 31 12:48:04 1994
--- ircd/s_debug.c	Sun May 18 22:35:26 1997
***************
*** 116,121 ****
--- 116,136 ----
  #ifdef	V28PlusOnly
  '8',
  #endif
+ ' ',
+ 'T',
+ 'S',
+ #ifdef TS_CURRENT
+ '0' + TS_CURRENT,
+ #endif
+ #ifdef	TSDEBUG
+ 'd',
+ #endif
+ #ifdef TS_ONLY
+ 'o',
+ #endif
+ #ifdef TS_WARNINGS
+ 'w',
+ #endif
  '\0'};
  
  #include "numeric.h"
*** ../irc2.8.21/ircd/s_err.c	Mon May  2 13:50:24 1994
--- ircd/s_err.c	Fri May 16 07:53:34 1997
***************
*** 153,159 ****
  /* 323 */	RPL_LISTEND, ":End of /LIST",
  /* 324 */	RPL_CHANNELMODEIS, "%s %s %s",
  		0, (char *)NULL, 0, (char *)NULL, 0, (char *)NULL,
! 		0, (char *)NULL, 0, (char *)NULL, 0, (char *)NULL,
  /* 331 */	RPL_NOTOPIC, "%s :No topic is set.",
  /* 332 */	RPL_TOPIC, "%s :%s",
  		0, (char *)NULL, 0, (char *)NULL, 0, (char *)NULL,
--- 153,161 ----
  /* 323 */	RPL_LISTEND, ":End of /LIST",
  /* 324 */	RPL_CHANNELMODEIS, "%s %s %s",
  		0, (char *)NULL, 0, (char *)NULL, 0, (char *)NULL,
! 		0, (char *)NULL, 
! /* 329 */	RPL_CREATIONTIME, "%s %lu",
! 		0, (char *)NULL, 
  /* 331 */	RPL_NOTOPIC, "%s :No topic is set.",
  /* 332 */	RPL_TOPIC, "%s :%s",
  		0, (char *)NULL, 0, (char *)NULL, 0, (char *)NULL,
*** ../irc2.8.21/ircd/s_misc.c	Mon May  2 13:50:22 1994
--- ircd/s_misc.c	Mon Dec 18 11:34:34 1995
***************
*** 212,223 ****
  		else
  		    {
  			if (showip)
! 				(void)sprintf(nbuf, "%s[%s@%s.%u]",
  					sptr->name,
  					(!(sptr->flags & FLAGS_GOTID)) ? "" :
  					sptr->username,
! 					inetntoa((char *)&sptr->ip),
! 					(unsigned int)sptr->port);
  			else
  			    {
  				if (mycmp(sptr->name, sptr->sockhost))
--- 212,222 ----
  		else
  		    {
  			if (showip)
! 				(void)sprintf(nbuf, "%s[%s@%s]",
  					sptr->name,
  					(!(sptr->flags & FLAGS_GOTID)) ? "" :
  					sptr->username,
! 					inetntoa((char *)&sptr->ip));
  			else
  			    {
  				if (mycmp(sptr->name, sptr->sockhost))
*** ../irc2.8.21/ircd/s_serv.c	Thu Nov 10 16:38:26 1994
--- ircd/s_serv.c	Sun May 18 22:51:50 1997
***************
*** 42,47 ****
--- 42,49 ----
  
  static	char	buf[BUFSIZE];
  
+ extern 	ts_val	timedelta;
+ 
  /*
  ** m_functions execute protocol messages on this server:
  **
***************
*** 243,248 ****
--- 245,312 ----
      }
  
  /*
+ ** ts_servcount
+ **	returns the number of TS servers that are connected to us
+ */
+ int	ts_servcount()
+ {
+ 	Reg1	int	i;
+ 	Reg2	aClient	*acptr;
+ 	Reg3	int	r = 0;
+ 
+ 	for (i = 0; i <= highest_fd; i++)
+ 	    	if ((acptr = local[i]) && IsServer(acptr) && DoesTS(acptr))
+ 			r++;
+ 	return r;
+ }
+ 
+ /*
+ ** m_svinfo
+ **	parv[0] = sender prefix
+ **	parv[1] = TS_CURRENT for the server
+ **	parv[2] = TS_MIN for the server
+ **	parv[3] = server is standalone or connected to non-TS only
+ **	parv[4] = server's idea of UTC time
+ */
+ int	m_svinfo(cptr, sptr, parc, parv)
+ aClient *cptr, *sptr;
+ int	parc;
+ char	*parv[];
+ {
+ 	Reg1	ts_val	v;
+ 
+ 	if (!IsServer(sptr) || !MyConnect(sptr) || !DoesTS(sptr) || parc < 5)
+ 		return 0;
+ 
+ 	if (TS_CURRENT < atoi(parv[2]) || atoi(parv[1]) < TS_MIN)
+ 	    {
+ #ifdef TS_ONLY
+ 		/*
+ 		** a server with the wrong TS version connected; since we're
+ 		** TS_ONLY we can't fall back to the non-TS protocol so
+ 		** we drop the link  -orabidoo
+ 		*/
+ 		sendto_ops("Link %s dropped, wrong TS protocol version (%s,%s)",
+ 			   get_client_name(sptr, TRUE), parv[1], parv[2]);
+ 		return exit_client(sptr, sptr, sptr, "Incompatible TS version");
+ #else
+ 		sptr->tsinfo = 0;
+ 		return 0;
+ #endif
+ 	    }
+ 
+ 	if (atoi(parv[3]))
+ 	    	v = (atol(parv[4]) - (ts_val)time(NULL) - timedelta) / 2;
+ 	else
+ 	    	v = atol(parv[4]) - (ts_val)time(NULL) - timedelta;
+ 
+ 	if (ts_servcount() == 1)
+ 		timedelta += v;
+ 
+ 	return 0;
+ }
+ 
+ /*
  ** m_server
  **	parv[0] = sender prefix
  **	parv[1] = servername
***************
*** 468,473 ****
--- 532,550 ----
  	** to be a SERVER. Check if this is allowed and change
  	** status accordingly...
  	*/
+ 
+ #ifdef TS_ONLY
+ 	/* 
+ 	** Reject a direct nonTS server connection if we're TS_ONLY -orabidoo
+ 	*/
+ 	if (!DoesTS(cptr))
+ 	    {
+ 		sendto_ops("Link %s dropped, non-TS server",
+ 			   get_client_name(cptr, TRUE));
+ 		return exit_client(cptr, cptr, cptr, "Non-TS server");
+ 	    }
+ #endif
+ 
  	strncpyzt(cptr->name, host, sizeof(cptr->name));
  	strncpyzt(cptr->info, info[0] ? info:me.name, sizeof(cptr->info));
  	cptr->hopcount = hop;
***************
*** 489,497 ****
--- 566,601 ----
  
  }
  
+ static void	sendnick_TS(cptr, acptr)
+ aClient	*cptr, *acptr;
+ {
+ 	static	char ubuf[12];
+ 
+ 	if (IsPerson(acptr))
+ 	    {
+ 		send_umode(NULL, acptr, 0, SEND_UMODES,
+ 			   ubuf);
+ 		if (!*ubuf)
+ 			strcpy(ubuf, "+");
+ 		sendto_one(cptr, "NICK %s %d %ld %s %s %s %s :%s", acptr->name, 
+ 			   acptr->hopcount + 1, acptr->tsinfo, ubuf,
+ 			   acptr->user->username, acptr->user->host,
+ 			   acptr->user->server, acptr->info);
+ 	    }
+ 	else if (IsService(acptr))
+ 	    {
+ 		sendto_one(cptr,"NICK %s :%d",
+ 			   acptr->name, 
+ 			   acptr->hopcount + 1);
+ 		sendto_one(cptr,":%s SERVICE * * :%s", acptr->name,
+ 			   acptr->info);
+ 	    }
+ }
+ 
  int	m_server_estab(cptr)
  Reg1	aClient	*cptr;
  {
+ 	Reg1	aChannel *chptr;
  	Reg2	aClient	*acptr;
  	Reg3	aConfItem	*aconf, *bconf;
  	char	*inpath, *host, *s, *encr;
***************
*** 560,566 ****
  	if (IsUnknown(cptr))
  	    {
  		if (bconf->passwd[0])
! 			sendto_one(cptr,"PASS :%s",bconf->passwd);
  		/*
  		** Pass my info to the new server
  		*/
--- 664,670 ----
  	if (IsUnknown(cptr))
  	    {
  		if (bconf->passwd[0])
! 			sendto_one(cptr,"PASS %s :TS", bconf->passwd);
  		/*
  		** Pass my info to the new server
  		*/
***************
*** 587,592 ****
--- 691,701 ----
  		*s = '@';
  	    }
  
+ 	if (DoesTS(cptr))
+ 		sendto_one(cptr, "SVINFO %d %d %d :%ld", TS_CURRENT, TS_MIN,
+ 			   (ts_servcount() == 0 ? 1 : 0),
+ 			   (ts_val)time(NULL) + timedelta);
+ 
  	det_confs_butmask(cptr, CONF_LEAF|CONF_HUB|CONF_NOCONNECT_SERVER);
  	/*
  	** *WARNING*
***************
*** 679,720 ****
  		    }
  	    }
  
! 	for (acptr = &me; acptr; acptr = acptr->prev)
  	    {
! 		/* acptr->from == acptr for acptr == cptr */
! 		if (acptr->from == cptr)
! 			continue;
! 		if (IsPerson(acptr))
  		    {
  			/*
  			** IsPerson(x) is true only when IsClient(x) is true.
  			** These are only true when *BOTH* NICK and USER have
  			** been received. -avalon
  			*/
! 			sendto_one(cptr,"NICK %s :%d",acptr->name,
! 				   acptr->hopcount + 1);
! 			sendto_one(cptr,":%s USER %s %s %s :%s", acptr->name,
! 				   acptr->user->username, acptr->user->host,
! 				   acptr->user->server, acptr->info);
! 			send_umode(cptr, acptr, 0, SEND_UMODES, buf);
! 			send_user_joins(cptr, acptr);
! 		    }
! 		else if (IsService(acptr))
! 		    {
! 			sendto_one(cptr,"NICK %s :%d",
! 				   acptr->name, acptr->hopcount + 1);
! 			sendto_one(cptr,":%s SERVICE * * :%s",
! 				   acptr->name, acptr->info);
  		    }
! 	    }
! 	/*
! 	** Last, pass all channels plus statuses
! 	*/
! 	{
! 		Reg1 aChannel *chptr;
  		for (chptr = channel; chptr; chptr = chptr->nextch)
  			send_channel_modes(cptr, chptr);
! 	}
  	return 0;
  }
  
--- 788,880 ----
  		    }
  	    }
  
! 	if (DoesTS(cptr))
  	    {
! 	    	Link	*l;
! 		static	char nickissent = 1;
! 
! 		/*
! 		** Send it in the shortened format with the TS, if
! 		** it's a TS server; walk the list of channels, sending
! 		** all the nicks that haven't been sent yet for each
! 		** channel, then send the channel itself -- it's less
! 		** obvious than sending all nicks first, but on the
! 		** receiving side memory will be allocated more nicely
! 		** saving a few seconds in the handling of a split
! 		** -orabidoo
! 		*/
! 
! 		nickissent = 3 - nickissent;
! 		/* flag used for each nick to check if we've sent it
! 		   yet - must be different each time and !=0, so we
! 		   alternate between 1 and 2 -orabidoo
! 	        */
! 		for (chptr = channel; chptr; chptr = chptr->nextch)
! 		    {
! 			for (l = chptr->members; l; l = l->next)
! 			    {
! 				acptr = l->value.cptr;
! 				if (acptr->nicksent != nickissent)
! 				    {
! 					acptr->nicksent = nickissent;
! 					if (acptr->from != cptr)
! 						sendnick_TS(cptr, acptr);
! 				    }
! 			    }
! 			send_channel_modes(cptr, chptr);
! 		    }
! 		/*
! 		** also send out those that are not on any channel
! 		*/
! 		for (acptr = &me; acptr; acptr = acptr->prev)
! 			if (acptr->nicksent != nickissent)
! 			    {
! 				acptr->nicksent = nickissent;
! 				if (acptr->from != cptr)
! 					sendnick_TS(cptr, acptr);
! 			    }
! 			
! 	    }
! 	else
! 	    {
! 	    	/*
! 		** do it the usual way, for non-TS servers  -orabidoo
! 		*/
! 		for (acptr = &me; acptr; acptr = acptr->prev)
  		    {
+ 			/* acptr->from == acptr for acptr == cptr */
+ 			if (acptr->from == cptr)
+ 				continue;
+ 			if (IsPerson(acptr))
+ 			    {
  			/*
  			** IsPerson(x) is true only when IsClient(x) is true.
  			** These are only true when *BOTH* NICK and USER have
  			** been received. -avalon
  			*/
! 				sendto_one(cptr,"NICK %s :%d",acptr->name,
! 					   acptr->hopcount + 1);
! 				sendto_one(cptr,
! 					":%s USER %s %s %s :%s", acptr->name,
! 				       acptr->user->username, acptr->user->host,
! 				       acptr->user->server, acptr->info);
! 				send_umode(cptr, acptr, 0, SEND_UMODES, buf);
! 				send_user_joins(cptr, acptr);
! 			    }
! 			else if (IsService(acptr))
! 			    {
! 				sendto_one(cptr,"NICK %s :%d",
! 					   acptr->name, acptr->hopcount + 1);
! 				sendto_one(cptr,":%s SERVICE * * :%s",
! 					   acptr->name, acptr->info);
! 			    }
  		    }
! 		/*
! 		** Last, pass all channels plus statuses
! 		*/
  		for (chptr = channel; chptr; chptr = chptr->nextch)
  			send_channel_modes(cptr, chptr);
! 	    }
  	return 0;
  }
  
*** ../irc2.8.21/ircd/s_user.c	Sat Sep 17 15:32:14 1994
--- ircd/s_user.c	Wed Jun 25 00:48:24 1997
***************
*** 37,47 ****
  #include <fcntl.h>
  #include "h.h"
  
! void	send_umode_out PROTO((aClient*, aClient *, int));
  void	send_umode PROTO((aClient *, aClient *, int, int, char *));
  
  static char buf[BUFSIZE], buf2[BUFSIZE];
  
  /*
  ** m_functions execute protocol messages on this server:
  **
--- 37,58 ----
  #include <fcntl.h>
  #include "h.h"
  
! void	send_umode_out PROTO((aClient*, aClient *, int, int));
  void	send_umode PROTO((aClient *, aClient *, int, int, char *));
  
+ int	do_user PROTO((char *, aClient *, aClient*, char *, char *, char *, 
+ 		       char *));
+ 
+ extern	ts_val	timedelta;
  static char buf[BUFSIZE], buf2[BUFSIZE];
  
+ static int user_modes[]	     = { FLAGS_OPER, 'o',
+ 				 FLAGS_LOCOP, 'O',
+ 				 FLAGS_INVISIBLE, 'i',
+ 				 FLAGS_WALLOP, 'w',
+ 				 FLAGS_SERVNOTICE, 's',
+ 				 0, 0 };
+ 
  /*
  ** m_functions execute protocol messages on this server:
  **
***************
*** 302,307 ****
--- 313,320 ----
  **	available. Doing it is not so simple though, would have
  **	to implement the following:
  **
+ **	(actually it has been implemented already for a while) -orabidoo
+ **
  **	1) user telnets in and gives only "NICK foobar" and waits
  **	2) another user far away logs in normally with the nick
  **	   "foobar" (quite legal, as this server didn't propagate
***************
*** 320,325 ****
--- 333,339 ----
  {
  	Reg1	aConfItem *aconf;
          char	*parv[3];
+ 	static	char ubuf[12];
  	short	oldstatus = sptr->status;
  	anUser	*user = sptr->user;
  	int	i;
***************
*** 432,443 ****
  		   }
  	    }
  
! 	sendto_serv_butone(cptr, "NICK %s :%d", nick, sptr->hopcount+1);
! 	sendto_serv_butone(cptr, ":%s USER %s %s %s :%s", nick,
  			   user->username, user->host,
  			   user->server, sptr->info);
! 	if (MyConnect(sptr))
! 		send_umode_out(cptr, sptr, 0);
  #ifdef	USE_SERVICES
  	check_services_butone(SERVICE_WANT_NICK, sptr, "NICK %s :%d",
  				nick, sptr->hopcount);
--- 446,472 ----
  		   }
  	    }
  
! 	send_umode(NULL, sptr, 0, SEND_UMODES, ubuf);
! 	if (!*ubuf)
! 		strcpy(ubuf, "+");
! 
! #ifdef TS_ONLY
! 	sendto_serv_butone(cptr, "NICK %s %d %ld %s %s %s %s :%s",
! 			   nick, sptr->hopcount+1, sptr->tsinfo, ubuf,
! 			   user->username, user->host, user->server,
! 			   sptr->info);
! #else
! 	sendto_TS_serv_butone(0, cptr, "NICK %s :%d", nick, sptr->hopcount+1);
! 	sendto_TS_serv_butone(0, cptr, ":%s USER %s %s %s :%s", nick,
  			   user->username, user->host,
  			   user->server, sptr->info);
! 	sendto_TS_serv_butone(1, cptr, "NICK %s %d %ld %s %s %s %s :%s",
! 			   nick, sptr->hopcount+1, sptr->tsinfo, ubuf,
! 			   user->username, user->host, user->server,
! 			   sptr->info);
! #endif
! 	if (ubuf[1])
! 		send_umode_out(cptr, sptr, 0, 0);
  #ifdef	USE_SERVICES
  	check_services_butone(SERVICE_WANT_NICK, sptr, "NICK %s :%d",
  				nick, sptr->hopcount);
***************
*** 453,458 ****
--- 482,494 ----
  ** m_nick
  **	parv[0] = sender prefix
  **	parv[1] = nickname
+ **	parv[2]	= optional hopcount when new user; TS when nick change
+ **	parv[3] = optional TS
+ **	parv[4] = optional umode
+ **	parv[5] = optional username
+ **	parv[6] = optional hostname
+ **	parv[7]	= optional server
+ **	parv[8]	= optional ircname
  */
  int	m_nick(cptr, sptr, parc, parv)
  aClient *cptr, *sptr;
***************
*** 461,473 ****
  {
  	aClient *acptr;
  	char	nick[NICKLEN+2], *s;
! 	
  	if (parc < 2)
  	    {
  		sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN),
  			   me.name, parv[0]);
  		return 0;
  	    }
  	if (MyConnect(sptr) && (s = (char *)index(parv[1], '~')))
  		*s = '\0';
  	strncpyzt(nick, parv[1], NICKLEN+1);
--- 497,518 ----
  {
  	aClient *acptr;
  	char	nick[NICKLEN+2], *s;
! 	ts_val	newts = 0;
! 	int	doests, sameuser = 0;
! 
  	if (parc < 2)
  	    {
  		sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN),
  			   me.name, parv[0]);
  		return 0;
  	    }
+ 	if (!IsServer(sptr) && IsServer(cptr) && parc > 2)
+ 		newts = atol(parv[2]);
+ 	else if (IsServer(sptr) && parc > 3)
+ 		newts = atol(parv[3]);
+ 	    	
+ 	doests = (IsServer(cptr) && DoesTS(cptr));
+ 
  	if (MyConnect(sptr) && (s = (char *)index(parv[1], '~')))
  		*s = '\0';
  	strncpyzt(nick, parv[1], NICKLEN+1);
***************
*** 610,646 ****
  	** same time, or net reconnecting) or just two net fragmens becoming
  	** joined and having same nicks in use. We cannot have TWO users with
  	** same nick--purge this NICK from the system with a KILL... >;)
- 	**
- 	** The client indicated by 'acptr' is dead meat, give at least some
- 	** indication of the reason why we are just dropping it cold.
  	*/
- 	sendto_one(acptr, err_str(ERR_NICKCOLLISION),
- 		   me.name, acptr->name, acptr->name);
  	/*
  	** This seemingly obscure test (sptr == cptr) differentiates
  	** between "NICK new" (TRUE) and ":old NICK new" (FALSE) forms.
  	*/
! 	if (sptr == cptr)
  	    {
! 		sendto_ops("Nick collision on %s(%s <- %s)",
! 			   acptr->name, acptr->from->name,
! 			   get_client_name(cptr, FALSE));
! 		/*
! 		** A new NICK being introduced by a neighbouring
! 		** server (e.g. message type "NICK new" received)
! 		*/
! 		ircstp->is_kill++;
! 		sendto_serv_butone(NULL, /* all servers */
! 				   ":%s KILL %s :%s (%s <- %s)",
! 				   me.name, acptr->name, me.name,
! 				   acptr->from->name,
  				   /* NOTE: Cannot use get_client_name twice
  				   ** here, it returns static string pointer:
  				   ** the other info would be lost
  				   */
  				   get_client_name(cptr, FALSE));
! 		acptr->flags |= FLAGS_KILLED;
! 		return exit_client(cptr, acptr, &me, "Nick collision");
  	    }
  	/*
  	** A NICK change has collided (e.g. message type
--- 655,730 ----
  	** same time, or net reconnecting) or just two net fragmens becoming
  	** joined and having same nicks in use. We cannot have TWO users with
  	** same nick--purge this NICK from the system with a KILL... >;)
  	*/
  	/*
  	** This seemingly obscure test (sptr == cptr) differentiates
  	** between "NICK new" (TRUE) and ":old NICK new" (FALSE) forms.
  	*/
! 	/* 
! 	** Changed to something reasonable like IsServer(sptr)
! 	** (true if "NICK new", false if ":old NICK new") -orabidoo
! 	*/
! 
! 	if (IsServer(sptr))
  	    {
! 	/*
! 	** A new NICK being introduced by a neighbouring
! 	** server (e.g. message type "NICK new" received)
! 	*/
! 		if (acptr->user)
! 			sameuser = (parc > 6) && 
! 			         mycmp(acptr->user->username, parv[5]) == 0 &&
! 			         mycmp(acptr->user->host, parv[6]) == 0;
! 		else
! 			newts = 0;  /* force both kills */
! 
! 		if (!doests || !newts || !acptr->tsinfo
! 		    || (newts == acptr->tsinfo))
! 		    {
! 		       sendto_ops("Nick collision on %s(%s <- %s)(both killed)",
! 				   acptr->name, acptr->from->name,
! 				   get_client_name(cptr, FALSE));
! 			ircstp->is_kill++;
! 			sendto_one(acptr, err_str(ERR_NICKCOLLISION),
! 				   me.name, acptr->name, acptr->name);
! 			sendto_serv_butone(NULL, /* all servers */
! 					   ":%s KILL %s :%s (%s <- %s)",
! 					   me.name, acptr->name, me.name,
! 					   acptr->from->name,
  				   /* NOTE: Cannot use get_client_name twice
  				   ** here, it returns static string pointer:
  				   ** the other info would be lost
  				   */
+ 					   get_client_name(cptr, FALSE));
+ 			acptr->flags |= FLAGS_KILLED;
+ 			return exit_client(cptr, acptr, &me, "Nick collision");
+ 		    }
+ 		else if ((sameuser && newts < acptr->tsinfo) ||
+ 			(!sameuser && newts > acptr->tsinfo))
+ 			return 0;
+ 		else
+ 		    {
+ 			if (sameuser)
+ 		      sendto_ops("Nick collision on %s(%s <- %s)(older killed)",
+ 				   acptr->name, acptr->from->name,
  				   get_client_name(cptr, FALSE));
! 			else
! 		      sendto_ops("Nick collision on %s(%s <- %s)(newer killed)",
! 				   acptr->name, acptr->from->name,
! 				   get_client_name(cptr, FALSE));
! 
! 			ircstp->is_kill++;
! 			sendto_one(acptr, err_str(ERR_NICKCOLLISION),
! 				   me.name, acptr->name, acptr->name);
! 			sendto_serv_butone(sptr, /* all servers but sptr */
! 					   ":%s KILL %s :%s (%s <- %s)",
! 					   me.name, acptr->name, me.name,
! 					   acptr->from->name,
! 					   get_client_name(cptr, FALSE));
! 			acptr->flags |= FLAGS_KILLED;
! 			(void)exit_client(cptr, acptr, &me, "Nick collision");
! 			goto nickkilldone;
! 		    }
  	    }
  	/*
  	** A NICK change has collided (e.g. message type
***************
*** 649,671 ****
  	** must be killed from the incoming connection, and "old" must
  	** be purged from all outgoing connections.
  	*/
! 	sendto_ops("Nick change collision from %s to %s(%s <- %s)",
! 		   sptr->name, acptr->name, acptr->from->name,
! 		   get_client_name(cptr, FALSE));
! 	ircstp->is_kill++;
! 	sendto_serv_butone(NULL, /* KILL old from outgoing servers */
! 			   ":%s KILL %s :%s (%s(%s) <- %s)",
  			   me.name, sptr->name, me.name, acptr->from->name,
! 			   acptr->name, get_client_name(cptr, FALSE));
! 	ircstp->is_kill++;
! 	sendto_serv_butone(NULL, /* Kill new from incoming link */
! 		   ":%s KILL %s :%s (%s <- %s(%s))",
! 		   me.name, acptr->name, me.name, acptr->from->name,
! 		   get_client_name(cptr, FALSE), sptr->name);
! 	acptr->flags |= FLAGS_KILLED;
! 	(void)exit_client(NULL, acptr, &me, "Nick collision(new)");
! 	sptr->flags |= FLAGS_KILLED;
! 	return exit_client(cptr, sptr, &me, "Nick collision(old)");
  
  nickkilldone:
  	if (IsServer(sptr))
--- 733,812 ----
  	** must be killed from the incoming connection, and "old" must
  	** be purged from all outgoing connections.
  	*/
! 	if (acptr->user && sptr->user)
! 		sameuser = mycmp(acptr->user->username, sptr->user->username)
! 				 == 0 &&
! 			   mycmp(acptr->user->host, sptr->user->host) == 0;
! 	else
! 		newts = 0;   /* force both kills */
! 
! 	if (!doests || !newts || !acptr->tsinfo || (newts == acptr->tsinfo))
! 	    {
! 	sendto_ops("Nick change collision from %s to %s(%s <- %s)(both killed)",
! 			   sptr->name, acptr->name, acptr->from->name,
! 			   get_client_name(cptr, FALSE));
! 		ircstp->is_kill++;
! 		sendto_one(acptr, err_str(ERR_NICKCOLLISION),
! 			   me.name, acptr->name, acptr->name);
! 		sendto_serv_butone(NULL, /* KILL old from outgoing servers */
! 				   ":%s KILL %s :%s (%s(%s) <- %s)",
! 			   me.name, sptr->name, me.name, acptr->from->name,
! 				   acptr->name, get_client_name(cptr, FALSE));
! 		ircstp->is_kill++;
! 		sendto_serv_butone(NULL, /* Kill new from incoming link */
! 			   ":%s KILL %s :%s (%s <- %s(%s))",
! 			   me.name, acptr->name, me.name, acptr->from->name,
! 			   get_client_name(cptr, FALSE), sptr->name);
! 		acptr->flags |= FLAGS_KILLED;
! 		(void)exit_client(NULL, acptr, &me, "Nick collision(new)");
! 		sptr->flags |= FLAGS_KILLED;
! 		return exit_client(cptr, sptr, &me, "Nick collision(old)");
! 	    }
! 	else if ((sameuser && newts < acptr->tsinfo) ||
! 		 (!sameuser && newts > acptr->tsinfo))
! 	    {
! 		if (sameuser)
!     sendto_ops("Nick change collision from %s to %s(%s <- %s)(older killed)",
! 			   sptr->name, acptr->name, acptr->from->name,
! 			   get_client_name(cptr, FALSE));
! 		else
!     sendto_ops("Nick change collision from %s to %s(%s <- %s)(newer killed)",
! 			   sptr->name, acptr->name, acptr->from->name,
! 			   get_client_name(cptr, FALSE));
! 		ircstp->is_kill++;
! 		sendto_serv_butone(cptr, /* KILL old from outgoing servers */
! 				   ":%s KILL %s :%s (%s(%s) <- %s)",
  			   me.name, sptr->name, me.name, acptr->from->name,
! 				   acptr->name, get_client_name(cptr, FALSE));
! 		sptr->flags |= FLAGS_KILLED;
! 		if (sameuser)
! 		return exit_client(cptr, sptr, &me, "Nick collision(old)");
! 		else
! 		return exit_client(cptr, sptr, &me, "Nick collision(new)");
! 	    }
! 	else
! 	    {
! 		if (sameuser)
! 		    sendto_ops("Nick collision on %s(%s <- %s)(older killed)",
! 				   acptr->name, acptr->from->name,
! 				   get_client_name(cptr, FALSE));
! 		else
! 		    sendto_ops("Nick collision on %s(%s <- %s)(newer killed)",
! 				   acptr->name, acptr->from->name,
! 				   get_client_name(cptr, FALSE));
! 
! 		    ircstp->is_kill++;
! 		    sendto_one(acptr, err_str(ERR_NICKCOLLISION),
! 			       me.name, acptr->name, acptr->name);
! 		    sendto_serv_butone(sptr, /* all servers but sptr */
! 				       ":%s KILL %s :%s (%s <- %s)",
! 				       me.name, acptr->name, me.name,
! 				       acptr->from->name,
! 				       get_client_name(cptr, FALSE));
! 		    acptr->flags |= FLAGS_KILLED;
! 		    (void)exit_client(cptr, acptr, &me, "Nick collision");
! 		  /*   goto nickkilldone; */
! 	    }
  
  nickkilldone:
  	if (IsServer(sptr))
***************
*** 676,681 ****
--- 817,857 ----
  		add_client_to_list(sptr);
  		if (parc > 2)
  			sptr->hopcount = atoi(parv[2]);
+ 		if (newts)
+ 			sptr->tsinfo = newts;
+ 		else
+ 		    {
+ 			newts = sptr->tsinfo = (ts_val)time(NULL) + timedelta;
+ #ifdef TS_WARNINGS
+ 			ts_warn("Remote nick %s introduced without a TS", nick);
+ #endif
+ 		    }
+ 		/* copy the nick in place */
+ 		(void)strcpy(sptr->name, nick);
+ 		(void)add_to_client_hash_table(nick, sptr);
+ 		if (parc > 8)
+ 		    {
+ 		    	Reg1	int *s, flag;
+ 			Reg2	char *m;
+ 
+ 			/*
+ 			** parse the usermodes -orabidoo
+ 			*/
+ 			m = &parv[4][1];
+ 			while (*m)
+ 			    {
+ 				for (s = user_modes; (flag = *s); s += 2)
+ 					if (*m == *(s+1))
+ 					    {
+ 						sptr->flags |= flag&SEND_UMODES;
+ 						break;
+ 					    }
+ 				m++;
+ 			    }
+ 
+ 			return do_user(nick, cptr, sptr, parv[5], parv[6],
+ 				parv[7], parv[8]);
+ 		    }
  	    }
  	else if (sptr->name[0])
  	    {
***************
*** 684,693 ****
  		** on a channel, send note of change to all clients
  		** on that channel. Propagate notice to other servers.
  		*/
  		sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
  		if (sptr->user)
  			add_history(sptr);
! 		sendto_serv_butone(cptr, ":%s NICK :%s", parv[0], nick);
  #ifdef	USE_SERVICES
  		check_services_butone(SERVICE_WANT_NICK, sptr, ":%s NICK :%s",
  					parv[0], nick);
--- 860,882 ----
  		** on a channel, send note of change to all clients
  		** on that channel. Propagate notice to other servers.
  		*/
+ 		if (mycmp(parv[0], nick))
+ 			sptr->tsinfo = newts ? newts : (ts_val)time(NULL) +
+ 					timedelta;
  		sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
  		if (sptr->user)
+ 		    {
  			add_history(sptr);
! #ifdef TS_ONLY
! 			sendto_serv_butone(cptr, ":%s NICK %s :%ld",
! 				      parv[0], nick, sptr->tsinfo);
! #else
! 			sendto_TS_serv_butone(1, cptr, ":%s NICK %s :%ld",
! 				      parv[0], nick, sptr->tsinfo);
! 			sendto_TS_serv_butone(0, cptr, ":%s NICK :%s", parv[0],
! 				      nick);
! #endif
! 		    }
  #ifdef	USE_SERVICES
  		check_services_butone(SERVICE_WANT_NICK, sptr, ":%s NICK :%s",
  					parv[0], nick);
***************
*** 699,704 ****
--- 888,894 ----
  
  		/* This had to be copied here to avoid problems.. */
  		(void)strcpy(sptr->name, nick);
+ 		sptr->tsinfo = time(NULL) + timedelta;
  		if (sptr->user)
  			/*
  			** USER already received, now we have NICK.
***************
*** 869,874 ****
--- 1059,1066 ----
  				*server = '@';
  			if (host)
  				*--host = '%';
+ 			if (acptr && IsInvisible(acptr))
+ 				acptr = NULL;
  			if (acptr)
  			    {
  				if (count == 1)
***************
*** 1208,1213 ****
--- 1400,1409 ----
  						*buf = '\0';
  						len = 0;
  					    }
+ #ifdef	TSDEBUG
+ 					if (is_deopped(acptr, chptr))
+ 						*(buf + len++) = '-';
+ #endif
  					if (is_chan_op(acptr, chptr))
  						*(buf + len++) = '@';
  					else if (has_voice(acptr, chptr))
***************
*** 1267,1273 ****
  {
  #define	UFLAGS	(FLAGS_INVISIBLE|FLAGS_WALLOP|FLAGS_SERVNOTICE)
  	char	*username, *host, *server, *realname;
- 	anUser	*user;
   
  	if (parc > 2 && (username = (char *)index(parv[1],'@')))
  		*username = '\0'; 
--- 1463,1468 ----
***************
*** 1290,1295 ****
--- 1485,1503 ----
  	server   = (parc < 4 || BadPtr(parv[3])) ? "<noserver>" : parv[3];
  	realname = (parc < 5 || BadPtr(parv[4])) ? "<bad-realname>" : parv[4];
  
+ 	return do_user(parv[0], cptr, sptr, username, host, server, realname);
+ }
+ 
+ 
+ /*
+ ** do_user
+ */
+ int	do_user(nick, cptr, sptr, username, host, server, realname)
+ aClient	*cptr, *sptr;
+ char	*nick, *username, *host, *server, *realname;
+ {
+ 	anUser	*user;
+ 
   	user = make_user(sptr);
  
  	if (!MyConnect(sptr))
***************
*** 1302,1308 ****
  	if (!IsUnknown(sptr))
  	    {
  		sendto_one(sptr, err_str(ERR_ALREADYREGISTRED),
! 			   me.name, parv[0]);
  		return 0;
  	    }
  #ifndef	NO_DEFAULT_INVISIBLE
--- 1510,1516 ----
  	if (!IsUnknown(sptr))
  	    {
  		sendto_one(sptr, err_str(ERR_ALREADYREGISTRED),
! 			   me.name, nick);
  		return 0;
  	    }
  #ifndef	NO_DEFAULT_INVISIBLE
***************
*** 1791,1797 ****
  			   sptr->user->username, sptr->user->host,
  			   IsOper(sptr) ? 'O' : 'o');
  		sptr->flags |= (FLAGS_SERVNOTICE|FLAGS_WALLOP);
! 		send_umode_out(cptr, sptr, old);
   		sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
  #if !defined(CRYPT_OPER_PASSWORD) && (defined(FNAME_OPERLOG) ||\
      (defined(USE_SYSLOG) && defined(SYSLOG_OPER)))
--- 1999,2005 ----
  			   sptr->user->username, sptr->user->host,
  			   IsOper(sptr) ? 'O' : 'o');
  		sptr->flags |= (FLAGS_SERVNOTICE|FLAGS_WALLOP);
! 		send_umode_out(cptr, sptr, old, 1);
   		sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
  #if !defined(CRYPT_OPER_PASSWORD) && (defined(FNAME_OPERLOG) ||\
      (defined(USE_SYSLOG) && defined(SYSLOG_OPER)))
***************
*** 1853,1858 ****
--- 2061,2067 ----
  ** m_pass
  **	parv[0] = sender prefix
  **	parv[1] = password
+ **	parv[2] = optional extra version information
  */
  int	m_pass(cptr, sptr, parc, parv)
  aClient *cptr, *sptr;
***************
*** 1874,1879 ****
--- 2083,2097 ----
  		return 0;
  	    }
  	strncpyzt(cptr->passwd, password, sizeof(cptr->passwd));
+ 	if (parc > 2)
+ 	    {
+ 		int l = strlen(parv[2]);
+ 
+ 		if (l < 2)
+ 			return 0;
+ 		if (strcmp(parv[2]+l-2, "TS") == 0)
+ 			cptr->tsinfo = (ts_val)TS_DOESTS;
+ 	    }
  	return 0;
      }
  
***************
*** 2020,2032 ****
  }
  #endif
  
- static int user_modes[]	     = { FLAGS_OPER, 'o',
- 				 FLAGS_LOCOP, 'O',
- 				 FLAGS_INVISIBLE, 'i',
- 				 FLAGS_WALLOP, 'w',
- 				 FLAGS_SERVNOTICE, 's',
- 				 0, 0 };
- 
  /*
   * m_umode() added 15/10/91 By Darren Reed.
   * parv[0] - sender
--- 2238,2243 ----
***************
*** 2156,2162 ****
  	 * compare new flags with old flags and send string which
  	 * will cause servers to update correctly.
  	 */
! 	send_umode_out(cptr, sptr, setflags);
  
  	return 0;
  }
--- 2367,2373 ----
  	 * compare new flags with old flags and send string which
  	 * will cause servers to update correctly.
  	 */
! 	send_umode_out(cptr, sptr, setflags, 1);
  
  	return 0;
  }
***************
*** 2216,2224 ****
  /*
   * added Sat Jul 25 07:30:42 EST 1992
   */
! void	send_umode_out(cptr, sptr, old)
  aClient *cptr, *sptr;
! int	old;
  {
  	Reg1    int     i;
  	Reg2    aClient *acptr;
--- 2427,2438 ----
  /*
   * added Sat Jul 25 07:30:42 EST 1992
   */
! /*
!  * extra argument evenTS added to send to TS servers or not -orabidoo
!  */
! void	send_umode_out(cptr, sptr, old, evenTS)
  aClient *cptr, *sptr;
! int	old, evenTS;
  {
  	Reg1    int     i;
  	Reg2    aClient *acptr;
***************
*** 2229,2238 ****
  # endif
  	for (i = highest_fd; i >= 0; i--)
  		if ((acptr = local[i]) && IsServer(acptr) &&
! 		    (acptr != cptr) && (acptr != sptr) && *buf)
  			sendto_one(acptr, ":%s MODE %s :%s",
  				   sptr->name, sptr->name, buf);
  
  	if (cptr && MyClient(cptr))
  		send_umode(cptr, sptr, old, ALL_UMODES, buf);
  }
--- 2443,2454 ----
  # endif
  	for (i = highest_fd; i >= 0; i--)
  		if ((acptr = local[i]) && IsServer(acptr) &&
! 		    (acptr != cptr) && (acptr != sptr) && *buf &&
! 		    (evenTS || !DoesTS(acptr)))
  			sendto_one(acptr, ":%s MODE %s :%s",
  				   sptr->name, sptr->name, buf);
  
  	if (cptr && MyClient(cptr))
  		send_umode(cptr, sptr, old, ALL_UMODES, buf);
  }
+ 
*** ../irc2.8.21/common/parse.c	Sat May 28 20:40:56 1994
--- common/parse.c	Sun Dec 10 04:33:38 1995
***************
*** 490,498 ****
  	 * I'm not sure I've got this all right...
  	 * - avalon
  	 */
! 	sendto_ops("Message (%s) for %s[%s!%s@%s] from %s", cmd,
! 		   sptr->name, sptr->from->name, sptr->from->username,
! 		   sptr->from->sockhost, get_client_name(cptr, TRUE));
  	/*
  	 * Incorrect prefix for a server from some connection.  If it is a
  	 * client trying to be annoying, just QUIT them, if it is a server
--- 490,511 ----
  	 * I'm not sure I've got this all right...
  	 * - avalon
  	 */
! 
! 	/*
! 	** with TS, fake prefixes are a common thing, during the
! 	** connect burst when there's a nick collision, and they
! 	** must be ignored rather than killed because one of the
! 	** two is surviving.. so we don't bother sending them to
! 	** all ops everytime, as this could send 'private' stuff
! 	** from lagged clients. we do send the ones that cause
! 	** servers to be dropped though, as well as the ones from
! 	** non-TS servers -orabidoo
! 	*/
! 	if (IsServer(sptr) || IsMe(sptr) || (IsServer(cptr) &&
! 	    !DoesTS(cptr)))
! 		sendto_ops("Message (%s) for %s[%s!%s@%s] from %s", cmd,
! 			   sptr->name, sptr->from->name, sptr->from->username,
! 			   sptr->from->sockhost, get_client_name(cptr, TRUE));
  	/*
  	 * Incorrect prefix for a server from some connection.  If it is a
  	 * client trying to be annoying, just QUIT them, if it is a server
***************
*** 509,522 ****
  	 * kill, else just exit the lame client.
  	 */
  	if (IsServer(cptr))
! 	    {
! 		sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s] != %s)",
! 				   me.name, sptr->name, me.name,
! 				   sptr->name, sptr->from->name,
! 				   get_client_name(cptr, TRUE));
! 		sptr->flags |= FLAGS_KILLED;
! 		return exit_client(cptr, sptr, &me, "Fake Prefix");
! 	    }
  	return exit_client(cptr, cptr, &me, "Fake prefix");
  }
  
--- 522,543 ----
  	 * kill, else just exit the lame client.
  	 */
  	if (IsServer(cptr))
! 	/*
! 	** If the fake prefix is coming from a TS server, discard it
! 	** silently -orabidoo
! 	*/
! 		if (DoesTS(cptr))
! 			return 0;
! 		else
! 		    {
! 		    	sendto_serv_butone(NULL,
! 					   ":%s KILL %s :%s (%s[%s] != %s)",
! 					   me.name, sptr->name, me.name,
! 					   sptr->name, sptr->from->name,
! 					   get_client_name(cptr, TRUE));
! 			sptr->flags |= FLAGS_KILLED;
! 			return exit_client(cptr, sptr, &me, "Fake Prefix");
! 		    }
  	return exit_client(cptr, cptr, &me, "Fake prefix");
  }
  
*** ../irc2.8.21/common/send.c	Mon May  2 13:50:11 1994
--- common/send.c	Sun May 18 22:03:21 1997
***************
*** 428,433 ****
--- 428,489 ----
  	return;
  }
  
+ 
+ #ifndef TS_ONLY
+ 
+ /*
+  * sendto_TS_server_butone
+  *
+  * Send a message to all connected TS servers except the 'one', if ts==1,
+  * and to all connected non-TS servers except the 'one', if ts==0.
+  */
+ # ifndef	USE_VARARGS
+ /*VARARGS*/
+ void	sendto_TS_serv_butone(ts, one, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+ int	ts;
+ aClient *one;
+ char	*pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+ {
+ # else
+ void	sendto_TS_serv_butone(ts, one, pattern, va_alist)
+ int	ts;
+ aClient	*one;
+ char	*pattern;
+ va_dcl
+ {
+ 	va_list	vl;
+ # endif
+ 	Reg1	int	i;
+ 	Reg2	aClient *cptr;
+ 
+ # ifdef	USE_VARARGS
+ 	va_start(vl);
+ # endif
+ 
+ # ifdef NPATH
+         check_command((long)2, pattern, p1, p2, p3);
+ # endif
+ 
+ 	for (i = 0; i <= highest_fd; i++)
+ 	    {
+ 		if (!(cptr = local[i]) || (one && cptr == one->from))
+ 			continue;
+ 		if (IsServer(cptr) && (DoesTS(cptr) != 0) == (ts != 0))
+ # ifdef	USE_VARARGS
+ 			sendto_one(cptr, pattern, vl);
+ 	    }
+ 	va_end(vl);
+ # else
+ 			sendto_one(cptr, pattern, p1, p2, p3, p4,
+ 				   p5, p6, p7, p8);
+ 	    }
+ # endif
+ 	return;
+ }
+ 
+ #endif /* TS_ONLY */
+ 
+ 
  /*
   * sendto_common_channels()
   *
***************
*** 613,618 ****
--- 669,747 ----
  #endif
  }
  
+ 
+ #ifndef TS_ONLY
+ 
+ /*
+  * sendto_match_TS_servs
+  *
+  * if ts==0, send to all non-TS servers matching the mask
+  * if ts==1, send to all TS servers matching the mask
+  * (or to all if no mask)
+  */
+ #ifndef	USE_VARARGS
+ /*VARARGS*/
+ void	sendto_match_TS_servs(ts, chptr, from, format, 
+ 				p1,p2,p3,p4,p5,p6,p7,p8,p9)
+ int	ts;
+ aChannel *chptr;
+ aClient	*from;
+ char	*format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
+ {
+ #else
+ void	sendto_match_TS_servs(ts, chptr, from, format, va_alist)
+ int	ts;
+ aChannel *chptr;
+ aClient	*from;
+ char	*format;
+ va_dcl
+ {
+ 	va_list	vl;
+ #endif
+ 	Reg1	int	i;
+ 	Reg2	aClient	*cptr;
+ 	char	*mask;
+ 
+ #ifdef	USE_VARARGS
+ 	va_start(vl);
+ #endif
+ 
+ # ifdef NPATH
+         check_command((long)3, format, p1, p2, p3);
+ # endif
+ 	if (chptr)
+ 	    {
+ 		if (*chptr->chname == '&')
+ 			return;
+ 		if ((mask = (char *)rindex(chptr->chname, ':')))
+ 			mask++;
+ 	    }
+ 	else
+ 		mask = (char *)NULL;
+ 
+ 	for (i = 0; i <= highest_fd; i++)
+ 	    {
+ 		if (!(cptr = local[i]))
+ 			continue;
+ 		if ((cptr == from) || !IsServer(cptr) || 
+ 		    (DoesTS(cptr) != 0) != (ts != 0))
+ 			continue;
+ 		if (!BadPtr(mask) && IsServer(cptr) &&
+ 		    matches(mask, cptr->name))
+ 			continue;
+ #ifdef	USE_VARARGS
+ 		sendto_one(cptr, format, vl);
+ 	    }
+ 	va_end(vl);
+ #else
+ 		sendto_one(cptr, format, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+ 	    }
+ #endif
+ }
+ 
+ #endif /* TS_ONLY */
+ 
+ 
  /*
   * sendto_match_butone
   *
***************
*** 770,775 ****
--- 899,956 ----
  			sendto_one(cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
  		    }
  # endif
+ #endif
+ 	return;
+ }
+ 
+ /*
+ ** ts_warn
+ **	Call sendto_ops, with some flood checking (at most 5 warnings
+ **	every 5 seconds)
+ */
+ 
+ #ifndef	USE_VARARGS
+ /*VARARGS*/
+ void	ts_warn(pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+ char	*pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+ {
+ #else
+ void	ts_warn(pattern, va_alist)
+ char	*pattern;
+ va_dcl
+ {
+ 	va_list	vl;
+ #endif
+ 	static	ts_val last = 0;
+ 	static	warnings = 0;
+ 	register ts_val now;
+ 
+ #ifdef	USE_VARARGS
+ 	va_start(vl);
+ #endif
+ 
+ 	/*
+ 	** if we're runnign with TS_WARNINGS enabled and someone does
+ 	** something silly like (remotely) connecting a nonTS server,
+ 	** we'll get a ton of warnings, so we make sure we don't send
+ 	** more than 5 every 5 seconds.  -orabidoo
+ 	*/
+ 	now = time(NULL);
+ 	if (now - last < 5)
+ 	    {
+ 		if (++warnings > 5)
+ 			return;
+ 	    }
+ 	else
+ 	    {
+ 		last = now;
+ 		warnings = 0;
+ 	    }
+ 
+ #ifdef	USE_VARARGS
+ 	sendto_ops(pattern, va_alist);
+ #else
+ 	sendto_ops(pattern, p1, p2, p3, p4, p5, p6, p7, p8);
  #endif
  	return;
  }
*** ../irc2.8.21/include/h.h	Mon May  2 13:50:15 1994
--- include/h.h	Sun May 18 22:03:41 1997
***************
*** 118,123 ****
--- 118,131 ----
  extern	void	sendto_channel_butone();
  /*VARARGS2*/
  extern	void	sendto_serv_butone();
+ 
+ #ifndef TS_ONLY
+ /*VARARGS2*/
+ extern	void	sendto_TS_serv_butone();
+ /*VARARGS3*/
+ extern	void	sendto_match_TS_servs();
+ #endif
+ 
  /*VARARGS2*/
  extern	void	sendto_common_channels();
  /*VARARGS3*/
***************
*** 130,135 ****
--- 138,145 ----
  extern	void	sendto_all_butone();
  /*VARARGS1*/
  extern	void	sendto_ops();
+ /*VARARGS1*/
+ extern	void	ts_warn();
  /*VARARGS3*/
  extern	void	sendto_ops_butone();
  /*VARARGS3*/
***************
*** 156,162 ****
  extern	int	m_names PROTO((aClient *, aClient *, int, char **));
  extern	int	m_server_estab PROTO((aClient *));
  extern	void	send_umode PROTO((aClient *, aClient *, int, int, char *));
! extern	void	send_umode_out PROTO((aClient*, aClient *, int));
  #endif
  
  extern	void	free_client PROTO((aClient *));
--- 166,172 ----
  extern	int	m_names PROTO((aClient *, aClient *, int, char **));
  extern	int	m_server_estab PROTO((aClient *));
  extern	void	send_umode PROTO((aClient *, aClient *, int, int, char *));
! extern	void	send_umode_out PROTO((aClient*, aClient *, int, int));
  #endif
  
  extern	void	free_client PROTO((aClient *));
*** ../irc2.8.21/include/msg.h	Mon Nov 14 15:29:49 1994
--- include/msg.h	Sat Dec 16 22:51:13 1995
***************
*** 70,75 ****
--- 70,80 ----
  #define	MSG_REHASH   "REHASH"	/* REHA */
  #define	MSG_RESTART  "RESTART"	/* REST */
  #define	MSG_CLOSE    "CLOSE"	/* CLOS */
+ #define	MSG_SVINFO   "SVINFO"	/* SVINFO */
+ #define	MSG_SJOIN    "SJOIN"	/* SJOIN */
+ #ifdef	TSDEBUG
+ #define	MSG_TS	     "TS"	/* TS */
+ #endif
  #define	MSG_DIE	     "DIE"
  #define	MSG_HASH     "HSAH"	/* HASH */
  #define	MSG_DNS      "DNS"	/* DNS  -> DNSS */
***************
*** 89,96 ****
  extern int m_time(), m_names(), m_admin();
  extern int m_lusers(), m_umode(), m_note(), m_close();
  extern int m_motd(), m_whowas();
! extern int m_service(), m_userhost(), m_ison();
  extern int m_service(), m_servset(), m_servlist(), m_squery();
  #if defined(OPER_REHASH) || defined(LOCOP_REHASH)
  extern	int	m_rehash();
  #endif
--- 94,104 ----
  extern int m_time(), m_names(), m_admin();
  extern int m_lusers(), m_umode(), m_note(), m_close();
  extern int m_motd(), m_whowas();
! extern int m_service(), m_userhost(), m_ison(), m_svinfo(), m_sjoin();
  extern int m_service(), m_servset(), m_servlist(), m_squery();
+ #ifdef TSDEBUG
+ extern int m_ts();
+ #endif
  #if defined(OPER_REHASH) || defined(LOCOP_REHASH)
  extern	int	m_rehash();
  #endif
***************
*** 148,153 ****
--- 156,166 ----
    { MSG_INFO,    m_info,     0, MAXPARA, 1 ,0L },
    { MSG_MOTD,    m_motd,     0, MAXPARA, 1 ,0L },
    { MSG_CLOSE,   m_close,    0, MAXPARA, 1 ,0L },
+   { MSG_SVINFO,  m_svinfo,   0, MAXPARA, 1, 0L },
+   { MSG_SJOIN,   m_sjoin,    0, MAXPARA, 1, 0L },
+ #ifdef TSDEBUG
+   { MSG_TS,      m_ts,       0, MAXPARA, 1, 0L },
+ #endif
  #if defined(NPATH) && !defined(CLIENT_COMPILE)
    { MSG_NOTE,    m_note,     0, 1, 1 ,0L },
  #endif
*** ../irc2.8.21/include/numeric.h	Mon May  2 13:50:13 1994
--- include/numeric.h	Fri May 16 07:55:59 1997
***************
*** 220,225 ****
--- 220,227 ----
  #define RPL_LISTEND          323
  #define RPL_CHANNELMODEIS    324
  
+ #define RPL_CREATIONTIME     329
+ 
  #define RPL_NOTOPIC          331
  #define RPL_TOPIC            332
  
*** ../irc2.8.21/include/patchlevel.h	Sat Dec  3 12:16:18 1994
--- include/patchlevel.h	Sat Dec 16 15:57:18 1995
***************
*** 17,21 ****
   */
  
  #ifndef	PATCHLEVEL
! #define PATCHLEVEL "2.8.21"
  #endif
--- 17,21 ----
   */
  
  #ifndef	PATCHLEVEL
! #define PATCHLEVEL "2.8.21+TSora"
  #endif
*** ../irc2.8.21/include/struct.h	Mon May  2 13:50:15 1994
--- include/struct.h	Sun May 18 20:25:09 1997
***************
*** 30,35 ****
--- 30,38 ----
  #ifdef STDDEFH
  # include <stddef.h>
  #endif
+ #ifdef ORATIMING
+ #include <sys/time.h>
+ #endif
  
  #ifdef USE_SYSLOG
  # include <syslog.h>
***************
*** 48,53 ****
--- 51,57 ----
  typedef	struct	Server	aServer;
  typedef	struct	SLink	Link;
  typedef	struct	SMode	Mode;
+ typedef	long	ts_val;
  
  #ifndef VMSP
  #include "class.h"
***************
*** 309,319 ****
--- 313,325 ----
  	time_t	lasttime;	/* ...should be only LOCAL clients? --msa */
  	time_t	firsttime;	/* time client was created */
  	time_t	since;		/* last time we parsed something */
+ 	ts_val	tsinfo;		/* TS on the nick, SVINFO on servers */
  	long	flags;		/* client flags */
  	aClient	*from;		/* == self, if Local Client, *NEVER* NULL! */
  	int	fd;		/* >= 0, for local clients */
  	int	hopcount;	/* number of servers to this 0 = local */
  	short	status;		/* Client type */
+ 	char	nicksent;
  	char	name[HOSTLEN+1]; /* Unique name of the client, nick or host */
  	char	username[USERLEN+1]; /* username here now for auth stuff */
  	char	info[REALLEN+1]; /* Free form additional client information */
***************
*** 431,439 ****
--- 437,451 ----
  	Link	*members;
  	Link	*invites;
  	Link	*banlist;
+ 	ts_val	channelts;
  	char	chname[1];
  };
  
+ #define	TS_CURRENT	3	/* current TS protocol version */
+ #define	TS_MIN		1	/* minimum supported TS protocol version */
+ #define	TS_DOESTS	0x20000000
+ #define	DoesTS(x)	((x)->tsinfo == TS_DOESTS)
+ 
  /*
  ** Channel Related macros follow
  */
***************
*** 442,463 ****
  
  #define	CHFL_CHANOP     0x0001 /* Channel operator */
  #define	CHFL_VOICE      0x0002 /* the power to speak */
! #define	CHFL_BAN	0x0004 /* ban channel flag */
  
  /* Channel Visibility macros */
  
  #define	MODE_CHANOP	CHFL_CHANOP
  #define	MODE_VOICE	CHFL_VOICE
! #define	MODE_PRIVATE	0x0004
! #define	MODE_SECRET	0x0008
! #define	MODE_MODERATED  0x0010
! #define	MODE_TOPICLIMIT 0x0020
! #define	MODE_INVITEONLY 0x0040
! #define	MODE_NOPRIVMSGS 0x0080
! #define	MODE_KEY	0x0100
! #define	MODE_BAN	0x0200
! #define	MODE_LIMIT	0x0400
! #define MODE_FLAGS	0x07ff
  /*
   * mode flags which take another parameter (With PARAmeterS)
   */
--- 454,477 ----
  
  #define	CHFL_CHANOP     0x0001 /* Channel operator */
  #define	CHFL_VOICE      0x0002 /* the power to speak */
! #define	CHFL_DEOPPED	0x0004 /* deopped by us, modes need to be bounced */
! #define	CHFL_BAN	0x0008 /* ban channel flag */
  
  /* Channel Visibility macros */
  
  #define	MODE_CHANOP	CHFL_CHANOP
  #define	MODE_VOICE	CHFL_VOICE
! #define	MODE_DEOPPED	CHFL_DEOPPED
! #define	MODE_PRIVATE	0x0008
! #define	MODE_SECRET	0x0010
! #define	MODE_MODERATED  0x0020
! #define	MODE_TOPICLIMIT 0x0040
! #define	MODE_INVITEONLY 0x0080
! #define	MODE_NOPRIVMSGS 0x0100
! #define	MODE_KEY	0x0200
! #define	MODE_BAN	0x0400
! #define	MODE_LIMIT	0x0800
! #define	MODE_FLAGS	0x0fff
  /*
   * mode flags which take another parameter (With PARAmeterS)
   */
***************
*** 534,539 ****
--- 548,572 ----
  #define	FLUSH_BUFFER	-2
  #define	UTMP		"/etc/utmp"
  #define	COMMA		","
+ 
+ #ifdef ORATIMING
+ /* Timing stuff (for performance measurements): compile with -DORATIMING
+    and put a TMRESET where you want the counter of time spent set to 0,
+    a TMPRINT where you want the accumulated results, and TMYES/TMNO pairs
+    around the parts you want timed -orabidoo
+ */
+ extern struct timeval tsdnow, tsdthen;
+ extern unsigned long tsdms;
+ #define TMRESET tsdms=0;
+ #define TMYES gettimeofday(&tsdthen, NULL);
+ #define TMNO gettimeofday(&tsdnow, NULL); if (tsdnow.tv_sec!=tsdthen.tv_sec) tsdms+=1000000*(tsdnow.tv_sec-tsdthen.tv_sec); tsdms+=tsdnow.tv_usec; tsdms-=tsdthen.tv_usec;
+ #define TMPRINT sendto_ops("Time spent: %ld ms", tsdms);
+ #else
+ #define TMRESET
+ #define TMYES
+ #define TMNO
+ #define TMPRINT
+ #endif
  
  /* IRC client structures */
  
