Bug 30477: Add new UNIMARC installer translation files
[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 $summary;
44
45 my $item_identifier;
46
47 my $fee_acknowledged = 0;
48
49 my $fee_type;
50 my $payment_type;
51 my $currency_type;
52 my $fee_amount;
53 my $fee_identifier;
54 my $transaction_id;
55 my $pickup_location;
56 my $hold_mode;
57
58 my $terminator = q{};
59
60 my @messages;
61
62 GetOptions(
63     "a|address|host|hostaddress=s" => \$host,              # sip server ip
64     "p|port=s"                     => \$port,              # sip server port
65     "su|sip_user=s"                => \$login_user_id,     # sip user
66     "sp|sip_pass=s"                => \$login_password,    # sip password
67     "l|location|location_code=s"   => \$location_code,     # sip location code
68
69     "patron=s"   => \$patron_identifier,                   # patron cardnumber or login
70     "password=s" => \$patron_password,                     # patron's password
71
72     "i|item=s" => \$item_identifier,
73
74     "fa|fee-acknowledged" => \$fee_acknowledged,
75
76     "s|summary=s" => \$summary,
77
78     "fee-type=s"        => \$fee_type,
79     "payment-type=s"    => \$payment_type,
80     "currency-type=s"   => \$currency_type,
81     "fee-amount=s"      => \$fee_amount,
82     "fee-identifier=s"  => \$fee_identifier,
83     "transaction-id=s"  => \$transaction_id,
84     "pickup-location=s" => \$pickup_location,
85     "hold-mode=s"       => \$hold_mode,
86
87     "t|terminator=s" => \$terminator,
88
89     "m|message=s" => \@messages,
90
91     'h|help|?' => \$help
92 );
93
94 if (   $help
95     || !$host
96     || !$login_user_id
97     || !$login_password
98     || !$location_code )
99 {
100     say &help();
101     exit();
102 }
103
104 $terminator = ( $terminator eq 'CR' ) ? $CR : $CRLF;
105
106 # Set perl to expect the same record terminator it is sending
107 $/ = $terminator;
108
109 my $transaction_date = C4::SIP::Sip::timestamp();
110
111 my $terminal_password = $login_password;
112
113 $| = 1;
114 print "Attempting socket connection to $host:$port...";
115
116 my $socket = IO::Socket::INET->new("$host:$port")
117   or die "failed! : $!\n";
118 say "connected!";
119
120 my $handlers = {
121     login => {
122         name       => 'Login',
123         subroutine => \&build_login_command_message,
124         parameters => {
125             login_user_id  => $login_user_id,
126             login_password => $login_password,
127             location_code  => $location_code,
128         },
129     },
130     sc_status_request => {
131         name       => 'SC Status',
132         subroutine => \&build_sc_status_command_message,
133         parameters => {
134         },
135     },
136     patron_status_request => {
137         name       => 'Patron Status Request',
138         subroutine => \&build_patron_status_request_command_message,
139         parameters => {
140             transaction_date  => $transaction_date,
141             institution_id    => $location_code,
142             patron_identifier => $patron_identifier,
143             terminal_password => $terminal_password,
144             patron_password   => $patron_password,
145         },
146         optional => [ 'patron_password', ],
147     },
148     patron_information => {
149         name       => 'Patron Information',
150         subroutine => \&build_patron_information_command_message,
151         parameters => {
152             transaction_date  => $transaction_date,
153             institution_id    => $location_code,
154             patron_identifier => $patron_identifier,
155             terminal_password => $terminal_password,
156             patron_password   => $patron_password,
157             summary           => $summary,
158         },
159         optional => [ 'patron_password', 'summary' ],
160     },
161     item_information => {
162         name       => 'Item Information',
163         subroutine => \&build_item_information_command_message,
164         parameters => {
165             transaction_date  => $transaction_date,
166             institution_id    => $location_code,
167             item_identifier   => $item_identifier,
168             terminal_password => $terminal_password,
169         },
170         optional => [],
171     },
172     checkout => {
173         name       => 'Checkout',
174         subroutine => \&build_checkout_command_message,
175         parameters => {
176             SC_renewal_policy => 'Y',
177             no_block          => 'N',
178             transaction_date  => $transaction_date,
179             nb_due_date       => undef,
180             institution_id    => $location_code,
181             patron_identifier => $patron_identifier,
182             item_identifier   => $item_identifier,
183             terminal_password => $terminal_password,
184             item_properties   => undef,
185             patron_password   => $patron_password,
186             fee_acknowledged  => $fee_acknowledged,
187             cancel            => undef,
188         },
189         optional => [
190             'nb_due_date',    # defaults to transaction date
191             'item_properties',
192             'patron_password',
193             'fee_acknowledged',
194             'cancel',
195         ],
196     },
197     checkin => {
198         name       => 'Checkin',
199         subroutine => \&build_checkin_command_message,
200         parameters => {
201             no_block          => 'N',
202             transaction_date  => $transaction_date,
203             return_date       => $transaction_date,
204             current_location  => $location_code,
205             institution_id    => $location_code,
206             item_identifier   => $item_identifier,
207             terminal_password => $terminal_password,
208             item_properties   => undef,
209             cancel            => undef,
210         },
211         optional => [
212             'return_date',    # defaults to transaction date
213             'item_properties',
214             'patron_password',
215             'cancel',
216         ],
217     },
218     renew => {
219         name       => 'Renew',
220         subroutine => \&build_renew_command_message,
221         parameters => {
222             third_party_allowed => 'N',
223             no_block            => 'N',
224             transaction_date    => $transaction_date,
225             nb_due_date         => undef,
226             institution_id      => $location_code,
227             patron_identifier   => $patron_identifier,
228             patron_password     => $patron_password,
229             item_identifier     => $item_identifier,
230             title_identifier    => undef,
231             terminal_password   => $terminal_password,
232             item_properties     => undef,
233             fee_acknowledged    => $fee_acknowledged,
234         },
235         optional => [
236             'nb_due_date',    # defaults to transaction date
237             'patron_password',
238             'item_identifier',
239             'title_identifier',
240             'terminal_password',
241             'item_properties',
242             'fee_acknowledged',
243         ],
244     },
245     fee_paid => {
246         name       => 'Fee Paid',
247         subroutine => \&build_fee_paid_command_message,
248         parameters => {
249             transaction_date  => $transaction_date,
250             fee_type          => $fee_type,
251             payment_type      => $payment_type,
252             currency_type     => $currency_type,
253             fee_amount        => $fee_amount,
254             institution_id    => $location_code,
255             patron_identifier => $patron_identifier,
256             terminal_password => $terminal_password,
257             patron_password   => $patron_password,
258             fee_identifier    => $fee_identifier,
259             transaction_id    => $transaction_id,
260         },
261         optional => [
262             'fee_type', # has default
263             'payment_type', # has default
264             'currency_type', #has default
265             'terminal_password',
266             'patron_password',
267             'fee_identifier',
268             'transaction_id',
269         ],
270     },
271     hold => {
272         name       => 'Hold',
273         subroutine => \&build_hold_command_message,
274         parameters => {
275             hold_mode           => $hold_mode eq '-' ? '-' : '+',
276             transaction_date    => $transaction_date,
277             expiration_date     => undef,
278             pickup_location     => $pickup_location,
279             hold_type           => undef,
280             institution_id      => $location_code,
281             patron_identifier   => $patron_identifier,
282             patron_password     => $patron_password,
283             item_identifier     => $item_identifier,
284             title_identifier    => undef,
285             terminal_password   => $terminal_password,
286             fee_acknowledged    => $fee_acknowledged,
287         },
288         optional => [
289             'expiration_date',
290             'pickup_location',
291             'hold_type',
292             'patron_password',
293             'item_identifier',
294             'title_identifier',
295             'terminal_password',
296             'fee_acknowledged',
297         ],
298     },
299 };
300
301 my $data = run_command_message('login');
302
303 if ( $data =~ '^941' ) {    ## we are logged in
304     foreach my $m (@messages) {
305         say "Trying '$m'";
306
307         my $data = run_command_message($m);
308
309     }
310 }
311 else {
312     say "Login Failed!";
313 }
314
315 sub build_command_message {
316     my ($message) = @_;
317
318     ##FIXME It would be much better to use exception handling so we aren't priting from subs
319     unless ( $handlers->{$message} ) {
320         say "$message is an unsupported command!";
321         return;
322     }
323
324     my $subroutine = $handlers->{$message}->{subroutine};
325     my $parameters = $handlers->{$message}->{parameters};
326     my %optional   = map { $_ => 1 } @{ $handlers->{$message}->{optional} };
327
328     foreach my $key ( keys %$parameters ) {
329         unless ( $parameters->{$key} ) {
330             unless ( $optional{$key} ) {
331                 say "$key is required for $message";
332                 return;
333             }
334         }
335     }
336
337     return &$subroutine($parameters);
338 }
339
340 sub run_command_message {
341     my ($message) = @_;
342
343     my $command_message = build_command_message($message);
344
345     return unless $command_message;
346
347     say "SEND: $command_message";
348     print $socket $command_message . $terminator;
349
350     my $data = <$socket>;
351
352     say "READ: $data";
353
354     return $data;
355 }
356
357 sub build_login_command_message {
358     my ($params) = @_;
359
360     my $login_user_id  = $params->{login_user_id};
361     my $login_password = $params->{login_password};
362     my $location_code  = $params->{location_code};
363
364     return
365         LOGIN . "00"
366       . build_field( FID_LOGIN_UID,     $login_user_id )
367       . build_field( FID_LOGIN_PWD,     $login_password )
368       . build_field( FID_LOCATION_CODE, $location_code );
369 }
370
371 sub build_sc_status_command_message {
372     my ($params) = @_;
373
374     return SC_STATUS . "0" . "030" . "2.00";
375 }
376
377 sub build_patron_status_request_command_message {
378     my ($params) = @_;
379
380     my $transaction_date  = $params->{transaction_date};
381     my $institution_id    = $params->{institution_id};
382     my $patron_identifier = $params->{patron_identifier};
383     my $terminal_password = $params->{terminal_password};
384     my $patron_password   = $params->{patron_password};
385
386     return
387         PATRON_STATUS_REQ
388       . LANGUAGE
389       . $transaction_date
390       . build_field( FID_INST_ID,      $institution_id )
391       . build_field( FID_PATRON_ID,    $patron_identifier )
392       . build_field( FID_TERMINAL_PWD, $terminal_password )
393       . build_field( FID_PATRON_PWD,   $patron_password );
394 }
395
396 sub build_patron_information_command_message {
397     my ($params) = @_;
398
399     my $transaction_date  = $params->{transaction_date};
400     my $institution_id    = $params->{institution_id};
401     my $patron_identifier = $params->{patron_identifier};
402     my $terminal_password = $params->{terminal_password};
403     my $patron_password   = $params->{patron_password};
404     my $summary           = $params->{summary};
405
406     $summary //= "          ";
407
408     return
409         PATRON_INFO
410       . LANGUAGE
411       . $transaction_date
412       . $summary
413       . build_field( FID_INST_ID,      $institution_id )
414       . build_field( FID_PATRON_ID,    $patron_identifier )
415       . build_field( FID_TERMINAL_PWD, $terminal_password )
416       . build_field( FID_PATRON_PWD,   $patron_password, { optional => 1 } );
417 }
418
419 sub build_item_information_command_message {
420     my ($params) = @_;
421
422     my $transaction_date  = $params->{transaction_date};
423     my $institution_id    = $params->{institution_id};
424     my $item_identifier   = $params->{item_identifier};
425     my $terminal_password = $params->{terminal_password};
426
427     return
428         ITEM_INFORMATION
429       . LANGUAGE
430       . $transaction_date
431       . build_field( FID_INST_ID,      $institution_id )
432       . build_field( FID_ITEM_ID,      $item_identifier )
433       . build_field( FID_TERMINAL_PWD, $terminal_password );
434 }
435
436 sub build_checkout_command_message {
437     my ($params) = @_;
438
439     my $SC_renewal_policy = $params->{SC_renewal_policy} || 'N';
440     my $no_block          = $params->{no_block} || 'N';
441     my $transaction_date  = $params->{transaction_date};
442     my $nb_due_date       = $params->{nb_due_date};
443     my $institution_id    = $params->{institution_id};
444     my $patron_identifier = $params->{patron_identifier};
445     my $item_identifier   = $params->{item_identifier};
446     my $terminal_password = $params->{terminal_password};
447     my $item_properties   = $params->{item_properties};
448     my $patron_password   = $params->{patron_password};
449     my $fee_acknowledged  = $params->{fee_acknowledged} || 'N';
450     my $cancel            = $params->{cancel} || 'N';
451
452     $SC_renewal_policy = $SC_renewal_policy eq 'Y' ? 'Y' : 'N';
453     $no_block          = $no_block          eq 'Y' ? 'Y' : 'N';
454     $fee_acknowledged  = $fee_acknowledged  eq 'Y' ? 'Y' : 'N';
455     $cancel            = $cancel            eq 'Y' ? 'Y' : 'N';
456
457     $nb_due_date ||= $transaction_date;
458
459     return
460         CHECKOUT
461       . $SC_renewal_policy
462       . $no_block
463       . $transaction_date
464       . $nb_due_date
465       . build_field( FID_INST_ID,      $institution_id )
466       . build_field( FID_PATRON_ID,    $patron_identifier )
467       . build_field( FID_ITEM_ID,      $item_identifier )
468       . build_field( FID_TERMINAL_PWD, $terminal_password )
469       . build_field( FID_ITEM_PROPS,   $item_properties, { optional => 1 } )
470       . build_field( FID_PATRON_PWD,   $patron_password, { optional => 1 } )
471       . build_field( FID_FEE_ACK,      $fee_acknowledged, { optional => 1 } )
472       . build_field( FID_CANCEL,       $cancel, { optional => 1 } );
473 }
474
475 sub build_checkin_command_message {
476     my ($params) = @_;
477
478     my $no_block          = $params->{no_block} || 'N';
479     my $transaction_date  = $params->{transaction_date};
480     my $return_date       = $params->{return_date};
481     my $current_location  = $params->{current_location};
482     my $institution_id    = $params->{institution_id};
483     my $item_identifier   = $params->{item_identifier};
484     my $terminal_password = $params->{terminal_password};
485     my $item_properties   = $params->{item_properties};
486     my $cancel            = $params->{cancel} || 'N';
487
488     $no_block = $no_block eq 'Y' ? 'Y' : 'N';
489     $cancel   = $cancel   eq 'Y' ? 'Y' : 'N';
490
491     $return_date ||= $transaction_date;
492
493     return
494         CHECKIN
495       . $no_block
496       . $transaction_date
497       . $return_date
498       . build_field( FID_CURRENT_LOCN, $current_location )
499       . build_field( FID_INST_ID,      $institution_id )
500       . build_field( FID_ITEM_ID,      $item_identifier )
501       . build_field( FID_TERMINAL_PWD, $terminal_password )
502       . build_field( FID_ITEM_PROPS,   $item_properties, { optional => 1 } )
503       . build_field( FID_CANCEL,       $cancel, { optional => 1 } );
504 }
505
506 sub build_hold_command_message {
507     my ($params) = @_;
508
509     my $hold_mode         = $params->{hold_mode};
510     my $transaction_date  = $params->{transaction_date};
511     my $expiration_date   = $params->{expiration_date};
512     my $pickup_location   = $params->{pickup_location};
513     my $hold_type         = $params->{hold_type};
514     my $institution_id    = $params->{institution_id};
515     my $patron_identifier = $params->{patron_identifier};
516     my $patron_password   = $params->{patron_password};
517     my $item_identifier   = $params->{item_identifier};
518     my $title_identifier  = $params->{title_identifier};
519     my $terminal_password = $params->{terminal_password};
520     my $fee_acknowledged  = $params->{fee_acknowledged} || 'N';
521
522     return
523         HOLD
524       . $hold_mode
525       . $transaction_date
526       . build_field( FID_EXPIRATION,   $expiration_date,   { optional => 1 } )
527       . build_field( FID_PICKUP_LOCN,  $pickup_location,   { optional => 1 } )
528       . build_field( FID_HOLD_TYPE,    $hold_type,         { optional => 1 } )
529       . build_field( FID_INST_ID,      $institution_id                       )
530       . build_field( FID_PATRON_ID,    $patron_identifier                    )
531       . build_field( FID_PATRON_PWD,   $patron_password,   { optional => 1 } )
532       . build_field( FID_ITEM_ID,      $item_identifier,   { optional => 1 } )
533       . build_field( FID_TITLE_ID,     $title_identifier,  { optional => 1 } )
534       . build_field( FID_TERMINAL_PWD, $terminal_password, { optional => 1 } )
535       . build_field( FID_FEE_ACK,      $fee_acknowledged,  { optional => 1 } );
536 }
537
538 sub build_renew_command_message {
539     my ($params) = @_;
540
541     my $third_party_allowed = $params->{third_party_allowed} || 'N';
542     my $no_block            = $params->{no_block}            || 'N';
543     my $transaction_date    = $params->{transaction_date};
544     my $nb_due_date         = $params->{nb_due_date};
545     my $institution_id      = $params->{institution_id};
546     my $patron_identifier   = $params->{patron_identifier};
547     my $patron_password     = $params->{patron_password};
548     my $item_identifier     = $params->{item_identifier};
549     my $title_identifier    = $params->{title_identifier};
550     my $terminal_password   = $params->{terminal_password};
551     my $item_properties     = $params->{item_properties};
552     my $fee_acknowledged    = $params->{fee_acknowledged}    || 'N';
553
554     $third_party_allowed = $third_party_allowed eq 'Y' ? 'Y' : 'N';
555     $no_block            = $no_block            eq 'Y' ? 'Y' : 'N';
556     $fee_acknowledged    = $fee_acknowledged    eq 'Y' ? 'Y' : 'N';
557
558     $nb_due_date ||= $transaction_date;
559
560     return
561         RENEW
562       . $third_party_allowed
563       . $no_block
564       . $transaction_date
565       . $nb_due_date
566       . build_field( FID_INST_ID,      $institution_id )
567       . build_field( FID_PATRON_ID,    $patron_identifier )
568       . build_field( FID_PATRON_PWD,   $patron_password, { optional => 1 } )
569       . build_field( FID_ITEM_ID,      $item_identifier )
570       . build_field( FID_TITLE_ID,     $title_identifier )
571       . build_field( FID_TERMINAL_PWD, $terminal_password )
572       . build_field( FID_ITEM_PROPS,   $item_properties, { optional => 1 } )
573       . build_field( FID_FEE_ACK,      $fee_acknowledged, { optional => 1 } );
574 }
575
576 sub build_fee_paid_command_message {
577     my ($params) = @_;
578
579     my $transaction_date  = $params->{transaction_date};
580     my $fee_type          = $params->{fee_type} || '01';
581     my $payment_type      = $params->{payment_type} || '00';
582     my $currency_type     = $params->{currency_type} || 'USD';
583     my $fee_amount        = $params->{fee_amount};
584     my $institution_id    = $params->{location_code};
585     my $patron_identifier = $params->{patron_identifier};
586     my $terminal_password = $params->{terminal_password};
587     my $patron_password   = $params->{patron_password};
588     my $fee_identifier    = $params->{fee_identifier};
589     my $transaction_id    = $params->{transaction_id};
590
591     return
592         FEE_PAID
593       . $transaction_date
594       . $fee_type
595       . $payment_type
596       . $currency_type
597       . build_field( FID_FEE_AMT,        $fee_amount )
598       . build_field( FID_INST_ID,        $institution_id )
599       . build_field( FID_PATRON_ID,      $patron_identifier )
600       . build_field( FID_TERMINAL_PWD,   $terminal_password, { optional => 1 } )
601       . build_field( FID_PATRON_PWD,     $patron_password, { optional => 1 } )
602       . build_field( FID_FEE_ID,         $fee_identifier, { optional => 1 } )
603       . build_field( FID_TRANSACTION_ID, $transaction_id, { optional => 1 } );
604 }
605
606 sub build_field {
607     my ( $field_identifier, $value, $params ) = @_;
608
609     $params //= {};
610
611     return q{} if ( $params->{optional} && !$value );
612
613     return $field_identifier . (($value) ? $value : '') . '|';
614 }
615
616 sub help {
617     say q/sip_cli_emulator.pl - SIP command line emulator
618
619 Test a SIP2 service by sending patron status and patron
620 information requests.
621
622 Usage:
623   sip_cli_emulator.pl [OPTIONS]
624
625 Options:
626   --help           display help message
627
628   -a --address     SIP server ip address or host name
629   -p --port        SIP server port
630
631   -su --sip_user   SIP server login username
632   -sp --sip_pass   SIP server login password
633
634   -l --location    SIP location code
635
636   --patron         ILS patron cardnumber or username
637   --password       ILS patron password
638
639   -s --summary     Optionally define the patron information request summary field.
640                    Please refer to the SIP2 protocol specification for details
641
642   --item           ILS item identifier ( item barcode )
643
644   -t --terminator  SIP2 message terminator, either CR, or CRLF
645                    (defaults to CRLF)
646
647   -fa --fee-acknowledged Sends a confirmation of checkout fee
648
649   --fee-type        Fee type for Fee Paid message, defaults to '01'
650   --payment-type    Payment type for Fee Paid message, default to '00'
651   --currency-type   Currency type for Fee Paid message, defaults to 'USD'
652   --fee-amount      Fee amount for Fee Paid message, required
653   --fee-identifier  Fee identifier for Fee Paid message, optional
654   --transaction-id  Transaction id for Fee Paid message, optional
655   --pickup-location Pickup location (branchcode) for Hold message, optional
656   --hold-mode       Accepts "+" to add hold or "-" to cancel hold, defaults to +
657
658   -m --message     SIP2 message to execute
659
660   Implemented Messages:
661     checkin
662     checkout
663     fee_paid
664     hold
665     item_information
666     patron_information
667     patron_status_request
668     sc_status_request
669     renew
670 /
671 }