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