--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"
"fmt"
"io"
"net"
"strings"
"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) {
newChannel := false
channelKey := strings.ToLower(channelName)
channel, exists := c.server.channelMap[channelKey]
if exists == false {
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
if channelKey != BLESSED_CHANNEL {
panic("?")
}
channel := c.server.channel
if _, inChannel := channel.clientMap[c.key]; inChannel {
//Client is already in the channel, do nothing
return
}
mode := new(ClientMode)
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
// Send notifications to /other/ clients that /we/ joined this room
for _, client := range channel.clientMap {
client.reply(rplJoin, c.nick, channel.name)
client.reply(rplJoin, c.nick, BLESSED_CHANNEL)
}
// Transmit topic
if channel.topic != "" {
c.reply(rplTopic, channel.name, channel.topic)
c.reply(rplTopic, BLESSED_CHANNEL, channel.topic)
} else {
c.reply(rplNoTopic, channel.name)
c.reply(rplNoTopic, BLESSED_CHANNEL)
}
//The capacity sets the max number of nicks to send per message
nicks := make([]string, 0, 128)
// Transmit the list of joined users to us
nicks := make([]string, 0, NICKS_PER_PROTOMSG)
for _, client := range channel.clientMap {
prefix := ""
@ -167,8 +163,7 @@ func (c *Client) clientThread() {
defer func() {
// Implicit part from all channels
delete(c.server.clientMap, c.key)
// FIXME also drop the upstream connection
c.connection.Close()
}()

17
main.go
View File

@ -2,6 +2,7 @@ package main
import (
"flag"
"libnmdc"
"log"
"net"
)
@ -15,16 +16,10 @@ var (
func main() {
flag.Parse()
log.Println("Starting server...")
server := NewServer(*serverName)
if len(server.name) == 0 {
if len(*serverName) == 0 {
log.Println("Please specify the -servername parameter.")
return
}
server.motd = "Connected to " + *serverName
listener, err := net.Listen("tcp", *ircAddress)
if err != nil {
@ -32,8 +27,6 @@ func main() {
return
}
go server.Run()
log.Printf("Listening on %s", *ircAddress)
for {
@ -43,6 +36,12 @@ func main() {
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)
}
}

View File

@ -2,25 +2,29 @@ package main
import (
"fmt"
"log"
"libnmdc"
"net"
"regexp"
"strings"
)
var (
nickRegexp = regexp.MustCompile(`^[a-zA-Z\[\]_^{|}][a-zA-Z0-9\[\]_^{|}]*$`)
channelRegexp = regexp.MustCompile(`^#[a-zA-Z0-9_\-]+$`)
)
type Server struct {
eventChan chan Event
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),
name: name,
clientMap: make(map[string]*Client),
motd: "",
name: name,
client: nil,
motd: "Connected to " + name,
upstreamAddr: upstream,
channel: Channel{
name: BLESSED_CHANNEL,
topic: name,
clientMap: make(map[string]*Client),
modeMap: make(map[string]*ClientMode),
},
@ -45,13 +49,6 @@ func (s *Server) HandleConnection(conn net.Conn) {
}
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 {
case connected:
@ -104,12 +101,20 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
client.disconnect()
case "USER":
if client.registered == true {
client.reply(rplKill, "You're already registered.", "")
client.disconnect()
}
if client.nick == "" {
client.reply(rplKill, "Your nickname is already being used", "")
client.disconnect()
} else {
client.reply(rplWelcome)
client.registered = true
// Spawn
}
case "JOIN":
@ -155,15 +160,21 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
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 {
if c != client {
c.reply(rplMsg, client.nick, args[0], message)
}
}
} else if client2, clientExists := s.clientMap[strings.ToLower(args[0])]; clientExists {
client2.reply(rplMsg, client.nick, client2.nick, message)
} else if nmdcUser, clientExists := s.upstream.Users[args[0]]; clientExists {
s.upstream.SayPrivate(recipient, message)
// client2.reply(rplMsg, client.nick, client2.nick, message)
} else {
client.reply(errNoSuchNick, args[0])
@ -197,7 +208,7 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
// Valid topic get
if len(args) == 1 {
client.reply(rplTopic, s.channel.name, s.name)
client.reply(rplTopic, BLESSED_CHANNEL, s.upstream.HubName)
return
}
@ -211,7 +222,7 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
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(rplListEnd)
@ -244,28 +255,8 @@ func (s *Server) handleCommand(client *Client, command string, args []string) {
return
}
if client.operator == false {
client.reply(errNoPriv)
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()
client.reply(errNoPriv)
return
case "KICK":
if client.registered == false {

View File

@ -1,34 +1,22 @@
package main
import "net"
import (
"regexp"
)
const (
VERSION = "1.0.0"
APP_DESCRIPTION = "nmdc-ircfrontend v" + VERSION
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
NICKS_PER_PROTOMSG = 128 //The capacity sets the max number of nicks to send per message
)
type Server struct {
eventChan chan Event
running bool
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
}
var (
nickRegexp = regexp.MustCompile(`^[a-zA-Z\[\]_^{|}][a-zA-Z0-9\[\]_^{|}]*$`)
channelRegexp = regexp.MustCompile(`^#[a-zA-Z0-9_\-]+$`)
)
type eventType int
@ -45,8 +33,6 @@ type Event struct {
}
type Channel struct {
name string
topic string
clientMap map[string]*Client
modeMap map[string]*ClientMode
}