diff --git a/client.go b/client.go index 4f48daf..3637d49 100644 --- a/client.go +++ b/client.go @@ -31,7 +31,7 @@ const ( ) type Client struct { - hostname string + hostname *string conn net.Conn registered bool nickname string @@ -49,7 +49,7 @@ func (client Client) String() string { return client.nickname + "!" + client.username + "@" + client.conn.RemoteAddr().String() } -func NewClient(hostname string, conn net.Conn) *Client { +func NewClient(hostname *string, conn net.Conn) *Client { return &Client{hostname: hostname, conn: conn, nickname: "*", password: ""} } @@ -89,7 +89,7 @@ func (client *Client) Msg(text string) { // Send message from server. It has ": servername" prefix. func (client *Client) Reply(text string) { - client.Msg(":" + client.hostname + " " + text) + client.Msg(":" + *client.hostname + " " + text) } // Send server message, concatenating all provided text parts and diff --git a/client_test.go b/client_test.go index f9e3887..5369bbe 100644 --- a/client_test.go +++ b/client_test.go @@ -96,7 +96,8 @@ func (conn TestingConn) SetWriteDeadline(t time.Time) error { func TestNewClient(t *testing.T) { conn := NewTestingConn() sink := make(chan ClientEvent) - client := NewClient("foohost", conn) + host := "foohost" + client := NewClient(&host, conn) go client.Processor(sink) event := <-sink @@ -123,7 +124,8 @@ func TestNewClient(t *testing.T) { // Test replies formatting func TestClientReplies(t *testing.T) { conn := NewTestingConn() - client := NewClient("foohost", conn) + host := "foohost" + client := NewClient(&host, conn) client.nickname = "мойник" client.Reply("hello") diff --git a/daemon.go b/daemon.go index 2bf696c..9b0d77f 100644 --- a/daemon.go +++ b/daemon.go @@ -25,7 +25,6 @@ import ( "regexp" "sort" "strings" - "sync" "time" ) @@ -39,13 +38,12 @@ var ( RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,9}$") ) -var passwordsRefreshLock sync.Mutex - type Daemon struct { Verbose bool version string - hostname string - motd string + hostname *string + motd *string + passwords *string clients map[*Client]bool clientAliveness map[*Client]*ClientAlivenessState rooms map[string]*Room @@ -53,11 +51,10 @@ type Daemon struct { lastAlivenessCheck time.Time logSink chan<- LogEvent stateSink chan<- StateEvent - passwords map[string]string } -func NewDaemon(version, hostname, motd string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Daemon { - daemon := Daemon{version: version, hostname: hostname, motd: motd} +func NewDaemon(version string, hostname, motd, passwords *string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Daemon { + daemon := Daemon{version: version, hostname: hostname, motd: motd, passwords: passwords} daemon.clients = make(map[*Client]bool) daemon.clientAliveness = make(map[*Client]*ClientAlivenessState) daemon.rooms = make(map[string]*Room) @@ -78,19 +75,19 @@ func (daemon *Daemon) SendLusers(client *Client) { } func (daemon *Daemon) SendMotd(client *Client) { - if len(daemon.motd) == 0 { + if daemon.motd == nil || *daemon.motd == "" { client.ReplyNicknamed("422", "MOTD File is missing") return } - motd, err := ioutil.ReadFile(daemon.motd) + motd, err := ioutil.ReadFile(*daemon.motd) if err != nil { - log.Printf("Can not read motd file %s: %v", daemon.motd, err) + log.Printf("Can not read motd file %s: %v", *daemon.motd, err) client.ReplyNicknamed("422", "Error reading MOTD File") return } - client.ReplyNicknamed("375", "- "+daemon.hostname+" Message of the day -") + client.ReplyNicknamed("375", "- "+*daemon.hostname+" Message of the day -") for _, s := range strings.Split(strings.Trim(string(motd), "\n"), "\n") { client.ReplyNicknamed("372", "- "+string(s)) } @@ -113,7 +110,7 @@ func (daemon *Daemon) SendWhois(client *Client, nicknames []string) { h = "Unknown" } client.ReplyNicknamed("311", c.nickname, c.username, h, "*", c.realname) - client.ReplyNicknamed("312", c.nickname, daemon.hostname, daemon.hostname) + client.ReplyNicknamed("312", c.nickname, *daemon.hostname, *daemon.hostname) subscriptions := []string{} for _, room := range daemon.rooms { for subscriber := range room.members { @@ -196,19 +193,34 @@ func (daemon *Daemon) ClientRegister(client *Client, command string, cols []stri client.realname = strings.TrimLeft(args[3], ":") } if client.nickname != "*" && client.username != "" { - passwordsRefreshLock.Lock() - if daemon.passwords != nil && (client.password == "" || daemon.passwords[client.nickname] != client.password) { - passwordsRefreshLock.Unlock() - client.ReplyParts("462", "You may not register") - client.conn.Close() - return + if daemon.passwords != nil && *daemon.passwords != "" { + if client.password == "" { + client.ReplyParts("462", "You may not register") + client.conn.Close() + return + } + contents, err := ioutil.ReadFile(*daemon.passwords) + if err != nil { + log.Fatalf("Can no read passwords file %s: %s", *daemon.passwords, err) + return + } + for _, entry := range strings.Split(string(contents), "\n") { + if entry == "" { + continue + } + if lp := strings.Split(entry, ":"); lp[0] == client.nickname && lp[1] != client.password { + client.ReplyParts("462", "You may not register") + client.conn.Close() + return + } + } } - passwordsRefreshLock.Unlock() + client.registered = true client.ReplyNicknamed("001", "Hi, welcome to IRC") - client.ReplyNicknamed("002", "Your host is "+daemon.hostname+", running goircd "+daemon.version) + client.ReplyNicknamed("002", "Your host is "+*daemon.hostname+", running goircd "+daemon.version) client.ReplyNicknamed("003", "This server was created sometime") - client.ReplyNicknamed("004", daemon.hostname+" goircd o o") + client.ReplyNicknamed("004", *daemon.hostname+" goircd o o") daemon.SendLusers(client) daemon.SendMotd(client) log.Println(client, "logged in") @@ -295,7 +307,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) { } if !aliveness.pingSent && aliveness.timestamp.Add(PingThreshold).Before(now) { if c.registered { - c.Msg("PING :" + daemon.hostname) + c.Msg("PING :" + *daemon.hostname) aliveness.pingSent = true } else { log.Println(c, "ping timeout") @@ -391,7 +403,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) { client.ReplyNicknamed("409", "No origin specified") continue } - client.Reply(fmt.Sprintf("PONG %s :%s", daemon.hostname, cols[1])) + client.Reply(fmt.Sprintf("PONG %s :%s", *daemon.hostname, cols[1])) case "PONG": continue case "NOTICE", "PRIVMSG": @@ -466,7 +478,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) { } else { debug = "" } - client.ReplyNicknamed("351", fmt.Sprintf("%s.%s %s :", daemon.version, debug, daemon.hostname)) + client.ReplyNicknamed("351", fmt.Sprintf("%s.%s %s :", daemon.version, debug, *daemon.hostname)) default: client.ReplyNicknamed("421", command, "Unknown command") } @@ -477,22 +489,3 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) { } } } - -func (daemon *Daemon) PasswordsRefresh() { - contents, err := ioutil.ReadFile(*passwords) - if err != nil { - log.Fatalf("Can no read passwords file %s: %s", *passwords, err) - return - } - processed := make(map[string]string) - for _, entry := range strings.Split(string(contents), "\n") { - loginAndPassword := strings.Split(entry, ":") - if len(loginAndPassword) == 2 { - processed[loginAndPassword[0]] = loginAndPassword[1] - } - } - log.Printf("Read %d passwords", len(processed)) - passwordsRefreshLock.Lock() - daemon.passwords = processed - passwordsRefreshLock.Unlock() -} diff --git a/daemon_test.go b/daemon_test.go index d049e42..ac9bc4c 100644 --- a/daemon_test.go +++ b/daemon_test.go @@ -25,11 +25,12 @@ import ( ) func TestRegistrationWorkflow(t *testing.T) { - daemon := NewDaemon("ver1", "foohost", "", nil, nil) + host := "foohost" + daemon := NewDaemon("ver1", &host, nil, nil, nil, nil) events := make(chan ClientEvent) go daemon.Processor(events) conn := NewTestingConn() - client := NewClient("foohost", conn) + client := NewClient(&host, conn) go client.Processor(events) conn.inbound <- "UNEXISTENT CMD" // should recieve nothing on this @@ -119,8 +120,10 @@ func TestMotd(t *testing.T) { fd.WriteString("catched\n") conn := NewTestingConn() - client := NewClient("foohost", conn) - daemon := NewDaemon("ver1", "foohost", fd.Name(), nil, nil) + host := "foohost" + client := NewClient(&host, conn) + motdName := fd.Name() + daemon := NewDaemon("ver1", &host, &motdName, nil, nil, nil) daemon.SendMotd(client) if r := <-conn.outbound; !strings.HasPrefix(r, ":foohost 375") { diff --git a/goircd.go b/goircd.go index b0f5e07..13efe2c 100644 --- a/goircd.go +++ b/goircd.go @@ -23,12 +23,9 @@ import ( "io/ioutil" "log" "net" - "os" - "os/signal" "path" "path/filepath" "strings" - "syscall" ) var ( @@ -53,7 +50,7 @@ func listenerLoop(sock net.Listener, events chan<- ClientEvent) { log.Println("Error during accepting connection", err) continue } - client := NewClient(*hostname, conn) + client := NewClient(hostname, conn) go client.Processor(events) } } @@ -79,9 +76,9 @@ func Run() { } stateSink := make(chan StateEvent) - daemon := NewDaemon(version, *hostname, *motd, logSink, stateSink) + daemon := NewDaemon(version, hostname, motd, passwords, logSink, stateSink) daemon.Verbose = *verbose - log.Println("goircd "+daemon.version+" is starting") + log.Println("goircd " + daemon.version + " is starting") if *statedir == "" { // Dummy statekeeper go func() { @@ -115,19 +112,6 @@ func Run() { log.Println(*statedir, "statekeeper initialized") } - if *passwords != "" { - daemon.PasswordsRefresh() - hups := make(chan os.Signal) - signal.Notify(hups, syscall.SIGHUP) - go func() { - for { - <-hups - daemon.PasswordsRefresh() - } - }() - } - - if *bind != "" { listener, err := net.Listen("tcp", *bind) if err != nil { diff --git a/room.go b/room.go index ccfdb07..ff57231 100644 --- a/room.go +++ b/room.go @@ -41,16 +41,16 @@ type Room struct { topic string key string members map[*Client]bool - hostname string + hostname *string logSink chan<- LogEvent stateSink chan<- StateEvent } -func (r Room) String() string { - return r.name +func (room Room) String() string { + return room.name } -func NewRoom(hostname, name string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Room { +func NewRoom(hostname *string, name string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Room { room := Room{name: name} room.members = make(map[*Client]bool) room.topic = "" @@ -128,7 +128,7 @@ func (room *Room) Processor(events <-chan ClientEvent) { room.StateSave() case EventWho: for m := range room.members { - client.ReplyNicknamed("352", room.name, m.username, m.conn.RemoteAddr().String(), room.hostname, m.nickname, "H", "0 "+m.realname) + client.ReplyNicknamed("352", room.name, m.username, m.conn.RemoteAddr().String(), *room.hostname, m.nickname, "H", "0 "+m.realname) } client.ReplyNicknamed("315", room.name, "End of /WHO list") case EventMode: diff --git a/room_test.go b/room_test.go index 6bd1cb0..3de4195 100644 --- a/room_test.go +++ b/room_test.go @@ -26,14 +26,15 @@ func notEnoughParams(t *testing.T, c *TestingConn) { func TestTwoUsers(t *testing.T) { logSink := make(chan LogEvent, 8) stateSink := make(chan StateEvent, 8) - daemon := NewDaemon("ver1", "foohost", "", logSink, stateSink) + host := "foohost" + daemon := NewDaemon("ver1", &host, nil, nil, logSink, stateSink) events := make(chan ClientEvent) go daemon.Processor(events) conn1 := NewTestingConn() conn2 := NewTestingConn() - client1 := NewClient("foohost", conn1) - client2 := NewClient("foohost", conn2) + client1 := NewClient(&host, conn1) + client2 := NewClient(&host, conn2) go client1.Processor(events) go client2.Processor(events) @@ -100,11 +101,12 @@ func TestTwoUsers(t *testing.T) { func TestJoin(t *testing.T) { logSink := make(chan LogEvent, 8) stateSink := make(chan StateEvent, 8) - daemon := NewDaemon("ver1", "foohost", "", logSink, stateSink) + host := "foohost" + daemon := NewDaemon("ver1", &host, nil, nil, logSink, stateSink) events := make(chan ClientEvent) go daemon.Processor(events) conn := NewTestingConn() - client := NewClient("foohost", conn) + client := NewClient(&host, conn) go client.Processor(events) conn.inbound <- "NICK nick2\r\nUSER foo2 bar2 baz2 :Long name2\r\n"