/* * Copyright (C) 2009 The Android Open Source Project * 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; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; import org.apache.sshd.common.Factory; import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SessionAware; import org.apache.sshd.server.session.ServerSession; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.util.SystemReader; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.models.UserModel; import com.gitblit.transport.ssh.commands.DispatchCommand; import com.gitblit.transport.ssh.commands.SshCommandFactory; import com.gitblit.utils.StringUtils; /** * Class that displays a welcome message for any shell requests. * */ public class WelcomeShell implements Factory { private final IStoredSettings settings; public WelcomeShell(IStoredSettings settings) { this.settings = settings; } @Override public Command create() { return new SendMessage(settings); } private static class SendMessage implements Command, SessionAware { private final IStoredSettings settings; private ServerSession session; private InputStream in; private OutputStream out; private OutputStream err; private ExitCallback exit; SendMessage(IStoredSettings settings) { this.settings = settings; } @Override public void setInputStream(final InputStream in) { this.in = in; } @Override public void setOutputStream(final OutputStream out) { this.out = out; } @Override public void setErrorStream(final OutputStream err) { this.err = err; } @Override public void setExitCallback(final ExitCallback callback) { this.exit = callback; } @Override public void setSession(final ServerSession session) { this.session = session; } @Override public void start(final Environment env) throws IOException { err.write(Constants.encode(getMessage())); err.flush(); in.close(); out.close(); err.close(); exit.onExit(127); } @Override public void destroy() { this.session = null; } String getMessage() { SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY); UserModel user = client.getUser(); String hostname = getHostname(); int port = settings.getInteger(Keys.git.sshPort, 0); final String b1 = StringUtils.rightPad("", 72, '═'); final String b2 = StringUtils.rightPad("", 72, '─'); final String nl = "\r\n"; StringBuilder msg = new StringBuilder(); msg.append(nl); msg.append(b1); msg.append(nl); msg.append(" "); msg.append(com.gitblit.Constants.getGitBlitVersion()); msg.append(nl); msg.append(b1); msg.append(nl); msg.append(nl); msg.append(" Hi "); msg.append(user.getDisplayName()); msg.append(", you have successfully connected over SSH."); msg.append(nl); msg.append(" Interactive shells are not available."); msg.append(nl); msg.append(nl); msg.append(" client: "); msg.append(session.getClientVersion()); msg.append(nl); msg.append(nl); msg.append(b2); msg.append(nl); msg.append(nl); msg.append(" You may clone a repository with the following Git syntax:"); msg.append(nl); msg.append(nl); msg.append(" git clone "); msg.append(formatUrl(hostname, port, user.username)); msg.append(nl); msg.append(nl); msg.append(b2); msg.append(nl); msg.append(nl); if (client.getKey() == null) { // user has authenticated with a password // display add public key instructions msg.append(" You may upload an SSH public key with the following syntax:"); msg.append(nl); msg.append(nl); msg.append(String.format(" cat ~/.ssh/id_rsa.pub | ssh -l %s -p %d %s keys add", user.username, port, hostname)); msg.append(nl); msg.append(nl); msg.append(b2); msg.append(nl); msg.append(nl); } // display the core commands SshCommandFactory cmdFactory = (SshCommandFactory) session.getFactoryManager().getCommandFactory(); DispatchCommand root = cmdFactory.createRootDispatcher(client, ""); String usage = root.usage().replace("\n", nl); msg.append(usage); return msg.toString(); } private String getHostname() { String host = null; String url = settings.getString(Keys.web.canonicalUrl, "https://localhost:8443"); if (url != null) { try { host = new URL(url).getHost(); } catch (MalformedURLException e) { } } if (StringUtils.isEmpty(host)) { host = SystemReader.getInstance().getHostname(); } return host; } private String formatUrl(String hostname, int port, String username) { int displayPort = settings.getInteger(Keys.git.sshAdvertisedPort, port); String displayHostname = settings.getString(Keys.git.sshAdvertisedHost, ""); if(displayHostname.isEmpty()) { displayHostname = hostname; } if (displayPort == 22) { // standard port return MessageFormat.format("{0}@{1}/REPOSITORY.git", username, displayHostname); } else { // non-standard port return MessageFormat.format("ssh://{0}@{1}:{2,number,0}/REPOSITORY.git", username, displayHostname, displayPort); } } } }