Atompub-0.3.7/000755 000765 000024 00000000000 11737440572 013424 5ustar00inouestaff000000 000000 Atompub-0.3.7/Changes000644 000765 000024 00000010330 11737440114 014704 0ustar00inouestaff000000 000000 2012-04-06 Takeru INOUE * release 0.3.7: * Makefile.PL - remove auto_include(_deps) 2012-04-05 Takeru INOUE * release 0.3.6: * Makefile.PL - use auto_include_deps instead of auto_include 2012-04-04 Takeru INOUE * release 0.3.5: * Makefile.PL, lib/Atompub/Client.pm, lib/Atompub/Server.pm - use Digest::SHA instead of Digest::SHA1. thanks Salvatore 2010-10-12 Takeru INOUE * release 0.3.4: * lib/Atompub/Server.pm - fix a typo. thanks Naveed. 2010-05-17 Takeru INOUE * release 0.3.3: * lib/Atompub/Client.pm - modify argument handling in the constructor. thanks Yury. - fix a bug on Slug encoding scheme. thanks Lars. * t/perlcritic.t - check $ENV{TEST_CRITIC} whether to execute this test. thanks Serguei. 2008-10-19 Takeru INOUE * release 0.3.2: * t/02.datetime.t - skip this test if the environmental variable ATOMPUB_TEST_LIVE is not set 2008-05-22 Takeru INOUE * release 0.3.1: * Makefile.PL - use Module::Install * lib/Atompub/Client.pm - fix slug encoding scheme. Thanks to Vincent 2008-03-13 Takeru INOUE * release 0.3.0: * lib/Atompub/Client.pm - use Class::Accessor::Fast instead of Class::Accessor::Lvalue::Fast, since lvalue module doesn't work with the debugger. Thanks to Loach * all perl codes - change coding conventions 2008-01-13 Takeru INOUE * release 0.2.4: * lib/Atompub/Server.pm - fix POD. Thanks to Thorsten * lib/Atompub/DateTime.pm, t/02.datetime.t - catch exceptions in creating timezone object * t/10.client.t - skip tests unless $ATOMPUB_TEST_LIVE is not defined 2007-11-17 Takeru INOUE * release 0.2.3: * Atompub.pm - use 5.006 * 10.client.t - use HTTP proxy if $HTTP_PROXY/$http_proxy is defined 2007-10-07 Takeru INOUE * release 0.2.2: * Build.PL - add Module::Build::Compat to requires 2007-10-06 Takeru INOUE * release 0.2.1: * Build.PL - add Time::Local to requires 2007-10-01 Takeru INOUE * release 0.2.0: * Atompub/Client.pm - change hash to hash reference in some internal interfaces * t/02.datetime.t - fix timezone bugs 2007-09-29 Takeru INOUE * release 0.1.9: * Atompub/DateTime.pm - replace Atompub::Datetime::TimeZone by DateTime::TimeZone - use DateTime::Format::W3CDTF and Class::Data::Inheritable 2007-09-28 Takeru INOUE * release 0.1.8: * Atompub/DateTime.pm - add init 2007-09-27 Takeru INOUE * release 0.1.7: * Atompub/Client.pm - fix POD coverage bugs 2007-09-27 Takeru INOUE * release 0.1.6: * Atompub/Util.pm - fix a bug in is_allowed_category, which reproduced if no app:categories 2007-09-17 Takeru INOUE * release 0.1.5: * Build.PL - fix dependency 2007-09-17 Takeru INOUE * release 0.1.4: * Build.PL - add Class::Accessor::Lvalue::Fast to requires 2007-09-16 Takeru INOUE * release 0.1.3: * Makefile.PL - build_class => Module::Build 2007-09-14 Takeru INOUE * release 0.1.2: * Atompub/Client.pm - don't use If-Match and/or If-Unmodified-Since headers on DELETE 2007-09-12 Takeru INOUE * release 0.1.1: * Atompub/DateTime.pm - rewrite POD 2007-09-10 Takeru INOUE * release 0.1.0: * Atompub.pm - add some methods to HTTP::Headers, HTTP::Request, and HTTP::Response * Atompub/Client.pm - merge operations for entry and media resources internally - support cache mechanizem using Last-Modified header - refactoring drastically * Atompub/DateTime.pm, Atompub/MediaType.pm, Atompub/Util.pm - newly added 2007-08-13 Takeru INOUE * release 0.0.2: * Build.PL - add Test::Perl::Critic to requires 2007-08-12 Takeru INOUE * release 0.0.1: created by Module::Starter Atompub-0.3.7/inc/000755 000765 000024 00000000000 11737440572 014175 5ustar00inouestaff000000 000000 Atompub-0.3.7/lib/000755 000765 000024 00000000000 11737440572 014172 5ustar00inouestaff000000 000000 Atompub-0.3.7/Makefile.PL000644 000765 000024 00000001512 11737437456 015404 0ustar00inouestaff000000 000000 use strict; use warnings; use inc::Module::Install; name('Atompub'); all_from('lib/Atompub.pm'); license('perl'); requires('Class::Accessor::Fast'); requires('Class::Data::Inheritable'); requires('DateTime'); requires('DateTime::Format::W3CDTF'); requires('DateTime::TimeZone'); requires('Digest::SHA'); requires('File::Slurp'); requires('HTTP::Date'); requires('HTTP::Headers'); requires('HTTP::Request'); requires('HTTP::Response'); requires('HTTP::Status'); requires('MIME::Base64'); requires('MIME::Types'); requires('Module::Build::Compat'); requires('Perl6::Export::Attrs'); requires('Test::Perl::Critic'); requires('Time::Local'); requires('URI::Escape'); requires('XML::Atom::Service', 0.016); requires('version', 0.74); build_requires('Test::More'); build_requires('Test::Perl::Critic'); #auto_include; auto_install; WriteAll; Atompub-0.3.7/MANIFEST000644 000765 000024 00000001370 11737440527 014556 0ustar00inouestaff000000 000000 Changes inc/Module/AutoInstall.pm inc/Module/Install.pm inc/Module/Install/AutoInstall.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Include.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm lib/Atompub.pm lib/Atompub/Client.pm lib/Atompub/DateTime.pm lib/Atompub/MediaType.pm lib/Atompub/Server.pm lib/Atompub/Util.pm Makefile.PL MANIFEST This list of files META.yml README t/.htaccess t/00.load.t t/01.atompub.t t/02.datetime.t t/03.media_type.t t/04.util.t t/05.info.t t/06.cache.t t/10.client.t t/20.server.t t/lib/My/Server.pm t/perlcritic.t t/pod-coverage.t t/pod.t t/samples/media1.gif t/samples/media2.gif t/server.cgi Atompub-0.3.7/META.yml000644 000765 000024 00000001757 11737440442 014703 0ustar00inouestaff000000 000000 --- abstract: 'Atom Publishing Protocol implementation' author: - 'Takeru INOUE, ' build_requires: ExtUtils::MakeMaker: 6.42 Test::More: 0 Test::Perl::Critic: 0 configure_requires: ExtUtils::MakeMaker: 6.42 distribution_type: module generated_by: 'Module::Install version 1.01' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Atompub no_index: directory: - inc - t requires: Class::Accessor::Fast: 0 Class::Data::Inheritable: 0 DateTime: 0 DateTime::Format::W3CDTF: 0 DateTime::TimeZone: 0 Digest::SHA: 0 File::Slurp: 0 HTTP::Date: 0 HTTP::Headers: 0 HTTP::Request: 0 HTTP::Response: 0 HTTP::Status: 0 MIME::Base64: 0 MIME::Types: 0 Module::Build::Compat: 0 Perl6::Export::Attrs: 0 Test::Perl::Critic: 0 Time::Local: 0 URI::Escape: 0 XML::Atom::Service: 0.016 perl: 5.6.0 version: 0.74 resources: license: http://dev.perl.org/licenses/ version: 0.003007 Atompub-0.3.7/README000644 000765 000024 00000002153 11557026706 014304 0ustar00inouestaff000000 000000 Atompub version 0.0.1 [ REPLACE THIS... The README is used to introduce the module and provide instructions on how to install the module, any machine dependencies it may have (for example C compilers and installed libraries) and any other information that should be understood before the module is installed. A README file is required for CPAN modules since CPAN extracts the README file from a module distribution so that people browsing the archive can use it get an idea of the modules uses. It is usually a good idea to provide version information here so that people can decide whether fixes for the module are worth downloading. ] INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install Alternatively, to install with Module::Build, you can use the following commands: perl Build.PL ./Build ./Build test ./Build install DEPENDENCIES None. COPYRIGHT AND LICENCE Copyright (C) 2007, Takeru INOUE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Atompub-0.3.7/t/000755 000765 000024 00000000000 11737440572 013667 5ustar00inouestaff000000 000000 Atompub-0.3.7/t/.htaccess000644 000765 000024 00000000107 11557026706 015462 0ustar00inouestaff000000 000000 AuthType None Options +ExecCGI AddType cgi-script cgi Atompub-0.3.7/t/00.load.t000644 000765 000024 00000000151 11557026706 015205 0ustar00inouestaff000000 000000 use Test::More tests => 1; BEGIN { use_ok( 'Atompub' ); } diag( "Testing Atompub $Atompub::VERSION" ); Atompub-0.3.7/t/01.atompub.t000644 000765 000024 00000000367 11557026706 015747 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More tests => 3; use Atompub; ok(Atompub->VERSION); is $XML::Atom::DefaultVersion, '1.0'; is $XML::Atom::Service::DefaultNamespace, 'http://www.w3.org/2007/app'; Atompub-0.3.7/t/02.datetime.t000644 000765 000024 00000002416 11557026706 016072 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More; plan skip_all => 'set ATOMPUB_TEST_LIVE to enable this test' unless $ENV{ATOMPUB_TEST_LIVE}; plan tests => 11; # current time is "Mon Jan 01 10:00:00 2007" in your timezone BEGIN { use HTTP::Date qw(str2time); *CORE::GLOBAL::time = sub { str2time '2007-01-01 10:00:00' }; } use Atompub::DateTime qw(datetime); use DateTime; use Time::Local; sub diff { my $dt = DateTime->from_epoch(epoch => time); # in UTC my $tz = Atompub::DateTime::tz(); # in local time $tz->offset_for_datetime($dt); # diff in sec. } sub tz { my $diff = diff(); my $tz = sprintf "%+03d:%02d", int( $diff / 3600 ), int( ( $diff % 3600 ) / 60 ); $tz eq '+00:00' ? 'Z' : $tz; } my $dt = datetime; is $dt->epoch, 1167645600 - diff(); is $dt->iso, '2007-01-01 10:00:00'; is $dt->w3c, '2007-01-01T10:00:00' . tz(); like $dt->isoz, qr{^20\d\d-\d\d-\d\d \d\d:\d\d:\d\dZ$}; like $dt->w3cz, qr{^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ$}; like $dt->str, qr{^[a-z]{3},\s+\d{1,2}\s+[a-z]{3}\s+20\d\d\s+\d\d:\d\d:\d\d\s+GMT$}i; is "$dt", $dt->w3c; is 0+$dt, $dt->epoch; my $dt2 = datetime($dt); ok $dt = $dt2; $dt2 = datetime($dt->epoch + 1); ok $dt < $dt2; ok $dt != $dt2; Atompub-0.3.7/t/03.media_type.t000644 000765 000024 00000002634 11557026706 016421 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More tests => 37; use Atompub::MediaType qw(media_type); my $png = media_type('image/png'); isa_ok $png, 'Atompub::MediaType'; is $png->type, 'image'; is $png->subtype, 'png'; is $png->parameters, undef; is $png->subtype_major, 'png'; is $png->without_parameters, 'image/png'; is $png->as_string, 'image/png'; is $png->extensions, 'png'; is $png->extension, 'png'; ok $png->is_a('*/*'); ok $png->is_a('image/*'); ok $png->is_a('image/png'); ok $png->is_not_a('text/*'); ok $png->is_not_a('image/jpeg'); is "$png", 'image/png'; ok $png eq '*/*'; ok $png ne 'text/*'; my $atom = media_type('entry'); isa_ok $atom, 'Atompub::MediaType'; is $atom->type, 'application'; is $atom->subtype, 'atom+xml'; is $atom->parameters, 'type=entry'; is $atom->subtype_major, 'xml'; is $atom->without_parameters, 'application/atom+xml'; is $atom->as_string, 'application/atom+xml;type=entry'; is $atom->extensions, 'atom'; is $atom->extension, 'atom'; ok $atom->is_a('*/*'); ok $atom->is_a('application/*'); ok $atom->is_a('application/xml'); ok $atom->is_a('application/atom+xml'); ok $atom->is_a('application/atom+xml;type=entry'); ok $atom->is_not_a('text/*'); ok $atom->is_not_a('application/octet-stream'); ok $atom->is_not_a('application/atom+xml;type=feed'); is "$atom", 'application/atom+xml;type=entry'; ok $atom eq '*/*'; ok $atom ne 'text/*'; Atompub-0.3.7/t/04.util.t000644 000765 000024 00000004375 11557026706 015263 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More tests => 25; use Atompub::MediaType qw(media_type); use Atompub::Util qw(is_acceptable_media_type is_allowed_category); use XML::Atom::Service; # is_acceptable_media_type my $coll = XML::Atom::Collection->new; ok is_acceptable_media_type($coll, media_type('entry')); ok !is_acceptable_media_type($coll, 'image/png'); $coll->accept('application/xml'); ok is_acceptable_media_type($coll, media_type('entry')); ok !is_acceptable_media_type($coll, 'image/png'); $coll->accept(media_type('entry')); ok is_acceptable_media_type($coll, media_type('entry')); ok !is_acceptable_media_type($coll, 'image/png'); $coll->accept('image/png'); ok !is_acceptable_media_type($coll, media_type('entry')); ok is_acceptable_media_type($coll, 'image/png'); $coll->accept('image/*'); ok !is_acceptable_media_type($coll, media_type('entry')); ok is_acceptable_media_type($coll, 'image/png'); $coll->accept('image/png', 'image/jpeg', 'image/gif'); ok !is_acceptable_media_type($coll, media_type('entry')); ok is_acceptable_media_type($coll, 'image/png'); $coll->accept('image/png,image/jpeg,image/gif'); ok !is_acceptable_media_type($coll, media_type('entry')); ok is_acceptable_media_type($coll, 'image/png'); # is_allowed_category my $cat1 = XML::Atom::Category->new; $cat1->term('animal'); my $cat1_s = XML::Atom::Category->new; $cat1_s->term('animal'); $cat1_s->scheme('http://example.com/cats/big3'); my $cat2 = XML::Atom::Category->new; $cat2->term('vegetable'); my $cats = XML::Atom::Categories->new; $coll->categories($cats); ok is_allowed_category($coll, $cat1); $cats->fixed('yes'); ok !is_allowed_category($coll, $cat1); $cats->category($cat1); ok is_allowed_category($coll, $cat1); ok is_allowed_category($coll, $cat1_s); ok !is_allowed_category($coll, $cat1, $cat2); $cats->category($cat1_s); ok !is_allowed_category($coll, $cat1); ok is_allowed_category($coll, $cat1_s); $cats->category($cat1, $cat2); ok is_allowed_category($coll, $cat1, $cat2); $cats->category($cat1); $cats->scheme('http://example.com/cats/big3'); ok !is_allowed_category($coll, $cat1); ok is_allowed_category($coll, $cat1_s); $coll = XML::Atom::Collection->new; # no app:categories ok is_allowed_category($coll, $cat1); Atompub-0.3.7/t/05.info.t000644 000765 000024 00000003577 11557026706 015245 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More tests => 18; use Atompub::Client; use XML::Atom::Service; # instance my $info = Atompub::Client::Info->instance; isa_ok $info, 'Atompub::Client::Info'; # put and get a resource, which has app:categories my $cats = XML::Atom::Categories->new; $cats->fixed( 'yes' ); $cats->scheme( 'http://example.com/cats/big3' ); my $cat = XML::Atom::Category->new; $cat->term( 'animal' ); $cats->add_category( $cat ); $cat = XML::Atom::Category->new; $cat->term( 'vegetable' ); $cats->add_category( $cat ); $cat = XML::Atom::Category->new; $cat->term( 'mineral' ); $cat->scheme( 'http://example.com/dogs/big3' ); $cats->add_category( $cat ); my $coll = XML::Atom::Collection->new; $coll->title( 'Text' ); $coll->href( 'http://example.com/text' ); $coll->add_categories( $cats ); $info->put( $coll->href, $coll ); $coll = $info->get( $coll->href ); isa_ok $coll, 'XML::Atom::Collection'; is $coll->title, 'Text'; is $coll->href, 'http://example.com/text'; is $coll->accept, undef; is $coll->categories->fixed, 'yes'; is $coll->categories->scheme, 'http://example.com/cats/big3'; my @cat = $coll->categories->category; is $cat[0]->term, 'animal'; is $cat[0]->scheme, undef; is $cat[1]->term, 'vegetable'; is $cat[1]->scheme, undef; is $cat[2]->term, 'mineral'; is $cat[2]->scheme, 'http://example.com/dogs/big3'; # put and get a resource, which has app:accept $coll = XML::Atom::Collection->new; $coll->title( 'Photo' ); $coll->href( 'http://example.com/photo' ); $coll->accept( 'image/png', 'image/jpeg', 'image/gif' ); $info->put( $coll->href, $coll ); $coll = $info->get( $coll->href ); isa_ok $coll, 'XML::Atom::Collection'; my @accepts = $coll->accepts; is $accepts[0], 'image/png'; is $accepts[1], 'image/jpeg'; is $accepts[2], 'image/gif'; # remove a resource $info->put( $coll->href ); is $info->get( $coll->href ), undef; Atompub-0.3.7/t/06.cache.t000644 000765 000024 00000001247 11557026706 015346 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More tests => 5; use Atompub::Client; use Atompub::MediaType qw(media_type); use XML::Atom::Entry; # instance my $cache = Atompub::Client::Cache->instance; isa_ok $cache, 'Atompub::Client::Cache'; # put and get a resource my $entry = XML::Atom::Entry->new; $entry->title('Entry 1'); my $uri = 'http://example.com/text/1'; $cache->put($uri, { rc => $entry, etag => 'tag:abc', }); my $rc = $cache->get($uri); isa_ok $rc, 'Atompub::Client::Cache::Resource'; is $rc->rc->title, 'Entry 1'; is $rc->etag, 'tag:abc'; # remove a resource $cache->put($uri); is $cache->get($uri), undef; Atompub-0.3.7/t/10.client.t000644 000765 000024 00000012005 11557026706 015546 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More; plan skip_all => 'set ATOMPUB_TEST_LIVE to enable this test' unless $ENV{ATOMPUB_TEST_LIVE}; plan tests => 84; use Atompub; use Atompub::Client; use Atompub::DateTime qw(datetime); use HTTP::Status; use URI::Escape; my $SERVICE = 'http://teahut.sakura.ne.jp:3000/service'; #my $SERVICE = 'http://localhost:3000/service'; my $USER = 'foo'; my $PASS = 'foo'; my $client = Atompub::Client->new; isa_ok $client, 'Atompub::Client'; $client->username($USER); $client->password($PASS); if (my $proxy = $ENV{HTTP_PROXY} || $ENV{http_proxy}) { diag "using HTTP proxy: $proxy"; $client->proxy( $proxy ) if $proxy; } # Service ok !$client->getService('http://example.com/service'); # Not Found like $client->errstr, qr/not found/i; isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; ok !$client->rc; is $client->res->code, RC_NOT_FOUND; ok $client->getService($SERVICE); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; isa_ok $client->rc, 'XML::Atom::Service'; ok $client->res->is_success; my $serv = $client->rc; my($entry_coll, $media_coll) = $serv->workspace->collections; isa_ok $client->info->get($entry_coll->href), 'XML::Atom::Collection'; isa_ok $client->info->get($media_coll->href), 'XML::Atom::Collection'; # Create Entry Resource my $entry = XML::Atom::Entry->new; $entry->title('Entry 1'); $entry->updated(datetime->w3c); $entry->id('tag:teahut.sakura.ne.jp,2007:1'); $entry->content('This is the 1st entry'); my $category = XML::Atom::Category->new; # Forbidden category $category->term('animal'); $category->scheme('http://example.com/dogs/big3'); $entry->category( $category ); ok !$client->createEntry($entry_coll->href, $entry, 'Entry 1'); like $client->errstr, qr/forbidden category/i; ok !$client->req; ok !$client->res; ok !$client->rc; $category = XML::Atom::Category->new; $category->term('animal'); $category->scheme('http://example.com/cats/big3'); $entry->category($category); ok $client->createEntry($entry_coll->href, $entry, 'Entry 1'); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; isa_ok $client->rc, 'XML::Atom::Entry'; is $client->req->slug, 'Entry 1'; is $client->res->code, RC_CREATED; ok my $uri = $client->res->location; $entry = $client->rc; is $entry->title, 'Entry 1'; isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource'; # List Entry Resources (Get Feed) ok $client->getFeed($entry_coll->href); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; isa_ok $client->rc, 'XML::Atom::Feed'; ok $client->res->is_success; # Get Entry Resource ok $client->getEntry($uri); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; isa_ok $client->rc, 'XML::Atom::Entry'; is $client->res->code, RC_NOT_MODIFIED; $entry = $client->rc; is $entry->title, 'Entry 1'; isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource'; # Update Entry Resource $entry->title('Entry 2'); ok $client->updateEntry($uri, $entry); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; isa_ok $client->rc, 'XML::Atom::Entry'; ok $client->res->is_success; $entry = $client->rc; is $entry->title, 'Entry 2'; isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource'; # Delete Entry Resource ok $client->deleteEntry($uri); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; ok !$client->rc; ok $client->res->is_success; # Create Media Resource # Unsupported media type ok !$client->createMedia($media_coll->href, 't/samples/media1.gif', 'text/plain', 'Media 1'); like $client->errstr, qr/unsupported media type/i; ok !$client->req; ok !$client->res; ok !$client->rc; ok $client->createMedia($media_coll->href, 't/samples/media1.gif', 'image/gif', 'Media 1'); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; isa_ok $client->rc, 'XML::Atom::Entry'; is $client->req->slug, 'Media 1'; is $client->res->code, RC_CREATED; ok $uri = $client->res->location; isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource'; # Get Media Resource ($uri) = map { $_->href } grep { $_->rel eq 'edit-media' } $client->rc->link; ok $client->getMedia($uri); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; ok $client->rc; ok $client->res->is_success; is $client->res->content_type, 'image/gif'; isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource'; # Update Media Resource ok $client->updateMedia($uri, 't/samples/media2.gif', 'image/gif'); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; ok $client->rc; ok $client->res->is_success; is $client->res->content_type, 'image/gif'; isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource'; # Delete Media Resource ok $client->deleteMedia($uri); isa_ok $client->req, 'HTTP::Request'; isa_ok $client->res, 'HTTP::Response'; ok !$client->rc; ok $client->res->is_success; Atompub-0.3.7/t/20.server.t000644 000765 000024 00000000220 11557026706 015573 0ustar00inouestaff000000 000000 use strict; use warnings; #use Data::Dumper; $Data::Dumper::Indent = 1; use Test::More tests => 1; BEGIN { use_ok( 'Atompub::Server' ); } Atompub-0.3.7/t/lib/000755 000765 000024 00000000000 11737440572 014435 5ustar00inouestaff000000 000000 Atompub-0.3.7/t/perlcritic.t000644 000765 000024 00000000436 11557026706 016216 0ustar00inouestaff000000 000000 #!perl use strict; use Test::More; plan skip_all => '$ENV{TEST_CRITIC} must be set to enable this test' unless $ENV{TEST_CRITIC}; eval { require Test::Perl::Critic }; plan skip_all => 'Test::Perl::Critic required to enable this test' if $@; Test::Perl::Critic::all_critic_ok(); Atompub-0.3.7/t/pod-coverage.t000644 000765 000024 00000000254 11557026706 016427 0ustar00inouestaff000000 000000 #!perl -T use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; all_pod_coverage_ok(); Atompub-0.3.7/t/pod.t000644 000765 000024 00000000214 11557026706 014632 0ustar00inouestaff000000 000000 #!perl -T use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); Atompub-0.3.7/t/samples/000755 000765 000024 00000000000 11737440572 015333 5ustar00inouestaff000000 000000 Atompub-0.3.7/t/server.cgi000755 000765 000024 00000000237 11557026706 015665 0ustar00inouestaff000000 000000 #!/usr/bin/perl use strict; use warnings; use lib ( $FindBin::Bin/lib, "$FindBin::Bin/../lib" ); use My::Server; my $server = My::Server->new; $server->run; Atompub-0.3.7/t/samples/media1.gif000644 000765 000024 00000000102 11557026706 017152 0ustar00inouestaff000000 000000 GIF89a !Created with The GIMP, c+;Atompub-0.3.7/t/samples/media2.gif000644 000765 000024 00000000102 11557026706 017153 0ustar00inouestaff000000 000000 GIF89a !Created with The GIMP, c+;Atompub-0.3.7/t/lib/My/000755 000765 000024 00000000000 11737440572 015022 5ustar00inouestaff000000 000000 Atompub-0.3.7/t/lib/My/Server.pm000644 000765 000024 00000001201 11557026706 016617 0ustar00inouestaff000000 000000 package My::Server; use strict; use warnings; use XML::Atom::Feed; use FindBin; use base qw( Atompub::Server ); sub init { my $server = shift; $server->realm('Atompub'); $server->SUPER::init(@_); } sub handle_request { my $server = shift; $server->authenticate || return; my $method = $server->request_method; if ( $method eq 'GET' ) { return $server->search_feed; } } my %Passwords = ( foo => 'foo' ); sub password_for_user { my $server = shift; my ( $username ) = @_; return $Passwords{$username}; } sub search_feed { my $server = shift; return XML::Atom::Feed->new->as_xml; } 1; Atompub-0.3.7/lib/Atompub/000755 000765 000024 00000000000 11737440572 015601 5ustar00inouestaff000000 000000 Atompub-0.3.7/lib/Atompub.pm000644 000765 000024 00000014041 11737440142 016130 0ustar00inouestaff000000 000000 package Atompub; use warnings; use strict; use 5.006; use version 0.74; our $VERSION = qv('0.3.7'); use HTTP::Headers; use HTTP::Request; use HTTP::Response; use XML::Atom; use XML::Atom::Service 0.15.4; our %REQUEST_HEADERS = ( accept => 'Accept', if_match => 'If-Match', if_none_match => 'If-None-Match', if_modified_since => 'If-Modified-Since', if_unmodified_since => 'If-Unmodified-Since', ); our %RESPONSE_HEADERS = ( content_location => 'Content-Location', etag => 'ETag', location => 'Location', ); our %ENTITY_HEADERS = ( last_modified => 'Last-Modified', slug => 'Slug', ); while (my($method, $header) = each %REQUEST_HEADERS) { no strict 'refs'; ## no critic *{"HTTP::Headers::$method"} = sub { shift->header($header, @_) } unless HTTP::Headers->can($method); *{"HTTP::Request::$method"} = sub { shift->header($header, @_)} unless (HTTP::Request->can($method)); } while (my($method, $header) = each %RESPONSE_HEADERS) { no strict 'refs'; ## no critic *{"HTTP::Headers::$method"} = sub { shift->header($header, @_) } unless HTTP::Headers->can($method); *{"HTTP::Response::$method"} = sub { shift->header($header, @_) } unless HTTP::Response->can($method); } while (my($method, $header) = each %ENTITY_HEADERS) { no strict 'refs'; ## no critic *{"HTTP::Headers::$method"} = sub { shift->header($header, @_) } unless HTTP::Headers->can($method); *{"HTTP::Request::$method"} = sub { shift->header($header, @_) } unless HTTP::Request->can($method); *{"HTTP::Response::$method"} = sub { shift->header($header, @_) } unless HTTP::Response->can($method); } 1; # Magic true value required at end of module __END__ =head1 NAME Atompub - Atom Publishing Protocol implementation =head1 DESCRIPTION The Atom Publishing Protocol (Atompub) is a protocol for publishing and editing Web resources described at L. L implements client L and server L for the protocol. XML formats used in the protocol are implemented in L and L. Catalyst extension L is also available. This module was tested in July2007InteropTokyo and November2007Interop, and interoperated with other implementations. See L and L in detail. =head1 METHODS of HTTP::Headers, HTTP::Request, and HTTP::Response Some accessors for the HTTP header fields, which are used in the Atom Publishing Protocol, are imported into L, L, and L. See L in detail. =head2 $headers->accept([ $value ]) An accessor for the I header field. This method is imported into L and L. =head2 $headers->if_match([ $value ]) An accessor for the I header field. This method is imported into L and L. =head2 $headers->if_none_match([ $value ]) An accessor for the I header field. This method is imported into L and L. =head2 $headers->if_modified_since([ $value ]) An accessor for the I header field. $value MUST be UTC epoch value, like C<1167609600>. This method is imported into L and L. =head2 $headers->if_unmodified_since([ $value ]) An accessor for the I header field. $value MUST be UTC epoch value, like C<1167609600>. This method is imported into L and L. =head2 $headers->content_location([ $value ]) An accessor for the I header field. This method is imported into L and L. =head2 $headers->etag([ $value ]) An accessor for the I header field. This method is imported into L and L. =head2 $headers->location([ $value ]) An accessor for the I header field. This method is imported into L and L. =head2 $headers->last_modified([ $value ]) An accessor for the I header field. This method is imported into L, L, and L. =head2 $headers->slug([ $value ]) An accessor for the I header field. This method is imported into L, L, and L. =head1 AUTHOR Takeru INOUE, Etakeru.inoue _ gmail.comE =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Takeru INOUE C<< >>. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Atompub-0.3.7/lib/Atompub/Client.pm000644 000765 000024 00000062143 11736745337 017371 0ustar00inouestaff000000 000000 package Atompub::Client; use warnings; use strict; use Atompub; use Atompub::DateTime qw(datetime); use Atompub::MediaType qw(media_type); use Atompub::Util qw(is_acceptable_media_type is_allowed_category); use Digest::SHA qw(sha1); use Encode qw(encode_utf8); use File::Slurp; use HTTP::Status; use MIME::Base64 qw(encode_base64); use NEXT; use URI::Escape; use XML::Atom::Entry; use XML::Atom::Service; use base qw(XML::Atom::Client Class::Accessor::Fast); my @ATTRS = qw(request response resource); __PACKAGE__->mk_accessors(@ATTRS, qw(ua info cache)); *req = \&request; *res = \&response; *rc = \&resource; sub init { my $client = shift; $client->NEXT::init(@_); $client->ua->agent('Atompub::Client/'.Atompub->VERSION); $client->info(Atompub::Client::Info->instance); $client->cache(Atompub::Client::Cache->instance); $client; } sub proxy { my($client, $proxy) = @_; $client->ua->proxy(['http', 'https'], $proxy); } sub getService { my($client, $uri) = @_; return $client->error('No URI') unless $uri; $client->_get_service({ uri => $uri }) or return; $client->rc; } sub getCategories { my($client, $uri) = @_; return $client->error('No URI') unless $uri; $client->_get_categories({ uri => $uri }) or return; $client->rc; } sub getFeed { my($client, $uri) = @_; return $client->error('No URI') unless $uri; $client->_get_feed({ uri => $uri }) or return; $client->rc; } sub createEntry { my($client, $uri, $entry, $slug) = @_; return $client->error('No URI') unless $uri; return $client->error('No Entry') unless $entry; unless (UNIVERSAL::isa($entry, 'XML::Atom::Entry')) { $entry = XML::Atom::Entry->new($entry) or return $client->error(XML::Atom::Entry->errstr); } my $headers = HTTP::Headers->new; $headers->content_type(media_type('entry')); $headers->slug(_escape(uri_unescape $slug)) if defined $slug; $client->_create_resource({ uri => $uri, rc => $entry, headers => $headers, }) or return; $client->res->location; } sub createMedia { my($client, $uri, $stream, $content_type, $slug) = @_; return $client->error('No URI') unless $uri; return $client->error('No stream') unless $stream; return $client->error('No Content-Type') unless $content_type; my $media = ref $stream ? $$stream : read_file($stream, binmode => ':raw') or return $client->error('No media'); my $headers = HTTP::Headers->new; $headers->content_type($content_type); $headers->slug(_escape( uri_unescape $slug)) if defined $slug; $client->_create_resource({ uri => $uri, rc => \$media, headers => $headers, }) or return; $client->res->location; } sub getEntry { my($client, $uri) = @_; return $client->error('No URI') unless $uri; $client->_get_resource({ uri => $uri }) or return; return $client->error('Response is not Atom Entry') unless UNIVERSAL::isa($client->rc, 'XML::Atom::Entry'); $client->rc; } sub getMedia { my($client, $uri) = @_; return $client->error('No URI') unless $uri; $client->_get_resource({ uri => $uri }) or return; return $client->error('Response is not Media Resource') if UNIVERSAL::isa($client->rc, 'XML::Atom::Entry'); wantarray ? ($client->rc, $client->res->content_type) : $client->rc; } sub updateEntry { my($client, $uri, $entry) = @_; return $client->error('No URI') unless $uri; return $client->error('No Entry') unless $entry; unless (UNIVERSAL::isa( $entry, 'XML::Atom::Entry')) { $entry = XML::Atom::Entry->new($entry) or return $client->error(XML::Atom::Entry->errstr); } my $headers = HTTP::Headers->new; $headers->content_type(media_type('entry')); $client->_update_resource({ uri => $uri, rc => $entry, headers => $headers, }); } sub updateMedia { my($client, $uri, $stream, $content_type) = @_; return $client->error('No URI') unless $uri; return $client->error('No stream') unless $stream; return $client->error('No Content-Type') unless $content_type; my $media = ref $stream ? $$stream : read_file($stream, binmode => ':raw') or return $client->error('No media resource'); my $headers = HTTP::Headers->new; $headers->content_type($content_type); $client->_update_resource({ uri => $uri, rc => \$media, headers => $headers, }); } sub deleteEntry { my($client, $uri) = @_; return $client->error('No URI') unless $uri; $client->_delete_resource({ uri => $uri }); } *deleteMedia = \&deleteEntry; sub _get_service { my($client, $args) = @_; my $uri = $args->{uri}; $client->_clear; return $client->error('No URI') unless $uri; $client->req(HTTP::Request->new(GET => $uri)); $client->res($client->make_request($client->req)); return $client->error(join "\n", $client->res->status_line, $client->res->content) unless is_success $client->res->code; warn 'Bad Content-Type: '.$client->res->content_type unless media_type($client->res->content_type)->is_a('service'); $client->rc(XML::Atom::Service->new(\$client->res->content)) or return $client->error(XML::Atom::Service->errstr); for my $work ($client->rc->workspaces) { $client->info->put($_->href, $_) for $work->collections; } $client; } sub _get_categories { my($client, $args) = @_; my $uri = $args->{uri}; $client->_clear; return $client->error('No URI') unless $uri; $client->req(HTTP::Request->new(GET => $uri)); $client->res($client->make_request($client->req)); return $client->error(join "\n", $client->res->status_line, $client->res->content) unless is_success $client->res->code; warn 'Bad Content-Type: '.$client->res->content_type unless media_type($client->res->content_type)->is_a('categories'); $client->rc(XML::Atom::Categories->new(\$client->res->content)) or return $client->error(XML::Atom::Categories->errstr); $client; } sub _get_feed { my($client, $args) = @_; my $uri = $args->{uri}; $client->_clear; return $client->error('No URI') unless $uri; $client->req(HTTP::Request->new(GET => $uri)); $client->res($client->make_request($client->req)); return $client->error(join "\n", $client->res->status_line, $client->res->content) unless is_success $client->res->code; warn 'Bad Content-Type: '.$client->res->content_type unless media_type($client->res->content_type)->is_a('feed'); $client->rc(XML::Atom::Feed->new(\$client->res->content)) or return $client->error(XML::Atom::Feed->errstr); $client; } sub _create_resource { my($client, $args) = @_; my $uri = $args->{uri}; my $rc = $args->{resource} || $args->{rc}; my $headers = $args->{headers}; $client->_clear; return $client->error('No URI') unless $uri; return $client->error('No resource') unless $rc; return $client->error('No headers') unless $headers; my $content_type = $headers->content_type; my $info = $client->info->get($uri); return $client->error("Unsupported media type: $content_type") unless is_acceptable_media_type($info, $content_type); my $content; if (UNIVERSAL::isa($rc, 'XML::Atom::Entry')) { my $entry = $rc; return $client->error('Forbidden category') unless is_allowed_category($info, $entry->category); $content = $entry->as_xml; XML::Atom::Client::_utf8_off($content); $headers->content_type(media_type('entry')); $headers->content_length(length $content); } elsif (UNIVERSAL::isa($rc, 'SCALAR')) { $content = $$rc; } $client->req(HTTP::Request->new(POST => $uri, $headers, $content)); $client->res($client->make_request($client->req)); return $client->error(join "\n", $client->res->status_line, $client->res->content) unless is_success $client->res->code; warn 'Bad status code: '.$client->res->code unless $client->res->code == RC_CREATED; return $client->error('No Locaiton') unless $client->res->location; # warn 'No Content-Locaiton' unless $client->res->content_location; return $client unless $client->res->content; warn 'Bad Content-Type: '.$client->res->content_type unless media_type($client->res->content_type)->is_a('entry'); $client->rc(XML::Atom::Entry->new(\$client->res->content)) or return $client->error(XML::Atom::Entry->errstr); my $last_modified = $client->res->last_modified; my $etag = $client->res->etag; $client->cache->put($client->res->location, { rc => $client->rc, last_modified => $last_modified, etag => $etag, }); $client; } sub _get_resource { my($client, $args) = @_; my $uri = $args->{uri}; $client->_clear; return $client->error('No URI') unless $uri; my $headers = HTTP::Headers->new; my $cache = $client->cache->get($uri); if ($cache) { $headers->if_modified_since(datetime($cache->last_modified)->epoch) if $cache->last_modified; $headers->if_none_match($cache->etag) if defined $cache->etag; } $client->req(HTTP::Request->new(GET => $uri, $headers)); $client->res($client->make_request($client->req)); if (is_success $client->res->code) { if (media_type($client->res->content_type)->is_a('entry')) { $client->rc(XML::Atom::Entry->new(\$client->res->content)) or return $client->error(XML::Atom::Entry->errstr); } else { $client->rc($client->res->content); } my $last_modified = $client->res->last_modified; my $etag = $client->res->etag; $client->cache->put($uri, { rc => $client->rc, last_modified => $last_modified, etag => $etag, }); } elsif ($client->res->code == RC_NOT_MODIFIED) { $client->rc($cache->rc); } else { return $client->error(join "\n", $client->res->status_line, $client->res->content); } $client; } sub _update_resource { my($client, $args) = @_; my $uri = $args->{uri}; my $rc = $args->{resource} || $args->{rc}; my $headers = $args->{headers}; $client->_clear; return $client->error('No URI') unless $uri; return $client->error('No resource') unless $rc; return $client->error('No headers') unless $headers; my $content; if (UNIVERSAL::isa($rc, 'XML::Atom::Entry')) { my $entry = $rc; $content = $entry->as_xml; XML::Atom::Client::_utf8_off($content); $headers->content_type(media_type('entry')); $headers->content_length(length $content); } elsif (UNIVERSAL::isa($rc, 'SCALAR')) { $content = $$rc; } if (my $cache = $client->cache->get($uri)) { $headers->if_unmodified_since(datetime($cache->last_modified)->epoch) if $cache->last_modified; $headers->if_match($cache->etag) if defined $cache->etag; } $client->req(HTTP::Request->new(PUT => $uri, $headers, $content)); $client->res($client->make_request($client->req)); return $client->error(join "\n", $client->res->status_line, $client->res->content) unless is_success $client->res->code; return $client unless $client->res->content; if (media_type($client->res->content_type)->is_a('entry')) { $client->rc(XML::Atom::Entry->new(\$client->res->content)) or return $client->error(XML::Atom::Entry->errstr); } else { $client->rc($client->res->content); } my $last_modified = $client->res->last_modified; my $etag = $client->res->etag; $client->cache->put($uri, { rc => $client->rc, last_modified => $last_modified, etag => $etag, }); $client; } sub _delete_resource { my($client, $args) = @_; my $uri = $args->{uri}; $client->_clear; return $client->error('No URI') unless $uri; my $headers = HTTP::Headers->new; # If-Match nor If-Unmodified-Since header is not required on DELETE # if (my $cache = $client->cache->get($uri)) { # $headers->if_unmodified_since(datetime($cache->last_modified)->epoch) # if $cache->last_modified; # $headers->if_match($cache->etag) if defined $cache->etag; # } $client->req(HTTP::Request->new(DELETE => $uri, $headers)); $client->res($client->make_request($client->req)); return $client->error(join "\n", $client->res->status_line, $client->res->content) unless is_success $client->res->code; $client; } sub _clear { my($client) = @_; $client->error(''); $client->{$_} = undef for @ATTRS; } sub munge_request { my($client, $req) = @_; $req->accept(join(',', media_type('entry')->without_parameters, media_type('service'), media_type('categories'), '*/*', )); return unless $client->username; my $nonce = sha1(sha1(time.{}.rand().$$)); my $now = datetime->w3cz; my $wsse = sprintf( qq{UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"}, ($client->username || ''), encode_base64(sha1($nonce.$now.($client->password || '')), ''), encode_base64($nonce, ''), $now, ); $req->header('X-WSSE' => $wsse); $req->authorization('WSSE profile="UsernameToken"'); } # see 9.7.1 in RFC 5023 sub _escape { my ($slug) = @_; return uri_escape(encode_utf8($slug), "\x00-\x19\x25-\x25\x7e-\xff"); } package Atompub::Client::Info; my $Info; sub instance { my($class) = @_; $Info ||= bless { info => {} }, $class; $Info; } sub put { my($self, $uri, @args) = @_; return unless $uri; if (@args) { $self->{info}{$uri} = $self->_clone_collection(@args); } else { delete $self->{info}{$uri}; } } sub get { my($self, $uri) = @_; return unless $uri; $self->{info}{$uri}; } sub _get_categories { my($self, $client, $href) = @_; return unless $client; $client->getCategories($href); } sub _clone_collection { my($self, $coll_arg, $client) = @_; return unless UNIVERSAL::isa($coll_arg, 'XML::Atom::Collection'); my $coll = XML::Atom::Collection->new; $coll->title($coll_arg->title); $coll->href($coll_arg->href); $coll->accept($coll_arg->accepts) if $coll_arg->accept; my @cats = grep { defined $_ } map { $_->href ? $self->_get_categories($client, $_->href) : $self->_clone_categories($_) } $coll_arg->categories; $coll->categories(@cats); $coll; } sub _clone_categories { my($self, $cats_arg) = @_; my $cats = XML::Atom::Categories->new; $cats->fixed($cats_arg->fixed) if $cats_arg->fixed; $cats->scheme($cats_arg->scheme) if $cats_arg->scheme; my @cat = map { my $cat = XML::Atom::Category->new; $cat->term($_->term); $cat->scheme($_->scheme) if $_->scheme; $cat->label($_->label) if $_->label; $cat } $cats_arg->category; $cats->category(@cat); $cats; } package Atompub::Client::Cache; my $Cache; sub instance { my($class) = @_; $Cache ||= bless { cache => {} }, $class; $Cache; } sub put { my($self, $uri, @args) = @_; return unless $uri; if (@args) { $self->{cache}{$uri} = Atompub::Client::Cache::Resource->new(@args); } else { delete $self->{cache}{$uri}; } } sub get { my($self, $uri) = @_; return unless $uri; $self->{cache}{$uri}; } package Atompub::Client::Cache::Resource; use strict; use warnings; use base qw(Class::Accessor::Fast); __PACKAGE__->mk_accessors(qw(resource last_modified etag)); *rc = \&resource; sub new { my($class, $args) = @_; my $rc = $args->{resource} || $args->{rc} or return; bless { resource => $rc, last_modified => $args->{last_modified}, etag => $args->{etag}, }, $class; } 1; __END__ =head1 NAME Atompub::Client - A client for the Atom Publishing Protocol =head1 SYNOPSIS use Atompub::Client; my $client = Atompub::Client->new; $client->username('Melody'); $client->password('Nelson'); #$client->proxy( $proxy_uri ); # Get a Service Document my $service = $client->getService($service_uri); my @workspaces = $service->workspaces; my @collections = $workspaces[0]->collections; # CRUD an Entry Resource; assuming that the 0-th collection supports # Entry Resources my $collection_uri = $collections[0]->href; my $name = 'New Post'; my $entry = XML::Atom::Entry->new; $entry->title($name); $entry->content('Content of my post.'); my $edit_uri = $client->createEntry($collection_uri, $entry, $name); my $feed = $client->getFeed($collection_uri); my @entries = $feed->entries; $entry = $client->getEntry($edit_uri); $client->updateEntry($edit_uri, $entry); $client->deleteEntry($edit_uri); # CRUD a Media Resource; assuming that the 1-st collection supports # Media Resources my $collection_uri = $collections[1]->href; my $name = 'My Photo'; my $edit_uri = $client->createMedia($collection_uri, 'sample1.png', 'image/png', $name); # Get a href attribute of an "edit-media" link my $edit_media_uri = $client->resource->edit_media_link; my $binary = $client->getMedia($edit_media_uri); $client->updateMedia($edit_media_uri, 'sample2.png', 'image/png'); $client->deleteEntry($edit_media_uri); # Access to the requested HTTP::Request object my $request = $client->request; # Access to the received HTTP::Response object my $response = $client->response; # Access to the received resource (XML::Atom object or binary data) my $resource = $client->resource; =head1 DESCRIPTION L implements a client for the Atom Publishing Protocol described at L. The client supports the following features: =over 4 =item * Authentication L supports the Basic and WSSE Authentication described in L. =item * Service Document L understands Service Documents, in which information of collections are described, such as URIs, acceptable media types, and allowable categories. =item * Media Resource support Media Resources (binary data) as well as Entry Resources are supported. You can create and edit Media Resources such as image and video by using L. =item * Media type check L checks media types of resources before creating and editing them to the collection. Acceptable media types are shown in I elements in the Service Document. =item * Category check L checks categories in Entry Resources before creating and editing them to the collection. Allowable categories are shown in I elements in the Service Document. =item * Cache controll and versioning On-memory cache and versioning, which are controlled by I and I header, are implemented in L. =item * Naming resources by I header The client can specify I header when creating a resource, which may be used as part of the resource URI. =back =head1 METHODS =head2 Atompub::Client->new([ %options ]) Creates a new Atompub client object. The options are same as L. =head2 $client->getService($service_uri) Retrieves a Service Document at URI $service_uri. Returns an L object on success, false otherwise. =head2 $client->getCategories($category_uri) Retrieves a Category Document at URI $category_uri. Returns an L object on success, false otherwise. =head2 $client->getFeed($collection_uri) Retrieves a Feed Document from the collection at URI $collection_uri. Returns an L object, false otherwise. =head2 $client->createEntry($collection_uri, $entry, [ $slug ]) Creates a new entry in the collection at URI $collection_uri. $entry must be an L object. If $slug is provided, it is set in I header and may be used as part of the resource URI. Returns a I header, which contains a URI of the newly created resource, or false on error. =head2 $client->createMedia($collection_uri, $media, $media_type, [ $slug ]) Creates a new Media Resource and a Media Link Entry in the collection at URI $collection_uri. If $media is a reference to a scalar, it is treated as the binary. If a scalar, treated as a file containing the Media Resource. $media_type is the media type of the Media Resource, such as 'image/png'. $slug is set in the I header, and may be used as part of the resource URI. Returns a I header, which contains a URI of the newly created resource, or false on error. =head2 $client->getEntry($edit_uri) Retrieves an Entry Document with the given URI $edit_uri. Returns an L object on success, false otherwise. If the server returns 304 (Not Modified), returns a cache of the Media Resource. =head2 $client->getMedia($edit_uri) Retrieves a Media Resource with the given URI $edit_uri. Returns binary data of the Media Resource on success, false otherwise. If the server returns 304 (Not Modified), returns a cache of the Media Resource. =head2 $client->updateEntry($edit_uri, $entry) Updates the Entry Document at URI $edit_uri with the new Entry Document $entry, which must be an L object. Returns true on success, false otherwise. =head2 $client->updateMedia($edit_uri, $media, $media_type) Updates the Media Resource at URI $edit_uri with the $media. If $media is a reference to a scalar, it is treated as the binary. If a scalar, treated as a file containing the Media Resource. $media_type is the media type of the Media Resource, such as 'image/png'. Returns true on success, false otherwise. =head2 $client->deleteEntry($edit_uri) Deletes the Entry Document at URI $edit_uri. Returns true on success, false otherwise. =head2 $client->deleteMedia($edit_uri) Deletes the Media Resource at URI $edit_uri and related Media Link Entry. Returns true on success, false otherwise. =head1 Accessors =head2 $client->username([ $username ]) If called with an argument, sets the username for login to $username. Returns the current username that will be used when logging in to the Atompub server. =head2 $client->password([ $password ]) If called with an argument, sets the password for login to $password. Returns the current password that will be used when logging in to the Atompub server. =head2 $client->proxy([ $proxy_uri ]) If called with an argument, sets URI of proxy server like 'http://proxy.example.com:8080'. Returns the current URI of the proxy server. =head2 $client->resource =head2 $client->rc An accessor for Entry or Media Resource, which was retrieved in the previous action. =head2 $client->request =head2 $client->req An accessor for an L object, which was used in the previous action. =head2 $client->response =head2 $client->res An accessor for an L object, which was used in the previous action. =head1 INTERNAL INTERFACES =head2 $client->init =head2 $client->ua Accessor to the UserAgent. =head2 $client->info An accessor to information of Collections described in a Service Document. =head2 $client->cache An accessor to the resource cache. =head2 $client->munge_request($req) =head2 $client->_clear =head2 $client->_get_service(\%args) =head2 $client->_get_categories(\%args) =head2 $client->_get_feed(\%args) =head2 $client->_create_resource(\%args) =head2 $client->_get_resource(\%args) =head2 $client->_update_resource(\%args) =head2 $client->_delete_resource(\%args) =head1 ERROR HANDLING Methods return C on error, and the error message can be retrieved using the I method. =head1 SEE ALSO L L L =head1 AUTHOR Takeru INOUE, Etakeru.inoue _ gmail.comE =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Takeru INOUE C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Atompub-0.3.7/lib/Atompub/DateTime.pm000644 000765 000024 00000013562 11557026706 017641 0ustar00inouestaff000000 000000 package Atompub::DateTime; use strict; use warnings; use Atompub; use DateTime; use DateTime::Format::W3CDTF; use DateTime::TimeZone; use HTTP::Date qw(str2time time2isoz time2str); use Perl6::Export::Attrs; use Time::Local; use base qw(Class::Accessor::Fast Class::Data::Inheritable); use overload ( q{""} => \&w3c, q{0+} => \&epoch, fallback => 1, ); my $tz; sub tz { unless ($tz) { eval { $tz = DateTime::TimeZone->new(name => 'local') }; if ($@) { $tz = DateTime::TimeZone->new(name => 'UTC' ) } } $tz; } __PACKAGE__->mk_classdata(fmt => DateTime::Format::W3CDTF->new); __PACKAGE__->mk_accessors(qw(dt)); sub new { my($class, @args) = @_; my $self = bless {}, $class; $self->init(@args) or return; $self; } sub init { my($self, $arg) = @_; my $epoch = !$arg ? time : UNIVERSAL::can($arg, 'epoch') ? $arg->epoch : $arg =~ qr{^\d{1,13}$} ? $arg : $arg =~ qr{^\d{14}$} ? _parse_timestamp($arg) : str2time $arg; return unless defined $epoch; $self->dt(DateTime->from_epoch( epoch => $epoch, time_zone => $self->tz, formatter => $self->fmt, )); $self; } sub datetime :Export { __PACKAGE__->new(@_) } sub _parse_timestamp { my @a = $_[0] =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/; $a[1]--; # month timelocal reverse @a; } sub epoch { $_[0]->dt->epoch } sub iso { my($self) = @_; $self->{iso} ||= sprintf '%s %s', $self->dt->ymd, $self->dt->hms; } sub isoz { my($self) = @_; $self->{isoz} ||= time2isoz $self->epoch; } sub w3c { my($self) = @_; $self->{w3c} ||= '' . $self->dt; } sub w3cz { my($self) = @_; unless ($self->{w3cz}) { my $w3cz = time2isoz $self->epoch; $w3cz =~ s/ /T/; $self->{w3cz} = $w3cz; } $self->{w3cz}; } sub str { my($self) = @_; $self->{str} ||= time2str $self->epoch; } 1; __END__ =head1 NAME Atompub::DateTime - A date and time object for the Atom Publishing Protocol =head1 SYNOPSIS # assuming the local timezone is JST (+09:00) use Atompub::DateTime qw(datetime); $dt = datetime; # current time $dt = datetime(DateTime->new); $dt = datetime(1167609600); # UTC epoch value $dt = datetime('20070101090000'); $dt = datetime('2007-01-01 09:00:00'); $dt = datetime('2007-01-01 00:00:00Z'); $dt = datetime('2007-01-01T09:00:00+09:00'); $dt = datetime('2007-01-01T00:00:00Z'); $dt = datetime('Mon, 01 Jan 2007 00:00:00 GMT'); $dt->epoch; # 1167609600 (UTC epoch value) $dt->iso; # 2007-01-01 09:00:00 (in localtime) $dt->isoz; # 2007-01-01 00:00:00Z $dt->w3c; # 2007-01-01T09:00:00+09:00 $dt->w3cz; # 2007-01-01T00:00:00Z $dt->str; # Mon, 01 Jan 2007 00:00:00 GMT my $dt2 = datetime($dt); # copy $dt == $dt2; # compare "$dt"; # $dt->w3c $dt->dt; # DateTime object =head1 METHODS =head2 Atompub::DateTime->new([ $str ]) Returns a datetime object representing the time $str. If the function is called without an argument, it will use the current time. =head2 datetime([ $str ]) An alias for Atompub::DateTime->new =head2 $datetime->epoch Returns UTC epoch value. 1167609600 =head2 $datetime->iso Returns a "YYYY-MM-DD hh:mm:ss"-formatted string representing time in the local time zone. 2007-01-01 09:00:00 =head2 $datetime->isoz Returns a "YYYY-MM-DD hh:mm:ssZ"-formatted string representing Universal Time. 2007-01-01 00:00:00Z =head2 $datetime->w3c Returns a "YYYY-MM-DDThh:mm:ssTZ"-formatted string (W3C DateTime Format) representing time in the local time zone. 2007-01-01T09:00:00+09:00 =head2 $datetime->w3cz Returns a "YYYY-MM-DDThh:mm:ssZ"-formatted string (W3C DateTime Format) representing Universal Time. 2007-01-01T00:00:00Z =head2 $datetime->str Returns a human readable representation. Mon, 01 Jan 2007 00:00:00 GMT =head2 $datetime->dt An accessor for the internal L object. =head2 $datetime->tz An accessor for the internal L object. =head2 $datetime->fmt An accessor for the internal L object. =head1 INTERNAL INTERFACES =head2 $datetime->init =head2 $datetime->_parse_timestamp =head1 SEE ALSO L =head1 AUTHOR Takeru INOUE, Etakeru.inoue _ gmail.comE =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Takeru INOUE C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Atompub-0.3.7/lib/Atompub/MediaType.pm000644 000765 000024 00000012035 11557026706 020020 0ustar00inouestaff000000 000000 package Atompub::MediaType; use strict; use warnings; use Atompub; use MIME::Types; use Perl6::Export::Attrs; use base qw(Class::Accessor::Fast); my %ATOM_TYPE = ( entry => 'application/atom+xml;type=entry', feed => 'application/atom+xml;type=feed', service => 'application/atomsvc+xml', categories => 'application/atomcat+xml', ); __PACKAGE__->mk_accessors(qw(type subtype parameters)); use overload ( q{""} => \&as_string, eq => \&is_a, ne => \&is_not_a, fallback => 1, ); sub new { my($class, $arg) = @_; my $media_type = $ATOM_TYPE{$arg} || $arg or return; my($type, $subtype, $param) = split m{[/;]}, $media_type; bless { type => $type, subtype => $subtype, parameters => $param, }, $class; } sub media_type :Export { __PACKAGE__->new(@_) } sub subtype_major { my($self) = @_; $self->subtype =~ /\+(.+)/ ? $1 : $self->subtype; } sub without_parameters { my($self) = @_; join '/', $self->type, $self->subtype; } sub as_string { my($self) = @_; join ';', grep { defined $_ } $self->without_parameters, $self->parameters; } sub extensions { my($self) = @_; my $mime = MIME::Types->new->type($self->without_parameters) or return; my @exts = $mime->extensions; wantarray ? @exts : $exts[0]; } sub extension { scalar shift->extensions } sub is_a { my($self, $test) = @_; $test = __PACKAGE__->new($test) unless UNIVERSAL::isa($test, __PACKAGE__); return 1 if $test->type eq '*'; return 0 unless $test->type eq $self->type; return 1 if $test->subtype eq '*'; if ($test->subtype eq $test->subtype_major) { # ex. application/xml return 0 unless $test->subtype_major eq $self->subtype_major; } else { # ex. application/atom+xml return 0 unless $test->subtype eq $self->subtype; } return 1 if ! $test->parameters || ! $self->parameters; return $test->parameters eq $self->parameters; } sub is_not_a { my($self, @args) = @_; !$self->is_a(@args); } 1; __END__ =head1 NAME Atompub::MediaType - a media type object for the Atom Publishing Protocol =head1 SYNOPSIS use Atompub::MediaType qw(media_type); my $type = media_type('image/png'); "$type"; # 'image/png' $type->type; # 'image' $type->subtype; # 'png' $type->extension; # 'png' $type->is_a('image/*'); # true $type->is_a('image/gif'); # false my $type = media_type('entry'); "$type"; # 'application/atom+xml;type=entry' $type->type; # 'application' $type->subtype; # 'atom+xml' $type->parameters; # 'type=entry' $type->subtype_major; # 'xml' $type->extension; # 'atom' $type->is_a('application/xml'); # true $type->is_a('feed'); # false =head1 METHODS =head2 Atompub::MediaType->new([ $type ]) Returns a media type object representing the time $type. $type is string representing media type like 'image/png'. Some aliases are defined for Atom, 'entry', 'feed', 'service', and 'categories'. =head2 media_type([ $str ]) Alias for Atompub::MediaType->new =head2 $type->type =head2 $type->subtype =head2 $type->parameters =head2 $type->subtype_major =head2 $type->extensions =head2 $type->extension =head2 $type->is_a =head2 $type->is_not_a =head2 $type->as_string =head2 $type->without_parameters =head1 SEE ALSO L =head1 AUTHOR Takeru INOUE, Etakeru.inoue _ gmail.comE =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Takeru INOUE C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Atompub-0.3.7/lib/Atompub/Server.pm000644 000765 000024 00000021564 11736745337 017423 0ustar00inouestaff000000 000000 package Atompub::Server; use strict; use warnings; use Atompub; use Digest::SHA qw(sha1); use MIME::Base64 qw(encode_base64 decode_base64); use HTTP::Status; use XML::Atom; use base qw(XML::Atom::Server); sub send_http_header { my($server) = @_; my $type = $server->response_content_type || 'application/atom+xml'; if ($ENV{MOD_PERL}) { $server->{apache}->status($server->response_code || RC_OK); $server->{apache}->send_http_header($type); } else { $server->{cgi_headers}{-status} = $server->response_code || RC_OK; $server->{cgi_headers}{-type} = $type; print $server->{cgi}->header(%{ $server->{cgi_headers} }); } } sub realm { my($server, $realm) = @_; $server->{realm} = $realm if $realm; $server->{realm}; } sub get_auth_info { my($server) = @_; my %param; # Basic Authentication if (my $auth = $server->request_header('Authorization')) { return unless $auth =~ s/^\s*Basic\s+//; require MIME::Base64; my $val = MIME::Base64::decode($auth); my($userid, $password) = split /:/, $val, 2; %param = (userid => $userid, password => $password); } # WSSE Authentication elsif (my $req = $server->request_header('X-WSSE')) { $req =~ s/^(?:WSSE|UsernameToken) //; for my $i (split /,\s*/, $req) { my($k, $v) = split /=/, $i, 2; $v =~ s/^"//; $v =~ s/"$//; $param{$k} = $v; } } else { return $server->auth_failure(RC_UNAUTHORIZED, 'Basic or WSSE authentication required'); } \%param; } sub authenticate { my($server) = @_; my $auth = $server->get_auth_info || return; # Basic Authentication if (defined $auth->{userid}) { my $password = $server->password_for_user($auth->{userid}); return $server->auth_failure(RC_FORBIDDEN, 'Invalid login') if !defined $password || $password ne $auth->{password}; } # WSSE Authentication else { for my $f (qw(Username PasswordDigest Nonce Created)) { return $server->auth_failure(RC_BAD_REQUEST, "X-WSSE requires $f") unless $auth->{$f}; } my $password = $server->password_for_user($auth->{Username}); return $server->auth_failure(RC_FORBIDDEN, 'Invalid login') unless defined $password; my $expected = encode_base64(sha1(decode_base64($auth->{Nonce}).$auth->{Created}.$password), ''); return $server->auth_failure(RC_FORBIDDEN, 'Invalid login') unless $expected eq $auth->{PasswordDigest}; } 1; } sub auth_failure { my($server) = @_; my $realm = $server->realm || 'Atompub'; $server->response_header( 'WWW-Authenticate', qq{Basic realm="$realm", WSSE profile="UsernameToken"}, ); $server->error(@_); } 1; __END__ =head1 NAME Atompub::Server - A server for the Atom Publishing Protocol =head1 SYNOPSIS package My::Server; use base qw(Atompub::Server); sub handle_request { my($server) = @_; $server->authenticate or return; my $method = $server->request_method; if ($method eq 'POST') { return $server->new_post; } ... } my %Passwords; sub password_for_user { my($server, $username) = @_; $Passwords{$username}; } sub new_post { my($server) = @_; my $entry = $server->atom_body or return; # $entry is an XML::Atom::Entry object. # ... Save the new entry ... } package main; my $server = My::Server->new; $server->run; =head1 DESCRIPTION L provides a base class for Atom Publishing Protocol servers. It handles all core server processing, and Basic and WSSE authentication. It can also run as either a mod_perl handler or as part of a CGI program. It does not provide functions specific to any particular implementation, such as creating an entry, retrieving a list of entries, deleting an entry, etc. Implementations should subclass L, overriding the C method, and handle all functions such as this themselves. L extends L, and basically provides same functions. However, this module has been fixed based on the Atom Publishing Protocol described at L, and supports Basic authentication rather than WSSE. =head1 SUBCLASSING =head2 Request Handling Subclasses of L must override the C method to perform all request processing. The implementation must set all response headers, including the response code and any relevant HTTP headers, and should return a scalar representing the response body to be sent back to the client. For example: sub handle_request { my($server) = @_; my $method = $server->request_method; if ($method eq 'POST') { return $server->new_post; } # ... handle GET, PUT, etc } sub new_post { my($server) = @_; my $entry = $server->atom_body or return; # Implementation-specific my $id = save_this_entry($entry); my $location = join '/', $server->uri, $id; my $etag = calc_etag($entry); $server->response_header(Location => $location); $server->response_header(ETag => $etag ); $server->response_code(RC_CREATED); $server->response_content_type('application/atom+xml;type=entry'); # Implementation-specific return serialize_entry($entry); } =head2 Authentication Servers that require authentication should override the C method. Given a username (from the Authorization or WSSE header), C should return that user's password in plaintext. If the supplied username doesn't exist in your user database or alike, just return C. For example: my %Passwords = (foo => 'bar'); # The password for "foo" is "bar". sub password_for_user { my($server, $username) = @_; $Passwords{$username}; } =over 2 =item * Basic Authentication I must be assigned before authentication for Basic authentication. $server->realm('MySite'); If your server runs as a CGI program and authenticates by Basic authenticate, you should use authentication mechanism of the http server, like C<.htaccess>. =item * WSSE Authentication Any pre-configuration is not required for WSSE. The password returned from C will be combined with the nonce and the creation time to generate the digest, which will be compared with the digest sent in the WSSE header. =back =head1 METHODS L provides a variety of methods to be used by subclasses for retrieving headers, content, and other request information, and for setting the same on the response. =head2 $server->realm If called with an argument, sets the I for Basic authentication. Returns the current I that will be used when receiving requests. =head2 $server->send_http_header($content_type) =head2 $server->get_auth_info =head2 $server->authenticate =head2 $server->auth_failure($status, $message) =head2 oether methods Descriptions are found in L. =head1 USAGE Once you have defined your server subclass, you can set it up either as a CGI program or as a mod_perl handler. See L in details. =head1 SEE ALSO L L L L =head1 AUTHOR Takeru INOUE, Etakeru.inoue _ gmail.comE =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Takeru INOUE C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Atompub-0.3.7/lib/Atompub/Util.pm000644 000765 000024 00000006101 11557026706 017051 0ustar00inouestaff000000 000000 package Atompub::Util; use warnings; use strict; use Atompub; use Atompub::MediaType qw(media_type); use Perl6::Export::Attrs; use XML::Atom::Category; sub is_acceptable_media_type :Export { my($coll, $content_type) = @_; return 1 unless $coll; my @accepts = map { split /[\s,]+/ } $coll->accept; @accepts = (media_type('entry')) unless @accepts; # assign default type grep { media_type($content_type)->is_a($_) } @accepts; } sub is_allowed_category :Export { my($coll, @tests) = @_; return 1 unless $coll; return 1 unless $coll->categories; return 1 if grep { ! $_->fixed || $_->fixed ne 'yes' } $coll->categories; my @allowed; for my $cats ($coll->categories) { push @allowed, map { my $cat = XML::Atom::Category->new; my $scheme = $_->scheme || $cats->scheme; $cat->term($_->term); $cat->scheme($scheme) if $scheme; $cat } $cats->category; } return 0 if ! @allowed && @tests; for my $t (@tests) { return 0 unless grep { _match_category($_, $t) } @allowed; } 1; } sub _match_category { my($allowed, $test) = @_; return $allowed->term eq $test->term && (!$allowed->scheme || $test->scheme && $allowed->scheme eq $test->scheme); } 1; __END__ =head1 NAME Atompub::Util - Utility functions =head1 FUNCTIONS =head2 is_acceptable_media_type($collection, $content_type) =head2 is_allowed_category($collection, $category, ...) =head1 INTERNAL INTERFACES =head2 _match_category =head1 SEE ALSO L =head1 AUTHOR Takeru INOUE, Etakeru.inoue _ gmail.comE =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Takeru INOUE C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Atompub-0.3.7/inc/Module/000755 000765 000024 00000000000 11737440572 015422 5ustar00inouestaff000000 000000 Atompub-0.3.7/inc/Module/AutoInstall.pm000644 000765 000024 00000054231 11737440437 020224 0ustar00inouestaff000000 000000 #line 1 package Module::AutoInstall; use strict; use Cwd (); use ExtUtils::MakeMaker (); use vars qw{$VERSION}; BEGIN { $VERSION = '1.03'; } # special map on pre-defined feature sets my %FeatureMap = ( '' => 'Core Features', # XXX: deprecated '-core' => 'Core Features', ); # various lexical flags my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS ); my ( $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps ); my ( $PostambleActions, $PostambleUsed ); # See if it's a testing or non-interactive session _accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); _init(); sub _accept_default { $AcceptDefault = shift; } sub missing_modules { return @Missing; } sub do_install { __PACKAGE__->install( [ $Config ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) : () ], @Missing, ); } # initialize various flags, and/or perform install sub _init { foreach my $arg ( @ARGV, split( /[\s\t]+/, $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || '' ) ) { if ( $arg =~ /^--config=(.*)$/ ) { $Config = [ split( ',', $1 ) ]; } elsif ( $arg =~ /^--installdeps=(.*)$/ ) { __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) ); exit 0; } elsif ( $arg =~ /^--default(?:deps)?$/ ) { $AcceptDefault = 1; } elsif ( $arg =~ /^--check(?:deps)?$/ ) { $CheckOnly = 1; } elsif ( $arg =~ /^--skip(?:deps)?$/ ) { $SkipInstall = 1; } elsif ( $arg =~ /^--test(?:only)?$/ ) { $TestOnly = 1; } elsif ( $arg =~ /^--all(?:deps)?$/ ) { $AllDeps = 1; } } } # overrides MakeMaker's prompt() to automatically accept the default choice sub _prompt { goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; my ( $prompt, $default ) = @_; my $y = ( $default =~ /^[Yy]/ ); print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] '; print "$default\n"; return $default; } # the workhorse sub import { my $class = shift; my @args = @_ or return; my $core_all; print "*** $class version " . $class->VERSION . "\n"; print "*** Checking for Perl dependencies...\n"; my $cwd = Cwd::cwd(); $Config = []; my $maxlen = length( ( sort { length($b) <=> length($a) } grep { /^[^\-]/ } map { ref($_) ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} ) : '' } map { +{@args}->{$_} } grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} } )[0] ); # We want to know if we're under CPAN early to avoid prompting, but # if we aren't going to try and install anything anyway then skip the # check entirely since we don't want to have to load (and configure) # an old CPAN just for a cosmetic message $UnderCPAN = _check_lock(1) unless $SkipInstall; while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) { my ( @required, @tests, @skiptests ); my $default = 1; my $conflict = 0; if ( $feature =~ m/^-(\w+)$/ ) { my $option = lc($1); # check for a newer version of myself _update_to( $modules, @_ ) and return if $option eq 'version'; # sets CPAN configuration options $Config = $modules if $option eq 'config'; # promote every features to core status $core_all = ( $modules =~ /^all$/i ) and next if $option eq 'core'; next unless $option eq 'core'; } print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n"; $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' ); unshift @$modules, -default => &{ shift(@$modules) } if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) { if ( $mod =~ m/^-(\w+)$/ ) { my $option = lc($1); $default = $arg if ( $option eq 'default' ); $conflict = $arg if ( $option eq 'conflict' ); @tests = @{$arg} if ( $option eq 'tests' ); @skiptests = @{$arg} if ( $option eq 'skiptests' ); next; } printf( "- %-${maxlen}s ...", $mod ); if ( $arg and $arg =~ /^\D/ ) { unshift @$modules, $arg; $arg = 0; } # XXX: check for conflicts and uninstalls(!) them. my $cur = _load($mod); if (_version_cmp ($cur, $arg) >= 0) { print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n"; push @Existing, $mod => $arg; $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { if (not defined $cur) # indeed missing { print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n"; } else { # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above print "too old. ($cur < $arg)\n"; } push @required, $mod => $arg; } } next unless @required; my $mandatory = ( $feature eq '-core' or $core_all ); if ( !$SkipInstall and ( $CheckOnly or ($mandatory and $UnderCPAN) or $AllDeps or _prompt( qq{==> Auto-install the } . ( @required / 2 ) . ( $mandatory ? ' mandatory' : ' optional' ) . qq{ module(s) from CPAN?}, $default ? 'y' : 'n', ) =~ /^[Yy]/ ) ) { push( @Missing, @required ); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } elsif ( !$SkipInstall and $default and $mandatory and _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) =~ /^[Nn]/ ) { push( @Missing, @required ); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { $DisabledTests{$_} = 1 for map { glob($_) } @tests; } } if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) { require Config; print "*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; # make an educated guess of whether we'll need root permission. print " (You may need to do that as the 'root' user.)\n" if eval '$>'; } print "*** $class configuration finished.\n"; chdir $cwd; # import to main:: no strict 'refs'; *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; return (@Existing, @Missing); } sub _running_under { my $thing = shift; print <<"END_MESSAGE"; *** Since we're running under ${thing}, I'll just let it take care of the dependency's installation later. END_MESSAGE return 1; } # Check to see if we are currently running under CPAN.pm and/or CPANPLUS; # if we are, then we simply let it taking care of our dependencies sub _check_lock { return unless @Missing or @_; my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING}; if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) { return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS'); } require CPAN; if ($CPAN::VERSION > '1.89') { if ($cpan_env) { return _running_under('CPAN'); } return; # CPAN.pm new enough, don't need to check further } # last ditch attempt, this -will- configure CPAN, very sorry _load_cpan(1); # force initialize even though it's already loaded # Find the CPAN lock-file my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" ); return unless -f $lock; # Check the lock local *LOCK; return unless open(LOCK, $lock); if ( ( $^O eq 'MSWin32' ? _under_cpan() : == getppid() ) and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore' ) { print <<'END_MESSAGE'; *** Since we're running under CPAN, I'll just let it take care of the dependency's installation later. END_MESSAGE return 1; } close LOCK; return; } sub install { my $class = shift; my $i; # used below to strip leading '-' from config keys my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } ); my ( @modules, @installed ); while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) { # grep out those already installed if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { push @installed, $pkg; } else { push @modules, $pkg, $ver; } } return @installed unless @modules; # nothing to do return @installed if _check_lock(); # defer to the CPAN shell print "*** Installing dependencies...\n"; return unless _connected_to('cpan.org'); my %args = @config; my %failed; local *FAILED; if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) { while () { chomp; $failed{$_}++ } close FAILED; my @newmod; while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) { push @newmod, ( $k => $v ) unless $failed{$k}; } @modules = @newmod; } if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) { _install_cpanplus( \@modules, \@config ); } else { _install_cpan( \@modules, \@config ); } print "*** $class installation finished.\n"; # see if we have successfully installed them while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { push @installed, $pkg; } elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) { print FAILED "$pkg\n"; } } close FAILED if $args{do_once}; return @installed; } sub _install_cpanplus { my @modules = @{ +shift }; my @config = _cpanplus_config( @{ +shift } ); my $installed = 0; require CPANPLUS::Backend; my $cp = CPANPLUS::Backend->new; my $conf = $cp->configure_object; return unless $conf->can('conf') # 0.05x+ with "sudo" support or _can_write($conf->_get_build('base')); # 0.04x # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $conf->get_conf('makeflags') || ''; if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) { # 0.03+ uses a hashref here $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; } else { # 0.02 and below uses a scalar $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); } $conf->set_conf( makeflags => $makeflags ); $conf->set_conf( prereqs => 1 ); while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) { $conf->set_conf( $key, $val ); } my $modtree = $cp->module_tree; while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { print "*** Installing $pkg...\n"; MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; my $success; my $obj = $modtree->{$pkg}; if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { delete $INC{$inc}; } my $rv = $cp->install( modules => [ $obj->{module} ] ); if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation cancelled.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; } return $installed; } sub _cpanplus_config { my @config = (); while ( @_ ) { my ($key, $value) = (shift(), shift()); if ( $key eq 'prerequisites_policy' ) { if ( $value eq 'follow' ) { $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL(); } elsif ( $value eq 'ask' ) { $value = CPANPLUS::Internals::Constants::PREREQ_ASK(); } elsif ( $value eq 'ignore' ) { $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE(); } else { die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n"; } } else { die "*** Cannot convert option $key to CPANPLUS version.\n"; } } return @config; } sub _install_cpan { my @modules = @{ +shift }; my @config = @{ +shift }; my $installed = 0; my %args; _load_cpan(); require Config; if (CPAN->VERSION < 1.80) { # no "sudo" support, probe for writableness return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) ) and _can_write( $Config::Config{sitelib} ); } # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $CPAN::Config->{make_install_arg} || ''; $CPAN::Config->{make_install_arg} = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); # don't show start-up info $CPAN::Config->{inhibit_startup_message} = 1; # set additional options while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) { ( $args{$opt} = $arg, next ) if $opt =~ /^force$/; # pseudo-option $CPAN::Config->{$opt} = $arg; } local $CPAN::Config->{prerequisites_policy} = 'follow'; while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; print "*** Installing $pkg...\n"; my $obj = CPAN::Shell->expand( Module => $pkg ); my $success = 0; if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { delete $INC{$inc}; } my $rv = $args{force} ? CPAN::Shell->force( install => $pkg ) : CPAN::Shell->install($pkg); $rv ||= eval { $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, ) ->{install} if $CPAN::META; }; if ( $rv eq 'YES' ) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation failed.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; } return $installed; } sub _has_cpanplus { return ( $HasCPANPLUS = ( $INC{'CPANPLUS/Config.pm'} or _load('CPANPLUS::Shell::Default') ) ); } # make guesses on whether we're under the CPAN installation directory sub _under_cpan { require Cwd; require File::Spec; my $cwd = File::Spec->canonpath( Cwd::cwd() ); my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} ); return ( index( $cwd, $cpan ) > -1 ); } sub _update_to { my $class = __PACKAGE__; my $ver = shift; return if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade if ( _prompt( "==> A newer version of $class ($ver) is required. Install?", 'y' ) =~ /^[Nn]/ ) { die "*** Please install $class $ver manually.\n"; } print << "."; *** Trying to fetch it from CPAN... . # install ourselves _load($class) and return $class->import(@_) if $class->install( [], $class, $ver ); print << '.'; exit 1; *** Cannot bootstrap myself. :-( Installation terminated. . } # check if we're connected to some host, using inet_aton sub _connected_to { my $site = shift; return ( ( _load('Socket') and Socket::inet_aton($site) ) or _prompt( qq( *** Your host cannot resolve the domain name '$site', which probably means the Internet connections are unavailable. ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/ ); } # check if a directory is writable; may create it on demand sub _can_write { my $path = shift; mkdir( $path, 0755 ) unless -e $path; return 1 if -w $path; print << "."; *** You are not allowed to write to the directory '$path'; the installation may fail due to insufficient permissions. . if ( eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt( qq( ==> Should we try to re-execute the autoinstall process with 'sudo'?), ((-t STDIN) ? 'y' : 'n') ) =~ /^[Yy]/ ) { # try to bootstrap ourselves from sudo print << "."; *** Trying to re-execute the autoinstall process with 'sudo'... . my $missing = join( ',', @Missing ); my $config = join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) if $Config; return unless system( 'sudo', $^X, $0, "--config=$config", "--installdeps=$missing" ); print << "."; *** The 'sudo' command exited with error! Resuming... . } return _prompt( qq( ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/; } # load a module and return the version it reports sub _load { my $mod = pop; # class/instance doesn't matter my $file = $mod; $file =~ s|::|/|g; $file .= '.pm'; local $@; return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 ); } # Load CPAN.pm and it's configuration sub _load_cpan { return if $CPAN::VERSION and $CPAN::Config and not @_; require CPAN; # CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to # CPAN::HandleConfig->load. CPAN reports that the redirection # is deprecated in a warning printed at the user. # CPAN-1.81 expects CPAN::HandleConfig->load, does not have # $CPAN::HandleConfig::VERSION but cannot handle # CPAN::Config->load # Which "versions expect CPAN::Config->load? if ( $CPAN::HandleConfig::VERSION || CPAN::HandleConfig->can('load') ) { # Newer versions of CPAN have a HandleConfig module CPAN::HandleConfig->load; } else { # Older versions had the load method in Config directly CPAN::Config->load; } } # compare two versions, either use Sort::Versions or plain comparison # return values same as <=> sub _version_cmp { my ( $cur, $min ) = @_; return -1 unless defined $cur; # if 0 keep comparing return 1 unless $min; $cur =~ s/\s+$//; # check for version numbers that are not in decimal format if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) { if ( ( $version::VERSION or defined( _load('version') )) and version->can('new') ) { # use version.pm if it is installed. return version->new($cur) <=> version->new($min); } elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) ) { # use Sort::Versions as the sorting algorithm for a.b.c versions return Sort::Versions::versioncmp( $cur, $min ); } warn "Cannot reliably compare non-decimal formatted versions.\n" . "Please install version.pm or Sort::Versions.\n"; } # plain comparison local $^W = 0; # shuts off 'not numeric' bugs return $cur <=> $min; } # nothing; this usage is deprecated. sub main::PREREQ_PM { return {}; } sub _make_args { my %args = @_; $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing } if $UnderCPAN or $TestOnly; if ( $args{EXE_FILES} and -e 'MANIFEST' ) { require ExtUtils::Manifest; my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); $args{EXE_FILES} = [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ]; } $args{test}{TESTS} ||= 't/*.t'; $args{test}{TESTS} = join( ' ', grep { !exists( $DisabledTests{$_} ) } map { glob($_) } split( /\s+/, $args{test}{TESTS} ) ); my $missing = join( ',', @Missing ); my $config = join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) if $Config; $PostambleActions = ( ($missing and not $UnderCPAN) ? "\$(PERL) $0 --config=$config --installdeps=$missing" : "\$(NOECHO) \$(NOOP)" ); return %args; } # a wrapper to ExtUtils::MakeMaker::WriteMakefile sub Write { require Carp; Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; if ($CheckOnly) { print << "."; *** Makefile not written in check-only mode. . return; } my %args = _make_args(@_); no strict 'refs'; $PostambleUsed = 0; local *MY::postamble = \&postamble unless defined &MY::postamble; ExtUtils::MakeMaker::WriteMakefile(%args); print << "." unless $PostambleUsed; *** WARNING: Makefile written with customized MY::postamble() without including contents from Module::AutoInstall::postamble() -- auto installation features disabled. Please contact the author. . return 1; } sub postamble { $PostambleUsed = 1; return <<"END_MAKE"; config :: installdeps \t\$(NOECHO) \$(NOOP) checkdeps :: \t\$(PERL) $0 --checkdeps installdeps :: \t$PostambleActions END_MAKE } 1; __END__ #line 1071 Atompub-0.3.7/inc/Module/Install/000755 000765 000024 00000000000 11737440572 017030 5ustar00inouestaff000000 000000 Atompub-0.3.7/inc/Module/Install.pm000644 000765 000024 00000030135 11737440437 017370 0ustar00inouestaff000000 000000 #line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.005; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.01'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::cwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::cwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split //, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_NEW sub _read { local *FH; open( FH, "< $_[0]" ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_OLD sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_NEW sub _write { local *FH; open( FH, "> $_[0]" ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_OLD # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version ($) { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp ($$) { _version($_[0]) <=> _version($_[1]); } # Cloned from Params::Util::_CLASS sub _CLASS ($) { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2011 Adam Kennedy. Atompub-0.3.7/inc/Module/Install/AutoInstall.pm000644 000765 000024 00000003632 11737440437 021631 0ustar00inouestaff000000 000000 #line 1 package Module::Install::AutoInstall; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub AutoInstall { $_[0] } sub run { my $self = shift; $self->auto_install_now(@_); } sub write { my $self = shift; $self->auto_install(@_); } sub auto_install { my $self = shift; return if $self->{done}++; # Flatten array of arrays into a single array my @core = map @$_, map @$_, grep ref, $self->build_requires, $self->requires; my @config = @_; # We'll need Module::AutoInstall $self->include('Module::AutoInstall'); require Module::AutoInstall; my @features_require = Module::AutoInstall->import( (@config ? (-config => \@config) : ()), (@core ? (-core => \@core) : ()), $self->features, ); my %seen; my @requires = map @$_, map @$_, grep ref, $self->requires; while (my ($mod, $ver) = splice(@requires, 0, 2)) { $seen{$mod}{$ver}++; } my @build_requires = map @$_, map @$_, grep ref, $self->build_requires; while (my ($mod, $ver) = splice(@build_requires, 0, 2)) { $seen{$mod}{$ver}++; } my @configure_requires = map @$_, map @$_, grep ref, $self->configure_requires; while (my ($mod, $ver) = splice(@configure_requires, 0, 2)) { $seen{$mod}{$ver}++; } my @deduped; while (my ($mod, $ver) = splice(@features_require, 0, 2)) { push @deduped, $mod => $ver unless $seen{$mod}{$ver}++; } $self->requires(@deduped); $self->makemaker_args( Module::AutoInstall::_make_args() ); my $class = ref($self); $self->postamble( "# --- $class section:\n" . Module::AutoInstall::postamble() ); } sub auto_install_now { my $self = shift; $self->auto_install(@_); Module::AutoInstall::do_install(); } 1; Atompub-0.3.7/inc/Module/Install/Base.pm000644 000765 000024 00000002147 11737440437 020244 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.01'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 Atompub-0.3.7/inc/Module/Install/Can.pm000644 000765 000024 00000003333 11737440442 020065 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Can; use strict; use Config (); use File::Spec (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; my $abs = File::Spec->catfile($dir, $_[1]); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 156 Atompub-0.3.7/inc/Module/Install/Fetch.pm000644 000765 000024 00000004627 11737440442 020424 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; Atompub-0.3.7/inc/Module/Install/Include.pm000644 000765 000024 00000001015 11737440437 020746 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Include; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub include { shift()->admin->include(@_); } sub include_deps { shift()->admin->include_deps(@_); } sub auto_include { shift()->admin->auto_include(@_); } sub auto_include_deps { shift()->admin->auto_include_deps(@_); } sub auto_include_dependent_dists { shift()->admin->auto_include_dependent_dists(@_); } 1; Atompub-0.3.7/inc/Module/Install/Makefile.pm000644 000765 000024 00000027032 11737440437 021107 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-seperated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # MakeMaker can complain about module versions that include # an underscore, even though its own version may contain one! # Hence the funny regexp to get rid of it. See RT #35800 # for details. my $v = $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/; $self->build_requires( 'ExtUtils::MakeMaker' => $v ); $self->configure_requires( 'ExtUtils::MakeMaker' => $v ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.42 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.42 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT $DB::single = 1; if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 541 Atompub-0.3.7/inc/Module/Install/Metadata.pm000644 000765 000024 00000043123 11737440437 021111 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; unless ( @_ ) { warn "You MUST provide an explicit true/false value to dynamic_config\n"; return $self; } $self->{values}->{dynamic_config} = $_[0] ? 1 : 0; return 1; } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the reall old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) \s* ; /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashs delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; Atompub-0.3.7/inc/Module/Install/Win32.pm000644 000765 000024 00000003403 11737440442 020264 0ustar00inouestaff000000 000000 #line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; Atompub-0.3.7/inc/Module/Install/WriteAll.pm000644 000765 000024 00000002376 11737440441 021114 0ustar00inouestaff000000 000000 #line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.01'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1;