#!/usr/bin/perl

# warranty
# perl script for checking Apple warranty/applecare status
#
# Copyright � 2008,2011 Thomas A. Fine
#
# Version 1.1
#
# Redistribution is permitted, in whole or in part, for commercial and
# non-commercial purposes, provided only that this copyright notice
# remains.
#

$plist="/Library/Preferences/com.apple.RemoteDesktop";

$|=1;

while (substr($ARGV[0],0,1) eq "-") {
  $opt=shift(@ARGV);
  if ($opt eq "-v") {
    $verbose=1;
  } elsif ($opt eq "-d") {
    $debug=1;
    $verbose=1;
  } elsif ($opt eq "-p") {
    $plist=shift(@ARGV);
    if ($plist =~ /\.plist$/) {
      $plist =~ s/.plist$//;
    }
  } elsif ($opt eq "-f") {
    if ($#ARGV == -1) { &usage; exit(1); }
    $field=shift(@ARGV);
    if ($n>=1) { &usage; exit(1); }
  } else {
    &usage;
    exit(1);
  }
}

if ($#ARGV > 0) {
  &usage;
  exit(0);
}

if ($#ARGV > -1) {
  $serial=shift(@ARGV);
} else {
  $top = &load_sysprof_xml("SPHardwareDataType");

  $tmp=${$top}[0];
  # Reformulate the mess we get back from this into a cleaner structure

  foreach $datatype (@{$top}) {
    $dtname = ${$datatype}{'_dataType'};

    #We want the items array, stuff it into a variable with datatype for name
    @{$dtname}=@{${$datatype}{'_items'}};
  }       

  $serial=${$SPHardwareDataType[0]}{'serial_number'};

  # This old non-xml way was simpler, but kept breaking with new OSX releases
  # because things would pop up that would match the pattern.
  #open(SP,"system_profiler SPHardwareDataType|");
  #while(<SP>) {
  #  if (/Serial Number/) {
  #    chop;
  #    s/^.*Serial Number.*:\s*//;
  #    $serial=$_;
  #  }
  #}
  #close(SP);
}

if ($debug) { print "Looking up serial number: >>$serial<<\n"; }

#getServiceOptions calls
#/ServiceOption.do&serialNo=   &SOcountry=
#getComponents calls
#/Warranty.do&serialNumber=    &country=    &fullCountryName=

#This one worked for a long time
#$host='selfsolve.apple.com';
#$port=443;
#$path='/Warranty.do';
#$path='/Warranty.do?serialNumber=' . $serial . '&country=USA&fullCountryName=United%20States';

$host='selfsolve.apple.com';
$port=443;
$path='/warrantyChecker.do';
$path='/warrantyChecker.do?sn=' . $serial;

open(CURL,"curl -s -k 'https://$host:$port$path'|");
while(<CURL>) {
  $page .= $_;
}

if ($debug) {
  print $page, "\n";
}

$tmp=$page;
$tmp =~ s/^[^{]*{//;
$tmp =~ s/}[^}]*$//;
$tmp =~ s/\\"/%22/g;  #simplify parsing by quoting quoted quotes
while (length($tmp)) {
  $tmp =~ s/^"([^"]*)":"([^"]*)",*//;
  $a=$1; $b=$2;
  $b =~ s/%22/"/g;    #put back the quoted quotes
  $data{$a}=$b;
  if ($debug) {
    print "$a: $b\n";
  }
}

if ($verbose) {
  if (length($data{PURCHASE_DATE})) {
    $start=$data{PURCHASE_DATE};
  } elsif (length($data{ESTIMATED_START_DT})) {
    $start=$data{ESTIMATED_START_DT} . " (estimated)";
  }
  if (length($data{COV_END_DATE})) {
    $end=$data{COV_END_DATE};
  } elsif (length($data{ESTIMATED_END_DT})) {
    $end=$data{ESTIMATED_END_DT} . " (estimated)";
  }
  print "Serial Number: ", $data{'SERIAL_ID'}, "\n";
  print "Product Type : ", $data{'PROD_DESCR'}, "\n";
  print "Purchase Date: ", $start, "\n";
  print "Covered Thru : ", $end, "\n";
  print "Coverage Type: ", $data{'HW_COVERAGE_DESC'}, "\n";
  print "Can buy APP? : ", $data{'IS_APP_ELIGIBLE'}, "\n";
  #print "Description  : ", $data{'HARDWAREMESSAGES'}, "\n";
  print "Description  : ", $data{'wc.desktopcomputers.hardware.pp.long'}, $data{'wc.portablecomputers.hardware.pp.long'}, "\n";
} else {
  if (length($data{'COV_END_DATE'})) {
    print $data{'COV_END_DATE'}, "\n";
  } else {
    print "Passed\n";
  }
}
if (length($field)) {
  if (length($data{'COVERAGE_DATE'})) {
    system("defaults write '$plist' $field $data{'COVERAGE_DATE'}");
  } else {
    system("defaults write '$plist' $field 'Passed'");
  }
  print STDERR "Note: changes may not show up in Remote Desktop until ARD agent is restarted\n";
}

#
# END OF MAIN
#

sub usage {
  print STDERR <<EEE;
usage: $0 [options] [serial_number]
  if no serial number is provided it uses the number from the current host
  Options:
    -v         - verbose
    -d         - debug (show web XML data)
    -f field   - put expiration date into Remote Desktop plist field
		 (usually Text1 through Text4)
    -p plist   - used with -f, use a different plist from the default
		 ($plist)
		 must be full path, without the .plist suffix

EEE
}

#
# load_sysprof_xml together with
# makeobj, dict, and array are designed to recursively build a structure
# of references.  an array becomes an array of objects, some of which
# are literals (like a string) and some of which are references to other
# arrays or dicts (hashes).
#
# The structure of the system_profiler data is basically
#   An array, containing one dictionary for each data type
#     Each dictionary contains some standard keys including:
#       dataType, which is a literaly string, e.g. SPHardwareDataType
#       _items, which is an array of the related objects
#         The first item in _items is dict with the stuff we really want
#           for example, serial_number
#
#  So what we end up with will be
#  $top is an arrayref
#    ${$arrayref}[0] is a dictref
#      ${...parentref...}{_dataType} is a string, e.g. SPHardwareDataType
#      ${...parentref...}{_items} is an arrayref
#        ${...parentref...}[0] is the dict of stuff we want
#          ${...parentref...}{'serial_number'} is a string
#
#

sub load_sysprof_xml {
  my $SPtype;
  while ($#_ >= 0) {
    $SPtype .= shift(@_) . " ";
  }

  my $ret;
  open(SP,"system_profiler $SPtype -xml|");
  while(<SP>) {
    if (/^\s*<\?xml /) { next; }
    if (/^\s*<!DOCTYPE /) { next; }
    if (/^\s*<plist /) { next; }
    if (/^\s*<\/plist>/) { next; }
    $ret=&makeobj($_);
    #print "ret=$ret  -- $_\n";
  }
  close(SP);
  return $ret;
}

sub makeobj {
  local ($_)=shift(@_);
  if (/\s*<array>/) {
    return(&array);
  } elsif (/\s*<dict>/) {
    return(&dict);
  } elsif (/\s*<array\/>/) {
    my @foo=();
    return(\@foo);
  } elsif (/\s*<true\/>/) {
    my $foo="1true";
    #actual string, not a reference
    return($foo);
  } elsif (/\s*<false\/>/) {
    my $foo="0false";
    #actual string, not a reference
    return($foo);
  } elsif (/\s*<string>/) {
    chop;
    s/^\s*<string>//;
    s/<\/string>.*$//;
    my $foo=$_;
    #actual string, not a reference
    return($foo);
  } elsif (/\s*<integer>/) {
    chop;
    s/^\s*<integer>//;
    s/<\/integer>.*$//;
    my $foo=$_;
    #actual string, not a reference
    return($foo);
  } elsif (/\s*<date>/) {
    chop;
    s/^\s*<date>//;
    s/<\/date>.*$//;
    my $foo=$_;
    #actual string, not a reference
    return($foo);
  } else {
    print "Gosh Darnit, what is this? $_\n";
  }
}

sub array {
  my @ret=();
  my $foo;
  #$arrayref=[];
  while($foo=<SP>) {
    if ($foo =~ /^\s*<\/array>/) { last; }
    push(@ret,&makeobj($foo));
    #push(@{$arrayref},&makeobj($foo));
  }
  return(\@ret);
  #return($arrayref);
}

sub dict {
  my %ret=();
  my $foo;
  my $key;
  while($foo=<SP>) {
    if ($foo =~ /^\s*<key>/) {
      $key=$foo;
      chop($key);
      $key=~ s/^\s*<key>//;
      $key=~ s/<\/key>.*$//;
      $foo=<SP>;
      $ret{$key}=&makeobj($foo);
    } elsif ($foo =~ /^\s*<\/dict>/) {
      last;
    } else {
      print "Gosh darnit! $foo\n";
    }
  }
  #print "  returning ", join(" ",keys(%ret)), "\n";
  return(\%ret);
}