hid: port PART, KICK, INVITE, JOIN, AWAY, ISON, ADMIN, DIE
This commit is contained in:
		
							parent
							
								
									b28c20a250
								
							
						
					
					
						commit
						5429c0b09d
					
				
							
								
								
									
										340
									
								
								hid/main.go
									
									
									
									
									
								
							
							
						
						
									
										340
									
								
								hid/main.go
									
									
									
									
									
								
							| @ -556,14 +556,40 @@ func initiateQuit() { | |||||||
| 	quitting = true | 	quitting = true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: ircChannelCreate | func ircChannelCreate(name string) *channel { | ||||||
| 
 | 	ch := &channel{ | ||||||
| func ircChannelDestroyIfEmpty(ch *channel) { | 		name:      name, | ||||||
| 	// TODO | 		created:   time.Now().UnixNano(), | ||||||
|  | 		userLimit: -1, | ||||||
|  | 	} | ||||||
|  | 	channels[ircToCanon(name)] = ch | ||||||
|  | 	return ch | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func ircChannelDestroyIfEmpty(ch *channel) { | ||||||
|  | 	if len(ch.userModes) == 0 { | ||||||
|  | 		delete(channels, ircToCanon(ch.name)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: Improve the name as it takes mode +q into account. | ||||||
| func ircSendToRoommates(c *client, message string) { | func ircSendToRoommates(c *client, message string) { | ||||||
| 	// TODO | 	targets := make(map[*client]bool) | ||||||
|  | 	for _, ch := range channels { | ||||||
|  | 		_, present := ch.userModes[c] | ||||||
|  | 		if !present || 0 != ch.modes&ircChanModeQuiet { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		for client := range ch.userModes { | ||||||
|  | 			targets[client] = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for roommate := range targets { | ||||||
|  | 		if roommate != c { | ||||||
|  | 			roommate.send(message) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // --- Clients (continued) ----------------------------------------------------- | // --- Clients (continued) ----------------------------------------------------- | ||||||
| @ -1662,13 +1688,248 @@ func ircHandleTOPIC(msg *message, c *client) { | |||||||
| 	ircChannelMulticast(ch, message, nil) | 	ircChannelMulticast(ch, message, nil) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: All the various real command handlers. | func ircTryPart(c *client, target string, reason string) { | ||||||
|  | 	if reason == "" { | ||||||
|  | 		reason = c.nickname | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| func ircHandleX(msg *message, c *client) { | 	ch := channels[ircToCanon(target)] | ||||||
|  | 	if ch == nil { | ||||||
|  | 		c.sendReply(ERR_NOSUCHCHANNEL, target) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, present := ch.userModes[c]; !present { | ||||||
|  | 		c.sendReply(ERR_NOTONCHANNEL, target) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	message := fmt.Sprintf(":%s@%s@%s PART %s :%s", | ||||||
|  | 		c.nickname, c.username, c.hostname, target, reason) | ||||||
|  | 	if 0 == ch.modes&ircChanModeQuiet { | ||||||
|  | 		ircChannelMulticast(ch, message, nil) | ||||||
|  | 	} else { | ||||||
|  | 		c.send(message) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	delete(ch.userModes, c) | ||||||
|  | 	ircChannelDestroyIfEmpty(ch) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircPartAllChannels(c *client) { | ||||||
|  | 	for _, ch := range channels { | ||||||
|  | 		if _, present := ch.userModes[c]; present { | ||||||
|  | 			ircTryPart(c, ch.name, "") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandlePART(msg *message, c *client) { | ||||||
| 	if len(msg.params) < 1 { | 	if len(msg.params) < 1 { | ||||||
| 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	reason := "" | ||||||
|  | 	if len(msg.params) > 1 { | ||||||
|  | 		reason = msg.params[1] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, target := range splitString(msg.params[0], ",", true) { | ||||||
|  | 		ircTryPart(c, target, reason) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: Undo the rename from channelName to target, also in ircTryPart. | ||||||
|  | func ircTryKick(c *client, target, nick, reason string) { | ||||||
|  | 	ch := channels[ircToCanon(target)] | ||||||
|  | 	if ch == nil { | ||||||
|  | 		c.sendReply(ERR_NOSUCHCHANNEL, target) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if modes, present := ch.userModes[c]; !present { | ||||||
|  | 		c.sendReply(ERR_NOTONCHANNEL, target) | ||||||
|  | 		return | ||||||
|  | 	} else if 0 == modes&ircChanModeOperator { | ||||||
|  | 		c.sendReply(ERR_CHANOPRIVSNEEDED, target) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	client := users[ircToCanon(nick)] | ||||||
|  | 	if _, present := ch.userModes[client]; client == nil || !present { | ||||||
|  | 		c.sendReply(ERR_USERNOTINCHANNEL, nick, target) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	message := fmt.Sprintf(":%s@%s@%s KICK %s %s :%s", | ||||||
|  | 		c.nickname, c.username, c.hostname, target, nick, reason) | ||||||
|  | 	if 0 == ch.modes&ircChanModeQuiet { | ||||||
|  | 		ircChannelMulticast(ch, message, nil) | ||||||
|  | 	} else { | ||||||
|  | 		c.send(message) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	delete(ch.userModes, client) | ||||||
|  | 	ircChannelDestroyIfEmpty(ch) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandleKICK(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) < 2 { | ||||||
|  | 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	reason := c.nickname | ||||||
|  | 	if len(msg.params) > 2 { | ||||||
|  | 		reason = msg.params[2] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	targetChannels := splitString(msg.params[0], ",", true) | ||||||
|  | 	targetUsers := splitString(msg.params[1], ",", true) | ||||||
|  | 
 | ||||||
|  | 	if len(channels) == 1 { | ||||||
|  | 		for i := 0; i < len(targetUsers); i++ { | ||||||
|  | 			ircTryKick(c, targetChannels[0], targetUsers[i], reason) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		for i := 0; i < len(channels) && i < len(targetUsers); i++ { | ||||||
|  | 			ircTryKick(c, targetChannels[i], targetUsers[i], reason) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircSendInviteNotifications(ch *channel, c, target *client) { | ||||||
|  | 	for client := range ch.userModes { | ||||||
|  | 		if client != target && 0 != client.capsEnabled&ircCapInviteNotify { | ||||||
|  | 			client.sendf(":%s!%s@%s INVITE %s %s", | ||||||
|  | 				c.nickname, c.username, c.hostname, target.nickname, ch.name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandleINVITE(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) < 2 { | ||||||
|  | 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	target, channelName := msg.params[0], msg.params[1] | ||||||
|  | 	client := users[ircToCanon(target)] | ||||||
|  | 	if client == nil { | ||||||
|  | 		c.sendReply(ERR_NOSUCHNICK, target) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if ch := channels[ircToCanon(channelName)]; ch != nil { | ||||||
|  | 		invitingModes, invitingPresent := ch.userModes[c] | ||||||
|  | 		if !invitingPresent { | ||||||
|  | 			c.sendReply(ERR_NOTONCHANNEL, channelName) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if _, present := ch.userModes[client]; present { | ||||||
|  | 			c.sendReply(ERR_USERONCHANNEL, target, channelName) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if 0 != invitingModes&ircChanModeOperator { | ||||||
|  | 			client.invites[ircToCanon(channelName)] = true | ||||||
|  | 		} else if 0 != ch.modes&ircChanModeInviteOnly { | ||||||
|  | 			c.sendReply(ERR_CHANOPRIVSNEEDED, channelName) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// It's not specified when and how we should send out invite-notify. | ||||||
|  | 		if 0 != ch.modes&ircChanModeInviteOnly { | ||||||
|  | 			ircSendInviteNotifications(ch, c, client) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	client.sendf(":%s!%s@%s INVITE %s %s", | ||||||
|  | 		c.nickname, c.username, c.hostname, client.nickname, channelName) | ||||||
|  | 	if client.awayMessage != "" { | ||||||
|  | 		c.sendReply(RPL_AWAY, client.nickname, client.awayMessage) | ||||||
|  | 	} | ||||||
|  | 	c.sendReply(RPL_INVITING, client.nickname, channelName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircTryJoin(c *client, channelName, key string) { | ||||||
|  | 	ch := channels[ircToCanon(channelName)] | ||||||
|  | 	var userMode uint | ||||||
|  | 	if ch == nil { | ||||||
|  | 		if !ircIsValidChannelName(channelName) { | ||||||
|  | 			c.sendReply(ERR_BADCHANMASK, channelName) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ch = ircChannelCreate(channelName) | ||||||
|  | 		userMode = ircChanModeOperator | ||||||
|  | 	} else if _, present := ch.userModes[c]; present { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, invitedByChanop := c.invites[ircToCanon(channelName)] | ||||||
|  | 	if 0 != ch.modes&ircChanModeInviteOnly && c.inMaskList(ch.inviteList) && | ||||||
|  | 		!invitedByChanop { | ||||||
|  | 		c.sendReply(ERR_INVITEONLYCHAN, channelName) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if ch.key != "" && (key == "" || key != ch.key) { | ||||||
|  | 		c.sendReply(ERR_BADCHANNELKEY, channelName) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if ch.userLimit != -1 && len(ch.userModes) >= ch.userLimit { | ||||||
|  | 		c.sendReply(ERR_CHANNELISFULL, channelName) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if c.inMaskList(ch.banList) && !c.inMaskList(ch.exceptionList) && | ||||||
|  | 		!invitedByChanop { | ||||||
|  | 		c.sendReply(ERR_BANNEDFROMCHAN, channelName) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Destroy any invitation as there's no other way to get rid of it. | ||||||
|  | 	delete(c.invites, ircToCanon(channelName)) | ||||||
|  | 
 | ||||||
|  | 	ch.userModes[c] = userMode | ||||||
|  | 
 | ||||||
|  | 	message := fmt.Sprintf(":%s!%s@%s JOIN %s", | ||||||
|  | 		c.nickname, c.username, c.hostname, channelName) | ||||||
|  | 	if 0 == ch.modes&ircChanModeQuiet { | ||||||
|  | 		ircChannelMulticast(ch, message, nil) | ||||||
|  | 	} else { | ||||||
|  | 		c.send(message) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ircSendRPLTOPIC(c, ch) | ||||||
|  | 	ircSendRPLNAMREPLY(c, ch, nil) | ||||||
|  | 	c.sendReply(RPL_ENDOFNAMES, ch.name) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandleJOIN(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) < 1 { | ||||||
|  | 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if msg.params[0] == "0" { | ||||||
|  | 		ircPartAllChannels(c) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	targetChannels := splitString(msg.params[0], ",", true) | ||||||
|  | 
 | ||||||
|  | 	var keys []string | ||||||
|  | 	if len(msg.params) > 1 { | ||||||
|  | 		keys = splitString(msg.params[1], ",", true) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i, name := range targetChannels { | ||||||
|  | 		key := "" | ||||||
|  | 		if i < len(keys) { | ||||||
|  | 			key = keys[i] | ||||||
|  | 		} | ||||||
|  | 		ircTryJoin(c, name, key) | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ircHandleSUMMON(msg *message, c *client) { | func ircHandleSUMMON(msg *message, c *client) { | ||||||
| @ -1679,11 +1940,63 @@ func ircHandleUSERS(msg *message, c *client) { | |||||||
| 	c.sendReply(ERR_USERSDISABLED) | 	c.sendReply(ERR_USERSDISABLED) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func ircHandleAWAY(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) < 1 { | ||||||
|  | 		c.awayMessage = "" | ||||||
|  | 		c.sendReply(RPL_UNAWAY) | ||||||
|  | 	} else { | ||||||
|  | 		c.awayMessage = msg.params[0] | ||||||
|  | 		c.sendReply(RPL_NOWAWAY) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandleISON(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) < 1 { | ||||||
|  | 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var on []string | ||||||
|  | 	for _, nick := range msg.params { | ||||||
|  | 		if client := users[ircToCanon(nick)]; client != nil { | ||||||
|  | 			on = append(on, nick) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	c.sendReply(RPL_ISON, strings.Join(on, " ")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandleADMIN(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) > 0 && !isThisMe(msg.params[0]) { | ||||||
|  | 		c.sendReply(ERR_NOSUCHSERVER, msg.params[0]) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	c.sendReply(ERR_NOADMININFO, serverName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: All the remaining command handlers. | ||||||
|  | 
 | ||||||
|  | func ircHandleX(msg *message, c *client) { | ||||||
|  | 	if len(msg.params) < 1 { | ||||||
|  | 		c.sendReply(ERR_NEEDMOREPARAMS, msg.command) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ircHandleDIE(msg *message, c *client) { | ||||||
|  | 	if 0 == c.mode&ircUserModeOperator { | ||||||
|  | 		c.sendReply(ERR_NOPRIVILEGES) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if !quitting { | ||||||
|  | 		initiateQuit() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ----------------------------------------------------------------------------- | // ----------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| // TODO: Add an index for IRC_ERR_NOSUCHSERVER validation? | // TODO: Add an index for IRC_ERR_NOSUCHSERVER validation? | ||||||
| // TODO: Add a minimal parameter count? | // TODO: Add a minimal parameter count? | ||||||
| // TODO: Add a field for oper-only commands? | // TODO: Add a field for oper-only commands? Use flags? | ||||||
| var ircHandlers = map[string]*ircCommand{ | var ircHandlers = map[string]*ircCommand{ | ||||||
| 	"CAP":  {false, ircHandleCAP, 0, 0}, | 	"CAP":  {false, ircHandleCAP, 0, 0}, | ||||||
| 	"PASS": {false, ircHandlePASS, 0, 0}, | 	"PASS": {false, ircHandlePASS, 0, 0}, | ||||||
| @ -1700,16 +2013,25 @@ var ircHandlers = map[string]*ircCommand{ | |||||||
| 	"VERSION":  {true, ircHandleVERSION, 0, 0}, | 	"VERSION":  {true, ircHandleVERSION, 0, 0}, | ||||||
| 	"USERS":    {true, ircHandleUSERS, 0, 0}, | 	"USERS":    {true, ircHandleUSERS, 0, 0}, | ||||||
| 	"SUMMON":   {true, ircHandleSUMMON, 0, 0}, | 	"SUMMON":   {true, ircHandleSUMMON, 0, 0}, | ||||||
|  | 	"AWAY":     {true, ircHandleAWAY, 0, 0}, | ||||||
|  | 	"ADMIN":    {true, ircHandleADMIN, 0, 0}, | ||||||
| 
 | 
 | ||||||
| 	"MODE":    {true, ircHandleMODE, 0, 0}, | 	"MODE":    {true, ircHandleMODE, 0, 0}, | ||||||
| 	"PRIVMSG": {true, ircHandlePRIVMSG, 0, 0}, | 	"PRIVMSG": {true, ircHandlePRIVMSG, 0, 0}, | ||||||
| 	"NOTICE":  {true, ircHandleNOTICE, 0, 0}, | 	"NOTICE":  {true, ircHandleNOTICE, 0, 0}, | ||||||
|  | 	"JOIN":    {true, ircHandleJOIN, 0, 0}, | ||||||
|  | 	"PART":    {true, ircHandlePART, 0, 0}, | ||||||
|  | 	"KICK":    {true, ircHandleKICK, 0, 0}, | ||||||
|  | 	"INVITE":  {true, ircHandleINVITE, 0, 0}, | ||||||
| 	"TOPIC":   {true, ircHandleTOPIC, 0, 0}, | 	"TOPIC":   {true, ircHandleTOPIC, 0, 0}, | ||||||
| 	"LIST":    {true, ircHandleLIST, 0, 0}, | 	"LIST":    {true, ircHandleLIST, 0, 0}, | ||||||
| 	"NAMES":   {true, ircHandleNAMES, 0, 0}, | 	"NAMES":   {true, ircHandleNAMES, 0, 0}, | ||||||
| 	"WHO":     {true, ircHandleWHO, 0, 0}, | 	"WHO":     {true, ircHandleWHO, 0, 0}, | ||||||
| 	"WHOIS":   {true, ircHandleWHOIS, 0, 0}, | 	"WHOIS":   {true, ircHandleWHOIS, 0, 0}, | ||||||
| 	"WHOWAS":  {true, ircHandleWHOWAS, 0, 0}, | 	"WHOWAS":  {true, ircHandleWHOWAS, 0, 0}, | ||||||
|  | 	"ISON":    {true, ircHandleISON, 0, 0}, | ||||||
|  | 
 | ||||||
|  | 	"DIE": {true, ircHandleDIE, 0, 0}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ircProcessMessage(c *client, msg *message, raw string) { | func ircProcessMessage(c *client, msg *message, raw string) { | ||||||
| @ -1739,7 +2061,7 @@ func ircProcessMessage(c *client, msg *message, raw string) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // --- ? ----------------------------------------------------------------------- | // --- Network I/O ------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| // Handle the results from initializing the client's connection. | // Handle the results from initializing the client's connection. | ||||||
| func (c *client) onPrepared(host string, isTLS bool) { | func (c *client) onPrepared(host string, isTLS bool) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user