001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2023 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.q2.ssh;
020
021import org.apache.sshd.common.Factory;
022import org.apache.sshd.common.SshConstants;
023import org.apache.sshd.server.*;
024import org.apache.sshd.server.channel.ChannelSession;
025import org.apache.sshd.server.command.Command;
026import org.apache.sshd.server.command.CommandFactory;
027import org.apache.sshd.server.session.ServerSession;
028import org.apache.sshd.server.session.ServerSessionAware;
029import org.apache.sshd.server.shell.ShellFactory;
030import org.jpos.q2.CLI;
031import org.jpos.q2.Q2;
032import org.jpos.util.Log;
033
034import java.io.IOException;
035import java.io.InputStream;
036import java.io.OutputStream;
037
038public class CliShellFactory implements Factory<Command>, CommandFactory, ShellFactory {
039    Q2 q2;
040    String[] prefixes;
041
042    public CliShellFactory(Q2 q2, String[] prefixes) {
043        this.q2 = q2;
044        this.prefixes = prefixes;
045    }
046
047    public Command create() {
048        return new JPosCLIShell(null);
049    }
050
051    @Override
052    public Command createCommand(ChannelSession channel, String command) {
053        return new JPosCLIShell(command);
054    }
055
056    @Override
057    public Command createShell(ChannelSession channel) { return new JPosCLIShell(null); }
058
059    public class JPosCLIShell implements Command, ServerSessionAware {
060        InputStream in;
061        OutputStream out;
062        OutputStream err;
063        SshCLI cli = null;
064        ServerSession serverSession;
065        String args;
066
067        public void setInputStream(InputStream in) {
068            this.in = in;
069        }
070
071        public void setOutputStream(OutputStream out) {
072            this.out = out;
073        }
074
075        public void setErrorStream(OutputStream err) {
076            this.err = err;
077        }
078
079        public void setExitCallback(ExitCallback exitCallback) {
080        }
081
082        public JPosCLIShell(String args) {
083            this.args = args;
084        }
085
086        public void setSession(ServerSession serverSession) {
087            this.serverSession = serverSession;
088        }
089
090        public void start(ChannelSession channel, Environment env) throws IOException {
091            cli = new SshCLI(q2, args != null ? null : in, out, args, args == null);
092            try {
093                cli.setServerSession(serverSession);
094                cli.start();
095            } catch (Exception e) {
096                throw new IOException("Could not start", e);
097            }
098        }
099
100        public void destroy(ChannelSession channel) {
101            if (cli != null) {
102                cli.stop();
103            }
104        }
105    }
106
107    public class SshCLI extends CLI {
108        ServerSession serverSession = null;
109
110        public SshCLI(Q2 q2, InputStream in, OutputStream out, String line, boolean keepRunning) throws IOException {
111            super(q2, in, out, line, keepRunning, true);
112        }
113
114        protected boolean running() {
115            return !ctx.isStopped();
116        }
117
118        protected void markStopped() {
119            ctx.setStopped(true);
120        }
121
122        protected void markStarted() {
123            ctx.setStopped(false);
124        }
125
126        protected String[] getCompletionPrefixes() {
127            return prefixes;
128        }
129
130        protected void handleExit() {
131            if (serverSession != null) {
132                try {
133                    serverSession.disconnect(SshConstants.SSH2_DISCONNECT_BY_APPLICATION, "");
134                } catch (IOException e) {
135                    Log.getLog(Q2.LOGGER_NAME, "sshd").error(e);
136                }
137            }
138        }
139
140        public void setServerSession(ServerSession serverSession) {
141            this.serverSession = serverSession;
142        }
143    }
144}