verbose support, WHO support, global messages in blessed channel, prevent leaving blessed channel
--HG-- branch : nmdc-ircfrontend
This commit is contained in:
parent
2c84934ab8
commit
479fbf104b
15
TODO.txt
15
TODO.txt
@ -4,10 +4,10 @@ PRE-RELEASE
|
|||||||
|
|
||||||
- nick list
|
- nick list
|
||||||
|
|
||||||
- expose unprefixed system messages in chat channel
|
|
||||||
|
|
||||||
- part all nicks on server disconnection
|
- part all nicks on server disconnection
|
||||||
|
|
||||||
|
- test the current password auth
|
||||||
|
|
||||||
|
|
||||||
WISHLIST
|
WISHLIST
|
||||||
========
|
========
|
||||||
@ -20,10 +20,19 @@ WISHLIST
|
|||||||
|
|
||||||
- expose upstream op status
|
- expose upstream op status
|
||||||
|
|
||||||
- support WHO, WHOIS, USERIP/KILL/KICK (if opped)
|
- support WHO
|
||||||
|
|
||||||
|
- support WHOIS
|
||||||
|
|
||||||
|
- support USERIP/KILL/KICK for ops
|
||||||
|
|
||||||
- use CTCP chat to support irc-special characters in chat messages (colon, newline)
|
- use CTCP chat to support irc-special characters in chat messages (colon, newline)
|
||||||
|
|
||||||
|
- respond to CAP LS 302 (hexchat) in some way that'll kick us into a modern protocol mode
|
||||||
|
|
||||||
|
- support SASL PLAIN authentication as well as just classic PASS
|
||||||
|
|
||||||
|
- threadsafe access to libnmdc hub options
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
11
main.go
11
main.go
@ -25,14 +25,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ircAddress = flag.String("bind", ":6697", "The address:port to bind to and listen for clients on")
|
|
||||||
dcAddress = flag.String("upstream", "127.0.0.1:411", "Upstream NMDC server")
|
|
||||||
serverName = flag.String("servername", "nmdc-ircfrontend", "Server name displayed to clients")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
ircAddress := flag.String("bind", ":6697", "The address:port to bind to and listen for clients on")
|
||||||
|
dcAddress := flag.String("upstream", "127.0.0.1:411", "Upstream NMDC server")
|
||||||
|
serverName := flag.String("servername", "nmdc-ircfrontend", "Server name displayed to clients")
|
||||||
|
verbose := flag.Bool("verbose", false, "Display debugging information")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.Printf("Listening on '%s'...", *ircAddress)
|
log.Printf("Listening on '%s'...", *ircAddress)
|
||||||
@ -52,6 +50,7 @@ func main() {
|
|||||||
|
|
||||||
// Spin up a worker for the new user.
|
// Spin up a worker for the new user.
|
||||||
server := NewServer(*serverName, libnmdc.HubAddress(*dcAddress), conn)
|
server := NewServer(*serverName, libnmdc.HubAddress(*dcAddress), conn)
|
||||||
|
server.verbose = *verbose
|
||||||
go server.RunWorker()
|
go server.RunWorker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
72
server.go
72
server.go
@ -40,6 +40,8 @@ type Server struct {
|
|||||||
upstreamCloser chan struct{}
|
upstreamCloser chan struct{}
|
||||||
upstream *libnmdc.HubConnection
|
upstream *libnmdc.HubConnection
|
||||||
|
|
||||||
|
verbose bool
|
||||||
|
|
||||||
lastMessage string // FIXME racey
|
lastMessage string // FIXME racey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +61,18 @@ func NewServer(name string, upstream libnmdc.HubAddress, conn net.Conn) *Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) verboseln(line string) {
|
||||||
|
if s.verbose {
|
||||||
|
log.Println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) verbosef(fmt string, args ...interface{}) {
|
||||||
|
if s.verbose {
|
||||||
|
log.Printf(fmt, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) RunWorker() {
|
func (s *Server) RunWorker() {
|
||||||
|
|
||||||
// Send the connection handshake.
|
// Send the connection handshake.
|
||||||
@ -72,13 +86,12 @@ func (s *Server) RunWorker() {
|
|||||||
|
|
||||||
buf := make([]byte, CLIENT_READ_BUFFSIZE)
|
buf := make([]byte, CLIENT_READ_BUFFSIZE)
|
||||||
|
|
||||||
//s.clientConn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
|
||||||
ln, err := s.clientConn.Read(buf)
|
ln, err := s.clientConn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break // abandon thread
|
break // abandon thread
|
||||||
}
|
}
|
||||||
//log.Println(err.Error())
|
s.verboseln(err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +117,7 @@ func (s *Server) RunWorker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Broken loop.")
|
s.verboseln("Broken loop.")
|
||||||
|
|
||||||
// Cleanup upstream
|
// Cleanup upstream
|
||||||
if s.clientRegistered {
|
if s.clientRegistered {
|
||||||
@ -123,7 +136,7 @@ func (s *Server) upstreamWorker() {
|
|||||||
select {
|
select {
|
||||||
case <-s.upstreamCloser:
|
case <-s.upstreamCloser:
|
||||||
// Abandon the upstream connection
|
// Abandon the upstream connection
|
||||||
log.Println("Abandoning upstream connection...")
|
s.verboseln("Abandoning upstream connection...")
|
||||||
s.upstream.Disconnect()
|
s.upstream.Disconnect()
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -155,7 +168,7 @@ func (s *Server) upstreamWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case libnmdc.EVENT_SYSTEM_MESSAGE_FROM_CONN, libnmdc.EVENT_SYSTEM_MESSAGE_FROM_HUB:
|
case libnmdc.EVENT_SYSTEM_MESSAGE_FROM_CONN, libnmdc.EVENT_SYSTEM_MESSAGE_FROM_HUB:
|
||||||
s.reply(rplMsg, BLESSED_CHANNEL, BLESSED_CHANNEL, hubEvent.Message) // experimental
|
s.reply(rplMsg, "", BLESSED_CHANNEL, hubEvent.Message) // experimental
|
||||||
// s.sendClientGlobalMessage(hubEvent.Message)
|
// s.sendClientGlobalMessage(hubEvent.Message)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -165,6 +178,8 @@ func (s *Server) upstreamWorker() {
|
|||||||
|
|
||||||
func (s *Server) handleCommand(command string, args []string) {
|
func (s *Server) handleCommand(command string, args []string) {
|
||||||
|
|
||||||
|
s.verbosef(" >>> '%s' %v", command, args)
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
case "PING":
|
case "PING":
|
||||||
s.reply(rplPong)
|
s.reply(rplPong)
|
||||||
@ -261,7 +276,15 @@ func (s *Server) handleRegisteredCommand(command string, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "PART":
|
case "PART":
|
||||||
// You can check out any time you like, but you can never leave
|
if len(args) < 1 {
|
||||||
|
s.reply(errMoreArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0] == BLESSED_CHANNEL {
|
||||||
|
// You can check out any time you like, but you can never leave
|
||||||
|
s.reply(rplJoin, s.upstreamLauncher.Self.Nick, BLESSED_CHANNEL)
|
||||||
|
}
|
||||||
|
|
||||||
case "PRIVMSG":
|
case "PRIVMSG":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
@ -325,18 +348,34 @@ func (s *Server) handleRegisteredCommand(command string, args []string) {
|
|||||||
// Can't use this command.
|
// Can't use this command.
|
||||||
s.reply(errPassword)
|
s.reply(errPassword)
|
||||||
|
|
||||||
case "KILL":
|
case "KILL", "KICK":
|
||||||
s.reply(errNoPriv)
|
s.reply(errNoPriv)
|
||||||
return
|
return
|
||||||
|
|
||||||
case "KICK":
|
case "WHO":
|
||||||
if s.clientRegistered == false {
|
if len(args) < 1 {
|
||||||
s.reply(errNotReg)
|
s.reply(errMoreArgs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.reply(errNoPriv)
|
if args[0] == BLESSED_CHANNEL {
|
||||||
return
|
// always include ourselves
|
||||||
|
s.reply(rplWho, s.upstreamLauncher.Self.Nick)
|
||||||
|
|
||||||
|
for nick, _ := range s.upstream.Users {
|
||||||
|
if nick != s.upstreamLauncher.Self.Nick { // but don't repeat ourselves
|
||||||
|
s.reply(rplWho, nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// argument is a filter
|
||||||
|
for nick, _ := range s.upstream.Users {
|
||||||
|
if strings.Contains(nick, args[0]) {
|
||||||
|
s.reply(rplWho, nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.reply(rplEndOfWho)
|
||||||
|
|
||||||
case "MODE":
|
case "MODE":
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
@ -409,10 +448,17 @@ func (s *Server) reply(code replyCode, args ...string) {
|
|||||||
s.writeClient(fmt.Sprintf(":%s 332 %s %s :%s", s.name, s.upstreamLauncher.Self.Nick, args[0], args[1]))
|
s.writeClient(fmt.Sprintf(":%s 332 %s %s :%s", s.name, s.upstreamLauncher.Self.Nick, args[0], args[1]))
|
||||||
case rplNoTopic:
|
case rplNoTopic:
|
||||||
s.writeClient(fmt.Sprintf(":%s 331 %s %s :No topic is set", s.name, s.upstreamLauncher.Self.Nick, args[0]))
|
s.writeClient(fmt.Sprintf(":%s 331 %s %s :No topic is set", s.name, s.upstreamLauncher.Self.Nick, args[0]))
|
||||||
|
|
||||||
case rplNames:
|
case rplNames:
|
||||||
s.writeClient(fmt.Sprintf(":%s 353 %s = %s :%s", s.name, s.upstreamLauncher.Self.Nick, args[0], args[1]))
|
s.writeClient(fmt.Sprintf(":%s 353 %s = %s :%s", s.name, s.upstreamLauncher.Self.Nick, args[0], args[1]))
|
||||||
case rplEndOfNames:
|
case rplEndOfNames:
|
||||||
s.writeClient(fmt.Sprintf(":%s 366 %s %s :End of NAMES list", s.name, s.upstreamLauncher.Self.Nick, args[0]))
|
s.writeClient(fmt.Sprintf(":%s 366 %s %s :End of NAMES list", s.name, s.upstreamLauncher.Self.Nick, args[0]))
|
||||||
|
|
||||||
|
case rplWho:
|
||||||
|
s.writeClient(fmt.Sprintf(":%s 352 %s %s %s %s %s %s H :0 %s", s.name, s.upstreamLauncher.Self.Nick, BLESSED_CHANNEL, args[0], args[0], s.name, args[0], args[0]))
|
||||||
|
case rplEndOfWho:
|
||||||
|
s.writeClient(fmt.Sprintf(":%s 315 :End of WHO list", s.name))
|
||||||
|
|
||||||
case rplNickChange:
|
case rplNickChange:
|
||||||
s.writeClient(fmt.Sprintf(":%s NICK %s", args[0], args[1]))
|
s.writeClient(fmt.Sprintf(":%s NICK %s", args[0], args[1]))
|
||||||
case rplKill:
|
case rplKill:
|
||||||
@ -473,6 +519,8 @@ func (s *Server) writeClient(output string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.verbosef(" <<< %s", output)
|
||||||
|
|
||||||
s.clientConn.SetWriteDeadline(time.Now().Add(time.Second * 30))
|
s.clientConn.SetWriteDeadline(time.Now().Add(time.Second * 30))
|
||||||
if _, err := fmt.Fprintf(s.clientConn, "%s\r\n", output); err != nil {
|
if _, err := fmt.Fprintf(s.clientConn, "%s\r\n", output); err != nil {
|
||||||
s.DisconnectClient()
|
s.DisconnectClient()
|
||||||
|
@ -39,6 +39,8 @@ const (
|
|||||||
rplNoTopic
|
rplNoTopic
|
||||||
rplNames
|
rplNames
|
||||||
rplEndOfNames
|
rplEndOfNames
|
||||||
|
rplWho
|
||||||
|
rplEndOfWho
|
||||||
rplNickChange
|
rplNickChange
|
||||||
rplKill
|
rplKill
|
||||||
rplMsg
|
rplMsg
|
||||||
|
Loading…
Reference in New Issue
Block a user