/usr/local/CPAN/TaskForest/TaskForest/REST.pm


package TaskForest::REST;

use strict;
use warnings;
use HTTP::Status;
use Data::Dumper;

BEGIN {
    use vars qw($VERSION);
    $VERSION     = '1.30';
}

sub methodNotAllowed {
    my ($hash, $allow) = @_;

    $hash->{response_headers}->header("Allow",  $allow);
    $hash->{http_status} = RC_METHOD_NOT_ALLOWED;
    $hash->{http_content} = '';

}

# 'correct' GET functionality.
# needs a reference to a check_existence and get_modified_tags() and a get_content() function
sub GET {
    my ($q, $parent_hash, $h, $hash, $resource_functions) = @_;

    my $exists                 = $resource_functions->{check_existence}->($q, $parent_hash, $h, $hash);

    if (!$exists) {
        $parent_hash->{http_status} = RC_NOT_FOUND;
        $parent_hash->{content} = "404 - Not Found";
        return;
    }
    
    my ($last_modified, $etag) = $resource_functions->{get_modified_tags}->($q, $parent_hash, $h, $hash);
    my $if_modified_since      = $parent_hash->{request_headers}->if_modified_since;
    my $if_none_match          = $parent_hash->{request_headers}->header('if-none-match');
    
    if (
        ($if_modified_since && $last_modified <= $if_modified_since)
        ||
        ($if_none_match     && $etag eq $if_none_match)
        ) {
        # don't need to send anything back;
        $parent_hash->{http_content} = "";
        $parent_hash->{http_status} = RC_NOT_MODIFIED;
        $parent_hash->{response_headers}->last_modified($last_modified);
        $parent_hash->{response_headers}->header('ETag', $etag);
        $parent_hash->{response_headers}->header('Cache-Control', 'Public');
        return;
    }

    # get the contents and populate the hash as needed
    $resource_functions->{get_content}->($q, $parent_hash, $h, $hash);
    $parent_hash->{http_status} = RC_OK;
    $parent_hash->{response_headers}->last_modified($last_modified);
    $parent_hash->{response_headers}->header('ETag', $etag);
    $parent_hash->{response_headers}->header('Cache-Control', 'Public');
}


# 'correct' HEAD functionality.
# needs a reference to a check_existence and get_modified_tags() function
sub HEAD {
    my ($q, $parent_hash, $h, $hash, $resource_functions) = @_;

    my $exists                 = $resource_functions->{check_existence}->($q, $parent_hash, $h, $hash);

    if (!$exists) {
        $parent_hash->{http_status} = RC_NOT_FOUND;
        $parent_hash->{content} = "404 - Not Found";
        return;
    }
    
    my ($last_modified, $etag) = $resource_functions->{get_modified_tags}->($q, $parent_hash, $h, $hash);
    my $if_modified_since      = $parent_hash->{request_headers}->if_modified_since;
    my $if_none_match          = $parent_hash->{request_headers}->header('if-none-match');
    
    if (
        ($if_modified_since && $last_modified <= $if_modified_since)
        ||
        ($if_none_match     && $etag eq $if_none_match)
        ) {
        # don't need to send anything back;
        $parent_hash->{http_content} = "";
        $parent_hash->{http_status} = RC_NOT_MODIFIED;
        $parent_hash->{response_headers}->last_modified($last_modified);
        $parent_hash->{response_headers}->header('ETag', $etag);
        return;
    }

    # get the contents and populate the hash as needed
    $parent_hash->{http_content} = '';
    $parent_hash->{http_status} = RC_OK;
    $parent_hash->{response_headers}->last_modified($last_modified);
    $parent_hash->{response_headers}->header('ETag', $etag);
}



# 'correct' PUT functionality.
# needs a the following funcitons :
#  get content
#  put content
#  get_modified_tags
sub PUT {
    my ($q, $parent_hash, $h, $hash, $resource_functions) = @_;


    my $status = $resource_functions->{put_content}->($q, $parent_hash, $h, $hash);
    $parent_hash->{http_status} = $status;  # not 201 CREATED because we want it to look just like an update
 
    return unless $status == RC_OK;
    
    my ($last_modified, $etag) = $resource_functions->{get_modified_tags}->($q, $parent_hash, $h, $hash);

    # get the contents and populate the hash as needed
    $resource_functions->{get_content}->($q, $parent_hash, $h, $hash);
    $parent_hash->{response_headers}->last_modified($last_modified);
    $parent_hash->{response_headers}->header('ETag', $etag);
}

# 'correct' DELETE functionality.
# needs a the following funcitons :
#  delete_resource
sub DELETE {
    my ($q, $parent_hash, $h, $hash, $resource_functions) = @_;


    $hash->{http_status} = $resource_functions->{delete_resource}->($q, $parent_hash, $h, $hash);
    $hash->{http_content} = '';
}


sub deleteFile {
    my ($q, $parent_hash, $h, $hash) = @_;
    my $file_name = $hash->{_resource_file_name};

    unlink $file_name;

    
    if (-e $file_name) {
        $parent_hash->{http_content} = "500 - Internal Server Error";
        return RC_INTERNAL_SERVER_ERROR;
    }
    return RC_NO_CONTENT;
}



sub checkExistenceForFile {
    my ($q, $parent_hash, $h, $hash) = @_;
    my $file_name = $hash->{_resource_file_name};

    return -e $file_name;
}



sub getModifiedTagsForFile {
    my ($q, $parent_hash, $h, $hash) = @_;
    my $file_name = $hash->{_resource_file_name};

    my @stat = stat($file_name);
    my $last_modified = $stat[9];
    my $etag = $stat[7].$stat[9];

    return ($last_modified, $etag);
}


sub getContentForTextFile {
    my ($q, $parent_hash, $h, $hash) = @_;
    my $file_name = $hash->{_resource_file_name};

    if (open (F, "$file_name")) {
        my @lines = <F>;
        close F;

        $hash->{file_contents}  = join("", map { s/\r//; $_; } @lines);
        $hash->{full_file_name} = $file_name;
    }
}    


sub putContentForTextFile {
    my ($q, $parent_hash, $h, $hash) = @_;
    my $file_name = $hash->{_resource_file_name};

    if (!defined($h->{file_contents})) {
        return RC_BAD_REQUEST;
    }

    if (open (F, ">$file_name")) {
        $h->{file_contents} =~ s/\r//g;
        print F $h->{file_contents};
        close F;
        return RC_OK;
    }
    else {
        $parent_hash->{http_content} = "500 - Internal Server Error";
        return RC_INTERNAL_SERVER_ERROR;
    }
}    

sub getDirListing {
    my ($q, $parent_hash, $h, $hash) = @_;
    my $dir = $hash->{_resource_file_name};

    if (opendir(DIR, $dir)) {
        my @files = grep { -f "$dir/$_" } readdir(DIR);
        closedir DIR;
        my $n0 = 0;
        foreach my $file (@files) {
            push (@{$hash->{files}}, { file_name => $file,
                                       n0        => $n0,
                                       n1        => $n0 + 1,
                                       oddeven   => !($n0 % 2),
                  });
        }
        $hash->{no_files} = (@{$hash->{files}})? 1 : 0;
    }
}
    


                

1;