]> mj.ucw.cz Git - eval.git/blob - submit/MO/Submit.pm
0ae3f059d35c835c2be98e1e4e0f1e4e29a99b13
[eval.git] / submit / MO / Submit.pm
1 # A Perl module for communicating with the MO Submit Server
2 # (c) 2007 Martin Mares <mj@ucw.cz>
3
4 package MO::Submit;
5
6 use strict;
7 use warnings;
8
9 use IO::Socket::INET;
10 use IO::Socket::SSL; # qw(debug3);
11 use Sherlock::Object;
12
13 sub new($) {
14         my $user = $ENV{"USER"} or die "Environment variable USER not set\n";
15         my $home = $ENV{"HOME"} or die "Environment variable HOME not set\n";
16         my $mo = "$home/.mo";
17         my $root = $ENV{"MO_ROOT"} or die "Environment variable MO_ROOT not set\n";
18         my $self = {
19                 "Server" => "localhost:8888",
20                 "Key" => "$mo/key.pem",
21                 "Cert" => "$mo/cert.pem",
22                 "CACert" => "$mo/ca-cert.pem",
23                 "Trace" => defined $ENV{"MO_SUBMIT_TRACE"},
24                 "user" => $user,
25                 "sk" => undef,
26                 "error" => undef,
27         };
28         return bless $self;
29 }
30
31 sub DESTROY($) {
32         my $self = shift @_;
33         $self->disconnect;
34 }
35
36 sub log($$) {
37         my ($self, $msg) = @_;
38         print STDERR "LOG: $msg\n" if $self->{"Trace"};
39 }
40
41 sub err($$) {
42         my ($self, $msg) = @_;
43         print STDERR "ERROR: $msg\n" if $self->{"Trace"};
44         $self->{"error"} = $msg;
45         $self->disconnect;
46 }
47
48 sub is_connected($) {
49         my $self = shift @_;
50         return defined $self->{"sk"};
51 }
52
53 sub disconnect($) {
54         my $self = shift @_;
55         if ($self->is_connected) {
56                 close $self->{"sk"};
57                 $self->{"sk"} = undef;
58                 $self->log("Disconnected");
59         }
60 }
61
62 sub connect($) {
63         my $self = shift @_;
64         $self->disconnect;
65         $self->log("Connecting to submit server");
66         my $sk = new IO::Socket::INET(
67                 PeerAddr => $self->{"Server"},
68                 Proto => "tcp",
69         );
70         if (!defined $sk) {
71                 $self->err("Cannot connect to server: $!");
72                 return undef;
73         }
74         my $z = <$sk>;
75         if (!defined $z) {
76                 $self->err("Server failed to send a welcome message");
77                 close $sk;
78                 return undef;
79         }
80         chomp $z;
81         if ($z !~ /^\+/) {
82                 $self->err("Server rejected the connection: $z");
83                 close $sk;
84                 return undef;
85         }
86         if ($z =~ /TLS/) {
87                 $self->log("Starting TLS");
88                 $sk = IO::Socket::SSL->start_SSL(
89                         $sk,
90                         SSL_version => 'TLSv1',
91                         SSL_use_cert => 1,
92                         SSL_key_file => $self->{"Key"},
93                         SSL_cert_file => $self->{"Cert"},
94                         SSL_ca_file => $self->{"CACert"},
95                         SSL_verify_mode => 3,
96                 );
97                 if (!defined $sk) {
98                         $self->err("Cannot establish TLS connection: " . IO::Socket::SSL::errstr());
99                         return undef;
100                 }
101         }
102         $self->{"sk"} = $sk;
103
104         $self->log("Logging in");
105         my $req = new Sherlock::Object("U" => $self->{"user"});
106         my $reply = $self->request($req);
107         my $err = $reply->get("-");
108         if (defined $err) {
109                 $self->err("Cannot log in: $err");
110                 return undef;
111         }
112
113         $self->log("Connected");
114         return 1;
115 }
116
117 sub request($$) {
118         my ($self, $obj) = @_;
119         my $sk = $self->{"sk"};
120         $obj->write($sk);       ### FIXME: Flushing
121         if ($sk->error) {
122                 $self->err("Connection broken");
123                 return undef;
124         }
125         print $sk "\n";
126         return $self->reply;
127 }
128
129 sub reply($) {
130         my ($self, $obj) = @_;
131         my $sk = $self->{"sk"};
132         my $reply = new Sherlock::Object;
133         if ($reply->read($sk)) {
134                 return $reply;
135         } else {
136                 $self->err("Connection broken");
137                 return undef;
138         }
139 }
140
141 sub send_file($$$) {
142         my ($self, $fh, $size) = @_;
143         my $sk = $self->{"sk"};
144         while ($size) {
145                 my $l = ($size < 4096 ? $size : 4096);
146                 my $buf = "";
147                 if ($fh->read($buf, $l) != $l) {
148                         $self->err("File shrunk during upload");
149                         return undef;
150                 }
151                 $sk->write($buf, $l);
152                 if ($sk->error) {
153                         $self->err("Connection broken");
154                         return undef;
155                 }
156                 $size -= $l;
157         }
158         return $self->reply;
159 }
160
161 1;