1
/* Copyright (c) 2007 SNAP Innovation GmbH
3
* BLOB Streaming for MySQL
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
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.
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
29
package org.blobstreaming.www;
31
import org.blobstreaming.lib.str;
33
import java.io.IOException;
34
import java.lang.StringBuffer;
35
import java.lang.InstantiationException;
36
import java.lang.IllegalAccessException;
38
public class FTP extends ResourceHandler {
39
protected String currentTypeCode = "A";
41
public final String DEFAULT_TYPE_CODE = "I";
44
public final int BASIC = 0;
45
public final int UPLOAD = 1;
46
public final int DOWNLOAD = 2;
48
protected Socket dataChannel = null;
50
public FTP() throws IOException
54
setOptions("DefaultPort=21");
59
* USER <user-name> - Submit the users name.
60
* PASS <password> - Submit the users password.
61
* ACCT <account-information> <CRLF>
62
* CWD <path-name> - Change directory.
63
* CDUP - Change to parent directory.
64
* SMNT <path-name> - Structure mount.
65
* QUIT - Logout and close connections.
66
* REIN - Re-initialize (logout)
67
* PORT <host-port> - Host and port for data connections.
69
* PASV - Pasive mode, the server will listen.
70
* TYPE <type-code> - (A)SCII, (I)mage [Type-Code]
71
* STRU <structure-code> - (F)ile, (R)ecord, (P)age.
72
* MODE <mode-code> - (S)tream, (B)lock, (C)ompressed
73
* RETR <path-name> - Retrieve, download. [DOWNLOAD]
74
* STOR <path-name> - Store, upload. [UPLOAD]
75
* STOU - Store unique. [UPLOAD]
76
* APPE <path-name> - Append, with create. [UPLOAD]
77
* ALLO <allocation-info> (<decimal-int>
78
* [ R <decimal-int>]) <CRLF> - Allocate.
79
* REST <restart-marker> - Restart.
80
* RNFR <path-name> - Rename from
81
* RNTO <path-name> - Rename to
82
* ABOR - Abort command and transfer.
83
* DELE <path-name> - Delete file.
84
* RMD <path-name> - Remove directory.
85
* MKD <path-name> - Make directory.
86
* PWD - Print Working directory.
87
* LIST [ <path-name>] - List directory. [DOWNLOAD]
88
* NLST [ <path-name>] - Name list. [DOWNLOAD]
89
* SITE <site-info> - Site specific info
91
* 214- The following SITE commands are recognized (* =>'s unimplemented).
92
* UMASK IDLE CHMOD HELP
93
* 214 Direct comments to ftp-bugs@suse2.snap.de.
95
* 502 Unknown command UMASK.
97
* 214 Syntax: SITE UMASK [ <sp> umask ]
99
* 214 Syntax: SITE IDLE [ <sp> maximum-idle-time ]
101
* 214 Syntax: SITE CHMOD <sp> mode <sp> file-name
103
* 214 Syntax: SITE HELP [ <sp> <string> ]
105
* STAT [ <path-name>] - Server returns status
106
* HELP [ <help-info>] - Get help on <help-info>.
107
* NOOP - Do nothing (keep alive).
109
public int getCommandType(String command)
111
if (str.locate("STOR STOU APPE", command) >= 0)
113
if (str.locate("RETR LIST NLST", command) >= 0)
118
public String getUserName()
122
user = inputProperties.getProperty("User-Name");
124
user = locator.getUser();
130
public String getPassword()
134
password = inputProperties.getProperty("Password");
135
if (password == null);
136
password = locator.getPassword();
137
if (password == null)
138
password = "bill.gates@microsoft.com";
142
public String getPathName()
146
file = inputProperties.getProperty("Path-Name");
148
file = locator.getFile();
152
public String getTypeCode()
154
String type = inputProperties.getProperty("Type-Code");
157
return(DEFAULT_TYPE_CODE);
161
public String getCommandParameter(String command)
163
if (str.locate("CWD SMNT RETR STOR APPE DELE RMD MKD LIST STAT", command) >= 0)
164
return(getPathName());
165
if (str.locate("CDUP QUIT REIN PASV STOU ABOR PWD SYST NOOP", command) >= 0)
167
if (command.equals("RNFR")) {
170
file = inputProperties.getProperty("Rename-From");
172
file = getPathName();
175
if (command.equals("RNTO"))
176
return(inputProperties.getProperty("Rename-To"));
177
if (command.equals("USER"))
178
return(getUserName());
179
if (command.equals("PASS"))
180
return(getPassword());
181
if (command.equals("ACCT"))
182
return(inputProperties.getProperty("Account-Info"));
183
if (command.equals("PORT")) {
184
String host = inputProperties.getProperty("Data-Channel-Host");
185
int portNo = Integer.valueOf(inputProperties.getProperty("Data-Channel-Port")).intValue();
188
host = str.replaceAll(host, ".", ",");
189
port = (portNo / 256) + "," + (portNo % 256);
191
return(host+","+port);
193
if (command.equals("TYPE"))
194
return(getTypeCode());
195
if (command.equals("STRU"))
196
return(inputProperties.getProperty("Structure-Code"));
197
if (command.equals("MODE"))
198
return(inputProperties.getProperty("Mode-Code"));
199
if (command.equals("ALLO"))
200
return(inputProperties.getProperty("Allocation-Info"));
201
if (command.equals("REST"))
202
return(inputProperties.getProperty("Restart-Marker"));
203
if (command.equals("SITE"))
204
return(inputProperties.getProperty("Site-Info"));
205
if (command.equals("HELP"))
206
return(inputProperties.getProperty("Help-Info"));
210
public String getCommandArgument(String command)
214
argument = getCommandParameter(command);
215
if (argument.length() > 0) {
216
return(" "+argument);
221
protected int readResponse(boolean haveData) throws IOException, InstantiationException, IllegalAccessException
227
result = "426 Connection closed";
228
if (str.substr(result, 3, 1).equals("-")) {
229
// Multi-line response.
230
String code = str.substr(result, 0, 3);
231
StringBuffer response = new StringBuffer();
236
result = "426 Connection closed";
237
if (str.locate("123456789", str.substr(result, 0, 1)) >= 0) {
238
/* PMC - 1/8/2001 - 4017, some FTP servers
239
* send multiple such lines (not in FTP spec!)
241
if (str.substr(result, 0, 3).equals(code) &&
242
!str.substr(result, 3, 1).equals("-"))
245
response.append(result);
247
/* PMC - Do not overwrite the data when have data already! */
249
outputContent = getContentHandler("text/plain");
250
outputContent.setData(this, response.toString(), null);
254
return (getStatusType(status));
257
private static Object portLock = new Object();
258
private static int nextPort = 61234;
260
public int getFreePort()
262
synchronized (portLock) {
267
if (nextPort > 62234)
270
while (!ResourceHandler.isPortFree(nextPort));
275
protected int setupTransferType() throws IOException, InstantiationException, IllegalAccessException
277
String type = getTypeCode();
279
if (currentTypeCode != type) {
280
currentTypeCode = type;
281
return(performCommand("TYPE"));
286
protected Socket prepareDataChannel() throws IOException, InstantiationException, IllegalAccessException
288
Socket channel = null;
289
int portNo = getFreePort();
291
if (setupTransferType() == OK) {
292
inputProperties.setProperty("Data-Channel-Host", getLocalHost());
293
inputProperties.setProperty("Data-Channel-Port", portNo);
295
if (performCommand("PORT") == OK) {
296
channel = new Socket();
298
channel.setOptions("ListenPort="+portNo+";BlockOnListen=false");
305
public int performCommand(String command) throws IOException, InstantiationException, IllegalAccessException
310
type = getCommandType(command);
313
dataChannel = prepareDataChannel();
314
if (dataChannel == null)
318
//print "FTP. "+command+getCommandArgument(command);
319
/* PMC - writeLine() - \r\n - is correct.
320
* writeLine(command+getCommandArgument(command));
321
* but some FTP servers do not want it!
323
* write(command+getCommandArgument(command)+str.format("\n"));
325
* PMC - I have reverted to the original implementation.
326
* The problem is (I believe), that some FTP servers
327
* want the entire line in one TCP packet!!!
328
* After version 4022, writeLine() works in this
331
//PBE.debug(FTP. "+command+getCommandArgument(command));
332
writeLine(command+getCommandArgument(command));
333
result = readResponse(false);
335
if (result == EXCHANGE) {
338
dataChannel.write(inputContent, inputContent.getLength());
341
outputContent = getContentHandler("text/plain");
342
outputContent.setData(this, dataChannel.getInputStream(), -1, null);
345
result = readResponse(type == DOWNLOAD);
348
if (dataChannel != null)
352
//PBE.debug("FTP: "+this.status);
356
public String getMethod()
358
String m = super.getMethod();
363
if (inputContent != null)
365
if (getPathName().endsWith("/"))
372
public int login() throws IOException, InstantiationException, IllegalAccessException
376
if ((result = performCommand("USER")) == MORE) {
377
if ((result = performCommand("PASS")) == MORE)
378
result = performCommand("ACCT");
379
else if (statusCode(status) == "530")
380
status = status + " (Incorrect password)";
382
else if (statusCode(status) == "530")
383
status = status + " (User unknown)";
387
public void connect() throws IOException, InstantiationException, IllegalAccessException
390
if (getStatusType(status) == OK) {
392
if (getStatusType(status) == OK) {
393
outputProperties:setProperty("Greetings", status);
399
public void syncronize() throws IOException, InstantiationException, IllegalAccessException
401
performCommand(getMethod());
409
performCommand("QUIT");
411
catch (Exception e) { }
416
catch (Exception e) { }
419
public void disconnect()
421
if (dataChannel != null)
423
/* Use the super.getMethod(). We want to check if
424
* a method has been explicity specified. If this
425
* is the case, then the user is responsible for closing
426
* the connection himself.
428
if (super.getMethod() == null)
432
public static void register()
434
register(FTP.class, "ftp");