~pbms-core/pbms/async_read

« back to all changes in this revision

Viewing changes to mybs/java/src/org/blobstreaming/www/FTP.java

  • Committer: paul-mccullagh
  • Date: 2008-03-26 11:35:17 UTC
  • Revision ID: paul-mccullagh-afb1610c21464a577ae428d72fc725eb986c05a5
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2007 SNAP Innovation GmbH
 
2
 *
 
3
 * BLOB Streaming for MySQL
 
4
 *
 
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.
 
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
 * Paul McCullagh
 
20
 *
 
21
 * 2007-07-30
 
22
 *
 
23
 * H&G2JCtL
 
24
 *
 
25
 * FTP (unused)
 
26
 *
 
27
 */
 
28
 
 
29
package org.blobstreaming.www;
 
30
 
 
31
import org.blobstreaming.lib.str;
 
32
 
 
33
import java.io.IOException;
 
34
import java.lang.StringBuffer;
 
35
import java.lang.InstantiationException;
 
36
import java.lang.IllegalAccessException;
 
37
 
 
38
public class FTP extends ResourceHandler {
 
39
        protected String currentTypeCode = "A";
 
40
        
 
41
        public final String                     DEFAULT_TYPE_CODE = "I";
 
42
 
 
43
        // Command types:
 
44
        public final int                        BASIC = 0;
 
45
        public final int                        UPLOAD = 1;
 
46
        public final int                        DOWNLOAD = 2;
 
47
 
 
48
        protected Socket                        dataChannel = null;
 
49
 
 
50
        public FTP() throws IOException
 
51
        {
 
52
                super();
 
53
                protocol = "ftp";
 
54
                setOptions("DefaultPort=21");
 
55
        }
 
56
 
 
57
        /**
 
58
         * FTP Commands:
 
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.
 
68
         *                              - h1,h2,h3,h4,p1,p2
 
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
 
90
         * HELP SITE
 
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.
 
94
         * HELP UMASK
 
95
         * 502 Unknown command UMASK.
 
96
         * SITE HELP UMASK
 
97
         * 214 Syntax: SITE UMASK [ <sp> umask ]
 
98
         * SITE HELP IDLE
 
99
         * 214 Syntax: SITE IDLE [ <sp> maximum-idle-time ]
 
100
         * SITE HELP CHMOD
 
101
         * 214 Syntax: SITE CHMOD <sp> mode <sp> file-name
 
102
         * SITE HELP HELP
 
103
         * 214 Syntax: SITE HELP [ <sp> <string> ]
 
104
         * SYST - Server OS
 
105
         * STAT [ <path-name>] - Server returns status
 
106
         * HELP [ <help-info>] - Get help on <help-info>.
 
107
         * NOOP - Do nothing (keep alive).
 
108
         */
 
109
        public int getCommandType(String command)
 
110
        {
 
111
                if (str.locate("STOR STOU APPE", command) >= 0)
 
112
                        return(UPLOAD);
 
113
                if (str.locate("RETR LIST NLST", command) >= 0)
 
114
                        return(DOWNLOAD);
 
115
                return(BASIC);
 
116
        }
 
117
 
 
118
        public String getUserName()
 
119
        {
 
120
                String user;
 
121
                
 
122
                user = inputProperties.getProperty("User-Name");
 
123
                if (user == null);
 
124
                        user = locator.getUser();
 
125
                if (user == null)
 
126
                        user = "anonymous";
 
127
                return(user);
 
128
        }
 
129
 
 
130
        public String getPassword()
 
131
        {
 
132
                String password;
 
133
                
 
134
                password = inputProperties.getProperty("Password");
 
135
                if (password == null);
 
136
                        password = locator.getPassword();
 
137
                if (password == null)
 
138
                        password = "bill.gates@microsoft.com";
 
139
                return(password);
 
140
        }
 
141
 
 
142
        public String getPathName()
 
143
        {
 
144
                String file;
 
145
                
 
146
                file = inputProperties.getProperty("Path-Name");
 
147
                if (file == null)
 
148
                        file = locator.getFile();
 
149
                return(file);
 
150
        }
 
151
 
 
152
        public String getTypeCode()
 
153
        {
 
154
                String type = inputProperties.getProperty("Type-Code");
 
155
 
 
156
                if (type == null)
 
157
                        return(DEFAULT_TYPE_CODE);
 
158
                return(type);
 
159
        }
 
160
 
 
161
        public String getCommandParameter(String command)
 
162
        {
 
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)
 
166
                        return("");
 
167
                if (command.equals("RNFR")) {
 
168
                        String file;
 
169
                        
 
170
                        file = inputProperties.getProperty("Rename-From");
 
171
                        if (file == null)
 
172
                                file = getPathName();
 
173
                        return(file);
 
174
                }
 
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();
 
186
                        String port;
 
187
        
 
188
                        host = str.replaceAll(host, ".", ",");
 
189
                        port = (portNo / 256) + "," + (portNo % 256);
 
190
 
 
191
                        return(host+","+port);
 
192
                }
 
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"));
 
207
                return("");
 
208
        }
 
209
 
 
210
        public String getCommandArgument(String command)
 
211
        {
 
212
                String argument;
 
213
 
 
214
                argument = getCommandParameter(command);
 
215
                if (argument.length() > 0) {
 
216
                        return(" "+argument);
 
217
                }
 
218
                return("");
 
219
        }
 
220
 
 
221
        protected int readResponse(boolean haveData) throws IOException, InstantiationException, IllegalAccessException
 
222
        {
 
223
                String  result;
 
224
 
 
225
                result = readLine();
 
226
                if (result == null)
 
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();
 
232
 
 
233
                        for (;;) {
 
234
                                result = readLine();
 
235
                                if (result == null)
 
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!)
 
240
                                         */
 
241
                                        if (str.substr(result, 0, 3).equals(code) &&
 
242
                                                !str.substr(result, 3, 1).equals("-"))
 
243
                                                break;
 
244
                                }
 
245
                                response.append(result);
 
246
                        }
 
247
                        /* PMC - Do not overwrite the data when have data already! */
 
248
                        if (!haveData) {
 
249
                                outputContent = getContentHandler("text/plain");
 
250
                                outputContent.setData(this, response.toString(), null);
 
251
                        }
 
252
                }
 
253
                status = result;
 
254
                return (getStatusType(status));
 
255
        }
 
256
 
 
257
        private static Object portLock = new Object();
 
258
        private static int nextPort = 61234;
 
259
 
 
260
        public int getFreePort()
 
261
        {
 
262
                synchronized (portLock) {
 
263
                        int port = nextPort;
 
264
 
 
265
                        do {
 
266
                                nextPort++;
 
267
                                if (nextPort > 62234)
 
268
                                        nextPort = 61234;
 
269
                        }
 
270
                        while (!ResourceHandler.isPortFree(nextPort));
 
271
                }
 
272
                return(port);
 
273
        }
 
274
 
 
275
        protected int setupTransferType() throws IOException, InstantiationException, IllegalAccessException
 
276
        {
 
277
                String type = getTypeCode();
 
278
 
 
279
                if (currentTypeCode != type) {
 
280
                        currentTypeCode = type;
 
281
                        return(performCommand("TYPE"));
 
282
                }
 
283
                return(OK);
 
284
        }
 
285
 
 
286
        protected Socket prepareDataChannel() throws IOException, InstantiationException, IllegalAccessException
 
287
        {
 
288
                Socket  channel = null;
 
289
                int             portNo = getFreePort();
 
290
 
 
291
                if (setupTransferType() == OK) {
 
292
                        inputProperties.setProperty("Data-Channel-Host", getLocalHost());
 
293
                        inputProperties.setProperty("Data-Channel-Port", portNo);
 
294
        
 
295
                        if (performCommand("PORT") == OK) {
 
296
                                channel = new Socket();
 
297
        
 
298
                                channel.setOptions("ListenPort="+portNo+";BlockOnListen=false");
 
299
                                channel.listen();
 
300
                        }
 
301
                }
 
302
                return(channel);
 
303
        }
 
304
 
 
305
        public int performCommand(String command) throws IOException, InstantiationException, IllegalAccessException
 
306
        {
 
307
                int             result;
 
308
                int             type;
 
309
 
 
310
                type = getCommandType(command);
 
311
 
 
312
                if (type != BASIC) {
 
313
                        dataChannel = prepareDataChannel();
 
314
                        if (dataChannel == null)
 
315
                                return(FAILED);
 
316
                }
 
317
 
 
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!
 
322
                 *
 
323
                 * write(command+getCommandArgument(command)+str.format("\n"));
 
324
                 *
 
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
 
329
                 * manner!
 
330
                 */
 
331
                //PBE.debug(FTP. "+command+getCommandArgument(command));
 
332
                writeLine(command+getCommandArgument(command));
 
333
                result = readResponse(false);
 
334
 
 
335
                if (result == EXCHANGE) {
 
336
                        switch (type) {
 
337
                                case UPLOAD:
 
338
                                        dataChannel.write(inputContent, inputContent.getLength());
 
339
                                        break;
 
340
                                case DOWNLOAD:
 
341
                                        outputContent = getContentHandler("text/plain");
 
342
                                        outputContent.setData(this, dataChannel.getInputStream(), -1, null);
 
343
                                        break;
 
344
                        }
 
345
                        result = readResponse(type == DOWNLOAD);
 
346
                }
 
347
                else {
 
348
                        if (dataChannel != null)
 
349
                                dataChannel.close();
 
350
                }
 
351
        
 
352
                //PBE.debug("FTP: "+this.status);
 
353
                return(result);
 
354
        }
 
355
 
 
356
        public String getMethod()
 
357
        {
 
358
                String m = super.getMethod();
 
359
 
 
360
                if (m == null) {
 
361
                        String file;
 
362
 
 
363
                        if (inputContent != null)
 
364
                                return("STOR");
 
365
                        if (getPathName().endsWith("/"))
 
366
                                return("LIST");
 
367
                        m = "RETR";
 
368
                }
 
369
                return(m);
 
370
        }
 
371
 
 
372
        public int login() throws IOException, InstantiationException, IllegalAccessException
 
373
        {
 
374
                int result = OK;
 
375
 
 
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)";
 
381
                }
 
382
                else if (statusCode(status) == "530")
 
383
                        status = status + " (User unknown)";
 
384
                return(result);
 
385
        }
 
386
 
 
387
        public void connect() throws IOException, InstantiationException, IllegalAccessException
 
388
        {
 
389
                super.connect();
 
390
                if (getStatusType(status) == OK) {
 
391
                        status = readLine();
 
392
                        if (getStatusType(status) == OK) {
 
393
                                outputProperties:setProperty("Greetings", status);
 
394
                                login();
 
395
                        }
 
396
                }
 
397
        }
 
398
 
 
399
        public void syncronize() throws IOException, InstantiationException, IllegalAccessException
 
400
        {
 
401
                performCommand(getMethod());
 
402
        }
 
403
 
 
404
        public void close()
 
405
        {
 
406
                if (isConnected()) {
 
407
                        // Ignore any error
 
408
                        try {
 
409
                                performCommand("QUIT");
 
410
                        }
 
411
                        catch (Exception e) { }
 
412
                }
 
413
                try {
 
414
                        super.close();
 
415
                }
 
416
                catch (Exception e) { }
 
417
        }
 
418
 
 
419
        public void disconnect()
 
420
        {
 
421
                if (dataChannel != null)
 
422
                        dataChannel.close();
 
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.
 
427
                 */
 
428
                if (super.getMethod() == null)
 
429
                        super.disconnect();
 
430
        }
 
431
 
 
432
        public static void register()
 
433
        {
 
434
                register(FTP.class, "ftp");
 
435
        }
 
436
}
 
437