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 }