#!/usr/bin/perl -w

$Grab{version}='0.9.2';

# Debug routine.

sub debugprint {
	print STDERR "### ", @_ if($Options{verbose});
}

# Read in command line.

while(@ARGV) {
	if($ARGV[0] eq '-m') {
		shift;
		$Options{method}=$ARGV[0];
		shift;
	} elsif($ARGV[0] eq '--method') {
		shift;
		$Options{method}=$ARGV[0];
		shift;
	} elsif($ARGV[0] eq '-i') {
		shift;
		$Options{info}=1;
		$Options{method}='HEAD';
	} elsif($ARGV[0] eq '--info') {
		shift;
		$Options{info}=$1;
		$Options{method}='HEAD';
	} elsif($ARGV[0] eq '--verbose') {
		$Options{verbose}=1;
		shift;
	} elsif($ARGV[0] eq '-v') {
		$Options{verbose}=1;
		shift;
	} elsif($ARGV[0] eq '--listen') {
		$Options{method}='LISTEN';
		shift;
	} elsif($ARGV[0] eq '-l') {
		$Options{method}='LISTEN';
		shift;
	} elsif($ARGV[0] eq '--proxy') {
		shift;
		$Options{proxy}=$ARGV[0];
		shift;
	} elsif($ARGV[0] eq '-p') {
		shift;
		$Options{proxy}=$ARGV[0];
		shift;
	} elsif($ARGV[0] eq '--recurse') {
		shift;
		$Options{recurse}=1;
	} elsif($ARGV[0] eq '-R') {
		shift;
		$Options{recurse}=1;
	} elsif($ARGV[0] eq '--reget') {
		shift;
		$Options{reget}=1;
	} elsif($ARGV[0] eq '-r') {
		shift;
		$Options{reget}=1;
	} elsif($ARGV[0] eq '--display') {
		shift;
		$Options{display}=1;
	} elsif($ARGV[0] eq '-d') {
		shift;
		$Options{display}=1;
	} elsif($Options{URL}) {
		$Options{output}=$ARGV[0];
		shift;
	} else {
		debugprint "Found URL $ARGV[0] on cmdline.\n";
		$Options{URL}=$ARGV[0];
		shift;
	}
}

# Parse mimetypes file if present.

if(open(IN, "/usr/lib/mime.types") || open(IN, '/etc/httpd/conf/mime.types')) {
	debugprint "Loading mime.types\n";
	while(<IN>) {
		next if(/^[\#\;]/);
		if(/^(\S+)\s+(.*)$/) {
			$Stuff=$2;
			$Type=$1;
			while($Stuff =~ s/(\S+)\s*//) {
				debugprint "Setting \*.$1 to be $Type\n";
				$MimeTypes{$1}=$Type;
			}
		}
	}
}

# Read in .grabrc

if(open(IN, "$ENV{HOME}/.grabrc")) {
	$Section='aliases';
	while(<IN>) {
		chomp;
		s/[\;\#].*$//;
		# Strip off commentry
		debugprint "<RC>", $_, "\n";
		if(/^\[(.*)\]$/) {
			$Section=$1;
			next;
		}
		if($Section eq 'options') {
			if(/^(.*)\=(.*)$/) {
				$Options{$1}=$2;
			}
		} elsif($Section eq 'aliases') {
			if(/^(.*)\=(.*)$/) {
				$Aliases{$1}=$2;
				debugprint "Loaded alias $1 => $2\n";
			}
		} elsif($Section eq 'hosts') {
			if(/^(.*)\=(.*)$/) {
				$Hosts{$1}=$2;
				debugprint "Loaded default host for $1 => $2\n";
			}
		} elsif($Section eq 'ports') {
			if(/^(.*)\=(.*)$/) {
				$Ports{$1}=$2;
				debugprint "Loaded default port for $1 => $2\n";
			}
		} elsif($Section eq 'displays') {
			if(/^(.*)\=(.*)$/) {
				$Displays{$1}=$2;
				debugprint "Loaded display for $1 => $2\n";
			}
		}
	}
}

# Define the useful functions.

sub BreakURL {
	$URL=$_[0];
	debugprint "Starting with URL $URL.\n";
	if($URL =~ m!^([a-z]+):(.*)$!) {
		$URL{scheme}=$1;
		debugprint "Extracted scheme $URL{scheme}\n";
		$URL=$2;
	} else {
		debugprint "No scheme... Assuming 'raw'\n";
		$URL{scheme}='raw';
	}
	if($URL =~ m!^//!) {
		debugprint "Host found...\n";
		if($URL =~ m!^//([a-zA-Z0-9\:\@\.\-]+)/(.*)!) {
			$URL{host}=$1;
			$URL{request}=$2;
			debugprint "Extracted host information $URL{host} and request $URL{request}\n";
		} else {
			$URL =~ s!^//!!;
			$URL{host}=$URL;
			debugprint "Extracted host information $URL{host}\n";
		}
		if($URL{host} =~ s!\:([0-9a-z]+)$!!) {
			unless($URL{port}=getservbyname($1, 'tcp')) {
				$URL{port}=$1;
			}
		} else {
			unless($URL{port}=$Ports{$URL{scheme}}) {
				unless($URL{port}=getservbyname($URL{scheme}, 'tcp')) {
					$URL{port}=80;
				}
			}
		}
		debugprint "Using port $URL{port}\n";
		if($URL{host} =~ s!^(.*)\@!!) {
			$URL{user} = $1;
			debugprint "Found user information $URL{user}\n";
			if($URL{user} =~ s!\:(.*)$!!) {
				$URL{pass}=$1;
				debugprint "Found password $URL{pass}\n";
			}
		}
	} elsif($Hosts{$URL{scheme}}) {
		debugprint "No host... assuming $Hosts{$URL{scheme}}...\n";
		$URL{host}=$Hosts{$URL{sceme}};
		$URL{request}=$URL;
	} else {
		debugprint "No host... assuming localhost...\n";
		$URL{host}='localhost';
		$URL{request}=$URL;
	}
	return %URL;
}

# Now we need a way of getting URL's.

sub ConnectSocket {
	use Socket;
	my($iaddr, $paddr, $proto);
#	local *CTRL;
	
	debugprint "Connecting to $GoURL{host}, port $GoURL{port}...\n";
	if($GoURL{host} =~ /[a-zA-Z]/) {
		$iaddr=gethostbyname($GoURL{host});
	} else {
		$iaddr=inet_aton($GoURL{host});
	}
	$paddr=sockaddr_in($GoURL{port}, $iaddr);
	$proto=getprotobyname('tcp');
	socket(CTRL, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
	connect(CTRL, $paddr) || die "connect: $!";
	select(CTRL); $|=1; select(STDOUT);
	debugprint "Done.\n";
	return \*CTRL;
}

sub StartDataPassive {
	use Socket;
	my($iaddr, $paddr, $proto);
#	local *CTRL;
	
	$portdata=shift;
	debugprint "Handed $portdata...\n";
	$portdata =~ s/,([0-9]+),([0-9]+)$//;
	$port=($1*256)+$2;
	$portdata =~ s/,/./g;
	
	debugprint "Connecting to $portdata, port $port...\n";
	$iaddr=inet_aton($portdata);
	$paddr=sockaddr_in($port, $iaddr);
	$proto=getprotobyname('tcp');
	socket(DATA, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
	connect(DATA, $paddr) || die "connect: $!";
	select(DATA); $|=1; select(STDOUT);
	debugprint "Done.\n";
	return \*DATA;
}

sub ConnectOutput {
#	local *OUT;

	if($Options{output}) {
		if($Options{reget}) {
			open(OUT, ">>$Options{output}");
		} else {
			open(OUT, ">$Options{output}");
		}
		return \*OUT;
	} elsif($Options{display}) {
		open(OUT, ">/tmp/.grab.$$");
		return \*OUT;
	} else {
		return \*STDOUT;
	}
}

sub ConnectSocketListen {
	use Socket;
	my($iaddr, $paddr, $proto);
#	local *LIST;
	
	$proto=getprotobyname('tcp');
	
	socket(LIST, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
	setsockopt(LIST, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) || die "setsockopt: $!";
	bind(LIST, sockaddr_in($GoURL{port}, INADDR_ANY)) || die "bind: $!";
	listen(LIST, SOMAXCONN) || die "listen: $!";
	return(\*LIST);
}

sub StartData {
	use Socket;
	my($iaddr, $paddr, $proto);
	($CTRL)=@_;
#	local *LIST;
	
	$proto=getprotobyname('tcp');
	
	socket(LIST, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
	setsockopt(LIST, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) || die "setsockopt: $!";
	for($port=10000;$port!=11000;$port++) {
		bind(LIST, sockaddr_in($port, INADDR_ANY)) || next;
		debugprint "Success on $port!\n";
		last;
	}
	listen(LIST, SOMAXCONN) || die "listen: $!";
	($_, $iaddr)=unpack_sockaddr_in(getsockname($CTRL));
	$desc=inet_ntoa($iaddr);
	$desc =~ s/\./,/g;
	$port1=int($port/256);
	$port2=$port % 256;
	$desc .= ",$port1,$port2";
	return($desc, \*LIST);
}

sub GetData {
	($LIST, $OUT)=@_;
	
	debugprint "Starting data setup final...\n";
	if($Options{method} eq 'PORT') {
#		local *DATA;
		accept(DATA, $LIST) || die "accept: $!";
		$DATA=\*DATA;
	} else {
		$DATA=$LIST;
	}
	
	debugprint "Getting Data...\n";
	while(<$DATA>) {
		if($Options{info}) {
			if( /src\=[\"\']*([^\"\'\s]+)/i ) {
				push @Images, $1;
			}
		} elsif($Options{recurse} && $ContentType) {
			chop; chop;
			unless(/^\.*$/) {
				print STDERR "$_: retrieve $Options{URL}/$_ to $Options{output}/$_\n";
				if($Options{method} eq 'PASV') {
					system($0, '--recurse', '--method', 'PASV', "$Options{URL}/$_", "$Options{output}/$_");
				} else {
					system($0, '--recurse', "$Options{URL}/$_", "$Options{output}/$_");
				}
			}
		} else {
			print $OUT $_;
		}
		print STDERR "<<< ", $_ if($Options{verbose});
	}
	debugprint "Got Data...\n";
}

sub GetPostData {
	my($line, $var, $val);
	$PostData='';
	while(<>) {
		chomp;
		if(m/^([^\=]+)=(.*)$/) {
			$PostData="$PostData&" if($PostData ne '');
			($var, $val)=($1, $2);
			$var =~ s/\\n/\r\n/g;
			$val =~ s/([^a-zA-Z0-9])/join('', '%', unpack('H', $1))/eg;
			$PostData="$PostData$var=$val";
		}
	}
}

sub sockprint {
	$S=shift;
	print $S @_;
	print STDERR ">>> ", @_ if($Options{verbose});
}

sub GoHTTP {
	unless($Options{proxy}) {
		$GoURL{request}='/' unless($GoURL{request});
		$GoURL{request} = "/$GoURL{request}" if($GoURL{request} !~ /^\//);
	}
	unless($Options{method}) {
		$Options{method}='GET';
	}
	$Protocol='HTTP/1.1';
	TRY:while($Protocol ne '') {
		$SOCK=ConnectSocket();
		$OUT=ConnectOutput();
		undef $Hunt;
		sockprint($SOCK,"$Options{method} $GoURL{request} $Protocol\r\n");
		sockprint($SOCK,"Host: $FetchURL{host}\r\n") if($Protocol eq 'HTTP/1.1');
		sockprint($SOCK,"User-Agent: Grab/", $Grab{version}, "\r\n");
		sockprint($SOCK,"Content-Type: application/x-url-encoded\r\n") if($Options{method} eq 'POST');
		sockprint($SOCK,"Content-Length: ", length($PostData), "\r\n") if($Options{method} eq 'POST');
		sockprint($SOCK,"\n");
		if($Options{method} eq 'POST') {
			sockprint($SOCK,$PostData);
		}
		$Line=<$SOCK>;
		chop $Line;
		chop $Line;
		if($Line =~ m!(HTTP/[0-9\.]+)\s+([0-9]+)\s*(.*)$!) {
			print STDERR "<<< ", $Line, "\n" if($Options{verbose});
			if($1 ne $Protocol) {
				debugprint "Protocol version not understood.\n";
				debugprint "Switching to HTTP/1.0\n";
				$Protocol='HTTP/1.0';
				next;
			}
			($Code, $Message)=($2, $3);
			if($2 !~ /^2/) {
				# Damn. we got an error.
				# We could actually ignore it - 
				# HTTP is forgiving of bad implementations.
				# But I'm not.
				if($Code =~ /^3/) {
					$Hunt=1;
				} else {
					print STDERR "HTTP Error: <$Code> <$Message>\n";
				}
			}
			while($Line ne '') {
				$Line=<$SOCK>;
				chop $Line;
				chop $Line;
				if($Options{verbose}) {
					print STDERR "<<< ", $Line, "\n";
				}
				if($Line =~ m/^([a-zA-Z\-]+):\s+(.*)$/) {
					($Header, $Value)=($1, $2);
					$Header =~ tr /[A-Z]/[a-z]/;
					$ObjectInfo{$Header}=$Value;
					if($Header eq 'content-type') {
						$ContentType=$Value;
					}
				}
				if(defined $Hunt) {
					if($Line =~ /^Location: (.*)/i) {
						debugprint "Redirected to $1\n";
						$SaveHost=$GoURL{host};
						$SaveRequest=$GoURL{request};
						if($Options{proxy}) {
							$GoURL{request} = $1;
						} else {
							%GoURL=BreakURL($1);
							if($GoURL{request}) {
								$GoURL{request}="/$GoURL{request}";
							} else {
								$GoURL{request}='/';
							}
						}
						$Protocol='HTTP/1.1' if($SaveHost ne $GoURL{host});
						next TRY if(($GoURL{request} ne $SaveRequest) || ($GoURL{host} ne $SaveHost));
						debugprint "Ignoring redirection - $SaveHost/$SaveRequest\n";
					}
				}
			}
		} else {
			debugprint "Falling back to HTTP/0.9...\n";
			if($Options{info}) {
				print STDERR "Info mode failed: Not supported on HTTP/0.9 (Broken server anyway)\n";
			} else {
				print $OUT $Line;
				print STDERR "<<< ", $Line if($Options{verbose});
			}
		}
		GetData($SOCK, $OUT);
		$Protocol='';
	}
}

sub GetReturnFTP {
	$S=shift;
	
	while(<$S>) {
		chop;
		chop;
		print STDERR "<<< ", $_, "\n" if($Options{verbose});
		if(/^([0-9][0-9][0-9]) (.*)$/) {
			return ($1, $2);
		}
	}
	return('421', 'Remote server has closed connection');
}

sub CleanKill {
	print STDERR "Server: $_[0]\n";
	exit(5);
}

sub GoFTP {
	$SOCK=ConnectSocket();
	($Code, $Message)=GetReturnFTP($SOCK);
	if($Code !~ /^2/) {
		CleanKill $Message;
	}
	# USER
	if($GoURL{user}) {
		sockprint($SOCK, "USER $GoURL{user}\r\n");
	} else {
		sockprint($SOCK, "USER ftp\r\n");
	}
	($Code, $Message)=GetReturnFTP($SOCK);
	if($Code =~ /^3/) {
		# PASS
		if($GoURL{pass}) {
			sockprint($SOCK, "PASS $GoURL{pass}\r\n");
		} else {
			if($Options{email}) {
				sockprint($SOCK, "PASS $Options{email}\r\n");
			} else {
				sockprint($SOCK, "PASS $ENV{USER}\@$Options{hostname}\r\n");
			}
		}
		($Code, $Message)=GetReturnFTP($SOCK);
		CleanKill $Message if($Code !~ /^2/);
	} elsif($Code !~ /^2/) {
		CleanKill $Message;
	}
	$Dir="/$GoURL{request}";
	if($Dir =~ m!/$!) {
		$Fail=1;
	} else {
		if($Dir =~ s!/([^/]*)$!/!) {
			$File=$1;
		}
		$Fail=2;
	}
	$File='.' unless($File);
	if($Options{recurse}) {
		$Options{output}=$File unless($Options{output});
	}
	sockprint($SOCK, "CWD $Dir\r\n");
	($Code, $Message)=GetReturnFTP($SOCK);
	CleanKill $Message if($Code !~ /^2/);
	# File/List retreival.
	while($Fail) {
		if($Fail > 1) {
			sockprint($SOCK,"TYPE I\r\n");
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^2/);
		} else {
			sockprint($SOCK,"TYPE A\r\n");
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^2/);
		}
		if($Options{method} eq 'PORT') {
			($Desc, $DS)=StartData($SOCK);
			sockprint($SOCK, "PORT $Desc\r\n");
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^2/);
		} else {
			sockprint($SOCK, "PASV\r\n");
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^2/);
			if($Message =~ /\(([0-9\,]+)\)/) {
				$DS=StartDataPassive($1);
			} else {
				die "Can't figure out $Message";
			}
		}
		if($Fail > 1) {
			# Reget stuff.
			if($Options{reget} && seek($OUT, 0, 2)) {
				sockprint($SOCK,"REST ".tell($OUT)."\r\n");
				($Code, $Message)=GetReturnFTP($SOCK);
				if($Code !~ /^[23]/) {
					Cleankill $Message;
				}
			}
			sockprint($SOCK,"RETR $File\r\n");
			($Code, $Message)=GetReturnFTP($SOCK);
			if($Code !~ /^[12]/) {
				$Fail--;
				sockprint($SOCK, "CWD $File\r\n");
				($Code, $Message)=GetReturnFTP($SOCK);
				CleanKill $Message if($Code !~ /^2/);
				if($Options{recurse}) {
					print "$File: mkdir\n";
					mkdir $File, 0700;
				}
				next;
			}
			$OUT=ConnectOutput();
			GetData($DS, $OUT);
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^[12]/);
		} else {
			$ContentType='text/plain';
			if($Options{recurse}) {
				sockprint($SOCK,"NLST\r\n");
			} else {
				sockprint($SOCK,"LIST\r\n");
			}
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^1/);
			$OUT=ConnectOutput() unless($Options{recurse});
			GetData($DS, $OUT);
			($Code, $Message)=GetReturnFTP($SOCK);
			CleanKill $Message if($Code !~ /^2/);
		}
		$Fail=0;
	}
	sockprint($SOCK, "QUIT\r\n");
	($Code, $Message)=GetReturnFTP($SOCK);
	CleanKill $Message if($Code !~ /^2/);
}

# Do stuff.

$Options{hostname}=`hostname`;
chomp $Options{hostname};

unless($Options{URL}) {
	die "No URL";
}

if($Aliases{$Options{URL}}) {
	$Options{URL}=$Aliases{$Options{URL}};
	debugprint "Transated alias to $Options{URL}\n";
}

%FetchURL=&BreakURL($Options{URL});

# Right. Now plug the URL together - we may require a proxy, you see.

if($Options{proxy}) {
	%GoURL=BreakURL($Options{proxy});
	$GoURL{request}=$Options{URL};
	debugprint "Using proxy for $Options{URL}\n";
} else {
	%GoURL=%FetchURL;
}

$GoURL{request}='' unless($GoURL{request});

if($GoURL{scheme} eq 'http') {
	if(defined $Options{method}) {
		undef $Options{method} if(($Options{method} eq 'PASV')||($Options{method} eq 'PORT'));
	}
	if($FetchURL{scheme} eq 'http') {
		if(not -t STDIN) {
			$Options{method}='POST' unless($Options{method});
		}
		$Options{method}='GET' unless($Options{method});
		GetPostData() if($Options{method} eq 'POST');
	}
	if($Options{info}) {
		$Options{method}='HEAD';
	}
	GoHTTP();
	if($Options{info} && ($ContentType eq 'text/html')) {
		$Options{method}='GET';
		GoHTTP();
		$Options{method}='HEAD';
		%SaveInfo=%ObjectInfo;
		$TotalData=$ObjectInfo{'content-length'};
		if(@Images) {
			%SaveURL=%GoURL;
			foreach $image (@Images) {
				%GoURL=%SaveURL;
				if($image =~ m!^/!) {
					$GoURL{request} =~ s!/[^/].*$!/$image!;
				} elsif($image =~ m!^http://!) {
					$GoURL{request}=$image;
				} else {
					$GoURL{request} =~ s!/[^/]*$!/$image!;
				}
				GoHTTP();
				$TotalData += $ObjectInfo{'content-length'};
			}
		}
		%ObjectInfo=%SaveInfo;
	}
} elsif($GoURL{scheme} eq 'ftp') {
	if(defined $Options{method}) {
		undef $Options{method} if(($Options{method} eq 'GET')||($Options{method} eq 'HEAD')||($Options{method} eq 'POST'));
	}
	$Options{method}='PORT' unless($Options{method});
	GoFTP();
} elsif($GoURL{scheme} eq 'raw') {
	$Options{method}='NOWT' unless($Options{method});
	undef $Options{verbose};
	if($Options{method} ne 'LISTEN') {
		$SOCK=ConnectSocket();
		$OUT=ConnectOutput();
		while(<STDIN>) {
			sockprint($SOCK,$_);
		}
		GetData($SOCK,$OUT);
	} else {
		$SOCK=ConnectSocketListen();
		$OUT=ConnectOutput();
		while(1) {
			GetData($SOCK, $OUT);
		}
	}
} elsif($GoURL{scheme} eq 'finger') {
	$Options{method}='NOWT';
	$SOCK=ConnectSocket();
	$OUT=ConnectOutput();
	if($GoURL{user}) {
		sockprint($SOCK,$GoURL{user}, "\r\n");
	} else {
		sockprint($SOCK, $GoURL{request}, "\r\n");
	}
	GetData($SOCK, $OUT);
} elsif($GoURL{scheme} eq 'mailto') {
	$Options{method}='NOWT';
	debugprint("Start: $FetchURL{request}\n");
	($Mailto{user}, $Mailto{domain})=split(/\@/, $FetchURL{request});
	open(HOST, "host -t mx $Mailto{domain} |");
	$Mailto{host}=$Mailto{domain};
	$Mailto{pri}=65535;
	while(<HOST>) {
		if(/^$Mailto{domain}.+\(pri\=([0-9]+)\) by (.*)$/) {
			if($1 < $Mailto{pri}) {
				$Mailto{pri}=$1;
				$Mailto{host}=$2;
			}
		}
	}
	print "Mail for $Mailto{user}\@$Mailto{domain} goes to $Mailto{host}\n";
	$GoURL{host}=$Mailto{host};
	$GoURL{port}=25;
	chomp($MyHost=`hostname`);
	$SOCK=ConnectSocket();
	($Code, $Message)=GetReturnFTP($SOCK);
	if($Code !~ /^2/) {
		CleanKill $Message;
	}
	sockprint($SOCK, "HELO $MyHost\r\n");
	($Code, $Message)=GetReturnFTP($SOCK);
	if($Code !~ /^2/) {
		CleanKill $Message;
	}
	sockprint($SOCK, "EXPN \<$Mailto{user}\@$Mailto{domain}\>\r\n");
	while(<$SOCK>) {
		if($_ !~ /^2/) {
			s/^[0-9]+//;
			print "Can't trace past $Mailto{host}: $_";
			last;
		}
		while(/\<([^\@]+)\@([^\>]+)\>/) {
			print "At $Mailto{host}, mail is forwarded to $1\@$2.\n";
			if($Mailto{host} eq $2) {
				print "Trail ends here.\n";
			} else {
				system($0, "mailto:$1\@$2");
			}
			s/\<[^\>]+\>//;
		}
		last if(/^[0-9][0-9][0-9]\s/);
	}
	sockprint($SOCK, "QUIT\r\n");
	($Code, $Message)=GetReturnFTP($SOCK);
	if($Code !~ /^2/) {
		CleanKill $Message;
	}
	exit(0);
} else {
	die "Unknown scheme...\n";
}

if($ContentType && $Displays{$ContentType}) {
	debugprint "Would print $ContentType with $Displays{$ContentType}\n";
}

sub TypeFromFile {
	($Filename)=@_;
	$_=`file $Filename`;
	study;
	if(/GIF/) {
		return 'image/gif';
	} elsif(/smtp mail text/) {
		return 'message/rfc822';
	} elsif(/Zip/) {
		return 'application/x-zip';
	} elsif(/HTML/) {
		return 'text/html';
	} elsif(/text/) {
		return 'text/plain';
	} elsif(/Module.*sound/) {
		return 'audio/x-mod';
	} elsif(/RPM/) {
		return 'application/x-rpm';
	}
}

if($Options{display}) {
	$ContentType=&TypeFromFile("/tmp/.grab.$$") unless($ContentType);
	unless($ContentType) {
		if(/\.([a-z]+)$/) {
			$ContentType=$MimeTypes{$1} if($MimeTypes{$1});
		}
	}
	$ContentType='application/octet-stream' unless($ContentType);
	if($Displays{$ContentType}) {
		system("$Displays{$ContentType} /tmp/.grab.$$");
	} else {
		system("metamail -b -c $ContentType /tmp/.grab.$$");
	}
}

if($Options{info}) {
	# Present information on URL.
	if($GoURL{scheme} eq 'http') {
		$FetchURL{request}='/' unless($FetchURL{request});
		print "Served by $ObjectInfo{server} at host $GoURL{host}\n";
		print "$FetchURL{request} is of type $ContentType, and is ".$ObjectInfo{'content-length'}." bytes.\n";
		print "Total data for page, including graphics, is $TotalData bytes.\n" if($TotalData);
	}
}
