Lines 87-98
use IO::Socket::INET;
Link Here
|
87 |
use IO::Select; |
87 |
use IO::Select; |
88 |
use POSIX; |
88 |
use POSIX; |
89 |
use HTTP::Status qw(:constants); |
89 |
use HTTP::Status qw(:constants); |
|
|
90 |
use strict; |
91 |
use warnings; |
90 |
|
92 |
|
91 |
use LWP::UserAgent; |
93 |
use LWP::UserAgent; |
92 |
use XML::Simple; |
94 |
use XML::Simple; |
|
|
95 |
use MARC::Record; |
96 |
use MARC::File::XML; |
93 |
|
97 |
|
94 |
use constant CLIENT_READ_TIMEOUT => 5; |
98 |
use constant CLIENT_READ_TIMEOUT => 5; |
95 |
use constant CLIENT_READ_BUFFER_SIZE => 4 * 1024; |
99 |
use constant CLIENT_READ_BUFFER_SIZE => 100000; |
96 |
use constant AUTH_URI => "/cgi-bin/koha/mainpage.pl"; |
100 |
use constant AUTH_URI => "/cgi-bin/koha/mainpage.pl"; |
97 |
use constant IMPORT_SVC_URI => "/cgi-bin/koha/svc/import_bib"; |
101 |
use constant IMPORT_SVC_URI => "/cgi-bin/koha/svc/import_bib"; |
98 |
|
102 |
|
Lines 222-228
sub _ua {
Link Here
|
222 |
sub read_request { |
226 |
sub read_request { |
223 |
my ( $self, $io ) = @_; |
227 |
my ( $self, $io ) = @_; |
224 |
|
228 |
|
225 |
my ($in, @in, $timeout); |
229 |
my ($in, @in_arr, $timeout, $bad_marc); |
226 |
my $select = IO::Select->new($io) ; |
230 |
my $select = IO::Select->new($io) ; |
227 |
while ( "FOREVER" ) { |
231 |
while ( "FOREVER" ) { |
228 |
if ( $select->can_read(CLIENT_READ_TIMEOUT) ){ |
232 |
if ( $select->can_read(CLIENT_READ_TIMEOUT) ){ |
Lines 231-252
sub read_request {
Link Here
|
231 |
|
235 |
|
232 |
# XXX ignore after NULL |
236 |
# XXX ignore after NULL |
233 |
if ( $in =~ m/^(.*)\000/so ) { # null received, EOT |
237 |
if ( $in =~ m/^(.*)\000/so ) { # null received, EOT |
234 |
push @in, $1; |
238 |
push @in_arr, $1; |
235 |
last; |
239 |
last; |
236 |
} |
240 |
} |
237 |
push @in, $in; |
241 |
push @in_arr, $in; |
238 |
} |
242 |
} |
239 |
else { |
243 |
else { |
240 |
$timeout = 1; |
|
|
241 |
last; |
244 |
last; |
242 |
} |
245 |
} |
243 |
} |
246 |
} |
244 |
|
247 |
|
245 |
$in = join '', @in; |
248 |
$in = join '', @in_arr; |
246 |
|
249 |
|
|
|
250 |
$in =~ m/(.)$/; |
251 |
my $lastchar = $1; |
247 |
my ($xml, $user, $password, $local_user); |
252 |
my ($xml, $user, $password, $local_user); |
248 |
my $data = $in; # copy for diagmostic purposes |
253 |
my $data = $in; # copy for diagmostic purposes |
249 |
while ( my $first = substr( $data, 0, 1 ) ) { |
254 |
while () { |
|
|
255 |
my $first = substr( $data, 0, 1 ); |
256 |
if (!defined $first) { |
257 |
last; |
258 |
} |
250 |
$first eq 'U' && do { |
259 |
$first eq 'U' && do { |
251 |
($user, $data) = _trim_identifier($data); |
260 |
($user, $data) = _trim_identifier($data); |
252 |
next; |
261 |
next; |
Lines 256-281
sub read_request {
Link Here
|
256 |
next; |
265 |
next; |
257 |
}; |
266 |
}; |
258 |
$first eq 'P' && do { |
267 |
$first eq 'P' && do { |
259 |
($password,, $data) = _trim_identifier($data); |
268 |
($password, $data) = _trim_identifier($data); |
260 |
next; |
269 |
next; |
261 |
}; |
270 |
}; |
262 |
$first eq ' ' && do { |
271 |
$first eq ' ' && do { |
263 |
$data = substr( $data, 1 ); # trim |
272 |
$data = substr( $data, 1 ); # trim |
264 |
next; |
273 |
next; |
265 |
}; |
274 |
}; |
266 |
$first eq '<' && do { |
275 |
$data =~ m/^[0-9]/ && do { |
267 |
$xml = $data; |
276 |
# What we have here might be a MARC record... |
|
|
277 |
my $marc_record; |
278 |
eval { $marc_record = MARC::Record->new_from_usmarc($data); }; |
279 |
if ($@) { |
280 |
$bad_marc = 1; |
281 |
} |
282 |
else { |
283 |
$xml = $marc_record->as_xml(); |
284 |
} |
268 |
last; |
285 |
last; |
269 |
}; |
286 |
}; |
270 |
|
|
|
271 |
last; # unexpected input |
287 |
last; # unexpected input |
272 |
} |
288 |
} |
273 |
|
289 |
|
274 |
my @details; |
290 |
my @details; |
275 |
push @details, "Timeout" if $timeout; |
291 |
push @details, "Timeout" if $timeout; |
|
|
292 |
push @details, "Bad MARC" if $bad_marc; |
276 |
push @details, "User: $user" if $user; |
293 |
push @details, "User: $user" if $user; |
277 |
push @details, "Password: " . ( $self->{debug} ? $password : ("x" x length($password)) ) if $password; |
294 |
push @details, "Password: " . ( $self->{debug} ? $password : ("x" x length($password)) ) if $password; |
278 |
push @details, "Local user: $local_user" if $local_user; |
295 |
push @details, "Local user: $local_user" if $local_user; |
|
|
296 |
push @details, "XML: $xml" if $xml; |
297 |
push @details, "Remaining data: $data" if ($data && !$xml); |
279 |
unless ($xml) { |
298 |
unless ($xml) { |
280 |
$self->log("Invalid request", $in, @details); |
299 |
$self->log("Invalid request", $in, @details); |
281 |
return; |
300 |
return; |
Lines 287-295
sub read_request {
Link Here
|
287 |
} |
306 |
} |
288 |
|
307 |
|
289 |
sub _trim_identifier { |
308 |
sub _trim_identifier { |
290 |
my ($a, $len) = unpack "cc", substr( $_[0], 0, 2 ); |
309 |
#my ($a, $len) = unpack "cc", substr( $_[0], 0, 2 ); |
291 |
|
310 |
my $len=ord(substr ($_[0], 1, 1)) - 64; |
292 |
return ( substr( $_[0], 2, $len ), substr( $_[0], 2 + $len ) ); |
311 |
if ($len <0) { #length is numeric, and thus comes from the web client, not the desktop client. |
|
|
312 |
$_[0] =~ m/.(\d+)/; |
313 |
$len = $1; |
314 |
return ( substr( $_[0], length($len)+1 , $len ), substr( $_[0], length($len) + 1 + $len ) ); |
315 |
} |
316 |
return ( substr( $_[0], 2 , $len ), substr( $_[0], 2 + $len ) ); |
293 |
} |
317 |
} |
294 |
|
318 |
|
295 |
sub handle_request { |
319 |
sub handle_request { |
Lines 309-321
sub handle_request {
Link Here
|
309 |
} |
333 |
} |
310 |
|
334 |
|
311 |
my $base_url = $self->{koha}; |
335 |
my $base_url = $self->{koha}; |
312 |
my $resp = $ua->post( $base_url.IMPORT_SVC_URI, $self->{params}, 'Content-Type' => 'text/plain', Content => $data ); |
336 |
my $resp = $ua->post( $base_url.IMPORT_SVC_URI, |
|
|
337 |
{'nomatch_action' => $self->{params}->{nomatch_action}, |
338 |
'overlay_action' => $self->{params}->{overlay_action}, |
339 |
'match' => $self->{params}->{match}, |
340 |
'import_mode' => $self->{params}->{import_mode}, |
341 |
'framework' => $self->{params}->{framework}, |
342 |
'item_action' => $self->{params}->{item_action}, |
343 |
'xml' => $data}); |
344 |
|
313 |
my $status = $resp->code; |
345 |
my $status = $resp->code; |
314 |
if ($status == HTTP_UNAUTHORIZED || $status == HTTP_FORBIDDEN) { |
346 |
if ($status == HTTP_UNAUTHORIZED || $status == HTTP_FORBIDDEN) { |
315 |
my $user = $self->{user}; |
347 |
my $user = $self->{user}; |
316 |
my $password = $self->{password}; |
348 |
my $password = $self->{password}; |
317 |
$resp = $ua->post( $base_url.AUTH_URI, { userid => $user, password => $password } ); |
349 |
$resp = $ua->post( $base_url.AUTH_URI, { userid => $user, password => $password } ); |
318 |
$resp = $ua->post( $base_url.IMPORT_SVC_URI, $self->{params}, 'Content-Type' => 'text/plain', Content => $data ) |
350 |
$resp = $ua->post( $base_url.IMPORT_SVC_URI, |
|
|
351 |
{'nomatch_action' => $self->{params}->{nomatch_action}, |
352 |
'overlay_action' => $self->{params}->{overlay_action}, |
353 |
'match' => $self->{params}->{match}, |
354 |
'import_mode' => $self->{params}->{import_mode}, |
355 |
'framework' => $self->{params}->{framework}, |
356 |
'item_action' => $self->{params}->{item_action}, |
357 |
'xml' => $data}) |
319 |
if $resp->is_success; |
358 |
if $resp->is_success; |
320 |
} |
359 |
} |
321 |
unless ($resp->is_success) { |
360 |
unless ($resp->is_success) { |
Lines 323-333
sub handle_request {
Link Here
|
323 |
return $self->error_response("Unsuccessful request"); |
362 |
return $self->error_response("Unsuccessful request"); |
324 |
} |
363 |
} |
325 |
|
364 |
|
326 |
my ($koha_status, $bib, $batch_id, $error); |
365 |
my ($koha_status, $bib, $overlay, $batch_id, $error, $url); |
327 |
if ( my $r = eval { XMLin($resp->content) } ) { |
366 |
if ( my $r = eval { XMLin($resp->content) } ) { |
328 |
$koha_status = $r->{status}; |
367 |
$koha_status = $r->{status}; |
329 |
$batch_id = $r->{import_batch_id}; |
368 |
$batch_id = $r->{import_batch_id}; |
330 |
$error = $r->{error}; |
369 |
$error = $r->{error}; |
|
|
370 |
$bib = $r->{biblionumber}; |
371 |
$overlay = $r->{match_status}; |
372 |
$url = $r->{url}; |
331 |
} |
373 |
} |
332 |
else { |
374 |
else { |
333 |
$koha_status = "error"; |
375 |
$koha_status = "error"; |
Lines 336-342
sub handle_request {
Link Here
|
336 |
} |
378 |
} |
337 |
|
379 |
|
338 |
if ($koha_status eq "ok") { |
380 |
if ($koha_status eq "ok") { |
339 |
return $self->response( sprintf( "Success. Import batch id: %s", $batch_id ) ); |
381 |
my $response_string = sprintf( "Success. Batch number %s - biblio record number %s", |
|
|
382 |
$batch_id,$bib); |
383 |
$response_string .= $overlay eq 'no_match' ? ' added to Koha.' : ' overlaid by import.'; |
384 |
$response_string .= "\n\n$url"; |
385 |
|
386 |
return $self->response( $response_string ); |
340 |
} |
387 |
} |
341 |
|
388 |
|
342 |
return $self->error_response( sprintf( "%s. Please contact administrator.", $error ) ); |
389 |
return $self->error_response( sprintf( "%s. Please contact administrator.", $error ) ); |