diff options
| -rw-r--r-- | client/client.go | 80 | ||||
| -rw-r--r-- | cmd/client/main.go | 74 | ||||
| -rw-r--r-- | docs/rfc.html | 10 | ||||
| -rw-r--r-- | docs/rfc.txt | 4 |
4 files changed, 106 insertions, 62 deletions
diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..a294bca --- /dev/null +++ b/client/client.go @@ -0,0 +1,80 @@ +package client + +import ( + "net" + + "git.rctt.net/solec/core" +) + +type Handler interface { + HandleMessage(msg core.Message) + HandleError(err error) +} + +type Client struct { + h Handler + conn net.Conn + + addr string + uname string + pass string +} + +func NewClient(handler Handler, addr, uname, pass string) *Client { + return &Client{ + h: handler, + addr: addr, + uname: uname, + pass: pass, + } +} + +func (c *Client) Connect() error { + var err error + c.conn, err = net.Dial("tcp", c.addr) + if err != nil { + return err + } + defer c.conn.Close() + + hs := core.Handshake{0, 1} + if err := core.Send(c.conn, hs); err != nil { + return err + } + + auth := core.Auth{c.uname, c.pass} + if err := core.Send(c.conn, auth); err != nil { + return err + } + + c.read() + return nil +} + +func (c *Client) SendMessage(target, content string) error { + msg := core.Message{ + Source: c.uname + "@" + c.addr, + Target: target, + Content: content, + } + + return core.Send(c.conn, msg) +} + +func (c *Client) read() { + for { + payload, err := core.Read(c.conn) + if err != nil { + c.h.HandleError(err) + } + + c.handlePayload(payload) + } +} + +func (c *Client) handlePayload(payload any) { + switch v := payload.(type) { + case core.Message: + c.h.HandleMessage(v) + } +} diff --git a/cmd/client/main.go b/cmd/client/main.go index c362247..defcc17 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -2,12 +2,11 @@ package main import ( "flag" - "fmt" "log" "net" "strings" - "time" + "git.rctt.net/solec/client" "git.rctt.net/solec/core" "git.rctt.net/solec/prompt" ) @@ -16,8 +15,19 @@ var ( serverAddr string user string conn net.Conn + c *client.Client ) +type Handler struct{} + +func (h *Handler) HandleMessage(msg core.Message) { + log.Println("received message", msg.Source, "->", msg.Target, msg.Content) +} + +func (h *Handler) HandleError(err error) { + log.Println("client error:", err) +} + func main() { prompt.Commands["send"] = sendMessage @@ -25,67 +35,21 @@ func main() { flag.StringVar(&user, "u", "user", "username") flag.Parse() - var ( - d net.Dialer - err error - ) - log.Println("connecting to " + serverAddr + " as " + user) - conn, err = d.Dial("tcp", serverAddr) - if err != nil { - log.Fatal("cannot dial: ", err) - } - defer conn.Close() - - hs := core.Handshake{0, 1} - if err := core.Send(conn, hs); err != nil { - panic(err) - } - - auth := core.Auth{user, "valid"} - if err := core.Send(conn, auth); err != nil { - panic(err) - } + c = client.NewClient(&Handler{}, serverAddr, user, "valid") go prompt.Read() - read(conn) -} - -func read(conn net.Conn) { - for { - payload, err := core.Read(conn) - if err != nil { - panic(err) - } - - handlePayload(conn, payload) - } -} - -func handlePayload(conn net.Conn, payload any) { - switch v := payload.(type) { - case core.Message: - handleMessage(conn, v) - default: - fmt.Println(payload) + if err := c.Connect(); err != nil { + log.Fatal(err) + return } } -func handleMessage(conn net.Conn, msg core.Message) { - log.Println("received message", msg.Source, "->", msg.Target, msg.Content) -} - func sendMessage(args []string) { - msg := core.Message{ - Source: user, - Target: args[0], - Timestamp: time.Now(), - Content: strings.Join(args[1:], " "), - } - - if err := core.Send(conn, msg); err != nil { - panic(err) + err := c.SendMessage(args[0], strings.Join(args[1:], " ")) + if err != nil { + log.Println("cannot send message:", err) } } diff --git a/docs/rfc.html b/docs/rfc.html index 30f5bb7..aaa92ce 100644 --- a/docs/rfc.html +++ b/docs/rfc.html @@ -1216,11 +1216,11 @@ li > p:last-of-type:only-child { <thead><tr> <td class="left">Internet-Draft</td> <td class="center">SOLEC</td> -<td class="right">March 2026</td> +<td class="right">April 2026</td> </tr></thead> <tfoot><tr> <td class="left">bt</td> -<td class="center">Expires 2 October 2026</td> +<td class="center">Expires 12 October 2026</td> <td class="right">[Page]</td> </tr></tfoot> </table> @@ -1233,12 +1233,12 @@ li > p:last-of-type:only-child { <dd class="internet-draft">SOLEC</dd> <dt class="label-published">Published:</dt> <dd class="published"> -<time datetime="2026-03-31" class="published">31 March 2026</time> +<time datetime="2026-04-10" class="published">10 April 2026</time> </dd> <dt class="label-intended-status">Intended Status:</dt> <dd class="intended-status">Experimental</dd> <dt class="label-expires">Expires:</dt> -<dd class="expires"><time datetime="2026-10-02">2 October 2026</time></dd> +<dd class="expires"><time datetime="2026-10-12">12 October 2026</time></dd> <dt class="label-authors">Author:</dt> <dd class="authors"> <div class="author"> @@ -1274,7 +1274,7 @@ SOLEC system.<a href="#section-abstract-1" class="pilcrow">¶</a></p> time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."<a href="#section-boilerplate.1-3" class="pilcrow">¶</a></p> <p id="section-boilerplate.1-4"> - This Internet-Draft will expire on 2 October 2026.<a href="#section-boilerplate.1-4" class="pilcrow">¶</a></p> + This Internet-Draft will expire on 12 October 2026.<a href="#section-boilerplate.1-4" class="pilcrow">¶</a></p> </section> </div> <div id="copyright"> diff --git a/docs/rfc.txt b/docs/rfc.txt index 2029e6f..a19de19 100644 --- a/docs/rfc.txt +++ b/docs/rfc.txt @@ -1,7 +1,7 @@ SOLEC Working Group bt, Ed. Internet-Draft RCTT.net -Intended status: Experimental 31 March 2026 -Expires: 2 October 2026 +Intended status: Experimental 10 April 2026 +Expires: 12 October 2026 System of Lightweight Electronic Communication |
