Unverified Commit 2112f5d8 authored by Kevin Lyda's avatar Kevin Lyda
Browse files

Implement quit and exit

parent 08e4dc1e
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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
+72 −4
Original line number Diff line number Diff line
// 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
}
+8 −4
Original line number Diff line number Diff line
@@ -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
+17 −3
Original line number Diff line number Diff line
@@ -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
)
+25 −0
Original line number Diff line number Diff line
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=
Loading