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