File Coverage

blib/lib/RPC/PlServer.pm
Criterion Covered Total %
statement 6 132 4.5
branch 0 70 0.0
condition 0 20 0.0
subroutine 2 15 13.3
pod 0 13 0.0
total 8 250 3.2


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             #
4             # PlRPC - Perl RPC, package for writing simple, RPC like clients and
5             # servers
6             #
7             #
8             # Copyright (c) 1997,1998 Jochen Wiedmann
9             #
10             # You may distribute under the terms of either the GNU General Public
11             # License or the Artistic License, as specified in the Perl README file.
12             #
13             # Author: Jochen Wiedmann
14             # Email: jochen.wiedmann at freenet.de
15             #
16             #
17              
18 1     1   9350 use strict;
  1         3  
  1         399  
19              
20             require Net::Daemon;
21             require RPC::PlServer::Comm;
22              
23              
24             package RPC::PlServer;
25              
26             @RPC::PlServer::ISA = qw(Net::Daemon);
27             $RPC::PlServer::VERSION = '0.2020';
28              
29              
30             ############################################################################
31             #
32             # Name: Version (Class method)
33             #
34             # Purpose: Returns version string
35             #
36             # Inputs: $class - This class
37             #
38             # Result: Version string; suitable for printed by "--version"
39             #
40             ############################################################################
41              
42             sub Version ($) {
43 0     0 0   "RPC::PlServer application, Copyright (C) 1997, 1998, Jochen Wiedmann";
44             }
45              
46              
47             ############################################################################
48             #
49             # Name: Options (Class method)
50             #
51             # Purpose: Returns a hash ref of command line options
52             #
53             # Inputs: $class - This class
54             #
55             # Result: Options array; any option is represented by a hash ref;
56             # used keys are 'template', a string suitable for describing
57             # the option to Getopt::Long::GetOptions and 'description',
58             # a string for the Usage message
59             #
60             ############################################################################
61              
62             sub Options ($) {
63 0     0 0   my $options = shift->SUPER::Options();
64 0           $options->{'maxmessage'} =
65             { 'template' => 'maxmessage=i',
66             'description' => '--maxmessage '
67             . 'Set max message size to (Default 65535).'
68             };
69 0           $options->{'compression'} =
70             { 'template' => 'compression=s',
71             'description' => '--compression '
72             . 'Set compression type to off (default) or gzip.'
73             };
74 0           $options;
75             }
76              
77              
78             ############################################################################
79             #
80             # Name: AcceptApplication, AcceptVersion, AcceptUser
81             # (Instance methods)
82             #
83             # Purpose: Called for authentication purposes; these three in common
84             # are replacing Net::Daemon's Accept().
85             #
86             # Inputs: $self - Server instance
87             # $app - Application name
88             # $version - Version number
89             # $user, $password - User name and password
90             #
91             # Result: TRUE, if the client has successfully authorized, FALSE
92             # otherwise. The AcceptUser method (being called as the
93             # last) may additionally return an array ref as a TRUE
94             # value: This is treated as welcome message.
95             #
96             ############################################################################
97              
98             sub AcceptApplication ($$) {
99 0     0 0   my $self = shift; my $app = shift;
  0            
100 0           $self->Debug("Client requests application $app");
101 0           UNIVERSAL::isa($self, $app);
102             }
103              
104             sub AcceptVersion ($$) {
105 0     0 0   my $self = shift; my $version = shift;
  0            
106 0           $self->Debug("Client requests version $version");
107 1     1   6 no strict 'refs';
  1         2  
  1         1371  
108 0           my $myversion = ${ref($self) . "::VERSION"};
  0            
109 0 0         ($version <= $myversion) ? 1 : 0;
110             }
111              
112             sub AcceptUser ($$$) {
113 0     0 0   my $self = shift; my $user = shift; my $password = shift;
  0            
  0            
114              
115 0           my $client = $self->{'client'};
116 0 0         return 1 unless $client->{'users'};
117 0           my $users = $client->{'users'};
118 0           foreach my $u (@$users) {
119 0           my $au;
120 0 0         if (ref($u)) {
121 0           $au = $u;
122 0 0         $u = defined($u->{'name'}) ? $u->{'name'} : '';
123             }
124 0 0         if ($u eq $user) {
125 0           $self->{'authorized_user'} = $au;
126 0           return 1;
127             }
128             }
129 0           0;
130             }
131              
132             sub Accept ($) {
133 0     0 0   my $self = shift;
134 0           my $socket = $self->{'socket'};
135 0           my $comm = $self->{'comm'};
136 0 0         return 0 if (!$self->SUPER::Accept());
137 0           my $client;
138 0 0         if ($client = $self->{'client'}) {
139 0 0         if (my $cipher = $client->{'cipher'}) {
140 0           $self->Debug("Host encryption: %s", $cipher);
141 0           $self->{'cipher'} = $cipher;
142             }
143             }
144              
145 0           my $msg = $comm->Read($socket);
146 0 0         die "Unexpected EOF from client" unless defined $msg;
147 0 0         die "Login message: Expected array, got $msg" unless ref($msg) eq 'ARRAY';
148              
149 0   0       my $app = $self->{'application'} = $msg->[0] || '';
150 0   0       my $version = $self->{'version'} = $msg->[1] || 0;
151 0   0       my $user = $self->{'user'} = $msg->[2] || '';
152 0   0       my $password = $self->{'password'} = $msg->[3] || '';
153              
154 0           $self->Debug("Client logs in: Application %s, version %s, user %s",
155             $app, $version, $user);
156              
157 0 0         if (!$self->AcceptApplication($app)) {
158 0           $comm->Write($socket,
159             [0, "This is a " . ref($self) . " server, go away!"]);
160 0           return 0;
161             }
162 0 0         if (!$self->AcceptVersion($version)) {
163 0           $comm->Write($socket,
164             [0, "Sorry, but I am not running version $version."]);
165 0           return 0;
166             }
167 0           my $result;
168 0 0         if (!($result = $self->AcceptUser($user, $password))) {
169 0           $comm->Write($socket,
170             [0, "User $user is not permitted to connect."]);
171 0           return 0;
172             }
173 0 0         $comm->Write($socket, (ref($result) ? $result : [1, "Welcome!"]));
174 0 0         if (my $au = $self->{'authorized_user'}) {
175 0 0 0       if (ref($au) && (my $cipher = $au->{'cipher'})) {
176 0           $self->Debug("User encryption: %s", $cipher);
177 0           $self->{'cipher'} = $cipher;
178             }
179             }
180              
181 0 0         if (my $client = $self->{'client'}) {
182 0 0         if (my $methods = $client->{'methods'}) {
183 0           $self->{'methods'} = $methods;
184             }
185             }
186 0 0         if (my $au = $self->{'authorized_user'}) {
187 0 0         if (my $methods = $au->{'methods'}) {
188 0           $self->{'methods'} = $methods;
189             }
190             }
191              
192 0           1;
193             }
194              
195              
196             ############################################################################
197             #
198             # Name: new (Class method)
199             #
200             # Purpose: Constructor
201             #
202             # Inputs: $class - This class
203             # $attr - Hash ref of attributes
204             # $args - Array ref of command line arguments
205             #
206             # Result: Server object for success, error message otherwise
207             #
208             ############################################################################
209              
210             sub new ($$;$) {
211 0     0 0   my $self = shift->SUPER::new(@_);
212 0           $self->{'comm'} = RPC::PlServer::Comm->new($self);
213 0           $self;
214             }
215              
216              
217             ############################################################################
218             #
219             # Name: Run
220             #
221             # Purpose: Process client requests
222             #
223             # Inputs: $self - Server instance
224             #
225             # Returns: Nothing, dies in case of errors.
226             #
227             ############################################################################
228              
229             sub Run ($) {
230 0     0 0   my $self = shift;
231 0           my $comm = $self->{'comm'};
232 0           my $socket = $self->{'socket'};
233              
234 0           while (!$self->Done()) {
235 0           my $msg = $comm->Read($socket);
236 0 0         last unless defined($msg);
237 0 0         die "Expected array" unless ref($msg) eq 'ARRAY';
238 0           my($error, $command);
239 0 0         if (!($command = shift @$msg)) {
240 0           $error = "Expected method name";
241             } else {
242 0 0         if ($self->{'methods'}) {
243 0           my $class = $self->{'methods'}->{ref($self)};
244 0 0 0       if (!$class || !$class->{$command}) {
245 0           $error = "Not permitted for method $command of class "
246             . ref($self);
247             }
248             }
249 0 0         if (!$error) {
250 0           $self->Debug("Client executes method $command");
251 0           my @result = eval { $self->$command(@$msg) };
  0            
252 0 0         if ($@) {
253 0           $error = "Failed to execute method $command: $@";
254             } else {
255 0           $comm->Write($socket, \@result);
256             }
257             }
258             }
259 0 0         if ($error) {
260 0           $comm->Write($socket, \$error);
261             }
262             }
263             }
264              
265              
266             ############################################################################
267             #
268             # Name: StoreHandle, NewHandle, UseHandle, DestroyHandle,
269             # CallMethod
270             #
271             # Purpose: Support functions for working with objects
272             #
273             # Inputs: $self - server instance
274             # $object - Server side object
275             # $handle - Client side handle
276             #
277             ############################################################################
278              
279             sub StoreHandle ($$) {
280 0     0 0   my $self = shift; my $object = shift;
  0            
281 0           my $handle = "$object";
282 0           $self->{'handles'}->{$handle} = $object;
283 0           $handle;
284             }
285              
286             sub NewHandle ($$$@) {
287 0     0 0   my($self, $handle, $method, @args) = @_;
288 0           my $object = $self->CallMethod($handle, $method, @args);
289 0 0         die "Constructor $method didn't return a true value" unless $object;
290 0           $self->StoreHandle($object)
291             }
292              
293             sub UseHandle ($$) {
294 0     0 0   my $self = shift; my $handle = shift;
  0            
295 0 0         $self->{'handles'}->{$handle} || die "No such object: $handle";
296             }
297              
298             sub DestroyHandle ($$) {
299 0     0 0   my $self = shift; my $handle = shift;
  0            
300 0 0         (delete $self->{'handles'}->{$handle}) || die "No such object: $handle";
301 0           ();
302             }
303              
304             sub CallMethod ($$$@) {
305 0     0 0   my($self, $handle, $method, @args) = @_;
306 0           my($ref, $object);
307              
308 0           my $call_by_instance;
309             {
310 0 0 0       my $lock = lock($Net::Daemon::RegExpLock)
  0            
311             if $Net::Daemon::RegExpLock && $self->{'mode'} eq 'threads';
312 0           $call_by_instance = ($handle =~ /=\w+\(0x/);
313             }
314 0 0         if ($call_by_instance) {
315             # Looks like a call by instance
316 0           $object = $self->UseHandle($handle);
317 0           $ref = ref($object);
318             } else {
319             # Call by class
320 0           $ref = $object = $handle;
321             }
322              
323 0 0         if ($self->{'methods'}) {
324 0           my $class = $self->{'methods'}->{$ref};
325 0 0 0       if (!$class || !$class->{$method}) {
326 0           die "Not permitted for method $method of class $ref";
327             }
328             }
329              
330 0           $object->$method(@args);
331             }
332              
333              
334             1;
335              
336              
337             __END__