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