1 2 3 4 package net.nutch.protocol.ftp; 5 6 import javax.activation.MimetypesFileTypeMap ; 7 10 import org.apache.commons.net.ftp.FTP; 11 import org.apache.commons.net.ftp.FTPFile; 12 import org.apache.commons.net.ftp.FTPReply; 13 14 import org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory; 15 import org.apache.commons.net.ftp.parser.ParserInitializationException; 16 17 import net.nutch.protocol.Content; 18 19 import java.net.InetAddress ; 20 import java.net.URL ; 21 22 import java.lang.Exception ; 23 import java.lang.StackTraceElement ; 24 25 import java.util.List ; 26 import java.util.LinkedList ; 27 import java.util.Map ; 28 import java.util.TreeMap ; 29 import java.util.Properties ; 30 31 import java.util.logging.Level ; 32 33 import java.io.ByteArrayOutputStream ; 34 import java.io.IOException ; 36 37 50 public class FtpResponse { 51 private String orig; 52 private String base; 53 private byte[] content; 54 private int code; 55 private Properties headers = new Properties (); 56 57 private final Ftp ftp; 58 59 60 public int getCode() { return code; } 61 62 63 public String getHeader(String name) { 64 return (String )headers.get(name); 65 } 66 67 public byte[] getContent() { return content; } 68 69 public Content toContent() { 70 return new Content(orig, base, content, 71 getHeader("Content-Type"), 72 headers); 73 } 74 75 public FtpResponse(URL url, Ftp ftp) 76 throws FtpException, IOException { 77 this(url.toString(), url, ftp); 78 } 79 80 public FtpResponse(String orig, URL url, Ftp ftp) 81 throws FtpException, IOException { 82 83 this.orig = orig; 84 this.base = url.toString(); 85 this.ftp = ftp; 86 87 if (!"ftp".equals(url.getProtocol())) 88 throw new FtpException("Not a ftp url:" + url); 89 90 if (url.getPath() != url.getFile()) 91 Ftp.LOG.warning("url.getPath() != url.getFile(): " + url); 92 93 String path = "".equals(url.getPath()) ? "/" : url.getPath(); 94 95 try { 96 97 if (ftp.followTalk) { 98 Ftp.LOG.info("fetching "+url); 99 } else { 100 if (Ftp.LOG.isLoggable(Level.FINE)) 101 Ftp.LOG.fine("fetching "+url); 102 } 103 104 InetAddress addr = InetAddress.getByName(url.getHost()); 105 106 if (ftp.client != null && ftp.keepConnection 109 && ftp.renewalTime < System.currentTimeMillis()) { 110 Ftp.LOG.info("delete client because idled too long"); 111 ftp.client = null; 112 } 113 114 if (ftp.client == null) { 116 if (ftp.followTalk) 117 Ftp.LOG.info("start client"); 118 ftp.client = new Client(); 120 124 ftp.client.setDefaultTimeout(ftp.timeout); 126 ftp.client.setDataTimeout(ftp.timeout); 128 129 if (ftp.followTalk) 131 ftp.client.addProtocolCommandListener( 132 new PrintCommandListener(ftp.LOG)); 133 } 134 135 if (ftp.client.isConnected()) { 137 InetAddress remoteAddress = ftp.client.getRemoteAddress(); 138 if (!addr.equals(remoteAddress)) { 139 if (ftp.followTalk) 140 Ftp.LOG.info("disconnect from "+remoteAddress 141 +" before connect to "+addr); 142 ftp.client.logout(); 144 ftp.client.disconnect(); 145 } 146 } 147 148 if (!ftp.client.isConnected()) { 150 151 if (ftp.followTalk) 152 Ftp.LOG.info("connect to "+addr); 153 154 ftp.client.connect(addr); 155 if (!FTPReply.isPositiveCompletion(ftp.client.getReplyCode())) { 156 ftp.client.disconnect(); 157 Ftp.LOG.warning("ftp.client.connect() failed: " 158 + addr + " " + ftp.client.getReplyString()); 159 this.code = 500; return; 161 } 162 163 if (ftp.followTalk) 164 Ftp.LOG.info("log into "+addr); 165 166 if (!ftp.client.login(ftp.userName, ftp.passWord)) { 167 ftp.client.disconnect(); 173 Ftp.LOG.warning("ftp.client.login() failed: "+addr); 174 this.code = 401; return; 176 } 177 178 if (!ftp.client.setFileType(FTP.BINARY_FILE_TYPE)) { 180 ftp.client.logout(); 181 ftp.client.disconnect(); 182 Ftp.LOG.warning("ftp.client.setFileType() failed: "+addr); 183 this.code = 500; return; 185 } 186 187 if (ftp.followTalk) 188 Ftp.LOG.info("set parser for "+addr); 189 190 try { 192 ftp.parser = null; 193 String parserKey = ftp.client.getSystemName(); 194 if (parserKey.startsWith("UNKNOWN Type: L8")) 196 parserKey = "UNIX Type: L8"; 197 ftp.parser = (new DefaultFTPFileEntryParserFactory()) 198 .createFileEntryParser(parserKey); 199 } catch (FtpExceptionBadSystResponse e) { 200 Ftp.LOG.warning("ftp.client.getSystemName() failed: "+addr+" "+e); 201 ftp.parser = null; 202 } catch (ParserInitializationException e) { 203 Ftp.LOG.warning("createFileEntryParser() failed. "+addr+" "+e); 206 ftp.parser = null; 207 } finally { 208 if (ftp.parser == null) { 209 Ftp.LOG.warning("ftp.parser is null: "+addr); 212 ftp.client.logout(); 213 ftp.client.disconnect(); 214 this.code = 500; return; 216 } 217 } 218 219 } else { 220 if (ftp.followTalk) 221 Ftp.LOG.info("use existing connection"); 222 } 223 224 this.content = null; 225 226 if (path.endsWith("/")) { 227 getDirAsHttpResponse(path); 228 } else { 229 getFileAsHttpResponse(path); 230 } 231 232 if (ftp.client != null && ftp.keepConnection) { 234 ftp.renewalTime = System.currentTimeMillis() 235 + ((ftp.timeout<ftp.serverTimeout) ? ftp.timeout : ftp.serverTimeout); 236 if (ftp.followTalk) 237 Ftp.LOG.info("reset renewalTime to " 238 +ftp.httpDateFormat.toString(ftp.renewalTime)); 239 } 240 241 if (ftp.client != null && !ftp.keepConnection) { 244 if (ftp.followTalk) 245 Ftp.LOG.info("disconnect from "+addr); 246 ftp.client.logout(); 247 ftp.client.disconnect(); 248 } 249 250 } catch (Exception e) { 251 ftp.LOG.warning(""+e); 252 StackTraceElement stes[] = e.getStackTrace(); 253 for (int i=0; i<stes.length; i++) { 254 ftp.LOG.warning(" "+stes[i].toString()); 255 } 256 if (ftp.followTalk) 259 Ftp.LOG.info("delete client due to exception"); 260 ftp.client = null; 261 throw new FtpException(e); 270 } 272 273 } 274 275 private void getFileAsHttpResponse(String path) 277 throws IOException { 278 279 ByteArrayOutputStream os = null; 280 List list = null; 281 282 try { 283 list = new LinkedList (); 285 ftp.client.retrieveList(path, list, ftp.maxContentLength, ftp.parser); 286 287 os = new ByteArrayOutputStream (ftp.BUFFER_SIZE); 288 ftp.client.retrieveFile(path, os, ftp.maxContentLength); 289 290 FTPFile ftpFile = (FTPFile) list.get(0); 291 this.headers.put("Content-Length", 292 new Long (ftpFile.getSize()).toString()); 293 this.headers.put("Last-Modified", 295 ftp.httpDateFormat.toString(ftpFile.getTimestamp())); 296 this.content = os.toByteArray(); 297 298 String contentType = null; 299 if (contentType == null && ftp.TYPE_MAP != null) 303 contentType = ftp.TYPE_MAP.getContentType(path); 304 if (contentType != null) 305 this.headers.put("Content-Type", contentType); 306 307 313 this.code = 200; 315 } catch (FtpExceptionControlClosedByForcedDataClose e) { 316 317 if (ftp.followTalk) 320 Ftp.LOG.info("delete client because server cut off control channel: "+e); 321 ftp.client = null; 322 323 if (os == null) { Ftp.LOG.warning( 328 "Please try larger maxContentLength for ftp.client.retrieveList(). " 329 + e); 330 this.code = 400; return; 333 } 334 335 FTPFile ftpFile = (FTPFile) list.get(0); 336 this.headers.put("Content-Length", 337 new Long (ftpFile.getSize()).toString()); 338 this.headers.put("Last-Modified", 340 ftp.httpDateFormat.toString(ftpFile.getTimestamp())); 341 this.content = os.toByteArray(); 342 343 String contentType = null; 344 if (contentType == null && ftp.TYPE_MAP != null) 348 contentType = ftp.TYPE_MAP.getContentType(path); 349 if (contentType != null) 350 this.headers.put("Content-Type", contentType); 351 352 358 this.code = 200; 360 } catch (FtpExceptionCanNotHaveDataConnection e) { 361 362 if (FTPReply.isPositiveCompletion(ftp.client.cwd(path))) { 363 this.headers.put("Location", path + "/"); 365 this.code = 300; } else { 368 this.code = 404; } 371 372 } catch (FtpExceptionUnknownForcedDataClose e) { 373 Ftp.LOG.warning( 376 "Unrecognized reply after forced close of data channel. " 377 + "If this is acceptable, please modify Client.java accordingly. " 378 + e); 379 this.code = 400; } 381 382 } 383 384 private void getDirAsHttpResponse(String path) 386 throws IOException { 387 List list = new LinkedList (); 388 389 try { 390 391 if (!FTPReply.isPositiveCompletion(ftp.client.cwd(path))) { 393 this.code = 404; return; 395 } 396 397 399 ftp.client.retrieveList(null, list, ftp.maxContentLength, ftp.parser); 400 this.content = list2html(list, path, "/".equals(path) ? false : true); 401 this.headers.put("Content-Length", 402 new Integer (this.content.length).toString()); 403 this.headers.put("Content-Type", "text/html"); 404 406 412 this.code = 200; 414 } catch (FtpExceptionControlClosedByForcedDataClose e) { 415 416 if (ftp.followTalk) 419 Ftp.LOG.info("delete client because server cut off control channel: "+e); 420 ftp.client = null; 421 422 this.content = list2html(list, path, "/".equals(path) ? false : true); 423 this.headers.put("Content-Length", 424 new Integer (this.content.length).toString()); 425 this.headers.put("Content-Type", "text/html"); 426 428 434 this.code = 200; 436 } catch (FtpExceptionUnknownForcedDataClose e) { 437 Ftp.LOG.warning( 440 "Unrecognized reply after forced close of data channel. " 441 + "If this is acceptable, please modify Client.java accordingly. " 442 + e); 443 this.code = 400; } catch (FtpExceptionCanNotHaveDataConnection e) { 445 Ftp.LOG.warning(""+ e); 446 this.code = 500; } 448 449 } 450 451 private byte[] list2html(List list, String path, boolean includeDotDot) { 453 454 StringBuffer x = new StringBuffer ("<html><head>"); 456 x.append("<title>Index of "+path+"</title></head>\n"); 457 x.append("<body><h1>Index of "+path+"</h1><pre>\n"); 458 459 if (includeDotDot) { 460 x.append("<a HREF='../'>../</a>\t-\t-\t-\n"); 461 } 462 463 for (int i=0; i<list.size(); i++) { 464 FTPFile f = (FTPFile) list.get(i); 465 String name = f.getName(); 466 String time = ftp.httpDateFormat.toString(f.getTimestamp()); 467 if (f.isDirectory()) { 468 if (name.equals(".") || name.equals("..")) 470 continue; 471 x.append("<a HREF='"+name+"/"+"'>"+name+"/</a>\t"); 472 x.append(time+"\t-\n"); 473 } else if (f.isFile()) { 474 x.append("<a HREF='"+name+ "'>"+name+"</a>\t"); 475 x.append(time+"\t"+f.getSize()+"\n"); 476 } else { 477 } 480 } 481 482 x.append("</pre></body></html>\n"); 483 484 return new String (x).getBytes(); 485 } 486 487 } 488 | Popular Tags |