#!/usr/bin/env perl

use strict;
use warnings;

# ABSTRACT: Share files over HTTP easily and conveniently

# PODNAME: charon

use Getopt::Long::Descriptive;
use IO::Async::Loop;
use Net::Async::HTTP::Server::PSGI;
use IO::All;
use Future;
use IO::Socket::IP;

use App::Charon::Web;
use App::Charon::ConfigLoader;
my $c = App::Charon::ConfigLoader->new(
   env_key => 'CHARON',
   config_class => 'App::Charon::Config',
   location => '.charonrc',
)->load;

my ($opt, $usage) = describe_options(
  'charon %o [file|dir]',
  [ 'listen|l=s',   "Listens on an address, whether HOST:PORT or :PORT",
     $c->opt_default_for('listen')],
  [ 'autoshutdown=s', 'Shutdown after a given time period (example: 1h30m)',
     $c->opt_default_for('autoshutdown')],
  [ 'query-param-auth=s', 'Use query param auth.  Eg: pass=station will require ?pass=station in requests',
     $c->opt_default_for('query_param_auth')],
  [ 'index!', 'Show index', $c->opt_default_for('index', { default => 1 })],
  [],
  [ 'help|h',       "print usage message and exit" ],
  { show_defaults => 1 },
);

print($usage->text), exit if $opt->help;

my $loop = IO::Async::Loop->new;

my $quit = $loop->new_future;

my $app = App::Charon::Web->new(
   quit => $quit,
   (
      $ARGV[0] ? (root => $ARGV[0]) : ()
   ),
   (
      defined $opt->query_param_auth
         ? (query_param_auth => $opt->query_param_auth)
         : ()
   ),
   show_index => $opt->index,
);
my $handler = Net::Async::HTTP::Server::PSGI->new(
   app => $app->to_psgi_app,
);
$loop->add($handler);

my $host = '0.0.0.0';
my $port;

if ($opt->listen and ($host, $port) = $opt->listen =~ m/^(.*):(\d+)$/) {
   $host = '0.0.0.0' if $host eq '*' || $host eq '';
}

my $sock;

if ($port) {
   $sock = IO::Socket::IP->new(
      LocalHost => $host,
      LocalService => $port,
      Listen => 1,
   ) or die "could not listen on $host:$port: $!\n";
} else {
   for my $port (3000..9999) {
      last if $sock = IO::Socket::IP->new(
         LocalHost => $host,
         LocalService => $port,
         Listen => 1,
      )
   }
   die "could not listen on any port!\n" unless $sock;
}

$handler->set_handle($sock);

warn 'serving ' . io->file($app->_root)->rel2abs . ' on ' .
   $sock->sockhost . ':' .
   $sock->sockport .
   (
      @{$app->_query_param_auth}
         ? ' with a query param auth of ?'.
            $app->_query_param_auth->[0] . '=' .
            $app->_query_param_auth->[1]
         : ''
   ) .
   "\n";

if (my $t = $opt->autoshutdown) {
   require Time::Duration::Parse;
   $quit = Future->wait_any(
      $quit,
      $loop->delay_future(
         after => Time::Duration::Parse::parse_duration($t),
      ),
   );
}

$quit->get;

__END__

=pod

=encoding UTF-8

=head1 NAME

charon - Share files over HTTP easily and conveniently

=head1 VERSION

version 0.001003

=head1 SYNOPSIS

Start up C<charon>:

 $ charon
 serving /home/frew on 0.0.0.0:3000 with a query param auth of ?auth=br5Y2jX55

Now you can navigate to C<http://127.0.0.1:3000/?auth=br5Y2jX55> and download
files under C</home/frew>.

Here's an example of using it with all the switches:

 charon ~ --listen *:8080 --autoshutdown 1h --query-param-auth '' --no-index

=head1 DESCRIPTION

 Charon is the ferryman of Hades who carries souls of the newly deceased across
 the rivers Styx and Acheron that divided the world of the living from the world
 of the dead

C<charon> is a tool for easily sharing files.  I have found that at work I often
have trouble ensuring all the various permissions are correct and all the
various machines have the right tools and whatnot to serve files from one
machine to another.  C<charon> helps with this by making some of what used to be
Plack oneliners into a much more robust and convenient tool.

=head1 CONFIGURATION

C<charon> uses
L<a configuration system I developed|https://blog.afoolishmanifesto.com/posts/configuration-station/>
that allows the user to use either a file, or environment variables, or both.
Currently all the configuration allows you to do is set defaults for the
commandline options.  Basically it works like this:

=over 3

=item * if C<CHARON_$FOO> is set, use that to set C<foo>

=item * if C<$foo> exists in the config file, use that to set C<foo>

=back

The location of the config file is similarly dynamic.  The default is just
F<.charonrc> in the current directory.  The location can be overridden by
setting C<CHARON_CONFLOC>.  Currently the configuration file is L<JSONY>.

So to be clear, if you wanted to set a default for C<--query-param-auth>, you
can either:

=over 3

=item 1. Set C<CHARON_QUERY_PARAM_AUTH=password=station>

=item 2. Put a line like C< query_param_auth: password=station > in F<.charonrc>

=item 3. Set C<CHARON_CONFLOC> to F<~/.charonrc> and add a line like the above to that file.

=back

=head1 COMMANDLINE OPTIONS

=head2 single positional argument

 charon ~/code/DBIx-Class-Candy

The one and only positional argument to C<charon> is the directory or file to
serve.  The default is the current directory, but you can pass another directory
or even file if you want to serve something else.

=head2 --listen -l

 charon --listen *:5000

 charon -l 10.6.1.6:9090

If you want to choose the port and IP to listen on use this option.  Currently
only one C<--listen> is supported, but I would be glad to add support for
multiple in the future.

=head2 --autoshutdown

 charon --autoshutdown 30m

 charon --autoshutdown 1h3m20s

C<--autoshutdown> requires a single argument that expresses a duration.  The
point is so that you can timebox your file sharing and then after the duration
it will just go away, instead of you forgetting to shut it down and leaving it
running forever.

=head2 --query-param-auth

 charon --query-param-auth 'password=station'

 charon --query-param-auth ''

Query param auth is just a handy way to be able to send moderately authenticated
links to your friends.  This is on by default and generates a password with the
key of C<auth>, so links will be C<http://foo:3001/bar.tar.gz?auth=bd75z4j>.  If
you want to choose the key and value pass C<$key=$value>, so if you pass
C<password=42> you could use the link C<http://foo:3001/bar.tar.gz?password=42>.

Note that while this is on by default I may decide to change that in the future.

=head2 --index --no-index

 charon --no-index

This option is on by default, and allows the client to get an html based
directory listing.  You might turn this off if you want to have a bit more
security-through-obscurity to allow only sharing files you give people links to.

=head2 --help -h

 charon -h

Use C<--help> to get a brief listing of the options.

=head1 AUTHOR

Arthur Axel "fREW" Schmidt <frioux+cpan@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Arthur Axel "fREW" Schmidt.

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

=cut
