3 import java.util.Hashtable;
\r
4 import java.util.Vector;
\r
5 import javax.swing.*;
\r
8 * This class implements mlDonkey Core/GUI protocol.
\r
9 * This is a part of <a href="http://royale.zerezo.com/jmoule/" target="_blank">jMoule</a>.
\r
10 * This project is licenced under GPL.
\r
11 * @author Antoine Jacquet
\r
13 public class DonkeyCore extends Thread
\r
15 byte[] buffer=new byte[1024*100];
\r
22 * This contains the informations about the files being downloaded.
\r
23 * File ID are the hash keys.
\r
24 * The values of the hash are vectors containing file informations : filename, size, downloaded, %, rate, ETA and running state.
\r
26 public Hashtable fileInfos=new Hashtable();
\r
29 * This contains the informations about the results of searches.
\r
30 * Files ID are the hash keys.
\r
31 * The values of the hash are vectors containing result informations : filename, size, tags and download state.
\r
33 public Hashtable resultInfos=new Hashtable();
\r
36 * This contains the informations about the donkey servers.
\r
37 * Servers ID are the hash keys.
\r
38 * The values of the hash are vectors containing server informations : host, users, files, connected state.
\r
40 public Hashtable serverInfos=new Hashtable();
\r
43 * This contains all the searches results.
\r
44 * Each entry is a vector containing a specific search result.
\r
45 * Each specific search result contains vectors with same format as resultInfos.
\r
47 public Vector searchResults=new Vector();
\r
50 * This contains statistics informations.
\r
51 * The keys are the variables and the values are numbers.
\r
53 public Hashtable statInfos=new Hashtable();
\r
56 * This is a text panel containing console messages...
\r
58 public JTextArea console=new JTextArea();
\r
59 //String console=new String();
\r
62 * Constructs an unconnected DonkeyCore.
\r
68 fileInfo=new Vector();
\r
74 fileInfo.add(new Boolean(true));
\r
75 fileInfos.put(new Long(0),fileInfo);
\r
76 fileInfo=new Vector();
\r
82 fileInfo.add(new Boolean(true));
\r
83 fileInfos.put(new Long(1),fileInfo);
\r
84 fileInfo=new Vector();
\r
90 fileInfo.add(new Boolean(true));
\r
91 fileInfos.put(new Long(2),fileInfo);
\r
93 serverInfo=new Vector();
\r
94 serverInfo.add("a");
\r
95 serverInfo.add("a");
\r
96 serverInfo.add("a");
\r
97 serverInfo.add(Boolean.FALSE);
\r
98 serverInfos.put(new Long(0),serverInfo);
\r
99 serverInfo=new Vector();
\r
100 serverInfo.add("b");
\r
101 serverInfo.add("d");
\r
102 serverInfo.add("e");
\r
103 serverInfo.add(new Boolean(true));
\r
104 serverInfos.put(new Long(1),serverInfo);
\r
105 serverInfo=new Vector();
\r
106 serverInfo.add("e");
\r
107 serverInfo.add("x");
\r
108 serverInfo.add("q");
\r
109 serverInfo.add(new Boolean(false));
\r
110 serverInfos.put(new Long(2),serverInfo);
\r
111 serverInfo=new Vector();
\r
112 serverInfo.add("a");
\r
113 serverInfo.add("b");
\r
114 serverInfo.add("n");
\r
115 serverInfo.add(new Boolean(true));
\r
116 serverInfos.put(new Long(3),serverInfo);
\r
121 * Constructs a connected DonkeyCore.
\r
122 * Same parameters as "connect".
\r
124 public DonkeyCore(String host,int port,String password)
\r
127 connect(host,port,password);
\r
131 * Connects an unconnected DonkeyCore.
\r
132 * @param host mlDonkey host you wish to connect to.
\r
133 * @param port mlDonkey core port.
\r
134 * @param password Password if needed.
\r
136 public boolean connect(String host,int port,String password)
\r
138 this.password=password;
\r
139 //System.out.println(password);
\r
142 connection=new Socket(host,port);
\r
146 catch (Exception e)
\r
148 e.printStackTrace();
\r
154 * Disconnects this DonkeyCore.
\r
156 public void disconnect()
\r
160 connection.close();
\r
163 catch (Exception e)
\r
165 e.printStackTrace();
\r
170 * Tests if this DonkeyCore is already connected.
\r
171 * @return true if connected.
\r
173 public boolean isConnected()
\r
175 return connection!=null;
\r
178 void debug(String message)
\r
180 //System.out.println(message);
\r
183 boolean readMessage()
\r
187 InputStream i=connection.getInputStream();
\r
188 // read the length of the message
\r
189 long l=i.read()+256*(i.read()+256*(i.read()+256*i.read()));
\r
190 //System.out.print("size: "+l+" ");
\r
193 while (pos<l) pos+=i.read(buffer,pos,(int)l-pos);
\r
201 System.out.println("Dump: "+new String(buffer,0,(int)l));
\r
202 for (pos=0;pos<l;pos++)
\r
204 System.out.print(buffer[pos]+",");
\r
212 catch (Exception e)
\r
214 e.printStackTrace();
\r
221 byte temp=buffer[pos++];
\r
222 return(temp<0?temp+256:temp);
\r
227 return(readByte()+(readByte()<<8));
\r
232 return(readInt()+((long)readInt()<<16));
\r
235 String readString()
\r
240 String temp=new String(buffer,pos,l);
\r
247 synchronized boolean sendMessage(byte[] buffer,int l)
\r
251 connection.getOutputStream().write(l%256);
\r
252 connection.getOutputStream().write(l/256);
\r
253 connection.getOutputStream().write(0);
\r
254 connection.getOutputStream().write(0);
\r
255 connection.getOutputStream().write(buffer,0,l);
\r
258 catch (Exception e)
\r
260 e.printStackTrace();
\r
266 * Asks the mlDonkey core to connect more servers.
\r
268 public void connectMore()
\r
270 byte[] buffer=new byte[2];
\r
271 buffer[0]=1; buffer[1]=0; // function
\r
272 sendMessage(buffer,2);
\r
276 * Asks the mlDonkey core to clean old servers.
\r
278 public void cleanOld()
\r
280 byte[] buffer=new byte[2];
\r
281 buffer[0]=2; buffer[1]=0; // function
\r
282 sendMessage(buffer,2);
\r
286 * Asks the mlDonkey core to kill (terminate) itself.
\r
290 byte[] buffer=new byte[2];
\r
291 buffer[0]=3; buffer[1]=0; // function
\r
292 sendMessage(buffer,2);
\r
296 * Extends (redo) the last search.
\r
298 public void extend()
\r
300 byte[] buffer=new byte[2];
\r
301 buffer[0]=4; buffer[1]=0; // function
\r
302 sendMessage(buffer,2);
\r
306 * Start a new download.
\r
307 * @param fileID ID of the file to download (found in resultInfos key).
\r
309 public void download(Long fileID)
\r
311 byte[] buffer=new byte[9];
\r
313 buffer[i++]=50; buffer[i++]=0; // function
\r
314 buffer[i++]=0; buffer[i++]=0; // result_names (empty list)
\r
315 long id=fileID.longValue();
\r
316 for (int j=0;j<4;j++)
\r
318 buffer[i++]=(byte)(id%256);
\r
321 buffer[i++]=0; // force
\r
322 sendMessage(buffer,i);
\r
326 * Pause/Resume a download.
\r
327 * @param fileID ID of the file to switch (found in resultInfos key).
\r
328 * @param bool true = resume, false = pause.
\r
330 public void switchDownload(Long fileID,boolean bool)
\r
332 byte[] buffer=new byte[9];
\r
334 buffer[i++]=23; buffer[i++]=0; // function
\r
335 long id=fileID.longValue();
\r
336 for (int j=0;j<4;j++)
\r
338 buffer[i++]=(byte)(id%256);
\r
345 sendMessage(buffer,i);
\r
349 * Asks mlDonkey to connect to a new server.
\r
350 * @param serverID ID of the server to connect to (found in serverInfos key).
\r
352 public void connectServer(Long serverID)
\r
354 byte[] buffer=new byte[6];
\r
356 buffer[i++]=21; buffer[i++]=0; // function
\r
357 long id=serverID.longValue();
\r
358 for (int j=0;j<4;j++)
\r
360 buffer[i++]=(byte)(id%256);
\r
363 sendMessage(buffer,i);
\r
367 * Asks mlDonkey to disconnect from a server.
\r
368 * @param serverID ID of the server to disconnect (found in serverInfos key).
\r
370 public void disconnectServer(Long serverID)
\r
372 byte[] buffer=new byte[6];
\r
374 buffer[i++]=22; buffer[i++]=0; // function
\r
375 long id=serverID.longValue();
\r
376 for (int j=0;j<4;j++)
\r
378 buffer[i++]=(byte)(id%256);
\r
381 sendMessage(buffer,i);
\r
385 * Start a new search.
\r
386 * @param query A simple string to describe what you want to search.
\r
387 * @return A vector of search results (entries of this vector have same structure as resultInfos entries).
\r
389 public Vector search(String query)
\r
391 byte[] buffer=new byte[1024*100];
\r
393 buffer[i++]=42; buffer[i++]=0; // function
\r
394 buffer[i++]=(byte)(nbsearches%256); buffer[i++]=(byte)(nbsearches/256);
\r
395 buffer[i++]=0; buffer[i++]=0; // num
\r
396 buffer[i++]=4; // keywords
\r
397 buffer[i++]=0; buffer[i++]=0; // empty string
\r
398 buffer[i++]=(byte)(query.length()%256);
\r
399 buffer[i++]=(byte)(query.length()/256);
\r
400 for (j=0;j<query.length();j++)
\r
401 buffer[i++]=(byte)query.charAt(j);
\r
402 buffer[i++]=0; buffer[i++]=4; buffer[i++]=0; buffer[i++]=0; // max hits
\r
403 buffer[i++]=1; // type
\r
404 Vector searchResult=new Vector();
\r
405 searchResults.add(searchResult);
\r
406 sendMessage(buffer,i);
\r
409 Vector resultInfo=new Vector();
\r
410 resultInfo.add(new Long(1));
\r
411 resultInfo.add("a");
\r
412 resultInfo.add("a");
\r
413 resultInfo.add("a");
\r
414 resultInfo.add(new Boolean(false));
\r
415 searchResult.add(resultInfo);
\r
416 resultInfo=new Vector();
\r
417 resultInfo.add(new Long(2));
\r
418 resultInfo.add("b");
\r
419 resultInfo.add("b");
\r
420 resultInfo.add("b");
\r
421 resultInfo.add(new Boolean(false));
\r
422 searchResult.add(resultInfo);
\r
423 resultInfo=new Vector();
\r
424 resultInfo.add(new Long(3));
\r
425 resultInfo.add("c");
\r
426 resultInfo.add("c");
\r
427 resultInfo.add("c");
\r
428 resultInfo.add(new Boolean(false));
\r
429 searchResult.add(resultInfo);
\r
431 return searchResult;
\r
435 * This class is a thread.
\r
439 while(readMessage())
\r
441 //String serverStates[]={"NotConnected -1","Connecting","Connected_initiating","Connected_downloading","Connected -1","Connected n","NewHost","RemovedHost","BlackListedHost","NotConnected n"};
\r
442 Boolean serverStates[]={
\r
443 Boolean.FALSE, // "NotConnected -1"
\r
444 Boolean.FALSE, // "Connecting"
\r
445 Boolean.TRUE, // "Connected_initiating"
\r
446 Boolean.TRUE, // "Connected_downloading"
\r
447 Boolean.TRUE, // "Connected -1"
\r
448 Boolean.TRUE, // "Connected n"
\r
449 Boolean.FALSE, // "NewHost"
\r
450 Boolean.FALSE, // "RemovedHost"
\r
451 Boolean.FALSE, // "BlackListedHost"
\r
452 Boolean.FALSE // "NotConnected n"
\r
454 Boolean fileStates[]={
\r
455 Boolean.TRUE, // FileDownloading
\r
456 Boolean.FALSE, // FilePaused
\r
457 Boolean.TRUE, // FileDownloaded
\r
458 Boolean.FALSE, // FileShared
\r
459 Boolean.FALSE, // FileCancelled
\r
460 Boolean.FALSE, // FileNew
\r
461 Boolean.FALSE, // FileAborted s
\r
462 Boolean.FALSE // FileQueued
\r
467 switch(f=readInt()) // fonction
\r
472 debug("CoreProtocol: "+readLong());
\r
473 // (06:00:00:00:) 00:00:10:00:00:00 GuiProtocol
\r
474 for (i=0;i<6;i++) buffer[i]=0;
\r
476 sendMessage(buffer,6);
\r
477 // (04:00:00:00:) 05:00:00:00 Password (empty)
\r
479 buffer[i++]=5; buffer[i++]=0; // function
\r
480 buffer[i++]=(byte)(password.length()%256);
\r
481 buffer[i++]=(byte)(password.length()/256);
\r
482 for (int j=0;j<password.length();j++)
\r
483 buffer[i++]=(byte)password.charAt(j);
\r
484 sendMessage(buffer,i);
\r
489 debug("Options_info:");
\r
492 debug(readString()+" = "+readString());
\r
498 debug("DefineSearches: "+n);
\r
499 /*for (i=0;i<n;i++)
\r
501 System.out.println(readString()); // name
\r
502 System.out.println(readByte());
\r
508 Vector resultInfo=new Vector();
\r
509 debug("Result_info: ");
\r
510 id=new Long(readLong());
\r
511 resultInfo.add(id);
\r
512 readLong(); // network
\r
514 resultInfo.add(readString());
\r
515 for (i=0;i<n-1;i++) readString(); // names
\r
516 for (i=0;i<16;i++) readByte(); // hash
\r
517 resultInfo.add(new Long(readLong())); // Size
\r
518 readString(); // Format
\r
519 readString(); // Type
\r
524 readString(); // TagName
\r
525 switch (readByte()) // TagValue
\r
529 tags=tags.concat(""+readLong()+" ");
\r
532 tags=tags.concat(readString()+" ");
\r
535 tags=tags.concat(""+readByte()+"."+readByte()+"."+readByte()+"."+readByte()+" ");
\r
539 resultInfo.add(tags);
\r
540 readString(); // Comment
\r
541 readByte(); // Done (boolean)
\r
542 resultInfo.add(new Boolean(false)); // download ?
\r
543 resultInfos.put(id,resultInfo);
\r
548 int search=(int)readLong(); // Search
\r
549 Long info=new Long(readLong()); // Result_Info
\r
550 ((Vector)searchResults.get(search)).add(resultInfos.get(info));
\r
557 Vector fileInfo=new Vector();
\r
558 debug("FileInformation: ");
\r
559 id=new Long(readLong()); // id
\r
560 readLong(); // network
\r
564 // no name, this should not happen...
569 // no name but the ID string
\r
570 fileInfo.add(readString());
576 // and get the first name
\r
577 fileInfo.add(readString());
\r
578 for (i=0;i<n-2;i++) debug(readString()); // names
\r
579 //for (i=0;i<n;i++) debug(readString()); // names
581 for (i=0;i<16;i++) readByte(); // hash
\r
582 //readLong(); // size
\r
583 //readLong(); // downloaded
\r
584 long filesize=readLong();
\r
585 long filedl=readLong();
\r
586 fileInfo.add(new Long(filesize));
\r
587 fileInfo.add(new Long(filedl));
\r
588 fileInfo.add(new Float(Math.round(10000*filedl/filesize)/100.0));
\r
589 //debug(readLong()+"/"+readLong());
\r
590 readLong(); // nlocations
\r
591 readLong(); // nclients
\r
592 int state=readByte(); // file_state
\r
593 readString(); // chunks
\r
594 readString(); // availability
\r
595 //fileInfo.add(new Float(readString())); // rate
\r
596 double rate=Math.round(Float.parseFloat(readString()))/1000.0;
\r
597 fileInfo.add(new Float(rate));
\r
598 long time=Math.round((filesize-filedl)/(1024*rate));
\r
600 fileInfo.add(new Float(time/3600.0));
\r
602 fileInfo.add(new Float(0));
\r
604 for (i=0;i<n;i++) readString(); // chunks age
\r
605 readString(); // age
\r
606 switch (readByte()) // format
\r
609 debug("Unknown_format");
\r
611 case 1: // FormatType
\r
612 //readString(); readString();
\r
613 debug("FormatType: "+readString()+" "+readString());
\r
617 debug("codec: "+readString()); // codec
\r
618 debug(readLong()+"x"+readLong()+", "+readLong()+" fps "+readLong()+" rate") ;
\r
622 debug("title: "+readString()); // title
\r
623 debug("artist: "+readString()); // artist
\r
624 debug("album: "+readString()); // album
\r
625 debug("year: "+readString()); // year
\r
626 debug("comment: "+readString()); // comment
\r
627 debug("track: "+readLong()); // track number
\r
628 debug("genre: "+readLong()); // genre
\r
631 fileInfo.add(fileStates[state]);
\r
632 fileInfos.put(id,fileInfo);
\r
637 // file_num f, client_num c
\r
638 //debug("File_source: "+readInt()+" "+readInt());
\r
643 //debug("Server_state: "+readLong()+" etat: "+readByte());
\r
644 id=new Long(readLong());
\r
645 ((Vector)serverInfos.get(id)).setElementAt(serverStates[readByte()],3); // server_state
\r
646 //System.out.println(id+" : state changed");
\r
651 debug("Client_state: "+readLong()+" etat: "+readByte());
\r
657 debug("Client_info: ");
\r
658 debug("num: "+readLong());
\r
659 debug("network: "+readLong());
\r
664 debug("type: Known_location");
\r
665 debug("ip: "+readByte()+"."+readByte()+"."+readByte()+"."+readByte());
\r
666 debug("port: "+readInt());
\r
669 // Indirect_location
\r
670 debug("type: Indirect_location");
\r
671 debug("name: "+readString());
\r
679 debug("Console: ");
\r
680 //console=console.concat(readString());
\r
681 console.append(readString());
\r
687 debug("Network_info: "+readString());
\r
692 Vector serverInfo=new Vector();
\r
693 debug("Server_info: ");
\r
694 id=new Long(readLong()); // id
\r
695 readLong(); // network
\r
696 // only works if proto>=2
\r
701 //debug("ip: "+readByte()+"."+readByte()+"."+readByte()+"."+readByte());
\r
702 host=""+readByte()+"."+readByte()+"."+readByte()+"."+readByte();
\r
705 //debug("name: "+readString());
\r
709 //debug("port: "+readInt());
\r
710 host=host+":"+readInt(); // port
\r
711 serverInfo.add(host);
\r
712 readLong(); // score
\r
717 readString(); // TagName
\r
718 switch (readByte()) // TagValue
\r
722 tags=tags.concat(""+readLong()+" ");
\r
725 tags=tags.concat(readString()+" ");
\r
728 tags=tags.concat(""+readByte()+"."+readByte()+"."+readByte()+"."+readByte()+" ");
\r
732 serverInfo.add(new Long(readLong())); // nusers
\r
733 serverInfo.add(new Long(readLong())); // nfiles
\r
734 serverInfo.add(serverStates[readByte()]); // server_state
\r
735 serverInfos.put(id,serverInfo);
\r
738 // Add_section_option
\r
740 debug("Add_section_option: "+readString()+" "+readString()+" "+readString());
\r
743 // Add_plugin_option
\r
745 debug("Add_plugin_option: "+readString()+" "+readString()+" "+readString());
\r
750 debug("File_downloaded: ");
\r
751 debug("n: "+readLong());
\r
752 debug("size: "+readLong());
\r
753 debug("rate: "+readString());
\r
754 debug("last_seen: "+readLong());
\r
757 // Shared_file_info
\r
759 debug("Shared_file_info: ");
\r
760 debug("num: "+readLong());
\r
761 debug("network: "+readLong());
\r
762 debug("name: "+readString());
\r
763 debug("size: "+readLong());
\r
769 debug("Client_stats: ");
\r
770 statInfos.put("upload_counter",new Long(readLong()+(readLong()<<32)));
\r
771 statInfos.put("download_counter",new Long(readLong()+(readLong()<<32)));
\r
772 statInfos.put("shared_counter",new Long(readLong()+(readLong()<<32)));
\r
773 statInfos.put("nshared_files",new Long(readLong()));
\r
774 statInfos.put("tcp_upload_rate",new Long(readLong()));
\r
775 statInfos.put("tcp_download_rate",new Long(readLong()));
\r
776 statInfos.put("udp_upload_rate",new Long(readLong()));
\r
777 statInfos.put("udp_download_rate",new Long(readLong()));
\r
778 statInfos.put("ndownloading_files",new Long(readLong()));
\r
779 statInfos.put("ndownloaded_files",new Long(readLong()));
\r
784 debug("Fonction: "+f);
\r
788 //System.out.println();
\r