include $(s)/utils/Makefile
include $(s)/eval/Makefile
include $(s)/judge/Makefile
+include $(s)/submit/Makefile
# And finally the default rules of the build system
include $(s)/build/Makebottom
+++ /dev/null
-# A Perl module for communicating with the MO Submit Server
-# (c) 2007 Martin Mares <mj@ucw.cz>
-
-package MO::Submit;
-
-use strict;
-use warnings;
-
-use IO::Socket::INET;
-use IO::Socket::SSL; # qw(debug3);
-use Sherlock::Object;
-use POSIX;
-
-sub new($) {
- my $user = $ENV{"USER"} or die "Environment variable USER not set\n";
- my $home = $ENV{"HOME"} or die "Environment variable HOME not set\n";
- my $mo = "$home/.mo";
- my $root = $ENV{"MO_ROOT"} or die "Environment variable MO_ROOT not set\n";
- my $self = {
- "Contest" => "CEOI 2007",
- "Server" => "ceoi-gamma:8888",
- "Key" => "$mo/key.pem", # Keys and certificates
- "Cert" => "$mo/cert.pem",
- "CACert" => "$mo/ca-cert.pem",
- "Trace" => defined $ENV{"MO_SUBMIT_TRACE"},
- "Checks" => 1, # Run `check' before submitting
- "AllowOverride" => 1, # Allow overriding a failed check
- "History" => "$home/.history", # Keep submission history in this directory
- "RefreshTimer" => 60000, # How often GUI sends STATUS commands [ms]
- "root" => $root,
- "user" => $user,
- "sk" => undef,
- "error" => undef,
- };
- return bless $self;
-}
-
-sub DESTROY($) {
- my $self = shift @_;
- $self->disconnect;
-}
-
-sub log($$) {
- my ($self, $msg) = @_;
- print STDERR "SUBMIT: $msg\n" if $self->{"Trace"};
-}
-
-sub err($$) {
- my ($self, $msg) = @_;
- print STDERR "ERROR: $msg\n" if $self->{"Trace"};
- $self->{"error"} = $msg;
- $self->disconnect;
-}
-
-sub is_connected($) {
- my $self = shift @_;
- return defined $self->{"sk"};
-}
-
-sub disconnect($) {
- my $self = shift @_;
- if ($self->is_connected) {
- close $self->{"sk"};
- $self->{"sk"} = undef;
- $self->log("Disconnected");
- }
-}
-
-sub connect($) {
- my $self = shift @_;
- $self->disconnect;
-
- $self->log("Connecting to submit server");
- my $sk = new IO::Socket::INET(
- PeerAddr => $self->{"Server"},
- Proto => "tcp",
- );
- if (!defined $sk) {
- $self->err("Cannot connect to server: $!");
- return undef;
- }
- my $z = <$sk>;
- if (!defined $z) {
- $self->err("Server failed to send a welcome message");
- close $sk;
- return undef;
- }
- chomp $z;
- if ($z !~ /^\+/) {
- $self->err("Server rejected the connection: $z");
- close $sk;
- return undef;
- }
- if ($z =~ /TLS/) {
- $self->log("Starting TLS");
- $sk = IO::Socket::SSL->start_SSL(
- $sk,
- SSL_version => 'TLSv1',
- SSL_use_cert => 1,
- SSL_key_file => $self->{"Key"},
- SSL_cert_file => $self->{"Cert"},
- SSL_ca_file => $self->{"CACert"},
- SSL_verify_mode => 3,
- );
- if (!defined $sk) {
- $self->err("Cannot establish TLS connection: " . IO::Socket::SSL::errstr());
- return undef;
- }
- }
- $self->{"sk"} = $sk;
- $sk->autoflush(0);
-
- $self->log("Logging in");
- my $req = new Sherlock::Object("U" => $self->{"user"});
- my $reply = $self->request($req);
- my $err = $reply->get("-");
- if (defined $err) {
- $self->err("Cannot log in: $err");
- return undef;
- }
-
- $self->log("Connected");
- return 1;
-}
-
-sub request($$) {
- my ($self, $obj) = @_;
- my $sk = $self->{"sk"};
- local $SIG{'PIPE'} = 'ignore';
- $obj->write($sk);
- print $sk "\n";
- $sk->flush();
- if ($sk->error) {
- $self->err("Connection broken");
- return undef;
- }
- return $self->reply;
-}
-
-sub reply($) {
- my ($self, $obj) = @_;
- my $sk = $self->{"sk"};
- my $reply = new Sherlock::Object;
- if ($reply->read($sk)) {
- return $reply;
- } else {
- $self->err("Connection broken");
- return undef;
- }
-}
-
-sub send_file($$$) {
- my ($self, $fh, $size) = @_;
- my $sk = $self->{"sk"};
- local $SIG{'PIPE'} = 'ignore';
- while ($size) {
- my $l = ($size < 4096 ? $size : 4096);
- my $buf = "";
- if ($fh->read($buf, $l) != $l) {
- $self->err("File shrunk during upload");
- return undef;
- }
- $sk->write($buf, $l);
- if ($sk->error) {
- $self->err("Connection broken");
- return undef;
- }
- $size -= $l;
- }
- return $self->reply;
-}
-
-sub write_history($$$$$) {
- my ($self, $task, $part, $ext, $filename) = @_;
- my $hist = $self->{"History"};
- -d $hist or mkdir $hist or return "Unable to create $hist: $!";
- my $now = POSIX::strftime("%H:%M:%S", localtime(time));
- my $maybe_part = ($part eq $task) ? "" : ":$part";
- my $name = "$hist/$now-$task$maybe_part.$ext";
- $self->log("Backing up to $name");
- `cp "$filename" "$name"`;
- return "Unable to back up $filename as $name" if $?;
- return undef;
-}
-
-1;
-UCWCF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --cflags libucw libsh)
-UCWLF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --libs libucw libsh)
+# Makefile for MO-Eval submitter
+# (c) 2008 Martin Mares <mj@ucw.cz>
+
TLSCF:=$(shell libgnutls-config --cflags)
TLSLF:=$(shell libgnutls-config --libs)
GCRCF:=$(shell libgcrypt-config --cflags)
-CFLAGS+=$(UCWCF) $(TLSCF) $(GCRCF)
-LDLIBS+=$(UCWLF) $(TLSLF)
+DIRS+=submit
+SDIR=$(o)/submit
+
+### The submit server ###
+
+PROGS+=$(addprefix $(SDIR)/,submitd privkey connect)
+
+$(SHERLOCK_PERL_MODS): PERL_MODULE_DIR=MO
+
+$(SDIR)/submitd: $(addprefix $(SDIR)/,submitd.o commands.o tasks.o) $(LIBUCW) $(LIBSH)
+$(SDIR)/submitd: CFLAGS+=$(TLSCF) $(GCRCF)
+$(SDIR)/submitd: LIBS+=$(TLSLF)
+
+$(SDIR)/connect: $(SDIR)/connect.o $(LIBUCW)
+$(SDIR)/connect: CFLAGS+=$(TLSCF) $(GCRCF)
+$(SDIR)/connect: LIBS+=$(TLSLF)
+
+$(SDIR)/privkey: $(SDIR)/privkey.o $(LIBUCW)
+$(SDIR)/privkey: CFLAGS+=$(TLSCF) $(GCRCF)
+$(SDIR)/privkey: LIBS+=$(TLSLF)
+
+### Submitter perl module ###
-all: submitd privkey lib
+EXTRA_RUNDIRS+=lib/perl5/MO
+SUBMIT_PERL_MODS=$(o)/submit/Submit.pm
+PROGS+=$(SUBMIT_PERL_MODS)
-submitd: submitd.o commands.o tasks.o
-submitd.o: submitd.c submitd.h
-commands.o: commands.c submitd.h
-tasks.o: tasks.c submitd.h
-connect: connect.o
-connect.o: connect.c
-privkey: privkey.o
-privkey.o: privkey.c
+### The submit client and utilities ###
+
+PROGS+=$(addprefix $(SDIR)/,contest create-certs remote-status remote-submit show-submits)
+
+$(SDIR)/contest: $(s)/submit/contest.pl
+$(SDIR)/create-certs: $(s)/submit/create-certs.sh
+$(SDIR)/remote-status: $(s)/submit/remote-status.pl
+$(SDIR)/remote-submit: $(s)/submit/remote-submit.pl
+$(SDIR)/show-submits: $(s)/submit/show-submits.pl
certs:
certtool --generate-privkey --outfile ca-key.pem
certtool --generate-request --load-privkey client-key.pem --outfile client-req.pem --template client-cert.tpl
certtool --generate-certificate --load-request client-req.pem --outfile client-cert.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem --template client-cert.tpl
# Beware of serial numbers
-
-lib: .lib-stamp lib/perl5/MO/Submit.pm lib/perl5/Sherlock/Object.pm
-
-.lib-stamp:
- mkdir -p lib/perl5/{MO,Sherlock}
- touch .lib-stamp
-
-lib/perl5/MO/Submit.pm: MO/Submit.pm
- cp $^ $@
-
-lib/perl5/Sherlock/Object.pm: $(LIBUCW)/lib/perl5/Sherlock/Object.pm
- cp $^ $@
-
-clean:
- rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*`
- rm -f submitd connect privkey
- rm -rf lib .lib-stamp
-
-.PHONY: lib
--- /dev/null
+# A Perl module for communicating with the MO Submit Server
+# (c) 2007 Martin Mares <mj@ucw.cz>
+
+package MO::Submit;
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use IO::Socket::SSL; # qw(debug3);
+use Sherlock::Object;
+use POSIX;
+
+sub new($) {
+ my $user = $ENV{"USER"} or die "Environment variable USER not set\n";
+ my $home = $ENV{"HOME"} or die "Environment variable HOME not set\n";
+ my $mo = "$home/.mo";
+ my $root = $ENV{"MO_ROOT"} or die "Environment variable MO_ROOT not set\n";
+ my $self = {
+ "Contest" => "CEOI 2007",
+ "Server" => "ceoi-gamma:8888",
+ "Key" => "$mo/key.pem", # Keys and certificates
+ "Cert" => "$mo/cert.pem",
+ "CACert" => "$mo/ca-cert.pem",
+ "Trace" => defined $ENV{"MO_SUBMIT_TRACE"},
+ "Checks" => 1, # Run `check' before submitting
+ "AllowOverride" => 1, # Allow overriding a failed check
+ "History" => "$home/.history", # Keep submission history in this directory
+ "RefreshTimer" => 60000, # How often GUI sends STATUS commands [ms]
+ "root" => $root,
+ "user" => $user,
+ "sk" => undef,
+ "error" => undef,
+ };
+ return bless $self;
+}
+
+sub DESTROY($) {
+ my $self = shift @_;
+ $self->disconnect;
+}
+
+sub log($$) {
+ my ($self, $msg) = @_;
+ print STDERR "SUBMIT: $msg\n" if $self->{"Trace"};
+}
+
+sub err($$) {
+ my ($self, $msg) = @_;
+ print STDERR "ERROR: $msg\n" if $self->{"Trace"};
+ $self->{"error"} = $msg;
+ $self->disconnect;
+}
+
+sub is_connected($) {
+ my $self = shift @_;
+ return defined $self->{"sk"};
+}
+
+sub disconnect($) {
+ my $self = shift @_;
+ if ($self->is_connected) {
+ close $self->{"sk"};
+ $self->{"sk"} = undef;
+ $self->log("Disconnected");
+ }
+}
+
+sub connect($) {
+ my $self = shift @_;
+ $self->disconnect;
+
+ $self->log("Connecting to submit server");
+ my $sk = new IO::Socket::INET(
+ PeerAddr => $self->{"Server"},
+ Proto => "tcp",
+ );
+ if (!defined $sk) {
+ $self->err("Cannot connect to server: $!");
+ return undef;
+ }
+ my $z = <$sk>;
+ if (!defined $z) {
+ $self->err("Server failed to send a welcome message");
+ close $sk;
+ return undef;
+ }
+ chomp $z;
+ if ($z !~ /^\+/) {
+ $self->err("Server rejected the connection: $z");
+ close $sk;
+ return undef;
+ }
+ if ($z =~ /TLS/) {
+ $self->log("Starting TLS");
+ $sk = IO::Socket::SSL->start_SSL(
+ $sk,
+ SSL_version => 'TLSv1',
+ SSL_use_cert => 1,
+ SSL_key_file => $self->{"Key"},
+ SSL_cert_file => $self->{"Cert"},
+ SSL_ca_file => $self->{"CACert"},
+ SSL_verify_mode => 3,
+ );
+ if (!defined $sk) {
+ $self->err("Cannot establish TLS connection: " . IO::Socket::SSL::errstr());
+ return undef;
+ }
+ }
+ $self->{"sk"} = $sk;
+ $sk->autoflush(0);
+
+ $self->log("Logging in");
+ my $req = new Sherlock::Object("U" => $self->{"user"});
+ my $reply = $self->request($req);
+ my $err = $reply->get("-");
+ if (defined $err) {
+ $self->err("Cannot log in: $err");
+ return undef;
+ }
+
+ $self->log("Connected");
+ return 1;
+}
+
+sub request($$) {
+ my ($self, $obj) = @_;
+ my $sk = $self->{"sk"};
+ local $SIG{'PIPE'} = 'ignore';
+ $obj->write($sk);
+ print $sk "\n";
+ $sk->flush();
+ if ($sk->error) {
+ $self->err("Connection broken");
+ return undef;
+ }
+ return $self->reply;
+}
+
+sub reply($) {
+ my ($self, $obj) = @_;
+ my $sk = $self->{"sk"};
+ my $reply = new Sherlock::Object;
+ if ($reply->read($sk)) {
+ return $reply;
+ } else {
+ $self->err("Connection broken");
+ return undef;
+ }
+}
+
+sub send_file($$$) {
+ my ($self, $fh, $size) = @_;
+ my $sk = $self->{"sk"};
+ local $SIG{'PIPE'} = 'ignore';
+ while ($size) {
+ my $l = ($size < 4096 ? $size : 4096);
+ my $buf = "";
+ if ($fh->read($buf, $l) != $l) {
+ $self->err("File shrunk during upload");
+ return undef;
+ }
+ $sk->write($buf, $l);
+ if ($sk->error) {
+ $self->err("Connection broken");
+ return undef;
+ }
+ $size -= $l;
+ }
+ return $self->reply;
+}
+
+sub write_history($$$$$) {
+ my ($self, $task, $part, $ext, $filename) = @_;
+ my $hist = $self->{"History"};
+ -d $hist or mkdir $hist or return "Unable to create $hist: $!";
+ my $now = POSIX::strftime("%H:%M:%S", localtime(time));
+ my $maybe_part = ($part eq $task) ? "" : ":$part";
+ my $name = "$hist/$now-$task$maybe_part.$ext";
+ $self->log("Backing up to $name");
+ `cp "$filename" "$name"`;
+ return "Unable to back up $filename as $name" if $?;
+ return undef;
+}
+
+1;
+++ /dev/null
-#!/usr/bin/perl
-# Contest UI for the MO Submitter
-# (c) 2007 Martin Mares <mj@ucw.cz>
-
-use strict;
-use warnings;
-
-BEGIN {
- defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
-}
-use lib $ENV{"MO_ROOT"} . "/lib/perl5";
-use lib $ENV{"MO_ROOT"} . "/submit/lib/perl5";
-
-use MO::Submit;
-use Sherlock::Object;
-use POSIX;
-use IO::File;
-use File::stat;
-use Gtk2 -init;
-
-my $conn = new MO::Submit;
-
-### GUI INITIALIZATION ###
-
-sub init_refresh();
-sub start_refresh_timer($);
-sub stop_refresh_timer();
-my $force_refresh = 0;
-sub refresh();
-
-sub submit($);
-
-my $busy_cursor = Gtk2::Gdk::Cursor->new('watch');
-
-# The main window
-my $window = Gtk2::Window->new('toplevel');
-$window->signal_connect("delete-event" => sub { Gtk2->main_quit });
-$window->set_title($conn->{"Contest"} . " Submitter");
-$window->set_wmclass("submitter", "Submitter");
-$window->set_default_size(640, 480);
-
-# The title label
-my $title_lab = Gtk2::Label->new;
-$title_lab->set_markup("<span size='x-large'>" . $conn->{"Contest"} . "</span>");
-
-# The row of buttons
-my $b_submit = Gtk2::Button->new('Submit');
-$b_submit->set_image(Gtk2::Image->new_from_stock('gtk-apply', 'button'));
-$b_submit->signal_connect(clicked => sub { submit(0) });
-$b_submit->set_sensitive(0);
-
-my $b_check = Gtk2::Button->new('Check');
-$b_check->set_image(Gtk2::Image->new_from_stock('gtk-find', 'button'));
-$b_check->signal_connect(clicked => sub { submit(1) });
-$b_check->set_sensitive(0);
-
-my $b_refresh = Gtk2::Button->new('Refresh');
-$b_refresh->set_image(Gtk2::Image->new_from_stock('gtk-refresh', 'button'));
-$b_refresh->signal_connect(clicked => sub { start_refresh_timer(1) });
-
-my $button_box = Gtk2::HBox->new;
-$button_box->pack_start_defaults($b_submit);
-$button_box->pack_start_defaults($b_check);
-$button_box->pack_start_defaults($b_refresh);
-$button_box->set_border_width(5);
-
-# The list of tasks
-my $task_store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
-
-my $task_view = Gtk2::TreeView->new($task_store);
-my $task_renderer = Gtk2::CellRendererText->new;
-my $task_col1 = Gtk2::TreeViewColumn->new_with_attributes("Task", $task_renderer, "text", 0);
-$task_view->append_column($task_col1);
-my $task_col2 = Gtk2::TreeViewColumn->new_with_attributes("Status", $task_renderer, "text", 1);
-$task_view->append_column($task_col2);
-$task_view->set_headers_visible(0);
-
-my $task_scroll = Gtk2::ScrolledWindow->new;
-$task_scroll->set_policy("automatic", "automatic");
-$task_scroll->add($task_view);
-$task_scroll->set_border_width(5);
-
-my $task_frame = Gtk2::Frame->new("Tasks");
-$task_frame->add($task_scroll);
-
-my $selected_task;
-my $task_sel = $task_view->get_selection;
-$task_sel->set_mode('single');
-$task_sel->signal_connect(changed => sub {
- my $iter = $_[0]->get_selected;
- if ($iter) {
- $selected_task = $task_store->get($iter, 0);
- $b_submit->set_sensitive(1);
- $b_check->set_sensitive(1);
- } else {
- $selected_task = undef;
- print "Deselected task\n";
- $b_submit->set_sensitive(0);
- $b_check->set_sensitive(0);
- }
-});
-
-my $status_bar = Gtk2::Statusbar->new;
-my $bar_ctx = $status_bar->get_context_id('xyzzy');
-
-my $vbox = Gtk2::VBox->new;
-$vbox->pack_start($title_lab, 0, 0, 10);
-$vbox->pack_start($task_frame, 1, 1, 0);
-$vbox->pack_start($button_box, 0, 0, 0);
-$vbox->pack_start($status_bar, 0, 0, 0);
-
-$window->add($vbox);
-$window->signal_connect("expose-event" => sub { init_refresh(); return 0; });
-$window->show_all;
-
-### REFRESHING ###
-
-my $last_status_id;
-
-sub msg($) {
- print "GUI: ", $_[0], "\n" if $conn->{"Trace"};
-}
-
-sub status($) {
- msg($_[0]);
- defined $last_status_id and $status_bar->remove($bar_ctx, $last_status_id);
- $last_status_id = $status_bar->push($bar_ctx, shift @_);
-}
-
-sub busy($) {
- status($_[0]);
- $window->window->set_cursor($busy_cursor);
- $window->Gtk2::Gdk::flush;
-}
-
-sub ready($) {
- status($_[0]);
- $window->window->set_cursor(undef);
- $window->Gtk2::Gdk::flush;
-}
-
-my $window_inited = 0;
-sub init_refresh()
-{
- if (!$window_inited) {
- $force_refresh = 1;
- start_refresh_timer(1);
- $window_inited = 1;
- }
- return 1;
-}
-
-my $refresh_timer_id;
-
-sub timed_refresh()
-{
- refresh();
- return 1; # We wish to re-run the timer
-}
-
-sub start_refresh_timer($) {
- my ($go) = @_;
- stop_refresh_timer();
- refresh() if $go;
- $refresh_timer_id = Glib::Timeout->add($conn->{"RefreshTimer"}, \&timed_refresh);
-}
-
-sub stop_refresh_timer() {
- if (defined $refresh_timer_id) {
- Glib::Source->remove($refresh_timer_id);
- $refresh_timer_id = undef;
- }
-}
-
-my $task_status_object;
-my @task_parts = ();
-my @task_stat = ();
-
-sub recalc_task_list() {
- my @new_tp = ();
- my @new_stat = ();
- foreach my $t ($task_status_object->getarray("(T")) {
- my $task = $t->get("T");
- foreach my $p ($t->getarray("(P")) {
- my $part = $p->get("P");
- my $taskpart = ($task eq $part) ? $task : "$task/$part";
- push @new_tp, $taskpart;
- my $status = "---";
- my $current_ver = $p->get("V");
- foreach my $v ($p->getarray("(V")) {
- if ($v->get("V") == $current_ver) {
- my $time = strftime("%H:%M:%S", localtime $v->get("T"));
- $status = "OK (" .
- "$part." . $v->get("X") . ", " .
- $v->get("L") . " bytes, " .
- $v->get("S") . " $time)";
- last;
- }
- }
- push @new_stat, $status;
- }
- }
-
- if (join("\n", @new_tp) ne join("\n", @task_parts)) {
- # The tasks have changed, repopulate the whole structure
- $task_store->clear;
- my @s = @new_stat;
- foreach my $taskpart (@new_tp) {
- my $iter = $task_store->append;
- $task_store->set($iter,
- 0, $taskpart,
- 1, shift @s);
- }
- } else {
- # Update the task status
- my @s = @task_stat;
- my @ns = @new_stat;
- $task_store->foreach(sub {
- my ($obj, $path, $iter) = @_;
- if ($s[0] ne $ns[0]) {
- $task_store->set($iter, 1, $ns[0]);
- }
- shift @s;
- shift @ns;
- return 0;
- });
- }
-
- @task_parts = @new_tp;
- @task_stat = @new_stat;
-}
-
-sub refresh()
-{
- if (!$conn->is_connected || $force_refresh) {
- busy("Connecting to server...");
- if ($conn->connect) {
- ready("Connected successfully");
- } else {
- ready($conn->{"error"});
- }
- }
- if ($conn->is_connected) {
- busy("Updating status...");
- my $r = new Sherlock::Object("!" => "STATUS");
- $r = $conn->request($r);
- if (!defined $r) {
- ready($conn->{"error"});
- } elsif ($r->get("-")) {
- ready($r->get("-"));
- } else {
- $task_status_object = $r;
- recalc_task_list;
- $force_refresh = 0;
- ready("Ready");
- }
- }
- if (!$conn->is_connected && !$force_refresh) {
- # Retry
- $conn->log("Retrying");
- $force_refresh = 1;
- refresh();
- }
-}
-
-### SUBMITTING ###
-
-my $subwin;
-my $chooser;
-my $subwin_vbox;
-my $subwin_label;
-my $bbutton_box;
-my $submitting_label;
-my $text_frame;
-my $status_label;
-my $check_only;
-my $submit_filename;
-my $submit_extension;
-my %submit_fn_cache = ();
-
-sub end_submit($) {
- my ($close) = @_;
- $subwin->destroy if $close;
- start_refresh_timer(1);
-}
-
-sub finish_submit() {
- my $button = Gtk2::Button->new_from_stock('gtk-close');
- $button->signal_connect(clicked => sub { end_submit(1) });
-
- $bbutton_box = Gtk2::HButtonBox->new;
- $bbutton_box->pack_start_defaults($button);
- $subwin_vbox->pack_start($bbutton_box, 0, 0, 10);
-
- ready("Ready");
- $subwin->show_all;
- $subwin->window->set_cursor(undef);
-}
-
-sub submit_ok() {
- $status_label->set_markup("<span size='large'>Submitted OK</span>");
- $submitting_label->set_markup("<span size='large'>The task has been successfully submitted.</span>");
- refresh();
- finish_submit();
-}
-
-sub submit_failed($) {
- my ($msg) = @_;
- $status_label->set_markup("<span size='large'>Submit failed</span>");
- $submitting_label->set_markup("<span size='large'>$msg</span>");
- finish_submit();
-}
-
-sub run_submit() {
- my ($task, $part) = split /\//, $selected_task;
- defined $part or $part = $task;
-
- if (defined $conn->{"History"}) {
- busy("Submitting locally to " . $conn->{"History"});
- my $err = $conn->write_history($task, $part, $submit_extension, $submit_filename);
- if (defined $err) {
- submit_failed("Recording to local history failed\n($err)");
- return;
- }
- }
-
- if ($conn->is_connected) {
- busy("Checking server status...");
- my $r = new Sherlock::Object("!" => "NOP");
- $r = $conn->request($r);
- }
- if (!$conn->is_connected) {
- busy("Reconnecting to server...");
- if (!$conn->connect) {
- ready($conn->{"error"});
- submit_failed("Unable to connect to the server");
- return;
- }
- }
- busy("Submitting...");
-
- my $fh = new IO::File($submit_filename);
- if (!$fh) {
- submit_failed("Unable to open $submit_filename\n($!)");
- return;
- }
- my $stat = File::stat::populate($fh->stat);
- if (!$stat) {
- submit_failed("Unable to stat $submit_filename\n($!)");
- return;
- }
- my $size = $stat->size;
-
- my $r = new Sherlock::Object("!" => "SUBMIT", "T" => $task, "P" => $part, "X" => $submit_extension, "S" => $size);
- $r = $conn->request($r);
- if (!defined($r)) {
- submit_failed("Connection to the server lost");
- return;
- } elsif ($r->get("-")) {
- submit_failed($r->get("-"));
- return;
- }
-
- $r = $conn->send_file($fh, $size);
- if (!defined($r)) {
- submit_failed("Connection to the server lost");
- return;
- } elsif ($r->get("-")) {
- submit_failed($r->get("-"));
- return;
- }
-
- close $fh;
- submit_ok();
-}
-
-sub checks_ok() {
- if ($check_only) {
- $status_label->set_markup("<span size='large'>Checked successfully</span>");
- $submitting_label->set_markup("<span size='large'>The task has passed the checks.</span>");
- finish_submit();
- return;
- }
-
- $status_label->set_markup("<span size='large'>Submitting</span>");
- $subwin->show_all;
-
- # Continue when everything is displayed
- Glib::Idle->add(sub {
- $window->Gtk2::Gdk::flush;
- run_submit();
- return 0;
- });
-}
-
-sub checks_override() {
- $submitting_label = Gtk2::Label->new("Please wait...");
- $subwin_vbox->pack_start_defaults($submitting_label);
-
- $subwin->window->set_cursor($busy_cursor);
- $bbutton_box->destroy;
- $text_frame->destroy;
- checks_ok();
-}
-
-sub checks_failed($) {
- my ($msg) = @_;
-
- $status_label->set_markup("<span size='large'>Check failed</span>");
- $submitting_label->destroy;
-
- my $text_buffer = Gtk2::TextBuffer->new;
- $text_buffer->set_text($msg);
-
- my $text_view = Gtk2::TextView->new_with_buffer($text_buffer);
- $text_view->set_editable(0);
- $text_view->set_cursor_visible(0);
-
- my $text_scroll = Gtk2::ScrolledWindow->new;
- $text_scroll->set_policy("automatic", "automatic");
- $text_scroll->add($text_view);
-
- $text_frame = Gtk2::Frame->new("Checker log");
- $text_frame->add($text_scroll);
-
- $subwin_vbox->pack_start_defaults($text_frame);
-
- if ($check_only || !$conn->{"AllowOverride"}) {
- finish_submit();
- return;
- }
-
- my $close_button = Gtk2::Button->new_from_stock('gtk-close');
- $close_button->signal_connect(clicked => sub { end_submit(1) });
-
- my $anyway_button = Gtk2::Button->new('Submit anyway');
- $anyway_button->signal_connect(clicked => \&checks_override);
-
- $bbutton_box = Gtk2::HButtonBox->new;
- $bbutton_box->pack_start_defaults($anyway_button);
- $bbutton_box->pack_start_defaults($close_button);
- $bbutton_box->set_border_width(5);
- $subwin_vbox->pack_start($bbutton_box, 0, 0, 10);
-
- ready("Ready");
- $subwin->show_all;
- $subwin->window->set_cursor(undef);
-}
-
-sub run_checks() {
- ($submit_extension) = ($submit_filename =~ /\.([^.]+)$/);
- if (!$submit_extension) {
- checks_failed("The filename does not have a valid extension");
- return;
- }
- if (!$conn->{"Checks"}) {
- checks_ok();
- return;
- }
- my $root = $conn->{"root"};
- my ($task, $part) = split /\//, $selected_task;
- defined $part or $part = "";
- my $verdict = `$root/bin/check -s "$submit_filename" $task $part 2>&1`;
- if ($?) {
- checks_failed($verdict);
- } else {
- checks_ok();
- }
-}
-
-sub do_submit() {
- $submit_filename = $chooser->get_filename;
- $submit_fn_cache{$selected_task} = $submit_filename;
- msg "Selected $submit_filename";
- defined $submit_filename or return;
- -f $submit_filename or return;
-
- $chooser->destroy;
- $bbutton_box->destroy;
-
- $status_label->set_markup("<span size='large'>Checking</span>");
-
- $submitting_label = Gtk2::Label->new("Please wait...");
- $subwin_vbox->pack_start_defaults($submitting_label);
- $subwin->show_all;
- $subwin->window->set_cursor($busy_cursor);
-
- # Continue when everything is displayed
- Glib::Idle->add(sub {
- $window->Gtk2::Gdk::flush;
- run_checks();
- return 0;
- });
-}
-
-sub submit($) {
- $check_only = shift @_;
-
- stop_refresh_timer();
-
- $subwin = Gtk2::Window->new('toplevel');
- $subwin->set_default_size(640, 480);
- $subwin->set_modal(1);
- $subwin->set_transient_for($window);
- $subwin->set_destroy_with_parent(1);
- $subwin->set_title("Submit task $selected_task");
- $subwin->set_wmclass("submitter", "Submitter");
- $subwin->signal_connect("delete-event" => sub { end_submit(0); return 0; });
-
- my $bb_submit = Gtk2::Button->new($check_only ? 'Check' : 'Submit');
- $bb_submit->set_image(Gtk2::Image->new_from_stock('gtk-apply', 'button'));
- $bb_submit->signal_connect(clicked => \&do_submit);
-
- my $bb_cancel = Gtk2::Button->new_from_stock('gtk-cancel');
- $bb_cancel->signal_connect(clicked => sub { end_submit(1) });
-
- $bbutton_box = Gtk2::HButtonBox->new;
- $bbutton_box->pack_start_defaults($bb_submit);
- $bbutton_box->pack_start_defaults($bb_cancel);
- $bbutton_box->set_border_width(5);
-
- my $subwin_label = Gtk2::Label->new;
- $subwin_label->set_markup("<span size='x-large'>" . ($check_only ? "Checking" : "Submitting") . " $selected_task</span>");
-
- $status_label = Gtk2::Label->new;
- $status_label->set_markup("<span size='large'>Please select file to " . ($check_only ? "check" : "submit") . "</span>");
-
- $chooser = Gtk2::FileChooserWidget->new("open");
- $chooser->set_local_only(1);
- $chooser->signal_connect("file-activated" => \&do_submit);
- $chooser->set_filename($submit_fn_cache{$selected_task}) if defined $submit_fn_cache{$selected_task};
-
- $subwin_vbox = Gtk2::VBox->new;
- $subwin_vbox->pack_start($subwin_label, 0, 0, 10);
- $subwin_vbox->pack_start($status_label, 0, 0, 10);
- $subwin_vbox->pack_start_defaults($chooser);
- $subwin_vbox->pack_start($bbutton_box, 0, 0, 0);
-
- $subwin->add($subwin_vbox);
- $subwin->show_all;
-}
-
-### MAIN ###
-
-Gtk2->main;
-exit 0;
--- /dev/null
+#!/usr/bin/perl
+# Contest UI for the MO Submitter
+# (c) 2007 Martin Mares <mj@ucw.cz>
+
+use strict;
+use warnings;
+
+BEGIN {
+ defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
+}
+use lib $ENV{"MO_ROOT"} . "/lib/perl5";
+use lib $ENV{"MO_ROOT"} . "/submit/lib/perl5";
+
+use MO::Submit;
+use Sherlock::Object;
+use POSIX;
+use IO::File;
+use File::stat;
+use Gtk2 -init;
+
+my $conn = new MO::Submit;
+
+### GUI INITIALIZATION ###
+
+sub init_refresh();
+sub start_refresh_timer($);
+sub stop_refresh_timer();
+my $force_refresh = 0;
+sub refresh();
+
+sub submit($);
+
+my $busy_cursor = Gtk2::Gdk::Cursor->new('watch');
+
+# The main window
+my $window = Gtk2::Window->new('toplevel');
+$window->signal_connect("delete-event" => sub { Gtk2->main_quit });
+$window->set_title($conn->{"Contest"} . " Submitter");
+$window->set_wmclass("submitter", "Submitter");
+$window->set_default_size(640, 480);
+
+# The title label
+my $title_lab = Gtk2::Label->new;
+$title_lab->set_markup("<span size='x-large'>" . $conn->{"Contest"} . "</span>");
+
+# The row of buttons
+my $b_submit = Gtk2::Button->new('Submit');
+$b_submit->set_image(Gtk2::Image->new_from_stock('gtk-apply', 'button'));
+$b_submit->signal_connect(clicked => sub { submit(0) });
+$b_submit->set_sensitive(0);
+
+my $b_check = Gtk2::Button->new('Check');
+$b_check->set_image(Gtk2::Image->new_from_stock('gtk-find', 'button'));
+$b_check->signal_connect(clicked => sub { submit(1) });
+$b_check->set_sensitive(0);
+
+my $b_refresh = Gtk2::Button->new('Refresh');
+$b_refresh->set_image(Gtk2::Image->new_from_stock('gtk-refresh', 'button'));
+$b_refresh->signal_connect(clicked => sub { start_refresh_timer(1) });
+
+my $button_box = Gtk2::HBox->new;
+$button_box->pack_start_defaults($b_submit);
+$button_box->pack_start_defaults($b_check);
+$button_box->pack_start_defaults($b_refresh);
+$button_box->set_border_width(5);
+
+# The list of tasks
+my $task_store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
+
+my $task_view = Gtk2::TreeView->new($task_store);
+my $task_renderer = Gtk2::CellRendererText->new;
+my $task_col1 = Gtk2::TreeViewColumn->new_with_attributes("Task", $task_renderer, "text", 0);
+$task_view->append_column($task_col1);
+my $task_col2 = Gtk2::TreeViewColumn->new_with_attributes("Status", $task_renderer, "text", 1);
+$task_view->append_column($task_col2);
+$task_view->set_headers_visible(0);
+
+my $task_scroll = Gtk2::ScrolledWindow->new;
+$task_scroll->set_policy("automatic", "automatic");
+$task_scroll->add($task_view);
+$task_scroll->set_border_width(5);
+
+my $task_frame = Gtk2::Frame->new("Tasks");
+$task_frame->add($task_scroll);
+
+my $selected_task;
+my $task_sel = $task_view->get_selection;
+$task_sel->set_mode('single');
+$task_sel->signal_connect(changed => sub {
+ my $iter = $_[0]->get_selected;
+ if ($iter) {
+ $selected_task = $task_store->get($iter, 0);
+ $b_submit->set_sensitive(1);
+ $b_check->set_sensitive(1);
+ } else {
+ $selected_task = undef;
+ print "Deselected task\n";
+ $b_submit->set_sensitive(0);
+ $b_check->set_sensitive(0);
+ }
+});
+
+my $status_bar = Gtk2::Statusbar->new;
+my $bar_ctx = $status_bar->get_context_id('xyzzy');
+
+my $vbox = Gtk2::VBox->new;
+$vbox->pack_start($title_lab, 0, 0, 10);
+$vbox->pack_start($task_frame, 1, 1, 0);
+$vbox->pack_start($button_box, 0, 0, 0);
+$vbox->pack_start($status_bar, 0, 0, 0);
+
+$window->add($vbox);
+$window->signal_connect("expose-event" => sub { init_refresh(); return 0; });
+$window->show_all;
+
+### REFRESHING ###
+
+my $last_status_id;
+
+sub msg($) {
+ print "GUI: ", $_[0], "\n" if $conn->{"Trace"};
+}
+
+sub status($) {
+ msg($_[0]);
+ defined $last_status_id and $status_bar->remove($bar_ctx, $last_status_id);
+ $last_status_id = $status_bar->push($bar_ctx, shift @_);
+}
+
+sub busy($) {
+ status($_[0]);
+ $window->window->set_cursor($busy_cursor);
+ $window->Gtk2::Gdk::flush;
+}
+
+sub ready($) {
+ status($_[0]);
+ $window->window->set_cursor(undef);
+ $window->Gtk2::Gdk::flush;
+}
+
+my $window_inited = 0;
+sub init_refresh()
+{
+ if (!$window_inited) {
+ $force_refresh = 1;
+ start_refresh_timer(1);
+ $window_inited = 1;
+ }
+ return 1;
+}
+
+my $refresh_timer_id;
+
+sub timed_refresh()
+{
+ refresh();
+ return 1; # We wish to re-run the timer
+}
+
+sub start_refresh_timer($) {
+ my ($go) = @_;
+ stop_refresh_timer();
+ refresh() if $go;
+ $refresh_timer_id = Glib::Timeout->add($conn->{"RefreshTimer"}, \&timed_refresh);
+}
+
+sub stop_refresh_timer() {
+ if (defined $refresh_timer_id) {
+ Glib::Source->remove($refresh_timer_id);
+ $refresh_timer_id = undef;
+ }
+}
+
+my $task_status_object;
+my @task_parts = ();
+my @task_stat = ();
+
+sub recalc_task_list() {
+ my @new_tp = ();
+ my @new_stat = ();
+ foreach my $t ($task_status_object->getarray("(T")) {
+ my $task = $t->get("T");
+ foreach my $p ($t->getarray("(P")) {
+ my $part = $p->get("P");
+ my $taskpart = ($task eq $part) ? $task : "$task/$part";
+ push @new_tp, $taskpart;
+ my $status = "---";
+ my $current_ver = $p->get("V");
+ foreach my $v ($p->getarray("(V")) {
+ if ($v->get("V") == $current_ver) {
+ my $time = strftime("%H:%M:%S", localtime $v->get("T"));
+ $status = "OK (" .
+ "$part." . $v->get("X") . ", " .
+ $v->get("L") . " bytes, " .
+ $v->get("S") . " $time)";
+ last;
+ }
+ }
+ push @new_stat, $status;
+ }
+ }
+
+ if (join("\n", @new_tp) ne join("\n", @task_parts)) {
+ # The tasks have changed, repopulate the whole structure
+ $task_store->clear;
+ my @s = @new_stat;
+ foreach my $taskpart (@new_tp) {
+ my $iter = $task_store->append;
+ $task_store->set($iter,
+ 0, $taskpart,
+ 1, shift @s);
+ }
+ } else {
+ # Update the task status
+ my @s = @task_stat;
+ my @ns = @new_stat;
+ $task_store->foreach(sub {
+ my ($obj, $path, $iter) = @_;
+ if ($s[0] ne $ns[0]) {
+ $task_store->set($iter, 1, $ns[0]);
+ }
+ shift @s;
+ shift @ns;
+ return 0;
+ });
+ }
+
+ @task_parts = @new_tp;
+ @task_stat = @new_stat;
+}
+
+sub refresh()
+{
+ if (!$conn->is_connected || $force_refresh) {
+ busy("Connecting to server...");
+ if ($conn->connect) {
+ ready("Connected successfully");
+ } else {
+ ready($conn->{"error"});
+ }
+ }
+ if ($conn->is_connected) {
+ busy("Updating status...");
+ my $r = new Sherlock::Object("!" => "STATUS");
+ $r = $conn->request($r);
+ if (!defined $r) {
+ ready($conn->{"error"});
+ } elsif ($r->get("-")) {
+ ready($r->get("-"));
+ } else {
+ $task_status_object = $r;
+ recalc_task_list;
+ $force_refresh = 0;
+ ready("Ready");
+ }
+ }
+ if (!$conn->is_connected && !$force_refresh) {
+ # Retry
+ $conn->log("Retrying");
+ $force_refresh = 1;
+ refresh();
+ }
+}
+
+### SUBMITTING ###
+
+my $subwin;
+my $chooser;
+my $subwin_vbox;
+my $subwin_label;
+my $bbutton_box;
+my $submitting_label;
+my $text_frame;
+my $status_label;
+my $check_only;
+my $submit_filename;
+my $submit_extension;
+my %submit_fn_cache = ();
+
+sub end_submit($) {
+ my ($close) = @_;
+ $subwin->destroy if $close;
+ start_refresh_timer(1);
+}
+
+sub finish_submit() {
+ my $button = Gtk2::Button->new_from_stock('gtk-close');
+ $button->signal_connect(clicked => sub { end_submit(1) });
+
+ $bbutton_box = Gtk2::HButtonBox->new;
+ $bbutton_box->pack_start_defaults($button);
+ $subwin_vbox->pack_start($bbutton_box, 0, 0, 10);
+
+ ready("Ready");
+ $subwin->show_all;
+ $subwin->window->set_cursor(undef);
+}
+
+sub submit_ok() {
+ $status_label->set_markup("<span size='large'>Submitted OK</span>");
+ $submitting_label->set_markup("<span size='large'>The task has been successfully submitted.</span>");
+ refresh();
+ finish_submit();
+}
+
+sub submit_failed($) {
+ my ($msg) = @_;
+ $status_label->set_markup("<span size='large'>Submit failed</span>");
+ $submitting_label->set_markup("<span size='large'>$msg</span>");
+ finish_submit();
+}
+
+sub run_submit() {
+ my ($task, $part) = split /\//, $selected_task;
+ defined $part or $part = $task;
+
+ if (defined $conn->{"History"}) {
+ busy("Submitting locally to " . $conn->{"History"});
+ my $err = $conn->write_history($task, $part, $submit_extension, $submit_filename);
+ if (defined $err) {
+ submit_failed("Recording to local history failed\n($err)");
+ return;
+ }
+ }
+
+ if ($conn->is_connected) {
+ busy("Checking server status...");
+ my $r = new Sherlock::Object("!" => "NOP");
+ $r = $conn->request($r);
+ }
+ if (!$conn->is_connected) {
+ busy("Reconnecting to server...");
+ if (!$conn->connect) {
+ ready($conn->{"error"});
+ submit_failed("Unable to connect to the server");
+ return;
+ }
+ }
+ busy("Submitting...");
+
+ my $fh = new IO::File($submit_filename);
+ if (!$fh) {
+ submit_failed("Unable to open $submit_filename\n($!)");
+ return;
+ }
+ my $stat = File::stat::populate($fh->stat);
+ if (!$stat) {
+ submit_failed("Unable to stat $submit_filename\n($!)");
+ return;
+ }
+ my $size = $stat->size;
+
+ my $r = new Sherlock::Object("!" => "SUBMIT", "T" => $task, "P" => $part, "X" => $submit_extension, "S" => $size);
+ $r = $conn->request($r);
+ if (!defined($r)) {
+ submit_failed("Connection to the server lost");
+ return;
+ } elsif ($r->get("-")) {
+ submit_failed($r->get("-"));
+ return;
+ }
+
+ $r = $conn->send_file($fh, $size);
+ if (!defined($r)) {
+ submit_failed("Connection to the server lost");
+ return;
+ } elsif ($r->get("-")) {
+ submit_failed($r->get("-"));
+ return;
+ }
+
+ close $fh;
+ submit_ok();
+}
+
+sub checks_ok() {
+ if ($check_only) {
+ $status_label->set_markup("<span size='large'>Checked successfully</span>");
+ $submitting_label->set_markup("<span size='large'>The task has passed the checks.</span>");
+ finish_submit();
+ return;
+ }
+
+ $status_label->set_markup("<span size='large'>Submitting</span>");
+ $subwin->show_all;
+
+ # Continue when everything is displayed
+ Glib::Idle->add(sub {
+ $window->Gtk2::Gdk::flush;
+ run_submit();
+ return 0;
+ });
+}
+
+sub checks_override() {
+ $submitting_label = Gtk2::Label->new("Please wait...");
+ $subwin_vbox->pack_start_defaults($submitting_label);
+
+ $subwin->window->set_cursor($busy_cursor);
+ $bbutton_box->destroy;
+ $text_frame->destroy;
+ checks_ok();
+}
+
+sub checks_failed($) {
+ my ($msg) = @_;
+
+ $status_label->set_markup("<span size='large'>Check failed</span>");
+ $submitting_label->destroy;
+
+ my $text_buffer = Gtk2::TextBuffer->new;
+ $text_buffer->set_text($msg);
+
+ my $text_view = Gtk2::TextView->new_with_buffer($text_buffer);
+ $text_view->set_editable(0);
+ $text_view->set_cursor_visible(0);
+
+ my $text_scroll = Gtk2::ScrolledWindow->new;
+ $text_scroll->set_policy("automatic", "automatic");
+ $text_scroll->add($text_view);
+
+ $text_frame = Gtk2::Frame->new("Checker log");
+ $text_frame->add($text_scroll);
+
+ $subwin_vbox->pack_start_defaults($text_frame);
+
+ if ($check_only || !$conn->{"AllowOverride"}) {
+ finish_submit();
+ return;
+ }
+
+ my $close_button = Gtk2::Button->new_from_stock('gtk-close');
+ $close_button->signal_connect(clicked => sub { end_submit(1) });
+
+ my $anyway_button = Gtk2::Button->new('Submit anyway');
+ $anyway_button->signal_connect(clicked => \&checks_override);
+
+ $bbutton_box = Gtk2::HButtonBox->new;
+ $bbutton_box->pack_start_defaults($anyway_button);
+ $bbutton_box->pack_start_defaults($close_button);
+ $bbutton_box->set_border_width(5);
+ $subwin_vbox->pack_start($bbutton_box, 0, 0, 10);
+
+ ready("Ready");
+ $subwin->show_all;
+ $subwin->window->set_cursor(undef);
+}
+
+sub run_checks() {
+ ($submit_extension) = ($submit_filename =~ /\.([^.]+)$/);
+ if (!$submit_extension) {
+ checks_failed("The filename does not have a valid extension");
+ return;
+ }
+ if (!$conn->{"Checks"}) {
+ checks_ok();
+ return;
+ }
+ my $root = $conn->{"root"};
+ my ($task, $part) = split /\//, $selected_task;
+ defined $part or $part = "";
+ my $verdict = `$root/bin/check -s "$submit_filename" $task $part 2>&1`;
+ if ($?) {
+ checks_failed($verdict);
+ } else {
+ checks_ok();
+ }
+}
+
+sub do_submit() {
+ $submit_filename = $chooser->get_filename;
+ $submit_fn_cache{$selected_task} = $submit_filename;
+ msg "Selected $submit_filename";
+ defined $submit_filename or return;
+ -f $submit_filename or return;
+
+ $chooser->destroy;
+ $bbutton_box->destroy;
+
+ $status_label->set_markup("<span size='large'>Checking</span>");
+
+ $submitting_label = Gtk2::Label->new("Please wait...");
+ $subwin_vbox->pack_start_defaults($submitting_label);
+ $subwin->show_all;
+ $subwin->window->set_cursor($busy_cursor);
+
+ # Continue when everything is displayed
+ Glib::Idle->add(sub {
+ $window->Gtk2::Gdk::flush;
+ run_checks();
+ return 0;
+ });
+}
+
+sub submit($) {
+ $check_only = shift @_;
+
+ stop_refresh_timer();
+
+ $subwin = Gtk2::Window->new('toplevel');
+ $subwin->set_default_size(640, 480);
+ $subwin->set_modal(1);
+ $subwin->set_transient_for($window);
+ $subwin->set_destroy_with_parent(1);
+ $subwin->set_title("Submit task $selected_task");
+ $subwin->set_wmclass("submitter", "Submitter");
+ $subwin->signal_connect("delete-event" => sub { end_submit(0); return 0; });
+
+ my $bb_submit = Gtk2::Button->new($check_only ? 'Check' : 'Submit');
+ $bb_submit->set_image(Gtk2::Image->new_from_stock('gtk-apply', 'button'));
+ $bb_submit->signal_connect(clicked => \&do_submit);
+
+ my $bb_cancel = Gtk2::Button->new_from_stock('gtk-cancel');
+ $bb_cancel->signal_connect(clicked => sub { end_submit(1) });
+
+ $bbutton_box = Gtk2::HButtonBox->new;
+ $bbutton_box->pack_start_defaults($bb_submit);
+ $bbutton_box->pack_start_defaults($bb_cancel);
+ $bbutton_box->set_border_width(5);
+
+ my $subwin_label = Gtk2::Label->new;
+ $subwin_label->set_markup("<span size='x-large'>" . ($check_only ? "Checking" : "Submitting") . " $selected_task</span>");
+
+ $status_label = Gtk2::Label->new;
+ $status_label->set_markup("<span size='large'>Please select file to " . ($check_only ? "check" : "submit") . "</span>");
+
+ $chooser = Gtk2::FileChooserWidget->new("open");
+ $chooser->set_local_only(1);
+ $chooser->signal_connect("file-activated" => \&do_submit);
+ $chooser->set_filename($submit_fn_cache{$selected_task}) if defined $submit_fn_cache{$selected_task};
+
+ $subwin_vbox = Gtk2::VBox->new;
+ $subwin_vbox->pack_start($subwin_label, 0, 0, 10);
+ $subwin_vbox->pack_start($status_label, 0, 0, 10);
+ $subwin_vbox->pack_start_defaults($chooser);
+ $subwin_vbox->pack_start($bbutton_box, 0, 0, 0);
+
+ $subwin->add($subwin_vbox);
+ $subwin->show_all;
+}
+
+### MAIN ###
+
+Gtk2->main;
+exit 0;
+++ /dev/null
-#!/bin/sh
-# A script for creation of all the certificates used by submitd
-# (c) 2007 Martin Mares <mj@ucw.cz>
-
-set -e
-if [ ! -f submit/ca-cert.tpl ] ; then
- echo >&2 "Please run from the MO root directory."
- exit 1
-fi
-
-umask 033
-rm -rf certs
-mkdir certs
-
-echo "### Creating CA certificate ###"
-submit/privkey >certs/ca-key.pem
-certtool --generate-self-signed --load-privkey certs/ca-key.pem --outfile certs/ca-cert.pem --template submit/ca-cert.tpl
-
-echo "### Creating server certificate ###"
-submit/privkey >certs/server-key.pem
-certtool --generate-request --load-privkey certs/server-key.pem --outfile certs/server-req.pem --template submit/server-cert.tpl
-certtool --generate-certificate --load-request certs/server-req.pem --outfile certs/server-cert.pem --load-ca-certificate certs/ca-cert.pem --load-ca-privkey certs/ca-key.pem --template submit/server-cert.tpl
-
-seq=1
-for user in `bin/mo-get-users` ; do
- seq=$(($seq+1))
- echo "### Creating certificate for user #$seq ($user) ###"
- sed <submit/client-cert.tpl >certs/$user-cert.tpl "s/cn = \".*\"/cn = \"$user\"/; s/serial = .*/serial = $seq/;"
- submit/privkey >certs/$user-key.pem
- certtool --generate-request --load-privkey certs/$user-key.pem --outfile certs/$user-req.pem --template certs/$user-cert.tpl
- certtool --generate-certificate --load-request certs/$user-req.pem --outfile certs/$user-cert.pem --load-ca-certificate certs/ca-cert.pem --load-ca-privkey certs/ca-key.pem --template certs/$user-cert.tpl
-done
--- /dev/null
+#!/bin/sh
+# A script for creation of all the certificates used by submitd
+# (c) 2007 Martin Mares <mj@ucw.cz>
+
+set -e
+if [ ! -f submit/ca-cert.tpl ] ; then
+ echo >&2 "Please run from the MO root directory."
+ exit 1
+fi
+
+umask 033
+rm -rf certs
+mkdir certs
+
+echo "### Creating CA certificate ###"
+submit/privkey >certs/ca-key.pem
+certtool --generate-self-signed --load-privkey certs/ca-key.pem --outfile certs/ca-cert.pem --template submit/ca-cert.tpl
+
+echo "### Creating server certificate ###"
+submit/privkey >certs/server-key.pem
+certtool --generate-request --load-privkey certs/server-key.pem --outfile certs/server-req.pem --template submit/server-cert.tpl
+certtool --generate-certificate --load-request certs/server-req.pem --outfile certs/server-cert.pem --load-ca-certificate certs/ca-cert.pem --load-ca-privkey certs/ca-key.pem --template submit/server-cert.tpl
+
+seq=1
+for user in `bin/mo-get-users` ; do
+ seq=$(($seq+1))
+ echo "### Creating certificate for user #$seq ($user) ###"
+ sed <submit/client-cert.tpl >certs/$user-cert.tpl "s/cn = \".*\"/cn = \"$user\"/; s/serial = .*/serial = $seq/;"
+ submit/privkey >certs/$user-key.pem
+ certtool --generate-request --load-privkey certs/$user-key.pem --outfile certs/$user-req.pem --template certs/$user-cert.tpl
+ certtool --generate-certificate --load-request certs/$user-req.pem --outfile certs/$user-cert.pem --load-ca-certificate certs/ca-cert.pem --load-ca-privkey certs/ca-key.pem --template certs/$user-cert.tpl
+done
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-BEGIN {
- defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
-}
-use lib $ENV{"MO_ROOT"} . "/lib/perl5";
-use lib $ENV{"MO_ROOT"} . "/submit/lib/perl5";
-
-use MO::Submit;
-use Sherlock::Object;
-use POSIX;
-
-@ARGV == 0 or die "Usage: remote-status\n";
-
-my $conn = new MO::Submit;
-$conn->connect or die $conn->{"error"} . "\n";
-
-sub or_die($) {
- my $r = shift @_;
- if (!defined $r) { die $conn->{"error"} . "\n"; }
- my $err = $r->get("-");
- if ($err) { die "$err\n"; }
-}
-
-my $r = new Sherlock::Object("!" => "STATUS");
-$r = $conn->request($r);
-or_die($r);
-#$r->write_indented(*STDOUT);
-
-foreach my $t ($r->getarray("(T")) {
- my $task = $t->get("T");
- foreach my $p ($t->getarray("(P")) {
- my $part = $p->get("P");
- my $name = $task;
- $part eq $task or $name .= "/$part";
- printf "%-16s", $name;
-
- my $current_ver = $p->get("V");
- my $printed = 0;
- foreach my $v ($p->getarray("(V")) {
- if ($v->get("V") == $current_ver) {
- my $time = strftime("%H:%M:%S", localtime $v->get("T"));
- print "OK (",
- "$part.", $v->get("X"), ", ",
- $v->get("L"), " bytes, ",
- $v->get("S"), " $time)\n";
- $printed = 1;
- }
- }
- $printed or print "---\n";
- }
-}
-
-$conn->disconnect;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+BEGIN {
+ defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
+}
+use lib $ENV{"MO_ROOT"} . "/lib/perl5";
+use lib $ENV{"MO_ROOT"} . "/submit/lib/perl5";
+
+use MO::Submit;
+use Sherlock::Object;
+use POSIX;
+
+@ARGV == 0 or die "Usage: remote-status\n";
+
+my $conn = new MO::Submit;
+$conn->connect or die $conn->{"error"} . "\n";
+
+sub or_die($) {
+ my $r = shift @_;
+ if (!defined $r) { die $conn->{"error"} . "\n"; }
+ my $err = $r->get("-");
+ if ($err) { die "$err\n"; }
+}
+
+my $r = new Sherlock::Object("!" => "STATUS");
+$r = $conn->request($r);
+or_die($r);
+#$r->write_indented(*STDOUT);
+
+foreach my $t ($r->getarray("(T")) {
+ my $task = $t->get("T");
+ foreach my $p ($t->getarray("(P")) {
+ my $part = $p->get("P");
+ my $name = $task;
+ $part eq $task or $name .= "/$part";
+ printf "%-16s", $name;
+
+ my $current_ver = $p->get("V");
+ my $printed = 0;
+ foreach my $v ($p->getarray("(V")) {
+ if ($v->get("V") == $current_ver) {
+ my $time = strftime("%H:%M:%S", localtime $v->get("T"));
+ print "OK (",
+ "$part.", $v->get("X"), ", ",
+ $v->get("L"), " bytes, ",
+ $v->get("S"), " $time)\n";
+ $printed = 1;
+ }
+ }
+ $printed or print "---\n";
+ }
+}
+
+$conn->disconnect;
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-BEGIN {
- defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
-}
-use lib $ENV{"MO_ROOT"} . "/submit";
-use lib $ENV{"MO_ROOT"} . "/lib/perl5";
-
-use MO::Submit;
-use Sherlock::Object;
-use File::stat;
-
-@ARGV == 2 || @ARGV == 3 or die "Usage: remote-submit <task> [<part>] <filename>\n";
-my $task = $ARGV[0];
-my $part = $task;
-if (@ARGV == 3) {
- $part = $ARGV[1];
- shift @ARGV;
-}
-my $file = $ARGV[1];
-my ($ext) = ($file =~ /\.([^.]+)$/) or die "Unable to determine filename extension\n";
-
-open F, $file or die "Unable to open $file: $!\n";
-my $s = stat(*F) or die;
-my $size = $s->size;
-
-my $conn = new MO::Submit;
-
-my $he = $conn->write_history($task, $part, $ext, $file);
-if (defined $he) { die "$he\n"; }
-
-$conn->connect or die $conn->{"error"} . "\n";
-
-sub or_die($) {
- my $r = shift @_;
- if (!defined $r) { die $conn->{"error"} . "\n"; }
- my $err = $r->get("-");
- if ($err) { die "$err\n"; }
-}
-
-my $r = new Sherlock::Object("!" => "SUBMIT", "T" => $task, "P" => $part, "X" => $ext, "S" => $size);
-$r = $conn->request($r);
-or_die($r);
-
-$r = $conn->send_file(\*F, $size);
-or_die($r);
-
-print "Submitted OK.\n";
-
-$conn->disconnect;
-close F;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+BEGIN {
+ defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
+}
+use lib $ENV{"MO_ROOT"} . "/submit";
+use lib $ENV{"MO_ROOT"} . "/lib/perl5";
+
+use MO::Submit;
+use Sherlock::Object;
+use File::stat;
+
+@ARGV == 2 || @ARGV == 3 or die "Usage: remote-submit <task> [<part>] <filename>\n";
+my $task = $ARGV[0];
+my $part = $task;
+if (@ARGV == 3) {
+ $part = $ARGV[1];
+ shift @ARGV;
+}
+my $file = $ARGV[1];
+my ($ext) = ($file =~ /\.([^.]+)$/) or die "Unable to determine filename extension\n";
+
+open F, $file or die "Unable to open $file: $!\n";
+my $s = stat(*F) or die;
+my $size = $s->size;
+
+my $conn = new MO::Submit;
+
+my $he = $conn->write_history($task, $part, $ext, $file);
+if (defined $he) { die "$he\n"; }
+
+$conn->connect or die $conn->{"error"} . "\n";
+
+sub or_die($) {
+ my $r = shift @_;
+ if (!defined $r) { die $conn->{"error"} . "\n"; }
+ my $err = $r->get("-");
+ if ($err) { die "$err\n"; }
+}
+
+my $r = new Sherlock::Object("!" => "SUBMIT", "T" => $task, "P" => $part, "X" => $ext, "S" => $size);
+$r = $conn->request($r);
+or_die($r);
+
+$r = $conn->send_file(\*F, $size);
+or_die($r);
+
+print "Submitted OK.\n";
+
+$conn->disconnect;
+close F;
+++ /dev/null
-#!/usr/bin/perl
-
-use lib "lib/perl5";
-use Sherlock::Object;
-
-foreach my $user (split /\s+/,`cd solutions && echo *`) {
- print "$user:\t";
- if (open S, "solutions/$user/status") {
- my @status = ();
- my $s = new Sherlock::Object;
- $s->read(\*S) or die "Cannot load status";
- foreach my $t ($s->getarray("(T")) {
- my $task = $t->get("T");
- foreach my $p ($t->getarray("(P")) {
- my $part = $p->get("P");
- my $ver = $p->get("V");
- my $name = $task . ($part eq $task ? "" : "/$part");
- push @status, "$name(v$ver)";
- }
- }
- close S;
- print join(" ", @status), "\n";
- } else {
- print "---\n";
- }
-}
--- /dev/null
+#!/usr/bin/perl
+
+use lib "lib/perl5";
+use Sherlock::Object;
+
+foreach my $user (split /\s+/,`cd solutions && echo *`) {
+ print "$user:\t";
+ if (open S, "solutions/$user/status") {
+ my @status = ();
+ my $s = new Sherlock::Object;
+ $s->read(\*S) or die "Cannot load status";
+ foreach my $t ($s->getarray("(T")) {
+ my $task = $t->get("T");
+ foreach my $p ($t->getarray("(P")) {
+ my $part = $p->get("P");
+ my $ver = $p->get("V");
+ my $name = $task . ($part eq $task ? "" : "/$part");
+ push @status, "$name(v$ver)";
+ }
+ }
+ close S;
+ print join(" ", @status), "\n";
+ } else {
+ print "---\n";
+ }
+}