--HG--
branch : nmdc-ircfrontend
This commit is contained in:
. 2016-05-03 18:25:27 +12:00
parent a9f1283dd6
commit be5715a562
4 changed files with 78 additions and 107 deletions

View File

@ -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
View File

@ -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)
} }
} }

View File

@ -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 {

View File

@ -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
} }