aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2024-12-08 08:52:23 +0800
committerRunxi Yu <me@runxiyu.org>2024-12-08 08:52:23 +0800
commitd201e74fff4e6d82d639d858826bdb4190936c07 (patch)
tree7e5de8e206bf913fc0bbef5eba81b06fa9c1e06c
parentAdd basic command handling (diff)
downloadmeseircd-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.go2
-rw-r--r--cmd_ping.go9
-rw-r--r--errors.go12
-rw-r--r--main.go27
-rw-r--r--msg.go53
-rw-r--r--servers.go43
-rw-r--r--tags.go1
8 files changed, 129 insertions, 35 deletions
diff --git a/client.go b/clients.go
index 9c73444..e1b80a6 100644
--- a/client.go
+++ b/clients.go
@@ -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[:])
+}
diff --git a/cmd.go b/cmd.go
index 6d60f4f..6cdcaa0 100644
--- a/cmd.go
+++ b/cmd.go
@@ -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
}
diff --git a/errors.go b/errors.go
index 2d4ed02..c8b053f 100644
--- a/errors.go
+++ b/errors.go
@@ -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")
)
diff --git a/main.go b/main.go
index c320df0..f7e0f2d 100644
--- a/main.go
+++ b/main.go
@@ -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
}
}
diff --git a/msg.go b/msg.go
index 1743116..87ed1ab 100644
--- a/msg.go
+++ b/msg.go
@@ -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
diff --git a/tags.go b/tags.go
index 31f1154..186cb73 100644
--- a/tags.go
+++ b/tags.go
@@ -113,4 +113,3 @@ func validateTagName(name string) bool {
}
return true
}
-