package mailmux import ( "errors" "fmt" "io" "net" "os" ) type Server struct { ln net.Listener Aliases AliasStore Users UserStore } func (srv *Server) ListenAndServe() error { if srv.ln == nil { return errors.New("nil listener") } return srv.Serve(srv.ln) } func (srv *Server) Serve(ln net.Listener) error { if ln == nil { return errors.New("nil listener") } for { conn, err := ln.Accept() if err != nil { return err } go func() { if err := srv.handleConn(conn); err != nil { fmt.Fprintln(os.Stderr, err) } conn.Close() }() } return errors.New("unreachable?") } func (srv *Server) handleConn(conn net.Conn) error { tmsg, err := ParseMcall(conn) if err != nil { err = fmt.Errorf("parse mcall: %w", err) rerror(conn, err.Error()) return err } if tmsg.Type != Tauth { rerror(conn, "unauthorised") return nil } if err := srv.Users.Authenticate(tmsg.Username, Password(tmsg.Password)); err != nil { rerror(conn, "authentication failed") return err } rmsg := &Mcall{Type: Rauth, Username: tmsg.Username} writeMcall(conn, rmsg) user := tmsg.Username for { tmsg, err := ParseMcall(conn) if errors.Is(err, io.EOF) { return nil } else if err != nil { rerror(conn, err.Error()) continue } rmsg := &Mcall{} switch tmsg.Type { case Tauth: rmsg = &Mcall{Type: Rerror, Error: "already authenticated"} case Tlist: rmsg = srv.listAliases(user) case Tcreate: rmsg = srv.createAlias(user) default: rmsg = &Mcall{Type: Rerror, Error: "this tmessage not implemented yet"} } writeMcall(conn, rmsg) } } func (srv *Server) createAlias(username string) *Mcall { alias, err := srv.Aliases.Create(username) if err != nil { return &Mcall{Type: Rerror, Error: err.Error()} } return &Mcall{Type: Rcreate, Aliases: []Alias{alias}} } func (srv *Server) listAliases(username string) *Mcall { a, err := srv.Aliases.Aliases(username) if err != nil { return &Mcall{Type: Rerror, Error: fmt.Sprintf("aliases for %s: %v", username, err)} } return &Mcall{Type: Rlist, Aliases: a} }