PageRenderTime 54ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/FTP/tags/release-0-6/ftp/FtpConnection.java

#
Java | 559 lines | 529 code | 10 blank | 20 comment | 12 complexity | 0a72e8e80edcfa31419404590ebcd069 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. home = msg.substring(1,index);
  74. }
  75. }
  76. }
  77. FtpVFS.FtpDirectoryEntry[] listDirectory(String path) throws IOException
  78. {
  79. //CWD into the directory - Doing a LIST on a path with spaces in the
  80. //name fails; however, if you CWD to the dir and then LIST it
  81. // succeeds.
  82. client.changeWorkingDirectory(path);
  83. //Check for successful response
  84. FtpResponse response = client.getResponse();
  85. if(response != null
  86. && response.getReturnCode() != null
  87. && response.getReturnCode().charAt(0) != '2')
  88. {
  89. throw new FtpException(response);
  90. }
  91. // some servers might not support -a, so if we get an error
  92. // try without -a
  93. ArrayList directoryVector = _listDirectory(true);
  94. if(directoryVector == null || directoryVector.size() == 0)
  95. directoryVector = _listDirectory(false);
  96. if(directoryVector == null)
  97. {
  98. // error occurred
  99. return null;
  100. }
  101. return (FtpVFS.FtpDirectoryEntry[])directoryVector.toArray(
  102. new FtpVFS.FtpDirectoryEntry[directoryVector.size()]);
  103. }
  104. FtpVFS.FtpDirectoryEntry getDirectoryEntry(String path) throws IOException
  105. {
  106. //CWD into the directory - Doing a LIST on a path with spaces in the
  107. //name fails; however, if you CWD to the dir and then LIST it
  108. // succeeds.
  109. //First we get the parent path of the file passed to us in the path
  110. // field. We use the MiscUtilities.getParentOfPath as opposed to our own
  111. //because the path here is not a URL and our own version expects a URL
  112. // when it instantiates an FtpAddress object.
  113. String parentPath = MiscUtilities.getParentOfPath(path);
  114. client.changeWorkingDirectory(parentPath);
  115. //Check for successful response
  116. FtpResponse response = client.getResponse();
  117. if(response != null
  118. && response.getReturnCode() != null
  119. && response.getReturnCode().charAt(0) != '2')
  120. {
  121. throw new FtpException(response);
  122. }
  123. setupSocket();
  124. //Here we do a LIST for on the specific file
  125. //Since we are in the right dir, we list only the filename, not the
  126. // whole path...
  127. Reader _reader = client.list(path.substring(parentPath.length()));
  128. if(_reader == null)
  129. {
  130. // eg, file not found
  131. return null;
  132. }
  133. BufferedReader reader = new BufferedReader(_reader);
  134. try
  135. {
  136. String line = reader.readLine();
  137. if(line != null)
  138. {
  139. while(line.length() == 0)
  140. {
  141. line = reader.readLine();
  142. if(line == null)
  143. return null;
  144. }
  145. FtpVFS.FtpDirectoryEntry dirEntry = lineToDirectoryEntry(line);
  146. if(dirEntry == null)
  147. {
  148. // ok, this really sucks.
  149. // we were asked to get the directory
  150. // entry for a directory. This stupid
  151. // implementation will only work for
  152. // the resolveSymlink() method. A proper
  153. // version will be written some other time.
  154. return new FtpVFS.FtpDirectoryEntry(null,null,null,
  155. FtpVFS.FtpDirectoryEntry.DIRECTORY,0L,false,0,null);
  156. }
  157. else
  158. return dirEntry;
  159. }
  160. }
  161. finally
  162. {
  163. reader.close();
  164. }
  165. return null;
  166. }
  167. boolean removeFile(String path) throws IOException
  168. {
  169. client.delete(path);
  170. return client.getResponse().isPositiveCompletion();
  171. }
  172. boolean removeDirectory(String path) throws IOException
  173. {
  174. client.removeDirectory(path);
  175. return client.getResponse().isPositiveCompletion();
  176. }
  177. boolean rename(String from, String to) throws IOException
  178. {
  179. client.renameFrom(from);
  180. client.renameTo(to);
  181. return client.getResponse().isPositiveCompletion();
  182. }
  183. boolean makeDirectory(String path) throws IOException
  184. {
  185. client.makeDirectory(path);
  186. return client.getResponse().isPositiveCompletion();
  187. }
  188. InputStream retrieve(String path) throws IOException
  189. {
  190. setupSocket();
  191. InputStream in = client.retrieveStream(path);
  192. if(in == null)
  193. throw new FtpException(client.getResponse());
  194. else
  195. return in;
  196. }
  197. OutputStream store(String path) throws IOException
  198. {
  199. setupSocket();
  200. OutputStream out = client.storeStream(path);
  201. if(out == null)
  202. throw new FtpException(client.getResponse());
  203. else
  204. return out;
  205. }
  206. void chmod(String path, int permissions) throws IOException
  207. {
  208. String cmd = "CHMOD " + Integer.toString(permissions,8)
  209. + " " + path;
  210. client.siteParameters(cmd);
  211. }
  212. // Passed 'name' in an array as a hack to be able to return multiple values
  213. public String resolveSymlink(String path, String[] name)
  214. throws IOException
  215. {
  216. String _name = name[0];
  217. int index = _name.indexOf(" -> ");
  218. if(index == -1)
  219. {
  220. //non-standard link representation. Treat as a file
  221. //Some Mac and NT based servers do not use the "->" for symlinks
  222. Log.log(Log.NOTICE,this,"File '"
  223. + name
  224. + "' is listed as a link, but will be treated"
  225. + " as a file because no '->' was found.");
  226. return null;
  227. }
  228. String link = _name.substring(index + " -> ".length());
  229. name[0] = _name.substring(0,index);
  230. return link;
  231. }
  232. boolean checkIfOpen() throws IOException
  233. {
  234. try
  235. {
  236. // to ensure that the server didn't disconnect
  237. // before the keep-alive timeout expires
  238. client.noOp();
  239. client.getResponse();
  240. return true;
  241. }
  242. catch(Exception e)
  243. {
  244. return false;
  245. }
  246. }
  247. void logout() throws IOException
  248. {
  249. client.logout();
  250. }
  251. // Private members
  252. private FtpClient client;
  253. // used to parse VMS file listings, which can span more than one line
  254. private String prevLine;
  255. private static UncheckedRE[] unixRegexps;
  256. private static UncheckedRE dosRegexp;
  257. private static UncheckedRE vmsRegexp;
  258. private static UncheckedRE vmsPartial1Regexp;
  259. private static UncheckedRE vmsPartial2Regexp;
  260. private static UncheckedRE as400Regexp;
  261. static
  262. {
  263. unixRegexps = new UncheckedRE[jEdit.getIntegerProperty(
  264. "vfs.ftp.list.count",-1)];
  265. for(int i = 0; i < unixRegexps.length; i++)
  266. {
  267. unixRegexps[i] = new UncheckedRE(jEdit.getProperty(
  268. "vfs.ftp.list." + i),0,
  269. RESearchMatcher.RE_SYNTAX_JEDIT);
  270. }
  271. dosRegexp = new UncheckedRE(jEdit.getProperty(
  272. "vfs.ftp.list.dos"),0,
  273. RESearchMatcher.RE_SYNTAX_JEDIT);
  274. vmsRegexp = new UncheckedRE(jEdit.getProperty(
  275. "vfs.ftp.list.vms"),0,
  276. RESearchMatcher.RE_SYNTAX_JEDIT);
  277. vmsPartial1Regexp = new UncheckedRE(jEdit.getProperty(
  278. "vfs.ftp.list.vms.partial.1"),0,
  279. RESearchMatcher.RE_SYNTAX_JEDIT);
  280. vmsPartial2Regexp = new UncheckedRE(jEdit.getProperty(
  281. "vfs.ftp.list.vms.partial.2"),0,
  282. RESearchMatcher.RE_SYNTAX_JEDIT);
  283. as400Regexp = new UncheckedRE(jEdit.getProperty(
  284. "vfs.ftp.list.as400"),0,
  285. RESearchMatcher.RE_SYNTAX_JEDIT);
  286. }
  287. private void setupSocket()
  288. throws IOException
  289. {
  290. if(jEdit.getBooleanProperty("vfs.ftp.passive"))
  291. client.passive();
  292. else
  293. client.dataPort();
  294. // See if we should use Binary mode to transfer files.
  295. if (jEdit.getBooleanProperty("vfs.ftp.binary"))
  296. {
  297. //Go with Binary
  298. client.representationType(FtpClient.IMAGE_TYPE);
  299. }
  300. else
  301. {
  302. //Stick to ASCII - let the line endings get converted
  303. client.representationType(FtpClient.ASCII_TYPE);
  304. }
  305. }
  306. private ArrayList _listDirectory(boolean tryHiddenFiles)
  307. throws IOException
  308. {
  309. BufferedReader in = null;
  310. try
  311. {
  312. ArrayList directoryVector = new ArrayList();
  313. setupSocket();
  314. Reader _in = (tryHiddenFiles ? client.list("-a") : client.list());
  315. if(_in == null)
  316. {
  317. if(!tryHiddenFiles)
  318. throw new FtpException(client.getResponse());
  319. else
  320. return null;
  321. }
  322. in = new BufferedReader(_in);
  323. String line;
  324. while((line = in.readLine()) != null)
  325. {
  326. if(line.length() == 0)
  327. continue;
  328. FtpVFS.FtpDirectoryEntry entry = lineToDirectoryEntry(line);
  329. if(entry == null
  330. || entry.name.equals(".")
  331. || entry.name.equals(".."))
  332. {
  333. Log.log(Log.DEBUG,this,"Discarding " + line);
  334. continue;
  335. }
  336. else
  337. ; //Log.log(Log.DEBUG,this,"Parsed " + line);
  338. directoryVector.add(entry);
  339. }
  340. return directoryVector;
  341. }
  342. finally
  343. {
  344. if(in != null)
  345. {
  346. try
  347. {
  348. in.close();
  349. }
  350. catch(Exception e)
  351. {
  352. Log.log(Log.ERROR,this,e);
  353. }
  354. }
  355. }
  356. }
  357. // Convert a line of LIST output to an FtpDirectoryEntry
  358. private FtpVFS.FtpDirectoryEntry lineToDirectoryEntry(String line)
  359. {
  360. try
  361. {
  362. // we use one of several regexps to obtain
  363. // the file name, type, and size
  364. int type = FtpVFS.FtpDirectoryEntry.FILE;
  365. String name = null;
  366. long length = 0L;
  367. int permissions = 0;
  368. String permissionString = null;
  369. boolean ok = false;
  370. if(prevLine != null)
  371. {
  372. // handle VMS listings split over several lines
  373. line = prevLine + line;
  374. prevLine = null;
  375. }
  376. for(int i = 0; i < unixRegexps.length; i++)
  377. {
  378. UncheckedRE regexp = unixRegexps[i];
  379. REMatch match;
  380. if((match = regexp.getMatch(line)) != null)
  381. {
  382. switch(line.charAt(0))
  383. {
  384. case 'd':
  385. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  386. break;
  387. case 'l':
  388. type = FtpVFS.FtpDirectoryEntry.LINK;
  389. break;
  390. case '-':
  391. type = FtpVFS.FtpDirectoryEntry.FILE;
  392. break;
  393. }
  394. permissionString = match.toString(1);
  395. permissions = MiscUtilities.parsePermissions(
  396. permissionString);
  397. try
  398. {
  399. length = Long.parseLong(match.toString(2));
  400. }
  401. catch(NumberFormatException nf)
  402. {
  403. length = 0L;
  404. }
  405. name = match.toString(3);
  406. ok = true;
  407. break;
  408. }
  409. }
  410. if(!ok)
  411. {
  412. REMatch match;
  413. if((match = vmsPartial1Regexp.getMatch(line)) != null)
  414. {
  415. prevLine = line;
  416. return null;
  417. }
  418. if((match = vmsRegexp.getMatch(line)) != null)
  419. {
  420. name = match.toString(1);
  421. length = Long.parseLong(match.toString(2)) * 512;
  422. if(name.endsWith(".DIR"))
  423. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  424. permissionString = match.toString(3);
  425. ok = true;
  426. }
  427. }
  428. if(!ok)
  429. {
  430. REMatch match;
  431. if((match = as400Regexp.getMatch(line)) != null)
  432. {
  433. String dirFlag = match.toString(2);
  434. if (dirFlag.equals("*DIR"))
  435. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  436. else
  437. type = FtpVFS.FtpDirectoryEntry.FILE;
  438. try
  439. {
  440. length = Long.parseLong(match.toString(1));
  441. }
  442. catch(NumberFormatException nf)
  443. {
  444. length = 0L;
  445. }
  446. name = match.toString(3);
  447. if(name.endsWith("/"))
  448. name = name.substring(0,name.length() - 1);
  449. ok = true;
  450. }
  451. }
  452. if(!ok)
  453. {
  454. REMatch match;
  455. if((match = dosRegexp.getMatch(line)) != null)
  456. {
  457. try
  458. {
  459. String sizeStr = match.toString(1);
  460. if(sizeStr.equals("<DIR>"))
  461. type = FtpVFS.FtpDirectoryEntry.DIRECTORY;
  462. else
  463. length = Long.parseLong(sizeStr);
  464. }
  465. catch(NumberFormatException nf)
  466. {
  467. length = 0L;
  468. }
  469. name = match.toString(2);
  470. ok = true;
  471. }
  472. }
  473. if(!ok)
  474. return null;
  475. // path is null; it will be created later, by listDirectory()
  476. return new FtpVFS.FtpDirectoryEntry(name,null,null,type,
  477. length,name.charAt(0) == '.' /* isHidden */,
  478. permissions,permissionString);
  479. }
  480. catch(Exception e)
  481. {
  482. Log.log(Log.NOTICE,this,"lineToDirectoryEntry("
  483. + line + ") failed:");
  484. Log.log(Log.NOTICE,this,e);
  485. return null;
  486. }
  487. }
  488. }