diff --git a/folders/folders.go b/folders/folders.go
index 436e71c8c5e795efc65b6d285eb5e4a240b10f94..2f9cdf6e513f2551d2f7b5a9814c036f6806b0a4 100644
--- a/folders/folders.go
+++ b/folders/folders.go
@@ -2,6 +2,7 @@
 package folders
 
 import (
+	"database/sql"
 	"errors"
 	"strings"
 
@@ -63,7 +64,10 @@ func FindFolder(name string) storage.Folder {
 	if folder.Name != "" {
 		return folder
 	}
-	folder, _ = this.Q.FindFolderPrefix(ctx, name)
+	folder, _ = this.Q.FindFolderPrefix(ctx, sql.NullString{
+		String: name,
+		Valid:  true,
+	})
 	return folder
 }
 
diff --git a/folders/messages.go b/folders/messages.go
index fd762401e1e428a60026be8fa0c50b9f9e061caa..055ec887514a944439b554699cceaa96f8492764 100644
--- a/folders/messages.go
+++ b/folders/messages.go
@@ -2,7 +2,6 @@ package folders
 
 import (
 	"errors"
-	"fmt"
 	"time"
 
 	"git.lyda.ie/kevin/bulletin/storage"
@@ -39,10 +38,9 @@ func CreateMessage(author, subject, message, folder string, permanent, shutdown
 	return err
 }
 
-// ReadMessage reads a message for a user.
-func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) {
+// GetMessage reads a message for a user.
+func GetMessage(login, folder string, msgid int64) (*storage.Message, error) {
 	ctx := storage.Context()
-	fmt.Printf("TODO(ReadMessage): Make sure %s can read this message.\n", login)
 	msg, err := this.Q.ReadMessage(ctx, storage.ReadMessageParams{
 		Folder: folder,
 		ID:     msgid,
diff --git a/key/key.go b/key/key.go
index 77f267b4d7d4f96ec1f5aacbe54cdd50e300f80d..46a6ceee691b96a2b18aaac406b1a13540a64447 100644
--- a/key/key.go
+++ b/key/key.go
@@ -212,7 +212,7 @@ func Fetch(login, nickname, username string) string {
 	keys := 0
 	for scanner.Scan() {
 		keyline := string(bytes.TrimSpace(scanner.Bytes()))
-		Add(login, keyline)
+		Add(strings.ToUpper(login), keyline)
 		keys++
 	}
 	switch keys {
diff --git a/main.go b/main.go
index 8d48d462829cbb4f8f43f47ec646a499c8a608c9..ce9562b2be82f1fa585d7d13242427bba45f5ad8 100644
--- a/main.go
+++ b/main.go
@@ -6,6 +6,7 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"strings"
 
 	"git.lyda.ie/kevin/bulletin/batch"
 	"git.lyda.ie/kevin/bulletin/repl"
@@ -33,7 +34,7 @@ func main() {
 			},
 		},
 		Action: func(_ context.Context, cmd *cli.Command) error {
-			user := cmd.String("user")
+			user := strings.ToUpper(cmd.String("user"))
 			batchFlag := cmd.String("batch")
 
 			if batchFlag != "" {
diff --git a/repl/messages.go b/repl/messages.go
index 03b926872355f63da4456b2efc20f945565645f7..83b387bd729eed7c978d2d4df7fe70a84bfb49f6 100644
--- a/repl/messages.go
+++ b/repl/messages.go
@@ -58,6 +58,8 @@ func ActionDirectory(cmd *dclish.Command) error {
 
 // ActionAdd handles the `ADD` command.  This adds a message to a folder.
 func ActionAdd(cmd *dclish.Command) error {
+	ctx := storage.Context()
+
 	optAll := 0
 	optBell := 0
 	optBroadcast := 0
@@ -74,15 +76,19 @@ func ActionAdd(cmd *dclish.Command) error {
 
 	if cmd.Flags["/ALL"].Value == "true" {
 		optAll = 1
+		fmt.Printf("TODO: optAll is not yet implemented - you set it to %d\n", optAll)
 	}
 	if cmd.Flags["/BELL"].Value == "true" {
 		optBell = 1
+		fmt.Printf("TODO: optBell is not yet implemented - you set it to %d\n", optBell)
 	}
 	if cmd.Flags["/BROADCAST"].Value == "true" {
 		optBroadcast = 1
+		fmt.Printf("TODO: optBroadcast is not yet implemented - you set it to %d\n", optBroadcast)
 	}
 	if cmd.Flags["/EDIT"].Value == "true" {
 		optEdit = 1
+		fmt.Printf("TODO: optEdit is not yet implemented - you set it to %d\n", optEdit)
 	}
 	if cmd.Flags["/EXPIRATION"].Value != "" {
 		// dd-mmm-yyyy, or delta time: dddd
@@ -100,12 +106,14 @@ func ActionAdd(cmd *dclish.Command) error {
 	}
 	if cmd.Flags["/EXTRACT"].Value == "true" {
 		optExtract = 1
+		fmt.Printf("TODO: optExtract is not yet implemented - you set it to %d\n", optExtract)
 	}
 	if cmd.Flags["/FOLDER"].Value != "" {
 		optFolder = strings.Split(cmd.Flags["/FOLDER"].Value, ",")
 	}
 	if cmd.Flags["/INDENT"].Value == "true" {
 		optIndent = 1
+		fmt.Printf("TODO: optIndent is not yet implemented - you set it to %d\n", optIndent)
 	}
 	if cmd.Flags["/PERMANENT"].Value == "true" {
 		optPermanent = 1
@@ -115,34 +123,38 @@ func ActionAdd(cmd *dclish.Command) error {
 	}
 	if cmd.Flags["/SIGNATURE"].Value == "true" {
 		optSignature = 1
+		fmt.Printf("TODO: optSignature is not yet implemented - you set it to %d\n", optSignature)
 	}
 	if cmd.Flags["/SUBJECT"].Value != "" {
 		optSubject = cmd.Flags["/SUBJECT"].Value
 	}
 	if cmd.Flags["/SYSTEM"].Value == "true" {
 		optSystem = 1
+		fmt.Printf("TODO: optSystem is not yet implemented - you set it to %d\n", optSystem)
 	}
 
-	fmt.Printf("TODO: optAll is not yet implemented - you set it to %d\n", optAll)
-	fmt.Printf("TODO: optBell is not yet implemented - you set it to %d\n", optBell)
-	fmt.Printf("TODO: optBroadcast is not yet implemented - you set it to %d\n", optBroadcast)
-	fmt.Printf("TODO: optEdit is not yet implemented - you set it to %d\n", optEdit)
-	fmt.Printf("TODO: optExtract is not yet implemented - you set it to %d\n", optExtract)
-	fmt.Printf("TODO: optIndent is not yet implemented - you set it to %d\n", optIndent)
-	fmt.Printf("TODO: optSignature is not yet implemented - you set it to %d\n", optSignature)
-	fmt.Printf("TODO: optSystem is not yet implemented - you set it to %d\n", optSystem)
-
 	if len(optFolder) == 0 {
 		optFolder = []string{this.Folder.Name}
 	}
-	// TODO: check if folders exist.
+	for i := range optFolder {
+		f, _ := this.Q.FindFolderExact(ctx, optFolder[i])
+		if f.Name == "" {
+			return fmt.Errorf("Folder '%s' does not exist", optFolder[i])
+		}
+		if f.Visibility != 0 && this.User.Admin == 0 && this.User.Login != f.Owner {
+			return fmt.Errorf("Folder '%s' is not accessible", optFolder[i])
+		}
+	}
+
 	if optSubject == "" {
 		optSubject, _ = ask.GetLine("Enter subject of message: ")
 		if optSubject == "" {
 			return errors.New("Must enter a subject")
 		}
 	}
-	// TODO: check we have permission for shutdown and permanent
+	if optShutdown == 1 && this.User.Admin == 0 {
+		return errors.New("Must be an admin to set shutdown")
+	}
 
 	message, err := editor.Editor(
 		fmt.Sprintf("Enter message for '%s'...", optSubject),
@@ -160,7 +172,7 @@ func ActionAdd(cmd *dclish.Command) error {
 
 // ActionCurrent handles the `CURRENT` command.
 func ActionCurrent(_ *dclish.Command) error {
-	msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, this.MsgID)
+	msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, this.MsgID)
 	if err != nil {
 		return err
 	}
@@ -179,7 +191,7 @@ func ActionBack(_ *dclish.Command) error {
 		fmt.Println("No previous messages")
 		return nil
 	}
-	msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
+	msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgid)
 	if err != nil {
 		return err
 	}
@@ -315,7 +327,7 @@ func ActionChange(cmd *dclish.Command) error {
 
 	isFolderOwner := folders.IsFolderOwner(this.Folder.Name, this.User.Login)
 	for i := range msgids {
-		msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgids[i])
+		msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgids[i])
 		if err != nil {
 			return err
 		}
@@ -376,7 +388,7 @@ func ActionFirst(_ *dclish.Command) error {
 		return nil
 	}
 	this.MsgID = msgid
-	msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
+	msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgid)
 	if err != nil {
 		return err
 	}
@@ -394,7 +406,7 @@ func ActionLast(_ *dclish.Command) error {
 		return nil
 	}
 	this.MsgID = msgid
-	msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
+	msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgid)
 	if err != nil {
 		return err
 	}
@@ -411,7 +423,7 @@ func ActionNext(_ *dclish.Command) error {
 		fmt.Println("No next messages")
 		return nil
 	}
-	msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
+	msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgid)
 	if err != nil {
 		return err
 	}
@@ -446,7 +458,7 @@ func ActionPrint(cmd *dclish.Command) error {
 	}
 	print("\033[5i")
 	for _, msgid := range msgids {
-		msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
+		msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgid)
 		if err != nil {
 			fmt.Printf("Message %d not found.\n", msgid)
 		} else {
@@ -476,7 +488,7 @@ func ActionRead(cmd *dclish.Command) error {
 		}
 	}
 	this.MsgID = msgid
-	msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
+	msg, err := folders.GetMessage(this.User.Login, this.Folder.Name, msgid)
 	if err != nil {
 		return err
 	}
@@ -496,7 +508,7 @@ func ActionReply(cmd *dclish.Command) error {
 	if cmd.Flags["/INDENT"].Value == "false" {
 		indent = false
 	}
-	original, err := folders.ReadMessage(this.User.Login, this.Folder.Name, this.MsgID)
+	original, err := folders.GetMessage(this.User.Login, this.Folder.Name, this.MsgID)
 	origMsg := ""
 	if extract {
 		if indent {
diff --git a/storage/folders.sql.go b/storage/folders.sql.go
index 98722dabe26e97740d2f1099af6e3960542b74fd..b479c9efd98fd7f98bd6f672a8d4f56142064b5f 100644
--- a/storage/folders.sql.go
+++ b/storage/folders.sql.go
@@ -7,6 +7,7 @@ package storage
 
 import (
 	"context"
+	"database/sql"
 )
 
 const createFolder = `-- name: CreateFolder :exec
@@ -64,12 +65,12 @@ func (q *Queries) FindFolderExact(ctx context.Context, name string) (Folder, err
 }
 
 const findFolderPrefix = `-- name: FindFolderPrefix :one
-SELECT name, "always", alert, description, owner, system, expire, visibility, create_at, update_at FROM folders where name LIKE ?
+SELECT name, "always", alert, description, owner, system, expire, visibility, create_at, update_at FROM folders where name LIKE ?1 || '%'
 ORDER BY name
 LIMIT 1
 `
 
-func (q *Queries) FindFolderPrefix(ctx context.Context, name string) (Folder, error) {
+func (q *Queries) FindFolderPrefix(ctx context.Context, name sql.NullString) (Folder, error) {
 	row := q.db.QueryRowContext(ctx, findFolderPrefix, name)
 	var i Folder
 	err := row.Scan(
diff --git a/storage/queries/folders.sql b/storage/queries/folders.sql
index 7f965c5a67d3710bbbe92ee6e775b508b253dd1a..6c8aba995606f32ae9a45b343e877fa506370ce7 100644
--- a/storage/queries/folders.sql
+++ b/storage/queries/folders.sql
@@ -20,7 +20,7 @@ ORDER BY f.name;
 SELECT * FROM folders where name = ?;
 
 -- name: FindFolderPrefix :one
-SELECT * FROM folders where name LIKE ?
+SELECT * FROM folders where name LIKE sqlc.arg(name) || '%'
 ORDER BY name
 LIMIT 1;