1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package wjhk.jupload2.upload;
23
24 import java.io.BufferedOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.util.Iterator;
28 import java.util.SortedSet;
29 import java.util.StringTokenizer;
30 import java.util.TreeSet;
31 import java.util.concurrent.BlockingQueue;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import org.apache.commons.net.ftp.FTP;
36 import org.apache.commons.net.ftp.FTPClient;
37 import org.apache.commons.net.ftp.FTPConnectionClosedException;
38 import org.apache.commons.net.ftp.FTPReply;
39
40 import wjhk.jupload2.exception.JUploadException;
41 import wjhk.jupload2.exception.JUploadExceptionUploadFailed;
42 import wjhk.jupload2.exception.JUploadIOException;
43 import wjhk.jupload2.policies.UploadPolicy;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class FileUploadThreadFTP extends DefaultFileUploadThread {
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 private OutputStream ftpOutputStream = null;
95
96
97
98
99 private BufferedOutputStream bufferedOutputStream = null;
100
101 private Matcher uriMatch;
102
103
104 private FTPClient ftp = new FTPClient();
105
106
107 private String user;
108
109
110 private String pass;
111
112
113 private String host;
114
115
116 private String port;
117
118
119
120
121
122 private String ftpRootFolder;
123
124
125
126
127
128 private boolean bConnected = false;
129
130
131
132
133 public final Pattern ftpPattern = Pattern
134 .compile("^ftp://(([^:]+):([^\\@]+)\\@)?([^/:]+):?([0-9]+)?(/(.*))?$");
135
136
137
138
139
140
141
142
143
144
145
146 public FileUploadThreadFTP(UploadPolicy uploadPolicy,
147 BlockingQueue<UploadFilePacket> packetQueue,
148 FileUploadManagerThread fileUploadManagerThread)
149 throws JUploadException {
150 super("FileUploadThreadFTP thread", packetQueue, uploadPolicy,
151 fileUploadManagerThread);
152 this.uploadPolicy.displayDebug("[FileUploadThreadFTP] Using "
153 + this.getClass().getName(), 30);
154
155
156
157
158 if (uploadPolicy.getStringUploadSuccess() != null) {
159 uploadPolicy
160 .displayWarn("FTP mode: stringUploadSuccess parameter ignored (forced to null)");
161 uploadPolicy.setProperty(UploadPolicy.PROP_STRING_UPLOAD_SUCCESS,
162 null);
163 }
164
165
166 if (uploadPolicy.getNbFilesPerRequest() != 1) {
167 uploadPolicy
168 .displayWarn("FTP mode: nbFilesPerRequest parameter ignored (forced to 1)");
169 uploadPolicy.setProperty(UploadPolicy.PROP_NB_FILES_PER_REQUEST,
170 "1");
171 }
172
173
174 if (uploadPolicy.getMaxChunkSize() != Long.MAX_VALUE) {
175 uploadPolicy
176 .displayWarn("FTP mode: maxChunkSize parameter ignored (forced to Long.MAX_VALUE)");
177 uploadPolicy.setProperty(UploadPolicy.PROP_MAX_CHUNK_SIZE,
178 Long.toString(Long.MAX_VALUE));
179 }
180 }
181
182
183 @Override
184 void beforeRequest(UploadFilePacket packet) throws JUploadException {
185
186
187 if (this.bConnected) {
188
189 try {
190 this.ftp.sendNoOp();
191 } catch (FTPConnectionClosedException eClosed) {
192
193 this.bConnected = false;
194 } catch (IOException e) {
195
196
197
198
199 if (e.getMessage().equals("Connection is not open")) {
200
201 this.bConnected = false;
202 } else {
203 throw new JUploadIOException(e.getClass().getName()
204 + " while checking FTP connection to the server", e);
205 }
206 }
207 }
208
209
210 if (!this.bConnected) {
211
212 String url = this.uploadPolicy.getPostURL();
213 this.uriMatch = this.ftpPattern.matcher(url);
214 if (!this.uriMatch.matches()) {
215 throw new JUploadException("invalid URI: " + url);
216 }
217 this.user = this.uriMatch.group(2) == null ? "anonymous"
218 : this.uriMatch.group(2);
219 this.pass = this.uriMatch.group(3) == null ? "JUpload"
220 : this.uriMatch.group(3);
221 this.host = this.uriMatch.group(4);
222 this.port = this.uriMatch.group(5) == null ? "21" : this.uriMatch
223 .group(5);
224 this.ftpRootFolder = (this.uriMatch.group(7) == null) ? null : "/"
225 + this.uriMatch.group(7);
226
227 if (this.ftpRootFolder != null && !this.ftpRootFolder.endsWith("/")) {
228 this.ftpRootFolder += "/";
229 }
230
231
232 try {
233 this.ftp.setDefaultPort(Integer.parseInt(this.port));
234 this.ftp.connect(this.host);
235 this.uploadPolicy.displayDebug(
236 "[FileUploadThreadFTP] Connected to " + this.host, 10);
237 this.uploadPolicy.displayDebug(this.ftp.getReplyString(), 80);
238
239 if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode()))
240 throw new JUploadException("FTP server refused connection.");
241
242
243 this.ftp.login(this.user, this.pass);
244 this.uploadPolicy.displayDebug("[FileUploadThreadFTP] "
245 + this.ftp.getReplyString(), 80);
246
247 if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode()))
248 throw new JUploadException(
249 "Invalid ftp username / password");
250
251 this.bConnected = true;
252 } catch (JUploadException jue) {
253
254 throw jue;
255 } catch (IOException ioe) {
256 throw new JUploadIOException(ioe.getClass().getName()
257 + "[FTP] Could not connect to server ("
258 + ioe.getMessage() + ")", ioe);
259 } catch (Exception e) {
260 throw new JUploadException(e.getClass().getName()
261 + "[FTP] Could not connect to server ("
262 + e.getMessage() + ")", e);
263 }
264
265
266 if (this.uploadPolicy.getFtpTransfertPassive()) {
267 this.ftp.enterLocalPassiveMode();
268 } else {
269 this.ftp.enterLocalActiveMode();
270 }
271
272 }
273 }
274
275
276 @Override
277 void afterFile(UploadFileData uploadFileData) {
278
279 }
280
281
282 @Override
283 void beforeFile(UploadFilePacket uploadFilePacket,
284 UploadFileData uploadFileData) throws JUploadException {
285 String workingDir = null;
286 String action = "Starting beforeFile";
287 try {
288
289
290 if (this.uploadPolicy.getFtpCreateDirectoryStructure()) {
291
292
293 action = "Before createDirectoryStructure";
294 createDirectoryStructure(uploadFilePacket);
295
296 workingDir = this.ftpRootFolder
297 + uploadFileData.getRelativeDir();
298
299
300 workingDir = workingDir.replace("\\", "/");
301
302 this.uploadPolicy
303 .displayDebug(
304 "[FileUploadThreadFTP] ftpCreateDirectoryStructure: Changing working directory to: "
305 + workingDir, 80);
306 } else {
307 workingDir = this.ftpRootFolder;
308 }
309
310 if (workingDir != null && !workingDir.equals("")
311 && !workingDir.equals(".")) {
312 action = "Before changeWorkingDirectory";
313 this.ftp.changeWorkingDirectory(workingDir);
314 action = "After changeWorkingDirectory";
315 this.uploadPolicy.displayDebug("[FileUploadThreadFTP] "
316 + this.ftp.getReplyString(), 80);
317 }
318
319 if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
320 throw new JUploadException(
321 "[FTP] Error while changing directory to: "
322 + workingDir + " (" + this.ftp.getReplyString()
323 + ")");
324 }
325
326 action = "Before setTransferType";
327 setTransferType(uploadFileData);
328 action = "After setTransferType";
329
330
331
332
333
334
335
336 action = "Before storeFileStream";
337 this.ftpOutputStream = this.ftp.storeFileStream(uploadFileData
338 .getFileName());
339 action = "After storeFileStream";
340 if (this.ftpOutputStream == null) {
341 throw new JUploadIOException(
342 "Stream connection to the server error. Check that your path on the URL is valid. postURL used is: "
343 + this.uploadPolicy.getPostURL());
344 }
345
346
347 this.bufferedOutputStream = new BufferedOutputStream(
348 this.ftpOutputStream);
349 } catch (IOException e) {
350 throw new JUploadIOException(e.getClass().getName()
351 + " in the action: " + action, e);
352 }
353 }
354
355
356 @Override
357 void cleanAll() {
358 try {
359 if (this.ftp.isConnected()) {
360 this.ftp.disconnect();
361 this.uploadPolicy.displayDebug(
362 "[FileUploadThreadFTP] disconnected", 50);
363 }
364 } catch (IOException e) {
365
366 this.uploadPolicy.displayDebug(
367 "[FileUploadThreadFTP] Not connected", 50);
368 } finally {
369 this.ftpOutputStream = null;
370 this.bufferedOutputStream = null;
371 }
372 }
373
374
375 @Override
376 void cleanRequest() throws JUploadException {
377 if (this.bufferedOutputStream != null) {
378 try {
379 this.bufferedOutputStream.close();
380 this.ftpOutputStream.close();
381 if (!this.ftp.completePendingCommand()) {
382 throw new JUploadExceptionUploadFailed(
383 "ftp.completePendingCommand() returned false");
384 }
385 } catch (IOException e) {
386 throw new JUploadException(e);
387 } finally {
388 this.bufferedOutputStream = null;
389 }
390 }
391 }
392
393
394
395
396
397 @Override
398 int finishRequest() throws JUploadException {
399 try {
400 getOutputStream().flush();
401 return 200;
402 } catch (IOException ioe) {
403 throw new JUploadIOException("FileUploadThreadFTP.finishRequest()",
404 ioe);
405 } catch (Exception e) {
406
407
408 throw new JUploadException(
409 "FileUploadThreadFTP.finishRequest() (check the user permission on the server)",
410 e);
411 }
412 }
413
414
415 @Override
416 long getAdditionnalBytesForUpload(UploadFileData uploadFileData) {
417
418 return 0;
419 }
420
421
422 @Override
423 OutputStream getOutputStream() {
424 return this.bufferedOutputStream;
425 }
426
427
428 @Override
429 void startRequest(long contentLength, boolean bChunkEnabled, int chunkPart,
430 boolean bLastChunk) {
431
432 }
433
434
435
436
437
438
439
440
441 private void setTransferType(UploadFileData uploadFileData)
442 throws JUploadIOException {
443 try {
444
445 if (this.uploadPolicy.getFtpTransfertBinary()) {
446 this.ftp.setFileType(FTP.BINARY_FILE_TYPE);
447 } else {
448 this.ftp.setFileType(FTP.ASCII_FILE_TYPE);
449 }
450 } catch (IOException ioe) {
451 throw new JUploadIOException(
452 "Cannot set transfert binary or ascii mode (binary: "
453 + this.uploadPolicy.getFtpTransfertBinary() + ")",
454 ioe);
455 }
456 }
457
458
459
460
461
462
463
464
465 private void createDirectoryStructure(UploadFilePacket packet)
466 throws JUploadIOException {
467 SortedSet<String> foldersToCreate = new TreeSet<String>();
468 String folderName;
469 String intermediateFolderName;
470 StringTokenizer st;
471
472
473 for (UploadFileData uploadFileData : packet) {
474 if (isInterrupted()) {
475 break;
476 }
477 folderName = uploadFileData.getRelativeDir();
478 folderName = folderName.replaceAll("\\\\", "/");
479
480 if (!foldersToCreate.contains(folderName)) {
481
482 st = new StringTokenizer(folderName, "/");
483 intermediateFolderName = this.ftpRootFolder;
484 while (st.hasMoreTokens()) {
485 intermediateFolderName += st.nextToken() + "/";
486 if (!foldersToCreate.contains(intermediateFolderName)) {
487 this.uploadPolicy.displayDebug(
488 "FTP structure identification: Adding subfolder "
489 + intermediateFolderName, 80);
490 foldersToCreate.add(intermediateFolderName);
491 }
492 }
493 }
494 }
495
496
497 try {
498 String folder;
499 for (Iterator<String> it = foldersToCreate.iterator(); it.hasNext();) {
500 folder = it.next();
501
502
503
504
505 this.ftp.changeWorkingDirectory(folder);
506 if (FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
507 this.uploadPolicy.displayDebug(
508 "[FileUploadThreadFTP] Folder " + folder
509 + " already exist", 80);
510 } else {
511
512
513
514 this.ftp.mkd(folder);
515 this.uploadPolicy.displayDebug(
516 "[FileUploadThreadFTP] Folder " + folder
517 + " created", 80);
518 if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
519 throw new JUploadIOException(
520 "Error while creating folder '"
521 + folder
522 + "' ("
523 + this.ftp.getReplyString().replaceAll(
524 "\r\n", "") + ")");
525 }
526 }
527 }
528 } catch (IOException ioe) {
529 throw new JUploadIOException(ioe.getClass().getName()
530 + " in FileUploadThreadFTP.createDirectoryStructure()", ioe);
531 }
532 }
533
534
535 @Override
536 void interruptionReceived() {
537 cleanAll();
538 }
539 }