Source code: cxtable/xDownloadLinlyn.java
1 package cxtable;
2
3 /*this is a modified Linlyn (under the KDE artistic license)...
4 it is for future filesharing issues...not currently in use... */
5
6
7 import java.io.*;
8 import java.net.*;
9 import java.util.*;
10
11 public class xDownloadLinlyn extends Thread{
12
13 // FOR DEBUGGING: set the variable to "true"
14 private boolean DEBUG = false;
15 private String dir="";
16 private String server,file; private int port;private xDownloadListener xdl;
17 // constructor needs servername, username and passwd
18
19
20 public xDownloadLinlyn(String _server, int _port, String _file, xDownloadListener _xdl)
21 {
22 port = _port; server = _server; file = _file;xdl=_xdl;
23 }
24 public xDownloadLinlyn(String _server, String _file, xDownloadListener _xdl)
25 {
26 port = CNTRL_PORT; server = _server; file = _file;xdl=_xdl;
27 }
28 public void setDir(String d)
29 {
30 dir=d;
31 }
32
33
34 public void run()
35 { String contents="";
36 try {
37 ftpConnect(server);
38 }
39 catch(IOException ioe) {
40 xdl.dl_error(file, ioe); return;}
41 try{
42 contents = download(false);
43 }
44 catch(Exception e)
45 {xdl.dl_error(file,e); return;}
46 xdl.process(file,contents);
47 return;
48 }
49
50 private String download(boolean asc)
51 throws IOException {
52 if (dir.equals("")==false){ftpSetDir(dir);}
53 ftpSetTransferType(asc);
54 dsock = ftpGetDataSock();
55 InputStream is = dsock.getInputStream();
56 ftpSendCmd("RETR "+file);
57
58 String cont = getAsString(is);
59 ftpLogout();
60 return cont;
61 }
62 ///////////////// private fields ////////////////////
63 private boolean pauser = false; // it's a hack. We're going to
64 // stall (refuse further requests) till we get a reply back
65 // from server for the current request.
66
67 private String getAsString(InputStream is) {
68 int c=0;
69 char lineBuffer[]=new char[128], buf[]=lineBuffer;
70 int room= buf.length, offset=0;
71 try {
72 loop:
73 while (true) {
74 // read chars into a buffer which grows as needed
75 switch (c = is.read() ) {
76 case -1:
77 break loop;
78
79 default:
80 if (--room < 0) {
81 buf = new char[offset + 128];
82 room = buf.length - offset - 1;
83 System.arraycopy(lineBuffer, 0,
84 buf, 0, offset);
85 lineBuffer = buf;
86 }
87 buf[offset++] = (char) c;
88 break;
89 }
90 }
91 }
92 catch(IOException ioe) {
93 ioe.printStackTrace();}
94 if ((c == -1) && (offset == 0)) {
95 return null;
96 }
97 return String.copyValueOf(buf, 0, offset);
98 }
99
100 private void ftpConnect(String server)
101 throws IOException {
102 // Set up socket, control streams, connect to ftp server
103 // Open socket to server control port 21
104 csock = new Socket(server, port);
105 // Open control streams
106 InputStream cis = csock.getInputStream();
107 dcis = new BufferedReader(new InputStreamReader(cis));
108 OutputStream cos = csock.getOutputStream();
109 pos = new PrintWriter(cos, true); // set auto flush true.
110 // See if server is alive or dead...
111 String numerals = responseHandler(null);
112 if(numerals.substring(0,3).equals("220")) // ftp server alive
113 ; // System.out.println("Connected to ftp server");
114 else System.err.println("Error connecting to ftp server.");
115 }
116
117 private void ftpSetDir(String dir)
118 throws IOException {
119 // cwd to dir
120 ftpSendCmd("CWD "+dir);
121 }
122
123 private void ftpSetTransferType(boolean asc)
124 throws IOException {
125 // set file transfer type
126 String ftype = (asc? "A" : "I");
127 ftpSendCmd("TYPE "+ftype);
128 }
129
130 private Socket ftpGetDataSock()
131 throws IOException {
132 // Go to PASV mode, capture server reply, parse for socket setup
133 // V2.1: generalized port parsing, allows more server variations
134 String reply = ftpSendCmd("PASV");
135
136 // New technique: just find numbers before and after ","!
137 StringTokenizer st = new StringTokenizer(reply, ",");
138 String[] parts = new String[6]; // parts, incl. some garbage
139 int i = 0; // put tokens into String array
140 while(st.hasMoreElements()) {
141 // stick pieces of host, port in String array
142 try {
143 parts[i] = st.nextToken();
144 i++;
145 }
146 catch(NoSuchElementException nope){
147 nope.printStackTrace();}
148 } // end getting parts of host, port
149
150 // Get rid of everything before first "," except digits
151 String[] possNum = new String[3];
152 for(int j = 0; j < 3; j++) {
153 // Get 3 characters, inverse order, check if digit/character
154 possNum[j] = parts[0].substring(parts[0].length() - (j + 1),
155 parts[0].length() - j); // next: digit or character?
156 if(!Character.isDigit(possNum[j].charAt(0)))
157 possNum[j] = "";
158 }
159 parts[0] = possNum[2] + possNum[1] + possNum[0];
160 // Get only the digits after the last ","
161 String[] porties = new String[3];
162 for(int k = 0; k < 3; k++) {
163 // Get 3 characters, in order, check if digit/character
164 // May be less than 3 characters
165 if((k + 1) <= parts[5].length())
166 porties[k] = parts[5].substring(k, k + 1);
167 else porties[k] = "FOOBAR"; // definitely not a digit!
168 // next: digit or character?
169 if(!Character.isDigit(porties[k].charAt(0)))
170 porties[k] = "";
171 } // Have to do this one in order, not inverse order
172 parts[5] = porties[0] + porties[1] + porties[2];
173 // Get dotted quad IP number first
174 String ip = parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3];
175
176 // Determine port
177 int port = -1;
178 try { // Get first part of port, shift by 8 bits.
179 int big = Integer.parseInt(parts[4]) << 8;
180 int small = Integer.parseInt(parts[5]);
181 port = big + small; // port number
182 }
183 catch(NumberFormatException nfe) {
184 nfe.printStackTrace();}
185 if((ip != null) && (port != -1))
186
187 dsock = new Socket(ip, port);
188 else throw new IOException();
189 return dsock;
190 }
191
192 private String ftpSendCmd(String cmd)
193 throws IOException
194 { // This sends a dialog string to the server, returns reply
195 // V2.0 Updated to parse multi-string responses a la RFC 959
196 // Prints out only last response string of the lot.
197 if (pauser) // i.e. we already issued a request, and are
198 // waiting for server to reply to it.
199 {
200 if (dcis != null)
201 {
202 String discard = dcis.readLine(); // will block here
203 // preventing this further client request until server
204 // responds to the already outstanding one.
205 if (DEBUG) {
206 System.out.println("keeping handler in sync"+
207 " by discarding next response: ");
208 System.out.println(discard);
209 }
210 pauser = false;
211 }
212 }
213 pos.print(cmd + "\r\n" );
214 pos.flush();
215 String response = responseHandler(cmd);
216 return response;
217 }
218
219 // new method to read multi-line responses
220 // responseHandler: takes a String command or null and returns
221 // just the last line of a possibly multi-line response
222 private String responseHandler(String cmd)
223 throws IOException
224 { // handle more than one line returned
225 String reply = this.responseParser(dcis.readLine());
226 String numerals = reply.substring(0, 3);
227 String hyph_test = reply.substring(3, 4);
228 String next = null;
229 if(hyph_test.equals("-")) {
230 // Create "tester", marks end of multi-line output
231 String tester = numerals + " ";
232 boolean done = false;
233 while(!done) { // read lines til finds last line
234 next = dcis.readLine();
235 // Read "over" blank line responses
236 while (next.equals("") || next.equals(" ")) {
237 next = dcis.readLine();
238 }
239
240 // If next starts with "tester", we're done
241 if(next.substring(0,4).equals(tester))
242 done = true;
243 }
244
245 if(DEBUG)
246 if(cmd != null)
247 System.out.println("Response to: "+cmd+" was: "+next);
248 else
249 System.out.println("Response was: "+next);
250 return next;
251
252 }
253 else // "if (hyph_test.equals("-")) not true"
254 if(DEBUG)
255 if(cmd != null)
256 System.out.println("Response to: "+cmd+" was: "+reply);
257 else
258 System.out.println("Response was: "+reply);
259 return reply;
260 }
261
262 // responseParser: check first digit of first line of response
263 // and take action based on it; set up to read an extra line
264 // if the response starts with "1"
265 private String responseParser(String resp)
266 throws IOException
267 { // Check first digit of resp, take appropriate action.
268 String digit1 = resp.substring(0, 1);
269 if(digit1.equals("1")) {
270 // server to act, then give response
271 if(DEBUG) System.out.println("in 1 handler");
272 // set pauser
273 pauser = true;
274 return resp;
275 }
276 else if(digit1.equals("2")) { // do usual handling
277 if(DEBUG) System.out.println("in 2 handler");
278 // reset pauser
279 pauser = false;
280 return resp;
281 }
282 else if(digit1.equals("3") || digit1.equals("4")
283 || digit1.equals("5")) { // do usual handling
284 if(DEBUG) System.out.println("in 3-4-5 handler");
285 return resp;
286 }
287 else { // not covered, so return null
288 return null;
289 }
290 }
291
292
293 private void ftpLogout() {// logout, close streams
294 try {
295 if(DEBUG) System.out.println("sending BYE");
296 pos.print("BYE" + "\r\n" );
297 pos.flush();
298 pos.close();
299 dcis.close();
300 csock.close();
301 dsock.close();
302 }
303 catch(IOException ioe) {
304 ioe.printStackTrace();}
305 }
306
307
308 private static final int CNTRL_PORT = 21;
309 private Socket csock = null;
310 private Socket dsock = null;
311 private BufferedReader dcis;
312 private PrintWriter pos;
313 }
314
315 /*this software is based on Peter van der Linden and Steve Lynch's "Linlyn" ftp
316 utility. The "upload" and "append" routines have been removed. It has been threaded, and this
317 particular edit makes it purely the client download end with no upload
318 capabilities. The "Linlyn" program is readily available at Peter's site
319 (<insert link>) and is licensed under the "Artistic License".
320 */