3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
20 #-----------------------------------
21 # Script Name: zed-koha-server.pl
24 # Author: Joshua Ferraro [jmf at kados dot org]
25 # Description: A very basic Z3950 Server
26 # Usage: zed-koha-server.pl
28 # 0.00 2003/08/14: Original version; search works.
29 # 0.01 2003/10/02: First functional version; search and fetch working
30 # records returned in USMARC (ISO2709) format,
31 # Bath compliant to Level 1 in Functional Areas A, B.
32 # 0.02 2004/04/14: Cleaned up documentation, etc. No functional
34 # 1.30 2004/04/22: Changing version numbers to correspond with CVS;
35 # Fixed the substitution bug (e.g., 4=100 before 4=1);
36 # Added support for the truncation attribute (5=1 and
37 # 5=100; thanks to Tomasz M. Wolniewicz for pointing
38 # out these improvements)
39 # 1.4.0 2004/06/02: Changed sql queries to account for the difference
40 # between bibid and biblionumber. Thanks again to
41 # Tomasz M. Wolniewicz for suggesting a great solution
43 #-----------------------------------
44 # Note: After installing SimpleServer (indexdata.dk/simpleserver) and
45 # changing the leader information in Koha's MARCgetbiblio subroutine in
46 # Biblio.pm you can run this script as root:
48 # ./zed-koha-server.pl
50 # and the server will start running on port 9999 and will allow searching
51 # and retrieval of records in MARC21 (USMARC; ISO2709) bibliographic format.
52 # ----------------------------------
55 use Net::Z3950::SimpleServer;
60 my $dbh = C4::Context->dbh;
61 my @bib_list; ## Stores the list of biblionumbers in a query
62 ## I should eventually move this to different scope
64 my $handler = Net::Z3950::SimpleServer->new(INIT => \&init_handler,
65 SEARCH => \&search_handler,
66 FETCH => \&fetch_handler);
68 $handler->launch_server("zed-koha-server.pl", @ARGV);
74 # FIXME: I should force use of my database name
75 $args->{IMP_NAME} = "Zed-Koha";
76 $args->{IMP_VER} = "1.40";
77 $args->{ERR_CODE} = 0;
78 $args->{HANDLE} = $session;
79 if (defined($args->{PASS}) && defined($args->{USER})) {
80 printf("Received USER/PASS=%s/%s\n", $args->{USER},$args->{PASS});
86 sub run_query { ## Run the query and store the biblionumbers:
87 my ($sql_query, $query, $args) = @_;
88 my $sth_get = $dbh->prepare("$sql_query");
90 ## Send the query to the database:
91 $sth_get->execute($query);
93 while(my ($data)=$sth_get->fetchrow_array) {
95 ## Store Biblioitem info for later
96 $bib_list[$count] = "$data";
101 $args->{HITS} = $count;
102 print "got search: ", $args->{RPN}->{query}->render(), "\n";
107 ## Place the user's query into a variable
108 my $query = $args->{QUERY};
111 my $term = $args->{term};
113 $term .= "\%"; ## Add the wildcard to search term
117 ## Strip out the junk and call the mysql query subroutine:
119 $query =~ s|\@attrset 1.2.840.10003.3.1 \@attr 1=7 ||g;
123 ## Bib-1 Structure Attributes:
124 $query =~ s|\@attr||g;
126 $query =~ s|4=100||g; ## date (un-normalized)
127 $query =~ s|4=101||g; ## name (normalized)
128 $query =~ s|4=102||g; ## sme (un-normalized)
129 $query =~ s|4=1||g; ## Phrase
130 $query =~ s|4=2||g; ## Keyword
131 $query =~ s|4=3||g; ## Key
132 $query =~ s|4=4||g; ## year
133 $query =~ s|4=5||g; ## Date (normalized)
134 $query =~ s|4=6||g; ## word list
135 $query =~ s|5=100||g; ## truncation
136 $query =~ s|5=1||g; ## truncation
137 $query =~ s|\@and ||g;
140 $query =~ s|,|%|g; ## replace commas with wildcard
141 $query .= "\%"; ## Add the wildcard to search term
142 $query .= "\%"; ## Add the wildcard to search term
143 print "The term was:\n";
145 print "The query was:\n";
147 my $sql_query = "SELECT marc_biblio.bibid FROM marc_biblio RIGHT JOIN biblio ON marc_biblio.biblionumber = biblio.biblionumber WHERE biblio.isbn LIKE ?";
148 &run_query($sql_query, $query, $args);
151 elsif (/1=1003/) { ## author
152 $query =~ s|\@attrset||g;
153 $query =~ s|1.2.840.10003.3.1||g;
154 $query =~ s|1=1003||g;
156 ## Bib-1 Structure Attributes:
157 $query =~ s|\@attr ||g;
159 $query =~ s|4=100||g; ## date (un-normalized)
160 $query =~ s|4=101||g; ## name (normalized)
161 $query =~ s|4=102||g; ## sme (un-normalized)
162 $query =~ s|4=1||g; ## Phrase
163 $query =~ s|4=2||g; ## Keyword
164 $query =~ s|4=3||g; ## Key
165 $query =~ s|4=4||g; ## year
166 $query =~ s|4=5||g; ## Date (normalized)
167 $query =~ s|4=6||g; ## word list
168 $query =~ s|5=100||g; ## truncation
169 $query =~ s|5=1||g; ## truncation
174 $query .= "\%"; ## Add the wildcard to search term
176 my $sql_query = "SELECT marc_biblio.bibid FROM marc_biblio RIGHT JOIN biblio ON marc_biblio.biblionumber = biblio.biblionumber WHERE biblio.author LIKE ?";
177 &run_query($sql_query, $query, $args);
178 ## used for debugging--works!
179 ## print "@bib_list\n";
181 elsif (/1=4/) { ## title
182 $query =~ s|\@attrset||g;
183 $query =~ s|1.2.840.10003.3.1||g;
188 ## Bib-1 Structure Attributes:
189 $query =~ s|\@attr||g;
191 $query =~ s|4=100||g; ## date (un-normalized)
192 $query =~ s|4=101||g; ## name (normalized)
193 $query =~ s|4=102||g; ## sme (un-normalized)
194 $query =~ s|4=1||g; ## Phrase
195 $query =~ s|4=2||g; ## Keyword
196 $query =~ s|4=3||g; ## Key
197 $query =~ s|4=4||g; ## year
198 $query =~ s|4=5||g; ## Date (normalized)
199 $query =~ s|4=6||g; ## word list
200 $query =~ s|5=100||g; ## truncation
201 $query =~ s|5=1||g; ## truncation
204 #$query =~ s|\@and||g;
205 $query .= "\%"; ## Add the wildcard to search term
206 print "The term was:\n";
208 print "The query was:\n";
210 my $sql_query = "SELECT marc_biblio.bibid FROM marc_biblio RIGHT JOIN biblio ON marc_biblio.biblionumber = biblio.biblionumber WHERE biblio.title LIKE ?";
211 &run_query($sql_query, $query, $args);
213 elsif (/1=21/) { ## subject
214 $query =~ s|\@attrset 1.2.840.10003.3.1 \@attr 1=21 ||g;
218 ## Bib-1 Structure Attributes:
219 $query =~ s|\@attr ||g;
220 $query =~ s|4=100||g; ## date (un-normalized)
221 $query =~ s|4=101||g; ## name (normalized)
222 $query =~ s|4=102||g; ## sme (un-normalized)
224 $query =~ s|4=1||g; ## Phrase
225 $query =~ s|4=2||g; ## Keyword
226 $query =~ s|4=3||g; ## Key
227 $query =~ s|4=4||g; ## year
228 $query =~ s|4=5||g; ## Date (normalized)
229 $query =~ s|4=6||g; ## word list
230 $query =~ s|5=100||g; ## truncation
231 $query =~ s|5=1||g; ## truncation
233 $query .= "\%"; ## Add the wildcard to search term
235 my $sql_query = "SELECT marc_biblio.bibid FROM marc_biblio RIGHT JOIN biblio ON marc_biblio.biblionumber = biblio.biblionumber WHERE biblio.subject LIKE ?";
236 &run_query($sql_query, $query, $args);
238 elsif (/1=1016/) { ## any
239 $query =~ s|\@attrset 1.2.840.10003.3.1 \@attr 1=1016 ||g;
243 ## Bib-1 Structure Attributes:
244 $query =~ s|\@attr||g;
246 $query =~ s|4=100||g; ## date (un-normalized)
247 $query =~ s|4=101||g; ## name (normalized)
248 $query =~ s|4=102||g; ## sme (un-normalized)
250 $query =~ s|4=1||g; ## Phrase
251 $query =~ s|4=2||g; ## Keyword
252 $query =~ s|4=3||g; ## Key
253 $query =~ s|4=4||g; ## year
254 $query =~ s|4=5||g; ## Date (normalized)
255 $query =~ s|4=6||g; ## word list
256 $query =~ s|5=100||g; ## truncation
257 $query =~ s|5=1||g; ## truncation
259 $query .= "\%"; ## Add the wildcard to search term
261 my $sql_query = "SELECT bibid FROM marc_word WHERE word LIKE?";
262 &run_query($sql_query, $query, $args);
267 # warn "in fetch_handler"; ## troubleshooting
268 my $offset = $args->{OFFSET};
269 $offset -= 1; ## because $args->{OFFSET} 1 = record #1
270 chomp (my $bibid = $bib_list[$offset]); ## Not sure about this
271 ## print "the bibid is:$bibid\n";
272 my $MARCRecord = &MARCgetbiblio($dbh,$bibid);
273 my $recordstring=$MARCRecord->as_usmarc();
274 ## print "here is my record: $recordstring\n";
278 ## Dumper $recordstring;
279 ## open (MARC, ">/root/marc.dump");
280 ## print MARC "$recordstring";
283 ## Convert from 852/4 to 952:
284 ## 942a --> 852a Organization code
285 ## 952b --> 852b Home branch
286 ## 942k --> 852h Classification
287 ## 952p --> 852p Barcode
289 my $record = MARC::Record->new_from_usmarc($recordstring);
290 my @fields942 = $record->field('942');
291 my $field842 = $fields942[0];
292 my ($field952, $sub852a, $sub852k, $sub852b, $sub852p, $sub852h);
295 ## while ( my $record = $batch->next() ) {
296 ## my @fields942 = $record->field('942');
297 ## my $field842 = $fields942[0];
298 ## #grab first 942 (only need one, they are same for all items)
299 ## my $sub852a = ($field842->subfield('a') || '');
300 ## my $sub852h = ($field842->subfield('k') || '');
302 ## my @fields952 = $record->field('952');
303 ## foreach my $field952 (@fields952) { #get all 952s
304 ## my $sub852b = ($field952->subfield('b') || '');
305 ## my $sub852p = ($field952->subfield('p') || '');
308 #grab first 942 (only need one, they are same for all items)
309 unless (! $field952){
310 $sub852a = ($field952->subfield('a') || '') ;
312 unless (! $field952){ #->subfield('k')) {
313 $sub852k = ($field952->subfield('k') || '') ;
317 my @fields952 = $record->field('952');
318 foreach my $field952 (@fields952) { #get all 952s
320 unless (! $field952) { #->subfield('b')) {
321 $sub852b = ($field952->subfield('b') || '') ;
323 unless (! $field952) { #->subfield('p')) {
324 $sub852p = ($field952->subfield('p') || '') ;
326 #make it one big happy family
327 my $new852 = MARC::Field->new(
334 $record->append_fields($new852);
338 my $recordstringdone = $record->as_usmarc();
341 $args->{REP_FORM} = &Net::Z3950::OID::usmarc;
343 ## Return the record string to the client
344 $args->{RECORD} = $recordstringdone;
352 sub fetch_handler_old {
354 # warn "in fetch_handler"; ## troubleshooting
355 my $offset = $args->{OFFSET};
356 $offset -= 1; ## because $args->{OFFSET} 1 = record #1
357 chomp (my $bibid = $bib_list[$offset]); ## Not sure about this
358 my $sql_query = "SELECT tag, subfieldcode, subfieldvalue FROM marc_subfield_table where bibid=?";
359 my $sth_get = $dbh->prepare("$sql_query");
360 $sth_get->execute($bibid);
362 ## create a MARC::Record object
363 my $rec = MARC::Record->new();
366 while (my @data=$sth_get->fetchrow_array) {
369 my $subfieldcode = $data[1];
370 my $subfieldvalue = $data[2];
372 my $field = MARC::Field->new(
374 $subfieldcode => $subfieldvalue,
377 $rec->append_fields($field);
379 ## build the marc string and put into $record
380 my $tmp_record = $rec->as_usmarc();
381 my $reclen = length $tmp_record;
382 my $baseaddr = "$reclen + dirlen";
383 # set_leader_lengths($reclen,$baseaddr);
384 my $record = $rec->as_usmarc();
385 $args->{RECORD} = $record;
391 ## This stuff doesn't work yet...I should include boolean searching someday
393 package Net::Z3950::RPN::Term;
396 return '"' . $self->{term} . '"';
399 package Net::Z3950::RPN::And;
402 return '(' . $self->[0]->render() . ' AND ' .
403 $self->[1]->render() . ')';
406 package Net::Z3950::RPN::Or;
409 return '(' . $self->[0]->render() . ' OR ' .
410 $self->[1]->render() . ')';
413 package Net::Z3950::RPN::AndNot;
416 return '(' . $self->[0]->render() . ' ANDNOT ' .
417 $self->[1]->render() . ')';