001package lucee.runtime.net.ftp; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.io.OutputStream; 006import java.net.InetAddress; 007import java.net.SocketException; 008import java.util.ArrayList; 009import java.util.Iterator; 010import java.util.List; 011import java.util.Locale; 012import java.util.Vector; 013 014import lucee.print; 015import lucee.commons.io.IOUtil; 016import lucee.commons.lang.ExceptionUtil; 017import lucee.commons.lang.StringUtil; 018import lucee.runtime.exp.DatabaseException; 019import lucee.runtime.exp.PageException; 020import lucee.runtime.op.Caster; 021import lucee.runtime.tag.FileTag; 022import lucee.runtime.tag.Ftp; 023import lucee.runtime.type.Collection; 024import lucee.runtime.type.Collection.Key; 025import lucee.runtime.type.KeyImpl; 026import lucee.runtime.type.QueryImpl; 027import lucee.runtime.type.dt.DateTimeImpl; 028import lucee.runtime.type.util.KeyConstants; 029 030import org.apache.commons.net.ftp.FTPFile; 031 032import com.jcraft.jsch.*; 033 034public class SFTPClientImpl extends AFTPClient { 035 036 037 private static final Key IS_DIRECTORY = new KeyImpl("isDirectory"); 038 private JSch jsch; 039 private int timeout=60000; 040 private Session session; 041 private ChannelSftp channelSftp; 042 private InetAddress host; 043 private int port; 044 private String username; 045 private String password; 046 private boolean stopOnError; 047 private String fingerprint; 048 private String replyString; 049 private int replyCode; 050 private boolean positiveCompletion; 051 052 SFTPClientImpl(){ 053 jsch = new JSch(); 054 } 055 056 @Override 057 public void init(InetAddress host, int port, String username, String password, String fingerprint, boolean stopOnError) throws SocketException, IOException { 058 if(port<1)port=22; 059 this.host=host; 060 this.port=port; 061 this.username=username; 062 this.password=password; 063 this.fingerprint=fingerprint==null?null:fingerprint.trim(); 064 this.stopOnError=stopOnError; 065 } 066 067 068 @Override 069 public void connect() throws SocketException, IOException { 070 try { 071 //jsch.setKnownHosts(""); 072 session = jsch.getSession(username,host.getHostAddress() , port); 073 /**/ 074 java.util.Properties config = new java.util.Properties(); 075 config.put("StrictHostKeyChecking", "no"); 076 session.setConfig(config); 077 078 079 080 //UserInfo ui=new UserInfoImpl(); 081 if(!StringUtil.isEmpty(password)) 082 session.setPassword(password); 083 084 //session.setUserInfo(ui); 085 if(timeout>0) session.setTimeout(timeout); 086 session.connect(); 087 088 Channel channel=session.openChannel("sftp"); 089 channel.connect(); 090 channelSftp = (ChannelSftp)channel; 091 092 093 // check fingerprint 094 095 if(!StringUtil.isEmpty(fingerprint)) { 096 if(!fingerprint.equalsIgnoreCase(fingerprint())) { 097 disconnect(); 098 throw new IOException("given fingerprint is not a match."); 099 } 100 } 101 handleSucess(); 102 } 103 catch(JSchException e){ 104 handleFail(e, stopOnError); 105 } 106 } 107 108 109 private String fingerprint() { 110 return session.getHostKey().getFingerPrint(jsch); 111 } 112 113 114 public static void main(String[] args) throws SocketException, IOException { 115 SFTPClientImpl client=new SFTPClientImpl(); 116 InetAddress host = InetAddress.getByName("virola.viviotech.net"); 117 String user = "root"; 118 String pass="Eunn:.TidkiWaj3"; 119 String fingerprint="aa:e8:43:3e:6b:44:91:a3:d3:da:07:34:90:2a:c8:5b"; 120 int port=10022; 121 122 client.init(host, port, user, pass,null,true); 123 client.connect(); 124 125 // fingerprint 126 127 // list files 128 FTPFile[] files = client.listFiles("/"); 129 130 131 client.disconnect(); 132 133 // UnknownHostKey: 205.210.189.210. RSA key fingerprint is aa:e8:43:3e:6b:44:91:a3:d3:da:07:34:90:2a:c8:5b 134 135 } 136 137 @Override 138 public boolean rename(String from, String to) throws IOException { 139 try { 140 channelSftp.rename(from, to); 141 handleSucess(); 142 return true; 143 } 144 catch (SftpException e) { 145 handleFail(e, stopOnError); 146 } 147 return false; 148 } 149 150 @Override 151 public boolean removeDirectory(String pathname) throws IOException { 152 try { 153 channelSftp.rmdir(pathname); 154 handleSucess(); 155 return true; 156 } 157 catch(SftpException ioe) { 158 handleFail(ioe, stopOnError); 159 } 160 return false; 161 } 162 163 164 @Override 165 public boolean makeDirectory(String pathname) throws IOException { 166 try { 167 channelSftp.mkdir(pathname); 168 handleSucess(); 169 return true; 170 } 171 catch(SftpException ioe) { 172 handleFail(ioe, stopOnError); 173 } 174 return false; 175 } 176 177 @Override 178 public boolean directoryExists(String pathname) throws IOException { 179 try { 180 String pwd=channelSftp.pwd(); 181 channelSftp.cd(pathname); 182 channelSftp.cd(pwd); // we change it back to what it was 183 handleSucess(); 184 return true; 185 } 186 catch(SftpException e) {/*do nothing*/} 187 return false; 188 } 189 190 191 @Override 192 public boolean changeWorkingDirectory(String pathname) throws IOException { 193 try { 194 channelSftp.cd(pathname); 195 handleSucess(); 196 return true; 197 } 198 catch(SftpException ioe) { 199 handleFail(ioe, stopOnError); 200 } 201 return false; 202 } 203 204 205 @Override 206 public String printWorkingDirectory() throws IOException { 207 try { 208 String pwd = channelSftp.pwd(); 209 handleSucess(); 210 return pwd; 211 } 212 catch(SftpException ioe) { 213 handleFail(ioe, stopOnError); 214 } 215 return null; 216 } 217 218 219 @Override 220 public boolean deleteFile(String pathname) throws IOException { 221 try { 222 channelSftp.rm(pathname); 223 handleSucess(); 224 return true; 225 } 226 catch(SftpException ioe) { 227 handleFail(ioe, stopOnError); 228 } 229 return false; 230 } 231 232 233 @Override 234 public boolean retrieveFile(String remote, OutputStream local) throws IOException { 235 boolean success=false; 236 try { 237 channelSftp.get(remote, local); 238 handleSucess(); 239 success = true; 240 } 241 catch(SftpException ioe) { 242 handleFail(ioe, stopOnError); 243 } 244 return success; 245 } 246 247 248 249 @Override 250 public boolean storeFile(String remote, InputStream local) throws IOException { 251 try { 252 this.channelSftp.put(local, remote); // TODO add progress monitor? 253 handleSucess(); 254 return true; 255 } 256 catch(SftpException ioe) { 257 handleFail(ioe, stopOnError); 258 } 259 return false; 260 } 261 262 @Override 263 public int getReplyCode() { 264 return replyCode; 265 } 266 267 @Override 268 public String getReplyString() { 269 return replyString; 270 } 271 272 @Override 273 public FTPFile[] listFiles(String pathname) throws IOException { 274 pathname=cleanPath(pathname); 275 List<FTPFile> files=new ArrayList<FTPFile>(); 276 try { 277 Vector list = channelSftp.ls(pathname); 278 Iterator<ChannelSftp.LsEntry> it = list.iterator(); 279 ChannelSftp.LsEntry entry; 280 SftpATTRS attrs; 281 FTPFile file; 282 String fileName; 283 284 while(it.hasNext()){ 285 entry=it.next(); 286 attrs = entry.getAttrs(); 287 fileName=entry.getFilename(); 288 if(fileName.equals(".") || fileName.equals("..")) continue; 289 290 file=new FTPFile(); 291 files.add(file); 292 // is dir 293 file.setType(attrs.isDir()?FTPFile.DIRECTORY_TYPE:FTPFile.FILE_TYPE); 294 file.setTimestamp(Caster.toCalendar(attrs.getMTime()*1000L, null, Locale.ENGLISH)); 295 file.setSize(attrs.isDir()?0:attrs.getSize()); 296 FTPConstant.setPermission(file, attrs.getPermissions()); 297 file.setName(fileName); 298 } 299 handleSucess(); 300 } 301 catch (SftpException e) { 302 handleFail(e, stopOnError); 303 } 304 305 return files.toArray(new FTPFile[files.size()]); 306 } 307 308 309 310 311 312 private String cleanPath(String pathname) { 313 if(!pathname.endsWith("/")) 314 pathname = pathname + "/"; 315 316 return pathname; 317 } 318 319 320 321 322 323 @Override 324 public boolean setFileType(int fileType) throws IOException { 325 // not used 326 return true; 327 } 328 329 330 @Override 331 public String getPrefix() { 332 return "sftp"; 333 } 334 335 @Override 336 public InetAddress getRemoteAddress() { 337 return host; 338 } 339 340 @Override 341 public boolean isConnected() { 342 return channelSftp.isConnected(); 343 } 344 345 @Override 346 public int quit() throws IOException { 347 // do nothing 348 return 0; 349 } 350 351 @Override 352 public void disconnect() throws IOException { 353 if(session!=null && session.isConnected()) { 354 session.disconnect(); 355 session=null; 356 } 357 } 358 359 @Override 360 public void setTimeout(int timeout) { 361 this.timeout=timeout; 362 if(session!=null) { 363 try { 364 session.setTimeout(timeout); 365 } 366 catch (JSchException e) {} 367 } 368 } 369 370 @Override 371 public int getDataConnectionMode() { 372 // not used 373 return -1; 374 } 375 376 @Override 377 public void enterLocalPassiveMode() { 378 // not used 379 } 380 381 @Override 382 public void enterLocalActiveMode() { 383 // not used 384 385 } 386 387 @Override 388 public boolean isPositiveCompletion() { 389 return positiveCompletion; 390 } 391 392 private void handleSucess() { 393 replyCode=0; 394 replyString="SSH_FX_OK successful completion of the operation"; 395 positiveCompletion=true; 396 } 397 398 399 400 private void handleFail(Exception e, boolean stopOnError) throws IOException { 401 String msg = e.getMessage()==null?"":e.getMessage(); 402 if (StringUtil.indexOfIgnoreCase(msg, "AUTHENTICATION") != -1 || StringUtil.indexOfIgnoreCase(msg, "PRIVATEKEY") != -1) { 403 replyCode = 51; 404 } 405 else replyCode = 82; 406 replyString=msg; 407 positiveCompletion=false; 408 409 if (stopOnError) { 410 disconnect(); 411 if(e instanceof IOException) throw (IOException)e; 412 throw new IOException(e); 413 } 414 } 415 416}