1 5 package org.h2.server.ftp; 6 7 import java.io.BufferedReader ; 8 import java.io.IOException ; 9 import java.io.InputStreamReader ; 10 import java.io.OutputStreamWriter ; 11 import java.io.PrintWriter ; 12 import java.net.ServerSocket ; 13 import java.net.Socket ; 14 15 import org.h2.engine.Constants; 16 import org.h2.util.StringUtils; 17 18 public class FtpControl extends Thread { 19 20 private static final String SERVER_NAME = "Small FTP Server"; 21 22 private FtpServer server; 23 private Socket control; 24 private FtpData data; 25 private PrintWriter output; 26 private String userName; 27 private boolean connected, readonly; 28 private String currentDir = "/"; 29 private String serverIpAddress; 30 private boolean stop; 31 private String renameFrom; 32 private boolean replied; 33 private long restart; 34 35 public FtpControl(Socket control, FtpServer server, boolean stop) { 36 this.server = server; 37 this.control = control; 38 this.stop = stop; 39 } 40 41 public void run() { 42 try { 43 output = new PrintWriter (new OutputStreamWriter (control.getOutputStream(), Constants.UTF8)); 44 if(stop) { 45 reply(421, "Too many users"); 46 } else { 47 reply(220, SERVER_NAME); 48 serverIpAddress = control.getLocalAddress().getHostAddress().replace('.', ','); 50 BufferedReader input = new BufferedReader (new InputStreamReader (control.getInputStream())); 51 while(!stop) { 52 String command = null; 53 try { 54 command = input.readLine(); 55 } catch(IOException e) { 56 } 58 if(command == null) { 59 break; 60 } 61 process(command); 62 } 63 if(data != null) { 64 data.close(); 65 } 66 } 67 } catch(Throwable t) { 68 server.logError(t); 69 } 70 server.closeConnection(); 71 } 72 73 private void process(String command) throws IOException { 74 int idx = command.indexOf(' '); 75 String param = ""; 76 if(idx >= 0) { 77 param = command.substring(idx).trim(); 78 command = command.substring(0, idx); 79 } 80 command = StringUtils.toUpperEnglish(command); 81 if(command.length() == 0) { 82 reply(506, "No command"); 83 return; 84 } 85 server.log(">" + command); 86 replied = false; 87 if(connected) { 88 processConnected(command, param); 89 } 90 if(!replied) { 91 if("USER".equals(command)) { 92 userName = param; 93 reply(331, "Need password"); 94 } else if("QUIT".equals(command)) { 95 reply(221, "Bye"); 96 stop = true; 97 } else if("PASS".equals(command)) { 98 if(userName == null) { 99 reply(332, "Need username"); 100 } else if(server.checkUserPassword(userName, param)) { 101 reply(230, "Ok"); 102 readonly = false; 103 connected = true; 104 } else if(server.checkUserPasswordReadOnly(userName, param)) { 105 reply(230, "Ok, readonly"); 106 readonly = true; 107 connected = true; 108 } else { 109 reply(431, "Wrong user/password"); 110 } 111 } else if("REIN".equals(command)) { 112 userName = null; 113 connected = false; 114 currentDir = "/"; 115 reply(200, "Ok"); 116 } else if("HELP".equals(command)) { 117 reply(214, SERVER_NAME); 118 } 119 } 120 if(!replied) { 121 reply(506, "Invalid command"); 122 } 123 } 124 125 private void processConnected(String command, String param) throws IOException { 126 switch(command.charAt(0)) { 127 case 'C': 128 if("CWD".equals(command)) { 129 param = getFileName(param); 130 FileObject file = server.getFile(param); 131 if(file.exists() && file.isDirectory()) { 132 if(!param.endsWith("/")) { 133 param += "/"; 134 } 135 currentDir = param; 136 reply(250, "Ok"); 137 } else { 138 reply(550, "Failed"); 139 } 140 } else if("CDUP".equals(command)) { 141 if(currentDir.length()>1) { 142 int idx = currentDir.lastIndexOf("/", currentDir.length()-2); 143 currentDir = currentDir.substring(0, idx+1); 144 reply(250, "Ok"); 145 } else { 146 reply(550, "Failed"); 147 } 148 } 149 break; 150 case 'D': 151 if("DELE".equals(command)) { 152 FileObject file = server.getFile(getFileName(param)); 153 if(!readonly && file.exists() && file.isFile() && file.delete()) { 154 if(server.getAllowTask() && param.endsWith(FtpServer.TASK_SUFFIX)) { 155 server.stopTask(file); 156 } 157 reply(250, "Ok"); 158 } else { 159 reply(500, "Delete failed"); 160 } 161 } 162 break; 163 case 'L': 164 if("LIST".equals(command)) { 165 processList(param, true); 166 } 167 break; 168 case 'M': 169 if("MKD".equals(command)) { 170 param = getFileName(param); 171 FileObject file = server.getFile(param); 172 if(!readonly && file.mkdirs()) { 173 reply(257, "\"" + param + "\" directory"); } else { 175 reply(500, "Failed"); 176 } 177 } else if("MODE".equals(command)) { 178 if("S".equals(StringUtils.toUpperEnglish(param))) { 179 reply(200, "Ok"); 180 } else { 181 reply(504, "Invalid"); 182 } 183 } else if("MDTM".equals(command)) { 184 FileObject file = server.getFile(getFileName(param)); 185 if(file.exists() && file.isFile()) { 186 reply(213, server.formatLastModified(file)); 187 } else { 188 reply(550, "Failed"); 189 } 190 } 191 break; 192 case 'N': 193 if("NLST".equals(command)) { 194 processList(param, false); 195 } else if("NOOP".equals(command)) { 196 reply(200, "Ok"); 197 } 198 break; 199 case 'P': 200 if("PWD".equals(command)) { 201 reply(257, "\"" + currentDir + "\" directory"); } else if("PASV".equals(command)) { 203 ServerSocket dataSocket = server.createDataSocket(); 204 data = new FtpData(server, control.getInetAddress(), dataSocket); 205 data.start(); 206 int port = dataSocket.getLocalPort(); 207 reply(227, "Passive Mode (" + serverIpAddress + "," + (port >> 8) + "," + (port & 255) + ")"); 208 } 210 break; 211 case 'R': 212 if("RNFR".equals(command)) { 213 param = getFileName(param); 214 FileObject file = server.getFile(param); 215 if(file.exists()) { 216 renameFrom = param; 217 reply(350, "Ok"); 218 } else { 219 reply(450, "Not found"); 220 } 221 } else if("RNTO".equals(command)) { 222 if (renameFrom == null) { 223 reply(503, "RNFR required"); 224 } else { 225 FileObject fileOld = server.getFile(renameFrom); 226 FileObject fileNew = server.getFile(getFileName(param)); 227 if(!readonly && fileOld.renameTo(fileNew)) { 228 reply(250, "Ok"); 229 } else { 230 reply(550, "Failed"); 231 } 232 } 233 } else if("RETR".equals(command)) { 234 FileObject file = server.getFile(getFileName(param)); 235 if(file.exists() && file.isFile()) { 236 reply(150, "Starting transfer"); 237 try { 238 data.send(file, restart); 239 reply(226, "Ok"); 240 } catch(IOException e) { 241 reply(426, "Failed"); 242 } 243 restart = 0; 244 } else { 245 processList(param, true); } 248 } else if("RMD".equals(command)) { 249 FileObject file = server.getFile(getFileName(param)); 250 if(!readonly && file.exists() && file.isDirectory() && file.delete()) { 251 reply(250, "Ok"); 252 } else { 253 reply(500, "Failed"); 254 } 255 } else if("REST".equals(command)) { 256 try { 257 restart = Integer.parseInt(param); 258 reply(350, "Ok"); 259 } catch(NumberFormatException e) { 260 reply(500, "Invalid"); 261 } 262 } 263 break; 264 case 'S': 265 if("SYST".equals(command)) { 266 reply(215, "UNIX Type: L8"); 267 } else if("SITE".equals(command)) { 268 reply(500, "Not understood"); 269 } else if("SIZE".equals(command)) { 270 FileObject file = server.getFile(getFileName(param)); 271 if(file.exists() && file.isFile()) { 272 reply(250, String.valueOf(file.length())); 273 } else { 274 reply(500, "Failed"); 275 } 276 } else if("STOR".equals(command)) { 277 FileObject file = server.getFile(getFileName(param)); 278 if(!readonly && !file.exists() || file.isFile()) { 279 reply(150, "Starting transfer"); 280 try { 281 data.receive(file); 282 if(server.getAllowTask() && param.endsWith(FtpServer.TASK_SUFFIX)) { 283 server.startTask(file); 284 } 285 reply(226, "Ok"); 286 } catch(IOException e) { 287 reply(426, "Failed"); 288 } 289 } else { 290 reply(550, "Failed"); 291 } 292 } else if("STRU".equals(command)) { 293 if("F".equals(StringUtils.toUpperEnglish(param))) { 294 reply(200, "Ok"); 295 } else { 296 reply(504, "Invalid"); 297 } 298 } 299 break; 300 case 'T': 301 if("TYPE".equals(command)) { 302 param = StringUtils.toUpperEnglish(param); 303 if("A".equals(param) || "A N".equals(param)) { 304 reply(200, "Ok"); 305 } else if("I".equals(param) || "L 8".equals(param)) { 306 reply(200, "Ok"); 307 } else { 308 reply(500, "Invalid"); 309 } 310 } 311 break; 312 } 313 } 314 315 private String getFileName(String file) { 316 return file.startsWith("/") ? file : currentDir + file; 317 } 318 319 private void processList(String param, boolean directories) throws IOException { 320 FileObject directory = server.getFile(getFileName(param)); 321 if(!directory.exists()) { 322 reply(450, "Directory does not exist"); 323 return; 324 } else if(!directory.isDirectory()) { 325 reply(450, "Not a directory"); 326 return; 327 } 328 String list = server.getDirectoryListing(directory, directories); 329 reply(150, "Starting transfer"); 330 server.log(list); 331 data.send(list.getBytes()); 333 reply(226, "Done"); 334 } 335 336 private void reply(int code, String message) throws IOException { 337 server.log(code + " " + message); 338 output.print(code + " " + message + "\r\n"); 339 output.flush(); 340 replied = true; 341 } 342 343 } 344 | Popular Tags |