The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:
#
use strict;

package Rex::Repositorio::Server::Yum::File;

use Mojo::Base 'Mojolicious::Controller';
use File::Spec;
use File::Path 'make_path';
use File::Basename qw'dirname';
use Mojo::UserAgent;

our $VERSION = '1.2.1'; # VERSION

sub serve {
  my ($self) = @_;

  my $file = $self->req->url;
  $self->app->log->debug("Serving: $file");

  #  my $repo_dir = $self->app->get_repo_dir( repo => $self->repo->{name} );
  my $repo_dir = File::Spec->rel2abs( $self->config->{RepositoryRoot} );

  $self->app->log->debug("Path: $repo_dir");

  my $serve_dir = File::Spec->catdir( $repo_dir, $file );
  $self->app->log->debug("Serving URL: $serve_dir");

  my $orig_url   = $file;
  my $local_part = $self->repo->{local};
  my $repo_url   = $self->repo->{url};

  if ($repo_url) {
    $repo_url =~ s/\/$//;

    $orig_url =~ s/^\/([^\/]+)\/\Q$local_part\E//;

    if ( $repo_url !~ m/\/$/ ) {
      $repo_url .= "/";
    }

    $orig_url = $repo_url . $orig_url;

    $self->app->log->debug("Orig-URL: $orig_url");
  }

  my $do_proxy = lc( $self->repo->{proxy} || "false" );

  if ( -d $serve_dir ) {
    my @entries;
    opendir( my $dh, $serve_dir ) or die($!);
    while ( my $entry = readdir($dh) ) {
      next if ( $entry =~ m/^\./ );
      push @entries,
        {
        name => $entry,
        file => ( -f File::Spec->catfile( $serve_dir, $entry ) ),
        };
    }
    closedir($dh);

    @entries =
      sort { "$a->{file}-$a->{name}" cmp "$b->{file}-$b->{name}" } @entries;

    $self->stash( path    => $file );
    $self->stash( entries => \@entries );

    $self->render("file/serve");
  }
  elsif ( -f $serve_dir ) {
    $self->app->log->debug("File-Download: $serve_dir");

    if ( -f "$serve_dir.etag" && ( $do_proxy eq "true" || $do_proxy eq "on" ) )
    {

      # there is an etag file, so the file was downloaded via proxy
      # check upstream if file is out-of-date
      my ($etag) = eval { local ( @ARGV, $/ ) = ("$serve_dir.etag"); <>; };
      $self->app->log->debug(
        "Making ETag request to upstream ($orig_url): ETag: $etag.");
      my $tx = $self->ua->head( $orig_url,
        { Accept => '*/*', 'If-None-Match' => $etag } );
      if ( $tx->success ) {
        my $upstream = $tx->res->headers->header('ETag');
        $upstream =~ s/"//g;
        if ( $upstream ne $etag ) {
          $self->app->log->debug(
            "Upstream ETag and local ETag does not match: $upstream != $etag");
          return $self->_proxy_url( $orig_url, $serve_dir );
        }
        else {
          $self->app->log->debug(
            "Upstream ETag and local ETag are the same: $upstream == $etag");
        }
      }
    }

    return $self->render_file( filepath => $serve_dir );
  }
  elsif ( $do_proxy eq "true" || $do_proxy eq "on" ) {
    $self->app->log->debug("Need to get file from upstream: $orig_url");
    return $self->_proxy_url( $orig_url, $serve_dir );
  }
  else {
    $self->render( text => "Not found", status => 404 );
  }
}

sub index {
  my ($self) = @_;

  my $repo_dir = File::Spec->rel2abs( $self->config->{RepositoryRoot} );

  # get tags
  opendir( my $dh, $repo_dir ) or die($!);
  my @tags;
  while ( my $entry = readdir($dh) ) {
    next if ( $entry =~ m/^\./ );
    if ( -d File::Spec->catdir( $repo_dir, $entry, $self->repo->{name} ) ) {
      push @tags, $entry;
    }
  }
  closedir($dh);

  $self->stash( "path", "/" );
  $self->stash( "tags", \@tags );
  $self->stash( repo_name => $self->repo->{name} );

  $self->render("file/index");
}

sub _proxy_url {
  my ( $self, $orig_url, $serve_dir ) = @_;

  return $self->proxy_to(
    $orig_url,
    sub {
      my ( $c, $tx ) = @_;
      $c->app->log->debug("Got data from upstream ($orig_url)...");
      make_path( dirname($serve_dir) );
      open my $fh, '>', $serve_dir or die($!);
      binmode $fh;
      print $fh $tx->res->body;
      close $fh;

      my $etag = $tx->res->headers->header('ETag') || '';
      if($etag) {
        $etag =~ s/"//g;
        open my $fh_e, '>', "$serve_dir.etag" or die($!);
        print $fh_e $etag;
        close $fh_e;
      }
    }
  );
}

1;