Bug 13159 [QA Followup] - Fix transaction_date and undefined warning
[koha.git] / misc / sip_cli_emulator.pl
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2012-2013 ByWater Solutions
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Socket qw(:crlf);
23 use IO::Socket::INET;
24 use Getopt::Long;
25
26 use C4::SIP::Sip::Constants qw(:all);
27 use C4::SIP::Sip;
28
29 use constant { LANGUAGE => '001' };
30
31 my $help = 0;
32
33 my $host;
34 my $port = '6001';
35
36 my $login_user_id;
37 my $login_password;
38 my $location_code;
39
40 my $patron_identifier;
41 my $patron_password;
42
43 my $item_identifier;
44
45 my $fee_acknowledged = 0;
46
47 my $terminator = q{};
48
49 my @messages;
50
51 GetOptions(
52     "a|address|host|hostaddress=s" => \$host,              # sip server ip
53     "p|port=s"                     => \$port,              # sip server port
54     "su|sip_user=s"                => \$login_user_id,     # sip user
55     "sp|sip_pass=s"                => \$login_password,    # sip password
56     "l|location|location_code=s"   => \$location_code,     # sip location code
57
58     "patron=s"   => \$patron_identifier,    # patron cardnumber or login
59     "password=s" => \$patron_password,      # patron's password
60
61     "i|item=s" => \$item_identifier,
62
63     "fa|fee-acknowledged" => \$fee_acknowledged,
64
65     "t|terminator=s" => \$terminator,
66
67     "m|message=s" => \@messages,
68
69     'h|help|?' => \$help
70 );
71
72 if (   $help
73     || !$host
74     || !$login_user_id
75     || !$login_password
76     || !$location_code )
77 {
78     say &help();
79     exit();
80 }
81
82 $terminator = ( $terminator eq 'CR' ) ? $CR : $CRLF;
83
84 # Set perl to expect the same record terminator it is sending
85 $/ = $terminator;
86
87 my ( $sec, $min, $hour, $day, $month, $year ) = localtime(time);
88 $year += 1900;
89 my $transaction_date = C4::SIP::Sip::timestamp();
90
91 my $terminal_password = $login_password;
92
93 $| = 1;
94 print "Attempting socket connection to $host:$port...";
95
96 my $socket = IO::Socket::INET->new("$host:$port")
97   or die "failed! : $!\n";
98 say "connected!";
99
100 my $handlers = {
101     login => {
102         name       => 'Login',
103         subroutine => \&build_login_command_message,
104         parameters => {
105             login_user_id  => $login_user_id,
106             login_password => $login_password,
107             location_code  => $location_code,
108         },
109     },
110     patron_status_request => {
111         name       => 'Patron Status Request',
112         subroutine => \&build_patron_status_request_command_message,
113         parameters => {
114             transaction_date  => $transaction_date,
115             institution_id    => $location_code,
116             patron_identifier => $patron_identifier,
117             terminal_password => $terminal_password,
118             patron_password   => $patron_password,
119         },
120         optional => [ 'patron_password', ],
121     },
122     patron_information => {
123         name       => 'Patron Information',
124         subroutine => \&build_patron_information_command_message,
125         parameters => {
126             transaction_date  => $transaction_date,
127             institution_id    => $location_code,
128             patron_identifier => $patron_identifier,
129             terminal_password => $terminal_password,
130             patron_password   => $patron_password,
131         },
132         optional => [ 'patron_password', ],
133     },
134     item_information => {
135         name       => 'Item Information',
136         subroutine => \&build_item_information_command_message,
137         parameters => {
138             transaction_date  => $transaction_date,
139             institution_id    => $location_code,
140             item_identifier   => $item_identifier,
141             terminal_password => $terminal_password,
142         },
143         optional => [],
144     },
145     checkout => {
146         name       => 'Checkout',
147         subroutine => \&build_checkout_command_message,
148         parameters => {
149             SC_renewal_policy => 'Y',
150             no_block          => 'N',
151             transaction_date  => $transaction_date,
152             nb_due_date       => undef,
153             institution_id    => $location_code,
154             patron_identifier => $patron_identifier,
155             item_identifier   => $item_identifier,
156             terminal_password => $terminal_password,
157             item_properties   => undef,
158             patron_password   => $patron_password,
159             fee_acknowledged  => $fee_acknowledged,
160             cancel            => undef,
161         },
162         optional => [
163             'nb_due_date',    # defaults to transaction date
164             'item_properties',
165             'patron_password',
166             'fee_acknowledged',
167             'cancel',
168         ],
169     },
170     checkin => {
171         name       => 'Checkin',
172         subroutine => \&build_checkin_command_message,
173         parameters => {
174             no_block          => 'N',
175             transaction_date  => $transaction_date,
176             return_date       => $transaction_date,
177             current_location  => $location_code,
178             institution_id    => $location_code,
179             item_identifier   => $item_identifier,
180             terminal_password => $terminal_password,
181             item_properties   => undef,
182             cancel            => undef,
183         },
184         optional => [
185             'return_date',    # defaults to transaction date
186             'item_properties',
187             'patron_password',
188             'cancel',
189         ],
190     },
191     renew => {
192         name       => 'Renew',
193         subroutine => \&build_renew_command_message,
194         parameters => {
195             third_party_allowed => 'N',
196             no_block            => 'N',
197             transaction_date    => $transaction_date,
198             nb_due_date         => undef,
199             institution_id      => $location_code,
200             patron_identifier   => $patron_identifier,
201             patron_password     => $patron_password,
202             item_identifier     => $item_identifier,
203             title_identifier    => undef,
204             terminal_password   => $terminal_password,
205             item_properties     => undef,
206             fee_acknowledged    => $fee_acknowledged,
207         },
208         optional => [
209             'nb_due_date',    # defaults to transaction date
210             'patron_password',
211             'item_identifier',
212             'title_identifier',
213             'terminal_password',
214             'item_properties',
215             'fee_acknowledged',
216         ],
217     },
218 };
219
220 my $data = run_command_message('login');
221
222 if ( $data =~ '^941' ) {    ## we are logged in
223     foreach my $m (@messages) {
224         say "Trying '$m'";
225
226         my $data = run_command_message($m);
227
228     }
229 }
230 else {
231     say "Login Failed!";
232 }
233
234 sub build_command_message {
235     my ($message) = @_;
236
237     ##FIXME It would be much better to use exception handling so we aren't priting from subs
238     unless ( $handlers->{$message} ) {
239         say "$message is an unsupported command!";
240         return;
241     }
242
243     my $subroutine = $handlers->{$message}->{subroutine};
244     my $parameters = $handlers->{$message}->{parameters};
245     my %optional   = map { $_ => 1 } @{ $handlers->{$message}->{optional} };
246
247     foreach my $key ( keys %$parameters ) {
248         unless ( $parameters->{$key} ) {
249             unless ( $optional{$key} ) {
250                 say "$key is required for $message";
251                 return;
252             }
253         }
254     }
255
256     return &$subroutine($parameters);
257 }
258
259 sub run_command_message {
260     my ($message) = @_;
261
262     my $command_message = build_command_message($message);
263
264     return unless $command_message;
265
266     say "SEND: $command_message";
267     print $socket $command_message . $terminator;
268
269     my $data = <$socket>;
270
271     say "READ: $data";
272
273     return $data;
274 }
275
276 sub build_login_command_message {
277     my ($params) = @_;
278
279     my $login_user_id  = $params->{login_user_id};
280     my $login_password = $params->{login_password};
281     my $location_code  = $params->{location_code};
282
283     return
284         LOGIN . "00"
285       . build_field( FID_LOGIN_UID,     $login_user_id )
286       . build_field( FID_LOGIN_PWD,     $login_password )
287       . build_field( FID_LOCATION_CODE, $location_code );
288 }
289
290 sub build_patron_status_request_command_message {
291     my ($params) = @_;
292
293     my $transaction_date  = $params->{transaction_date};
294     my $institution_id    = $params->{institution_id};
295     my $patron_identifier = $params->{patron_identifier};
296     my $terminal_password = $params->{terminal_password};
297     my $patron_password   = $params->{patron_password};
298
299     return
300         PATRON_STATUS_REQ
301       . LANGUAGE
302       . $transaction_date
303       . build_field( FID_INST_ID,      $institution_id )
304       . build_field( FID_PATRON_ID,    $patron_identifier )
305       . build_field( FID_TERMINAL_PWD, $terminal_password )
306       . build_field( FID_PATRON_PWD,   $patron_password );
307 }
308
309 sub build_patron_information_command_message {
310     my ($params) = @_;
311
312     my $transaction_date  = $params->{transaction_date};
313     my $institution_id    = $params->{institution_id};
314     my $patron_identifier = $params->{patron_identifier};
315     my $terminal_password = $params->{terminal_password};
316     my $patron_password   = $params->{patron_password};
317
318     my $summary = "          ";
319
320     return
321         PATRON_INFO
322       . LANGUAGE
323       . $transaction_date
324       . $summary
325       . build_field( FID_INST_ID,      $institution_id )
326       . build_field( FID_PATRON_ID,    $patron_identifier )
327       . build_field( FID_TERMINAL_PWD, $terminal_password )
328       . build_field( FID_PATRON_PWD,   $patron_password, { optional => 1 } );
329 }
330
331 sub build_item_information_command_message {
332     my ($params) = @_;
333
334     my $transaction_date  = $params->{transaction_date};
335     my $institution_id    = $params->{institution_id};
336     my $item_identifier   = $params->{item_identifier};
337     my $terminal_password = $params->{terminal_password};
338
339     return
340         ITEM_INFORMATION
341       . LANGUAGE
342       . $transaction_date
343       . build_field( FID_INST_ID,      $institution_id )
344       . build_field( FID_ITEM_ID,      $item_identifier )
345       . build_field( FID_TERMINAL_PWD, $terminal_password );
346 }
347
348 sub build_checkout_command_message {
349     my ($params) = @_;
350
351     my $SC_renewal_policy = $params->{SC_renewal_policy} || 'N';
352     my $no_block          = $params->{no_block} || 'N';
353     my $transaction_date  = $params->{transaction_date};
354     my $nb_due_date       = $params->{nb_due_date};
355     my $institution_id    = $params->{institution_id};
356     my $patron_identifier = $params->{patron_identifier};
357     my $item_identifier   = $params->{item_identifier};
358     my $terminal_password = $params->{terminal_password};
359     my $item_properties   = $params->{item_properties};
360     my $patron_password   = $params->{patron_password};
361     my $fee_acknowledged  = $params->{fee_acknowledged} || 'N';
362     my $cancel            = $params->{cancel} || 'N';
363
364     $SC_renewal_policy = $SC_renewal_policy eq 'Y' ? 'Y' : 'N';
365     $no_block          = $no_block          eq 'Y' ? 'Y' : 'N';
366     $fee_acknowledged  = $fee_acknowledged  eq 'Y' ? 'Y' : 'N';
367     $cancel            = $cancel            eq 'Y' ? 'Y' : 'N';
368
369     $nb_due_date ||= $transaction_date;
370
371     return
372         CHECKOUT
373       . $SC_renewal_policy
374       . $no_block
375       . $transaction_date
376       . $nb_due_date
377       . build_field( FID_INST_ID,      $institution_id )
378       . build_field( FID_PATRON_ID,    $patron_identifier )
379       . build_field( FID_ITEM_ID,      $item_identifier )
380       . build_field( FID_TERMINAL_PWD, $terminal_password )
381       . build_field( FID_ITEM_PROPS,   $item_properties, { optional => 1 } )
382       . build_field( FID_PATRON_PWD,   $patron_password, { optional => 1 } )
383       . build_field( FID_FEE_ACK,      $fee_acknowledged, { optional => 1 } )
384       . build_field( FID_CANCEL,       $cancel, { optional => 1 } );
385 }
386
387 sub build_checkin_command_message {
388     my ($params) = @_;
389
390     my $no_block          = $params->{no_block} || 'N';
391     my $transaction_date  = $params->{transaction_date};
392     my $return_date       = $params->{return_date};
393     my $current_location  = $params->{current_location};
394     my $institution_id    = $params->{institution_id};
395     my $item_identifier   = $params->{item_identifier};
396     my $terminal_password = $params->{terminal_password};
397     my $item_properties   = $params->{item_properties};
398     my $cancel            = $params->{cancel} || 'N';
399
400     $no_block = $no_block eq 'Y' ? 'Y' : 'N';
401     $cancel   = $cancel   eq 'Y' ? 'Y' : 'N';
402
403     $return_date ||= $transaction_date;
404
405     return
406         CHECKIN
407       . $no_block
408       . $transaction_date
409       . $return_date
410       . build_field( FID_CURRENT_LOCN, $current_location )
411       . build_field( FID_INST_ID,      $institution_id )
412       . build_field( FID_ITEM_ID,      $item_identifier )
413       . build_field( FID_TERMINAL_PWD, $terminal_password )
414       . build_field( FID_ITEM_PROPS,   $item_properties, { optional => 1 } )
415       . build_field( FID_CANCEL,       $cancel, { optional => 1 } );
416 }
417
418 sub build_renew_command_message {
419     my ($params) = @_;
420
421     my $third_party_allowed = $params->{third_party_allowed} || 'N';
422     my $no_block            = $params->{no_block}            || 'N';
423     my $transaction_date    = $params->{transaction_date};
424     my $nb_due_date         = $params->{nb_due_date};
425     my $institution_id      = $params->{institution_id};
426     my $patron_identifier   = $params->{patron_identifier};
427     my $patron_password     = $params->{patron_password};
428     my $item_identifier     = $params->{item_identifier};
429     my $title_identifier    = $params->{title_identifier};
430     my $terminal_password   = $params->{terminal_password};
431     my $item_properties     = $params->{item_properties};
432     my $fee_acknowledged    = $params->{fee_acknowledged}    || 'N';
433
434     $third_party_allowed = $third_party_allowed eq 'Y' ? 'Y' : 'N';
435     $no_block            = $no_block            eq 'Y' ? 'Y' : 'N';
436     $fee_acknowledged    = $fee_acknowledged    eq 'Y' ? 'Y' : 'N';
437
438     $nb_due_date ||= $transaction_date;
439
440     return
441         RENEW
442       . $third_party_allowed
443       . $no_block
444       . $transaction_date
445       . $nb_due_date
446       . build_field( FID_INST_ID,      $institution_id )
447       . build_field( FID_PATRON_ID,    $patron_identifier )
448       . build_field( FID_PATRON_PWD,   $patron_password, { optional => 1 } )
449       . build_field( FID_ITEM_ID,      $item_identifier )
450       . build_field( FID_TITLE_ID,     $title_identifier )
451       . build_field( FID_TERMINAL_PWD, $terminal_password )
452       . build_field( FID_ITEM_PROPS,   $item_properties, { optional => 1 } )
453       . build_field( FID_FEE_ACK,      $fee_acknowledged, { optional => 1 } );
454 }
455
456 sub build_field {
457     my ( $field_identifier, $value, $params ) = @_;
458
459     $params //= {};
460
461     return q{} if ( $params->{optional} && !$value );
462
463     return $field_identifier . (($value) ? $value : '') . '|';
464 }
465
466 sub help {
467     say q/sip_cli_emulator.pl - SIP command line emulator
468
469 Test a SIP2 service by sending patron status and patron
470 information requests.
471
472 Usage:
473   sip_cli_emulator.pl [OPTIONS]
474
475 Options:
476   --help           display help message
477
478   -a --address     SIP server ip address or host name
479   -p --port        SIP server port
480
481   -su --sip_user   SIP server login username
482   -sp --sip_pass   SIP server login password
483
484   -l --location    SIP location code
485
486   --patron         ILS patron cardnumber or username
487   --password       ILS patron password
488
489   --item           ILS item identifier ( item barcode )
490
491   -t --terminator  SIP2 message terminator, either CR, or CRLF
492                    (defaults to CRLF)
493
494   -fa --fee-acknowledged Sends a confirmation of checkout fee
495
496   -m --message     SIP2 message to execute
497
498   Implemented Messages:
499     patron_status_request
500     patron_information
501     item_information
502     checkout
503     checkin
504     renew
505
506 /
507 }