wip
--HG-- branch : nmdc-ircfrontend
This commit is contained in:
parent
a9f1283dd6
commit
be5715a562
53
client.go
53
client.go
@ -4,55 +4,51 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
server *Server
|
||||||
|
connection net.Conn
|
||||||
|
signalChan chan signalCode
|
||||||
|
outputChan chan string
|
||||||
|
nick string
|
||||||
|
key string
|
||||||
|
registered bool
|
||||||
|
connected bool
|
||||||
|
operator bool
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) joinChannel(channelName string) {
|
func (c *Client) joinChannel(channelName string) {
|
||||||
newChannel := false
|
newChannel := false
|
||||||
|
|
||||||
channelKey := strings.ToLower(channelName)
|
channelKey := strings.ToLower(channelName)
|
||||||
channel, exists := c.server.channelMap[channelKey]
|
if channelKey != BLESSED_CHANNEL {
|
||||||
if exists == false {
|
panic("?")
|
||||||
mode := ChannelMode{secret: true,
|
|
||||||
topicLocked: true,
|
|
||||||
noExternal: true}
|
|
||||||
channel = &Channel{name: channelName,
|
|
||||||
topic: "",
|
|
||||||
clientMap: make(map[string]*Client),
|
|
||||||
modeMap: make(map[string]*ClientMode),
|
|
||||||
mode: mode}
|
|
||||||
c.server.channelMap[channelKey] = channel
|
|
||||||
newChannel = true
|
|
||||||
}
|
}
|
||||||
|
channel := c.server.channel
|
||||||
|
|
||||||
if _, inChannel := channel.clientMap[c.key]; inChannel {
|
if _, inChannel := channel.clientMap[c.key]; inChannel {
|
||||||
//Client is already in the channel, do nothing
|
//Client is already in the channel, do nothing
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mode := new(ClientMode)
|
// Send notifications to /other/ clients that /we/ joined this room
|
||||||
if newChannel {
|
|
||||||
//If they created the channel, make them op
|
|
||||||
mode.operator = true
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.clientMap[c.key] = c
|
|
||||||
channel.modeMap[c.key] = mode
|
|
||||||
c.channelMap[channelKey] = channel
|
|
||||||
|
|
||||||
for _, client := range channel.clientMap {
|
for _, client := range channel.clientMap {
|
||||||
client.reply(rplJoin, c.nick, channel.name)
|
client.reply(rplJoin, c.nick, BLESSED_CHANNEL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transmit topic
|
||||||
if channel.topic != "" {
|
if channel.topic != "" {
|
||||||
c.reply(rplTopic, channel.name, channel.topic)
|
c.reply(rplTopic, BLESSED_CHANNEL, channel.topic)
|
||||||
} else {
|
} else {
|
||||||
c.reply(rplNoTopic, channel.name)
|
c.reply(rplNoTopic, BLESSED_CHANNEL)
|
||||||
}
|
}
|
||||||
|
|
||||||
//The capacity sets the max number of nicks to send per message
|
// Transmit the list of joined users to us
|
||||||
nicks := make([]string, 0, 128)
|
nicks := make([]string, 0, NICKS_PER_PROTOMSG)
|
||||||
|
|
||||||
for _, client := range channel.clientMap {
|
for _, client := range channel.clientMap {
|
||||||
prefix := ""
|
prefix := ""
|
||||||
@ -167,8 +163,7 @@ func (c *Client) clientThread() {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// Implicit part from all channels
|
// Implicit part from all channels
|
||||||
|
// FIXME also drop the upstream connection
|
||||||
delete(c.server.clientMap, c.key)
|
|
||||||
c.connection.Close()
|
c.connection.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
17
main.go
17
main.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"libnmdc"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
@ -15,16 +16,10 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
if len(*serverName) == 0 {
|
||||||
log.Println("Starting server...")
|
|
||||||
|
|
||||||
server := NewServer(*serverName)
|
|
||||||
|
|
||||||
if len(server.name) == 0 {
|
|
||||||
log.Println("Please specify the -servername parameter.")
|
log.Println("Please specify the -servername parameter.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
server.motd = "Connected to " + *serverName
|
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", *ircAddress)
|
listener, err := net.Listen("tcp", *ircAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -32,8 +27,6 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go server.Run()
|
|
||||||
|
|
||||||
log.Printf("Listening on %s", *ircAddress)
|
log.Printf("Listening on %s", *ircAddress)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -43,6 +36,12 @@ func main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spin up an IRC server for the new user.
|
||||||
|
// The upstream connection doesn't get launched until we hear a nick
|
||||||
|
// from the irc client
|
||||||
|
|
||||||
|
server := NewServer(*serverName, libnmdc.HubAddress(*dcAddress))
|
||||||
|
go server.Run()
|
||||||
server.HandleConnection(conn)
|
server.HandleConnection(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
83
server.go
83
server.go
@ -2,25 +2,29 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"libnmdc"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Server struct {
|
||||||
nickRegexp = regexp.MustCompile(`^[a-zA-Z\[\]_^{|}][a-zA-Z0-9\[\]_^{|}]*$`)
|
eventChan chan Event
|
||||||
channelRegexp = regexp.MustCompile(`^#[a-zA-Z0-9_\-]+$`)
|
running bool
|
||||||
)
|
name string
|
||||||
|
client *Client
|
||||||
|
channel Channel // Single blessed channel
|
||||||
|
upstreamAddr libnmdc.HubAddress
|
||||||
|
upstream libnmdc.HubConnection
|
||||||
|
motd string
|
||||||
|
}
|
||||||
|
|
||||||
func NewServer(name string) *Server {
|
func NewServer(name string, upstream libnmdc.HubAddress) *Server {
|
||||||
return &Server{eventChan: make(chan Event),
|
return &Server{eventChan: make(chan Event),
|
||||||
name: name,
|
name: name,
|
||||||
clientMap: make(map[string]*Client),
|
client: nil,
|
||||||
motd: "",
|
motd: "Connected to " + name,
|
||||||
|
upstreamAddr: upstream,
|
||||||
channel: Channel{
|
channel: Channel{
|
||||||
name: BLESSED_CHANNEL,
|
|
||||||
topic: name,
|
|
||||||
clientMap: make(map[string]*Client),
|
clientMap: make(map[string]*Client),
|
||||||
modeMap: make(map[string]*ClientMode),
|
modeMap: make(map[string]*ClientMode),
|
||||||
},
|
},
|
||||||
@ -45,13 +49,6 @@ func (s *Server) HandleConnection(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleEvent(e Event) {
|
func (s *Server) handleEvent(e Event) {
|
||||||
defer func(event Event) {
|
|
||||||
err := recover()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Recovered from error when handling event: %+v", event)
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}(e)
|
|
||||||
|
|
||||||
switch e.event {
|
switch e.event {
|
||||||
case connected:
|
case connected:
|
||||||
@ -104,12 +101,20 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
|
|||||||
client.disconnect()
|
client.disconnect()
|
||||||
|
|
||||||
case "USER":
|
case "USER":
|
||||||
|
if client.registered == true {
|
||||||
|
client.reply(rplKill, "You're already registered.", "")
|
||||||
|
client.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
if client.nick == "" {
|
if client.nick == "" {
|
||||||
client.reply(rplKill, "Your nickname is already being used", "")
|
client.reply(rplKill, "Your nickname is already being used", "")
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
client.reply(rplWelcome)
|
client.reply(rplWelcome)
|
||||||
client.registered = true
|
client.registered = true
|
||||||
|
|
||||||
|
// Spawn
|
||||||
}
|
}
|
||||||
|
|
||||||
case "JOIN":
|
case "JOIN":
|
||||||
@ -155,15 +160,21 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
|
|||||||
|
|
||||||
message := strings.Join(args[1:], " ")
|
message := strings.Join(args[1:], " ")
|
||||||
|
|
||||||
if strings.ToLower(args[0]) == BLESSED_CHANNEL {
|
// IRC is case-insensitive case-preserving. We can respect that for the
|
||||||
|
// channel name, but not really for user nicks
|
||||||
|
recipient := strings.ToLower(args[0])
|
||||||
|
|
||||||
|
if recipient == BLESSED_CHANNEL {
|
||||||
for _, c := range s.channel.clientMap {
|
for _, c := range s.channel.clientMap {
|
||||||
if c != client {
|
if c != client {
|
||||||
c.reply(rplMsg, client.nick, args[0], message)
|
c.reply(rplMsg, client.nick, args[0], message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if client2, clientExists := s.clientMap[strings.ToLower(args[0])]; clientExists {
|
} else if nmdcUser, clientExists := s.upstream.Users[args[0]]; clientExists {
|
||||||
client2.reply(rplMsg, client.nick, client2.nick, message)
|
|
||||||
|
s.upstream.SayPrivate(recipient, message)
|
||||||
|
// client2.reply(rplMsg, client.nick, client2.nick, message)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
client.reply(errNoSuchNick, args[0])
|
client.reply(errNoSuchNick, args[0])
|
||||||
@ -197,7 +208,7 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
|
|||||||
|
|
||||||
// Valid topic get
|
// Valid topic get
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
client.reply(rplTopic, s.channel.name, s.name)
|
client.reply(rplTopic, BLESSED_CHANNEL, s.upstream.HubName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +222,7 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
listItem := fmt.Sprintf("%s %d :%s", s.channel.name, len(s.channel.clientMap), s.channel.topic)
|
listItem := fmt.Sprintf("%s %d :%s", BLESSED_CHANNEL, len(s.channel.clientMap), s.upstream.HubName)
|
||||||
client.reply(rplList, listItem)
|
client.reply(rplList, listItem)
|
||||||
client.reply(rplListEnd)
|
client.reply(rplListEnd)
|
||||||
|
|
||||||
@ -244,28 +255,8 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.operator == false {
|
client.reply(errNoPriv)
|
||||||
client.reply(errNoPriv)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
client.reply(errMoreArgs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nick := args[0]
|
|
||||||
|
|
||||||
reason := strings.Join(args[1:], " ")
|
|
||||||
|
|
||||||
client, exists := s.clientMap[strings.ToLower(nick)]
|
|
||||||
if !exists {
|
|
||||||
client.reply(errNoSuchNick, nick)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.reply(rplKill, client.nick, reason)
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
case "KICK":
|
case "KICK":
|
||||||
if client.registered == false {
|
if client.registered == false {
|
||||||
|
32
typedefs.go
32
typedefs.go
@ -1,34 +1,22 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VERSION = "1.0.0"
|
VERSION = "1.0.0"
|
||||||
APP_DESCRIPTION = "nmdc-ircfrontend v" + VERSION
|
APP_DESCRIPTION = "nmdc-ircfrontend v" + VERSION
|
||||||
BLESSED_CHANNEL = "#chat" // must be lowercase
|
BLESSED_CHANNEL = "#chat" // must be lowercase
|
||||||
BLESSED_CHANNEL_MODE = "n" // means that you have to be in the channel to chat, but that's it
|
BLESSED_CHANNEL_MODE = "n" // means that you have to be in the channel to chat, but that's it
|
||||||
|
|
||||||
|
NICKS_PER_PROTOMSG = 128 //The capacity sets the max number of nicks to send per message
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
var (
|
||||||
eventChan chan Event
|
nickRegexp = regexp.MustCompile(`^[a-zA-Z\[\]_^{|}][a-zA-Z0-9\[\]_^{|}]*$`)
|
||||||
running bool
|
channelRegexp = regexp.MustCompile(`^#[a-zA-Z0-9_\-]+$`)
|
||||||
name string
|
)
|
||||||
clientMap map[string]*Client // Map of nicks -> clients
|
|
||||||
channel Channel // Single blessed channel
|
|
||||||
motd string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
server *Server
|
|
||||||
connection net.Conn
|
|
||||||
signalChan chan signalCode
|
|
||||||
outputChan chan string
|
|
||||||
nick string
|
|
||||||
key string
|
|
||||||
registered bool
|
|
||||||
connected bool
|
|
||||||
operator bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type eventType int
|
type eventType int
|
||||||
|
|
||||||
@ -45,8 +33,6 @@ type Event struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
name string
|
|
||||||
topic string
|
|
||||||
clientMap map[string]*Client
|
clientMap map[string]*Client
|
||||||
modeMap map[string]*ClientMode
|
modeMap map[string]*ClientMode
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user