Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/ftp/impl/FtpClient.java
38923 views
1
/*
2
* Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package sun.net.ftp.impl;
26
27
import java.net.*;
28
import java.io.*;
29
import java.security.AccessController;
30
import java.security.PrivilegedAction;
31
import java.text.DateFormat;
32
import java.text.ParseException;
33
import java.text.SimpleDateFormat;
34
import java.util.ArrayList;
35
import java.util.Calendar;
36
import java.util.Date;
37
import java.util.Iterator;
38
import java.util.List;
39
import java.util.TimeZone;
40
import java.util.Vector;
41
import java.util.regex.Matcher;
42
import java.util.regex.Pattern;
43
import javax.net.ssl.SSLSocket;
44
import javax.net.ssl.SSLSocketFactory;
45
import sun.misc.BASE64Decoder;
46
import sun.misc.BASE64Encoder;
47
import sun.net.ftp.*;
48
import sun.util.logging.PlatformLogger;
49
50
51
public class FtpClient extends sun.net.ftp.FtpClient {
52
53
private static int defaultSoTimeout;
54
private static int defaultConnectTimeout;
55
private static final PlatformLogger logger =
56
PlatformLogger.getLogger("sun.net.ftp.FtpClient");
57
private Proxy proxy;
58
private Socket server;
59
private PrintStream out;
60
private InputStream in;
61
private int readTimeout = -1;
62
private int connectTimeout = -1;
63
64
/* Name of encoding to use for output */
65
private static String encoding = "ISO8859_1";
66
/** remember the ftp server name because we may need it */
67
private InetSocketAddress serverAddr;
68
private boolean replyPending = false;
69
private boolean loggedIn = false;
70
private boolean useCrypto = false;
71
private SSLSocketFactory sslFact;
72
private Socket oldSocket;
73
/** Array of strings (usually 1 entry) for the last reply from the server. */
74
private Vector<String> serverResponse = new Vector<String>(1);
75
/** The last reply code from the ftp daemon. */
76
private FtpReplyCode lastReplyCode = null;
77
/** Welcome message from the server, if any. */
78
private String welcomeMsg;
79
/**
80
* Only passive mode used in JDK. See Bug 8010784.
81
*/
82
private final boolean passiveMode = true;
83
private TransferType type = TransferType.BINARY;
84
private long restartOffset = 0;
85
private long lastTransSize = -1; // -1 means 'unknown size'
86
private String lastFileName;
87
/**
88
* Static members used by the parser
89
*/
90
private static String[] patStrings = {
91
// drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
92
"([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d\\d:\\d\\d)\\s*(\\p{Print}*)",
93
// drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog
94
"([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d{4})\\s*(\\p{Print}*)",
95
// 04/28/2006 09:12a 3,563 genBuffer.sh
96
"(\\d{2}/\\d{2}/\\d{4})\\s*(\\d{2}:\\d{2}[ap])\\s*((?:[0-9,]+)|(?:<DIR>))\\s*(\\p{Graph}*)",
97
// 01-29-97 11:32PM <DIR> prog
98
"(\\d{2}-\\d{2}-\\d{2})\\s*(\\d{2}:\\d{2}[AP]M)\\s*((?:[0-9,]+)|(?:<DIR>))\\s*(\\p{Graph}*)"
99
};
100
private static int[][] patternGroups = {
101
// 0 - file, 1 - size, 2 - date, 3 - time, 4 - year, 5 - permissions,
102
// 6 - user, 7 - group
103
{7, 4, 5, 6, 0, 1, 2, 3},
104
{7, 4, 5, 0, 6, 1, 2, 3},
105
{4, 3, 1, 2, 0, 0, 0, 0},
106
{4, 3, 1, 2, 0, 0, 0, 0}};
107
private static Pattern[] patterns;
108
private static Pattern linkp = Pattern.compile("(\\p{Print}+) \\-\\> (\\p{Print}+)$");
109
private DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, java.util.Locale.US);
110
111
static {
112
final int vals[] = {0, 0};
113
final String encs[] = {null};
114
115
AccessController.doPrivileged(
116
new PrivilegedAction<Object>() {
117
118
public Object run() {
119
vals[0] = Integer.getInteger("sun.net.client.defaultReadTimeout", 300_000).intValue();
120
vals[1] = Integer.getInteger("sun.net.client.defaultConnectTimeout", 300_000).intValue();
121
encs[0] = System.getProperty("file.encoding", "ISO8859_1");
122
return null;
123
}
124
});
125
if (vals[0] == 0) {
126
defaultSoTimeout = -1;
127
} else {
128
defaultSoTimeout = vals[0];
129
}
130
131
if (vals[1] == 0) {
132
defaultConnectTimeout = -1;
133
} else {
134
defaultConnectTimeout = vals[1];
135
}
136
137
encoding = encs[0];
138
try {
139
if (!isASCIISuperset(encoding)) {
140
encoding = "ISO8859_1";
141
}
142
} catch (Exception e) {
143
encoding = "ISO8859_1";
144
}
145
146
patterns = new Pattern[patStrings.length];
147
for (int i = 0; i < patStrings.length; i++) {
148
patterns[i] = Pattern.compile(patStrings[i]);
149
}
150
}
151
152
/**
153
* Test the named character encoding to verify that it converts ASCII
154
* characters correctly. We have to use an ASCII based encoding, or else
155
* the NetworkClients will not work correctly in EBCDIC based systems.
156
* However, we cannot just use ASCII or ISO8859_1 universally, because in
157
* Asian locales, non-ASCII characters may be embedded in otherwise
158
* ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398)
159
* are a little ambiguous in this matter. For instance, RFC2398 [part 2.1]
160
* says that the HTTP request URI should be escaped using a defined
161
* mechanism, but there is no way to specify in the escaped string what
162
* the original character set is. It is not correct to assume that
163
* UTF-8 is always used (as in URLs in HTML 4.0). For this reason,
164
* until the specifications are updated to deal with this issue more
165
* comprehensively, and more importantly, HTTP servers are known to
166
* support these mechanisms, we will maintain the current behavior
167
* where it is possible to send non-ASCII characters in their original
168
* unescaped form.
169
*/
170
private static boolean isASCIISuperset(String encoding) throws Exception {
171
String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
172
"abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,";
173
174
// Expected byte sequence for string above
175
byte[] chkB = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72,
176
73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99,
177
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
178
115, 116, 117, 118, 119, 120, 121, 122, 45, 95, 46, 33, 126, 42, 39, 40, 41, 59,
179
47, 63, 58, 64, 38, 61, 43, 36, 44};
180
181
byte[] b = chkS.getBytes(encoding);
182
return java.util.Arrays.equals(b, chkB);
183
}
184
185
private class DefaultParser implements FtpDirParser {
186
187
/**
188
* Possible patterns:
189
*
190
* drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
191
* drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog
192
* drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog
193
* lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000
194
* drwxr-xr-x 1 username ftp 512 Jan 29 23:32 prog
195
* -rw-r--r-- 1 jcc staff 105009 Feb 3 15:05 test.1
196
*
197
* 01-29-97 11:32PM <DIR> prog
198
* 04/28/2006 09:12a 3,563 genBuffer.sh
199
*
200
* drwxr-xr-x folder 0 Jan 29 23:32 prog
201
*
202
* 0 DIR 01-29-97 23:32 PROG
203
*/
204
private DefaultParser() {
205
}
206
207
public FtpDirEntry parseLine(String line) {
208
String fdate = null;
209
String fsize = null;
210
String time = null;
211
String filename = null;
212
String permstring = null;
213
String username = null;
214
String groupname = null;
215
boolean dir = false;
216
Calendar now = Calendar.getInstance();
217
int year = now.get(Calendar.YEAR);
218
219
Matcher m = null;
220
for (int j = 0; j < patterns.length; j++) {
221
m = patterns[j].matcher(line);
222
if (m.find()) {
223
// 0 - file, 1 - size, 2 - date, 3 - time, 4 - year,
224
// 5 - permissions, 6 - user, 7 - group
225
filename = m.group(patternGroups[j][0]);
226
fsize = m.group(patternGroups[j][1]);
227
fdate = m.group(patternGroups[j][2]);
228
if (patternGroups[j][4] > 0) {
229
fdate += (", " + m.group(patternGroups[j][4]));
230
} else if (patternGroups[j][3] > 0) {
231
fdate += (", " + String.valueOf(year));
232
}
233
if (patternGroups[j][3] > 0) {
234
time = m.group(patternGroups[j][3]);
235
}
236
if (patternGroups[j][5] > 0) {
237
permstring = m.group(patternGroups[j][5]);
238
dir = permstring.startsWith("d");
239
}
240
if (patternGroups[j][6] > 0) {
241
username = m.group(patternGroups[j][6]);
242
}
243
if (patternGroups[j][7] > 0) {
244
groupname = m.group(patternGroups[j][7]);
245
}
246
// Old DOS format
247
if ("<DIR>".equals(fsize)) {
248
dir = true;
249
fsize = null;
250
}
251
}
252
}
253
254
if (filename != null) {
255
Date d;
256
try {
257
d = df.parse(fdate);
258
} catch (Exception e) {
259
d = null;
260
}
261
if (d != null && time != null) {
262
int c = time.indexOf(":");
263
now.setTime(d);
264
now.set(Calendar.HOUR, Integer.parseInt(time.substring(0, c)));
265
now.set(Calendar.MINUTE, Integer.parseInt(time.substring(c + 1)));
266
d = now.getTime();
267
}
268
// see if it's a symbolic link, i.e. the name if followed
269
// by a -> and a path
270
Matcher m2 = linkp.matcher(filename);
271
if (m2.find()) {
272
// Keep only the name then
273
filename = m2.group(1);
274
}
275
boolean[][] perms = new boolean[3][3];
276
for (int i = 0; i < 3; i++) {
277
for (int j = 0; j < 3; j++) {
278
perms[i][j] = (permstring.charAt((i * 3) + j) != '-');
279
}
280
}
281
FtpDirEntry file = new FtpDirEntry(filename);
282
file.setUser(username).setGroup(groupname);
283
file.setSize(Long.parseLong(fsize)).setLastModified(d);
284
file.setPermissions(perms);
285
file.setType(dir ? FtpDirEntry.Type.DIR : (line.charAt(0) == 'l' ? FtpDirEntry.Type.LINK : FtpDirEntry.Type.FILE));
286
return file;
287
}
288
return null;
289
}
290
}
291
292
private class MLSxParser implements FtpDirParser {
293
294
private SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmss");
295
296
public FtpDirEntry parseLine(String line) {
297
String name = null;
298
int i = line.lastIndexOf(";");
299
if (i > 0) {
300
name = line.substring(i + 1).trim();
301
line = line.substring(0, i);
302
} else {
303
name = line.trim();
304
line = "";
305
}
306
FtpDirEntry file = new FtpDirEntry(name);
307
while (!line.isEmpty()) {
308
String s;
309
i = line.indexOf(";");
310
if (i > 0) {
311
s = line.substring(0, i);
312
line = line.substring(i + 1);
313
} else {
314
s = line;
315
line = "";
316
}
317
i = s.indexOf("=");
318
if (i > 0) {
319
String fact = s.substring(0, i);
320
String value = s.substring(i + 1);
321
file.addFact(fact, value);
322
}
323
}
324
String s = file.getFact("Size");
325
if (s != null) {
326
file.setSize(Long.parseLong(s));
327
}
328
s = file.getFact("Modify");
329
if (s != null) {
330
Date d = null;
331
try {
332
d = df.parse(s);
333
} catch (ParseException ex) {
334
}
335
if (d != null) {
336
file.setLastModified(d);
337
}
338
}
339
s = file.getFact("Create");
340
if (s != null) {
341
Date d = null;
342
try {
343
d = df.parse(s);
344
} catch (ParseException ex) {
345
}
346
if (d != null) {
347
file.setCreated(d);
348
}
349
}
350
s = file.getFact("Type");
351
if (s != null) {
352
if (s.equalsIgnoreCase("file")) {
353
file.setType(FtpDirEntry.Type.FILE);
354
}
355
if (s.equalsIgnoreCase("dir")) {
356
file.setType(FtpDirEntry.Type.DIR);
357
}
358
if (s.equalsIgnoreCase("cdir")) {
359
file.setType(FtpDirEntry.Type.CDIR);
360
}
361
if (s.equalsIgnoreCase("pdir")) {
362
file.setType(FtpDirEntry.Type.PDIR);
363
}
364
}
365
return file;
366
}
367
};
368
private FtpDirParser parser = new DefaultParser();
369
private FtpDirParser mlsxParser = new MLSxParser();
370
private static Pattern transPat = null;
371
372
private void getTransferSize() {
373
lastTransSize = -1;
374
/**
375
* If it's a start of data transfer response, let's try to extract
376
* the size from the response string. Usually it looks like that:
377
*
378
* 150 Opening BINARY mode data connection for foo (6701 bytes).
379
*/
380
String response = getLastResponseString();
381
if (transPat == null) {
382
transPat = Pattern.compile("150 Opening .*\\((\\d+) bytes\\).");
383
}
384
Matcher m = transPat.matcher(response);
385
if (m.find()) {
386
String s = m.group(1);
387
lastTransSize = Long.parseLong(s);
388
}
389
}
390
391
/**
392
* extract the created file name from the response string:
393
* 226 Transfer complete (unique file name:toto.txt.1).
394
* Usually happens when a STOU (store unique) command had been issued.
395
*/
396
private void getTransferName() {
397
lastFileName = null;
398
String response = getLastResponseString();
399
int i = response.indexOf("unique file name:");
400
int e = response.lastIndexOf(')');
401
if (i >= 0) {
402
i += 17; // Length of "unique file name:"
403
lastFileName = response.substring(i, e);
404
}
405
}
406
407
/**
408
* Pulls the response from the server and returns the code as a
409
* number. Returns -1 on failure.
410
*/
411
private int readServerResponse() throws IOException {
412
StringBuffer replyBuf = new StringBuffer(32);
413
int c;
414
int continuingCode = -1;
415
int code;
416
String response;
417
418
serverResponse.setSize(0);
419
while (true) {
420
while ((c = in.read()) != -1) {
421
if (c == '\r') {
422
if ((c = in.read()) != '\n') {
423
replyBuf.append('\r');
424
}
425
}
426
replyBuf.append((char) c);
427
if (c == '\n') {
428
break;
429
}
430
}
431
response = replyBuf.toString();
432
replyBuf.setLength(0);
433
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
434
logger.finest("Server [" + serverAddr + "] --> " + response);
435
}
436
437
if (response.length() == 0) {
438
code = -1;
439
} else {
440
try {
441
code = Integer.parseInt(response.substring(0, 3));
442
} catch (NumberFormatException e) {
443
code = -1;
444
} catch (StringIndexOutOfBoundsException e) {
445
/* this line doesn't contain a response code, so
446
we just completely ignore it */
447
continue;
448
}
449
}
450
serverResponse.addElement(response);
451
if (continuingCode != -1) {
452
/* we've seen a ###- sequence */
453
if (code != continuingCode ||
454
(response.length() >= 4 && response.charAt(3) == '-')) {
455
continue;
456
} else {
457
/* seen the end of code sequence */
458
continuingCode = -1;
459
break;
460
}
461
} else if (response.length() >= 4 && response.charAt(3) == '-') {
462
continuingCode = code;
463
continue;
464
} else {
465
break;
466
}
467
}
468
469
return code;
470
}
471
472
/** Sends command <i>cmd</i> to the server. */
473
private void sendServer(String cmd) {
474
out.print(cmd);
475
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
476
logger.finest("Server [" + serverAddr + "] <-- " + cmd);
477
}
478
}
479
480
/** converts the server response into a string. */
481
private String getResponseString() {
482
return serverResponse.elementAt(0);
483
}
484
485
/** Returns all server response strings. */
486
private Vector<String> getResponseStrings() {
487
return serverResponse;
488
}
489
490
/**
491
* Read the reply from the FTP server.
492
*
493
* @return <code>true</code> if the command was successful
494
* @throws IOException if an error occurred
495
*/
496
private boolean readReply() throws IOException {
497
lastReplyCode = FtpReplyCode.find(readServerResponse());
498
499
if (lastReplyCode.isPositivePreliminary()) {
500
replyPending = true;
501
return true;
502
}
503
if (lastReplyCode.isPositiveCompletion() || lastReplyCode.isPositiveIntermediate()) {
504
if (lastReplyCode == FtpReplyCode.CLOSING_DATA_CONNECTION) {
505
getTransferName();
506
}
507
return true;
508
}
509
return false;
510
}
511
512
/**
513
* Sends a command to the FTP server and returns the error code
514
* (which can be a "success") sent by the server.
515
*
516
* @param cmd
517
* @return <code>true</code> if the command was successful
518
* @throws IOException
519
*/
520
private boolean issueCommand(String cmd) throws IOException,
521
sun.net.ftp.FtpProtocolException {
522
if (!isConnected()) {
523
throw new IllegalStateException("Not connected");
524
}
525
if (replyPending) {
526
try {
527
completePending();
528
} catch (sun.net.ftp.FtpProtocolException e) {
529
// ignore...
530
}
531
}
532
if (cmd.indexOf('\n') != -1) {
533
sun.net.ftp.FtpProtocolException ex
534
= new sun.net.ftp.FtpProtocolException("Illegal FTP command");
535
ex.initCause(new IllegalArgumentException("Illegal carriage return"));
536
throw ex;
537
}
538
sendServer(cmd + "\r\n");
539
return readReply();
540
}
541
542
/**
543
* Send a command to the FTP server and check for success.
544
*
545
* @param cmd String containing the command
546
*
547
* @throws FtpProtocolException if an error occurred
548
*/
549
private void issueCommandCheck(String cmd) throws sun.net.ftp.FtpProtocolException, IOException {
550
if (!issueCommand(cmd)) {
551
throw new sun.net.ftp.FtpProtocolException(cmd + ":" + getResponseString(), getLastReplyCode());
552
}
553
}
554
private static Pattern epsvPat = null;
555
private static Pattern pasvPat = null;
556
557
/**
558
* Opens a "PASSIVE" connection with the server and returns the connected
559
* <code>Socket</code>.
560
*
561
* @return the connected <code>Socket</code>
562
* @throws IOException if the connection was unsuccessful.
563
*/
564
private Socket openPassiveDataConnection(String cmd) throws sun.net.ftp.FtpProtocolException, IOException {
565
String serverAnswer;
566
int port;
567
InetSocketAddress dest = null;
568
569
/**
570
* Here is the idea:
571
*
572
* - First we want to try the new (and IPv6 compatible) EPSV command
573
* But since we want to be nice with NAT software, we'll issue the
574
* EPSV ALL command first.
575
* EPSV is documented in RFC2428
576
* - If EPSV fails, then we fall back to the older, yet ok, PASV
577
* - If PASV fails as well, then we throw an exception and the calling
578
* method will have to try the EPRT or PORT command
579
*/
580
if (issueCommand("EPSV ALL")) {
581
// We can safely use EPSV commands
582
issueCommandCheck("EPSV");
583
serverAnswer = getResponseString();
584
585
// The response string from a EPSV command will contain the port number
586
// the format will be :
587
// 229 Entering Extended PASSIVE Mode (|||58210|)
588
//
589
// So we'll use the regular expresions package to parse the output.
590
591
if (epsvPat == null) {
592
epsvPat = Pattern.compile("^229 .* \\(\\|\\|\\|(\\d+)\\|\\)");
593
}
594
Matcher m = epsvPat.matcher(serverAnswer);
595
if (!m.find()) {
596
throw new sun.net.ftp.FtpProtocolException("EPSV failed : " + serverAnswer);
597
}
598
// Yay! Let's extract the port number
599
String s = m.group(1);
600
port = Integer.parseInt(s);
601
InetAddress add = server.getInetAddress();
602
if (add != null) {
603
dest = new InetSocketAddress(add, port);
604
} else {
605
// This means we used an Unresolved address to connect in
606
// the first place. Most likely because the proxy is doing
607
// the name resolution for us, so let's keep using unresolved
608
// address.
609
dest = InetSocketAddress.createUnresolved(serverAddr.getHostName(), port);
610
}
611
} else {
612
// EPSV ALL failed, so Let's try the regular PASV cmd
613
issueCommandCheck("PASV");
614
serverAnswer = getResponseString();
615
616
// Let's parse the response String to get the IP & port to connect
617
// to. The String should be in the following format :
618
//
619
// 227 Entering PASSIVE Mode (A1,A2,A3,A4,p1,p2)
620
//
621
// Note that the two parenthesis are optional
622
//
623
// The IP address is A1.A2.A3.A4 and the port is p1 * 256 + p2
624
//
625
// The regular expression is a bit more complex this time, because
626
// the parenthesis are optionals and we have to use 3 groups.
627
628
if (pasvPat == null) {
629
pasvPat = Pattern.compile("227 .* \\(?(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)?");
630
}
631
Matcher m = pasvPat.matcher(serverAnswer);
632
if (!m.find()) {
633
throw new sun.net.ftp.FtpProtocolException("PASV failed : " + serverAnswer);
634
}
635
// Get port number out of group 2 & 3
636
port = Integer.parseInt(m.group(3)) + (Integer.parseInt(m.group(2)) << 8);
637
// IP address is simple
638
String s = m.group(1).replace(',', '.');
639
dest = new InetSocketAddress(s, port);
640
}
641
// Got everything, let's open the socket!
642
Socket s;
643
if (proxy != null) {
644
if (proxy.type() == Proxy.Type.SOCKS) {
645
s = AccessController.doPrivileged(
646
new PrivilegedAction<Socket>() {
647
648
public Socket run() {
649
return new Socket(proxy);
650
}
651
});
652
} else {
653
s = new Socket(Proxy.NO_PROXY);
654
}
655
} else {
656
s = new Socket();
657
}
658
659
InetAddress serverAddress = AccessController.doPrivileged(
660
new PrivilegedAction<InetAddress>() {
661
@Override
662
public InetAddress run() {
663
return server.getLocalAddress();
664
}
665
});
666
667
// Bind the socket to the same address as the control channel. This
668
// is needed in case of multi-homed systems.
669
s.bind(new InetSocketAddress(serverAddress, 0));
670
if (connectTimeout >= 0) {
671
s.connect(dest, connectTimeout);
672
} else {
673
if (defaultConnectTimeout > 0) {
674
s.connect(dest, defaultConnectTimeout);
675
} else {
676
s.connect(dest);
677
}
678
}
679
if (readTimeout >= 0) {
680
s.setSoTimeout(readTimeout);
681
} else if (defaultSoTimeout > 0) {
682
s.setSoTimeout(defaultSoTimeout);
683
}
684
if (useCrypto) {
685
try {
686
s = sslFact.createSocket(s, dest.getHostName(), dest.getPort(), true);
687
} catch (Exception e) {
688
throw new sun.net.ftp.FtpProtocolException("Can't open secure data channel: " + e);
689
}
690
}
691
if (!issueCommand(cmd)) {
692
s.close();
693
if (getLastReplyCode() == FtpReplyCode.FILE_UNAVAILABLE) {
694
// Ensure backward compatibility
695
throw new FileNotFoundException(cmd);
696
}
697
throw new sun.net.ftp.FtpProtocolException(cmd + ":" + getResponseString(), getLastReplyCode());
698
}
699
return s;
700
}
701
702
/**
703
* Opens a data connection with the server according to the set mode
704
* (ACTIVE or PASSIVE) then send the command passed as an argument.
705
*
706
* @param cmd the <code>String</code> containing the command to execute
707
* @return the connected <code>Socket</code>
708
* @throws IOException if the connection or command failed
709
*/
710
private Socket openDataConnection(String cmd) throws sun.net.ftp.FtpProtocolException, IOException {
711
Socket clientSocket;
712
713
if (passiveMode) {
714
try {
715
return openPassiveDataConnection(cmd);
716
} catch (sun.net.ftp.FtpProtocolException e) {
717
// If Passive mode failed, fall back on PORT
718
// Otherwise throw exception
719
String errmsg = e.getMessage();
720
if (!errmsg.startsWith("PASV") && !errmsg.startsWith("EPSV")) {
721
throw e;
722
}
723
}
724
}
725
ServerSocket portSocket;
726
InetAddress myAddress;
727
String portCmd;
728
729
if (proxy != null && proxy.type() == Proxy.Type.SOCKS) {
730
// We're behind a firewall and the passive mode fail,
731
// since we can't accept a connection through SOCKS (yet)
732
// throw an exception
733
throw new sun.net.ftp.FtpProtocolException("Passive mode failed");
734
}
735
// Bind the ServerSocket to the same address as the control channel
736
// This is needed for multi-homed systems
737
portSocket = new ServerSocket(0, 1, server.getLocalAddress());
738
try {
739
myAddress = portSocket.getInetAddress();
740
if (myAddress.isAnyLocalAddress()) {
741
myAddress = server.getLocalAddress();
742
}
743
// Let's try the new, IPv6 compatible EPRT command
744
// See RFC2428 for specifics
745
// Some FTP servers (like the one on Solaris) are bugged, they
746
// will accept the EPRT command but then, the subsequent command
747
// (e.g. RETR) will fail, so we have to check BOTH results (the
748
// EPRT cmd then the actual command) to decide whether we should
749
// fall back on the older PORT command.
750
portCmd = "EPRT |" + ((myAddress instanceof Inet6Address) ? "2" : "1") + "|" +
751
myAddress.getHostAddress() + "|" + portSocket.getLocalPort() + "|";
752
if (!issueCommand(portCmd) || !issueCommand(cmd)) {
753
// The EPRT command failed, let's fall back to good old PORT
754
portCmd = "PORT ";
755
byte[] addr = myAddress.getAddress();
756
757
/* append host addr */
758
for (int i = 0; i < addr.length; i++) {
759
portCmd = portCmd + (addr[i] & 0xFF) + ",";
760
}
761
762
/* append port number */
763
portCmd = portCmd + ((portSocket.getLocalPort() >>> 8) & 0xff) + "," + (portSocket.getLocalPort() & 0xff);
764
issueCommandCheck(portCmd);
765
issueCommandCheck(cmd);
766
}
767
// Either the EPRT or the PORT command was successful
768
// Let's create the client socket
769
if (connectTimeout >= 0) {
770
portSocket.setSoTimeout(connectTimeout);
771
} else {
772
if (defaultConnectTimeout > 0) {
773
portSocket.setSoTimeout(defaultConnectTimeout);
774
}
775
}
776
clientSocket = portSocket.accept();
777
if (readTimeout >= 0) {
778
clientSocket.setSoTimeout(readTimeout);
779
} else {
780
if (defaultSoTimeout > 0) {
781
clientSocket.setSoTimeout(defaultSoTimeout);
782
}
783
}
784
} finally {
785
portSocket.close();
786
}
787
if (useCrypto) {
788
try {
789
clientSocket = sslFact.createSocket(clientSocket, serverAddr.getHostName(), serverAddr.getPort(), true);
790
} catch (Exception ex) {
791
throw new IOException(ex.getLocalizedMessage());
792
}
793
}
794
return clientSocket;
795
}
796
797
private InputStream createInputStream(InputStream in) {
798
if (type == TransferType.ASCII) {
799
return new sun.net.TelnetInputStream(in, false);
800
}
801
return in;
802
}
803
804
private OutputStream createOutputStream(OutputStream out) {
805
if (type == TransferType.ASCII) {
806
return new sun.net.TelnetOutputStream(out, false);
807
}
808
return out;
809
}
810
811
/**
812
* Creates an instance of FtpClient. The client is not connected to any
813
* server yet.
814
*
815
*/
816
protected FtpClient() {
817
}
818
819
/**
820
* Creates an instance of FtpClient. The client is not connected to any
821
* server yet.
822
*
823
*/
824
public static sun.net.ftp.FtpClient create() {
825
return new FtpClient();
826
}
827
828
/**
829
* Set the transfer mode to <I>passive</I>. In that mode, data connections
830
* are established by having the client connect to the server.
831
* This is the recommended default mode as it will work best through
832
* firewalls and NATs.
833
*
834
* @return This FtpClient
835
* @see #setActiveMode()
836
*/
837
public sun.net.ftp.FtpClient enablePassiveMode(boolean passive) {
838
839
// Only passive mode used in JDK. See Bug 8010784.
840
// passiveMode = passive;
841
return this;
842
}
843
844
/**
845
* Gets the current transfer mode.
846
*
847
* @return the current <code>FtpTransferMode</code>
848
*/
849
public boolean isPassiveModeEnabled() {
850
return passiveMode;
851
}
852
853
/**
854
* Sets the timeout value to use when connecting to the server,
855
*
856
* @param timeout the timeout value, in milliseconds, to use for the connect
857
* operation. A value of zero or less, means use the default timeout.
858
*
859
* @return This FtpClient
860
*/
861
public sun.net.ftp.FtpClient setConnectTimeout(int timeout) {
862
connectTimeout = timeout;
863
return this;
864
}
865
866
/**
867
* Returns the current connection timeout value.
868
*
869
* @return the value, in milliseconds, of the current connect timeout.
870
* @see #setConnectTimeout(int)
871
*/
872
public int getConnectTimeout() {
873
return connectTimeout;
874
}
875
876
/**
877
* Sets the timeout value to use when reading from the server,
878
*
879
* @param timeout the timeout value, in milliseconds, to use for the read
880
* operation. A value of zero or less, means use the default timeout.
881
* @return This FtpClient
882
*/
883
public sun.net.ftp.FtpClient setReadTimeout(int timeout) {
884
readTimeout = timeout;
885
return this;
886
}
887
888
/**
889
* Returns the current read timeout value.
890
*
891
* @return the value, in milliseconds, of the current read timeout.
892
* @see #setReadTimeout(int)
893
*/
894
public int getReadTimeout() {
895
return readTimeout;
896
}
897
898
public sun.net.ftp.FtpClient setProxy(Proxy p) {
899
proxy = p;
900
return this;
901
}
902
903
/**
904
* Get the proxy of this FtpClient
905
*
906
* @return the <code>Proxy</code>, this client is using, or <code>null</code>
907
* if none is used.
908
* @see #setProxy(Proxy)
909
*/
910
public Proxy getProxy() {
911
return proxy;
912
}
913
914
/**
915
* Connects to the specified destination.
916
*
917
* @param dest the <code>InetSocketAddress</code> to connect to.
918
* @throws IOException if the connection fails.
919
*/
920
private void tryConnect(InetSocketAddress dest, int timeout) throws IOException {
921
if (isConnected()) {
922
disconnect();
923
}
924
server = doConnect(dest, timeout);
925
try {
926
out = new PrintStream(new BufferedOutputStream(server.getOutputStream()),
927
true, encoding);
928
} catch (UnsupportedEncodingException e) {
929
throw new InternalError(encoding + "encoding not found", e);
930
}
931
in = new BufferedInputStream(server.getInputStream());
932
}
933
934
private Socket doConnect(InetSocketAddress dest, int timeout) throws IOException {
935
Socket s;
936
if (proxy != null) {
937
if (proxy.type() == Proxy.Type.SOCKS) {
938
s = AccessController.doPrivileged(
939
new PrivilegedAction<Socket>() {
940
941
public Socket run() {
942
return new Socket(proxy);
943
}
944
});
945
} else {
946
s = new Socket(Proxy.NO_PROXY);
947
}
948
} else {
949
s = new Socket();
950
}
951
// Instance specific timeouts do have priority, that means
952
// connectTimeout & readTimeout (-1 means not set)
953
// Then global default timeouts
954
// Then no timeout.
955
if (timeout >= 0) {
956
s.connect(dest, timeout);
957
} else {
958
if (connectTimeout >= 0) {
959
s.connect(dest, connectTimeout);
960
} else {
961
if (defaultConnectTimeout > 0) {
962
s.connect(dest, defaultConnectTimeout);
963
} else {
964
s.connect(dest);
965
}
966
}
967
}
968
if (readTimeout >= 0) {
969
s.setSoTimeout(readTimeout);
970
} else if (defaultSoTimeout > 0) {
971
s.setSoTimeout(defaultSoTimeout);
972
}
973
return s;
974
}
975
976
private void disconnect() throws IOException {
977
if (isConnected()) {
978
server.close();
979
}
980
server = null;
981
in = null;
982
out = null;
983
lastTransSize = -1;
984
lastFileName = null;
985
restartOffset = 0;
986
welcomeMsg = null;
987
lastReplyCode = null;
988
serverResponse.setSize(0);
989
}
990
991
/**
992
* Tests whether this client is connected or not to a server.
993
*
994
* @return <code>true</code> if the client is connected.
995
*/
996
public boolean isConnected() {
997
return server != null;
998
}
999
1000
public SocketAddress getServerAddress() {
1001
return server == null ? null : server.getRemoteSocketAddress();
1002
}
1003
1004
public sun.net.ftp.FtpClient connect(SocketAddress dest) throws sun.net.ftp.FtpProtocolException, IOException {
1005
return connect(dest, -1);
1006
}
1007
1008
/**
1009
* Connects the FtpClient to the specified destination.
1010
*
1011
* @param dest the address of the destination server
1012
* @throws IOException if connection failed.
1013
*/
1014
public sun.net.ftp.FtpClient connect(SocketAddress dest, int timeout) throws sun.net.ftp.FtpProtocolException, IOException {
1015
if (!(dest instanceof InetSocketAddress)) {
1016
throw new IllegalArgumentException("Wrong address type");
1017
}
1018
serverAddr = (InetSocketAddress) dest;
1019
tryConnect(serverAddr, timeout);
1020
if (!readReply()) {
1021
throw new sun.net.ftp.FtpProtocolException("Welcome message: " +
1022
getResponseString(), lastReplyCode);
1023
}
1024
welcomeMsg = getResponseString().substring(4);
1025
return this;
1026
}
1027
1028
private void tryLogin(String user, char[] password) throws sun.net.ftp.FtpProtocolException, IOException {
1029
issueCommandCheck("USER " + user);
1030
1031
/*
1032
* Checks for "331 User name okay, need password." answer
1033
*/
1034
if (lastReplyCode == FtpReplyCode.NEED_PASSWORD) {
1035
if ((password != null) && (password.length > 0)) {
1036
issueCommandCheck("PASS " + String.valueOf(password));
1037
}
1038
}
1039
}
1040
1041
/**
1042
* Attempts to log on the server with the specified user name and password.
1043
*
1044
* @param user The user name
1045
* @param password The password for that user
1046
* @return <code>true</code> if the login was successful.
1047
* @throws IOException if an error occurred during the transmission
1048
*/
1049
public sun.net.ftp.FtpClient login(String user, char[] password) throws sun.net.ftp.FtpProtocolException, IOException {
1050
if (!isConnected()) {
1051
throw new sun.net.ftp.FtpProtocolException("Not connected yet", FtpReplyCode.BAD_SEQUENCE);
1052
}
1053
if (user == null || user.length() == 0) {
1054
throw new IllegalArgumentException("User name can't be null or empty");
1055
}
1056
tryLogin(user, password);
1057
1058
// keep the welcome message around so we can
1059
// put it in the resulting HTML page.
1060
String l;
1061
StringBuffer sb = new StringBuffer();
1062
for (int i = 0; i < serverResponse.size(); i++) {
1063
l = serverResponse.elementAt(i);
1064
if (l != null) {
1065
if (l.length() >= 4 && l.startsWith("230")) {
1066
// get rid of the "230-" prefix
1067
l = l.substring(4);
1068
}
1069
sb.append(l);
1070
}
1071
}
1072
welcomeMsg = sb.toString();
1073
loggedIn = true;
1074
return this;
1075
}
1076
1077
/**
1078
* Attempts to log on the server with the specified user name, password and
1079
* account name.
1080
*
1081
* @param user The user name
1082
* @param password The password for that user.
1083
* @param account The account name for that user.
1084
* @return <code>true</code> if the login was successful.
1085
* @throws IOException if an error occurs during the transmission.
1086
*/
1087
public sun.net.ftp.FtpClient login(String user, char[] password, String account) throws sun.net.ftp.FtpProtocolException, IOException {
1088
1089
if (!isConnected()) {
1090
throw new sun.net.ftp.FtpProtocolException("Not connected yet", FtpReplyCode.BAD_SEQUENCE);
1091
}
1092
if (user == null || user.length() == 0) {
1093
throw new IllegalArgumentException("User name can't be null or empty");
1094
}
1095
tryLogin(user, password);
1096
1097
/*
1098
* Checks for "332 Need account for login." answer
1099
*/
1100
if (lastReplyCode == FtpReplyCode.NEED_ACCOUNT) {
1101
issueCommandCheck("ACCT " + account);
1102
}
1103
1104
// keep the welcome message around so we can
1105
// put it in the resulting HTML page.
1106
StringBuffer sb = new StringBuffer();
1107
if (serverResponse != null) {
1108
for (String l : serverResponse) {
1109
if (l != null) {
1110
if (l.length() >= 4 && l.startsWith("230")) {
1111
// get rid of the "230-" prefix
1112
l = l.substring(4);
1113
}
1114
sb.append(l);
1115
}
1116
}
1117
}
1118
welcomeMsg = sb.toString();
1119
loggedIn = true;
1120
return this;
1121
}
1122
1123
/**
1124
* Logs out the current user. This is in effect terminates the current
1125
* session and the connection to the server will be closed.
1126
*
1127
*/
1128
public void close() throws IOException {
1129
if (isConnected()) {
1130
try {
1131
issueCommand("QUIT");
1132
} catch (FtpProtocolException e) {
1133
}
1134
loggedIn = false;
1135
}
1136
disconnect();
1137
}
1138
1139
/**
1140
* Checks whether the client is logged in to the server or not.
1141
*
1142
* @return <code>true</code> if the client has already completed a login.
1143
*/
1144
public boolean isLoggedIn() {
1145
return loggedIn;
1146
}
1147
1148
/**
1149
* Changes to a specific directory on a remote FTP server
1150
*
1151
* @param remoteDirectory path of the directory to CD to.
1152
* @return <code>true</code> if the operation was successful.
1153
* @exception <code>FtpProtocolException</code>
1154
*/
1155
public sun.net.ftp.FtpClient changeDirectory(String remoteDirectory) throws sun.net.ftp.FtpProtocolException, IOException {
1156
if (remoteDirectory == null || "".equals(remoteDirectory)) {
1157
throw new IllegalArgumentException("directory can't be null or empty");
1158
}
1159
1160
issueCommandCheck("CWD " + remoteDirectory);
1161
return this;
1162
}
1163
1164
/**
1165
* Changes to the parent directory, sending the CDUP command to the server.
1166
*
1167
* @return <code>true</code> if the command was successful.
1168
* @throws IOException
1169
*/
1170
public sun.net.ftp.FtpClient changeToParentDirectory() throws sun.net.ftp.FtpProtocolException, IOException {
1171
issueCommandCheck("CDUP");
1172
return this;
1173
}
1174
1175
/**
1176
* Returns the server current working directory, or <code>null</code> if
1177
* the PWD command failed.
1178
*
1179
* @return a <code>String</code> containing the current working directory,
1180
* or <code>null</code>
1181
* @throws IOException
1182
*/
1183
public String getWorkingDirectory() throws sun.net.ftp.FtpProtocolException, IOException {
1184
issueCommandCheck("PWD");
1185
/*
1186
* answer will be of the following format :
1187
*
1188
* 257 "/" is current directory.
1189
*/
1190
String answ = getResponseString();
1191
if (!answ.startsWith("257")) {
1192
return null;
1193
}
1194
return answ.substring(5, answ.lastIndexOf('"'));
1195
}
1196
1197
/**
1198
* Sets the restart offset to the specified value. That value will be
1199
* sent through a <code>REST</code> command to server before a file
1200
* transfer and has the effect of resuming a file transfer from the
1201
* specified point. After a transfer the restart offset is set back to
1202
* zero.
1203
*
1204
* @param offset the offset in the remote file at which to start the next
1205
* transfer. This must be a value greater than or equal to zero.
1206
* @throws IllegalArgumentException if the offset is negative.
1207
*/
1208
public sun.net.ftp.FtpClient setRestartOffset(long offset) {
1209
if (offset < 0) {
1210
throw new IllegalArgumentException("offset can't be negative");
1211
}
1212
restartOffset = offset;
1213
return this;
1214
}
1215
1216
/**
1217
* Retrieves a file from the ftp server and writes it to the specified
1218
* <code>OutputStream</code>.
1219
* If the restart offset was set, then a <code>REST</code> command will be
1220
* sent before the RETR in order to restart the tranfer from the specified
1221
* offset.
1222
* The <code>OutputStream</code> is not closed by this method at the end
1223
* of the transfer.
1224
*
1225
* @param name a <code>String<code> containing the name of the file to
1226
* retreive from the server.
1227
* @param local the <code>OutputStream</code> the file should be written to.
1228
* @throws IOException if the transfer fails.
1229
*/
1230
public sun.net.ftp.FtpClient getFile(String name, OutputStream local) throws sun.net.ftp.FtpProtocolException, IOException {
1231
int mtu = 1500;
1232
if (restartOffset > 0) {
1233
Socket s;
1234
try {
1235
s = openDataConnection("REST " + restartOffset);
1236
} finally {
1237
restartOffset = 0;
1238
}
1239
issueCommandCheck("RETR " + name);
1240
getTransferSize();
1241
InputStream remote = createInputStream(s.getInputStream());
1242
byte[] buf = new byte[mtu * 10];
1243
int l;
1244
while ((l = remote.read(buf)) >= 0) {
1245
if (l > 0) {
1246
local.write(buf, 0, l);
1247
}
1248
}
1249
remote.close();
1250
} else {
1251
Socket s = openDataConnection("RETR " + name);
1252
getTransferSize();
1253
InputStream remote = createInputStream(s.getInputStream());
1254
byte[] buf = new byte[mtu * 10];
1255
int l;
1256
while ((l = remote.read(buf)) >= 0) {
1257
if (l > 0) {
1258
local.write(buf, 0, l);
1259
}
1260
}
1261
remote.close();
1262
}
1263
return completePending();
1264
}
1265
1266
/**
1267
* Retrieves a file from the ftp server, using the RETR command, and
1268
* returns the InputStream from* the established data connection.
1269
* {@link #completePending()} <b>has</b> to be called once the application
1270
* is done reading from the returned stream.
1271
*
1272
* @param name the name of the remote file
1273
* @return the {@link java.io.InputStream} from the data connection, or
1274
* <code>null</code> if the command was unsuccessful.
1275
* @throws IOException if an error occurred during the transmission.
1276
*/
1277
public InputStream getFileStream(String name) throws sun.net.ftp.FtpProtocolException, IOException {
1278
Socket s;
1279
if (restartOffset > 0) {
1280
try {
1281
s = openDataConnection("REST " + restartOffset);
1282
} finally {
1283
restartOffset = 0;
1284
}
1285
if (s == null) {
1286
return null;
1287
}
1288
issueCommandCheck("RETR " + name);
1289
getTransferSize();
1290
return createInputStream(s.getInputStream());
1291
}
1292
1293
s = openDataConnection("RETR " + name);
1294
if (s == null) {
1295
return null;
1296
}
1297
getTransferSize();
1298
return createInputStream(s.getInputStream());
1299
}
1300
1301
/**
1302
* Transfers a file from the client to the server (aka a <I>put</I>)
1303
* by sending the STOR or STOU command, depending on the
1304
* <code>unique</code> argument, and returns the <code>OutputStream</code>
1305
* from the established data connection.
1306
* {@link #completePending()} <b>has</b> to be called once the application
1307
* is finished writing to the stream.
1308
*
1309
* A new file is created at the server site if the file specified does
1310
* not already exist.
1311
*
1312
* If <code>unique</code> is set to <code>true</code>, the resultant file
1313
* is to be created under a name unique to that directory, meaning
1314
* it will not overwrite an existing file, instead the server will
1315
* generate a new, unique, file name.
1316
* The name of the remote file can be retrieved, after completion of the
1317
* transfer, by calling {@link #getLastFileName()}.
1318
*
1319
* @param name the name of the remote file to write.
1320
* @param unique <code>true</code> if the remote files should be unique,
1321
* in which case the STOU command will be used.
1322
* @return the {@link java.io.OutputStream} from the data connection or
1323
* <code>null</code> if the command was unsuccessful.
1324
* @throws IOException if an error occurred during the transmission.
1325
*/
1326
public OutputStream putFileStream(String name, boolean unique)
1327
throws sun.net.ftp.FtpProtocolException, IOException
1328
{
1329
String cmd = unique ? "STOU " : "STOR ";
1330
Socket s = openDataConnection(cmd + name);
1331
if (s == null) {
1332
return null;
1333
}
1334
boolean bm = (type == TransferType.BINARY);
1335
return new sun.net.TelnetOutputStream(s.getOutputStream(), bm);
1336
}
1337
1338
/**
1339
* Transfers a file from the client to the server (aka a <I>put</I>)
1340
* by sending the STOR command. The content of the <code>InputStream</code>
1341
* passed in argument is written into the remote file, overwriting any
1342
* existing data.
1343
*
1344
* A new file is created at the server site if the file specified does
1345
* not already exist.
1346
*
1347
* @param name the name of the remote file to write.
1348
* @param local the <code>InputStream</code> that points to the data to
1349
* transfer.
1350
* @param unique <code>true</code> if the remote file should be unique
1351
* (i.e. not already existing), <code>false</code> otherwise.
1352
* @return <code>true</code> if the transfer was successful.
1353
* @throws IOException if an error occurred during the transmission.
1354
* @see #getLastFileName()
1355
*/
1356
public sun.net.ftp.FtpClient putFile(String name, InputStream local, boolean unique) throws sun.net.ftp.FtpProtocolException, IOException {
1357
String cmd = unique ? "STOU " : "STOR ";
1358
int mtu = 1500;
1359
if (type == TransferType.BINARY) {
1360
Socket s = openDataConnection(cmd + name);
1361
OutputStream remote = createOutputStream(s.getOutputStream());
1362
byte[] buf = new byte[mtu * 10];
1363
int l;
1364
while ((l = local.read(buf)) >= 0) {
1365
if (l > 0) {
1366
remote.write(buf, 0, l);
1367
}
1368
}
1369
remote.close();
1370
}
1371
return completePending();
1372
}
1373
1374
/**
1375
* Sends the APPE command to the server in order to transfer a data stream
1376
* passed in argument and append it to the content of the specified remote
1377
* file.
1378
*
1379
* @param name A <code>String</code> containing the name of the remote file
1380
* to append to.
1381
* @param local The <code>InputStream</code> providing access to the data
1382
* to be appended.
1383
* @return <code>true</code> if the transfer was successful.
1384
* @throws IOException if an error occurred during the transmission.
1385
*/
1386
public sun.net.ftp.FtpClient appendFile(String name, InputStream local) throws sun.net.ftp.FtpProtocolException, IOException {
1387
int mtu = 1500;
1388
Socket s = openDataConnection("APPE " + name);
1389
OutputStream remote = createOutputStream(s.getOutputStream());
1390
byte[] buf = new byte[mtu * 10];
1391
int l;
1392
while ((l = local.read(buf)) >= 0) {
1393
if (l > 0) {
1394
remote.write(buf, 0, l);
1395
}
1396
}
1397
remote.close();
1398
return completePending();
1399
}
1400
1401
/**
1402
* Renames a file on the server.
1403
*
1404
* @param from the name of the file being renamed
1405
* @param to the new name for the file
1406
* @throws IOException if the command fails
1407
*/
1408
public sun.net.ftp.FtpClient rename(String from, String to) throws sun.net.ftp.FtpProtocolException, IOException {
1409
issueCommandCheck("RNFR " + from);
1410
issueCommandCheck("RNTO " + to);
1411
return this;
1412
}
1413
1414
/**
1415
* Deletes a file on the server.
1416
*
1417
* @param name a <code>String</code> containing the name of the file
1418
* to delete.
1419
* @return <code>true</code> if the command was successful
1420
* @throws IOException if an error occurred during the exchange
1421
*/
1422
public sun.net.ftp.FtpClient deleteFile(String name) throws sun.net.ftp.FtpProtocolException, IOException {
1423
issueCommandCheck("DELE " + name);
1424
return this;
1425
}
1426
1427
/**
1428
* Creates a new directory on the server.
1429
*
1430
* @param name a <code>String</code> containing the name of the directory
1431
* to create.
1432
* @return <code>true</code> if the operation was successful.
1433
* @throws IOException if an error occurred during the exchange
1434
*/
1435
public sun.net.ftp.FtpClient makeDirectory(String name) throws sun.net.ftp.FtpProtocolException, IOException {
1436
issueCommandCheck("MKD " + name);
1437
return this;
1438
}
1439
1440
/**
1441
* Removes a directory on the server.
1442
*
1443
* @param name a <code>String</code> containing the name of the directory
1444
* to remove.
1445
*
1446
* @return <code>true</code> if the operation was successful.
1447
* @throws IOException if an error occurred during the exchange.
1448
*/
1449
public sun.net.ftp.FtpClient removeDirectory(String name) throws sun.net.ftp.FtpProtocolException, IOException {
1450
issueCommandCheck("RMD " + name);
1451
return this;
1452
}
1453
1454
/**
1455
* Sends a No-operation command. It's useful for testing the connection
1456
* status or as a <I>keep alive</I> mechanism.
1457
*
1458
* @throws FtpProtocolException if the command fails
1459
*/
1460
public sun.net.ftp.FtpClient noop() throws sun.net.ftp.FtpProtocolException, IOException {
1461
issueCommandCheck("NOOP");
1462
return this;
1463
}
1464
1465
/**
1466
* Sends the STAT command to the server.
1467
* This can be used while a data connection is open to get a status
1468
* on the current transfer, in that case the parameter should be
1469
* <code>null</code>.
1470
* If used between file transfers, it may have a pathname as argument
1471
* in which case it will work as the LIST command except no data
1472
* connection will be created.
1473
*
1474
* @param name an optional <code>String</code> containing the pathname
1475
* the STAT command should apply to.
1476
* @return the response from the server or <code>null</code> if the
1477
* command failed.
1478
* @throws IOException if an error occurred during the transmission.
1479
*/
1480
public String getStatus(String name) throws sun.net.ftp.FtpProtocolException, IOException {
1481
issueCommandCheck((name == null ? "STAT" : "STAT " + name));
1482
/*
1483
* A typical response will be:
1484
* 213-status of t32.gif:
1485
* -rw-r--r-- 1 jcc staff 247445 Feb 17 1998 t32.gif
1486
* 213 End of Status
1487
*
1488
* or
1489
*
1490
* 211-jsn FTP server status:
1491
* Version wu-2.6.2+Sun
1492
* Connected to localhost (::1)
1493
* Logged in as jccollet
1494
* TYPE: ASCII, FORM: Nonprint; STRUcture: File; transfer MODE: Stream
1495
* No data connection
1496
* 0 data bytes received in 0 files
1497
* 0 data bytes transmitted in 0 files
1498
* 0 data bytes total in 0 files
1499
* 53 traffic bytes received in 0 transfers
1500
* 485 traffic bytes transmitted in 0 transfers
1501
* 587 traffic bytes total in 0 transfers
1502
* 211 End of status
1503
*
1504
* So we need to remove the 1st and last line
1505
*/
1506
Vector<String> resp = getResponseStrings();
1507
StringBuffer sb = new StringBuffer();
1508
for (int i = 1; i < resp.size() - 1; i++) {
1509
sb.append(resp.get(i));
1510
}
1511
return sb.toString();
1512
}
1513
1514
/**
1515
* Sends the FEAT command to the server and returns the list of supported
1516
* features in the form of strings.
1517
*
1518
* The features are the supported commands, like AUTH TLS, PROT or PASV.
1519
* See the RFCs for a complete list.
1520
*
1521
* Note that not all FTP servers support that command, in which case
1522
* the method will return <code>null</code>
1523
*
1524
* @return a <code>List</code> of <code>Strings</code> describing the
1525
* supported additional features, or <code>null</code>
1526
* if the command is not supported.
1527
* @throws IOException if an error occurs during the transmission.
1528
*/
1529
public List<String> getFeatures() throws sun.net.ftp.FtpProtocolException, IOException {
1530
/*
1531
* The FEAT command, when implemented will return something like:
1532
*
1533
* 211-Features:
1534
* AUTH TLS
1535
* PBSZ
1536
* PROT
1537
* EPSV
1538
* EPRT
1539
* PASV
1540
* REST STREAM
1541
* 211 END
1542
*/
1543
ArrayList<String> features = new ArrayList<String>();
1544
issueCommandCheck("FEAT");
1545
Vector<String> resp = getResponseStrings();
1546
// Note that we start at index 1 to skip the 1st line (211-...)
1547
// and we stop before the last line.
1548
for (int i = 1; i < resp.size() - 1; i++) {
1549
String s = resp.get(i);
1550
// Get rid of leading space and trailing newline
1551
features.add(s.substring(1, s.length() - 1));
1552
}
1553
return features;
1554
}
1555
1556
/**
1557
* sends the ABOR command to the server.
1558
* It tells the server to stop the previous command or transfer.
1559
*
1560
* @return <code>true</code> if the command was successful.
1561
* @throws IOException if an error occurred during the transmission.
1562
*/
1563
public sun.net.ftp.FtpClient abort() throws sun.net.ftp.FtpProtocolException, IOException {
1564
issueCommandCheck("ABOR");
1565
// TODO: Must check the ReplyCode:
1566
/*
1567
* From the RFC:
1568
* There are two cases for the server upon receipt of this
1569
* command: (1) the FTP service command was already completed,
1570
* or (2) the FTP service command is still in progress.
1571
* In the first case, the server closes the data connection
1572
* (if it is open) and responds with a 226 reply, indicating
1573
* that the abort command was successfully processed.
1574
* In the second case, the server aborts the FTP service in
1575
* progress and closes the data connection, returning a 426
1576
* reply to indicate that the service request terminated
1577
* abnormally. The server then sends a 226 reply,
1578
* indicating that the abort command was successfully
1579
* processed.
1580
*/
1581
1582
1583
return this;
1584
}
1585
1586
/**
1587
* Some methods do not wait until completion before returning, so this
1588
* method can be called to wait until completion. This is typically the case
1589
* with commands that trigger a transfer like {@link #getFileStream(String)}.
1590
* So this method should be called before accessing information related to
1591
* such a command.
1592
* <p>This method will actually block reading on the command channel for a
1593
* notification from the server that the command is finished. Such a
1594
* notification often carries extra information concerning the completion
1595
* of the pending action (e.g. number of bytes transfered).</p>
1596
* <p>Note that this will return true immediately if no command or action
1597
* is pending</p>
1598
* <p>It should be also noted that most methods issuing commands to the ftp
1599
* server will call this method if a previous command is pending.
1600
* <p>Example of use:
1601
* <pre>
1602
* InputStream in = cl.getFileStream("file");
1603
* ...
1604
* cl.completePending();
1605
* long size = cl.getLastTransferSize();
1606
* </pre>
1607
* On the other hand, it's not necessary in a case like:
1608
* <pre>
1609
* InputStream in = cl.getFileStream("file");
1610
* // read content
1611
* ...
1612
* cl.logout();
1613
* </pre>
1614
* <p>Since {@link #logout()} will call completePending() if necessary.</p>
1615
* @return <code>true</code> if the completion was successful or if no
1616
* action was pending.
1617
* @throws IOException
1618
*/
1619
public sun.net.ftp.FtpClient completePending() throws sun.net.ftp.FtpProtocolException, IOException {
1620
while (replyPending) {
1621
replyPending = false;
1622
if (!readReply()) {
1623
throw new sun.net.ftp.FtpProtocolException(getLastResponseString(), lastReplyCode);
1624
}
1625
}
1626
return this;
1627
}
1628
1629
/**
1630
* Reinitializes the USER parameters on the FTP server
1631
*
1632
* @throws FtpProtocolException if the command fails
1633
*/
1634
public sun.net.ftp.FtpClient reInit() throws sun.net.ftp.FtpProtocolException, IOException {
1635
issueCommandCheck("REIN");
1636
loggedIn = false;
1637
if (useCrypto) {
1638
if (server instanceof SSLSocket) {
1639
javax.net.ssl.SSLSession session = ((SSLSocket) server).getSession();
1640
session.invalidate();
1641
// Restore previous socket and streams
1642
server = oldSocket;
1643
oldSocket = null;
1644
try {
1645
out = new PrintStream(new BufferedOutputStream(server.getOutputStream()),
1646
true, encoding);
1647
} catch (UnsupportedEncodingException e) {
1648
throw new InternalError(encoding + "encoding not found", e);
1649
}
1650
in = new BufferedInputStream(server.getInputStream());
1651
}
1652
}
1653
useCrypto = false;
1654
return this;
1655
}
1656
1657
/**
1658
* Changes the transfer type (binary, ascii, ebcdic) and issue the
1659
* proper command (e.g. TYPE A) to the server.
1660
*
1661
* @param type the <code>FtpTransferType</code> to use.
1662
* @return This FtpClient
1663
* @throws IOException if an error occurs during transmission.
1664
*/
1665
public sun.net.ftp.FtpClient setType(TransferType type) throws sun.net.ftp.FtpProtocolException, IOException {
1666
String cmd = "NOOP";
1667
1668
this.type = type;
1669
if (type == TransferType.ASCII) {
1670
cmd = "TYPE A";
1671
}
1672
if (type == TransferType.BINARY) {
1673
cmd = "TYPE I";
1674
}
1675
if (type == TransferType.EBCDIC) {
1676
cmd = "TYPE E";
1677
}
1678
issueCommandCheck(cmd);
1679
return this;
1680
}
1681
1682
/**
1683
* Issues a LIST command to the server to get the current directory
1684
* listing, and returns the InputStream from the data connection.
1685
* {@link #completePending()} <b>has</b> to be called once the application
1686
* is finished writing to the stream.
1687
*
1688
* @param path the pathname of the directory to list, or <code>null</code>
1689
* for the current working directory.
1690
* @return the <code>InputStream</code> from the resulting data connection
1691
* @throws IOException if an error occurs during the transmission.
1692
* @see #changeDirectory(String)
1693
* @see #listFiles(String)
1694
*/
1695
public InputStream list(String path) throws sun.net.ftp.FtpProtocolException, IOException {
1696
Socket s;
1697
s = openDataConnection(path == null ? "LIST" : "LIST " + path);
1698
if (s != null) {
1699
return createInputStream(s.getInputStream());
1700
}
1701
return null;
1702
}
1703
1704
/**
1705
* Issues a NLST path command to server to get the specified directory
1706
* content. It differs from {@link #list(String)} method by the fact that
1707
* it will only list the file names which would make the parsing of the
1708
* somewhat easier.
1709
*
1710
* {@link #completePending()} <b>has</b> to be called once the application
1711
* is finished writing to the stream.
1712
*
1713
* @param path a <code>String</code> containing the pathname of the
1714
* directory to list or <code>null</code> for the current working
1715
* directory.
1716
* @return the <code>InputStream</code> from the resulting data connection
1717
* @throws IOException if an error occurs during the transmission.
1718
*/
1719
public InputStream nameList(String path) throws sun.net.ftp.FtpProtocolException, IOException {
1720
Socket s;
1721
s = openDataConnection(path == null ? "NLST" : "NLST " + path);
1722
if (s != null) {
1723
return createInputStream(s.getInputStream());
1724
}
1725
return null;
1726
}
1727
1728
/**
1729
* Issues the SIZE [path] command to the server to get the size of a
1730
* specific file on the server.
1731
* Note that this command may not be supported by the server. In which
1732
* case -1 will be returned.
1733
*
1734
* @param path a <code>String</code> containing the pathname of the
1735
* file.
1736
* @return a <code>long</code> containing the size of the file or -1 if
1737
* the server returned an error, which can be checked with
1738
* {@link #getLastReplyCode()}.
1739
* @throws IOException if an error occurs during the transmission.
1740
*/
1741
public long getSize(String path) throws sun.net.ftp.FtpProtocolException, IOException {
1742
if (path == null || path.length() == 0) {
1743
throw new IllegalArgumentException("path can't be null or empty");
1744
}
1745
issueCommandCheck("SIZE " + path);
1746
if (lastReplyCode == FtpReplyCode.FILE_STATUS) {
1747
String s = getResponseString();
1748
s = s.substring(4, s.length() - 1);
1749
return Long.parseLong(s);
1750
}
1751
return -1;
1752
}
1753
private static String[] MDTMformats = {
1754
"yyyyMMddHHmmss.SSS",
1755
"yyyyMMddHHmmss"
1756
};
1757
private static SimpleDateFormat[] dateFormats = new SimpleDateFormat[MDTMformats.length];
1758
1759
static {
1760
for (int i = 0; i < MDTMformats.length; i++) {
1761
dateFormats[i] = new SimpleDateFormat(MDTMformats[i]);
1762
dateFormats[i].setTimeZone(TimeZone.getTimeZone("GMT"));
1763
}
1764
}
1765
1766
/**
1767
* Issues the MDTM [path] command to the server to get the modification
1768
* time of a specific file on the server.
1769
* Note that this command may not be supported by the server, in which
1770
* case <code>null</code> will be returned.
1771
*
1772
* @param path a <code>String</code> containing the pathname of the file.
1773
* @return a <code>Date</code> representing the last modification time
1774
* or <code>null</code> if the server returned an error, which
1775
* can be checked with {@link #getLastReplyCode()}.
1776
* @throws IOException if an error occurs during the transmission.
1777
*/
1778
public Date getLastModified(String path) throws sun.net.ftp.FtpProtocolException, IOException {
1779
issueCommandCheck("MDTM " + path);
1780
if (lastReplyCode == FtpReplyCode.FILE_STATUS) {
1781
String s = getResponseString().substring(4);
1782
Date d = null;
1783
for (SimpleDateFormat dateFormat : dateFormats) {
1784
try {
1785
d = dateFormat.parse(s);
1786
} catch (ParseException ex) {
1787
}
1788
if (d != null) {
1789
return d;
1790
}
1791
}
1792
}
1793
return null;
1794
}
1795
1796
/**
1797
* Sets the parser used to handle the directory output to the specified
1798
* one. By default the parser is set to one that can handle most FTP
1799
* servers output (Unix base mostly). However it may be necessary for
1800
* and application to provide its own parser due to some uncommon
1801
* output format.
1802
*
1803
* @param p The <code>FtpDirParser</code> to use.
1804
* @see #listFiles(String)
1805
*/
1806
public sun.net.ftp.FtpClient setDirParser(FtpDirParser p) {
1807
parser = p;
1808
return this;
1809
}
1810
1811
private class FtpFileIterator implements Iterator<FtpDirEntry>, Closeable {
1812
1813
private BufferedReader in = null;
1814
private FtpDirEntry nextFile = null;
1815
private FtpDirParser fparser = null;
1816
private boolean eof = false;
1817
1818
public FtpFileIterator(FtpDirParser p, BufferedReader in) {
1819
this.in = in;
1820
this.fparser = p;
1821
readNext();
1822
}
1823
1824
private void readNext() {
1825
nextFile = null;
1826
if (eof) {
1827
return;
1828
}
1829
String line = null;
1830
try {
1831
do {
1832
line = in.readLine();
1833
if (line != null) {
1834
nextFile = fparser.parseLine(line);
1835
if (nextFile != null) {
1836
return;
1837
}
1838
}
1839
} while (line != null);
1840
in.close();
1841
} catch (IOException iOException) {
1842
}
1843
eof = true;
1844
}
1845
1846
public boolean hasNext() {
1847
return nextFile != null;
1848
}
1849
1850
public FtpDirEntry next() {
1851
FtpDirEntry ret = nextFile;
1852
readNext();
1853
return ret;
1854
}
1855
1856
public void remove() {
1857
throw new UnsupportedOperationException("Not supported yet.");
1858
}
1859
1860
public void close() throws IOException {
1861
if (in != null && !eof) {
1862
in.close();
1863
}
1864
eof = true;
1865
nextFile = null;
1866
}
1867
}
1868
1869
/**
1870
* Issues a MLSD command to the server to get the specified directory
1871
* listing and applies the current parser to create an Iterator of
1872
* {@link java.net.ftp.FtpDirEntry}. Note that the Iterator returned is also a
1873
* {@link java.io.Closeable}.
1874
* If the server doesn't support the MLSD command, the LIST command is used
1875
* instead.
1876
*
1877
* {@link #completePending()} <b>has</b> to be called once the application
1878
* is finished iterating through the files.
1879
*
1880
* @param path the pathname of the directory to list or <code>null</code>
1881
* for the current working directoty.
1882
* @return a <code>Iterator</code> of files or <code>null</code> if the
1883
* command failed.
1884
* @throws IOException if an error occurred during the transmission
1885
* @see #setDirParser(FtpDirParser)
1886
* @see #changeDirectory(String)
1887
*/
1888
public Iterator<FtpDirEntry> listFiles(String path) throws sun.net.ftp.FtpProtocolException, IOException {
1889
Socket s = null;
1890
BufferedReader sin = null;
1891
try {
1892
s = openDataConnection(path == null ? "MLSD" : "MLSD " + path);
1893
} catch (sun.net.ftp.FtpProtocolException FtpException) {
1894
// The server doesn't understand new MLSD command, ignore and fall
1895
// back to LIST
1896
}
1897
1898
if (s != null) {
1899
sin = new BufferedReader(new InputStreamReader(s.getInputStream()));
1900
return new FtpFileIterator(mlsxParser, sin);
1901
} else {
1902
s = openDataConnection(path == null ? "LIST" : "LIST " + path);
1903
if (s != null) {
1904
sin = new BufferedReader(new InputStreamReader(s.getInputStream()));
1905
return new FtpFileIterator(parser, sin);
1906
}
1907
}
1908
return null;
1909
}
1910
1911
private boolean sendSecurityData(byte[] buf) throws IOException,
1912
sun.net.ftp.FtpProtocolException {
1913
BASE64Encoder encoder = new BASE64Encoder();
1914
String s = encoder.encode(buf);
1915
return issueCommand("ADAT " + s);
1916
}
1917
1918
private byte[] getSecurityData() {
1919
String s = getLastResponseString();
1920
if (s.substring(4, 9).equalsIgnoreCase("ADAT=")) {
1921
BASE64Decoder decoder = new BASE64Decoder();
1922
try {
1923
// Need to get rid of the leading '315 ADAT='
1924
// and the trailing newline
1925
return decoder.decodeBuffer(s.substring(9, s.length() - 1));
1926
} catch (IOException e) {
1927
//
1928
}
1929
}
1930
return null;
1931
}
1932
1933
/**
1934
* Attempts to use Kerberos GSSAPI as an authentication mechanism with the
1935
* ftp server. This will issue an <code>AUTH GSSAPI</code> command, and if
1936
* it is accepted by the server, will followup with <code>ADAT</code>
1937
* command to exchange the various tokens until authentification is
1938
* successful. This conforms to Appendix I of RFC 2228.
1939
*
1940
* @return <code>true</code> if authentication was successful.
1941
* @throws IOException if an error occurs during the transmission.
1942
*/
1943
public sun.net.ftp.FtpClient useKerberos() throws sun.net.ftp.FtpProtocolException, IOException {
1944
/*
1945
* Comment out for the moment since it's not in use and would create
1946
* needless cross-package links.
1947
*
1948
issueCommandCheck("AUTH GSSAPI");
1949
if (lastReplyCode != FtpReplyCode.NEED_ADAT)
1950
throw new sun.net.ftp.FtpProtocolException("Unexpected reply from server");
1951
try {
1952
GSSManager manager = GSSManager.getInstance();
1953
GSSName name = manager.createName("SERVICE:ftp@"+
1954
serverAddr.getHostName(), null);
1955
GSSContext context = manager.createContext(name, null, null,
1956
GSSContext.DEFAULT_LIFETIME);
1957
context.requestMutualAuth(true);
1958
context.requestReplayDet(true);
1959
context.requestSequenceDet(true);
1960
context.requestCredDeleg(true);
1961
byte []inToken = new byte[0];
1962
while (!context.isEstablished()) {
1963
byte[] outToken
1964
= context.initSecContext(inToken, 0, inToken.length);
1965
// send the output token if generated
1966
if (outToken != null) {
1967
if (sendSecurityData(outToken)) {
1968
inToken = getSecurityData();
1969
}
1970
}
1971
}
1972
loggedIn = true;
1973
} catch (GSSException e) {
1974
1975
}
1976
*/
1977
return this;
1978
}
1979
1980
/**
1981
* Returns the Welcome string the server sent during initial connection.
1982
*
1983
* @return a <code>String</code> containing the message the server
1984
* returned during connection or <code>null</code>.
1985
*/
1986
public String getWelcomeMsg() {
1987
return welcomeMsg;
1988
}
1989
1990
/**
1991
* Returns the last reply code sent by the server.
1992
*
1993
* @return the lastReplyCode
1994
*/
1995
public FtpReplyCode getLastReplyCode() {
1996
return lastReplyCode;
1997
}
1998
1999
/**
2000
* Returns the last response string sent by the server.
2001
*
2002
* @return the message string, which can be quite long, last returned
2003
* by the server.
2004
*/
2005
public String getLastResponseString() {
2006
StringBuffer sb = new StringBuffer();
2007
if (serverResponse != null) {
2008
for (String l : serverResponse) {
2009
if (l != null) {
2010
sb.append(l);
2011
}
2012
}
2013
}
2014
return sb.toString();
2015
}
2016
2017
/**
2018
* Returns, when available, the size of the latest started transfer.
2019
* This is retreived by parsing the response string received as an initial
2020
* response to a RETR or similar request.
2021
*
2022
* @return the size of the latest transfer or -1 if either there was no
2023
* transfer or the information was unavailable.
2024
*/
2025
public long getLastTransferSize() {
2026
return lastTransSize;
2027
}
2028
2029
/**
2030
* Returns, when available, the remote name of the last transfered file.
2031
* This is mainly useful for "put" operation when the unique flag was
2032
* set since it allows to recover the unique file name created on the
2033
* server which may be different from the one submitted with the command.
2034
*
2035
* @return the name the latest transfered file remote name, or
2036
* <code>null</code> if that information is unavailable.
2037
*/
2038
public String getLastFileName() {
2039
return lastFileName;
2040
}
2041
2042
/**
2043
* Attempts to switch to a secure, encrypted connection. This is done by
2044
* sending the "AUTH TLS" command.
2045
* <p>See <a href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</a></p>
2046
* If successful this will establish a secure command channel with the
2047
* server, it will also make it so that all other transfers (e.g. a RETR
2048
* command) will be done over an encrypted channel as well unless a
2049
* {@link #reInit()} command or a {@link #endSecureSession()} command is issued.
2050
*
2051
* @return <code>true</code> if the operation was successful.
2052
* @throws IOException if an error occurred during the transmission.
2053
* @see #endSecureSession()
2054
*/
2055
public sun.net.ftp.FtpClient startSecureSession() throws sun.net.ftp.FtpProtocolException, IOException {
2056
if (!isConnected()) {
2057
throw new sun.net.ftp.FtpProtocolException("Not connected yet", FtpReplyCode.BAD_SEQUENCE);
2058
}
2059
if (sslFact == null) {
2060
try {
2061
sslFact = (SSLSocketFactory) SSLSocketFactory.getDefault();
2062
} catch (Exception e) {
2063
throw new IOException(e.getLocalizedMessage());
2064
}
2065
}
2066
issueCommandCheck("AUTH TLS");
2067
Socket s = null;
2068
try {
2069
s = sslFact.createSocket(server, serverAddr.getHostName(), serverAddr.getPort(), true);
2070
} catch (javax.net.ssl.SSLException ssle) {
2071
try {
2072
disconnect();
2073
} catch (Exception e) {
2074
}
2075
throw ssle;
2076
}
2077
// Remember underlying socket so we can restore it later
2078
oldSocket = server;
2079
server = s;
2080
try {
2081
out = new PrintStream(new BufferedOutputStream(server.getOutputStream()),
2082
true, encoding);
2083
} catch (UnsupportedEncodingException e) {
2084
throw new InternalError(encoding + "encoding not found", e);
2085
}
2086
in = new BufferedInputStream(server.getInputStream());
2087
2088
issueCommandCheck("PBSZ 0");
2089
issueCommandCheck("PROT P");
2090
useCrypto = true;
2091
return this;
2092
}
2093
2094
/**
2095
* Sends a <code>CCC</code> command followed by a <code>PROT C</code>
2096
* command to the server terminating an encrypted session and reverting
2097
* back to a non crypted transmission.
2098
*
2099
* @return <code>true</code> if the operation was successful.
2100
* @throws IOException if an error occurred during transmission.
2101
* @see #startSecureSession()
2102
*/
2103
public sun.net.ftp.FtpClient endSecureSession() throws sun.net.ftp.FtpProtocolException, IOException {
2104
if (!useCrypto) {
2105
return this;
2106
}
2107
2108
issueCommandCheck("CCC");
2109
issueCommandCheck("PROT C");
2110
useCrypto = false;
2111
// Restore previous socket and streams
2112
server = oldSocket;
2113
oldSocket = null;
2114
try {
2115
out = new PrintStream(new BufferedOutputStream(server.getOutputStream()),
2116
true, encoding);
2117
} catch (UnsupportedEncodingException e) {
2118
throw new InternalError(encoding + "encoding not found", e);
2119
}
2120
in = new BufferedInputStream(server.getInputStream());
2121
2122
return this;
2123
}
2124
2125
/**
2126
* Sends the "Allocate" (ALLO) command to the server telling it to
2127
* pre-allocate the specified number of bytes for the next transfer.
2128
*
2129
* @param size The number of bytes to allocate.
2130
* @return <code>true</code> if the operation was successful.
2131
* @throws IOException if an error occurred during the transmission.
2132
*/
2133
public sun.net.ftp.FtpClient allocate(long size) throws sun.net.ftp.FtpProtocolException, IOException {
2134
issueCommandCheck("ALLO " + size);
2135
return this;
2136
}
2137
2138
/**
2139
* Sends the "Structure Mount" (SMNT) command to the server. This let the
2140
* user mount a different file system data structure without altering his
2141
* login or accounting information.
2142
*
2143
* @param struct a <code>String</code> containing the name of the
2144
* structure to mount.
2145
* @return <code>true</code> if the operation was successful.
2146
* @throws IOException if an error occurred during the transmission.
2147
*/
2148
public sun.net.ftp.FtpClient structureMount(String struct) throws sun.net.ftp.FtpProtocolException, IOException {
2149
issueCommandCheck("SMNT " + struct);
2150
return this;
2151
}
2152
2153
/**
2154
* Sends a SYST (System) command to the server and returns the String
2155
* sent back by the server describing the operating system at the
2156
* server.
2157
*
2158
* @return a <code>String</code> describing the OS, or <code>null</code>
2159
* if the operation was not successful.
2160
* @throws IOException if an error occurred during the transmission.
2161
*/
2162
public String getSystem() throws sun.net.ftp.FtpProtocolException, IOException {
2163
issueCommandCheck("SYST");
2164
/*
2165
* 215 UNIX Type: L8 Version: SUNOS
2166
*/
2167
String resp = getResponseString();
2168
// Get rid of the leading code and blank
2169
return resp.substring(4);
2170
}
2171
2172
/**
2173
* Sends the HELP command to the server, with an optional command, like
2174
* SITE, and returns the text sent back by the server.
2175
*
2176
* @param cmd the command for which the help is requested or
2177
* <code>null</code> for the general help
2178
* @return a <code>String</code> containing the text sent back by the
2179
* server, or <code>null</code> if the command failed.
2180
* @throws IOException if an error occurred during transmission
2181
*/
2182
public String getHelp(String cmd) throws sun.net.ftp.FtpProtocolException, IOException {
2183
issueCommandCheck("HELP " + cmd);
2184
/**
2185
*
2186
* HELP
2187
* 214-The following commands are implemented.
2188
* USER EPRT STRU ALLO DELE SYST RMD MDTM ADAT
2189
* PASS EPSV MODE REST CWD STAT PWD PROT
2190
* QUIT LPRT RETR RNFR LIST HELP CDUP PBSZ
2191
* PORT LPSV STOR RNTO NLST NOOP STOU AUTH
2192
* PASV TYPE APPE ABOR SITE MKD SIZE CCC
2193
* 214 Direct comments to ftp-bugs@jsn.
2194
*
2195
* HELP SITE
2196
* 214-The following SITE commands are implemented.
2197
* UMASK HELP GROUPS
2198
* IDLE ALIAS CHECKMETHOD
2199
* CHMOD CDPATH CHECKSUM
2200
* 214 Direct comments to ftp-bugs@jsn.
2201
*/
2202
Vector<String> resp = getResponseStrings();
2203
if (resp.size() == 1) {
2204
// Single line response
2205
return resp.get(0).substring(4);
2206
}
2207
// on multiple lines answers, like the ones above, remove 1st and last
2208
// line, concat the the others.
2209
StringBuffer sb = new StringBuffer();
2210
for (int i = 1; i < resp.size() - 1; i++) {
2211
sb.append(resp.get(i).substring(3));
2212
}
2213
return sb.toString();
2214
}
2215
2216
/**
2217
* Sends the SITE command to the server. This is used by the server
2218
* to provide services specific to his system that are essential
2219
* to file transfer.
2220
*
2221
* @param cmd the command to be sent.
2222
* @return <code>true</code> if the command was successful.
2223
* @throws IOException if an error occurred during transmission
2224
*/
2225
public sun.net.ftp.FtpClient siteCmd(String cmd) throws sun.net.ftp.FtpProtocolException, IOException {
2226
issueCommandCheck("SITE " + cmd);
2227
return this;
2228
}
2229
}
2230
2231