diff options
author | Runxi Yu <me@runxiyu.org> | 2024-12-08 08:52:23 +0800 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2024-12-08 08:52:23 +0800 |
commit | d201e74fff4e6d82d639d858826bdb4190936c07 (patch) | |
tree | 7e5de8e206bf913fc0bbef5eba81b06fa9c1e06c | |
parent | Add basic command handling (diff) | |
download | meseircd-d201e74fff4e6d82d639d858826bdb4190936c07.tar.gz meseircd-d201e74fff4e6d82d639d858826bdb4190936c07.tar.zst meseircd-d201e74fff4e6d82d639d858826bdb4190936c07.zip |
Server and self awareness
-rw-r--r-- | clients.go (renamed from client.go) | 17 | ||||
-rw-r--r-- | cmd.go | 2 | ||||
-rw-r--r-- | cmd_ping.go | 9 | ||||
-rw-r--r-- | errors.go | 12 | ||||
-rw-r--r-- | main.go | 27 | ||||
-rw-r--r-- | msg.go | 53 | ||||
-rw-r--r-- | servers.go | 43 | ||||
-rw-r--r-- | tags.go | 1 |
8 files changed, 129 insertions, 35 deletions
@@ -5,8 +5,12 @@ import ( ) type Client struct { - conn net.Conn - uid [6]byte + conn net.Conn + UID [6]byte + Nick string + Ident string + Host string + Server Server } func (client *Client) Send(msg SMsg) { @@ -23,3 +27,12 @@ func (client *Client) SendRaw(s string) { client.conn.Close() } } + +func (client Client) ClientSource() string { + // TODO: Edge cases where these aren't available + return client.Nick + "!" + client.Ident + "@" + client.Host +} + +func (client Client) ServerSource() string { + return string(client.Server.SID[:]) + string(client.UID[:]) +} @@ -1,5 +1,5 @@ package main -var commandHandlers = map[string](func(RMsg, *Client) (error)){} +var commandHandlers = map[string](func(RMsg, *Client) bool){} /* Maybe we should make command handlers return their values for easier labelled-reply? */ diff --git a/cmd_ping.go b/cmd_ping.go index 4ad07c5..c5442b5 100644 --- a/cmd_ping.go +++ b/cmd_ping.go @@ -4,10 +4,11 @@ func init() { commandHandlers["PING"] = handleClientPing } -func handleClientPing(msg RMsg, client *Client) (error) { +func handleClientPing(msg RMsg, client *Client) bool { if len(msg.Params) < 1 { - client.Send(SMsg{Command: ERR_NEEDMOREPARAMS, Params: []string{"PING", "Not enough parameters"}}) + client.Send(MakeMsg(self, ERR_NEEDMOREPARAMS, "PING", "Not enough parameters")) + return true } - client.Send(SMsg{Command: "PONG", Params: []string{msg.Params[0]}}) - return nil + client.Send(MakeMsg(self, "PONG", msg.Params[0])) + return true } @@ -5,9 +5,11 @@ import ( ) var ( - ErrEmptyMessage = errors.New("empty message") - ErrIllegalByte = errors.New("illegal byte") - ErrTagsTooLong = errors.New("tags too long") - ErrInvalidTagContent = errors.New("invalid tag content") - ErrBodyTooLong = errors.New("body too long") + ErrEmptyMessage = errors.New("empty message") + ErrIllegalByte = errors.New("illegal byte") + ErrTagsTooLong = errors.New("tags too long") + ErrInvalidTagContent = errors.New("invalid tag content") + ErrBodyTooLong = errors.New("body too long") + ErrNotConnectedServer = errors.New("not connected server") + ErrSendToSelf = errors.New("attempt to send message to self") ) @@ -8,6 +8,12 @@ import ( ) func main() { + self = Server{ + conn: nil, + SID: [3]byte{'1', 'H', 'C'}, + Name: "irc.runxiyu.org", + } + listener, err := net.Listen("tcp", ":6667") if err != nil { log.Fatal(err) @@ -21,7 +27,8 @@ func main() { } client := &Client{ - conn: conn, + conn: conn, + Server: self, } go func() { defer func() { @@ -41,7 +48,8 @@ func main() { func (client *Client) handleConnection() { reader := bufio.NewReader(client.conn) - messageLoop: for { +messageLoop: + for { line, err := reader.ReadString('\n') if err != nil { slog.Error("error while reading from connection", "error", err) @@ -50,32 +58,31 @@ func (client *Client) handleConnection() { } msg, err := parseIRCMsg(line) if err != nil { - switch (err) { + switch err { case ErrEmptyMessage: continue messageLoop case ErrIllegalByte: - client.Send(SMsg{Command: "ERROR", Params: []string{err.Error()}}) + client.Send(MakeMsg(self, "ERROR", err.Error())) break messageLoop case ErrTagsTooLong: fallthrough case ErrBodyTooLong: - client.Send(SMsg{Command: ERR_INPUTTOOLONG, Params: []string{err.Error()}}) + client.Send(MakeMsg(self, ERR_INPUTTOOLONG, err.Error())) continue messageLoop default: - client.Send(SMsg{Command: "ERROR", Params: []string{err.Error()}}) + client.Send(MakeMsg(self, "ERROR", err.Error())) break messageLoop } } handler, ok := commandHandlers[msg.Command] if !ok { - client.Send(SMsg{Command: ERR_UNKNOWNCOMMAND, Params: []string{msg.Command, "Unknown command"}}) + client.Send(MakeMsg(self, ERR_UNKNOWNCOMMAND, msg.Command, "Unknown command")) continue } - err = handler(msg, client) - if err != nil { - client.Send(SMsg{Command: "ERROR", Params: []string{err.Error()}}) + cont := handler(msg, client) + if !cont { break } } @@ -5,10 +5,10 @@ import ( ) type RMsg struct { - RawSource string - Command string - Tags map[string]string - Params []string + RawSource string + Command string + Tags map[string]string + Params []string } type Sourceable interface { @@ -17,36 +17,65 @@ type Sourceable interface { } type SMsg struct { - Source *Sourceable - Command string - Tags map[string]string - Params []string + Source Sourceable + Command string + Tags map[string]string + Params []string } func (msg *SMsg) ClientSerialize() (final string) { if msg.Tags != nil && len(msg.Tags) != 0 { final = "@" - for k, v := range msg.Tags{ + for k, v := range msg.Tags { // TODO: Tag values must be escaped final += k + "=" + v + ";" } final += " " } if msg.Source != nil { - final += ":" + (*msg.Source).ClientSource() + " " + final += ":" + msg.Source.ClientSource() + " " } final += msg.Command + " " if len(msg.Params) > 0 { - for i := 0; i < len(msg.Params) - 1; i++ { + for i := 0; i < len(msg.Params)-1; i++ { final += msg.Params[i] + " " } - final += ":" + msg.Params[len(msg.Params) - 1] + final += ":" + msg.Params[len(msg.Params)-1] } final += "\n" return } +func (msg *SMsg) ServerSerialize() (final string) { + if msg.Tags != nil && len(msg.Tags) != 0 { + final = "@" + for k, v := range msg.Tags { + // TODO: Tag values must be escaped + final += k + "=" + v + ";" + } + final += " " + } + if msg.Source != nil { + final += ":" + msg.Source.ServerSource() + " " + } + final += msg.Command + " " + + if len(msg.Params) > 0 { + for i := 0; i < len(msg.Params)-1; i++ { + final += msg.Params[i] + " " + } + final += ":" + msg.Params[len(msg.Params)-1] + } + final += "\n" + return +} + +func MakeMsg(source Sourceable, command string, params ...string) (msg SMsg) { + // TODO: Add tags + return SMsg{Source: source, Command: command, Params: params} +} + // Partially adapted from https://github.com/ergochat/irc-go.git func parseIRCMsg(line string) (msg RMsg, err error) { msg = RMsg{} diff --git a/servers.go b/servers.go new file mode 100644 index 0000000..eb91072 --- /dev/null +++ b/servers.go @@ -0,0 +1,43 @@ +package main + +import ( + "net" +) + +type Server struct { + conn *net.Conn + SID [3]byte + Name string +} + +func (server *Server) Send(msg SMsg) error { + return server.SendRaw(msg.ServerSerialize()) +} + +func (server *Server) SendRaw(s string) error { + if server == &self { + return ErrSendToSelf + } + if server.conn == nil { + // TODO: Propagate across mesh + return ErrNotConnectedServer + } + _, err := (*server.conn).Write([]byte(s)) + if err != nil { + // TODO: Should shut down the netFd instead but the stdlib + // doesn't expose a way to do this. + (*server.conn).Close() + return err + } + return nil +} + +func (server Server) ClientSource() string { + return server.Name +} + +func (server Server) ServerSource() string { + return string(server.SID[:]) +} + +var self Server @@ -113,4 +113,3 @@ func validateTagName(name string) bool { } return true } - |