diff --git a/irc/channel.go b/irc/channel.go index d8209949..fdef18dc 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -216,15 +216,15 @@ func (channel *Channel) Join(client *Client, key string) { return } - isInvited := channel.lists[InviteMask].Match(client.UserHost()) + isInvited := channel.lists[InviteMask].Match(client.nickMaskCasefolded) if channel.flags[InviteOnly] && !isInvited { client.Send(nil, client.server.name, ERR_INVITEONLYCHAN, channel.name, "Cannot join channel (+i)") return } - if channel.lists[BanMask].Match(client.UserHost()) && + if channel.lists[BanMask].Match(client.nickMaskCasefolded) && !isInvited && - !channel.lists[ExceptMask].Match(client.UserHost()) { + !channel.lists[ExceptMask].Match(client.nickMaskCasefolded) { client.Send(nil, client.server.name, ERR_BANNEDFROMCHAN, channel.name, "Cannot join channel (+b)") return } @@ -514,7 +514,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) { //TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list? if channel.flags[InviteOnly] { - channel.lists[InviteMask].Add(invitee.UserHost()) + channel.lists[InviteMask].Add(invitee.nickMaskCasefolded) } // send invite-notify diff --git a/irc/client.go b/irc/client.go index 1397dd00..11156523 100644 --- a/irc/client.go +++ b/irc/client.go @@ -27,6 +27,7 @@ var ( TIMEOUT_STATED_SECONDS = strconv.Itoa(int((IDLE_TIMEOUT + QUIT_TIMEOUT).Seconds())) ) +// Client is an IRC client. type Client struct { account *ClientAccount atime time.Time @@ -191,10 +192,12 @@ func (client *Client) connectionIdle() { // server goroutine // +// Active marks the client as 'active' (i.e. the user should be there). func (client *Client) Active() { client.atime = time.Now() } +// Touch marks the client as alive. func (client *Client) Touch() { if client.quitTimer != nil { client.quitTimer.Stop() @@ -207,6 +210,7 @@ func (client *Client) Touch() { } } +// Idle resets the timeout handlers and sends the client a PING. func (client *Client) Idle() { client.Send(nil, "", "PING", client.nick) @@ -229,22 +233,27 @@ func (client *Client) Register() { client.alertMonitors() } +// IdleTime returns how long this client's been idle. func (client *Client) IdleTime() time.Duration { return time.Since(client.atime) } +// SignonTime returns this client's signon time as a unix timestamp. func (client *Client) SignonTime() int64 { return client.ctime.Unix() } +// IdleSeconds returns the number of seconds this client's been idle. func (client *Client) IdleSeconds() uint64 { return uint64(client.IdleTime().Seconds()) } +// HasNick returns true if the client's nickname is set (used in registration). func (client *Client) HasNick() bool { return client.nick != "" && client.nick != "*" } +// HasNick returns true if the client's username is set (used in registration). func (client *Client) HasUsername() bool { return client.username != "" && client.username != "*" } @@ -275,14 +284,6 @@ func (c *Client) ModeString() (str string) { return } -func (c *Client) UserHost() string { - return fmt.Sprintf("%s!%s@%s", c.nick, c.username, c.hostname) -} - -func (c *Client) Id() string { - return c.UserHost() -} - // Friends refers to clients that share a channel with this client. func (client *Client) Friends(Capabilities ...Capability) ClientSet { friends := make(ClientSet) @@ -331,6 +332,7 @@ func (client *Client) updateNickMask() { client.nickMaskCasefolded = nickMaskCasefolded } +// SetNickname sets the very first nickname for the client. func (client *Client) SetNickname(nickname string) { if client.HasNick() { Log.error.Printf("%s nickname already set!", client.nickMaskString) @@ -341,6 +343,7 @@ func (client *Client) SetNickname(nickname string) { client.server.clients.Add(client) } +// ChangeNickname changes the existing nickname of the client. func (client *Client) ChangeNickname(nickname string) { origNickMask := client.nickMaskString client.server.clients.Remove(client) @@ -353,16 +356,12 @@ func (client *Client) ChangeNickname(nickname string) { } } -func (client *Client) Reply(reply string) error { - //TODO(dan): We'll be passing around real message objects instead of raw strings - return client.socket.WriteLine(reply) -} - func (client *Client) Quit(message string) { client.Send(nil, client.nickMaskString, "QUIT", message) client.Send(nil, client.nickMaskString, "ERROR", message) } +// destroy gets rid of a client, removes them from server lists etc. func (client *Client) destroy() { if client.isDestroyed { return diff --git a/irc/server.go b/irc/server.go index d1eaa635..415c7017 100644 --- a/irc/server.go +++ b/irc/server.go @@ -59,20 +59,22 @@ type ListenerEvent struct { // Server is the main Oragono server. type Server struct { + accountRegistration *AccountRegistration accounts map[string]*ClientAccount authenticationEnabled bool channels ChannelNameMap + checkIdent bool clients *ClientLookupSet commands chan Command configFilename string ctime time.Time currentOpers map[*Client]bool - store buntdb.DB idle chan *Client + isupport *ISupportList limits Limits listenerEventActMutex sync.Mutex - listenerUpdateMutex sync.Mutex listeners map[string]ListenerInterface + listenerUpdateMutex sync.Mutex monitoring map[string][]Client motdLines []string name string @@ -84,16 +86,15 @@ type Server struct { password []byte passwords *PasswordManager rehashMutex sync.Mutex - accountRegistration *AccountRegistration - signals chan os.Signal rehashSignal chan os.Signal + signals chan os.Signal + store buntdb.DB whoWas *WhoWasList - isupport *ISupportList - checkIdent bool } var ( - SERVER_SIGNALS = []os.Signal{ + // ServerExitSignals are the signals the server will exit on. + ServerExitSignals = []os.Signal{ syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, @@ -152,7 +153,7 @@ func NewServer(configFilename string, config *Config) *Server { newConns: make(chan clientConn), operclasses: *operClasses, operators: opers, - signals: make(chan os.Signal, len(SERVER_SIGNALS)), + signals: make(chan os.Signal, len(ServerExitSignals)), rehashSignal: make(chan os.Signal, 1), whoWas: NewWhoWasList(config.Limits.WhowasEntries), checkIdent: config.Server.CheckIdent, @@ -223,7 +224,7 @@ func NewServer(configFilename string, config *Config) *Server { server.accountRegistration = &accountReg // Attempt to clean up when receiving these signals. - signal.Notify(server.signals, SERVER_SIGNALS...) + signal.Notify(server.signals, ServerExitSignals...) signal.Notify(server.rehashSignal, syscall.SIGHUP) server.setISupport() @@ -289,6 +290,7 @@ func (server *Server) Shutdown() { } } +// Run starts the server. func (server *Server) Run() { // defer closing db/store defer server.store.Close() @@ -320,12 +322,13 @@ func (server *Server) Run() { // IRC protocol listeners // -func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { +// createListener starts the given listeners. +func (server *Server) createListener(addr string, tlsMap map[string]*tls.Config) { config, listenTLS := tlsMap[addr] - _, alreadyExists := s.listeners[addr] + _, alreadyExists := server.listeners[addr] if alreadyExists { - log.Fatal(s, "listener already exists:", addr) + log.Fatal(server, "listener already exists:", addr) } // make listener event channel @@ -334,7 +337,7 @@ func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { // make listener listener, err := net.Listen("tcp", addr) if err != nil { - log.Fatal(s, "listen error: ", err) + log.Fatal(server, "listen error: ", err) } tlsString := "plaintext" @@ -349,10 +352,10 @@ func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { Events: listenerEventChannel, Listener: listener, } - s.listeners[addr] = li + server.listeners[addr] = li // start listening - Log.info.Printf("%s listening on %s using %s.", s.name, addr, tlsString) + Log.info.Printf("%s listening on %s using %s.", server.name, addr, tlsString) // setup accept goroutine go func() { @@ -365,15 +368,15 @@ func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { IsTLS: listenTLS, } - s.newConns <- newConn + server.newConns <- newConn } select { - case event := <-s.listeners[addr].Events: + case event := <-server.listeners[addr].Events: // this is used to confirm that whoever passed us this event has closed the existing listener correctly (in an attempt to get us to notice the event). // this is required to keep REHASH from having a very small race possibility of killing the primary listener - s.listenerEventActMutex.Lock() - s.listenerEventActMutex.Unlock() + server.listenerEventActMutex.Lock() + server.listenerEventActMutex.Unlock() if event.Type == DestroyListener { // listener should already be closed, this is just for safety @@ -386,7 +389,7 @@ func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { // make new listener listener, err = net.Listen("tcp", addr) if err != nil { - log.Fatal(s, "listen error: ", err) + log.Fatal(server, "listen error: ", err) } tlsString := "plaintext" @@ -399,12 +402,12 @@ func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { // update server ListenerInterface li.Listener = listener - s.listenerUpdateMutex.Lock() - s.listeners[addr] = li - s.listenerUpdateMutex.Unlock() + server.listenerUpdateMutex.Lock() + server.listeners[addr] = li + server.listenerUpdateMutex.Unlock() // print notice - Log.info.Printf("%s updated listener %s using %s.", s.name, addr, tlsString) + Log.info.Printf("%s updated listener %s using %s.", server.name, addr, tlsString) } default: // no events waiting for us, fall-through and continue @@ -417,10 +420,10 @@ func (s *Server) createListener(addr string, tlsMap map[string]*tls.Config) { // websocket listen goroutine // -func (s *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { +func (server *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { - Log.error.Printf("%s method not allowed", s.name) + Log.error.Printf("%s method not allowed", server.name) return } @@ -433,7 +436,7 @@ func (s *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { ws, err := upgrader.Upgrade(w, r, nil) if err != nil { - Log.error.Printf("%s websocket upgrade error: %s", s.name, err) + Log.error.Printf("%s websocket upgrade error: %s", server.name, err) return } @@ -441,7 +444,7 @@ func (s *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { Conn: WSContainer{ws}, IsTLS: false, //TODO(dan): track TLS or not here properly } - s.newConns <- newConn + server.newConns <- newConn }) go func() { config, listenTLS := tlsMap[addr] @@ -451,7 +454,7 @@ func (s *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { if listenTLS { tlsString = "TLS" } - Log.info.Printf("%s websocket listening on %s using %s.", s.name, addr, tlsString) + Log.info.Printf("%s websocket listening on %s using %s.", server.name, addr, tlsString) if listenTLS { err = http.ListenAndServeTLS(addr, config.Cert, config.Key, nil) @@ -459,7 +462,7 @@ func (s *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { err = http.ListenAndServe(addr, nil) } if err != nil { - Log.error.Printf("%s listenAndServe (%s) error: %s", s.name, tlsString, err) + Log.error.Printf("%s listenAndServe (%s) error: %s", server.name, tlsString, err) } }() } @@ -468,7 +471,7 @@ func (s *Server) wslisten(addr string, tlsMap map[string]*TLSListenConfig) { // server functionality // -func (s *Server) tryRegister(c *Client) { +func (server *Server) tryRegister(c *Client) { if c.registered || !c.HasNick() || !c.HasUsername() || (c.capState == CapNegotiating) { return @@ -478,13 +481,13 @@ func (s *Server) tryRegister(c *Client) { // send welcome text //NOTE(dan): we specifically use the NICK here instead of the nickmask // see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask - c.Send(nil, s.name, RPL_WELCOME, c.nick, fmt.Sprintf("Welcome to the Internet Relay Network %s", c.nick)) - c.Send(nil, s.name, RPL_YOURHOST, c.nick, fmt.Sprintf("Your host is %s, running version %s", s.name, Ver)) - c.Send(nil, s.name, RPL_CREATED, c.nick, fmt.Sprintf("This server was created %s", s.ctime.Format(time.RFC1123))) + c.Send(nil, server.name, RPL_WELCOME, c.nick, fmt.Sprintf("Welcome to the Internet Relay Network %s", c.nick)) + c.Send(nil, server.name, RPL_YOURHOST, c.nick, fmt.Sprintf("Your host is %s, running version %s", server.name, Ver)) + c.Send(nil, server.name, RPL_CREATED, c.nick, fmt.Sprintf("This server was created %s", server.ctime.Format(time.RFC1123))) //TODO(dan): Look at adding last optional [] parameter - c.Send(nil, s.name, RPL_MYINFO, c.nick, s.name, Ver, supportedUserModesString, supportedChannelModesString) + c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString) c.RplISupport() - s.MOTD(c) + server.MOTD(c) c.Send(nil, c.nickMaskString, RPL_UMODEIS, c.nick, c.ModeString()) } @@ -501,12 +504,12 @@ func (server *Server) MOTD(client *Client) { client.Send(nil, server.name, RPL_ENDOFMOTD, client.nick, "End of MOTD command") } -func (s *Server) Id() string { - return s.name +func (server *Server) Id() string { + return server.name } -func (s *Server) Nick() string { - return s.Id() +func (server *Server) Nick() string { + return server.Id() } //