001: /*
002: * This program is free software; you can redistribute it and/or
003: * modify it under the terms of the GNU General Public License
004: * as published by the Free Software Foundation; either version 2
005: * of the License, or (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011:
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
015: */
016: package net.sf.jftp.net;
017:
018: import java.io.BufferedInputStream;
019: import java.io.BufferedOutputStream;
020: import java.io.File;
021: import java.io.FileOutputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.RandomAccessFile;
025: import java.io.StreamTokenizer;
026: import java.net.ServerSocket;
027: import java.net.Socket;
028:
029: import net.sf.jftp.config.Settings;
030: import net.sf.jftp.system.logging.Log;
031:
032: /**
033: * This class represents a ftp data connection.
034: * It is used internally by FtpConnection, so you probably don't have to use it directly.
035: */
036: public class DataConnection implements Runnable {
037: public final static String GET = "GET";
038: public final static String PUT = "PUT";
039: public final static String FAILED = "FAILED";
040: public final static String FINISHED = "FINISHED";
041: public final static String DFINISHED = "DFINISHED";
042: public final static String GETDIR = "DGET";
043: public final static String PUTDIR = "DPUT";
044:
045: private BufferedInputStream in = null;
046: private BufferedOutputStream out = null;
047: private Thread reciever;
048: private int port = 7000;
049: public Socket sock = null;
050: private ServerSocket ssock = null;
051: private String type;
052: private String file;
053: private String host;
054: private boolean resume = false;
055: public boolean finished = false;
056: private boolean isThere = false;
057: private long start;
058: private FtpConnection con;
059: private int skiplen = 0;
060: private boolean justStream = false;
061: private boolean ok = true;
062: private String localfile = null;
063: //private String outputCharset = "CP037";
064: private String newLine = null;
065: private String LINEEND = System.getProperty("line.separator");
066:
067: public DataConnection(FtpConnection con, int port, String host,
068: String file, String type) {
069: this .con = con;
070: this .file = file;
071: this .host = host;
072: this .port = port;
073: this .type = type;
074: reciever = new Thread(this );
075: reciever.start();
076: }
077:
078: public DataConnection(FtpConnection con, int port, String host,
079: String file, String type, boolean resume) {
080: this .con = con;
081: this .file = file;
082: this .host = host;
083: this .port = port;
084: this .type = type;
085: this .resume = resume;
086:
087: //resume = false;
088: reciever = new Thread(this );
089: reciever.start();
090: }
091:
092: public DataConnection(FtpConnection con, int port, String host,
093: String file, String type, boolean resume, boolean justStream) {
094: this .con = con;
095: this .file = file;
096: this .host = host;
097: this .port = port;
098: this .type = type;
099: this .resume = resume;
100: this .justStream = justStream;
101:
102: //resume = false;
103: reciever = new Thread(this );
104: reciever.start();
105: }
106:
107: public DataConnection(FtpConnection con, int port, String host,
108: String file, String type, boolean resume, String localfile) {
109: this .con = con;
110: this .file = file;
111: this .host = host;
112: this .port = port;
113: this .type = type;
114: this .resume = resume;
115: this .localfile = localfile;
116:
117: //resume = false;
118: reciever = new Thread(this );
119: reciever.start();
120: }
121:
122: public DataConnection(FtpConnection con, int port, String host,
123: String file, String type, boolean resume, int skiplen) {
124: this .con = con;
125: this .file = file;
126: this .host = host;
127: this .port = port;
128: this .type = type;
129: this .resume = resume;
130: this .skiplen = skiplen;
131:
132: //resume = false;
133: reciever = new Thread(this );
134: reciever.start();
135: }
136:
137: public DataConnection(FtpConnection con, int port, String host,
138: String file, String type, boolean resume, int skiplen,
139: InputStream i) {
140: this .con = con;
141: this .file = file;
142: this .host = host;
143: this .port = port;
144: this .type = type;
145: this .resume = resume;
146: this .skiplen = skiplen;
147:
148: if (i != null) {
149: this .in = new BufferedInputStream(i);
150: }
151:
152: //resume = false;
153: reciever = new Thread(this );
154: reciever.start();
155: }
156:
157: public void run() {
158: try {
159: newLine = con.getCRLF();
160: //Log.debug("NL: "+newLine+"\n\n\n\n\n");
161:
162: if (Settings.getFtpPasvMode()) {
163: try {
164: sock = new Socket(host, port);
165: sock.setSoTimeout(Settings.getSocketTimeout());
166: } catch (Exception ex) {
167: ok = false;
168: debug("Can't open Socket on port " + port);
169: }
170: } else {
171: //Log.debug("trying new server socket: "+port);
172: try {
173: ssock = new ServerSocket(port);
174: } catch (Exception ex) {
175: ok = false;
176: Log
177: .debug("Can't open ServerSocket on port "
178: + port);
179: }
180: }
181: } catch (Exception ex) {
182: debug(ex.toString());
183: }
184:
185: isThere = true;
186:
187: boolean ok = true;
188:
189: RandomAccessFile fOut = null;
190: BufferedOutputStream bOut = null;
191: RandomAccessFile fIn = null;
192:
193: try {
194: if (!Settings.getFtpPasvMode()) {
195: int retry = 0;
196:
197: while ((retry++ < 5) && (sock == null)) {
198: try {
199: ssock.setSoTimeout(Settings.connectionTimeout);
200: sock = ssock.accept();
201: } catch (IOException e) {
202: sock = null;
203: debug("Got IOException while trying to open a socket!");
204:
205: if (retry == 5) {
206: debug("Connection failed, tried 5 times - maybe try a higher timeout in Settings.java...");
207: }
208:
209: finished = true;
210:
211: throw e;
212: } finally {
213: ssock.close();
214: }
215:
216: debug("Attempt timed out, retrying...");
217: }
218: }
219:
220: if (ok) {
221: byte[] buf = new byte[Settings.bufferSize];
222: start = System.currentTimeMillis();
223:
224: int buflen = 0;
225:
226: //---------------download----------------------
227: if (type.equals(GET) || type.equals(GETDIR)) {
228: if (!justStream) {
229: try {
230: if (resume) {
231: File f = new File(file);
232: fOut = new RandomAccessFile(file, "rw");
233: fOut.skipBytes((int) f.length());
234: buflen = (int) f.length();
235: } else {
236: if (localfile == null) {
237: localfile = file;
238: }
239:
240: File f2 = new File(Settings.appHomeDir);
241: f2.mkdirs();
242:
243: File f = new File(localfile);
244:
245: if (f.exists()) {
246: f.delete();
247: }
248:
249: bOut = new BufferedOutputStream(
250: new FileOutputStream(localfile),
251: Settings.bufferSize);
252: }
253: } catch (Exception ex) {
254: debug("Can't create outputfile: " + file);
255: ok = false;
256: ex.printStackTrace();
257: }
258: }
259:
260: if (ok) {
261: try {
262: in = new BufferedInputStream(sock
263: .getInputStream(),
264: Settings.bufferSize);
265:
266: if (justStream) {
267: return;
268: }
269: } catch (Exception ex) {
270: ok = false;
271: debug("Can't get InputStream");
272: }
273:
274: if (ok) {
275: try {
276: int len = buflen;
277:
278: if (fOut != null) {
279: while (true) {
280: // resuming
281: int read = -2;
282:
283: try {
284: read = in.read(buf);
285: } catch (IOException es) {
286: Log
287: .out("got a IOException");
288: ok = false;
289: fOut.close();
290: finished = true;
291: con.fireProgressUpdate(
292: file, FAILED, -1);
293:
294: Log.out("last read: "
295: + read + ", len: "
296: + (len + read));
297: es.printStackTrace();
298:
299: return;
300: }
301:
302: len += read;
303:
304: if (read == -1) {
305: break;
306: }
307:
308: if (newLine != null) {
309: byte[] buf2 = modifyGet(
310: buf, read);
311: fOut.write(buf2, 0,
312: buf2.length);
313: } else {
314: fOut.write(buf, 0, read);
315: }
316:
317: con.fireProgressUpdate(file,
318: type, len);
319:
320: if (time()) {
321: // Log.debugSize(len, true, false, file);
322: }
323:
324: if (read == StreamTokenizer.TT_EOF) {
325: break;
326: }
327: }
328:
329: //Log.debugSize(len, true, true, file);
330: } else {
331: // no resuming
332: while (true) {
333: //System.out.println(".");
334: int read = -2;
335:
336: try {
337: read = in.read(buf);
338: } catch (IOException es) {
339: Log
340: .out("got a IOException");
341: ok = false;
342: bOut.close();
343: finished = true;
344: con.fireProgressUpdate(
345: file, FAILED, -1);
346:
347: Log.out("last read: "
348: + read + ", len: "
349: + (len + read));
350: es.printStackTrace();
351:
352: return;
353: }
354:
355: len += read;
356:
357: if (read == -1) {
358: break;
359: }
360:
361: if (newLine != null) {
362: byte[] buf2 = modifyGet(
363: buf, read);
364: bOut.write(buf2, 0,
365: buf2.length);
366: } else {
367: bOut.write(buf, 0, read);
368: }
369:
370: con.fireProgressUpdate(file,
371: type, len);
372:
373: if (time()) {
374: //Log.debugSize(len, true, false, file);
375: }
376:
377: if (read == StreamTokenizer.TT_EOF) {
378: break;
379: }
380: }
381:
382: bOut.flush();
383:
384: //Log.debugSize(len, true, true, file);
385: }
386: } catch (IOException ex) {
387: ok = false;
388: debug("Old connection removed");
389: con
390: .fireProgressUpdate(file,
391: FAILED, -1);
392:
393: //debug(ex + ": " + ex.getMessage());
394: ex.printStackTrace();
395: }
396: }
397: }
398: }
399:
400: //---------------upload----------------------
401: if (type.equals(PUT) || type.equals(PUTDIR)) {
402: if (in == null) {
403: try {
404: fIn = new RandomAccessFile(file, "r");
405:
406: if (resume) {
407: fIn.skipBytes(skiplen);
408: }
409:
410: //fIn = new BufferedInputStream(new FileInputStream(file));
411: } catch (Exception ex) {
412: debug("Can't open inputfile: " + " (" + ex
413: + ")");
414: ok = false;
415: }
416: }
417:
418: if (ok) {
419: try {
420: out = new BufferedOutputStream(sock
421: .getOutputStream());
422: } catch (Exception ex) {
423: ok = false;
424: debug("Can't get OutputStream");
425: }
426:
427: if (ok) {
428: try {
429: int len = skiplen;
430: char b;
431:
432: while (true) {
433: int read;
434:
435: if (in != null) {
436: read = in.read(buf);
437: } else {
438: read = fIn.read(buf);
439: }
440:
441: len += read;
442:
443: //System.out.println(file + " " + type+ " " + len + " " + read);
444: if (read == -1) {
445: break;
446: }
447:
448: if (newLine != null) {
449: byte[] buf2 = modifyPut(buf,
450: read);
451: out.write(buf2, 0, buf2.length);
452: } else {
453: out.write(buf, 0, read);
454: }
455:
456: con.fireProgressUpdate(file, type,
457: len);
458:
459: if (time()) {
460: // Log.debugSize(len, false, false, file);
461: }
462:
463: if (read == StreamTokenizer.TT_EOF) {
464: break;
465: }
466: }
467:
468: out.flush();
469:
470: //Log.debugSize(len, false, true, file);
471: } catch (IOException ex) {
472: ok = false;
473: debug("Error: Data connection closed.");
474: con
475: .fireProgressUpdate(file,
476: FAILED, -1);
477: ex.printStackTrace();
478: }
479: }
480: }
481: }
482: }
483: } catch (IOException ex) {
484: Log.debug("Can't connect socket to ServerSocket");
485: ex.printStackTrace();
486: } finally {
487: try {
488: if (out != null) {
489: out.flush();
490: out.close();
491: }
492: } catch (Exception ex) {
493: ex.printStackTrace();
494: }
495:
496: try {
497: if (bOut != null) {
498: bOut.flush();
499: bOut.close();
500: }
501: } catch (Exception ex) {
502: ex.printStackTrace();
503: }
504:
505: try {
506: if (fOut != null) {
507: fOut.close();
508: }
509: } catch (Exception ex) {
510: ex.printStackTrace();
511: }
512:
513: try {
514: if (in != null && !justStream) {
515: in.close();
516: }
517:
518: if (fIn != null) {
519: fIn.close();
520: }
521: } catch (Exception ex) {
522: ex.printStackTrace();
523: }
524: }
525:
526: try {
527: sock.close();
528: } catch (Exception ex) {
529: debug(ex.toString());
530: }
531:
532: if (!Settings.getFtpPasvMode()) {
533: try {
534: ssock.close();
535: } catch (Exception ex) {
536: debug(ex.toString());
537: }
538: }
539:
540: finished = true;
541:
542: if (ok) {
543: con.fireProgressUpdate(file, FINISHED, -1);
544: } else {
545: con.fireProgressUpdate(file, FAILED, -1);
546: }
547: }
548:
549: public InputStream getInputStream() {
550: return in;
551: }
552:
553: public FtpConnection getCon() {
554: return con;
555: }
556:
557: private void debug(String msg) {
558: Log.debug(msg);
559: }
560:
561: public void reset() {
562: reciever.destroy();
563: reciever = new Thread(this );
564: reciever.start();
565: }
566:
567: private void pause(int time) {
568: try {
569: reciever.sleep(time);
570: } catch (Exception ex) {
571: System.out.println(ex);
572: }
573: }
574:
575: private boolean time() {
576: long now = System.currentTimeMillis();
577: long offset = now - start;
578:
579: if (offset > Settings.statusMessageAfterMillis) {
580: start = now;
581:
582: return true;
583: }
584:
585: return false;
586: }
587:
588: public boolean isThere() {
589: if (finished) {
590: return true;
591: }
592:
593: return isThere;
594: }
595:
596: public void setType(String tmp) {
597: type = tmp;
598: }
599:
600: public boolean isOK() {
601: return ok;
602: }
603:
604: public void interrupt() {
605: if (Settings.getFtpPasvMode()
606: && (type.equals(GET) || type.equals(GETDIR))) {
607: try {
608: reciever.join();
609: } catch (InterruptedException ex) {
610: ex.printStackTrace();
611: }
612: }
613: }
614:
615: private byte[] modifyPut(byte[] buf, int len) {
616: //Log.debug("\n\n\n\nNewline: "+newLine);
617: if (newLine == null)
618: return buf;
619:
620: String s = (new String(buf)).substring(0, len);
621: s = s.replaceAll(LINEEND, newLine);
622: //Log.debug("Newline_own: "+LINEEND+", s:"+s);
623:
624: return s.getBytes();
625: }
626:
627: private byte[] modifyGet(byte[] buf, int len) {
628: //Log.debug("\n\n\n\nNewline: "+newLine);
629: if (newLine == null)
630: return buf;
631:
632: String s = (new String(buf)).substring(0, len);
633: s = s.replaceAll(newLine, LINEEND);
634: //Log.debug("Newline_own: "+LINEEND+", s:"+s);
635:
636: return s.getBytes();
637: }
638: }
|