package uc.protocols.hub;
import helpers.GH;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import logger.LoggerFactory;
import org.apache.log4j.Logger;
import uc.DCClient;
import uc.crypto.HashValue;
import uc.files.search.ISearchResult;
import uc.files.search.SearchResult;
import uc.protocols.DCProtocol;
import uc.user.User;
/** * @ sr - a string containing (from dcppWiki):
* $SR <source_nick> <result> <free_slots>/<total_slots><0x05><hub_name> (<hub_ip:listening_port>) //[<0x05><target_nick>] last part stripped away from hub
* where: <result> is one of the following:
* <file_name><0x05><file_size> for file results
* <directory> for directory results
*
* ex:
* $SR User1 mypathmotd.txt<0x05>437 3/4<0x05>Testhub (10.10.10.10:411)
*
*
* <result> is one of the following:
* <file_name><0x05><file_size> for file results
* <directory> for directory results
* The <0x05> characters used above for deliminators are the 5th character in the ASCII character set.
* Sent by a client when a match to a $Search is found.
* If the $Search was a passive one, the $SR is returned via the hub connection (TCP). In this case, <0x05><target_nick> must be included on the end of the $SR. The hub must strip the deliminator and <target_nick> before sending the $SR to <target_nick>. If the search was active, it is sent to the IP address and port specified in the $Search via UDP.
* The port for the hub only needs to specified if its listening port is not the default (411).
* On UNIX the path delimiter / must be converted to \ for compatibility.
* DC++ will send a maximum of 5 search results to passive users and 10 to active users. (we will do the same)
* For files containing TTH, the <hub_name> parameter is replaced with TTH:<base32_encoded_tth_hash> (ref: TTH_Hash)
* $SR <source_nick> <result> <free_slots>/<total_slots><0x05><hub_name> (<hub_ip:listening_port>)[<0x05><target_nick>]|
*
* $SR User1 mypathmotd.txt<0x05>437 3/4<0x05>TTH:J7HKCPIH..3W (10.10.10.10:411)<0x05>User2|
*
*
*Malformed Command received: $SR schlumpf 1_Daten\Malvorlagen\Windowcolor - Vorlagen\Scan10017.png62253 1/3TTH:L7G5FXZNSYDE3TVFLQHP6ISIIUT6PT2WX4CBUCQ (89.59.92.112)
*
*/
public class SR extends AbstractNMDCHubProtocolCommand {
private static Logger logger = LoggerFactory.make();
private static final Pattern pipesplit = Pattern.compile(Pattern.quote("|"));
/**
* used in search results as separator
* this is the RegExp string for detection
*/
private static final String CHARFIVE = "\\x05";
/**
* the same as char for sending
*/
private static final char FIVESEP = (char) 0x05;
/*
*
* DEBUG ConnectionProtocol.java Line:159
* Malformed Command received: $SR [daiz]coldshouldermulder G:_\Documentaries\BBC.Horizon.2008.How.Much.Is.Your.Dead.Body.Worth.DVBc.XviD.MP3.MVGroup.org.avi669853684 2/4TTH:FNY6UH5AOERHL4GEJQBLEY7WXBNLNJFCYA2UBWI (81.16.174.204:7777)
DEBUG ConnectionProtocol.java Line:159
Malformed Command received: $SR [daiz]coldshouldermulder G:_\Documentaries\BBC.Horizon.2008.How.to.Kill.a.Human.Being.DVBC.XviD.MP3.MVGroup.org.avi669845702 2/4TTH:UI36H4BSPDUZ4PNVJU4ZHQGRZQCVRVDI6AS4FZQ (81.16.174.204:7777)
*/
/**
* pattern that matches a SR that represents a directory
*/
private static final Pattern directorySR;
/**pattern that matches a SR that represents a file
*
*/
private static final Pattern fileSR;
static {
String prefix = "^\\Q$SR\\E";
String filename = "(?:.{1,255})";
String slots = "(\\d{1,5})/(\\d{1,5})";
directorySR = Pattern.compile(prefix + " ("+NMDCNICK+") ("+filename+") "+slots+CHARFIVE+"(?:.*) \\("+IPv4+":"+PORT+"\\)");
fileSR = Pattern.compile(prefix +" ("+NMDCNICK+") ("+filename+")"+CHARFIVE+"("+FILESIZE+")"+" "+slots+CHARFIVE+"TTH:("+TTH+") \\("+IPv4+":"+PORT+"\\)");
}
public SR() {
String filename = "(?:.{1,255})";
String result = filename+"(?:"+CHARFIVE+FILESIZE+")?";
String hubname = "(?:TTH\\:"+TTH+"|(?:.*))";
String slots = "(\\d{1,5})/(\\d{1,5})";
setPattern(prefix + " "+NMDCNICK+" "+result+" "+slots+CHARFIVE+hubname+" \\("+IPv4+":"+PORT+"\\)",true);
}
private static void receivedSR(Hub hub,String command) {
Matcher sr;
String path;
if ((sr = fileSR.matcher(command)).matches()) {
path = transformPath(sr.group(2));
User other = hub.getUserByNick(sr.group(1));
if (other != null) { // file
ISearchResult searchResult = SearchResult.create(
path, //filename
HashValue.createHash(sr.group(6)),
other,
Long.parseLong(sr.group(3)), //file size
Integer.parseInt(sr.group(4)), //slots current
Integer.parseInt(sr.group(5)), //slots max
true,null);
hub.getDcc().srReceived(searchResult);
}
} else if ( (sr = directorySR.matcher(command)).matches()) {
path = transformPath(sr.group(2));
User other = hub.getUserByNick(sr.group(1));
if (other != null) { //directory..
ISearchResult searchResult = SearchResult.create(
path, //filename
null,
other,
-1, //file size
Integer.parseInt(sr.group(3)), //slots current
Integer.parseInt(sr.group(4)), //slots max
false,null);
hub.getDcc().srReceived(searchResult);
}
} else {
logger.debug("invalid sr string: "+command);
}
}
@Override
public void handle(Hub hub,String command) throws IOException {
receivedSR(hub,command);
}
private static String transformPath(String sent) {
return GH.replaceInvalidFilpath(sent.replace('\\', File.separatorChar)) ;
}
public static void main(String[] args) {
String fileSR = "$SR guywithfile lazgapo\\ashda\\strangefile.bmp"
+((char)0x05)+"4587 2/4"+((char)0x05)
+"TTH:4CLZLU7TCB6C4YTHN7JNOIA7F7VQVJV5762AYJA (89.48.59.86:6999)";
Matcher m = SR.fileSR.matcher(fileSR);
boolean matches = m.matches();
System.out.println(matches);
if (matches) {
for (int i=0 ; i <= 6;i++)
System.out.println(m.group(i));
}
}
/**
* creates the part of the SR string that is needed by active and by passive results
* @param sr - the file/folder that was found
* @param hub - the hub in which the search appeared
*/
private static String getPartialSRString(SearchResult sr,Hub hub) {
String result = sr.getPath().replace(File.separatorChar,'\\'); //replace File separaters here with NMDC separater
if (sr.isFile()) {
result += FIVESEP ;
result += sr.getSize();
} else { //if its a folder cut away last file sep
result = result.substring(0, result.length()-1);
}
//partial sr string: $SR °^(AAA)Schnueffeltv2^° Musik\DJ Quicksilver - Bellissima.mp33708421 1/1TTH:YUMEHYMQI4XYDLS4YFAGH3ID2RMSWL44CU5HWXA (89.59.75.160:6999)
InetSocketAddress isa = hub.getHubIPAndPort();
String srpart = "$SR "
+ sr.getUser().getNick() + " "
+ result + " "
+ sr.getAvailabelSlots() +"/" + sr.getTotalSlots() + FIVESEP
+ (sr.isFile()? "TTH:"+sr.getTTHRoot() : hub.getName()) + " " //TTH of the file else
+ "("+isa.getAddress().getHostAddress() + ":"+isa.getPort() +")" ; //Hub name + ip:port
logger.debug("partial sr string: "+srpart);
return srpart;
}
public static String getUDPSrString(SearchResult sr, Hub hub) {
return getPartialSRString(sr,hub)+ "|";
}
/**
* sends SRs to a passive user back over the hubconnection..
* @param srs - the results of the search
* @param target - the user that will receive the searchResults
* @param hub - the hub that will transmit to the user..
*/
public static void sendSR(Set<SearchResult> srs , User target, Hub hub) {
// String command = "";
StringBuilder command = new StringBuilder();
if (target != null) {
for (SearchResult sr: srs) {
command.append(getPartialSRString(sr,hub));
command.append(FIVESEP);
command.append( target.getNick());
command.append( '|');
}
}
if (command.length() > 0) { // send the command if it is not empty..
hub.sendUnmodifiedRaw(command.toString());
}
}
/**
*
* - a string containing (from dcppWiki):
* $SR <source_nick> <result> <free_slots>/<total_slots><0x05><hub_name> (<hub_ip:listening_port>) //[<0x05><target_nick>] last part stripped away from hub
* where: <result> is one of the following:
* <file_name><0x05><file_size> for file results
* <directory> for directory results
*
* ex:
* $SR User1 mypathmotd.txt<0x05>437 3/4<0x05>Testhub (10.10.10.10:411)
*
*
* <result> is one of the following:
* <file_name><0x05><file_size> for file results
* <directory> for directory results
* The <0x05> characters used above for deliminators are the 5th character in the ASCII character set.
* Sent by a client when a match to a $Search is found.
* If the $Search was a passive one, the $SR is returned via the hub connection (TCP). In this case, <0x05><target_nick> must be included on the end of the $SR. The hub must strip the deliminator and <target_nick> before sending the $SR to <target_nick>. If the search was active, it is sent to the IP address and port specified in the $Search via UDP.
* The port for the hub only needs to specified if its listening port is not the default (411).
* On UNIX the path delimiter / must be converted to \ for compatibility.
* DC++ will send a maximum of 5 search results to passive users and 10 to active users. (we will do the same)
* For files containing TTH, the <hub_name> parameter is replaced with TTH:<base32_encoded_tth_hash> (ref: TTH_Hash)
*
**
* splits the packet in searchResult strings
* then retrieves nick and hubip from the packet
* and forwards the searchresult string then to
* the matching hub
*
* @param from - socket of the sender
* @param cs - CharSsequence containing the packet
*/
public static void receivedNMDCSR(InetSocketAddress from, CharSequence cs ,ByteBuffer originalpacket,DCClient dcc) {
for (String sr: pipesplit.split(cs)) {
logger.debug("received sr via udp: "+sr);
try {
int secondspace = sr.indexOf(' ', 5);
int braceOpen = sr.lastIndexOf('(');
if (sr.startsWith("$SR ") && -1 != secondspace && secondspace < braceOpen ) {
String nick = sr.substring(4, secondspace );
String hubip = sr.substring(braceOpen ,sr.length()-1);
Hub hub = dcc.hubForNickAndIP(nick,hubip);
if (hub != null) {
if (hub.getCharset().equals(DCProtocol.NMDC_CHARSET) || originalpacket == null ) {
receivedSR(hub, sr);
// hub.searchResultReceived(sr);
} else {
receivedNMDCSR(from, hub.getCharset().decode(originalpacket) , null,dcc);
}
}
}
} catch(RuntimeException re) {
logger.debug("Bad input in Searchresult: "+re+" "+cs);
}
}
}
}