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 //