#!/usr/local/bin/perl5 -w 
use strict;

use HTML::FormatText;
use HTML::Parse;
use vars qw($opt_h $opt_a $opt_e $opt_d $opt_c $opt_p);
use Getopt::Std;

# URL that handles our FedEx query
my $cgi = 'http://www.fedex.com/cgi-bin/track_it';

getopts('ha:e:d:c:p:');

# print help upon request or when arguments are missing
if ($opt_h  || !($opt_a && $opt_e && $opt_d && $opt_c )) {
  print_help();
  exit(0);
}

# 
my $tracker = new FedEx $cgi, $opt_e, $opt_p;

my $keep_checking = 1;

while ($keep_checking) {
  $tracker->check($opt_a, $opt_c, $opt_d);

  if ($tracker->retrieve_okay) {

    if ($tracker->delivered) {
      print "Tracking number $opt_a was delivered to: ",
             $tracker->who_got_it, "\n";
      $keep_checking = 0;

    } 
    else {

      # request was okay, but not delivered.  Let's wait
      sleep (60 * 30);  # sleep 30 minutes
    }

  } 
  else {

    # request not successful
    my $html_error_message = $tracker->error_info;

    my $parsed    = parse_html($html_error_message);
    my $converter = new HTML::FormatText;
    print $converter->format($parsed);
    
    $keep_checking = 0;
  }
}


sub  print_help {

  print <<HELP
This program prints a notification when a FedEx shipment is delivered.
fedex -a 1234 -e user\@host.com -d 120396 -c U.S.A. [ -p http://host:port/ ] 

h - this help text
a - airbill number
e - your email address
d - date in MMDDYY format that document was sent
c - country of recipient
p - use this proxy server [optional]
HELP
}

package FedEx;

use HTTP::Request;
use HTTP::Response;
use LWP::RobotUA;
use HTTP::Status;

sub new {

  my($class, $cgi_url, $email, $proxy) = @_;
  my $user_agent_name = 'ORA-Check-FedEx/1.0';

  my $self  = {};
  bless $self, $class;

  $self->{'url'} = new URI::URL $cgi_url;

  $self->{'robot'} = new LWP::RobotUA $user_agent_name, $email; 
  $self->{'robot'}->delay(0);    # we'll delay requests by hand

  if ($proxy) {
    $self->{'robot'}->proxy('http', $proxy);
  }

  $self;
}

sub check {

  my ($self, $track_num, $country, $date) = @_;

  $self->{'url'}->query("trk_num=$track_num&dest_cntry=" .
    "$country&ship_date=$date");
  my $request = new HTTP::Request 'GET', $self->{'url'};

  my $response = $self->{'robot'}->request($request);
  $self->{'status'} = $response->code();

  if ($response->code == RC_OK) {

    if ($response->content =~ /Delivered To : (\w.*)/) {

      # package delivered
      $self->{'who_got_it'} = $1;
      $self->{'delivered'} = 1;
    } 

    # Odd cases when package is delivered but "Delivered To" is blank.
    # Check for delivery time instead.

    elsif ($response->content =~ /Delivery Time : \w.*/) {

      # package delivered
      $self->{'who_got_it'} = 'left blank by FedEx computer';
      $self->{'delivered'} = 1;
    }
    else {

      # package wasn't delivered
      $self->{'delivered'} = 0;

      # if there isn't a "Delivered To : " field, something's wrong.
      # error messages seen between HTML comments

      if ($response->content !~ /Delivered To : /) {
        $self->{'status'} = RC_BAD_REQUEST;

        # get explanation from HTML response
	my $START = '<!-- BEGIN TRACKING INFORMATION -->';
	my $END = '<!-- END TRACKING INFORMATION -->';

        if   ($response->content =~ /$START(.*?)$END/s) {
    
	  $self->{'error_as_HTML'} = $1;
        
        } 
        else {
          # couldn't get explanation, use generic one
          $self->{'error_as_HTML'} = 'Unexpected HTML response from FedEx'; 

        }    # couldn't get error explanation
      }      # unexpected reply
    }        # not delivered yet
  }          # if HTTP response of RC_OK (200)
  else {
    $self->{'error_as_HTML'} = $response->error_as_HTML;
  }

}

sub retrieve_okay {

  my $self = shift;
  if ($self->{'status'} != RC_OK) {return 0;}
  1;
}

sub delivered {

  my $self = shift;
  $self->{'delivered'};
}

sub who_got_it {

  my $self = shift;
  $self->{'who_got_it'};
}

sub error_info {

  my $self = shift;
  $self->{'error_as_HTML'};
}

