Koha/C4/SIP/Sip.pm
Colin Campbell d85f757ce7 Bug 7904 Change SIP modules to use standard LIB path
For historical reasons the SIPServer and SIP modules
have used an extra module path in addition to the
standard Koha one. This has caused numerous irritants
in attempting to set up scripts and basic tests. It
does not help in attempting to modify or debug
this code

This patch changes the package value in the modules
under the C4/SIP directory and makes calls to
them use the full package name.

Where the export mechanism was being short circuited
routines have been explicitly exported and imported
declarations of 'use ILS' when that module was
not being used and which only generated warnings
have been removed.

As a lot of the changes affect lines where
an object is instantiated with new. The opportunity
has been taken to replace the ambiguous indirect
syntax with the preferred direct call

In intializing ILS the full path is added as this
will not require any changes to existing configs.
I suspect this feature is unused, and adds
obfuscation rather than flexibility but have kept
the feature as we need this change in order to
rationalize and extend the testing of the server.

The visible difference is that with the normal Koha
PERL5LIB setting. Compilation of Modules under C4/SIP
should be successful and not fail with unlocated modules,
allowing developers to see any perl warnings

All the SIP modules can now be run through the tests
in t/00-load.t now except for SIPServer itself

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>

Signed-off-by: Jonathan Druart <jonathan.druart@biblibre.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@gmail.com>
2015-02-05 14:44:54 -03:00

255 lines
7.6 KiB
Perl

#
# Sip.pm: General Sip utility functions
#
package C4::SIP::Sip;
use strict;
use warnings;
use Exporter;
use Encode;
use Sys::Syslog qw(syslog);
use POSIX qw(strftime);
use Socket qw(:crlf);
use IO::Handle;
use C4::SIP::Sip::Constants qw(SIP_DATETIME FID_SCREEN_MSG);
use C4::SIP::Sip::Checksum qw(checksum);
use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
BEGIN {
$VERSION = 3.07.00.049;
@ISA = qw(Exporter);
@EXPORT_OK = qw(y_or_n timestamp add_field maybe_add add_count
denied sipbool boolspace write_msg read_SIP_packet
$error_detection $protocol_version $field_delimiter
$last_response);
%EXPORT_TAGS = (
all => [qw(y_or_n timestamp add_field maybe_add
add_count denied sipbool boolspace write_msg
read_SIP_packet
$error_detection $protocol_version
$field_delimiter $last_response)]);
}
our $error_detection = 0;
our $protocol_version = 1;
our $field_delimiter = '|'; # Protocol Default
# We need to keep a copy of the last message we sent to the SC,
# in case there's a transmission error and the SC sends us a
# REQUEST_ACS_RESEND. If we receive a REQUEST_ACS_RESEND before
# we've ever sent anything, then we are to respond with a
# REQUEST_SC_RESEND (p.16)
our $last_response = '';
sub timestamp {
my $time = $_[0] || time();
if ( ref $time eq 'DateTime') {
return $time->strftime(SIP_DATETIME);
} elsif ($time=~m/^(\d{4})\-(\d{2})\-(\d{2})/) {
# passing a db returned date as is + bogus time
return sprintf( '%04d%02d%02d 235900', $1, $2, $3);
}
return strftime(SIP_DATETIME, localtime($time));
}
#
# add_field(field_id, value)
# return constructed field value
#
sub add_field {
my ($field_id, $value) = @_;
my ($i, $ent);
if (!defined($value)) {
syslog("LOG_DEBUG", "add_field: Undefined value being added to '%s'",
$field_id);
$value = '';
}
$value=~s/\r/ /g; # CR terminates a sip message
# Protect against them in sip text fields
# Replace any occurences of the field delimiter in the
# field value with the HTML character entity
$ent = sprintf("&#%d;", ord($field_delimiter));
while (($i = index($value, $field_delimiter)) != ($[-1)) {
substr($value, $i, 1) = $ent;
}
return $field_id . $value . $field_delimiter;
}
#
# maybe_add(field_id, value):
# If value is defined and non-empty, then return the
# constructed field value, otherwise return the empty string.
# NOTE: if zero is a valid value for your field, don't use maybe_add!
#
sub maybe_add {
my ($fid, $value, $server) = @_;
if ( $fid eq FID_SCREEN_MSG && $server->{account}->{screen_msg_regex} ) {
foreach my $regex (
ref $server->{account}->{screen_msg_regex} eq "ARRAY"
? @{ $server->{account}->{screen_msg_regex} }
: $server->{account}->{screen_msg_regex} )
{
$value =~ s/$regex->{find}/$regex->{replace}/g;
}
}
return (defined($value) && $value) ? add_field($fid, $value) : '';
}
#
# add_count() produce fixed four-character count field,
# or a string of four spaces if the count is invalid for some
# reason
#
sub add_count {
my ($label, $count) = @_;
# If the field is unsupported, it will be undef, return blanks
# as per the spec.
if (!defined($count)) {
return ' ' x 4;
}
$count = sprintf("%04d", $count);
if (length($count) != 4) {
syslog("LOG_WARNING", "handle_patron_info: %s wrong size: '%s'",
$label, $count);
$count = ' ' x 4;
}
return $count;
}
#
# denied($bool)
# if $bool is false, return true. This is because SIP statuses
# are inverted: we report that something has been denied, not that
# it's permitted. For example, 'renewal priv. denied' of 'Y' means
# that the user's not permitted to renew. I assume that the ILS has
# real positive tests.
#
sub denied {
my $bool = shift;
return boolspace(!$bool);
}
sub sipbool {
my $bool = shift;
return $bool ? 'Y' : 'N';
}
#
# boolspace: ' ' is false, 'Y' is true. (don't ask)
#
sub boolspace {
my $bool = shift;
return $bool ? 'Y' : ' ';
}
# read_SIP_packet($file)
#
# Read a packet from $file, using the correct record separator
#
sub read_SIP_packet {
my $record;
my $fh = shift or syslog("LOG_ERR", "read_SIP_packet: no filehandle argument!");
my $len1 = 999;
# local $/ = "\r"; # don't need any of these here. use whatever the prevailing $/ is.
local $/ = "\015"; # proper SPEC: (octal) \015 = (hex) x0D = (dec) 13 = (ascii) carriage return
{ # adapted from http://perldoc.perl.org/5.8.8/functions/readline.html
undef $!;
$record = readline($fh);
if ( defined($record) ) {
while ( chomp($record) ) { 1; }
$len1 = length($record);
syslog( "LOG_DEBUG", "read_SIP_packet, INPUT MSG: '$record'" );
$record =~ s/^\s*[^A-z0-9]+//s; # Every line must start with a "real" character. Not whitespace, control chars, etc.
$record =~ s/[^A-z0-9]+$//s; # Same for the end. Note this catches the problem some clients have sending empty fields at the end, like |||
$record =~ s/\015?\012//g; # Extra line breaks must die
$record =~ s/\015?\012//s; # Extra line breaks must die
$record =~ s/\015*\012*$//s; # treat as one line to include the extra linebreaks we are trying to remove!
while ( chomp($record) ) { 1; }
$record and last; # success
}
}
if ($record) {
my $len2 = length($record);
syslog("LOG_INFO", "read_SIP_packet, INPUT MSG: '$record'") if $record;
($len1 != $len2) and syslog("LOG_DEBUG", "read_SIP_packet, trimmed %s character(s) (after chomps).", $len1-$len2);
} else {
syslog("LOG_WARNING", "read_SIP_packet input %s, end of input.", (defined($record) ? "empty ($record)" : 'undefined'));
}
#
# Cen-Tec self-check terminals transmit '\r\n' line terminators.
# This is actually very hard to deal with in perl in a reasonable
# since every OTHER piece of hardware out there gets the protocol
# right.
#
# The incorrect line terminator presents as a \r at the end of the
# first record, and then a \n at the BEGINNING of the next record.
# So, the simplest thing to do is just throw away a leading newline
# on the input.
#
# This is now handled by the vigorous cleansing above.
# syslog("LOG_INFO", encode_utf8("INPUT MSG: '$record'")) if $record;
syslog("LOG_INFO", "INPUT MSG: '$record'") if $record;
return $record;
}
#
# write_msg($msg, $file)
#
# Send $msg to the SC. If error detection is active, then
# add the sequence number (if $seqno is non-zero) and checksum
# to the message, and save the whole thing as $last_response
#
# If $file is set, then it's a file handle: write to it, otherwise
# just write to the default destination.
#
sub write_msg {
my ($self, $msg, $file, $terminator, $encoding) = @_;
$terminator ||= q{};
$terminator = ( $terminator eq 'CR' ) ? $CR : $CRLF;
$msg = encode($encoding, $msg) if ( $encoding );
my $cksum;
# $msg = encode_utf8($msg);
if ($error_detection) {
if (defined($self->{seqno})) {
$msg .= 'AY' . $self->{seqno};
}
$msg .= 'AZ';
$cksum = checksum($msg);
$msg .= sprintf('%04.4X', $cksum);
}
if ($file) {
$file->autoflush(1);
print $file $msg, $terminator;
} else {
STDOUT->autoflush(1);
print $msg, $terminator;
syslog("LOG_INFO", "OUTPUT MSG: '$msg'");
}
$last_response = $msg;
}
1;