File Coverage

blib/lib/XML/FOAF.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package XML::FOAF;
2 5     5   2602 use strict;
  5         9  
  5         198  
3 5     5   134 use 5.008_001;
  5         17  
  5         343  
4              
5 5     5   33 use base qw( XML::FOAF::ErrorHandler );
  5         7  
  5         17147  
6              
7 5     5   6473 use LWP::UserAgent;
  5         421278  
  5         180  
8 5     5   4140 use XML::FOAF::Person;
  0            
  0            
9             use RDF::Core::Model;
10             use RDF::Core::Storage::Memory;
11             use RDF::Core::Model::Parser;
12             use RDF::Core::Resource;
13              
14             our $VERSION = '0.04';
15             our $NAMESPACE = 'http://xmlns.com/foaf/0.1/';
16              
17             sub new {
18             my $class = shift;
19             my $foaf = bless { }, $class;
20             my($stream, $base_uri) = @_;
21             my $store = RDF::Core::Storage::Memory->new;
22             $foaf->{model} = RDF::Core::Model->new(Storage => $store);
23             $foaf->{ua} = LWP::UserAgent->new;
24             my %pair;
25             if (UNIVERSAL::isa($stream, 'URI')) {
26             ($stream, my($data)) = $foaf->find_foaf($stream);
27             return $class->error("Can't find FOAF file") unless $stream;
28             $foaf->{foaf_url} = $stream->as_string;
29             $foaf->{raw_data} = \$data;
30             %pair = ( Source => $data, SourceType => 'string' );
31             unless ($base_uri) {
32             my $uri = $stream->clone;
33             my @segs = $uri->path_segments;
34             $uri->path_segments(@segs[0..$#segs-1]);
35             $base_uri = $uri->as_string;
36             }
37             } elsif (ref($stream) eq 'SCALAR') {
38             $foaf->{raw_data} = $stream;
39             %pair = ( Source => $$stream, SourceType => 'string' );
40             } elsif (ref $stream) {
41             ## In case we need to verify this data later, we need to read
42             ## it in now. This isn't great for memory usage, though.
43             my $data;
44             while (read $stream, my($chunk), 8192) {
45             $data .= $chunk;
46             }
47             $foaf->{raw_data} = \$data;
48             %pair = ( Source => $data, SourceType => 'string' );
49             } else {
50             $foaf->{raw_data} = $stream;
51             %pair = ( Source => $stream, SourceType => 'file' );
52             }
53             ## Turn off expanding external entities in XML::Parser to avoid
54             ## security risk reading local file due to usage of XML::Parser
55             ## in RDF::Core::Parser.
56             local $XML::Parser::Expat::Handler_Setters{ExternEnt} = sub {};
57             local $XML::Parser::Expat::Handler_Setters{ExternEntFin} = sub {};
58              
59             my $parser = RDF::Core::Model::Parser->new(
60             Model => $foaf->{model},
61             BaseURI => $base_uri,
62             %pair);
63             eval {
64             ## Turn off warnings, because RDF::Core::Parser gives a bunch of
65             ## annoying warnings about $ce->{parsetype} being undefined at
66             ## line 636.
67             local $^W = 0;
68             $parser->parse;
69             };
70             if ($@) {
71             return $class->error($@);
72             }
73             $foaf;
74             }
75              
76             sub foaf_url { $_[0]->{foaf_url} }
77              
78             sub find_foaf {
79             my $foaf = shift;
80             my($url) = @_;
81             my $ua = $foaf->{ua};
82             my $req = HTTP::Request->new(GET => $url);
83             my $res = $ua->request($req);
84             if ($res->content_type eq 'text/html') {
85             require HTML::Parser;
86             my $p = HTML::Parser->new(
87             api_version => 3,
88             start_h => [ \&_find_links, "self,tagname,attr" ]);
89             $p->{base_uri} = $url;
90             $p->parse($res->content);
91             if ($p->{foaf_url}) {
92             $req = HTTP::Request->new(GET => $p->{foaf_url});
93             $res = $ua->request($req);
94             return($p->{foaf_url}, $res->content)
95             if $res->is_success;
96             }
97             } else {
98             return($url, $res->content);
99             }
100             }
101              
102             sub find_foaf_in_html {
103             my $class = shift;
104             my($html, $base_uri) = @_;
105             require HTML::Parser;
106             my $p = HTML::Parser->new(
107             api_version => 3,
108             start_h => [ \&_find_links, "self,tagname,attr" ]
109             );
110             $p->{base_uri} = $base_uri;
111             $p->parse($$html);
112             $p->{foaf_url};
113             }
114              
115             sub _find_links {
116             my($p, $tag, $attr) = @_;
117             $p->{foaf_url} = URI->new_abs($attr->{href}, $p->{base_uri})
118             if $tag eq 'link' &&
119             $attr->{rel} eq 'meta' &&
120             $attr->{type} eq 'application/rdf+xml' &&
121             $attr->{title} eq 'FOAF';
122             }
123              
124             sub person {
125             my $foaf = shift;
126             my $type = RDF::Core::Resource->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
127             my $enum;
128             ## Look for case-insensitive "Person" or "person".
129             for my $e (qw( Person person )) {
130             $enum = $foaf->{model}->getStmts(undef, $type,
131             RDF::Core::Resource->new($NAMESPACE . $e)
132             );
133             last if $enum && $enum->getFirst;
134             }
135             return unless $enum && $enum->getFirst;
136             XML::FOAF::Person->new($foaf, $enum->getFirst->getSubject);
137             }
138              
139             sub assurance {
140             my $foaf = shift;
141             my $res = RDF::Core::Resource->new('http://xmlns.com/wot/0.1/assurance');
142             my $enum = $foaf->{model}->getStmts(undef, $res);
143             my $stmt = $enum->getFirst or return;
144             $stmt->getObject->getLabel;
145             }
146              
147             sub verify {
148             my $foaf = shift;
149             my $sig_url = $foaf->assurance or return;
150             require LWP::Simple;
151             my $sig = LWP::Simple::get($sig_url);
152             require Crypt::OpenPGP;
153             my $pgp = Crypt::OpenPGP->new( AutoKeyRetrieve => 1,
154             KeyServer => 'pgp.mit.edu' );
155             my %arg = ( Signature => $sig );
156             my $raw = $foaf->{raw_data};
157             if (ref($raw)) {
158             $arg{Data} = $$raw;
159             } else {
160             $arg{Files} = $raw;
161             }
162             my $valid = $pgp->verify(%arg) or return 0;
163             $valid;
164             }
165              
166             1;
167             __END__