From eec333e07be744bcc3a1fea4449000d48bd5234c Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 10 Apr 2014 18:58:09 -0400
Subject: [PATCH] Refactor 'gitblit' commands into several nested dispatchers

---
 /dev/null                                                              |   59 -------
 src/main/java/com/gitblit/transport/ssh/gitblit/TicketsDispatcher.java |   29 +++
 src/site/setup_transport_ssh.mkd                                       |   34 +--
 src/main/java/com/gitblit/transport/ssh/gitblit/ListDispatcher.java    |  190 +++++++++++++++++++++++
 src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java |   14 -
 src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java    |  121 +++++++++++++++
 6 files changed, 361 insertions(+), 86 deletions(-)

diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/AddKeyCommand.java b/src/main/java/com/gitblit/transport/ssh/gitblit/AddKeyCommand.java
deleted file mode 100644
index 6980834..0000000
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/AddKeyCommand.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2014 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.gitblit.transport.ssh.gitblit;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.kohsuke.args4j.Argument;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.transport.ssh.commands.CommandMetaData;
-
-/**
- * Add a key to the current user's authorized keys list.
- *
- * @author James Moger
- *
- */
-@CommandMetaData(name = "add-key", description = "Add an SSH public key to your account")
-public class AddKeyCommand extends BaseKeyCommand {
-
-	protected final Logger log = LoggerFactory.getLogger(getClass());
-
-	@Argument(metaVar = "<stdin>|KEY", usage = "the key to add")
-	private List<String> addKeys = new ArrayList<String>();
-
-	@Override
-	public void run() throws IOException, UnloggedFailure {
-		String username = getContext().getClient().getUsername();
-		List<String> keys = readKeys(addKeys);
-		for (String key : keys) {
-			getKeyManager().addKey(username, key);
-			log.info("added SSH public key for {}", username);
-		}
-	}
-}
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java
index eb3bb0c..fe9075d 100644
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java
@@ -24,16 +24,14 @@
 
 	@Override
 	protected void registerCommands(UserModel user) {
-		// normal usage commands
+		// commands in this dispatcher
 		registerCommand(user, VersionCommand.class);
-		registerCommand(user, AddKeyCommand.class);
-		registerCommand(user, RemoveKeyCommand.class);
-		registerCommand(user, LsCommand.class);
-		registerCommand(user, ReviewCommand.class);
-
-		// administrative commands
-		registerCommand(user, LsUsersCommand.class);
 		registerCommand(user, CreateRepository.class);
 		registerCommand(user, SetAccountCommand.class);
+
+		// nested dispatchers
+		registerDispatcher(user, ListDispatcher.class);
+		registerDispatcher(user, KeysDispatcher.class);
+		registerDispatcher(user, TicketsDispatcher.class);
 	}
 }
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
new file mode 100644
index 0000000..4430c68
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh.gitblit;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.sshd.common.util.Buffer;
+import org.kohsuke.args4j.Argument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.IPublicKeyManager;
+import com.gitblit.transport.ssh.commands.CommandMetaData;
+import com.gitblit.transport.ssh.commands.DispatchCommand;
+import com.gitblit.transport.ssh.commands.SshCommand;
+
+/**
+ * The dispatcher and it's commands for SSH public key management.
+ *
+ * @author James Moger
+ *
+ */
+@CommandMetaData(name = "keys", description = "SSH public key management commands")
+public class KeysDispatcher extends DispatchCommand {
+
+	@Override
+	protected void registerCommands(UserModel user) {
+		registerCommand(user, AddKey.class);
+		registerCommand(user, RemoveKey.class);
+		registerCommand(user, ListKeys.class);
+	}
+
+	@CommandMetaData(name = "add", description = "Add an SSH public key to your account")
+	public static class AddKey extends BaseKeyCommand {
+
+		protected final Logger log = LoggerFactory.getLogger(getClass());
+
+		@Argument(metaVar = "<stdin>|KEY", usage = "the key to add")
+		private List<String> addKeys = new ArrayList<String>();
+
+		@Override
+		public void run() throws IOException, UnloggedFailure {
+			String username = getContext().getClient().getUsername();
+			List<String> keys = readKeys(addKeys);
+			for (String key : keys) {
+				getKeyManager().addKey(username, key);
+				log.info("added SSH public key for {}", username);
+			}
+		}
+	}
+
+	@CommandMetaData(name = "remove", aliases = { "rm" }, description = "Remove an SSH public key from your account")
+	public static class RemoveKey extends BaseKeyCommand {
+
+		protected final Logger log = LoggerFactory.getLogger(getClass());
+
+		private final String ALL = "ALL";
+
+		@Argument(metaVar = "<stdin>|<KEY>|ALL", usage = "the key to remove")
+		private List<String> removeKeys = new ArrayList<String>();
+
+		@Override
+		public void run() throws IOException, UnloggedFailure {
+			String username = getContext().getClient().getUsername();
+			List<String> keys = readKeys(removeKeys);
+			if (keys.contains(ALL)) {
+				getKeyManager().removeAllKeys(username);
+				log.info("removed all SSH public keys from {}", username);
+			} else {
+				for (String key : keys) {
+					getKeyManager().removeKey(username, key);
+					log.info("removed SSH public key from {}", username);
+				}
+			}
+		}
+	}
+
+	@CommandMetaData(name = "list", aliases = { "ls" }, description = "List your public keys")
+	public static class ListKeys extends SshCommand {
+
+		@Override
+		public void run() {
+			IPublicKeyManager keyManager = getContext().getGitblit().getPublicKeyManager();
+			List<PublicKey> keys = keyManager.getKeys(getContext().getClient().getUsername());
+
+			for (PublicKey key : keys) {
+				// two-steps - perhaps this could be improved
+				Buffer buf = new Buffer();
+
+				// 1: identify the algorithm
+				buf.putRawPublicKey(key);
+				String alg = buf.getString();
+
+				// 2: encode the key
+				buf.clear();
+				buf.putPublicKey(key);
+				String b64 = Base64.encodeBase64String(buf.getBytes());
+
+				stdout.println(alg + " " + b64);
+			}
+		}
+	}
+}
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/ListDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/ListDispatcher.java
new file mode 100644
index 0000000..0c372df
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/ListDispatcher.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh.gitblit;
+
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+import org.kohsuke.args4j.Option;
+import org.parboiled.common.StringUtils;
+
+import com.gitblit.manager.IGitblit;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.commands.CommandMetaData;
+import com.gitblit.transport.ssh.commands.DispatchCommand;
+import com.gitblit.transport.ssh.commands.SshCommand;
+
+/**
+ * The dispatcher and it's commands for Gitblit object listing.
+ *
+ * @author James Moger
+ *
+ */
+@CommandMetaData(name = "list", aliases = { "ls" }, description = "Gitblit object list commands")
+public class ListDispatcher extends DispatchCommand {
+
+	@Override
+	protected void registerCommands(UserModel user) {
+		registerCommand(user, ListRepositories.class);
+		registerCommand(user, ListProjects.class);
+		registerCommand(user, ListUsers.class);
+		registerCommand(user, ListKeys.class);
+	}
+
+	/* List SSH public keys */
+	@CommandMetaData(name = "keys",  description = "List your public keys")
+	public static class ListKeys extends KeysDispatcher.ListKeys {
+	}
+
+	/* List repositories */
+	@CommandMetaData(name = "repositories", aliases = { "repos" }, description = "List repositories")
+	public static class ListRepositories extends SshCommand {
+
+		@Option(name = "--verbose", aliases = { "-v" }, usage = "verbose")
+		private boolean verbose;
+
+		@Override
+		public void run() {
+			IGitblit gitblit = getContext().getGitblit();
+			UserModel user = getContext().getClient().getUser();
+			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+			List<RepositoryModel> repositories = gitblit.getRepositoryModels(user);
+			int nameLen = 0;
+			int descLen = 0;
+			for (RepositoryModel repo : repositories) {
+				int len = repo.name.length();
+				if (len > nameLen) {
+					nameLen = len;
+				}
+				if (!StringUtils.isEmpty(repo.description)) {
+					len = repo.description.length();
+					if (len > descLen) {
+						descLen = len;
+					}
+				}
+			}
+
+			String pattern;
+			if (verbose) {
+				pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%s", nameLen, descLen);
+			} else {
+				pattern = "%s";
+			}
+
+			for (RepositoryModel repo : repositories) {
+				stdout.println(String.format(pattern,
+						repo.name,
+						repo.description == null ? "" : repo.description,
+						df.format(repo.lastChange)));
+			}
+		}
+	}
+
+	/* List projects */
+	@CommandMetaData(name = "projects", description = "List projects")
+	public static class ListProjects extends SshCommand {
+
+		@Option(name = "--verbose", aliases = { "-v" }, usage = "verbose")
+		private boolean verbose;
+
+		@Override
+		public void run() {
+			IGitblit gitblit = getContext().getGitblit();
+			UserModel user = getContext().getClient().getUser();
+			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+			List<ProjectModel> projects = gitblit.getProjectModels(user, false);
+			int nameLen = 0;
+			int descLen = 0;
+			for (ProjectModel project : projects) {
+				int len = project.name.length();
+				if (len > nameLen) {
+					nameLen = len;
+				}
+				if (!StringUtils.isEmpty(project.description)) {
+					len = project.description.length();
+					if (len > descLen) {
+						descLen = len;
+					}
+				}
+			}
+
+			String pattern;
+			if (verbose) {
+				pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%s", nameLen, descLen);
+			} else {
+				pattern = "%s";
+			}
+
+			for (ProjectModel project : projects) {
+				stdout.println(String.format(pattern,
+						project.name,
+						project.description == null ? "" : project.description,
+						df.format(project.lastChange)));
+			}
+		}
+	}
+
+	/* List users */
+	@CommandMetaData(name = "users", description = "List users", admin = true)
+	public static class ListUsers extends SshCommand {
+
+		@Option(name = "--verbose", aliases = { "-v" }, usage = "verbose")
+		private boolean verbose;
+
+		@Override
+		public void run() {
+			IGitblit gitblit = getContext().getGitblit();
+			List<UserModel> users = gitblit.getAllUsers();
+			int displaynameLen = 0;
+			int usernameLen = 0;
+			for (UserModel user : users) {
+				int len = user.getDisplayName().length();
+				if (len > displaynameLen) {
+					displaynameLen = len;
+				}
+				if (!StringUtils.isEmpty(user.username)) {
+					len = user.username.length();
+					if (len > usernameLen) {
+						usernameLen = len;
+					}
+				}
+			}
+
+			String pattern;
+			if (verbose) {
+				pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%-10s\t%s", displaynameLen, usernameLen);
+			} else {
+				pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s", displaynameLen, usernameLen);
+			}
+
+			for (UserModel user : users) {
+				if (user.disabled) {
+					continue;
+				}
+				stdout.println(String.format(pattern,
+						user.getDisplayName(),
+						(user.canAdmin() ? "*":" ") + user.username,
+						user.accountType,
+						user.emailAddress == null ? "" : user.emailAddress));
+			}
+		}
+	}
+}
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/LsCommand.java b/src/main/java/com/gitblit/transport/ssh/gitblit/LsCommand.java
deleted file mode 100644
index 1f0d06c..0000000
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/LsCommand.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2014 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.gitblit.transport.ssh.gitblit;
-
-import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
-import java.util.List;
-
-import org.kohsuke.args4j.Option;
-import org.parboiled.common.StringUtils;
-
-import com.gitblit.manager.IGitblit;
-import com.gitblit.models.ProjectModel;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.UserModel;
-import com.gitblit.transport.ssh.commands.CommandMetaData;
-import com.gitblit.transport.ssh.commands.SshCommand;
-
-@CommandMetaData(name = "ls", description = "List repositories or projects")
-public class LsCommand extends SshCommand {
-
-	@Option(name = "--projects", aliases = { "-p" }, usage = "list projects")
-	private boolean projects;
-
-	@Option(name = "--verbose", aliases = { "-v" }, usage = "verbose")
-	private boolean verbose;
-
-	@Override
-	public void run() {
-		if (projects) {
-			listProjects();
-		} else {
-			listRepositories();
-		}
-	}
-
-	protected void listProjects() {
-		IGitblit gitblit = getContext().getGitblit();
-		UserModel user = getContext().getClient().getUser();
-		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
-
-		List<ProjectModel> projects = gitblit.getProjectModels(user, false);
-		int nameLen = 0;
-		int descLen = 0;
-		for (ProjectModel project : projects) {
-			int len = project.name.length();
-			if (len > nameLen) {
-				nameLen = len;
-			}
-			if (!StringUtils.isEmpty(project.description)) {
-				len = project.description.length();
-				if (len > descLen) {
-					descLen = len;
-				}
-			}
-		}
-
-		String pattern;
-		if (verbose) {
-			pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%s", nameLen, descLen);
-		} else {
-			pattern = "%s";
-		}
-
-		for (ProjectModel project : projects) {
-			stdout.println(String.format(pattern,
-					project.name,
-					project.description == null ? "" : project.description,
-					df.format(project.lastChange)));
-		}
-	}
-
-	protected void listRepositories() {
-		IGitblit gitblit = getContext().getGitblit();
-		UserModel user = getContext().getClient().getUser();
-		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
-
-		List<RepositoryModel> repositories = gitblit.getRepositoryModels(user);
-		int nameLen = 0;
-		int descLen = 0;
-		for (RepositoryModel repo : repositories) {
-			int len = repo.name.length();
-			if (len > nameLen) {
-				nameLen = len;
-			}
-			if (!StringUtils.isEmpty(repo.description)) {
-				len = repo.description.length();
-				if (len > descLen) {
-					descLen = len;
-				}
-			}
-		}
-
-		String pattern;
-		if (verbose) {
-			pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%s", nameLen, descLen);
-		} else {
-			pattern = "%s";
-		}
-
-		for (RepositoryModel repo : repositories) {
-			stdout.println(String.format(pattern,
-					repo.name,
-					repo.description == null ? "" : repo.description,
-					df.format(repo.lastChange)));
-		}
-	}
-}
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/LsUsersCommand.java b/src/main/java/com/gitblit/transport/ssh/gitblit/LsUsersCommand.java
deleted file mode 100644
index e59e8b9..0000000
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/LsUsersCommand.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2014 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.gitblit.transport.ssh.gitblit;
-
-import java.text.MessageFormat;
-import java.util.List;
-
-import org.kohsuke.args4j.Option;
-import org.parboiled.common.StringUtils;
-
-import com.gitblit.manager.IGitblit;
-import com.gitblit.models.UserModel;
-import com.gitblit.transport.ssh.commands.CommandMetaData;
-import com.gitblit.transport.ssh.commands.SshCommand;
-
-@CommandMetaData(name = "ls-users", description = "List users", admin = true)
-public class LsUsersCommand extends SshCommand {
-
-	@Option(name = "--verbose", aliases = { "-v" }, usage = "verbose")
-	private boolean verbose;
-
-	@Override
-	public void run() {
-		IGitblit gitblit = getContext().getGitblit();
-		List<UserModel> users = gitblit.getAllUsers();
-		int displaynameLen = 0;
-		int usernameLen = 0;
-		for (UserModel user : users) {
-			int len = user.getDisplayName().length();
-			if (len > displaynameLen) {
-				displaynameLen = len;
-			}
-			if (!StringUtils.isEmpty(user.username)) {
-				len = user.username.length();
-				if (len > usernameLen) {
-					usernameLen = len;
-				}
-			}
-		}
-
-		String pattern;
-		if (verbose) {
-			pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s\t%-10s\t%s", displaynameLen, usernameLen);
-		} else {
-			pattern = MessageFormat.format("%-{0,number,0}s\t%-{1,number,0}s", displaynameLen, usernameLen);
-		}
-
-		for (UserModel user : users) {
-			if (user.disabled) {
-				continue;
-			}
-			stdout.println(String.format(pattern,
-					user.getDisplayName(),
-					(user.canAdmin() ? "*":" ") + user.username,
-					user.accountType,
-					user.emailAddress == null ? "" : user.emailAddress));
-		}
-	}
-}
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/RemoveKeyCommand.java b/src/main/java/com/gitblit/transport/ssh/gitblit/RemoveKeyCommand.java
deleted file mode 100644
index 915dbc0..0000000
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/RemoveKeyCommand.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2014 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.gitblit.transport.ssh.gitblit;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.kohsuke.args4j.Argument;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.transport.ssh.commands.CommandMetaData;
-
-
-/**
- * Remove an SSH public key from the current user's authorized key list.
- *
- * @author James Moger
- *
- */
-@CommandMetaData(name = "rm-key", description = "Remove an SSH public key from your account")
-public class RemoveKeyCommand extends BaseKeyCommand {
-
-	protected final Logger log = LoggerFactory.getLogger(getClass());
-
-	private static final String ALL = "ALL";
-
-	@Argument(metaVar = "<stdin>|<KEY>|ALL", usage = "the key to remove")
-	private List<String> removeKeys = new ArrayList<String>();
-
-	@Override
-	public void run() throws IOException, UnloggedFailure {
-		String username = getContext().getClient().getUsername();
-		List<String> keys = readKeys(removeKeys);
-		if (keys.contains(ALL)) {
-			getKeyManager().removeAllKeys(username);
-			log.info("removed all SSH public keys from {}", username);
-		} else {
-			for (String key : keys) {
-				getKeyManager().removeKey(username, key);
-				log.info("removed SSH public key from {}", username);
-			}
-		}
-	}
-}
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/TicketsDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/TicketsDispatcher.java
new file mode 100644
index 0000000..ca75a6c
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/TicketsDispatcher.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh.gitblit;
+
+import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.commands.CommandMetaData;
+import com.gitblit.transport.ssh.commands.DispatchCommand;
+
+@CommandMetaData(name = "tickets", description = "Ticket commands", hidden = true)
+public class TicketsDispatcher extends DispatchCommand {
+
+	@Override
+	protected void registerCommands(UserModel user) {
+		registerCommand(user, ReviewCommand.class);
+	}
+}
diff --git a/src/site/setup_transport_ssh.mkd b/src/site/setup_transport_ssh.mkd
index 38742ec..c3d97a3 100644
--- a/src/site/setup_transport_ssh.mkd
+++ b/src/site/setup_transport_ssh.mkd
@@ -23,8 +23,8 @@
 
 Then you can upload your *public* key right from the command-line.
 
-    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit add-key
-    cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit add-key
+    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
+    cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
 
 **NOTE:** It is important to note that *ssh-keygen* generates a public/private keypair (e.g. id_rsa and id_rsa.pub).  You want to upload the *public* key, which is denoted by the *.pub* file extension.
 
@@ -52,36 +52,32 @@
 
 ### SSH Commands
 
-#### git
-
-You will likely never directly interact with the git command, but it is used by your git client to clone, fetch, and push commits to/from your Gitblit server.
-
-##### git-receive-pack
-
-This is the command for processing pushes sent from clients.
-
-##### git-upload-pack
-
-This is the command for sending refs and commits to clients.
+Gitblit supports SSH command plugins and provides several commands out-of-the-box.
 
 #### gitblit
 
-The *gitblit* command has many subcommands for interacting gitblit.
+The *gitblit* command has many subcommands for interacting with Gitblit.
 
-##### add-key
+##### keys add
 
 Add an SSH public key to your account.  This command accepts a public key piped to stdin.
 
-    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit add-key
+    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
 
-##### rm-key
+##### keys remove
 
 Remove an SSH public key from your account.  This command accepts a public key piped to stdin.
 
-    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit rm-key
+    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys remove
 
 You can also remove all your public keys from your account.
 
-    ssh -l <username> -p 29418 <hostname> gitblit rm-key ALL
+    ssh -l <username> -p 29418 <hostname> gitblit keys remove ALL
+
+##### keys list
+
+Show the SSH keys you have added to your account.
+
+    ssh -l <username> -p 29418 <hostname> gitblit keys list
 
 

--
Gitblit v1.9.1