001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    package org.crsh.ssh.term;
020    
021    import jline.Terminal;
022    import jline.console.ConsoleReader;
023    import org.apache.sshd.server.Environment;
024    import org.crsh.console.jline.JLineProcessor;
025    import org.crsh.shell.Shell;
026    import org.crsh.util.Utils;
027    
028    import java.io.IOException;
029    import java.io.InputStream;
030    import java.io.OutputStream;
031    import java.io.PrintStream;
032    import java.security.Principal;
033    import java.util.concurrent.atomic.AtomicBoolean;
034    
035    public class CRaSHCommand extends AbstractCommand implements Runnable, Terminal {
036    
037      /** . */
038      private final CRaSHCommandFactory factory;
039    
040      /** . */
041      private Thread thread;
042    
043      public CRaSHCommand(CRaSHCommandFactory factory) {
044        this.factory = factory;
045      }
046    
047      /** . */
048      private SSHContext context;
049    
050      /** . */
051      private JLineProcessor console;
052    
053      public void start(Environment env) throws IOException {
054    
055        //
056        context = new SSHContext(env);
057    
058        //
059        thread = new Thread(this, "CRaSH");
060        thread.start();
061      }
062    
063      public SSHContext getContext() {
064        return context;
065      }
066    
067      public void destroy() {
068        Utils.close(console);
069        thread.interrupt();
070      }
071    
072      public void run() {
073        final AtomicBoolean exited = new AtomicBoolean(false);
074        try {
075          final String userName = session.getAttribute(SSHLifeCycle.USERNAME);
076          Principal user = new Principal() {
077            public String getName() {
078              return userName;
079            }
080          };
081          Shell shell = factory.shellFactory.create(user);
082          ConsoleReader reader = new ConsoleReader(in, out, this) {
083            @Override
084            public void shutdown() {
085              exited.set(true);
086              callback.onExit(0);
087              super.shutdown();
088            }
089          };
090          JLineProcessor processor = new JLineProcessor(shell, reader, new PrintStream(out), "\r\n");
091          processor.run();
092        } catch (java.io.InterruptedIOException e) {
093          // Expected behavior because of the onExit callback in the shutdown above
094        } catch (Exception e) {
095          e.printStackTrace();
096        } finally {
097          // Make sure we call it
098          if (!exited.get()) {
099            callback.onExit(0);
100          }
101        }
102      }
103    
104      //
105    
106      @Override
107      public void init() throws Exception {
108      }
109    
110      @Override
111      public void restore() throws Exception {
112      }
113    
114      @Override
115      public void reset() throws Exception {
116      }
117    
118      @Override
119      public boolean isSupported() {
120        return true;
121      }
122    
123      @Override
124      public int getWidth() {
125        return context.getWidth();
126      }
127    
128      @Override
129      public int getHeight() {
130        return context.getHeight();
131      }
132    
133      @Override
134      public boolean isAnsiSupported() {
135        return true;
136      }
137    
138      @Override
139      public OutputStream wrapOutIfNeeded(OutputStream out) {
140        return out;
141      }
142    
143      @Override
144      public InputStream wrapInIfNeeded(InputStream in) throws IOException {
145        return in;
146      }
147    
148      @Override
149      public boolean hasWeirdWrap() {
150        return false;
151      }
152    
153      @Override
154      public boolean isEchoEnabled() {
155        return false;
156      }
157    
158      @Override
159      public void setEchoEnabled(boolean enabled) {
160      }
161    }
162    
163