#!/usr/bin/perl
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL NETMESH INC. BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The use of this software is governed by a license agreement that
# is contained in the package that you downloaded.

package main;

use strict;
use CGI;
use XML::Xerces;
use XML::XPath;
use LWP;

my $CAPABILITIES_MIME = 'application/xrds+xml';
my $HTML_MIME         = 'text/html';
my $YADISYADIS_HTTP_HEADER = 'X-XRDS-Location';

my $q = new CGI();
my $url = $q->param( 'url' );

my $content = <<HTML;
<html>
 <head>
  <title>NetMesh script: Given a URL, determine its Yadis capabilities</title>
  <link rel="stylesheet" href="http://lid.netmesh.org/css/lid.css" type="text/css">
  <link rel="stylesheet" href="/css/netmesh.css" type="text/css">
  <style type="text/css">
div.result {  border-width: 1px; border-style: solid; border-color: #808080; background: #e8e8e8; padding: 10px; }
p.result-error { color: #ff4040; }
p.result-error:before { content: "Error: "; font-weight: bold; }
p.result-warn { color: black; }
p.result-warn:before { content: "Warning: "; font-weight: bold; color: #a0a000; }
table.capabilities { width: 100%; border-width: 1px; border-color: gray; border-style: solid; }
table.capabilities thead td { text-align: center; font-weight: bold; }
  </style>
 </head>
 <body>
  <h1>NetMesh script: Given a URL, determine its Yadis capabilities</h1>
HTML
if( $url ) {
$content .= <<HTML;
  <h2>Capability test result for URL: <a href="$url">$url</a></h2>
  <div class="result">
HTML
$content .= determineResult( $url );
$content .= <<HTML;
  </div>
HTML
}

$content .= <<HTML;
  <h2>New test:</h1>
  <form method="GET" action="">
   <p>Enter the URL whose Yadis capabilities you would like to determine:</p>
   <input type="text" size="64" maxLength="256" name="url" value="$url">
   <input type="submit" value="Check!" name="submit">
  </form>
  <p>Go <a href="../">back</a></p>
 </body>
</html>
HTML
if( $ENV{'HTTP_HOST'} ) {
    return( $content, 'text/html');
} else {
    print $content;
    exit;
}

#####

sub determineResult {
    my $url = shift;
    my $ret;

    ## First try a direct query with $CAPABILITIES_MIME only

    $ret .= "<h3>Direct query, asking for MIME type $CAPABILITIES_MIME</h3>\n";
    $ret .= "<p>Querying URL <a href=\"$url\">$url</a>.</p>\n";

    my $ua      = LWP::UserAgent->new( parse_head => 0 );
    my $headers = HTTP::Headers->new;
    $headers->header( 'Accept', $CAPABILITIES_MIME );
    my $request  = HTTP::Request->new( 'GET', $url, $headers );

    my $response = $ua->simple_request( $request );

    my $capabilities = undef;
    my $htmlMetaUrl;
    if( $response->code eq HTTP::Status::RC_OK ) {
        if( $response->content_type eq $CAPABILITIES_MIME ) {
            $capabilities = parseCapabilities( $response->content );
            if( $capabilities ) {
                $ret .= dumpCapabilities( $capabilities );
            } else {
                $ret .= "<p class=\"result-error\">No capabilities found in response of MIME type $CAPABILITIES_MIME.</p>\n";
            }
        } else {
            $ret .= "<p class=\"result-warn\">Response of MIME type '" . $response->content_type
                 . "' ignored the MIME type that was requested.</p>\n";
            if( $response->content_type eq $HTML_MIME ) {
                $htmlMetaUrl = findHtmlMetaUrl( $response->content );
                if( $htmlMetaUrl ) {
                    $ret .= "<p>Found META http-equiv header '$YADIS_HTTP_HEADER' in response.</p>\n";
                }
            }
        }
    } else {
        $ret .= "<p class=\"ok\">Empty response (HTTP code " . $response->code . ")</p>\n";
    }
    my $yadisMetaUrl = $response->header( $YADIS_HTTP_HEADER );
    if( $yadisMetaUrl ) {
        if( $yadisMetaUrl =~ m!^(https?|ftp)://...! ) {
            $ret .= "<p>Returning $YADIS_HTTP_HEADER pointing to URL <a href=\"$yadisMetaUrl\">$yadisMetaUrl</a>.</p>\n";
        } else {
            $ret .= "<p class=\"result-error\">Is returning $YADIS_HTTP_HEADER with syntactically invalid URL $yadisMetaUrl.</p>\n";
            $yadisMetaUrl = undef;
        }

    } else {
        $ret .= "<p>No HTTP header '$YADIS_HTTP_HEADER' in response.</p>\n";
    }
    $ret .= "<p>No META http-equiv header '$YADIS_HTTP_HEADER' in response.</p>\n" unless( $htmlMetaUrl );

    ## Now look at any returned $YADIS_HTTP_HEADER, if any
    if( $yadisMetaUrl ) {
        $ret .= "<h3>Query to $YADIS_HTTP_HEADER returned from HTTP</h3>\n";
        $ret .= "<p>Querying URL <a href=\"$yadisMetaUrl\">$yadisMetaUrl</a>.</p>\n";

        $ua      = LWP::UserAgent->new( parse_head => 0 );
        $request  = HTTP::Request->new( 'GET', $yadisMetaUrl );

        $response = $ua->simple_request( $request );
        if( $response->is_success ) {
            $capabilities = parseCapabilities( $response->content );
            if( $capabilities ) {
                $ret .= dumpCapabilities( $capabilities );
            } else {
                $ret .= "<p class=\"result-error\">No capabilities found in response from URL"
                     . " <a href=\"$yadisMetaUrl\">$yadisMetaUrl</a>.</p>\n";
            }
        } else {
            $ret .= "<p class=\"result-error\">Request to URL"
                 . " <a href=\"$yadisMetaUrl\">$yadisMetaUrl</a>"
                 . " failed with HTTP response code " . $response->code . "</p>\n";
        }
    }

    ## Now look at any meta-equiv in return HTML, if any
    if( $htmlMetaUrl ) {
        $ret .= "<h3>Query to $YADIS_HTTP_HEADER found as HTTP-EQUIV in HTML</h3>\n";
        $ret .= "<p>Querying URL <a href=\"$htmlMetaUrl\">$htmlMetaUrl</a>.</p>\n";

        $ua      = LWP::UserAgent->new( parse_head => 0 );
        $request  = HTTP::Request->new( 'GET', $htmlMetaUrl );

        $response = $ua->simple_request( $request );
        if( $response->is_success ) {
            $capabilities = parseCapabilities( $response->content );
            if( $capabilities ) {
                $ret .= dumpCapabilities( $capabilities );
            } else {
                $ret .= "<p class=\"result-error\">No capabilities found in response from URL"
                     . " <a href=\"$htmlMetaUrl\">$htmlMetaUrl</a>.</p>\n";
            }
        } else {
            $ret .= "<p class=\"result-error\">Request to URL"
                 . " <a href=\"$htmlMetaUrl\">$htmlMetaUrl</a>"
                 . " failed with HTTP response code " . $response->code . "</p>\n";
        }
    }
    $ret .= "<h3>Result</h3>\n";
    if( $capabilities ) {
        $ret .= "<p>Capabilities found, this is a Yadis URL.</p>\n";
    } else {
        $ret .= "<p class=\"result-error\">No capabilities found for URL"
             . " <a href=\"$url\">$url</a>. This is not a Yadis URL.</p>\n";
    }

    return $ret;
}

sub findHtmlMetaUrl {
    my $html = shift;

    require HTML::HeadParser;
    my $p = HTML::HeadParser->new;
    $p->parse( $html );

    return $p->header( $YADIS_HTTP_HEADER );
}

sub parseCapabilities {
    my $content = shift;

    my $error = 1;
    my @capNodes;
    eval {
        my $lidXp = XML::XPath->new(xml => $content);
        @capNodes = $lidXp->find( '/XRDS/xrd:XRD/xrd:Service' )->get_nodelist;

        $error = 0;
    };
    if( $error ) {
        return undef;
    } else {
        return \@capNodes;
    }
}

sub dumpCapabilities {
    my $cap = shift;

    my $ret = <<HTML;
<table class="capabilities">
 <thead>
  <tr>
   <td>Type</td>
  </tr>
 </thead>
HTML
    foreach my $capNode( @$cap ) {
        $ret .= " <tr>\n";
        foreach my $childNode ( $capNode->getChildNodes() ) {
            if( $childNode->getName eq 'xrd:Type' ) {
                $ret .= "  <td>";
                my $type;
                foreach my $grandChildNode( $childNode->getChildNodes() ) {
                    $type .= $grandChildNode->getData();
                }
                $ret .= "<a href=\"$type\">$type</a>\n";
                $ret .= "</td>\n";
            }
        }
        $ret .= " </tr>\n";
    }
    $ret .= "</table>\n";
    return $ret;
}


