1 //
2 // DonkeyTalker.java
3 // CocoDonkey
4 // $Id: DonkeyTalker.java,v 1.26 2003/01/01 19:27:15 fortun Exp $
5 //
6 // Created by Fred Bonzoun on Sat May 11 2002.
7 // Copyright (c) 2002 Bonzoun. All rights reserved.
8 //
9 // This library is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published
11 // by the Free Software Foundation; either version 2.1 of the License, or
12 // (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 //
23
24 package net.bonzoun.cocodonkey;
25
26 import com.apple.cocoa.foundation;
27 import com.apple.cocoa.application;
28
29 import java.io;
30 import java.net;
31 import java.util;
32 import gnu.regexp;
33
34 public class DonkeyTalker {
35
36 private String hostname;
37 private SshTunnel tunnel;
38 private int port;
39 private Socket socket;
40 private int defaultTimeout = 30000;
41 protected BufferedReader in;
42 protected PrintWriter out;
43 private long lastCallTime;
44 private StringBuffer dialog = new StringBuffer();
45 private int dialogOffset = 0;
46 private Vector listeners = new Vector();
47 private boolean isDead = false;
48 private Logger logger;
49 private String lastCommand;
50 private boolean shouldStopCommand = false;
51
52 private CamlFile fileFiles;
53 private CamlFile fileServers;
54 private CamlFile fileSharedFiles;
55
56 protected RE reVULine1;
57 protected RE reVULine2;
58 protected RE reVDDown0;
59 protected RE reVDDown0b;
60 protected RE reVDLine;
61 protected RE reVDLineb;
62 protected RE reVDDown1;
63 protected RE reVDDown;
64 protected RE reVDEnd;
65 protected RE reVDNLine1;
66 protected RE reVDNChunks;
67 protected RE reVDNSources;
68 protected RE reS;
69 protected RE reVRRunning;
70 protected RE reVRDone;
71 protected RE reVRTitle;
72 //protected RE reVRData;
73 //protected RE reVRSubData;
74 protected RE reVREnd;
75 protected RE reCOMMIT;
76 protected RE reVSTitle;
77 protected RE reVSLine;
78 protected RE reVSData;
79 protected RE reVOData;
80 protected RE reVMLine;
81 protected RE reVMALine;
82 protected RE reUPTitle;
83 protected RE reUPTitle2;
84 protected RE reUPLine;
85 protected RE reUPDone;
86 protected RE reError;
87
88 public DonkeyTalker(String path, Logger logger) {
89 this.logger = logger;
90 port = 4000;
91 init(path);
92 }
93
94 public DonkeyTalker(String path, SshTunnel tunnel, Logger logger) {
95 this.tunnel = tunnel;
96 this.logger = logger;
97 port = tunnel.port();
98 init(path);
99 }
100
101 public DonkeyTalker(String path, String host, Logger logger) {
102 hostname = host;
103 this.logger = logger;
104 port = 4000;
105 init(path);
106 }
107
108 private void init(String path) {
109 try {
110 reVULine1 = new RE("credits\\D+(\\d+)\\s*m");
111 reVULine2 = new RE("disabled\\D+(\\d+)\\s*m");
112 reVDDown0 = new RE("^Downloaded\\s+(\\d+)/");
113 reVDDown0b = new RE("^Downloading\\s+(\\d+)");
114 reVDLine = new RE("\\[\\s*(\\d+)\\s*\\]\\s+(.{1,50})\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s+\\S+\\s+(\\S+)");
115 reVDLineb = new RE("\\[\\s*(\\d+)\\s*\\]\\s+(.{1,50})(\\s+)(\\d+)\\s+(\\d+)\\s+(\\S+)");
116 reVDDown1 = new RE("^Downloaded\\s+(\\d+)\\s+files");
117 reVDDown = new RE("\\[\\s*(\\d+)\\s*\\]\\s+(\\S.*\\S)\\s+(\\d+)\\s+([0-9,A-F]+)");
118 reVDEnd = new RE("^Use 'commit'");
119 reVDNLine1 = new RE("\\[\\D*(\\d+)\\s*\\]\\s*(\\S.+\\S)\\s+(\\d+)\\s+(\\d+)");
120 reVDNChunks = new RE("^Chunks:\\s*\\[(\\d+)\\]");
121 reVDNSources = new RE("(\\d+)\\s*sources");
122 reS = new RE("Query\\D+(\\d+)\\D+(\\d+)");
123 reVRRunning = new RE("(\\d+)\\s+results\\D+(\\d+)\\s+waiting");
124 reVRDone = new RE("(\\d+)\\s+results.+done");
125 reVRTitle = new RE("\\[\\s*(\\d+)\\s*\\]\\s*(Donkey )?(.+)");
126 //reVRData = new RE("(\\d+)\\s+([0-9,A-F]+)\\s+(\\S.*\\S\\s+)?(\\d+)");
127 //reVRSubData = new RE("^(\\S+)\\s+(\\S+)\\s+((\\S.*\\S)\\s+(\\d+:\\s*\\d+)\\s+((\\d+)\\s+)?)?");
128 reVREnd = new RE("Bad number of arguments");
129 reCOMMIT = new RE("commited");
130 reVSTitle = new RE("Searching\\s+(\\d+)");
131 reVSLine = new RE("\\[(\\d+)\\s*\\]\\s*(\\S.*\\S)\\s+(\\S+)");
132 reVSData = new RE("CONTAINS\\[([^\\]]+)\\]");
133 reVOData = new RE("(\\S+)\\s*=\\s*(\\S+)");
134 reVMLine = new RE("\\[\\s*(\\d+)\\s*\\]\\s+(\\S+)\\s+(\\S.*\\S)?\\s+(\\d+)\\s+(\\d+)");
135 reVMALine = new RE("\\[\\s*(\\d+)\\s*\\]\\s+(\\S+)\\s*(\\S.*)?");
136 reUPTitle = new RE("^Total:\\s+(\\d+).+uploaded");
137 reUPTitle2 = new RE("^Total:\\s+(\\d+).+bytes.+uploaded");
138 reUPLine = new RE("^(.+)\\s+requests:\\s+(\\d+)\\s+blocs:\\s+(\\d+)");
139 reUPDone = new RE("^done$");
140 reError = new RE("^exception\\s*\\[(.+)\\]");
141 }
142 catch(REException e) {
143 throw new Error("Bad: " + e);
144 }
145 fileFiles = new CamlFile(path + "/files.ini");
146 fileServers = new CamlFile(path + "/servers.ini");
147 fileSharedFiles = new CamlFile(path + "/shared_files.ini");
148 }
149
150 public void stopCommandIfCurrent(String cmd) {
151 if (lastCommand!=null && lastCommand==cmd) {
152 shouldStopCommand = true;
153 }
154 }
155
156 public String hostname() {
157 return hostname;
158 }
159
160 public boolean processIsLocal() {
161 return (hostname==null || hostname.equals("127.0.0.1") || hostname.equals("localhost"))
162 && tunnel==null;
163 }
164
165 public synchronized void addListener(Listener listener) {
166 listeners.add(listener);
167 }
168
169 public synchronized void removeListener(Listener listener) {
170 listeners.remove(listener);
171 }
172
173 public int currentDialogOffset() {
174 return dialogOffset;
175 }
176
177 public String dialogSince(int n) {
178 if (n>=dialogOffset+dialog.length())
179 return "";
180 else if (n<=dialogOffset)
181 return dialog.toString();
182 else
183 return dialog.substring(n-dialogOffset);
184 }
185
186 public synchronized void reconnect() {
187 Thread.dumpStack();
188 try {
189 close();
190 }
191 catch(IOException e) {
192 }
193 try {
194 connect();
195 }
196 catch(IOException e) {
197 }
198 }
199
200 public void connect() throws IOException {
201 connect(30000);
202 }
203
204 /*package*/ synchronized void connect(int timeout) throws IOException {
205 if (isDead)
206 return;
207 if (socket!=null)
208 close();
209 defaultTimeout = timeout;
210 InetAddress addr = InetAddress.getByName(hostname!=null ? hostname : "127.0.0.1");
211 socket = new Socket(addr, port);
212 socket.setSoTimeout(timeout);
213 socket.setSoLinger(true, 20);
214 in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ISO8859_1"));
215 out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "ISO8859_1"));
216 logCommandln("<<Open connection>>");
217 testConnection();
218 }
219
220 public synchronized void finalClose() {
221 isDead = true;
222 try {
223 close();
224 }
225 catch (IOException e) {
226 }
227 finally {
228 if (tunnel!=null) {
229 tunnel.close();
230 tunnel = null;
231 }
232 }
233 }
234
235 public synchronized void close() throws IOException {
236 if (out!=null) {
237 out.close();
238 out = null;
239 }
240 if (in!=null) {
241 in.close();
242 in = null;
243 }
244 if (socket!=null) {
245 logCommandln("<<Close connection>>");
246 Socket s = socket;
247 socket = null;
248 s.close();
249 }
250 }
251
252 public synchronized boolean finalKill() {
253 isDead = true;
254 for(int i=0; i<listeners.size(); i++)
255 ((Listener)listeners.elementAt(i)).stopListening();
256 boolean cr = kill();
257 finalClose();
258 return cr;
259 }
260
261 public synchronized boolean kill() {
262 if (socket==null)
263 return true;
264 // sendNoWait("kill");
265 try {
266 socket.setSoTimeout(10000);
267 sendNoWait("kill");
268 String line = readLine();
269 if (line!=null && reError.getMatch(line)!=null)
270 return false;
271 else
272 return true;
273 }
274 catch(IOException e) {
275 return true;
276 }
277 finally {
278 try {
279 socket.setSoTimeout(defaultTimeout);
280 } catch(IOException e) {}
281 }
282 }
283
284 public synchronized void quit() {
285 if (socket==null)
286 return;
287 try {
288 socket.setSoTimeout(2000);
289 sendNoWait("q");
290 sendNoWait("");
291 finalClose();
292 }
293 catch(IOException e) {
294 e.printStackTrace();
295 }
296 }
297
298 public synchronized void testConnection() throws IOException {
299 send("");
300 if (socket==null)
301 return;
302 while (in.ready() && !shouldStopCommand)
303 readLine();
304 }
305
306 public synchronized void addServers() {
307 sendNoFail("c");
308 }
309
310 public synchronized void addAServer(String host, String port) {
311 if (port==null)
312 port = "";
313 sendNoFail("n " + host + " " + port);
314 }
315
316 public synchronized void addServerMet(String file) {
317 sendNoFail("servers " + file);
318 }
319
320 public synchronized void connectToServer(String id) {
321 sendNoFail("c " + id);
322 }
323
324 public synchronized void closeFds() {
325 sendNoFail("close_fds");
326 }
327
328 public synchronized void reshare() {
329 sendNoFail("reshare");
330 }
331
332 public synchronized void recoverTrash() {
333 sendNoFail("recover_temp");
334 }
335
336 public synchronized void removeOldServers() {
337 sendNoFail("remove_old_servers");
338 }
339
340 public synchronized void download(GenericItem item) {
341 sendNoFail("d " + item.size() + " " + item.hash());
342 // Addition for mldonkey version > 1.16
343 sendNoFail("dd " + item.size() + " " + item.hash());
344 }
345
346 public synchronized void download(String link) {
347 sendNoFail("dllink " + link);
348 }
349
350 public synchronized int uploadCredit() {
351 String result = sendAndSearchResult("vu", reVULine1, "$1");
352 if (result!=null)
353 return Integer.parseInt(result);
354 else
355 return -1;
356 }
357
358 public synchronized int uploadDisabledTime() {
359 String result = sendAndSearchResult("vu", reVULine2, "$1");
360 if (result!=null)
361 return Integer.parseInt(result);
362 else
363 return -1;
364 }
365
366 public synchronized void disableUploadFor(int n) {
367 sendAndSearchResult("nu " + n, reVULine2, "$1");
368 }
369
370 public synchronized NSArray downloadItems() {
371 NSMutableArray liste = new NSMutableArray();
372 int state = 0;
373 try {
374 send("vd");
375 if (socket==null)
376 return new NSArray();
377 while(state<3 && !shouldStopCommand) {
378 String line = readLine();
379 if (line==null)
380 break;
381 switch (state) {
382 case 0: {
383 REMatch result = reVDDown0.getMatch(line);
384 if (result==null)
385 result = reVDDown0b.getMatch(line);
386 if (result!=null) {
387 state = 1;
388 }
389 else {
390 result = reVDDown1.getMatch(line);
391 if (result!=null) {
392 int n = strongParseInt(result.substituteInto("$1"), -1);
393 if (n<=0)
394 state = 3;
395 else
396 state = 2;
397 }
398 }
399 break;
400 }
401 case 1: {
402 StringParser parser = new StringParser(line);
403 if (parser.goAfter('[')) {
404 parser.getNextStringUntil(StringParser.INTEGER);
405 int n = parser.getNextIntUntil("]", -1);
406 parser.goAfter(']');
407 String r = parser.getLastString();
408 float rate;
409 if (r.equalsIgnoreCase("paused"))
410 rate = -2;
411 else
412 rate = strongParseFloat(r, -1);
413 int total = parser.getLastInt(-1);
414 int downloaded;
415 float percent;
416 if (total<0) {
417 total = parser.getLastInt(-1);
418 downloaded = parser.getLastInt(-1);
419 percent = parser.getLastFloat(-1);
420 } else {
421 downloaded = parser.getLastInt(-1);
422 percent = -1;
423 }
424 String name = parser.getRemainingString();
425 if (n>=0)
426 liste.addObject(new DownloadItem(n, name, percent, downloaded, total, rate));
427 }
428
429 /*REMatch result = reVDLine.getMatch(line);
430 if (result==null)
431 result = reVDLineb.getMatch(line);
432 if (result!=null) {
433 int n = strongParseInt(result.substituteInto("$1"), -1);
434 String name = result.substituteInto("$2");
435 float percent = strongParseFloat(result.substituteInto("$3"), -1);
436 int downloaded = strongParseInt(result.substituteInto("$4"), -1);
437 int total = strongParseInt(result.substituteInto("$5"), -1);
438 String r = result.substituteInto("$6");
439 float rate;
440 if (r.equalsIgnoreCase("paused"))
441 rate = -2;
442 else
443 rate = strongParseFloat(r, -1);
444 liste.addObject(new DownloadItem(n, name, percent, downloaded, total, rate));
445 }*/
446 else {
447 REMatch result = reVDDown1.getMatch(line);
448 if (result!=null) {
449 int n = strongParseInt(result.substituteInto("$1"), -1);
450 if (n<=0)
451 state = 3;
452 else
453 state = 2;
454 }
455 }
456 break;
457 }
458 case 2: {
459 StringParser parser = new StringParser(line);
460 if (parser.goAfter('[')) {
461 parser.getNextStringUntil(StringParser.INTEGER + ']');
462 int n = parser.getNextIntUntil("]", -1);
463 parser.goAfter(']');
464 String hash = parser.getLastString();
465 int size = parser.getLastInt(-1);
466 String name = parser.getRemainingString();
467 if (n>=0)
468 liste.addObject(new DownloadItem(n, name, size, hash));
469 }
470 /*REMatch result = reVDDown.getMatch(line);
471 if (result!=null) {
472 int n = strongParseInt(result.substituteInto("$1"), -1);
473 String name = result.substituteInto("$2");
474 int size = strongParseInt(result.substituteInto("$3"), -1);
475 String hash = result.substituteInto("$4");
476 liste.addObject(new DownloadItem(n, name, size, hash));
477 }*/
478 else {
479 REMatch result = reVDEnd.getMatch(line);
480 if (result!=null) {
481 state = 3;
482 }
483 }
484 break;
485 }
486 }
487 }
488 }
489 catch(IOException e) {
490 e.printStackTrace();
491 reconnect();
492 }
493 return liste;
494 }
495
496 public synchronized void fillItemDetails(DownloadItem item) {
497 /*int nbLines = 0;
498 int nbConn = 0;
499 CamlData files = fileFiles.data().get("files");
500 String shortName = (item.name().length()>30 ? item.name().substring(0, 30) : item.name());
501 for(int i=0; i<files.size(); i++) {
502 CamlData file = files.get(i);
503 if (file.getInt("file_size")==item.size()
504 && file.get("file_filenames").getString(0).startsWith(shortName)) {
505 String name = file.get("file_filenames").getString(0);
506 int total = file.getInt("file_size");
507 int downloaded = file.getInt("file_downloaded");
508 String hash = file.getString("file_md4");
509 item.completeData(name, total, hash, downloaded);
510 CamlData clients = file.get("file_locations");
511 nbLines = clients.size();
512 for(int j=0; j<nbLines; j++) {
513 if (clients.get(j).getBoolean("client_checked"))
514 nbConn++;
515 }
516 item.setNbClients(nbLines);
517 item.setNbConn(nbConn);
518 String chunks = file.getString("file_all_chunks");
519 item.setChunks(chunks.replace('0', '_').replace('1', 'V'));
520 return;
521 }
522 }*/
523 long t0 = System.currentTimeMillis();
524 int state = 0;
525 try {
526 send("vd " + item.id());
527 if (socket==null)
528 return;
529 String name = "", hash = "";
530 int n = 0, total = 0, downloaded = 0;
531 int nbConn = 0, nbClients = 0, nbClientsSeen = 0;
532 while(state<5 && !shouldStopCommand) {
533 String line = readLine();
534 if (line==null)
535 break;
536 switch (state) {
537 case 0: {
538 REMatch result = reVDNLine1.getMatch(line);
539 if (result!=null) {
540 n = strongParseInt(result.substituteInto("$1"), -1);
541 hash = result.substituteInto("$2");
542 if (hash.length()!=32 || !StringParser.stringContainsOnly(hash, StringParser.HEXA)) {
543 name = hash;
544 hash = "";
545 }
546 total = strongParseInt(result.substituteInto("$3"), -1);
547 downloaded = strongParseInt(result.substituteInto("$4"), -1);
548 state = 1;
549 }
550 break;
551 }
552 case 1: {
553 REMatch result = reVDNChunks.getMatch(line);
554 if (result!=null) {
555 item.setChunks(result.substituteInto("$1"));
556 state = 2;
557 }
558 break;
559 }
560 case 2: {
561 StringParser parser = new StringParser(line);
562 if (parser.goAfter('(')) {
563 String s = parser.getRemainingString();
564 if (s.length()>0 && s.charAt(s.length()-1)==')')
565 s = s.substring(0, s.length()-1);
566 if (hash.length()==0 && s.length()==32 && StringParser.stringContainsOnly(s, StringParser.HEXA)) {
567 hash = s;
568 } else {
569 // We look for the name of the file
570 if (!StringParser.stringContainsOnly(s, StringParser.HEXA))
571 name = s;
572 }
573 if (hash.length()>0 && name.length()>0) {
574 if (n==item.id()) {
575 item.completeData(name, total, hash, downloaded);
576 } else {
577 System.out.println("## Bad id: got " + n + " for " + name + " instead of " + item.id());
578 }
579 state = 3;
580 }
581 break;
582 }
583 // No break, we want to go through case 3 by default
584 }
585 case 3: {
586 REMatch result = reVDNSources.getMatch(line);
587 if (result!=null) {
588 nbClients = strongParseInt(result.substituteInto("$1"), 0);
589 item.setNbClients(nbClients);
590 state = 4;
591 }
592 break;
593 }
594 case 4: {
595 if (line.indexOf("onlist true")>=0)
596 nbConn++;
597 if (++nbClientsSeen >= nbClients)
598 state = 5;
599 break;
600 }
601 }
602 }
603 if (nbConn>0)
604 item.setNbConn(nbConn);
605 else
606 item.setNbConn(nbClients);
607 }
608 catch(IOException e) {
609 e.printStackTrace();
610 reconnect();
611 }
612 }
613
614 public synchronized void uploadStats(UploadSpeed generalSpeed) {
615 int state = 0;
616 try {
617 send("upstats");
618 if (socket==null)
619 return;
620 while(state<2 && !shouldStopCommand) {
621 String line = readLine();
622 if (line==null)
623 break;
624 switch (state) {
625 case 0: {
626 REMatch result = reUPTitle2.getMatch(line);
627 if (result!=null) {
628 int n = strongParseInt(result.substituteInto("$1"), -1);
629 generalSpeed.setBytesCount(n);
630 state = 1;
631 } else {
632 result = reUPTitle.getMatch(line);
633 if (result!=null) {
634 int n = strongParseInt(result.substituteInto("$1"), -1);
635 generalSpeed.setBlocCount(n);
636 state = 1;
637 }
638 }
639 break;
640 }
641 case 1: {
642 /*REMatch result = reUPLine.getMatch(line);
643 if (result!=null) {
644 String name = result.substituteInto("$1");
645 int nbClients = strongParseInt(result.substituteInto("$2"), -1);
646 int nbBlocs = strongParseInt(result.substituteInto("$3"), -1);
647 DownloadItem item = dataSource.item(name);
648 if (item!=null)
649 item.setBlocsCount(nbBlocs);
650 } else {*/
651 REMatch result = reUPDone.getMatch(line);
652 if (result!=null) {
653 state = 2;
654 }
655 /*}*/
656 break;
657 }
658 }
659 }
660 }
661 catch(IOException e) {
662 e.printStackTrace();
663 reconnect();
664 }
665 }
666
667 public synchronized int[] startQuery(String pattern) {
668 return startQuery(pattern, 0, 0, null, null, null, null, null, 0);
669 }
670
671 private String commandFor(String prefix, String data) {
672 String out = "";
673 java.util.StringTokenizer st = new java.util.StringTokenizer(data);
674 while (st.hasMoreTokens()) {
675 out = out + prefix + st.nextToken();
676 }
677 return out;
678 }
679
680 public synchronized int[] startQuery(String pattern, int minSize, int maxSize, String media, String format, String artist, String album, String title, int minBitRate) {
681 try {
682 String cmd = "s " + pattern;
683 // We get the extended options
684 if (minSize>0)
685 cmd += " -minsize " + minSize;
686 if (maxSize>0)
687 cmd += " -maxsize " + maxSize;
688 if (media!=null && media.length()>0)
689 cmd += commandFor(" -media ", media);
690 if (format!=null && format.length()>0)
691 cmd += commandFor(" -format ", format);
692 if (artist!=null && artist.length()>0)
693 cmd += commandFor(" -artist ", artist);
694 if (album!=null && album.length()>0)
695 cmd += commandFor(" -album ", album);
696 if (title!=null && title.length()>0)
697 cmd += commandFor(" -title ", title);
698 if (minBitRate>0)
699 cmd += " -minbitrate " + minBitRate;
700 // Run !
701 send(cmd);
702 if (socket==null)
703 return new int[0];
704 while(in.ready() && !shouldStopCommand) {
705 String line = readLine();
706 if (line==null)
707 break;
708 REMatch result = reS.getMatch(line);
709 if (result!=null) {
710 int[] res = new int[2];
711 res[0] = strongParseInt(result.substituteInto("$1"), -1);
712 res[1] = strongParseInt(result.substituteInto("$2"), 0);
713 return res;
714 }
715 }
716 }
717 catch(IOException e) {
718 e.printStackTrace();
719 reconnect();
720 }
721 return null;
722 }
723
724 public synchronized Object[] queryResult(int resNb) {
725 ArrayList liste = new ArrayList();
726 int state = 0;
727 int toGo = 0;
728 SearchedItem currentItem = null;
729 try {
730 send("vr " + resNb);
731 if (socket==null)
732 return new Object[0];
733 while(state<2 && !shouldStopCommand) {
734 String line = readLine();
735 if (line==null || line.length()==0)
736 break;
737 switch (state) {
738 case 0: {
739 REMatch result = reVRRunning.getMatch(line);
740 if (result!=null) {
741 toGo = strongParseInt(result.substituteInto("$2"), -1);
742 state = 1;
743 }
744 else {
745 result = reVRDone.getMatch(line);
746 if (result!=null) {
747 toGo = 0;
748 int nb = strongParseInt(result.substituteInto("$1"), 0);
749 if (nb>0)
750 state = 1;
751 else
752 state = 2;
753 }
754 }
755 break;
756 }
757 case 1: {
758 REMatch result = reVRTitle.getMatch(line);
759 if (result!=null) {
760 int n = strongParseInt(result.substituteInto("$1"), -1);
761 String name = result.substituteInto("$3");
762 currentItem = new SearchedItem(n, name);
763 liste.add(currentItem);
764 }
765 else {
766 StringParser parser = new StringParser(line);
767 // We are looking for "size hash ..."
768 int size = parser.getNextInt(-1);
769 String hash = parser.getNextString();
770 if (size>=0 && StringParser.stringContainsOnly(hash, StringParser.HEXA)) {
771 // Last element can be "nb" or "type"
772 String type = parser.getLastString();
773 int nb = 0;
774 if (StringParser.stringContainsOnly(type, StringParser.INTEGER)) {
775 nb = StringParser.parseInt(type, 0);
776 type = parser.getLastString();
777 }
778 // Then comes the extension (avi, mp3, ...)
779 String ext = parser.getLastString();
780 String length = parser.getNextString();
781 // Did we read the quality instead of the length ?
782 if (length.length()>0 && StringParser.stringContainsOnly(length, StringParser.INTEGER)) {
783 // Maybe that we did not read the quality but nb ?
784 int i = StringParser.parseInt(length, -1);
785 length = parser.getNextString();
786 if ( ! (i!=0 && ((i%64)==0 || ((i%32)==0 && i>=64))) ) {
787 // We read nb
788 nb = i;
789 // We skip "quality"
790 if (StringParser.stringContainsOnly(length, StringParser.INTEGER))
791 length = parser.getNextString();
792 }
793 }
794 if (length.length()>0 && length.charAt(length.length()-1)==':') {
795 length += parser.getNextString();
796 }
797 String codec = parser.getRemainingString();
798 currentItem.addBasicInfo(size, hash, nb);
799 currentItem.addExtendedInfo(type, ext, codec, length);
800 }
801 /*result = reVRData.getMatch(line);
802 if (result!=null) {
803 int size = strongParseInt(result.substituteInto("$1"), -1);
804 String hash = result.substituteInto("$2");
805 int nb = strongParseInt(result.substituteInto("$4"), -1);
806 currentItem.addBasicInfo(size, hash, nb);
807 result = reVRSubData.getMatch(result.substituteInto("$3"));
808 if (result!=null) {
809 String type = result.substituteInto("$1");
810 String ext = result.substituteInto("$2");
811 String codec = result.substituteInto("$4");
812 String length = result.substituteInto("$5");
813 currentItem.addExtendedInfo(type, ext, codec, length);
814 }
815 }*/
816 else {
817 result = reVREnd.getMatch(line);
818 if (result!=null) {
819 state = 2;
820 }
821 else if (line.length()>0) {
822 currentItem.addAlias(line);
823 }
824 else {
825 state = 2;
826 }
827 }
828 }
829 break;
830 }
831 }
832 }
833 }
834 catch(IOException e) {
835 e.printStackTrace();
836 reconnect();
837 }
838 return new Object[]{new Integer(toGo), liste};
839 }
840
841 public synchronized void extendSearch() {
842 sendNoFail("xs");
843 }
844
845 public synchronized NSArray serversList() {
846 NSMutableArray liste = new NSMutableArray();
847 int state = 0;
848 try {
849 send("vm");
850 if (socket==null)
851 return new NSArray();
852 while(state<2 && !shouldStopCommand) {
853 String line = readLine();
854 if (line==null || line.length()==0)
855 break;
856 switch (state) {
857 case 0:
858 case 1: {
859 StringParser parser = new StringParser(line);
860 if (parser.goAfter('[')) {
861 parser.getNextStringUntil(StringParser.INTEGER);
862 int id = parser.getNextIntUntil("]", -1);
863 parser.goAfter(']');
864 String ip = parser.getNextString();
865 parser.getLastStringUntil(StringParser.INTEGER);
866 int n2 = parser.getLastInt(-1);
867 int n1 = parser.getLastInt(-1);
868 String name = parser.getRemainingString();
869 liste.addObject(new ServerItem(id, ip, name, n1, n2));
870 state = 1;
871 }
872 /*REMatch result = reVMLine.getMatch(line);
873 if (result!=null) {
874 int id = strongParseInt(result.substituteInto("$1"), -1);
875 String ip = result.substituteInto("$2");
876 String name;
877 if (result.getStartIndex(3)>0)
878 name = result.substituteInto("$3");
879 else
880 name = "";
881 int n1 = strongParseInt(result.substituteInto("$4"), -1);
882 int n2 = strongParseInt(result.substituteInto("$5"), -1);
883 liste.addObject(new ServerItem(id, ip, name, n1, n2));
884 state = 1;
885 }*/
886 else if (state==1) {
887 state = 2;
888 }
889 break;
890 }
891 }
892 }
893 }
894 catch(IOException e) {
895 e.printStackTrace();
896 reconnect();
897 }
898 return liste;
899 }
900
901 public synchronized List allServersList() {
902 ArrayList liste = new ArrayList();
903 CamlData servers = fileServers.data().get("known_servers");
904 int nb = servers.size();
905 for(int i=0; i<nb; i++) {
906 CamlData server = servers.get(i);
907 int id = i+1;
908 String ip = server.get("server_addr").getString(0) + ':' + server.get("server_addr").getString(1);
909 String name = server.getString("server_name") + " - " + server.getString("server_desc");
910 if (ip.length()>0)
911 liste.add(new ServerItem(id, ip, name, 0, 0));
912 }
913 return liste;
914 /*int state = 0;
915 try {
916 send("vma");
917 if (socket==null)
918 return new ArrayList();
919 while(state<2 && !shouldStopCommand) {
920 String line = readLine();
921 if (line==null || line.length()==0)
922 break;
923 switch (state) {
924 case 0:
925 case 1: {
926 StringParser parser = new StringParser(line);
927 parser.goAfter('[');
928 int id = parser.getNextIntUntil("]", -1);
929 parser.goAfter(']');
930 String ip = parser.getNextString();
931 String name = parser.getRemainingString();
932 if (ip.length()>0) {
933 liste.add(new ServerItem(id, ip, name, 0, 0));
934 state = 1;
935 }
936 else if (state==1) {
937 state = 2;
938 }
939 break;
940 }
941 }
942 }
943 }
944 catch(IOException e) {
945 e.printStackTrace();
946 reconnect();
947 }
948 return liste;*/
949 }
950
951 public synchronized NSDictionary options() {
952 NSMutableDictionary options = new NSMutableDictionary();
953 try {
954 send("vo");
955 if (socket==null)
956 return new NSDictionary();
957 while(!shouldStopCommand) {
958 String line = readLine();
959 if (line==null)
960 break;
961 REMatch result = reVOData.getMatch(line);
962 if (result!=null) {
963 String key = result.substituteInto("$1");
964 if (key.length()>0) {
965 String value = result.substituteInto("$2");
966 options.setObjectForKey(value, key);
967 if (key.equals("client_md4"))
968 break;
969 }
970 }
971 }
972 }
973 catch(IOException e) {
974 e.printStackTrace();
975 reconnect();
976 }
977 return options;
978 }
979
980 public synchronized void setOption(String option, String value) {
981 sendNoFail("set " + option + " " + value);
982 }
983
984 public synchronized void pause(DownloadItem item) {
985 sendNoFail("pause " + item.id());
986 }
987
988 public synchronized void resume(DownloadItem item) {
989 sendNoFail("resume " + item.id());
990 }
991
992 public synchronized void cancel(DownloadItem item) {
993 sendNoFail("cancel " + item.id());
994 // sendNoFail("close_fds");
995 }
996
997 public synchronized void forget(int nb) {
998 sendNoFail("forget " + nb);
999 }
1000
1001 public synchronized void commit() {
1002 try {
1003 send("commit");
1004 if (socket==null)
1005 return;
1006 while(in.ready() && !shouldStopCommand) {
1007 String line = readLine();
1008 if (line==null)
1009 break;
1010 REMatch result = reCOMMIT.getMatch(line);
1011 if (result!=null)
1012 return;
1013 }
1014 }
1015 catch(IOException e) {
1016 e.printStackTrace();
1017 reconnect();
1018 }
1019 }
1020
1021 public synchronized NSArray queryList() {
1022 NSMutableArray liste = new NSMutableArray();
1023 int state = 0;
1024 try {
1025 send("vs");
1026 if (socket==null)
1027 return new NSArray();
1028 while(state<2 && !shouldStopCommand) {
1029 String line = readLine();
1030 if (line==null)
1031 break;
1032 switch(state) {
1033 case 0: {
1034 REMatch result = reVSTitle.getMatch(line);
1035 if (result!=null) {
1036 int nb = strongParseInt(result.substituteInto("$1"), -1);
1037 if (nb==0)
1038 state = 2;
1039 else
1040 state = 1;
1041 }
1042 break;
1043 }
1044 case 1: {
1045 SearchParser parser = new SearchParser(line);
1046 if (parser.searchNumber()>=0) {
1047 liste.addObject(parser);
1048 }
1049 /*REMatch result = reVSLine.getMatch(line);
1050 if (result!=null) {
1051 int id = strongParseInt(result.substituteInto("$1"), -1);
1052 String data = result.substituteInto("$2");
1053 int toGo = strongParseInt(result.substituteInto("$3"), -1);
1054 StringBuffer query = new StringBuffer();
1055 REMatch[] results = reVSData.getAllMatches(data);
1056 for(int i=0; i<results.length; i++) {
1057 if (i>0)
1058 query.append(" ");
1059 query.append(results[i].substituteInto("$1"));
1060 }
1061 liste.addObject(new QueryItem(id, query.toString(), toGo));
1062 }*/
1063 else {
1064 state = 2;
1065 }
1066 break;
1067 }
1068 }
1069 }
1070 }
1071 catch(IOException e) {
1072 e.printStackTrace();
1073 reconnect();
1074 }
1075 return liste;
1076 }
1077
1078 protected synchronized String sendAndSearchResult(String command, RE match, String substitute) {
1079 String res = null;
1080 try {
1081 send(command);
1082 if (socket==null)
1083 return null;
1084 while(in.ready() && !shouldStopCommand) {
1085 String line = readLine();
1086 if (line==null)
1087 break;
1088 REMatch result = match.getMatch(line);
1089 if (result!=null)
1090 res = result.substituteInto(substitute);
1091 }
1092 }
1093 catch(IOException e) {
1094 e.printStackTrace();
1095 reconnect();
1096 }
1097 return res;
1098 }
1099
1100 protected synchronized void sendNoFail(String command) {
1101 try {
1102 send(command);
1103 }
1104 catch(IOException e) {
1105 e.printStackTrace();
1106 reconnect();
1107 }
1108 }
1109
1110 protected synchronized void send(String command) throws IOException {
1111 sendNoWait(command);
1112 if (socket==null)
1113 return;
1114 // We wait until we get an answer or 10s have elapsed
1115 for(int i=0; i<100 && !in.ready(); i++) {
1116 try {
1117 Thread.sleep(100);
1118 }
1119 catch(InterruptedException e) {
1120 }
1121 }
1122 }
1123
1124 protected synchronized void sendNoWait(String command) throws IOException {
1125 lastCommand = command; // Very soon because of the stopCommandIfCurrent() method
1126 // We guaranty that we don't send requests faster than one evry 50ms, just to quiet the core
1127 long t = System.currentTimeMillis();
1128 if (t-lastCallTime<50) {
1129 try {
1130 Thread.sleep(t-lastCallTime);
1131 } catch(InterruptedException e) {}
1132 }
1133 lastCallTime = t;
1134 // We send the command
1135 finishRead();
1136 if (socket==null)
1137 return;
1138 shouldStopCommand = false; // distant from "lastCommand=command" to avoid multithreading problems
1139 out.println(command);
1140 logCommandln(command);
1141 out.flush();
1142 }
1143
1144 protected synchronized void finishRead() throws IOException {
1145 if (socket==null)
1146 connect();
1147 if (socket==null)
1148 return;
1149 socket.setSoTimeout(5000);
1150 try {
1151 while (in.ready())
1152 readLine();
1153 }
1154 catch(IOException e) {
1155 e.printStackTrace();
1156 reconnect();
1157 }
1158 finally {
1159 socket.setSoTimeout(defaultTimeout);
1160 }
1161 }
1162
1163 protected int strongParseInt(String value, int def) {
1164 try {
1165 return Integer.parseInt(value);
1166 }
1167 catch(NumberFormatException e) {
1168 return def;
1169 }
1170 }
1171
1172 protected float strongParseFloat(String value, float def) {
1173 try {
1174 return Float.parseFloat(value);
1175 }
1176 catch(NumberFormatException e) {
1177 return def;
1178 }
1179 }
1180
1181 private String readLine() throws IOException {
1182 if (socket==null)
1183 return null;
1184 String s = in.readLine();
1185 if (s!=null)
1186 s = s.trim();
1187 logln(s);
1188 return s;
1189 }
1190
1191 private void logCommandln(String s) {
1192 logln("[" + new java.util.Date() + "] " + s);
1193 }
1194
1195 private void logln(String s) {
1196 if (s==null)
1197 return;
1198 dialog.append(s);
1199 dialog.append('\n');
1200 if (dialog.length()>60000) {
1201 dialogOffset += dialog.length()-30000;
1202 dialog.delete(0, dialog.length()-30000);
1203 }
1204 logger.log(s);
1205 }
1206
1207 public interface Listener extends Runnable {
1208 public void stopListening();
1209 }
1210
1211 public interface Logger {
1212 public void log(String s);
1213 }
1214 }
1215
1216 // $Log: DonkeyTalker.java,v $
1217 // Revision 1.26 2003/01/01 19:27:15 fortun
1218 // Delay in the full update of the screen
1219 //
1220 // Revision 1.25 2003/01/01 17:34:28 fortun
1221 // Bug in the downloadItems() and queryRsult() parsing
1222 //
1223 // Revision 1.24 2002/08/21 21:47:46 fortun
1224 // Adaptation for mldonkey v1.19
1225 // Reads servers list and file detail in the files instead of the telnet connection (does not work in v1.19)
1226 //
1227 // Revision 1.23 2002/08/10 12:20:35 fortun
1228 // Memory leak in the logln() function corrected
1229 //
1230 // Revision 1.22 2002/07/30 00:17:58 fortun
1231 // New extendSearch() method
1232 //
1233 // Revision 1.21 2002/07/28 19:45:10 fortun
1234 // allServersList() now returns a List instead of a NSArray
1235 //
1236 // Revision 1.20 2002/07/28 19:33:51 fortun
1237 // New addServerMet method
1238 //
1239 // Revision 1.19 2002/07/22 00:28:13 fortun
1240 // Acceleration for the queryResult() method
1241 // NSArrays are now Lists
1242 //
1243 // Revision 1.18 2002/07/21 18:38:25 fortun
1244 // Extended search implemented
1245 //
1246 // Revision 1.17 2002/07/21 15:16:02 fortun
1247 // Right creator set
1248 //
1249 // Revision 1.16 2002/07/14 22:40:21 fortun
1250 // New stopCommandIfCurrent() method to stop the current command
1251 // New reshare command
1252 // Bugs when getting the id in a line like [ddd] solved
1253 //
1254 // Revision 1.15 2002/07/09 22:12:34 fortun
1255 // kill() should now be faster
1256 // new connectToServer() method
1257 // downloadItems(), fillItemDetails() and allServersList() methods optimized with the new StringParser class
1258 // new allServersList() method
1259 //
1260 // Revision 1.14 2002/07/07 12:38:26 fortun
1261 // Several little enhancements in stability
1262 //
1263 // Revision 1.13 2002/07/05 00:01:08 fortun
1264 // Uses a logger to log the output
1265 // Better notion of a local core
1266 //
1267 // Revision 1.12 2002/06/30 21:08:53 fortun
1268 // Uses SO_LINGER option, to better quit from the daemon
1269 //
1270 // Revision 1.11 2002/06/29 17:59:07 fortun
1271 // Bug in the serversList if the server has no name
1272 //
1273 // Revision 1.10 2002/06/29 11:17:34 fortun
1274 // Socket is now 'KeepAlive' enabled
1275 // Better closing sequence
1276 //
1277 // Revision 1.9 2002/06/14 00:41:38 fortun
1278 // New finalClose and finalKill that marks the talker as dead (cannot be reconnected)
1279 // Better handling of the 'no connection' state
1280 // Handles a dialog log
1281 // Handles listener threads, and stops them when needed
1282 //
1283 // Revision 1.8 2002/06/09 15:41:42 fortun
1284 // Crash avoided when killing the core
1285 //
1286 // Revision 1.7 2002/06/08 12:19:28 fortun
1287 // Added "sendNoWait" that ensures a 50ms delay between calls to the core
1288 // Bug in finishRead() if not connected
1289 //
1290 // Revision 1.6 2002/06/02 18:09:26 fortun
1291 // Bug in connected servers list if server has no name
1292 //
1293 // Revision 1.5 2002/06/02 17:55:31 fortun
1294 // New "quit" function
1295 // "Kill" waits less than before
1296 // "send" should not be stuck anymore (timeout implemented)
1297 //
1298 // Revision 1.4 2002/06/02 12:05:22 fortun
1299 // Timeout shortened in finishRead
1300 //
1301 // Revision 1.3 2002/05/27 22:06:56 fortun
1302 // 'addAServer' method added
1303 //
1304 // Revision 1.2 2002/05/25 23:40:47 fortun
1305 // launchLocalDonkey has been moved to DonkeyLauncher
1306 // More connection options
1307 // kill now returns a state
1308 //
1309 // Revision 1.1.1.1 2002/05/21 21:26:13 fortun
1310 //
1311 //