diff --git a/NOTES.md b/NOTES.md
index 2e325420f0a0d5844aa81cbfcf4f4bfa67c2b376..7148dbda0a351f7962eacf04a96fc5e5d2a28bd1 100644
--- a/NOTES.md
+++ b/NOTES.md
@@ -56,10 +56,10 @@ Top level:
 SET:
 
   ACCESS          +ALWAYS           BRIEF            DEFAULT_EXPIRE
-  EXPIRE_LIMIT     FOLDER          +NOALWAYS         NOBRIEF
-  NONOTIFY         NOPROMPT_EXPIRE  NOREADNEW        NOSHOWNEW
-  NOSYSTEM         NOTIFY           PROMPT_EXPIRE    READNEW
-  SHOWNEW          SYSTEM
+  EXPIRE_LIMIT     FOLDER           NOACCESS        +NOALWAYS
+  NOBRIEF          NOPROMPT_EXPIRE  NOREADNEW        NOSHOWNEW
+  NOSYSTEM         PROMPT_EXPIRE    READNEW          SHOWNEW
+  SYSTEM
 
 SHOW:
 
diff --git a/dclish/completer.go b/dclish/completer.go
index 82d7f674573a6f90b89b9497dcbd4d9cedc55cce..2eace9b1858081ec45d6016671dfa418c5d1394c 100644
--- a/dclish/completer.go
+++ b/dclish/completer.go
@@ -76,6 +76,5 @@ func (c Completer) Do(line []rune, pos int) ([][]rune, int) {
 	}
 
 	// Command completely typed in.
-	// TODO: figure out flags.
 	return newline, pos
 }
diff --git a/folders/folders.go b/folders/folders.go
index 2f9cdf6e513f2551d2f7b5a9814c036f6806b0a4..fef08be1b60e655962c05a98f5338272a5a51d1e 100644
--- a/folders/folders.go
+++ b/folders/folders.go
@@ -115,10 +115,8 @@ func IsFolderOwner(folder, login string) bool {
 
 // DeleteFolder deletes a folder.
 func DeleteFolder(name string) error {
-	// TODO: make sure user can delete this table.
 	ctx := storage.Context()
 	err := this.Q.DeleteFolder(ctx, name)
-	// TODO: process this error a bit more to give a better error message.
 	if err != nil {
 		return err
 	}
diff --git a/folders/messages.go b/folders/messages.go
index 055ec887514a944439b554699cceaa96f8492764..07a6645fc58ccee3b8a3867c9eb3b2b900f320e5 100644
--- a/folders/messages.go
+++ b/folders/messages.go
@@ -25,7 +25,6 @@ func CreateMessage(author, subject, message, folder string, permanent, shutdown
 		exp := time.Now().AddDate(0, 0, int(days)).UTC()
 		expiration = &exp
 	}
-	// TODO: replace _ with rows and check.
 	err := this.Q.CreateMessage(ctx, storage.CreateMessageParams{
 		Folder:     folder,
 		Author:     author,
@@ -139,11 +138,29 @@ func LastMessage(folder string) int64 {
 // ListMessages lists messages.
 func ListMessages(folder string) ([]storage.Message, error) {
 	ctx := storage.Context()
-	// TODO: options aren't implemented - need to set them?
 	rows, err := this.Q.ListMessages(ctx, folder)
 	return rows, err
 }
 
+// WroteAllMessages returns true if login wrote all msgids
+func WroteAllMessages(login string, msgids []int64) bool {
+	ctx := storage.Context()
+
+	for _, msgid := range msgids {
+		msg, err := this.Q.GetMessage(ctx, storage.GetMessageParams{
+			ID:     msgid,
+			Folder: this.Folder.Name,
+		})
+		if err != nil {
+			return false
+		}
+		if msg.Author != login {
+			return false
+		}
+	}
+	return true
+}
+
 // DeleteMessages deletes a list of messages.
 func DeleteMessages(msgids []int64) error {
 	ctx := storage.Context()
diff --git a/pager/pager.go b/pager/pager.go
index 9ecd1dbe12e1f8ed5c0b6ee573f8ab44398f0e7a..79ddd9ea8e7170990d835d52aa64daf04b1b1d64 100644
--- a/pager/pager.go
+++ b/pager/pager.go
@@ -79,7 +79,6 @@ func Pager(content string) bool {
 			return false
 		}
 
-		// TODO: get '/' to work for searching.
 		switch key {
 		case ' ': // page down
 			start += pageSize
diff --git a/repl/folders.go b/repl/folders.go
index c8b7f4a49b39b4094a819242e14f41534325ca29..7f385af52692bb69c63e6dad23b4a34beb739f98 100644
--- a/repl/folders.go
+++ b/repl/folders.go
@@ -181,6 +181,12 @@ func ActionModify(cmd *dclish.Command) error {
 
 // ActionRemove handles the `REMOVE` command.  This modifies a folder.
 func ActionRemove(cmd *dclish.Command) error {
+	if this.User.Login != this.Folder.Owner && this.User.Admin == 0 {
+		return errors.New("Must be folder owner or admin to delete the folder")
+	}
+	if this.Folder.Name == "GENERAL" {
+		return errors.New("Can't delete folder GENERAL")
+	}
 	err := folders.DeleteFolder(cmd.Args[0])
 	if err == nil {
 		fmt.Println("Folder removed.")
diff --git a/repl/mail.go b/repl/mail.go
index 5b4469d107be3512cc9b70b5da8cb9d02bc0bc53..addac2a04f029882cab14248e3b90467ed10b18b 100644
--- a/repl/mail.go
+++ b/repl/mail.go
@@ -1,38 +1,25 @@
 package repl
 
 import (
-	"fmt"
+	"errors"
 
 	"git.lyda.ie/kevin/bulletin/dclish"
 )
 
-/*
-
-Instead of making MAIL and FORWARD the same, why not have "MAIL" enter a mail
-mode where you can read mails.
-
-Alternatively, make "mail" just a folder like any other except set to private and make the owner that user.  Maybe have a leading colon that CREATE can't use.
-
-Or... just get rid of the mail bit.
-
-Or... tie it into actual smtp mail.
-
-*/
-
 // ActionForward handles the `FORWARD` command.
 func ActionForward(_ *dclish.Command) error {
-	fmt.Println("ERROR: mail system is yet TODO.")
+	errors.New("Mail system is not yet implemented (see issue 9)")
 	return nil
 }
 
 // ActionMail handles the `MAIL` command.
 func ActionMail(_ *dclish.Command) error {
-	fmt.Println("ERROR: mail system is yet TODO.")
+	errors.New("Mail system is not yet implemented (see issue 9)")
 	return nil
 }
 
 // ActionRespond handles the `RESPOND` command.
 func ActionRespond(_ *dclish.Command) error {
-	fmt.Println("ERROR: mail system is yet TODO.")
+	errors.New("Mail system is not yet implemented (see issue 9)")
 	return nil
 }
diff --git a/repl/messages.go b/repl/messages.go
index 16d202755137b47e5f4aff110b9ede9e04ed36db..ee6211685ccbc95ee5b0bb40f31358be0d9454e5 100644
--- a/repl/messages.go
+++ b/repl/messages.go
@@ -20,7 +20,6 @@ import (
 // ActionDirectory handles the `DIRECTORY` command.  This lists all the
 // messages in the current folder.
 func ActionDirectory(cmd *dclish.Command) error {
-	// TODO: flag parsing.
 	showExpiration := false
 	if cmd.Flags["/EXPIRATION"].Value == "true" {
 		showExpiration = true
@@ -77,19 +76,19 @@ func ActionAdd(cmd *dclish.Command) error {
 		optAll = 1
 	}
 	if cmd.Flags["/ALL"].Set {
-		fmt.Printf("TODO: optAll is not yet implemented - you set it to %d\n", optAll)
+		fmt.Printf("/ALL is not yet implemented - you set it to %d\n", optAll)
 	}
 	if cmd.Flags["/BELL"].Value == "true" {
 		optBell = 1
 	}
 	if cmd.Flags["/BELL"].Set {
-		fmt.Printf("TODO: optBell is not yet implemented - you set it to %d\n", optBell)
+		fmt.Printf("/BELL is not yet implemented - you set it to %d\n", optBell)
 	}
 	if cmd.Flags["/BROADCAST"].Value == "true" {
 		optBroadcast = 1
 	}
 	if cmd.Flags["/BROADCAST"].Set {
-		fmt.Printf("TODO: optBroadcast is not yet implemented - you set it to %d\n", optBroadcast)
+		fmt.Printf("/BROADCAST is not yet implemented - you set it to %d\n", optBroadcast)
 	}
 	if cmd.Flags["/EXPIRATION"].Value != "" {
 		// dd-mmm-yyyy, or delta time: dddd
@@ -109,7 +108,7 @@ func ActionAdd(cmd *dclish.Command) error {
 		optExtract = 1
 	}
 	if cmd.Flags["/EXTRACT"].Set {
-		fmt.Printf("TODO: optExtract is not yet implemented - you set it to %d\n", optExtract)
+		fmt.Printf("/EXTRACT is not yet implemented - you set it to %d\n", optExtract)
 	}
 	if cmd.Flags["/FOLDER"].Value != "" {
 		optFolder = strings.Split(cmd.Flags["/FOLDER"].Value, ",")
@@ -118,7 +117,7 @@ func ActionAdd(cmd *dclish.Command) error {
 		optIndent = 1
 	}
 	if cmd.Flags["/INDENT"].Set {
-		fmt.Printf("TODO: optIndent is not yet implemented - you set it to %d\n", optIndent)
+		fmt.Printf("/INDENT is not yet implemented - you set it to %d\n", optIndent)
 	}
 	if cmd.Flags["/PERMANENT"].Value == "true" {
 		optPermanent = 1
@@ -130,7 +129,7 @@ func ActionAdd(cmd *dclish.Command) error {
 		optSignature = 1
 	}
 	if cmd.Flags["/SIGNATURE"].Set {
-		fmt.Printf("TODO: optSignature is not yet implemented - you set it to %d\n", optSignature)
+		fmt.Printf("/SIGNATURE is not yet implemented - you set it to %d\n", optSignature)
 	}
 	if cmd.Flags["/SUBJECT"].Value != "" {
 		optSubject = cmd.Flags["/SUBJECT"].Value
@@ -139,7 +138,7 @@ func ActionAdd(cmd *dclish.Command) error {
 		optSystem = 1
 	}
 	if cmd.Flags["/SYSTEM"].Set {
-		fmt.Printf("TODO: optSystem is not yet implemented - you set it to %d\n", optSystem)
+		fmt.Printf("/SYSTEM is not yet implemented - you set it to %d\n", optSystem)
 	}
 
 	if len(optFolder) == 0 {
@@ -547,7 +546,6 @@ func ActionReply(cmd *dclish.Command) error {
 
 // ActionSeen handles the `SEEN` command.
 func ActionSeen(cmd *dclish.Command) error {
-	// TODO: review help.
 	var err error
 	msgids := []int64{this.MsgID}
 	if len(cmd.Args) == 1 {
@@ -565,7 +563,6 @@ func ActionSeen(cmd *dclish.Command) error {
 
 // ActionUnseen handles the `UNSEEN` command.
 func ActionUnseen(cmd *dclish.Command) error {
-	// TODO: review help.
 	var err error
 	msgids := []int64{this.MsgID}
 	if len(cmd.Args) == 1 {
@@ -576,14 +573,13 @@ func ActionUnseen(cmd *dclish.Command) error {
 	}
 	err = folders.MarkUnseen(msgids)
 	if err != nil {
-		fmt.Printf("ERROR: %s.\n", err)
+		return err
 	}
 	return nil
 }
 
 // ActionDelete handles the `DELETE` command.
 func ActionDelete(cmd *dclish.Command) error {
-	// TODO: Follow permissions.
 	var err error
 
 	all := false
@@ -595,9 +591,12 @@ func ActionDelete(cmd *dclish.Command) error {
 			fmt.Println("ERROR: Can't specify both message numbers and /ALL flag.")
 			return nil
 		}
+		if this.User.Admin == 0 {
+			return errors.New("Must be admin to use /ALL")
+		}
 		err := folders.DeleteAllMessages()
 		if err != nil {
-			fmt.Printf("ERROR: %s.\n", err)
+			return err
 		}
 		return nil
 	}
@@ -606,13 +605,15 @@ func ActionDelete(cmd *dclish.Command) error {
 	if len(cmd.Args) == 1 {
 		msgids, err = ParseNumberList(cmd.Args[0])
 		if err != nil {
-			fmt.Printf("ERROR: %s.\n", err)
-			return nil
+			return err
 		}
 	}
+	if this.User.Admin == 0 && !folders.WroteAllMessages(this.User.Login, msgids) {
+		return errors.New("Can't delete messages you haven't written")
+	}
 	err = folders.DeleteMessages(msgids)
 	if err != nil {
-		fmt.Printf("ERROR: %s.\n", err)
+		return err
 	}
 	return nil
 }
diff --git a/repl/xfer.go b/repl/xfer.go
index c4c22542104e0ff160310a2af727827bb4bb216f..997a356acd5d1ee259ebb23ee2495a02d5e3e6a6 100644
--- a/repl/xfer.go
+++ b/repl/xfer.go
@@ -10,7 +10,6 @@ import (
 
 // ActionCopy handles the `COPY` command.
 func ActionCopy(cmd *dclish.Command) error {
-	// TODO: handle flags.
 	folder := cmd.Args[0]
 	msgids := []int64{this.MsgID}
 	if len(cmd.Args) == 2 {
@@ -20,7 +19,7 @@ func ActionCopy(cmd *dclish.Command) error {
 			return err
 		}
 	}
-	fmt.Printf("TODO: copy %+v to %s\n", msgids, folder)
+	fmt.Printf("copy %+v to %s is not implemented\n", msgids, folder)
 	/*
 		msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
 		if err != nil {
@@ -32,7 +31,6 @@ func ActionCopy(cmd *dclish.Command) error {
 
 // ActionMove handles the `MOVE` command.
 func ActionMove(cmd *dclish.Command) error {
-	// TODO: handle flags.
 	folder := cmd.Args[0]
 	msgids := []int64{this.MsgID}
 	if len(cmd.Args) == 2 {
@@ -42,7 +40,7 @@ func ActionMove(cmd *dclish.Command) error {
 			return err
 		}
 	}
-	fmt.Printf("TODO: move %+v to %s\n", msgids, folder)
+	fmt.Printf("move %+v to %s is not implemented\n", msgids, folder)
 	/*
 		msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid)
 		if err != nil {
diff --git a/storage/messages.sql.go b/storage/messages.sql.go
index ad7545c638d28ca44eb4ee61671c761057e1332f..84d85a8fa52a9972cbbe6cb08e825fd2b5d5b8d1 100644
--- a/storage/messages.sql.go
+++ b/storage/messages.sql.go
@@ -124,7 +124,7 @@ type GetLastReadRow struct {
 	Author string
 }
 
-// - TODO: These get the max message written, not read.  Leaving for now; easier to test.
+// GetLastRead gets the last message read by a login in a folder.
 func (q *Queries) GetLastRead(ctx context.Context, folder string) ([]GetLastReadRow, error) {
 	rows, err := q.db.QueryContext(ctx, getLastRead, folder)
 	if err != nil {
diff --git a/storage/queries/messages.sql b/storage/queries/messages.sql
index afef9bdbd56b5fc5df8713144597c23c4d55600e..5a49f8176808dd05c183ae32cfb9da6024ed3d15 100644
--- a/storage/queries/messages.sql
+++ b/storage/queries/messages.sql
@@ -38,7 +38,7 @@ DELETE FROM messages WHERE folder = ?;
 -- name: LastMsgidIgnoringSeen :one
 SELECT CAST(MAX(id) AS INT) FROM messages AS m WHERE m.folder = ?1;
 
---- TODO: These get the max message written, not read.  Leaving for now; easier to test.
+-- GetLastRead gets the last message read by a login in a folder.
 -- name: GetLastRead :many
 SELECT CAST(MAX(m.id) AS INT) AS id, m.author FROM messages AS m, users AS u
   WHERE folder = ? AND u.login == m.author