PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/FTP/tags/rel-0-7-3/ftp/FtpConnection.java

#
Java | 598 lines | 568 code | 10 blank | 20 comment | 12 complexity | 868abbe9bb3392b24adc6f42ff91de61 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * FtpConnection.java - A connection to an FTP server
  3. * Copyright (C) 2002, 2003 Slava Pestov
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2
  8. * of the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. package ftp;
  20. import com.fooware.net.*;
  21. import gnu.regexp.*;
  22. import java.io.*;
  23. import java.util.*;
  24. import org.gjt.sp.jedit.search.RESearchMatcher;
  25. import org.gjt.sp.jedit.jEdit;
  26. import org.gjt.sp.jedit.MiscUtilities;
  27. import org.gjt.sp.util.Log;
  28. class FtpConnection extends ConnectionManager.Connection
  29. {
  30. FtpConnection(ConnectionManager.ConnectionInfo info) throws IOException
  31. {
  32. super(info);
  33. client = new FtpClient();
  34. client.connect(info.host,info.port);
  35. if(!client.getResponse().isPositiveCompletion())
  36. {
  37. throw new FtpException(
  38. client.getResponse());
  39. }
  40. client.userName(info.user);
  41. if(client.getResponse().isPositiveIntermediary())
  42. {
  43. client.password(info.password);
  44. FtpResponse response = client.getResponse();
  45. if(!response.isPositiveCompletion())
  46. {
  47. client.logout();
  48. throw new FtpLoginException(response);
  49. }
  50. }
  51. else if(client.getResponse().isPositiveCompletion())
  52. {
  53. // do nothing, server let us in without
  54. // a password
  55. }
  56. else
  57. {
  58. FtpResponse response = client.getResponse();
  59. client.logout();
  60. throw new FtpLoginException(response);
  61. }
  62. client.printWorkingDirectory();
  63. FtpResponse response = client.getResponse();
  64. if(response != null
  65. && response.getReturnCode() != null
  66. && response.getReturnCode().charAt(0) == '2')
  67. {
  68. String msg = response.getMessage().substring(4);
  69. if(msg.startsWith("\""))
  70. {
  71. int index = msg.indexOf('"',1);
  72. if(index != -1)
  73. {
  74. home = msg.substring(1,index);
  75. if(!home.startsWith("/"))
  76. home = "/".concat(home);
  77. }
  78. }
  79. }
  80. }
  81. FtpVFS.FtpDirectoryEntry[] listDirectory(String path) throws IOException
  82. {
  83. //CWD into the directory - Doing a LIST on a path with spaces in the
  84. //name fails; however, if you CWD to the dir and then LIST it
  85. // succeeds.
  86. client.changeWorkingDirectory(path);
  87. //Check for successful response
  88. FtpResponse response = client.getResponse();
  89. if(response != null
  90. && response.getReturnCode() != null
  91. && response.getReturnCode().charAt(0) != '2')
  92. {
  93. throw new FtpException(response);
  94. }
  95. // some servers might not support -a, so if we get an error
  96. // try without -a
  97. ArrayList directoryVector = _listDirectory(true);
  98. if(directoryVector == null || directoryVector.size() == 0)
  99. directoryVector = _listDirectory(false);
  100. if(directoryVector == null)
  101. {
  102. // error occurred
  103. return null;
  104. }
  105. return (FtpVFS.FtpDirectoryEntry[])directoryVector.toArray(
  106. new FtpVFS.FtpDirectoryEntry[directoryVector.size()]);
  107. }
  108. /**
  109. * An incredibly broken implementation! Originally only good for
  110. * internal use by resolveSymlinks(), in FTP 0.7.1 we grafted on
  111. * support for file type detection, as required by jEdit 4.2.
  112. */
  113. FtpVFS.FtpDirectoryEntry getDirectoryEntry(String path) throws IOException
  114. {
  115. //CWD into the directory - Doing a LIST on a path with spaces in the
  116. //name fails; however, if you CWD to the dir and then LIST it
  117. // succeeds.
  118. //First we get the parent path of the file passed to us in the path
  119. // field. We use the MiscUtilities.getParentOfPath as opposed to our own
  120. //because the path here is not a URL and our own version expects a URL
  121. // when it instantiates an FtpAddress object.
  122. String parentPath = MiscUtilities.getParentOfPath(path);
  123. client.changeWorkingDirectory(parentPath);
  124. //Check for successful response
  125. FtpResponse response = client.getResponse();
  126. if(response != null
  127. && response.getReturnCode() != null
  128. && response.getReturnCode().charAt(0) != '2')
  129. {
  130. throw new FtpException(response);
  131. }
  132. setupSocket();
  133. String name = MiscUtilities.getFileName(path);
  134. //Here we do a LIST for on the specific file
  135. //Since we are in the right dir, we list only the filename, not the
  136. // whole path...
  137. Reader _reader = client.list(name);
  138. if(_reader == null)
  139. {
  140. // eg, file not found
  141. return null;
  142. }
  143. BufferedReader reader = new BufferedReader(_reader);
  144. // to determine if this is a file or a directory, we list it.
  145. // if the list contains 1 entry, guess that this is a file
  146. LinkedList listing = new LinkedList();
  147. try
  148. {
  149. String line;
  150. while((line = reader.readLine()) != null)
  151. {
  152. FtpVFS.FtpDirectoryEntry dirEntry = lineToDirectoryEntry(line);
  153. if(dirEntry != null)
  154. listing.add(dirEntry);
  155. else
  156. {
  157. Log.log(Log.DEBUG,this,"Discarding "
  158. + line);
  159. }
  160. }
  161. }
  162. finally
  163. {
  164. reader.close();
  165. }
  166. int type;
  167. if(listing.size() == 0)
  168. {
  169. // probably a file that does not exist.
  170. type = FtpVFS.FtpDirectoryEntry.FILE;
  171. }
  172. else if(listing.size() > 1)
  173. {
  174. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  175. }
  176. else
  177. {
  178. FtpVFS.FtpDirectoryEntry dirEntry
  179. = (FtpVFS.FtpDirectoryEntry)
  180. listing.get(0);
  181. //XXX: we even use startsWith to hot have to parse the
  182. //-> symlink indicator. Broken, broken, broken...
  183. if(dirEntry.name.startsWith(name))
  184. return dirEntry;
  185. else
  186. {
  187. // it could be a directory with 1 file in it!
  188. // but I don't care, I don't use FTP :-)
  189. type = FtpVFS.FtpDirectoryEntry.FILE;
  190. }
  191. }
  192. // this directory entry only has half an ass.
  193. return new FtpVFS.FtpDirectoryEntry(
  194. null,null,null,type,0L,false,0,null);
  195. }
  196. boolean removeFile(String path) throws IOException
  197. {
  198. client.delete(path);
  199. return client.getResponse().isPositiveCompletion();
  200. }
  201. boolean removeDirectory(String path) throws IOException
  202. {
  203. client.removeDirectory(path);
  204. return client.getResponse().isPositiveCompletion();
  205. }
  206. boolean rename(String from, String to) throws IOException
  207. {
  208. client.renameFrom(from);
  209. client.renameTo(to);
  210. return client.getResponse().isPositiveCompletion();
  211. }
  212. boolean makeDirectory(String path) throws IOException
  213. {
  214. client.makeDirectory(path);
  215. return client.getResponse().isPositiveCompletion();
  216. }
  217. InputStream retrieve(String path) throws IOException
  218. {
  219. setupSocket();
  220. InputStream in = client.retrieveStream(path);
  221. if(in == null)
  222. throw new FtpException(client.getResponse());
  223. else
  224. return in;
  225. }
  226. OutputStream store(String path) throws IOException
  227. {
  228. setupSocket();
  229. OutputStream out = client.storeStream(path);
  230. if(out == null)
  231. throw new FtpException(client.getResponse());
  232. else
  233. return out;
  234. }
  235. void chmod(String path, int permissions) throws IOException
  236. {
  237. String cmd = "CHMOD " + Integer.toString(permissions,8)
  238. + " " + path;
  239. client.siteParameters(cmd);
  240. }
  241. // Passed 'name' in an array as a hack to be able to return multiple values
  242. public String resolveSymlink(String path, String[] name)
  243. throws IOException
  244. {
  245. String _name = name[0];
  246. int index = _name.indexOf(" -> ");
  247. if(index == -1)
  248. {
  249. //non-standard link representation. Treat as a file
  250. //Some Mac and NT based servers do not use the "->" for symlinks
  251. Log.log(Log.NOTICE,this,"File '"
  252. + name
  253. + "' is listed as a link, but will be treated"
  254. + " as a file because no '->' was found.");
  255. return null;
  256. }
  257. String link = _name.substring(index + " -> ".length());
  258. name[0] = _name.substring(0,index);
  259. return link;
  260. }
  261. boolean checkIfOpen() throws IOException
  262. {
  263. try
  264. {
  265. // to ensure that the server didn't disconnect
  266. // before the keep-alive timeout expires
  267. client.noOp();
  268. client.getResponse();
  269. return true;
  270. }
  271. catch(Exception e)
  272. {
  273. return false;
  274. }
  275. }
  276. void logout() throws IOException
  277. {
  278. client.logout();
  279. }
  280. // Private members
  281. private FtpClient client;
  282. // used to parse VMS file listings, which can span more than one line
  283. private String prevLine;
  284. private static UncheckedRE[] unixRegexps;
  285. private static UncheckedRE dosRegexp;
  286. private static UncheckedRE vmsRegexp;
  287. private static UncheckedRE vmsPartialRegexp;
  288. private static UncheckedRE vmsRejectedRegexp;
  289. private static UncheckedRE as400Regexp;
  290. static
  291. {
  292. unixRegexps = new UncheckedRE[jEdit.getIntegerProperty(
  293. "vfs.ftp.list.count",-1)];
  294. for(int i = 0; i < unixRegexps.length; i++)
  295. {
  296. unixRegexps[i] = new UncheckedRE(jEdit.getProperty(
  297. "vfs.ftp.list." + i),0,
  298. RESearchMatcher.RE_SYNTAX_JEDIT);
  299. }
  300. dosRegexp = new UncheckedRE(jEdit.getProperty(
  301. "vfs.ftp.list.dos"),0,
  302. RESearchMatcher.RE_SYNTAX_JEDIT);
  303. vmsRegexp = new UncheckedRE(jEdit.getProperty(
  304. "vfs.ftp.list.vms"),0,
  305. RESearchMatcher.RE_SYNTAX_JEDIT);
  306. vmsPartialRegexp = new UncheckedRE(jEdit.getProperty(
  307. "vfs.ftp.list.vms.partial"),0,
  308. RESearchMatcher.RE_SYNTAX_JEDIT);
  309. vmsRejectedRegexp = new UncheckedRE(jEdit.getProperty(
  310. "vfs.ftp.list.vms.rejected"),0,
  311. RESearchMatcher.RE_SYNTAX_JEDIT);
  312. as400Regexp = new UncheckedRE(jEdit.getProperty(
  313. "vfs.ftp.list.as400"),0,
  314. RESearchMatcher.RE_SYNTAX_JEDIT);
  315. }
  316. private void setupSocket()
  317. throws IOException
  318. {
  319. // See if we should use Binary mode to transfer files.
  320. if (jEdit.getBooleanProperty("vfs.ftp.binary"))
  321. {
  322. //Go with Binary
  323. client.representationType(FtpClient.IMAGE_TYPE);
  324. }
  325. else
  326. {
  327. //Stick to ASCII - let the line endings get converted
  328. client.representationType(FtpClient.ASCII_TYPE);
  329. }
  330. if(jEdit.getBooleanProperty("vfs.ftp.passive"))
  331. client.passive();
  332. else
  333. client.dataPort();
  334. }
  335. private ArrayList _listDirectory(boolean tryHiddenFiles)
  336. throws IOException
  337. {
  338. BufferedReader in = null;
  339. try
  340. {
  341. ArrayList directoryVector = new ArrayList();
  342. setupSocket();
  343. Reader _in = (tryHiddenFiles ? client.list("-a") : client.list());
  344. if(_in == null)
  345. {
  346. if(!tryHiddenFiles)
  347. throw new FtpException(client.getResponse());
  348. else
  349. return null;
  350. }
  351. in = new BufferedReader(_in);
  352. String line;
  353. while((line = in.readLine()) != null)
  354. {
  355. if(line.length() == 0)
  356. continue;
  357. FtpVFS.FtpDirectoryEntry entry = lineToDirectoryEntry(line);
  358. if(entry == null
  359. || entry.name.equals(".")
  360. || entry.name.equals(".."))
  361. {
  362. Log.log(Log.DEBUG,this,"Discarding " + line);
  363. continue;
  364. }
  365. else
  366. ; //Log.log(Log.DEBUG,this,"Parsed " + line);
  367. directoryVector.add(entry);
  368. }
  369. return directoryVector;
  370. }
  371. finally
  372. {
  373. if(in != null)
  374. {
  375. try
  376. {
  377. in.close();
  378. }
  379. catch(Exception e)
  380. {
  381. Log.log(Log.ERROR,this,e);
  382. }
  383. }
  384. }
  385. }
  386. // Convert a line of LIST output to an FtpDirectoryEntry
  387. private FtpVFS.FtpDirectoryEntry lineToDirectoryEntry(String line)
  388. {
  389. try
  390. {
  391. // we use one of several regexps to obtain
  392. // the file name, type, and size
  393. int type = FtpVFS.FtpDirectoryEntry.FILE;
  394. String name = null;
  395. long length = 0L;
  396. int permissions = 0;
  397. String permissionString = null;
  398. boolean ok = false;
  399. if(prevLine != null)
  400. {
  401. // handle VMS listings split over several lines
  402. line = prevLine + line;
  403. prevLine = null;
  404. }
  405. for(int i = 0; i < unixRegexps.length; i++)
  406. {
  407. UncheckedRE regexp = unixRegexps[i];
  408. REMatch match;
  409. if((match = regexp.getMatch(line)) != null)
  410. {
  411. switch(line.charAt(0))
  412. {
  413. case 'd':
  414. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  415. break;
  416. case 'l':
  417. type = FtpVFS.FtpDirectoryEntry.LINK;
  418. break;
  419. case '-':
  420. type = FtpVFS.FtpDirectoryEntry.FILE;
  421. break;
  422. }
  423. permissionString = match.toString(1);
  424. permissions = MiscUtilities.parsePermissions(
  425. permissionString);
  426. try
  427. {
  428. length = Long.parseLong(match.toString(2));
  429. }
  430. catch(NumberFormatException nf)
  431. {
  432. length = 0L;
  433. }
  434. name = match.toString(3);
  435. ok = true;
  436. break;
  437. }
  438. }
  439. if(!ok)
  440. {
  441. REMatch match;
  442. if(vmsPartialRegexp.isMatch(line))
  443. {
  444. prevLine = line;
  445. return null;
  446. }
  447. else if(vmsRejectedRegexp.isMatch(line) == true)
  448. return null;
  449. else if((match = vmsRegexp.getMatch(line)) != null)
  450. {
  451. name = match.toString(1);
  452. length = Long.parseLong(
  453. match.toString(2)) * 512;
  454. if(name.endsWith(".DIR"))
  455. {
  456. name = name.substring(0,
  457. name.length() - 4);
  458. type = FtpVFS.FtpDirectoryEntry
  459. .DIRECTORY;
  460. }
  461. permissionString = match.toString(3);
  462. ok = true;
  463. }
  464. }
  465. if(!ok)
  466. {
  467. REMatch match;
  468. if((match = as400Regexp.getMatch(line)) != null)
  469. {
  470. String dirFlag = match.toString(2);
  471. if (dirFlag.equals("*DIR"))
  472. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  473. else
  474. type = FtpVFS.FtpDirectoryEntry.FILE;
  475. try
  476. {
  477. length = Long.parseLong(match.toString(1));
  478. }
  479. catch(NumberFormatException nf)
  480. {
  481. length = 0L;
  482. }
  483. name = match.toString(3);
  484. if(name.endsWith("/"))
  485. name = name.substring(0,name.length() - 1);
  486. ok = true;
  487. }
  488. }
  489. if(!ok)
  490. {
  491. REMatch match;
  492. if((match = dosRegexp.getMatch(line)) != null)
  493. {
  494. try
  495. {
  496. String sizeStr = match.toString(1);
  497. if(sizeStr.equals("<DIR>"))
  498. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  499. else
  500. length = Long.parseLong(sizeStr);
  501. }
  502. catch(NumberFormatException nf)
  503. {
  504. length = 0L;
  505. }
  506. name = match.toString(2);
  507. ok = true;
  508. }
  509. }
  510. if(!ok)
  511. return null;
  512. // path is null; it will be created later, by listDirectory()
  513. return new FtpVFS.FtpDirectoryEntry(name,null,null,type,
  514. length,name.charAt(0) == '.' /* isHidden */,
  515. permissions,permissionString);
  516. }
  517. catch(Exception e)
  518. {
  519. Log.log(Log.NOTICE,this,"lineToDirectoryEntry("
  520. + line + ") failed:");
  521. Log.log(Log.NOTICE,this,e);
  522. return null;
  523. }
  524. }
  525. }