
=head1 NAME

Archive::Any - Single interface to deal with file archives.

=head1 SYNOPSIS

  use Archive::Any;

  my $archive = Archive::Any->new($archive_file);

  my @files = $archive->files;

  $archive->extract;

  my $type = $archive->type;

  $archive->is_impolite;
  $archive->is_naughty;

=head1 DESCRIPTION

This module is a single interface for manipulating different archive formats.  Tarballs, zip files, etc.

=over 4

=item B<new>

  my $archive = Archive::Any->new($archive_file);
  my $archive = Archive::Any->new($archive_file, $type);

$type is optional.  It lets you force the file type in-case Archive::Any can't figure it out.

=item B<extract>

  $archive->extract;
  $archive->extract($directory);

Extracts the files in the archive to the given $directory.  If no $directory is given, it will go into the current working directory.

=item B<files>

  my @file = $archive->files;

A list of files in the archive.

=item B<mime_type>

 my $mime_type = $archive->mime_type();

Returns the mime type of the archive.

=item B<is_impolite>

  my $is_impolite = $archive->is_impolite;

Checks to see if this archive is going to unpack into the current directory rather than create its own.

=item B<is_naughty>

  my $is_naughty = $archive->is_naughty;

Checks to see if this archive is going to unpack B<outside> the current directory.

=back

=head1 DEPRECATED

=over 4

=item B<type>

  my $type = $archive->type;

Returns the type of archive.  This method is provided for backwards compatibility in the Tar and Zip plugins and will be going away B<soon> in favor of C<mime_type>.

=back

=head1 PLUGINS

For detailed information on writing plugins to work with Archive::Any, please see the pod documentation for L<Archive::Any::Plugin>.

=head1 AUTHOR

Clint Moore E<lt>cmoore@cpan.orgE<gt>

=head1 AUTHOR EMERITUS

Michael G Schwern

=head1 SEE ALSO

Archive::Any::Plugin

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

 perldoc Archive::Any

You can also look for information at:

=over 4

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Archive-Any>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Archive-Any>

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Archive-Any>

=item * Search CPAN

L<http://search.cpan.org/dist/Archive-Any>

=back

=head1 LICENSE

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See L<http://www.perl.com/perl/misc/Artistic.html>

=cut

package Archive::Any;
use strict;
use warnings;

use vars qw($VERSION);
$VERSION = 0.0932;

use Archive::Any::Plugin;
use File::Spec::Functions qw( rel2abs splitdir );
use File::MMagic;
use MIME::Types qw(by_suffix);

sub new {
    my ( $class, $file, $type ) = @_;

    $file = rel2abs($file);
    return unless -f $file;

    my %available;

    my @plugins = Archive::Any::Plugin->findsubmod;
    foreach my $plugin (@plugins) {
        eval "require $plugin";
        next if $@;

        my @types = $plugin->can_handle();
        foreach my $type ( @types ) {
            next if exists( $available{$type} );
            $available{$type} = $plugin;
        }
    }

    my $mime_type;

    if ($type) {
        # The user forced the type.
        ($mime_type) = by_suffix($type);
        unless( $mime_type ) {
            warn "No mime type found for type '$type'";
            return;
        }
    } else {
        # Autodetect the type.
        $mime_type = File::MMagic->new()->checktype_filename($file);
    }

    my $handler = $available{$mime_type};
    if( ! $handler ) {
        warn "No handler available for type '$mime_type'";
        return;
    }

    return bless {
        file    => $file,
        handler => $handler,
        type => $mime_type,
    }, $class;
}

sub extract {
    my $self = shift;
    my $dir  = shift;

    return defined($dir)
      ? $self->{handler}->_extract( $self->{file}, $dir )
      : $self->{handler}->_extract( $self->{file} );
}

sub files {
    my $self = shift;
    return $self->{handler}->files( $self->{file} );
}

sub is_impolite {
    my $self = shift;

    my @files       = $self->files;
    my $first_file  = $files[0];
    my ($first_dir) = splitdir($first_file);

    return grep( !/^\Q$first_dir\E/, @files ) ? 1 : 0;
}

sub is_naughty {
    my ($self) = shift;
    return ( grep { m{^(?:/|(?:\./)*\.\./)} } $self->files ) ? 1 : 0;
}

sub mime_type {
    my $self = shift;
    return $self->{type};
}

#
# This is not really here.  You are not seeing this.
#
sub type {
    my $self = shift;
    return $self->{handler}->type();
}
# End of what you are not seeing.

1;
