diff --git a/dclish/dclish.go b/dclish/dclish.go
index 3e6cb9e00c49d45f3323388bd3fd4d425c4291da..15788c5a34452a8fb92099bf755954ef738d1941 100644
--- a/dclish/dclish.go
+++ b/dclish/dclish.go
@@ -106,11 +106,35 @@ func split(line string) []string {
 	return words
 }
 
+// PrefixMatch searches for a command in a list of possible commands.
+func PrefixMatch(command string, commands []string) (string, error) {
+	cmd := strings.ToUpper(command)
+	possibles := []string{}
+	for i := range commands {
+		if strings.HasPrefix(cmd, commands[i]) {
+			possibles = append(possibles, commands[i])
+		}
+	}
+	switch len(possibles) {
+	case 0:
+		return "", fmt.Errorf("Unknown command '%s'", command)
+	case 1:
+		return possibles[0], nil
+	default:
+		return "", fmt.Errorf("Ambiguous command '%s' (matches %s)",
+			command, strings.Join(possibles, ", "))
+	}
+}
+
 // ParseAndRun parses a command line and runs the command.
 func (c Commands) ParseAndRun(line string) error {
 	// Split into words.
 	words := split(line)
 
+	return c.run(words)
+}
+
+func (c Commands) run(words []string) error {
 	// Find the command.
 	wordup := strings.ToUpper(words[0])
 	cmd, ok := c[wordup]
@@ -141,9 +165,7 @@ func (c Commands) ParseAndRun(line string) error {
 			fmt.Printf("ERROR: missing subcommand for %s.\n", wordup)
 			return nil
 		}
-		// TODO: quoting is probably an issue here - break ParseAndRun into ParseAndRun + Run.
-		newline := strings.Join(words[1:], " ")
-		return cmd.Commands.ParseAndRun(newline)
+		return cmd.Commands.run(words[1:])
 	}
 
 	if cmd.Action == nil {
diff --git a/folders/sql/1_create_table.up.sql b/folders/sql/1_create_table.up.sql
index 74bb11c115363182ad8a337d34b7d7cc3ae0cce8..250faa148115120fee7d0ba5b6f0d48595461b52 100644
--- a/folders/sql/1_create_table.up.sql
+++ b/folders/sql/1_create_table.up.sql
@@ -1,8 +1,9 @@
 CREATE TABLE users (
   login       VARCHAR(12)  NOT NULL PRIMARY KEY,
   name        VARCHAR(53)  NOT NULL,
-  admin       INT          DEFAULT 0,
-  disabled    INT          DEFAULT 0,
+  admin       INT          DEFAULT 0 NOT NULL,
+  moderator   INT          DEFAULT 0 NOT NULL,
+  disabled    INT          DEFAULT 0 NOT NULL,
   last_login  TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
   create_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
   update_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL
@@ -34,16 +35,15 @@ END;
 
 CREATE TABLE folders (
   name        VARCHAR(25)  NOT NULL PRIMARY KEY,
-  always      INT          DEFAULT 0,
-  brief       INT          DEFAULT 0,
-  description VARCHAR(53)  NOT NULL,
-  notify      INT          DEFAULT 0,
-  owner       VARCHAR(25)  REFERENCES users(login) ON UPDATE CASCADE,
-  readnew     INT          DEFAULT 0,
-  shownew     INT          DEFAULT 0,
-  system      INT          DEFAULT 0,
-  expire      INT          DEFAULT 14,
-  visibility  TEXT         DEFAULT 'public',
+  always      INT          DEFAULT 0 NOT NULL,
+  brief       INT          DEFAULT 0 NOT NULL,
+  description VARCHAR(53)  DEFAULT 0 NOT NULL,
+  notify      INT          DEFAULT 0 NOT NULL,
+  readnew     INT          DEFAULT 0 NOT NULL,
+  shownew     INT          DEFAULT 0 NOT NULL,
+  system      INT          DEFAULT 0 NOT NULL,
+  expire      INT          DEFAULT 14 NOT NULL,
+  visibility  TEXT         DEFAULT 'public' NOT NULL,
   create_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
   update_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL
 ) WITHOUT ROWID;
@@ -87,7 +87,7 @@ END;
 INSERT INTO folders (name, description, system, shownew, owner)
        VALUES ('GENERAL', 'Default general bulletin folder.', 1, 1, 'SYSTEM');
 
-CREATE TABLE co_owners (
+CREATE TABLE owners (
   folder      VARCHAR(25)  REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE,
   owner       VARCHAR(25)  REFERENCES users(login) ON UPDATE CASCADE,
   create_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
@@ -95,11 +95,11 @@ CREATE TABLE co_owners (
   PRIMARY KEY (folder, owner)
 ) WITHOUT ROWID;
 
-CREATE TRIGGER co_owners_after_update_update_at
-  AFTER UPDATE ON co_owners FOR EACH ROW
+CREATE TRIGGER owners_after_update_update_at
+  AFTER UPDATE ON owners FOR EACH ROW
     WHEN NEW.update_at = OLD.update_at    --- avoid infinite loop
 BEGIN
-  UPDATE co_owners SET update_at=CURRENT_TIMESTAMP WHERE folder=NEW.folder AND owner=NEW.owner;
+  UPDATE owners SET update_at=CURRENT_TIMESTAMP WHERE folder=NEW.folder AND owner=NEW.owner;
 END;
 
 CREATE TABLE messages (
@@ -108,8 +108,8 @@ CREATE TABLE messages (
   author      VARCHAR(25)  REFERENCES users(login) ON UPDATE CASCADE,
   subject     VARCHAR(53)  NOT NULL,
   message     TEXT         NOT NULL,
-  permanent   INT          DEFAULT 0,
-  shutdown    INT          DEFAULT 0,
+  permanent   INT          DEFAULT 0 NOT NULL,
+  shutdown    INT          DEFAULT 0 NOT NULL,
   expiration  TIMESTAMP    NOT NULL,
   create_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
   update_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
@@ -118,7 +118,7 @@ CREATE TABLE messages (
 CREATE INDEX messages_idx_shutdown ON messages(shutdown);
 CREATE INDEX messages_idx_expiration ON messages(expiration);
 
-CREATE TABLE read (
+CREATE TABLE seen (
   login       VARCHAR(25) REFERENCES users(login) ON DELETE CASCADE ON UPDATE CASCADE,
   folder      VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE,
   msgid       INT,
@@ -142,17 +142,24 @@ CREATE TABLE mark (
     ON UPDATE CASCADE
 ) WITHOUT ROWID;
 
+CREATE TABLE access (
+  login       VARCHAR(25) REFERENCES users(login) ON DELETE CASCADE ON UPDATE CASCADE,
+  folder      VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE,
+  PRIMARY KEY (login, folder),
+) WITHOUT ROWID;
+
 --- TODO: The following is incomplete:
 --- User configs.
 CREATE TABLE config (
   login       VARCHAR(25) REFERENCES users(login) ON DELETE CASCADE ON UPDATE CASCADE,
   folder      VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE,
-  always      INT NOT NULL,
-  alert       INT NOT NULL DEFAULT 0,  --- 0=no, 1=brief, 2=readnew
+  always      INT     NOT NULL DEFAULT 0,
+  alert       INT     NOT NULL DEFAULT 0,  --- 0=no, 1=brief, 2=readnew
+  always      INT     NOT NULL
 ) WITHOUT ROWID;
 
 --- System configs.
 CREATE TABLE system (
-  default_expire  INT NOT NULL DEFAULT -1,
-  expire_limit    INT NOT NULL DEFAULT -1,
+  default_expire  INT     NOT NULL DEFAULT -1,
+  expire_limit    INT     NOT NULL DEFAULT -1,
 ) WITHOUT ROWID;
diff --git a/repl/command.go b/repl/command.go
index 88e4f1bff3f887f870d680871c3880f056043fc7..30534049749ebf1897937d3e667b39c9fce941d6 100644
--- a/repl/command.go
+++ b/repl/command.go
@@ -1439,16 +1439,22 @@ to see what is presently set.  This is a privileged command.
 
 The parameters are one or  more  privileges  separated  by  commas.   To
 remove  a privilege, specify the privilege preceeded by "NO".  If /ID is
-specified, the parameters are rights identifiers.`,
-				Flags: dclish.Flags{
-					"/ID": {
-						Description: `/[NO]ID
+specified, the parameters are rights identifiers.
 
-  If specified,  then the  rights identifier which  is specified  as the
-  parameter will allow  users holding that rights  identifier to execute
-  privileged commands. If /NOID is specified, the identifier is removed.`,
-					},
-				},
+For the reimplementation this is used to manage users.  The following parameters
+are available.
+
+  Format:
+	  SET PRIVILEGES CREATE login
+	  SET PRIVILEGES DELETE login
+	  SET PRIVILEGES SSH [login]
+	  SET PRIVILEGES [NO]ADMIN login
+	  SET PRIVILEGES [NO]MOD login [folder]
+	  SET PRIVILEGES ENABLE login
+	  SET PRIVILEGES DISABLE login`,
+				MinArgs: 1,
+				MaxArgs: 3,
+				Action:  ActionSetPrivileges,
 			},
 			"PROMPT_EXPIRE": {
 				Description: `Specifies  that a  user will  be prompted  for an  expiration date  when
@@ -1492,17 +1498,8 @@ the EXIT command will cause you to skip to those folders.  (See HELP SET
 SYSTEM for a description of a SYSTEM folder).`,
 				Flags: dclish.Flags{
 					"/ALL": {
-						Description: `  Specifies that the SET [NO]READNEW option is the default for all users
-  for  the  specified  folder.  This  is  a  privileged  qualifier.  The
-  difference  between this  and /DEFAULT  is that  the latter  will only
-  apply  to  new  users  (i.e.  any  users  which  have  never  executed
-  BULLETIN).`,
-					},
-					"/DEFAULT": {
-						Description: `  Specifies that the [NO]READNEW option is the default for the specified
-  folder. This is a privileged qualifier.  It will only affect brand new
-  users (or  those that have  never logged in).  Use /ALL to  modify all
-  users.`,
+						Description: `  Specifies that the SET READNEW option is the default for all users for
+  the specified folder.  This is a privileged  qualifier.`,
 					},
 					"/PERMANENT": {
 						Description: `/[NO]PERMANENT
@@ -1515,30 +1512,11 @@ SYSTEM for a description of a SYSTEM folder).`,
 			"NOREADNEW": {
 				Description: `Turns off READNEW.
   Format:
-    SET NOREADNEW
-
-NOTE:  If  you  have several folders with READNEW enabled, each folder's
-messages will be displayed separately.  However, if you EXIT the READNEW
-mode before all the folders have been displayed, you will not be alerted
-of the new messages in the undisplayed folders the next time you  login.
-However,  if  you enter BULLETIN, you will be told that new messages are
-present in those other folders.  Also, it is not possible  to  EXIT  the
-READNEW mode if there are SYSTEM folders which have new messages. Typing
-the EXIT command will cause you to skip to those folders.  (See HELP SET
-SYSTEM for a description of a SYSTEM folder).`,
+    SET NOREADNEW`,
 				Flags: dclish.Flags{
 					"/ALL": {
-						Description: `  Specifies that the SET [NO]READNEW option is the default for all users
-  for  the  specified  folder.  This  is  a  privileged  qualifier.  The
-  difference  between this  and /DEFAULT  is that  the latter  will only
-  apply  to  new  users  (i.e.  any  users  which  have  never  executed
-  BULLETIN).`,
-					},
-					"/DEFAULT": {
-						Description: `  Specifies that the [NO]READNEW option is the default for the specified
-  folder. This is a privileged qualifier.  It will only affect brand new
-  users (or  those that have  never logged in).  Use /ALL to  modify all
-  users.`,
+						Description: `  Specifies that the  SET NOREADNEW option is the default  for all users
+  for  the  specified  folder.  This  is  a  privileged  qualifier.`,
 					},
 					"/FOLDER": {
 						Description: `/FOLDER=foldername
@@ -1564,15 +1542,8 @@ In order to apply this to a specific folder,  first  select  the  folder
 				Flags: dclish.Flags{
 					"/ALL": {
 						Description: `  Specifies that the SET SHOWNEW option is the default for all users for
-  the specified folder.  This is a privileged  qualifier. The difference
-  between this  and /DEFAULT is that  the latter will only  apply to new
-  users (i.e. any users which have never executed BULLETIN).`,
+  the specified folder.  This is a privileged  qualifier.`,
 					},
-					"/DEFAULT": {
-						Description: `  Specifies that  the SHOWNEW  option is the  default for  the specified
-  folder. This is a privileged qualifier.  It will only affect brand new
-  users (or  those that have  never logged in).  Use /ALL to  modify all
-  users.`},
 					"/PERMANENT": {
 						Description: `/[NO]PERMANENT
 
@@ -1590,16 +1561,7 @@ In order to apply this to a specific folder,  first  select  the  folder
 				Flags: dclish.Flags{
 					"/ALL": {
 						Description: `  Specifies that the  SET NOSHOWNEW option is the default  for all users
-  for  the  specified  folder.  This  is  a  privileged  qualifier.  The
-  difference  between this  and /DEFAULT  is that  the latter  will only
-  apply  to  new  users  (i.e.  any  users  which  have  never  executed
-  BULLETIN).`,
-					},
-					"/DEFAULT": {
-						Description: `  Specifies that the  NOSHOWNEW option is the default  for the specified
-  folder. This is a privileged qualifier.  It will only affect brand new
-  users (or  those that have  never logged in).  Use /ALL to  modify all
-  users.`,
+  for  the  specified  folder.  This  is  a  privileged  qualifier.`,
 					},
 					"/FOLDER": {
 						Description: `/FOLDER=foldername
diff --git a/repl/set.go b/repl/set.go
index f87749f4e2692570767625fefdea2f9886da9d32..bc6d276aebccadb0d5595fd846ecabe1661c7907 100644
--- a/repl/set.go
+++ b/repl/set.go
@@ -89,8 +89,38 @@ func ActionSetNonotify(_ *dclish.Command) error {
 }
 
 // ActionSetPrivileges handles the `SET PRIVILEGES` command.
-func ActionSetPrivileges(_ *dclish.Command) error {
-	fmt.Println("TODO: implement ActionSetPrivileges.")
+func ActionSetPrivileges(cmd *dclish.Command) error {
+	// TODO: OK, need a better parser.
+	switch cmd.Args[0] {
+	case "CREATE":
+		if len(cmd.Args) != 2 {
+			fmt.Println("ERROR: Must pass single login.")
+			return nil
+		}
+		fmt.Println("TODO: Create user creation routine - see repl/repl.go.")
+	case "DELETE":
+		if len(cmd.Args) != 2 {
+			fmt.Println("ERROR: Must pass single login.")
+			return nil
+		}
+		fmt.Println("TODO: Create user delete routine - see repl/repl.go.")
+	case "SSH":
+		fmt.Println("TODO: Create ssh routine.")
+	case "ADMIN":
+		fmt.Println("TODO: Create an admin bit set/unset routine.")
+	case "NOADMIN":
+		fmt.Println("TODO: Create an admin bit set/unset routine.")
+	case "MOD":
+		fmt.Println("TODO: Create a mod bit set/unset routine.")
+	case "NOMOD":
+		fmt.Println("TODO: Create a mod bit set/unset routine.")
+	case "ENABLE":
+		fmt.Println("TODO: Create a disable bit set/unset routine.")
+	case "DISABLE":
+		fmt.Println("TODO: Create a disable bit set/unset routine.")
+	default:
+		fmt.Println("ERROR: Command not understood.")
+	}
 	return nil
 }