source: trunk/gsdl/src/recpt/z3950server.cpp@ 1861

Last change on this file since 1861 was 1738, checked in by jrm21, 23 years ago

all calls to text_t.getcstr() now delete the returned ptr. Also, we
remove any " chars from the string as this may ruin the query. We don't
yet do any post-processing to filter for phrases...

  • Property svn:keywords set to Author Date Id Revision
File size: 9.9 KB
Line 
1/**********************************************************************
2 *
3 * z3950server.cpp --
4 * Copyright (C) 2000 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "z3950server.h"
27#include "comtypes.h"
28#include <stdio.h>
29// z39.50 yaz stuff
30
31extern "C" {
32#include "yaz/yaz_zclient.h"
33}
34
35/***
36 each z39.50 server+database pair is a GSDL collection.
37***/
38
39z3950_server::z3950_server() {
40 info=NULL;
41 connected=false;
42 titles=NULL;
43 gotsinglerecord=false;
44 // for now, assume that all records will have text associated with them.
45 meta["hastxt"]="1";
46 // for now, assume we don't want ANY DocumentButtons.
47 format["DocumentButtons"]="";
48}
49
50z3950_server::~z3950_server() {
51}
52
53void z3950_server::setMeta(const text_t &key, const text_t &value) {
54 meta[key]=value;
55}
56
57void z3950_server::setName(const text_t &newname) {
58 title=newname;
59 meta["collectionname"]=newname;
60}
61
62void z3950_server::addcfgAbout(const text_t &lang, const text_t &abouttext) {
63 about[lang]=abouttext;
64}
65
66bool z3950_server::getcfgAbout(const text_t &lang, text_t &abouttxt) {
67 text_tmap::iterator it;
68 it=about.find(lang);
69 if (it==about.end()) return (false);
70 abouttxt=((*it).second);
71 return (true);
72}
73
74
75// now functions that actually talk over the tcp connection.
76
77// create a tcp connection to the associated target. Currently, this will
78// re-initialise if we are already connected.
79bool z3950_server::connect() {
80 text_t server_and_port;
81 char *zserverinfo;
82
83 server_and_port=info->host+":"+info->port;
84 // remember that info.name is the database name
85
86 z_initialize();
87 char* serv_str=server_and_port.getcstr();
88 char* name_str=info->name.getcstr();
89 int retval=z_cmd_open(serv_str,name_str);
90 delete serv_str;
91 delete name_str;
92 if (retval==1)
93 // we got a connection error
94 return false;
95
96 // get initialisation response.
97 z_getnextAPDU();
98 zserverinfo=z_get_initResponse();
99 if (zserverinfo!=NULL) {
100 z_initstr.appendcstr(zserverinfo);
101 }
102 free(zserverinfo);
103
104 connected=true;
105 return true;
106}
107
108void z3950_server::parseQuery(const text_t &query,
109 const text_t &fields,
110 text_t &parsed_query) {
111 /****** FIXME *****/
112 // We need to format the query string into RPN -
113 // by just passing it like this, it will only work for simple queries.
114 // This will require us to actually come up with a query syntax and
115 // a parser. For now, we'll just do an "AND" query for all terms
116 // But look at Common Command Language (CCL) query syntax (ISO 8777).
117
118 // need to remove " chars from the query. We should really tell the server
119 // to do a phrase search on the terms that are between the "s, but we
120 // can't (easily) tell if the server can do that or not,
121 // so we'll currently just do a query and then post-process. (not yet
122 // implemented........)
123
124 // we need to count number of terms separated by a space
125 char *ptr=query.getcstr();
126 int strlength=strlen(ptr);
127 bool inword=false;
128 int num_terms=0;
129
130 for (int i=0;i<strlength;i++) {
131 if (*(ptr+i)=='"') { // convert " to SPACE...
132 *(ptr+i)=' ';
133 }
134 if (*(ptr+i)!=' ') {
135 if (inword==false) {
136 inword=true;
137 num_terms++;
138 }
139 }
140 else { // ptr+i is a space
141 inword=false;
142 }
143 }
144
145 // set the field(s) to search on - main ones include:
146 // 1016 => Any
147 // 1 => (Personal) Name
148 // 4 => Title
149 // 21 => Subject Heading
150 // 45 => Subject precis
151 // Note I have no idea how these actually work - I think some servers
152 // only have limited fields, and map all subject-type requests into that
153 // subject field, etc.
154
155 parsed_query="@attr 1=";
156 if (fields==".author")
157 parsed_query+="1 ";
158 else if (fields==".title")
159 parsed_query+="4 ";
160 else // fields==".any"
161 parsed_query+="1016 ";
162
163 // append "@and" for each term after the first
164 for (int i=1;i<num_terms;i++)
165 parsed_query+="@and ";
166 // append the actual query
167 parsed_query+=ptr;
168
169 delete ptr;
170}
171
172text_tarray *z3950_server::getrecordTitles(const text_t &query,
173 const text_t &fields,
174 int first, int count,
175 int *nummatches, comerror_t &err) {
176 /* NOTE!!!!!! Because this code currently only works in cgi-bin mode,
177 we only ever do one request. Therefore, it is CURRENTLY OK to store
178 (cache) the retrieved titles, because if this function is ever called
179 more than once, the arguments will be the same each time.
180 (I think :)
181 */
182
183 char **c_str_titles;
184 int i;
185 int last;
186
187 if (gotsinglerecord==true) {
188 /* If true, then this whole execution was done to retrieve a single
189 document. Therefore, the list of titles of all records matching the
190 query isn't actually required. It's just that for some reason our
191 filter (z3950proto::filter) gets called at least 7 times, with the
192 7th being a "QueryFilter" for some reason... */
193 nummatches=0;
194 return NULL; /* shouldn't really return NULL, but nummatches is checked
195 first (I hope) */
196 }
197
198 // if (titles!=NULL) delete (titles);
199 if (titles!=NULL) return titles;
200 titles=new text_tarray;
201
202 /* check if connected */
203 if (connected==false)
204 if (connect()==false) {
205 // we could not connect.
206 err=protocolError;
207 return (NULL);
208 }
209
210
211 text_t expanded_query="";
212 parseQuery(query,fields,expanded_query);
213
214 char* query_str=expanded_query.getcstr();
215 // following functions defined in yaz_zclient.c
216 *nummatches=z_cmd_dosearch(query_str); // returns # found, -1 on err.
217 delete query_str;
218 if (*nummatches<=0) {
219 if (*nummatches==0) {
220 // no matches
221 return (NULL);
222 } else if (*nummatches==-1) {
223 // prefix query error
224 err=protocolError;
225 return (NULL);
226 } else if (*nummatches==-2) {
227 // sendsearchRequest not answered by searchResponse
228 err=protocolError;
229 return (NULL);
230 }
231 }
232 // could do a sort eventually, eg on date, title, etc.
233 // (non-existent function) z_sort(field, asc|desc);
234 /* min of (count, first + (*nummatches) ) */
235 // z_getrecordTitles ( first, howmany )
236 c_str_titles=z_getrecordTitles(first,min(count,*nummatches-first+1));
237 if (c_str_titles==NULL) {
238 // an error occurred. we need a logout/err as an arg
239 return (NULL);
240 }
241 if (c_str_titles[0]==0) {
242 // no matches.
243 return (NULL);
244 }
245 last=(int)c_str_titles[0];
246 for (i=1;i<=last;i++) {
247 titles->push_back(c_str_titles[i]);
248 free(c_str_titles[i]);
249 }
250 free(c_str_titles);
251 return (titles);
252
253}
254
255bool z3950_server::getfullrecord(const text_t &query, const text_t &fields,
256 const int ID,
257 text_t &rettitle,
258 text_t &rettext, comerror_t &err) {
259
260 static char **c_str_titles=NULL;
261 static char *fulltext=NULL;
262 /* NOTE!!!!!! Because this code currently only works in cgi-bin mode,
263 we only ever do one request. Therefore, it is CURRENTLY OK to store
264 (cache) the retrieved titles, because if this function is ever called
265 more than once, the arguments will be the same each time.
266 (I think :)
267 */
268
269 gotsinglerecord=true; // well, not yet, but we've been called...
270
271 if (connected==false) {
272 if (connect()==false) {
273 // error connecting...
274 err=protocolError;
275 return (false);
276 }
277 // since we have just re-connected, we need to do the
278 // query again.
279
280 text_t expanded_query="";
281 parseQuery(query,fields,expanded_query);
282
283 char* query_str=expanded_query.getcstr();
284 int returned=z_cmd_dosearch(query_str);
285 delete query_str;
286 if (returned<=0) {
287 // 0 => none.
288 // <0 => error
289 err=protocolError;
290 return (false);
291 }
292 }
293
294 if (c_str_titles==NULL)
295 c_str_titles=z_getrecordTitles(ID,1); // check this return value.
296
297 if (rettitle!="unneeded") {
298 //int dummy;
299 if (c_str_titles!=NULL && (int)c_str_titles[0]==1) {
300 rettitle.setcstr(c_str_titles[1]); // and check this
301 ////// free (c_str_titles); - we want to "cache" it
302 } else {
303 // we didn't get something....
304 rettitle="Nothing Returned...";
305 }
306 }
307
308 if (fulltext==NULL)
309 // get the text
310 fulltext=z_getfullRecord(ID);
311
312 if (rettext!="unneeded") {
313 rettext.setcstr(fulltext);
314 }
315 return (true);
316 }
317
318text_t &z3950_server::getzAbout() {
319 text_t zserverresp;
320
321 // Assume we have not yet connected, so that must be done here.
322 if (connected==true)
323 return (z_initstr);
324
325 // we need to create the tcp connection to the target (server)
326 // z_initstr=new text_t;
327
328 if (connect()==false) {
329 z_initstr.setcstr("<H2>Server offline</H2>Error - could not connect to server <B>");
330 z_initstr += info->host;
331 z_initstr += "</B> on port ";
332 z_initstr += info->port;
333 z_initstr += "\n";
334 return (z_initstr);
335 }
336
337 // z_initstr currently contains the target's response. We want to
338 // PREPEND the following information.
339 zserverresp=z_initstr;
340 z_initstr="Internet server: <b>";
341 z_initstr+=info->host;
342 z_initstr+="</b> on port ";
343 z_initstr+=info->port;
344 z_initstr+=".<br>\n";
345 z_initstr+=zserverresp;
346
347 // should close /******* WHAT IF DOING A QUERY!??!?!? ********/
348 // z_cmd_close(0);
349 // connected=false;
350 return (z_initstr);
351}
352
Note: See TracBrowser for help on using the repository browser.