Blob Blame History Raw
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lufi;
use Mojo::Base 'Mojolicious';
use LufiDB;

$ENV{MOJO_MAX_WEBSOCKET_SIZE} = 100485760; # 10 * 1024 * 1024 = 10MiB

# This method will run once at server start
sub startup {
    my $self = shift;

    my $config = $self->plugin('Config' => {
        default =>  {
            provisioning => 100,
            provis_step   => 5,
            length        => 10,
            token_length  => 32,
            secret        => ['hfudsifdsih'],
            default_delay => 0,
            max_delay     => 0
        }
    });

    die "You need to provide a contact information in lufi.conf!" unless (defined($config->{contact}));

    # Internationalization
    $self->plugin('I18N');

    # Debug
    $self->plugin('DebugDumperHelper');

    $self->secrets($config->{secrets});

    # Helpers
    $self->helper(
        provisioning => sub {
            my $c = shift;

            # Create some short patterns for provisioning
            if (LufiDB::Files->count('WHERE created_at IS NULL') < $c->config('provisioning')) {
                for (my $i = 0; $i < $c->config('provis_step'); $i++) {
                    if (LufiDB->begin) {
                        my $short;
                        do {
                            $short= $c->shortener($c->config('length'));
                        } while (LufiDB::Files->count('WHERE short = ?', $short));

                        LufiDB::Files->create(
                            short => $short
                        );
                        LufiDB->commit;
                    }
                }
            }
        }
    );

    $self->helper(
        get_empty => sub {
            my $c =  shift;

            my @records = LufiDB::Files->select('WHERE created_at IS NULL LIMIT 1');
            return $records[0];
        }
    );

    $self->helper(
        shortener => sub {
            my $c      = shift;
            my $length = shift;

            my @chars  = ('a'..'z','A'..'Z','0'..'9', '-', '_');
            my $result = '';
            foreach (1..$length) {
                $result .= $chars[rand scalar(@chars)];
            }
            return $result;
        }
    );

    $self->helper(
        ip => sub {
            my $c           = shift;
            my $proxy       = $c->req->headers->header('X-Forwarded-For');
            my $ip          = ($proxy) ? $proxy : $c->tx->remote_address;
            my $remote_port = (defined($c->req->headers->header('X-Remote-Port'))) ? $c->req->headers->header('X-Remote-Port') : $c->tx->remote_port;

            return "$ip remote port:$remote_port";
        }
    );

    $self->helper(
        default_delay => sub {
            my $c = shift;

            return $c->config('default_delay') if ($c->config('default_delay') >= 0);

            warn "default_delay set to a negative value. Default to 0.";
            return 0;
        }
    );

    $self->helper(
        is_selected => sub {
            my $c   = shift;
            my $num = shift;

            return ($num == $c->default_delay) ? 'selected="selected"' : '';
        }
    );

    # Hooks
    $self->hook(
        after_dispatch => sub {
            shift->provisioning();
        }
    );

    # For the first launch (after, this isn't really useful)
    $self->provisioning();

    # Create directory if needed
    mkdir('files', 0700) unless (-d 'files');

    # Default layout
    $self->defaults(layout => 'default');

    # Router
    my $r = $self->routes;

    # Page for files uploading
    $r->get('/' => sub {
        shift->render(template => 'index');
    })->name('index');

    # Get a file
    $r->get('/r/:short')
        ->to('Files#r')
        ->name('render');

    # List of files (use localstorage, so the server know nothing about files
    $r->get('/files' => sub {
        shift->render(template => 'files');
    })->name('files');

    # Get counter informations about a file
    $r->post('/c')->
        to('Files#get_counter')->
        name('counter');

    # Get counter informations about a file
    $r->get('/d/:short/:token')->
        to('Files#delete')->
        name('delete');

    # About page
    $r->get('/about' => sub {
        shift->render(template => 'about');
    })->name('about');

    # Upload files websocket
    $r->websocket('/upload')
        ->to('Files#upload')
        ->name('upload');

    # Get files websocket
    $r->websocket('/download/:short')
        ->to('Files#download')
        ->name('download');
}

1;