gt: 				if ( request_type == TRTrackerServerRequest.RT_SCRAPE ){
context: /*
   * File    : TRTrackerServerProcessor.java
   * Created : 5 Oct. 2003
   * By      : Parg 
   * 
   * Azureus - a Java Bittorrent client
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details ( see the LICENSE file ).
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.gudy.azureus2.core3.tracker.server.impl.tcp;
  
  
  import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition;
  import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPositionManager;
  import com.aelitis.azureus.core.util.HTTPUtils;
  import org.bouncycastle.util.encoders.Base64;
  import org.gudy.azureus2.core3.tracker.server.TRTrackerServerException;
  import org.gudy.azureus2.core3.tracker.server.TRTrackerServerPeer;
  import org.gudy.azureus2.core3.tracker.server.TRTrackerServerRequest;
  import org.gudy.azureus2.core3.tracker.server.impl.TRTrackerServerImpl;
  import org.gudy.azureus2.core3.tracker.server.impl.TRTrackerServerPeerImpl;
  import org.gudy.azureus2.core3.tracker.server.impl.TRTrackerServerProcessor;
  import org.gudy.azureus2.core3.tracker.server.impl.TRTrackerServerTorrentImpl;
  import org.gudy.azureus2.core3.util.*;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import java.util.zip.GZIPOutputStream;
  
  public abstract class 
  TRTrackerServerProcessorTCP
  	extends 	TRTrackerServerProcessor
  {
  	protected static final int SOCKET_TIMEOUT				= 5000;
  
  	protected static final char		CR			= '\015';
  	protected static final char		FF			= '\012';
  	protected static final String	NL			= "\015\012";
  
  	private static final String	lc_azureus_name = Constants.AZUREUS_NAME.toLowerCase();
  
  	protected static final byte[]	HTTP_RESPONSE_START = (
  		"HTTP/1.1 200 OK" + NL + 
  		"Content-Type: text/html" + NL +
  		"Server: " + Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION + NL +
  		"Connection: close" + NL +
  		"Content-Length: ").getBytes();
  	
  	protected static final byte[]	HTTP_RESPONSE_XML_START = (
  			"HTTP/1.1 200 OK" + NL + 
  			"Content-Type: text/xml; charset=\"utf-8\"" + NL +
  			"Server: " + Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION + NL +
  			"Connection: close" + NL +
  			"Content-Length: ").getBytes();
  
  	protected static final byte[]	HTTP_RESPONSE_END_GZIP 		= (NL + "Content-Encoding: gzip" + NL + NL).getBytes();
  	protected static final byte[]	HTTP_RESPONSE_END_NOGZIP 	= (NL + NL).getBytes();
  	
  	private TRTrackerServerTCP	server;
  	private String				server_url;
  	
  	private boolean			disable_timeouts 	= false;
  
  	
  	protected
  	TRTrackerServerProcessorTCP(
  		TRTrackerServerTCP		_server )
  	{
  		server	= _server;
  		
  		server_url = (server.isSSL()?"https":"http") + "://" + UrlUtils.convertIPV6Host(server.getHost()) + ":" + server.getPort();
  	}	
  
  	public boolean
  	areTimeoutsDisabled()
  	{
  		return( disable_timeouts );
  	}
  	
  	public void
  	setTimeoutsDisabled(
  		boolean	d )
  	{
  		disable_timeouts	= d;
  	}
  	
  	protected TRTrackerServerTCP
  	getServer()
  	{
  		return( server );
  	}
  	
  	protected boolean
  	processRequest(
  		String				input_header,
  		String				lowercase_input_header,
  		String				url_path,
  		InetSocketAddress	local_address,
  		InetSocketAddress	remote_address,
  		boolean				announce_and_scrape_only,
  		boolean				keep_alive,
  		InputStream			is,
  		OutputStream		os,
  		AsyncController		async )
  		
  		throws IOException
  	{
  		String	str = url_path;
  			
  		int	request_type	= TRTrackerServerRequest.RT_UNKNOWN;
  
  		try{
  			Map	root = null;
  				
  			TRTrackerServerTorrentImpl	specific_torrent	= null;
  			
  			boolean	gzip_reply = false;
  			
  			boolean		xml_output		= false;
  			
  			
  			try{
  				if ( str.startsWith( "/announce?" )){
  					
  					request_type	= TRTrackerServerRequest.RT_ANNOUNCE;
  					
  					str = str.substring(10);
  					
  				}else if ( str.startsWith( "/scrape?" )){
  					
  					request_type	= TRTrackerServerRequest.RT_SCRAPE;
  					
  					str = str.substring(8);
  				
  				}else if ( str.equals( "/scrape" )){
  					
  					request_type	= TRTrackerServerRequest.RT_FULL_SCRAPE;
  					
  					str = "";
  				
  				}else if ( str.startsWith( "/query?" )){
  
  					request_type	= TRTrackerServerRequest.RT_QUERY;
  					
  					str = str.substring(7);
  
  				}else{
  					
  					String	redirect = TRTrackerServerImpl.redirect_on_not_found;
  					
  					if ( announce_and_scrape_only ){
  						
  						if ( redirect.length() == 0 ){
  							
  							throw( new Exception( "Tracker only supports announce and scrape functions" ));
  						}
  					}else{
  						
  						setTaskState( "external request" );
  	
  						disable_timeouts	= true;
  						
  							// check non-tracker authentication
  							
  						String user = doAuthentication( remote_address, url_path, input_header, os, false );
  						
  						if ( user == null ){
  							
  							return( false );
  						}
  						
  						boolean[] ka = new boolean[]{ keep_alive };
  						
  						if ( handleExternalRequest( local_address, remote_address, user, str, input_header, is, os, async, ka )){
  						
  							return( ka[0] );
  						}
  					}
  					
  					if ( redirect.length() > 0 ){
  						
  						os.write( ("HTTP/1.1 301 Moved Permanently" + NL + "Location: " + redirect + NL + "Connection: close" + NL +  "Content-Length: 0" + NL + NL).getBytes() );
  						
  					}else{
  						
  						os.write( ("HTTP/1.1 404 Not Found" + NL + "Connection: close" + NL + "Content-Length: 0" + NL + NL ).getBytes() );
  					}
  					
  					os.flush();
  
  					return( false ); // throw( new Exception( "Unsupported Request Type"));
  				}
  				
  					// OK, here its an announce, scrape or full scrape
  				
  					// check tracker authentication
  					
  				if ( doAuthentication( remote_address, url_path, input_header, os, true ) == null ){
  					
  					return ( false );
  				}
  				
  				
  				int	enc_pos = lowercase_input_header.indexOf( "accept-encoding:");
  				
  				if ( enc_pos != -1 ){
  					
  					int	e_pos = input_header.indexOf( NL, enc_pos );
  					
  					if ( e_pos != -1 ){
  						
  							// check we've not found X-Accept-Encoding (for example)
  						
  						if ( enc_pos > 0 ){
  							
  							char	c = lowercase_input_header.charAt(enc_pos-1);
  							
  							if ( c != FF && c != ' ' ){
  								
  								enc_pos	= -1;
  							}
  						}
  						
  						if ( enc_pos != -1 ){
  							
  							String	accept_encoding = lowercase_input_header.substring(enc_pos+16,e_pos);
  						
  							gzip_reply = HTTPUtils.canGZIP( accept_encoding );
  						}
  					}
  				}
  								
  				setTaskState( "decoding announce/scrape" );
  
  				int	pos = 0;
  					
  				byte[]		hash		= null;
  				List		hash_list	= null;
  				String		link		= null;
  				
  				HashWrapper	peer_id		= null;
  				int			tcp_port	= 0;
  				String		event		= null;
  					
  				long		uploaded		= 0;
  				long		downloaded		= 0;
  				long		left			= 0;
  				int			num_want		= -1;
  				boolean		no_peer_id		= false;
  				byte		compact_mode	= TRTrackerServerTorrentImpl.COMPACT_MODE_NONE;
  				String		key				= null;
  				byte		crypto_level 	= TRTrackerServerPeer.CRYPTO_NONE;
  				int			crypto_port		= 0;
  				int			udp_port		= 0;
  				int			http_port		= 0;
  				int			az_ver			= 0;
  				boolean		stop_to_queue	= false;
  				String		scrape_flags	= null;
  				int			up_speed		= 0;
  				boolean		hide			= false;
  				
  				DHTNetworkPosition	network_position = null;
  				
  				String		real_ip_address		= remote_address.getAddress().getHostAddress();
  				String		client_ip_address	= real_ip_address;
  				
  				while(pos < str.length()){
  						
  					int	p1 = str.indexOf( '&', pos );
  						
  					String	token;
  						
  					if ( p1 == -1 ){
  							
  						token = str.substring( pos );
  							
  					}else{
  							
  						token = str.substring( pos, p1 );
  							
  						pos = p1+1;
  					}
  					
  					int	p2 = token.indexOf('=');
  						
  					if ( p2 == -1 ){
  							
  						throw( new Exception( "format invalid" ));
  					}
  						
  					String	lhs = token.substring( 0, p2 ).toLowerCase();
  					String	rhs = URLDecoder.decode(token.substring( p2+1 ), Constants.BYTE_ENCODING );
  						
  					// System.out.println( "param:" + lhs + " = " + rhs );
  						
  					if ( lhs.equals( "info_hash" )){
  							
  						byte[] b = rhs.getBytes(Constants.BYTE_ENCODING);
  						
  						if ( hash == null ){
  							
  							hash = b;
  							
  						}else{
  							
  							if ( hash_list == null ){
  								
  								hash_list = new ArrayList();
  								
  								hash_list.add( hash );
  							}
  							
  							hash_list.add( b );
  						}
  							
  					}else if ( lhs.equals( "peer_id" )){
  						
  						peer_id	= new HashWrapper(rhs.getBytes(Constants.BYTE_ENCODING));
  						
  					}else if ( lhs.equals( "no_peer_id" )){
  						
  						no_peer_id = rhs.equals("1");
  						
  					}else if ( lhs.equals( "compact" )){
  						
  						if ( server.isCompactEnabled()){
  							
  							if ( rhs.equals("1") && compact_mode == TRTrackerServerTorrentImpl.COMPACT_MODE_NONE ){
  								
  								compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_NORMAL;
  							}
  						}
  					}else if ( lhs.equals( "key" )){
  						
  						if ( server.isKeyEnabled()){
  							
  							key = rhs;
  						}
  						
  					}else if ( lhs.equals( "port" )){
  							
  						tcp_port = Integer.parseInt( rhs );
  						
  					}else if ( lhs.equals( "event" )){
  							
  						event = rhs;
  							
  					}else if ( lhs.equals( "ip" )){
  							
  						// System.out.println( "override: " + real_ip_address + " -> " + rhs + " [" + input_header + "]" );
  						
  						if ( !HostNameToIPResolver.isNonDNSName( rhs )){
  							
  							for (int i=0;i<rhs.length();i++){
  								
  								char	c = rhs.charAt(i);
  								
  								if ( c != '.' && c != ':' && !Character.isDigit( c )){
  									
  									throw( new Exception( "IP override address must be resolved by the client" ));
  								}	
  							}
  							
  							try{
  								rhs	= HostNameToIPResolver.syncResolve( rhs ).getHostAddress();
  							
  							}catch( UnknownHostException e ){
  								
  								throw( new Exception( "IP override address must be resolved by the client" ));
  							}
  						}
  						
  						client_ip_address = rhs;
  						
  					}else if ( lhs.equals( "uploaded" )){
  							
  						uploaded = Long.parseLong( rhs );
  						
  					}else if ( lhs.equals( "downloaded" )){
  							
  						downloaded = Long.parseLong( rhs );
  						
  					}else if ( lhs.equals( "left" )){
  							
  						left = Long.parseLong( rhs );
  												
  					}else if ( lhs.equals( "numwant" )){
  						
  						num_want = Integer.parseInt( rhs );
  						
  					}else if ( lhs.equals( "azudp" )){
  						
  						udp_port 	= Integer.parseInt( rhs );
  						
  							// implicit compact mode for 2500 indicated by presence of udp port
  						
  						compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_AZ;
  						
  					}else if ( lhs.equals( "azhttp" )){
  						
  						http_port 	= Integer.parseInt( rhs );
  
  					}else if ( lhs.equals( "azver" )){
  						
  						az_ver 	= Integer.parseInt( rhs );
  												
  					}else if ( lhs.equals( "supportcrypto" )){
  						
  						if ( crypto_level == TRTrackerServerPeer.CRYPTO_NONE ){
  						
  							crypto_level	= TRTrackerServerPeer.CRYPTO_SUPPORTED;
  						}
  						
  					}else if ( lhs.equals( "requirecrypto" )){
  												
  						crypto_level	= TRTrackerServerPeer.CRYPTO_REQUIRED;
  					
  					}else if ( lhs.equals( "cryptoport" )){
  
  						crypto_port = Integer.parseInt( rhs );
  						
  					}else if ( lhs.equals( "azq" )){
  					
  						stop_to_queue	= true;
  												
  					}else if ( lhs.equals( "azsf" )){
  						
  						scrape_flags = rhs;
  						
  					}else if ( lhs.equals( "link" )){
  						
  						link = rhs;
  					
  					}else if ( lhs.equals( "outform" )){
  
  						if ( rhs.equals( "xml" )){
  							
  							xml_output	= true;
  						}
  						
  					}else if ( lhs.equals( "hide" )){
  						
  						hide 	= Integer.parseInt( rhs ) == 1;
  
  					}else if ( TRTrackerServerImpl.supportsExtensions()){
  						
  						if ( lhs.equals( "aznp" )){
  
  							try{
  								network_position = DHTNetworkPositionManager.deserialisePosition( remote_address.getAddress(), Base32.decode( rhs ));
  																
  							}catch( Throwable e ){
  								
  							}
  						}else if ( lhs.equals( "azup" )){
  	
  							up_speed = Integer.parseInt( rhs );
  						}
  					}
  					
  					if ( p1 == -1 ){
  							
  						break;
  					}
  				}
  				
  					// let them hide!
  					// this is also useful if an az client wants to just hide themselves on 
  					// particular torrents (to prevent inward connections) as they can just
  					// add a tracker-extension to append this option
  				
  				if ( hide ){
  					
  					tcp_port 	= 0;
  					crypto_port	= 0;
  					http_port	= 0;
  					udp_port	= 0;
  				}
  				
  				if ( crypto_level == TRTrackerServerPeer.CRYPTO_REQUIRED ){
  					
  					if ( crypto_port != 0 ){
  						
  						tcp_port = crypto_port;
  					}
  				}
  				
  				byte[][]	hashes = null;
  				
  				if ( hash_list != null ){
  						
  					hashes = new byte[hash_list.size()][];
  						
  					hash_list.toArray( hashes );
  						
  				}else if ( hash != null ){
  					
  					hashes = new byte[][]{ hash };
  				}
  				
  					// >= so that if this tracker is "old" and sees a version 3+ it replies with the
  					// best it can - version 2
  				
  				if ( xml_output ){
  					
  					compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_XML;
  					
  				}else if ( az_ver >= 2 ){
  					
  					compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_AZ_2;
  				}
  				
  				Map[]						root_out = new Map[1];
  				TRTrackerServerPeerImpl[]	peer_out = new TRTrackerServerPeerImpl[1];
  				
  				specific_torrent = 
  						processTrackerRequest( 
  							server, str,
  							root_out, peer_out,
  							request_type,
  							hashes, link, scrape_flags,
  							peer_id, no_peer_id, compact_mode, key, 
  							event, stop_to_queue,
  							tcp_port&0xffff, udp_port&0xffff, http_port&0xffff,
  							real_ip_address,
  							client_ip_address,
  							downloaded, uploaded, left,
  							num_want,
  							crypto_level,
  							(byte)az_ver,
  							up_speed,
  							network_position );
  				
  				root	= root_out[0];
  
  