diff --git a/cmd/jounce/main.go b/cmd/jounce/main.go index e5a055c..bbfc42c 100644 --- a/cmd/jounce/main.go +++ b/cmd/jounce/main.go @@ -15,11 +15,19 @@ func main() { log.Fatalf("failed to start listener: %v", err) } + // TODO: load from config/DB s := jounce.Server{ Hostname: "localhost", - Logger: log.New(log.Writer(), "", log.LstdFlags), + Logger: log.New(log.Writer(), "", log.LstdFlags), + Upstreams: []jounce.Upstream{{ + Addr: "chat.freenode.net:6697", + Nick: "jounce", + Username: "jounce", + Realname: "jounce", + }}, } log.Printf("Server listening on %v", addr) + go s.Run() log.Fatal(s.Serve(ln)) } diff --git a/server.go b/server.go index 600158a..cab6afd 100644 --- a/server.go +++ b/server.go @@ -1,6 +1,7 @@ package jounce import ( + "crypto/tls" "fmt" "io" "log" @@ -74,7 +75,7 @@ func (c *downstreamConn) handleMessage(msg *irc.Message) error { // TODO: handle params return c.WriteMessage(&irc.Message{ Command: "PONG", - Params: []string{c.srv.Hostname}, + Params: []string{c.srv.Hostname}, }) default: if c.registered { @@ -146,7 +147,7 @@ func (c *downstreamConn) register() error { err = c.WriteMessage(&irc.Message{ Command: irc.ERR_NOMOTD, - Params: []string{c.nick, "No MOTD"}, + Params: []string{c.nick, "No MOTD"}, }) if err != nil { return err @@ -172,9 +173,23 @@ func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error { } } -type Server struct{ - Hostname string - Logger Logger +type upstreamConn struct { + net net.Conn + irc *irc.Conn + srv *Server +} + +type Upstream struct { + Addr string + Nick string + Username string + Realname string +} + +type Server struct { + Hostname string + Logger Logger + Upstreams []Upstream // TODO: per-user } func (s *Server) prefix() *irc.Prefix { @@ -193,7 +208,7 @@ func (s *Server) handleConn(netConn net.Conn) error { } else if err != nil { return fmt.Errorf("failed to read IRC command: %v", err) } - s.Logger.Print(msg) + s.Logger.Printf("Downstream message: %v", msg) err = c.handleMessage(msg) if ircErr, ok := err.(ircError); ok { @@ -213,6 +228,53 @@ func (s *Server) handleConn(netConn net.Conn) error { return c.Close() } +func (s *Server) connect(upstream *Upstream) error { + s.Logger.Printf("Connecting to %v", upstream.Addr) + + netConn, err := tls.Dial("tcp", upstream.Addr, nil) + if err != nil { + return fmt.Errorf("failed to dial %q: %v", upstream.Addr, err) + } + + c := upstreamConn{net: netConn, irc: irc.NewConn(netConn), srv: s} + defer netConn.Close() + + err = c.irc.WriteMessage(&irc.Message{ + Command: "NICK", + Params: []string{upstream.Nick}, + }) + if err != nil { + return err + } + + err = c.irc.WriteMessage(&irc.Message{ + Command: "USER", + Params: []string{upstream.Username, "0", "*", upstream.Realname}, + }) + if err != nil { + return err + } + + for { + msg, err := c.irc.ReadMessage() + if err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("failed to read IRC command: %v", err) + } + log.Printf("Upstream message: %v", msg) + } + + return netConn.Close() +} + +func (s *Server) Run() { + for i := range s.Upstreams { + // TODO: retry connecting + go s.connect(&s.Upstreams[i]) + } +} + func (s *Server) Serve(ln net.Listener) error { for { c, err := ln.Accept()