Add cobra, move the server code into its own package
This commit is contained in:
parent
bb729a5c8e
commit
e63c012aad
57 changed files with 6910 additions and 145 deletions
589
server/bindata.go
Normal file
589
server/bindata.go
Normal file
File diff suppressed because one or more lines are too long
37
server/bindata_fs.go
Normal file
37
server/bindata_fs.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type BindataFileSystem struct{}
|
||||
|
||||
func (f BindataFileSystem) Open(name string) (http.File, error) {
|
||||
path := "dist" + name
|
||||
|
||||
data, err := Asset(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BindataFile{bytes.NewReader(data), path}, nil
|
||||
}
|
||||
|
||||
type BindataFile struct {
|
||||
*bytes.Reader
|
||||
path string
|
||||
}
|
||||
|
||||
func (f *BindataFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *BindataFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return make([]os.FileInfo, 0), nil
|
||||
}
|
||||
|
||||
func (f *BindataFile) Stat() (os.FileInfo, error) {
|
||||
return AssetInfo(f.path)
|
||||
}
|
293
server/irc.go
Normal file
293
server/irc.go
Normal file
|
@ -0,0 +1,293 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
PING = "PING"
|
||||
NICK = "NICK"
|
||||
JOIN = "JOIN"
|
||||
PART = "PART"
|
||||
MODE = "MODE"
|
||||
PRIVMSG = "PRIVMSG"
|
||||
NOTICE = "NOTICE"
|
||||
TOPIC = "TOPIC"
|
||||
QUIT = "QUIT"
|
||||
|
||||
RPL_WELCOME = "001"
|
||||
RPL_YOURHOST = "002"
|
||||
RPL_CREATED = "003"
|
||||
RPL_LUSERCLIENT = "251"
|
||||
RPL_LUSEROP = "252"
|
||||
RPL_LUSERUNKNOWN = "253"
|
||||
RPL_LUSERCHANNELS = "254"
|
||||
RPL_LUSERME = "255"
|
||||
|
||||
RPL_AWAY = "301"
|
||||
|
||||
RPL_WHOISUSER = "311"
|
||||
RPL_WHOISSERVER = "312"
|
||||
RPL_WHOISOPERATOR = "313"
|
||||
RPL_WHOISIDLE = "317"
|
||||
RPL_ENDOFWHOIS = "318"
|
||||
RPL_WHOISCHANNELS = "319"
|
||||
|
||||
RPL_TOPIC = "332"
|
||||
|
||||
RPL_NAMREPLY = "353"
|
||||
RPL_ENDOFNAMES = "366"
|
||||
|
||||
RPL_MOTD = "372"
|
||||
RPL_MOTDSTART = "375"
|
||||
RPL_ENDOFMOTD = "376"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Prefix string
|
||||
Command string
|
||||
Params []string
|
||||
Trailing string
|
||||
}
|
||||
|
||||
type IRC struct {
|
||||
conn net.Conn
|
||||
reader *bufio.Reader
|
||||
out chan string
|
||||
ready sync.WaitGroup
|
||||
nick string
|
||||
nickLock sync.Mutex
|
||||
|
||||
Messages chan *Message
|
||||
Server string
|
||||
Host string
|
||||
TLS bool
|
||||
TLSConfig *tls.Config
|
||||
Password string
|
||||
Username string
|
||||
Realname string
|
||||
}
|
||||
|
||||
func NewIRC(nick, username string) *IRC {
|
||||
return &IRC{
|
||||
nick: nick,
|
||||
Username: username,
|
||||
Realname: nick,
|
||||
Messages: make(chan *Message, 32),
|
||||
out: make(chan string, 32),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *IRC) Connect(address string) error {
|
||||
if idx := strings.Index(address, ":"); idx < 0 {
|
||||
i.Host = address
|
||||
|
||||
if i.TLS {
|
||||
address += ":6697"
|
||||
} else {
|
||||
address += ":6667"
|
||||
}
|
||||
} else {
|
||||
i.Host = address[:idx]
|
||||
}
|
||||
i.Server = address
|
||||
|
||||
dialer := &net.Dialer{Timeout: 30 * time.Second}
|
||||
|
||||
if i.TLS {
|
||||
if i.TLSConfig == nil {
|
||||
i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
if conn, err := tls.DialWithDialer(dialer, "tcp", address, i.TLSConfig); err != nil {
|
||||
return err
|
||||
} else {
|
||||
i.conn = conn
|
||||
}
|
||||
} else {
|
||||
if conn, err := dialer.Dial("tcp", address); err != nil {
|
||||
return err
|
||||
} else {
|
||||
i.conn = conn
|
||||
}
|
||||
}
|
||||
|
||||
i.reader = bufio.NewReader(i.conn)
|
||||
|
||||
if i.Password != "" {
|
||||
i.Pass(i.Password)
|
||||
}
|
||||
i.Nick(i.nick)
|
||||
i.User(i.Username, i.Realname)
|
||||
|
||||
i.ready.Add(1)
|
||||
go i.send()
|
||||
go i.recv()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IRC) Pass(password string) {
|
||||
i.write("PASS " + password)
|
||||
}
|
||||
|
||||
func (i *IRC) Nick(nick string) {
|
||||
i.write("NICK " + nick)
|
||||
|
||||
i.nickLock.Lock()
|
||||
i.nick = nick
|
||||
i.nickLock.Unlock()
|
||||
}
|
||||
|
||||
func (i *IRC) User(username, realname string) {
|
||||
i.writef("USER %s 0 * :%s", username, realname)
|
||||
}
|
||||
|
||||
func (i *IRC) Oper(name, password string) {
|
||||
i.Write("OPER " + name + " " + password)
|
||||
}
|
||||
|
||||
func (i *IRC) Mode(target, modes, params string) {
|
||||
i.Write(strings.TrimRight("MODE "+target+" "+modes+" "+params, " "))
|
||||
}
|
||||
|
||||
func (i *IRC) Quit() {
|
||||
go func() {
|
||||
i.ready.Wait()
|
||||
i.write("QUIT")
|
||||
i.conn.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
func (i *IRC) Join(channels ...string) {
|
||||
i.Write("JOIN " + strings.Join(channels, ","))
|
||||
}
|
||||
|
||||
func (i *IRC) Part(channels ...string) {
|
||||
i.Write("PART " + strings.Join(channels, ","))
|
||||
}
|
||||
|
||||
func (i *IRC) Topic(channel string) {
|
||||
i.Write("TOPIC " + channel)
|
||||
}
|
||||
|
||||
func (i *IRC) Invite(nick, channel string) {
|
||||
i.Write("INVITE " + nick + " " + channel)
|
||||
}
|
||||
|
||||
func (i *IRC) Kick(channel string, users ...string) {
|
||||
i.Write("KICK " + channel + " " + strings.Join(users, ","))
|
||||
}
|
||||
|
||||
func (i *IRC) Privmsg(target, msg string) {
|
||||
i.Writef("PRIVMSG %s :%s", target, msg)
|
||||
}
|
||||
|
||||
func (i *IRC) Notice(target, msg string) {
|
||||
i.Writef("NOTICE %s :%s", target, msg)
|
||||
}
|
||||
|
||||
func (i *IRC) Whois(nick string) {
|
||||
i.Write("WHOIS " + nick)
|
||||
}
|
||||
|
||||
func (i *IRC) Away(message string) {
|
||||
i.Write("AWAY :" + message)
|
||||
}
|
||||
|
||||
func (i *IRC) GetNick() string {
|
||||
i.nickLock.Lock()
|
||||
defer i.nickLock.Unlock()
|
||||
|
||||
return i.nick
|
||||
}
|
||||
|
||||
func (i *IRC) Write(data string) {
|
||||
i.out <- data + "\r\n"
|
||||
}
|
||||
|
||||
func (i *IRC) Writef(format string, a ...interface{}) {
|
||||
i.out <- fmt.Sprintf(format+"\r\n", a...)
|
||||
}
|
||||
|
||||
func (i *IRC) write(data string) {
|
||||
i.conn.Write([]byte(data + "\r\n"))
|
||||
}
|
||||
|
||||
func (i *IRC) writef(format string, a ...interface{}) {
|
||||
fmt.Fprintf(i.conn, format+"\r\n", a...)
|
||||
}
|
||||
|
||||
func (i *IRC) send() {
|
||||
i.ready.Wait()
|
||||
for {
|
||||
i.conn.Write([]byte(<-i.out))
|
||||
}
|
||||
}
|
||||
|
||||
func (i *IRC) recv() {
|
||||
defer i.conn.Close()
|
||||
for {
|
||||
line, err := i.reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Println("IRC connection to", i.Server, "died")
|
||||
return
|
||||
}
|
||||
|
||||
msg := parseMessage(line)
|
||||
msg.Prefix = parseUser(msg.Prefix)
|
||||
|
||||
switch msg.Command {
|
||||
case PING:
|
||||
i.write("PONG :" + msg.Trailing)
|
||||
|
||||
case RPL_WELCOME:
|
||||
i.ready.Done()
|
||||
}
|
||||
|
||||
i.Messages <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func parseMessage(line string) *Message {
|
||||
line = strings.Trim(line, "\r\n")
|
||||
msg := Message{}
|
||||
cmdStart := 0
|
||||
cmdEnd := len(line)
|
||||
|
||||
if strings.HasPrefix(line, ":") {
|
||||
cmdStart = strings.Index(line, " ") + 1
|
||||
msg.Prefix = line[1 : cmdStart-1]
|
||||
}
|
||||
|
||||
if i := strings.Index(line, " :"); i > 0 {
|
||||
cmdEnd = i
|
||||
msg.Trailing = line[i+2:]
|
||||
}
|
||||
|
||||
cmd := strings.Split(line[cmdStart:cmdEnd], " ")
|
||||
msg.Command = cmd[0]
|
||||
if len(cmd) > 1 {
|
||||
msg.Params = cmd[1:]
|
||||
}
|
||||
|
||||
if msg.Trailing != "" {
|
||||
msg.Params = append(msg.Params, msg.Trailing)
|
||||
}
|
||||
|
||||
return &msg
|
||||
}
|
||||
|
||||
func parseUser(user string) string {
|
||||
if i := strings.Index(user, "!"); i > 0 {
|
||||
return user[:i]
|
||||
}
|
||||
return user
|
||||
}
|
117
server/json_types.go
Normal file
117
server/json_types.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type WSRequest struct {
|
||||
Type string `json:"type"`
|
||||
Request json.RawMessage `json:"request"`
|
||||
}
|
||||
|
||||
type WSResponse struct {
|
||||
Type string `json:"type"`
|
||||
Response *json.RawMessage `json:"response"`
|
||||
}
|
||||
|
||||
type Connect struct {
|
||||
Name string `json:"name"`
|
||||
Server string `json:"server"`
|
||||
TLS bool `json:"tls"`
|
||||
Password string `json:"password"`
|
||||
Nick string `json:"nick"`
|
||||
Username string `json:"username"`
|
||||
Realname string `json:"realname"`
|
||||
}
|
||||
|
||||
type Nick struct {
|
||||
Server string `json:"server"`
|
||||
Old string `json:"old"`
|
||||
New string `json:"new"`
|
||||
}
|
||||
|
||||
type Join struct {
|
||||
Server string `json:"server"`
|
||||
User string `json:"user"`
|
||||
Channels []string `json:"channels"`
|
||||
}
|
||||
|
||||
type Part struct {
|
||||
Join
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type Mode struct {
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
User string `json:"user"`
|
||||
Add string `json:"add"`
|
||||
Remove string `json:"remove"`
|
||||
}
|
||||
|
||||
type Quit struct {
|
||||
Server string `json:"server"`
|
||||
User string `json:"user"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
Server string `json:"server"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to,omitempty"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Topic struct {
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
Topic string `json:"topic"`
|
||||
}
|
||||
|
||||
type Userlist struct {
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
Users []string `json:"users"`
|
||||
}
|
||||
|
||||
type MOTD struct {
|
||||
Server string `json:"server"`
|
||||
Title string `json:"title"`
|
||||
Content []string `json:"content"`
|
||||
}
|
||||
|
||||
type Invite struct {
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type Kick struct {
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type Whois struct {
|
||||
Server string `json:"server"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type WhoisReply struct {
|
||||
Nick string `json:"nick"`
|
||||
Username string `json:"username"`
|
||||
Host string `json:"host"`
|
||||
Realname string `json:"realname"`
|
||||
Server string `json:"server"`
|
||||
Channels []string `json:"channels"`
|
||||
}
|
||||
|
||||
type Away struct {
|
||||
Server string `json:"server"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Server string `json:"server"`
|
||||
Message string `json:"message"`
|
||||
}
|
207
server/message_handler.go
Normal file
207
server/message_handler.go
Normal file
|
@ -0,0 +1,207 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/khlieng/name_pending/storage"
|
||||
)
|
||||
|
||||
func handleMessages(irc *IRC, session *Session) {
|
||||
var whois WhoisReply
|
||||
userBuffers := make(map[string][]string)
|
||||
var motd MOTD
|
||||
|
||||
for msg := range irc.Messages {
|
||||
switch msg.Command {
|
||||
case NICK:
|
||||
session.sendJSON("nick", Nick{
|
||||
Server: irc.Host,
|
||||
Old: msg.Prefix,
|
||||
New: msg.Trailing,
|
||||
})
|
||||
|
||||
channelStore.RenameUser(msg.Prefix, msg.Trailing, irc.Host)
|
||||
|
||||
case JOIN:
|
||||
user := msg.Prefix
|
||||
|
||||
session.sendJSON("join", Join{
|
||||
Server: irc.Host,
|
||||
User: user,
|
||||
Channels: msg.Params,
|
||||
})
|
||||
|
||||
channelStore.AddUser(user, irc.Host, msg.Params[0])
|
||||
|
||||
if user == irc.GetNick() {
|
||||
session.user.AddChannel(storage.Channel{
|
||||
Server: irc.Host,
|
||||
Name: msg.Params[0],
|
||||
})
|
||||
}
|
||||
|
||||
case PART:
|
||||
user := msg.Prefix
|
||||
|
||||
session.sendJSON("part", Part{
|
||||
Join: Join{
|
||||
Server: irc.Host,
|
||||
User: user,
|
||||
Channels: msg.Params,
|
||||
},
|
||||
Reason: msg.Trailing,
|
||||
})
|
||||
|
||||
channelStore.RemoveUser(user, irc.Host, msg.Params[0])
|
||||
|
||||
if user == irc.GetNick() {
|
||||
session.user.RemoveChannel(irc.Host, msg.Params[0])
|
||||
}
|
||||
|
||||
case MODE:
|
||||
target := msg.Params[0]
|
||||
if len(msg.Params) > 2 && isChannel(target) {
|
||||
mode := parseMode(msg.Params[1])
|
||||
mode.Server = irc.Host
|
||||
mode.Channel = target
|
||||
mode.User = msg.Params[2]
|
||||
|
||||
session.sendJSON("mode", mode)
|
||||
|
||||
channelStore.SetMode(irc.Host, target, msg.Params[2], mode.Add, mode.Remove)
|
||||
}
|
||||
|
||||
case PRIVMSG, NOTICE:
|
||||
if msg.Params[0] == irc.GetNick() {
|
||||
session.sendJSON("pm", Chat{
|
||||
Server: irc.Host,
|
||||
From: msg.Prefix,
|
||||
Message: msg.Trailing,
|
||||
})
|
||||
} else {
|
||||
session.sendJSON("message", Chat{
|
||||
Server: irc.Host,
|
||||
From: msg.Prefix,
|
||||
To: msg.Params[0],
|
||||
Message: msg.Trailing,
|
||||
})
|
||||
}
|
||||
|
||||
if msg.Params[0] != "*" {
|
||||
session.user.LogMessage(irc.Host, msg.Prefix, msg.Params[0], msg.Trailing)
|
||||
}
|
||||
|
||||
case QUIT:
|
||||
user := msg.Prefix
|
||||
|
||||
session.sendJSON("quit", Quit{
|
||||
Server: irc.Host,
|
||||
User: user,
|
||||
Reason: msg.Trailing,
|
||||
})
|
||||
|
||||
channelStore.RemoveUserAll(user, irc.Host)
|
||||
|
||||
case RPL_WELCOME,
|
||||
RPL_YOURHOST,
|
||||
RPL_CREATED,
|
||||
RPL_LUSERCLIENT,
|
||||
RPL_LUSEROP,
|
||||
RPL_LUSERUNKNOWN,
|
||||
RPL_LUSERCHANNELS,
|
||||
RPL_LUSERME:
|
||||
session.sendJSON("pm", Chat{
|
||||
Server: irc.Host,
|
||||
From: msg.Prefix,
|
||||
Message: strings.Join(msg.Params[1:], " "),
|
||||
})
|
||||
|
||||
case RPL_WHOISUSER:
|
||||
whois.Nick = msg.Params[1]
|
||||
whois.Username = msg.Params[2]
|
||||
whois.Host = msg.Params[3]
|
||||
whois.Realname = msg.Params[5]
|
||||
|
||||
case RPL_WHOISSERVER:
|
||||
whois.Server = msg.Params[2]
|
||||
|
||||
case RPL_WHOISCHANNELS:
|
||||
whois.Channels = append(whois.Channels, strings.Split(strings.TrimRight(msg.Trailing, " "), " ")...)
|
||||
|
||||
case RPL_ENDOFWHOIS:
|
||||
session.sendJSON("whois", whois)
|
||||
|
||||
whois = WhoisReply{}
|
||||
|
||||
case RPL_TOPIC:
|
||||
session.sendJSON("topic", Topic{
|
||||
Server: irc.Host,
|
||||
Channel: msg.Params[1],
|
||||
Topic: msg.Trailing,
|
||||
})
|
||||
|
||||
channelStore.SetTopic(msg.Trailing, irc.Host, msg.Params[1])
|
||||
|
||||
case RPL_NAMREPLY:
|
||||
users := strings.Split(msg.Trailing, " ")
|
||||
userBuffer := userBuffers[msg.Params[2]]
|
||||
userBuffers[msg.Params[2]] = append(userBuffer, users...)
|
||||
|
||||
case RPL_ENDOFNAMES:
|
||||
channel := msg.Params[1]
|
||||
users := userBuffers[channel]
|
||||
|
||||
session.sendJSON("users", Userlist{
|
||||
Server: irc.Host,
|
||||
Channel: channel,
|
||||
Users: users,
|
||||
})
|
||||
|
||||
channelStore.SetUsers(users, irc.Host, channel)
|
||||
delete(userBuffers, channel)
|
||||
|
||||
case RPL_MOTDSTART:
|
||||
motd.Server = irc.Host
|
||||
motd.Title = msg.Trailing
|
||||
|
||||
case RPL_MOTD:
|
||||
motd.Content = append(motd.Content, msg.Trailing)
|
||||
|
||||
case RPL_ENDOFMOTD:
|
||||
session.sendJSON("motd", motd)
|
||||
|
||||
motd = MOTD{}
|
||||
|
||||
default:
|
||||
printMessage(msg, irc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseMode(mode string) *Mode {
|
||||
m := Mode{}
|
||||
add := false
|
||||
|
||||
for _, c := range mode {
|
||||
if c == '+' {
|
||||
add = true
|
||||
} else if c == '-' {
|
||||
add = false
|
||||
} else if add {
|
||||
m.Add += string(c)
|
||||
} else {
|
||||
m.Remove += string(c)
|
||||
}
|
||||
}
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
func isChannel(s string) bool {
|
||||
return strings.IndexAny(s, "&#+!") == 0
|
||||
}
|
||||
|
||||
func printMessage(msg *Message, irc *IRC) {
|
||||
log.Println(irc.GetNick()+":", msg.Prefix, msg.Command, msg.Params, msg.Trailing)
|
||||
}
|
124
server/server.go
Normal file
124
server/server.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/khlieng/name_pending/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
|
||||
"github.com/khlieng/name_pending/Godeps/_workspace/src/golang.org/x/net/websocket"
|
||||
|
||||
"github.com/khlieng/name_pending/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
channelStore *storage.ChannelStore
|
||||
sessions map[string]*Session
|
||||
sessionLock sync.Mutex
|
||||
fs http.Handler
|
||||
files []File
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Path string
|
||||
ContentType string
|
||||
}
|
||||
|
||||
func Run(port int, development bool) {
|
||||
defer storage.Cleanup()
|
||||
|
||||
channelStore = storage.NewChannelStore()
|
||||
sessions = make(map[string]*Session)
|
||||
fs = http.FileServer(BindataFileSystem{})
|
||||
|
||||
files = []File{
|
||||
File{"/bundle.js", "text/javascript"},
|
||||
File{"/css/style.css", "text/css"},
|
||||
File{"/css/fontello.css", "text/css"},
|
||||
File{"/font/fontello.eot", "application/vnd.ms-fontobject"},
|
||||
File{"/font/fontello.svg", "image/svg+xml"},
|
||||
File{"/font/fontello.ttf", "application/x-font-ttf"},
|
||||
File{"/font/fontello.woff", "application/font-woff"},
|
||||
}
|
||||
|
||||
if !development {
|
||||
reconnect()
|
||||
}
|
||||
|
||||
router := httprouter.New()
|
||||
|
||||
router.Handler("GET", "/ws", websocket.Handler(handleWS))
|
||||
router.NotFound = serveFiles
|
||||
|
||||
log.Println("Listening on port", port)
|
||||
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), router))
|
||||
}
|
||||
|
||||
func reconnect() {
|
||||
for _, user := range storage.LoadUsers() {
|
||||
session := NewSession()
|
||||
session.user = user
|
||||
sessions[user.UUID] = session
|
||||
go session.write()
|
||||
|
||||
channels := user.GetChannels()
|
||||
|
||||
for _, server := range user.GetServers() {
|
||||
irc := NewIRC(server.Nick, server.Username)
|
||||
irc.TLS = server.TLS
|
||||
irc.Password = server.Password
|
||||
irc.Realname = server.Realname
|
||||
|
||||
go func() {
|
||||
err := irc.Connect(server.Address)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
session.setIRC(irc.Host, irc)
|
||||
|
||||
go handleMessages(irc, session)
|
||||
|
||||
var joining []string
|
||||
for _, channel := range channels {
|
||||
if channel.Server == server.Address {
|
||||
joining = append(joining, channel.Name)
|
||||
}
|
||||
}
|
||||
irc.Join(joining...)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serveFiles(w http.ResponseWriter, r *http.Request) {
|
||||
var ext string
|
||||
|
||||
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
ext = ".gz"
|
||||
}
|
||||
|
||||
if r.URL.Path == "/" {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
r.URL.Path = "/index.html" + ext
|
||||
fs.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if strings.HasSuffix(r.URL.Path, file.Path) {
|
||||
w.Header().Set("Content-Type", file.ContentType)
|
||||
r.URL.Path = file.Path + ext
|
||||
fs.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
r.URL.Path = "/index.html" + ext
|
||||
|
||||
fs.ServeHTTP(w, r)
|
||||
}
|
96
server/session.go
Normal file
96
server/session.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/khlieng/name_pending/Godeps/_workspace/src/golang.org/x/net/websocket"
|
||||
"github.com/khlieng/name_pending/storage"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
irc map[string]*IRC
|
||||
ircLock sync.Mutex
|
||||
|
||||
ws map[string]*WebSocket
|
||||
wsLock sync.Mutex
|
||||
out chan []byte
|
||||
|
||||
user *storage.User
|
||||
}
|
||||
|
||||
func NewSession() *Session {
|
||||
return &Session{
|
||||
irc: make(map[string]*IRC),
|
||||
ws: make(map[string]*WebSocket),
|
||||
out: make(chan []byte, 32),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) getIRC(server string) (*IRC, bool) {
|
||||
s.ircLock.Lock()
|
||||
irc, ok := s.irc[server]
|
||||
s.ircLock.Unlock()
|
||||
|
||||
return irc, ok
|
||||
}
|
||||
|
||||
func (s *Session) setIRC(server string, irc *IRC) {
|
||||
s.ircLock.Lock()
|
||||
s.irc[server] = irc
|
||||
s.ircLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Session) deleteIRC(server string) {
|
||||
s.ircLock.Lock()
|
||||
delete(s.irc, server)
|
||||
s.ircLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Session) numIRC() int {
|
||||
s.ircLock.Lock()
|
||||
n := len(s.irc)
|
||||
s.ircLock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (s *Session) setWS(addr string, ws *websocket.Conn) {
|
||||
socket := NewWebSocket(ws)
|
||||
go socket.write()
|
||||
|
||||
s.wsLock.Lock()
|
||||
s.ws[addr] = socket
|
||||
s.wsLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Session) deleteWS(addr string) {
|
||||
s.wsLock.Lock()
|
||||
delete(s.ws, addr)
|
||||
s.wsLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Session) sendJSON(t string, v interface{}) {
|
||||
data, _ := json.Marshal(v)
|
||||
raw := json.RawMessage(data)
|
||||
res, _ := json.Marshal(WSResponse{Type: t, Response: &raw})
|
||||
|
||||
s.out <- res
|
||||
}
|
||||
|
||||
func (s *Session) sendError(err error, server string) {
|
||||
s.sendJSON("error", Error{
|
||||
Server: server,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) write() {
|
||||
for res := range s.out {
|
||||
s.wsLock.Lock()
|
||||
for _, ws := range s.ws {
|
||||
ws.Out <- res
|
||||
}
|
||||
s.wsLock.Unlock()
|
||||
}
|
||||
}
|
24
server/websocket.go
Normal file
24
server/websocket.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/khlieng/name_pending/Godeps/_workspace/src/golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
type WebSocket struct {
|
||||
conn *websocket.Conn
|
||||
|
||||
Out chan []byte
|
||||
}
|
||||
|
||||
func NewWebSocket(ws *websocket.Conn) *WebSocket {
|
||||
return &WebSocket{
|
||||
conn: ws,
|
||||
Out: make(chan []byte, 32),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebSocket) write() {
|
||||
for {
|
||||
w.conn.Write(<-w.Out)
|
||||
}
|
||||
}
|
204
server/websocket_handler.go
Normal file
204
server/websocket_handler.go
Normal file
|
@ -0,0 +1,204 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/khlieng/name_pending/Godeps/_workspace/src/golang.org/x/net/websocket"
|
||||
"github.com/khlieng/name_pending/storage"
|
||||
)
|
||||
|
||||
func handleWS(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
var session *Session
|
||||
var UUID string
|
||||
var req WSRequest
|
||||
|
||||
addr := ws.Request().RemoteAddr
|
||||
|
||||
log.Println(addr, "connected")
|
||||
|
||||
for {
|
||||
err := websocket.JSON.Receive(ws, &req)
|
||||
if err != nil {
|
||||
if session != nil {
|
||||
session.deleteWS(addr)
|
||||
}
|
||||
|
||||
log.Println(addr, "disconnected")
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Type {
|
||||
case "uuid":
|
||||
json.Unmarshal(req.Request, &UUID)
|
||||
|
||||
log.Println(addr, "set UUID", UUID)
|
||||
|
||||
sessionLock.Lock()
|
||||
|
||||
if storedSession, exists := sessions[UUID]; exists {
|
||||
sessionLock.Unlock()
|
||||
session = storedSession
|
||||
|
||||
log.Println(addr, "attached to", session.numIRC(), "existing IRC connections")
|
||||
|
||||
channels := session.user.GetChannels()
|
||||
for i, channel := range channels {
|
||||
channels[i].Topic = channelStore.GetTopic(channel.Server, channel.Name)
|
||||
}
|
||||
|
||||
session.sendJSON("channels", channels)
|
||||
session.sendJSON("servers", session.user.GetServers())
|
||||
|
||||
for _, channel := range channels {
|
||||
session.sendJSON("users", Userlist{
|
||||
Server: channel.Server,
|
||||
Channel: channel.Name,
|
||||
Users: channelStore.GetUsers(channel.Server, channel.Name),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
session = NewSession()
|
||||
session.user = storage.NewUser(UUID)
|
||||
|
||||
sessions[UUID] = session
|
||||
sessionLock.Unlock()
|
||||
|
||||
go session.write()
|
||||
}
|
||||
|
||||
session.setWS(addr, ws)
|
||||
|
||||
case "connect":
|
||||
var data Connect
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if _, ok := session.getIRC(data.Server); !ok {
|
||||
log.Println(addr, "connecting to", data.Server)
|
||||
|
||||
irc := NewIRC(data.Nick, data.Username)
|
||||
irc.TLS = data.TLS
|
||||
irc.Password = data.Password
|
||||
irc.Realname = data.Realname
|
||||
|
||||
if idx := strings.Index(data.Server, ":"); idx < 0 {
|
||||
session.setIRC(data.Server, irc)
|
||||
} else {
|
||||
session.setIRC(data.Server[:idx], irc)
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := irc.Connect(data.Server)
|
||||
if err != nil {
|
||||
session.deleteIRC(irc.Host)
|
||||
session.sendError(err, irc.Host)
|
||||
log.Println(err)
|
||||
} else {
|
||||
go handleMessages(irc, session)
|
||||
|
||||
session.user.AddServer(storage.Server{
|
||||
Name: data.Name,
|
||||
Address: irc.Host,
|
||||
TLS: data.TLS,
|
||||
Password: data.Password,
|
||||
Nick: data.Nick,
|
||||
Username: data.Username,
|
||||
Realname: data.Realname,
|
||||
})
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
log.Println(addr, "already connected to", data.Server)
|
||||
}
|
||||
|
||||
case "join":
|
||||
var data Join
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Join(data.Channels...)
|
||||
}
|
||||
|
||||
case "part":
|
||||
var data Part
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Part(data.Channels...)
|
||||
}
|
||||
|
||||
case "quit":
|
||||
var data Quit
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Quit()
|
||||
session.deleteIRC(data.Server)
|
||||
channelStore.RemoveUserAll(irc.GetNick(), data.Server)
|
||||
session.user.RemoveServer(data.Server)
|
||||
}
|
||||
|
||||
case "chat":
|
||||
var data Chat
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Privmsg(data.To, data.Message)
|
||||
}
|
||||
|
||||
case "nick":
|
||||
var data Nick
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Nick(data.New)
|
||||
session.user.SetNick(data.New, data.Server)
|
||||
}
|
||||
|
||||
case "invite":
|
||||
var data Invite
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Invite(data.User, data.Channel)
|
||||
}
|
||||
|
||||
case "kick":
|
||||
var data Invite
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Kick(data.Channel, data.User)
|
||||
}
|
||||
|
||||
case "whois":
|
||||
var data Whois
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Whois(data.User)
|
||||
}
|
||||
|
||||
case "away":
|
||||
var data Away
|
||||
|
||||
json.Unmarshal(req.Request, &data)
|
||||
|
||||
if irc, ok := session.getIRC(data.Server); ok {
|
||||
irc.Away(data.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue