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.jcr;
020    
021    import org.apache.sshd.server.Environment;
022    import org.crsh.ssh.term.AbstractCommand;
023    import org.crsh.ssh.term.SSHLifeCycle;
024    
025    import javax.jcr.Credentials;
026    import javax.jcr.Repository;
027    import javax.jcr.Session;
028    import javax.jcr.SimpleCredentials;
029    import java.io.ByteArrayOutputStream;
030    import java.io.IOException;
031    import java.io.InputStream;
032    import java.util.HashMap;
033    import java.util.Map;
034    import java.util.logging.Level;
035    import java.util.logging.Logger;
036    
037    public abstract class SCPCommand extends AbstractCommand implements Runnable {
038    
039      /** . */
040      protected final Logger log = Logger.getLogger(getClass().getName());
041    
042      /** . */
043      protected static final int OK = 0;
044    
045      /** . */
046      protected static final int ERROR = 2;
047    
048      /** . */
049      private Thread thread;
050    
051      /** . */
052      private Environment env;
053    
054      /** . */
055      private final String target;
056    
057      protected SCPCommand(String target) {
058        this.target = target;
059      }
060    
061      /**
062       * Read from the input stream an exact amount of bytes.
063       *
064       * @param length the expected data length to read
065       * @return an input stream for reading
066       * @throws IOException any io exception
067       */
068      final InputStream read(final int length) throws IOException {
069        log.log(Level.FINE, "Returning stream for length " + length);
070        return new InputStream() {
071    
072          /** How many we've read so far. */
073          int count = 0;
074    
075          @Override
076          public int read() throws IOException {
077            if (count < length) {
078              int value = in.read();
079              if (value == -1) {
080                throw new IOException("Abnormal end of stream reached");
081              }
082              count++;
083              return value;
084            } else {
085              return -1;
086            }
087          }
088        };
089      }
090    
091      final protected void ack() throws IOException {
092          out.write(0);
093          out.flush();
094      }
095    
096      final protected void readAck() throws IOException {
097        int c = in.read();
098        switch (c) {
099          case 0:
100            break;
101          case 1:
102            log.log(Level.FINE, "Received warning: " + readLine());
103            break;
104          case 2:
105            throw new IOException("Received nack: " + readLine());
106        }
107      }
108    
109      final protected String readLine() throws IOException {
110        ByteArrayOutputStream baos = new ByteArrayOutputStream();
111        while (true) {
112          int c = in.read();
113          if (c == '\n') {
114            return baos.toString();
115          }
116          else if (c == -1) {
117            throw new IOException("End of stream");
118          }
119          else {
120            baos.write(c);
121          }
122        }
123      }
124    
125      final public void start(Environment env) throws IOException {
126        this.env = env;
127    
128        //
129        thread = new Thread(this, "CRaSH");
130        thread.start();
131      }
132    
133      final public void destroy() {
134        thread.interrupt();
135      }
136    
137      final public void run() {
138        int exitStatus = OK;
139        String exitMsg = null;
140    
141        //
142        try {
143          execute();
144        }
145        catch (Exception e) {
146          log.log(Level.SEVERE, "Error during command execution", e);
147          exitMsg = e.getMessage();
148          exitStatus = ERROR;
149        }
150        finally {
151          // Say we are done
152          if (callback != null) {
153            callback.onExit(exitStatus, exitMsg);
154          }
155        }
156      }
157    
158      private void execute() throws Exception {
159        Map<String, String> properties = new HashMap<String, String>();
160    
161        // Need portal container name ?
162        int pos1 = target.indexOf(':');
163        String path;
164        String workspaceName;
165        if (pos1 != -1) {
166          int pos2 = target.indexOf(':', pos1 + 1);
167          if (pos2 != -1) {
168            // container:workspace_name:path
169            properties.put("container", target.substring(0, pos1));
170            workspaceName = target.substring(pos1 + 1, pos2);
171            path = target.substring(pos2 + 1);
172          }
173          else {
174            // workspace_name:path
175            workspaceName = target.substring(0, pos1);
176            path = target.substring(pos1 + 1);
177          }
178        }
179        else {
180          workspaceName = null;
181          path = target;
182        }
183    
184        //
185        Repository repository = JCRPlugin.findRepository(properties);
186    
187        // Obtain credentials from SSH
188        String userName = session.getAttribute(SSHLifeCycle.USERNAME);
189        String password = session.getAttribute(SSHLifeCycle.PASSWORD);
190        Credentials credentials = new SimpleCredentials(userName, password.toCharArray());
191    
192        //
193        Session session;
194        if (workspaceName != null) {
195          session = repository.login(credentials, workspaceName);
196        }
197        else {
198          session = repository.login(credentials);
199        }
200    
201        //
202        try {
203          execute(session, path);
204        }
205        finally {
206          session.logout();
207        }
208      }
209    
210      protected abstract void execute(Session session, String path) throws Exception;
211    
212    }