#!/usr/local/bin/perl 
=head1 GoogleHack web interface server

=head1 SYNOPSIS

ghack_server.pl acts as a server to which the web interface connects to for all user queries.
The server then retrieves the results of the queries and sends it back to the web interface.

=head1 DESCRIPTION

To install the server please follow these steps:

1) Change the lib path to the path where WebService::GoogleHack has been installed on your machine.
 
use lib "/home/lib/perl5/site_perl/";

2) Change the following variables accordingly:

$BASEDIR = '/webspace/cgi-bin/ghack'; 

$localport = 32983;

$lock_file = "$BASEDIR/ghack_server.lock";

$error_log = "$BASEDIR/error.log";

Basedir should be the path to the cgi-bin directory in which google_hack.cgi 
resides.

The localport should be a number above 1024, and less than around 66,000. Make
 sure that localport number is the same on both the client and server side.

The lockfile & error_log variables will remain the same. 

3)If your ghack server is running behind a firewall, you will need to
edit the file /etc/sysconfig/iptables to allow clients to connect to the machine through the port you had given.  There is a line that looks like this:

-A RH-Firewall-1-INPUT -p tcp --dport XXXXX -j ACCEPT

Where XXXXX is the port that your client will be connecting to (the value of $localport in ghack_server.pl).

Now start the server by running the ghack_server.pl as you would run a 
regular perl file.

=head1 AUTHOR

Ted Pedersen, E<lt>tpederse@d.umn.eduE<gt> 

Pratheepan Raveendranathan, E<lt>rave0029@d.umn.eduE<gt> 

Jason Michelizzi, E<lt>mich0212@d.umn.eduE<gt> 

Date 11/08/2004

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2003 by Pratheepan Raveendranathan, Ted Pedersen, Jason Michelizzi

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, or (at your option) any later
version.

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.

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.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. 

=cut

use lib "/home/vold/47/rave0029/lib/perl5/site_perl/";
use strict;

###########################################################
# Please make sure to change the value of these variables #
# according to documentation                              # 
###########################################################

my $BASEDIR = '/home/vold/47/rave0029/www/cgi-bin/ghack';
my $localport = 32983;
my $lock_file = "$BASEDIR/ghack_server.lock";
my $error_log = "$BASEDIR/error.log";

my $lockfh;
{
    if (-e $lock_file) {
	die "Lock file `$lock_file' already exists.  Make sure that another\n",
	    "instance of $0 isn't running, then delete the lock file.\n";
    }
    open ($lockfh, '>', $lock_file)
      or die "Cannot open lock file `$lock_file' for writing: $!";
    print $lockfh $$;
    close $lockfh or die "Cannot close lock file `lock_file': $!";
}
END {
    if (open FH, '<', $lock_file) {
	my $pid = <FH>;
	close FH;
	unlink $lock_file if $pid eq $$;
    }
}

# prototypes:
sub getlock ();
sub releaselock ();

use sigtrap handler => \&bailout, 'normal-signals';
use IO::Socket::INET;

use SOAP::Lite;
use WebService::GoogleHack;

# reset (untaint) the PATH
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';

# automatically reap child processes
$SIG{CHLD} = 'IGNORE';

# re-direct STDERR from wherever it is now to a log file
close STDERR;
open (STDERR, '>', $error_log) or die "Could not re-open STDERR";
chmod 0664, $error_log;


# The is the socket we listen to
my $socket = IO::Socket::INET->new (LocalPort => $localport,
				    Listen => SOMAXCONN,
				    Reuse => 1,
				    Type => SOCK_STREAM
				   ) or die "Could not be a server: $!";

print "SERVER started on port $localport "; 

my $search = new WebService::GoogleHack;

############################################################
# Change to you config file path                           #
############################################################

$search->initConfig("");

ACCEPT:
while (my $client = $socket->accept) {
    my $childpid;
    if ($childpid = fork) {
	# we're the parent here, so we just go wait for the next request
	next ACCEPT;
    }

    defined $childpid or die "Could not fork: $!";

    # here we're the child, so we actually handle the request
    my @requests;
    while (my $request = <$client>) {
	last if $request eq "\015\012";
	push @requests, $request;
    }

    foreach my $i (0..$#requests) {
        my $request = $requests[$i];
	my $rnum = $i + 1;
	$request =~ m/^(\w)\b/;
	my $type = $1 || 'UNDEFINED';
	my $query=$request;

#user wants word cluster
	if ($type eq 'c')
	{		   
	    my ($dummy,$searchString,$numResults,$cutOffs,$numIterations)= split(/\t/, $query);
	    my @terms=();
	    
	    my @temp= split(/:/, $searchString);
	    
	    foreach my $word (@temp)
	    {
	       if($word ne "")
	       {
		   push(@terms,$word);
	       }
	   
	    }
	  
 my $results=$search->wordClusterInPage(\@terms,$numResults,$cutOffs,$numIterations,"results.txt","true");
	    
	    print $client "$results";
	    print $client "\015\012";
	}
#if user wants to measure semantic relatedness/ PMI score
	elsif ($type eq 'p')
	{		   
	    my ($dummy,$searchString1,$searchString2)= split(/\t/, $query);
	    	  
	    my $results=$search->measureSemanticRelatedness($searchString1,$searchString2);
	 

	    print $client "$results";
	    print $client "\015\012";
	}
	else {
	    print $client "! Bad request type `$type'\015\012";
	}
    }

    # Terminate ALL messages with CRLF (\015\012).  Do NOT use
    # \r\n (the meaning of \r and \n varies on different platforms).
    print $client "\015\012";

 EXIT_CHILD:
    $client->close;
    $socket->close;

    # don't let the child accept:
    exit;
}

$socket->close;
exit;

# A signal handler, good for most normal signals (esp. INT).  Mostly we just
# want to close the socket we're listening to and delete the lock file.
sub bailout
{
    my $sig = shift;
    $sig = defined $sig ? $sig : "?UNKNOWN?";
    $socket->close if defined $socket;
    print STDERR "Bailing out (SIG$sig)\n";
    releaselock;
    unlink $lock_file;
    exit 1;
}


use Fcntl qw/:flock/;

# gets a lock on $lockfh.  The return value is that of flock.
sub getlock ()
{
    open $lockfh, '>>', $lock_file
	or die "Cannot open lock file $lock_file: $!";
    flock $lockfh, LOCK_EX;
}

# releases a lock on $lockfh.  The return value is that of flock.
sub releaselock ()
{
    flock $lockfh, LOCK_UN;
    close $lockfh;
}

__END__