Skip to content
Snippets Groups Projects
Unverified Commit 2112f5d8 authored by Kevin Lyda's avatar Kevin Lyda
Browse files

Implement quit and exit

parent 08e4dc1e
Branches
Tags
No related merge requests found
......@@ -4,10 +4,21 @@ These are the development notes for the Go version.
The idea is to use the help files to implement BULLETIN.
ssh `authorized_keys` should look like this:
```
command="/home/bulletin/bin/bulletin -u alice",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty KEY-TYPE KEY
```
Have readline store a history file for the user. Configure at the `accounts.Open` step.
Look up how godoc does references to other things.
## Things to do
* Implement a better dclish parser.
* Implement each command.
* Next: HELP - move some commands to a Help array.
* Decide on how the boards are managed.
## Module links
......
// Package accounts manages accounts.
package accounts
import "errors"
import (
"database/sql"
"errors"
"fmt"
"os"
"path"
"strings"
// Verify verifies that an account exists.
func Verify(acc string) error {
"github.com/adrg/xdg"
_ "modernc.org/sqlite" // Loads sqlite driver.
)
// UserData is the type for holding user data. Things like preferences,
// unread message counts, signatures, etc.
type UserData struct {
Account string
FullName string
pref *sql.DB
bull *sql.DB
}
// User is the user for this process. It is loaded by the `Verify` function.
var User *UserData
// ValidName makes sure that an account name is a valid name.
func ValidName(acc string) error {
if acc == "" {
return errors.New("Empty account is invalid")
return errors.New("empty account is invalid")
}
if strings.ContainsAny(acc, "./") {
return fmt.Errorf("account name '%s' is invalid", acc)
}
return nil
}
// Open verifies that an account exists.
func Open(acc string) error {
err := ValidName(acc)
if err != nil {
return err
}
User = &UserData{
Account: acc,
}
prefdir := path.Join(xdg.ConfigHome, "BULLETIN")
err = os.MkdirAll(prefdir, 0700)
if err != nil {
return errors.New("account preference directory problem")
}
User.pref, err = sql.Open("sqlite", path.Join(prefdir, acc, ".db"))
if err != nil {
return errors.New("account preference database problem")
}
bulldir := path.Join(xdg.ConfigHome, "BULLETIN")
err = os.MkdirAll(bulldir, 0700)
if err != nil {
return errors.New("bulletin directory problem")
}
User.bull, err = sql.Open("sqlite", path.Join(bulldir, acc, ".db"))
if err != nil {
return errors.New("bulletin database problem")
}
return nil
}
// Close closes the resources open for the account.
func (u *UserData) Close() {
u.pref.Close()
u.bull.Close()
}
// IsAdmin returns true if the user is an admin
func IsAdmin(acc string) bool {
if acc == "admin" {
return true
}
// TODO: Look up account otherwise.
return false
}
......@@ -7,7 +7,7 @@ import (
)
// ActionFunc is the function that a command runs.
type ActionFunc func(string, *Command) error
type ActionFunc func(*Command) error
// Flag is a flag for a command.
type Flag struct {
......@@ -36,13 +36,17 @@ type Commands []*Command
func (c Commands) ParseAndRun(line string) error {
// TODO: this doesn't handle a DCL command line completely.
words := strings.Fields(line)
fmt.Printf("TODO ParseAndRun sees: %s\n", words)
fmt.Printf("TODO ParseAndRun need to parse flags: %s\n", words)
cmd := strings.ToUpper(words[0])
for i := range c {
if c[i].Command == cmd {
fmt.Printf("Command help:\n%s\n", c[i].Description)
if c[i].Action == nil {
fmt.Printf("Command not implemented:\n%s\n", c[i].Description)
return nil
}
err := c[i].Action(c[i])
return err
}
}
fmt.Printf("ERROR: Unknown command '%s'\n", cmd)
return nil
......
......@@ -3,7 +3,21 @@ module git.lyda.ie/kevin/bulletin
go 1.24.2
require (
github.com/chzyer/readline v1.5.1 // indirect
github.com/urfave/cli/v3 v3.3.2 // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
github.com/adrg/xdg v0.5.3
github.com/chzyer/readline v1.5.1
github.com/urfave/cli/v3 v3.3.2
modernc.org/sqlite v1.37.0
)
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
golang.org/x/sys v0.33.0 // indirect
modernc.org/libc v1.65.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.10.0 // indirect
)
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/urfave/cli/v3 v3.3.2 h1:BYFVnhhZ8RqT38DxEYVFPPmGFTEf7tJwySTXsVRrS/o=
github.com/urfave/cli/v3 v3.3.2/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
modernc.org/libc v1.65.0 h1:e183gLDnAp9VJh6gWKdTy0CThL9Pt7MfcR/0bgb7Y1Y=
modernc.org/libc v1.65.0/go.mod h1:7m9VzGq7APssBTydds2zBcxGREwvIGpuUBaKTXdm2Qs=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4=
modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
......@@ -29,7 +29,7 @@ func main() {
},
Action: func(_ context.Context, cmd *cli.Command) error {
user := cmd.String("user")
err := accounts.Verify(user)
err := accounts.Open(user)
if err != nil {
return err
}
......
......@@ -874,6 +874,7 @@ is used.
Command: "EXIT",
Description: `Exits the BULLETIN program.
`,
Action: ActionExit,
},
{
Command: "EXTRACT",
......@@ -1740,6 +1741,7 @@ as the subject preceeded by "RE: ".
Command: "QUIT",
Description: `Exits the BULLETIN program.
`,
Action: ActionQuit,
},
{
Command: "SEARCH",
......
// Package repl implements the main event loop.
package repl
import (
"fmt"
"os"
"git.lyda.ie/kevin/bulletin/accounts"
"git.lyda.ie/kevin/bulletin/dclish"
)
// ActionQuit handles the `QUIT` command.
func ActionQuit(_ *dclish.Command) error {
accounts.User.Close()
fmt.Println("QUIT")
// TODO: IIRC, quit should not update unread data. Check.
os.Exit(0)
return nil
}
// ActionExit handles the `EXIT` command.
func ActionExit(_ *dclish.Command) error {
accounts.User.Close()
fmt.Println("EXIT")
// TODO: update unread data.
os.Exit(0)
return nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment