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

Last change on this file since 1371 was 1371, checked in by jrm21, 24 years ago

Added copyright notice.

  • Property svn:keywords set to Author Date Id Revision
File size: 7.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 // for now, assume that all records will have text associated with them.
44 meta["hastxt"]="1";
45 // for now, assume we don't want ANY DocumentButtons.
46 format["DocumentButtons"]="";
47}
48
49z3950_server::~z3950_server() {
50}
51
52void z3950_server::setMeta(const text_t &key, const text_t &value) {
53 meta[key]=value;
54}
55
56void z3950_server::setName(const text_t &newname) {
57 title=newname;
58 meta["collectionname"]=newname;
59}
60
61void z3950_server::addcfgAbout(const text_t &lang, const text_t &abouttext) {
62 about[lang]=abouttext;
63}
64
65bool z3950_server::getcfgAbout(const text_t &lang, text_t &abouttxt) {
66 text_tmap::iterator it;
67 it=about.find(lang);
68 if (it==about.end()) return (false);
69 abouttxt=((*it).second);
70 return (true);
71}
72
73
74// now functions that actually talk over the tcp connection.
75
76// create a tcp connection to the associated target. Currently, this will
77// re-initialise if we are already connected.
78bool z3950_server::connect() {
79 text_t server_and_port;
80 char *zserverinfo;
81
82 server_and_port=info->host+":"+info->port;
83 // remember that info.name is the database name
84
85 z_initialize();
86
87 if (z_cmd_open(server_and_port.getcstr(),info->name.getcstr())==1)
88 // we got a connection error
89 return false;
90
91 // get initialisation response.
92 z_getnextAPDU();
93 zserverinfo=z_get_initResponse();
94 if (zserverinfo!=NULL) {
95 z_initstr.appendcstr(zserverinfo);
96 }
97 free(zserverinfo);
98
99 connected=true;
100 return true;
101}
102
103void z3950_server::parseQuery(const text_t &query,
104 const text_t &fields,
105 text_t &parsed_query) {
106 /****** FIXME *****/
107 // We need to format the query string into RPN -
108 // by just passing it like this, it will only work for simple queries.
109 // This will require us to actually come up with a query syntax and
110 // a parser. For now, we'll just do an "AND" query for all terms
111
112 // we need to count number of terms separated by a space
113 char *ptr=query.getcstr();
114 int strlength=strlen(ptr);
115 bool inword=false;
116 int num_terms=0;
117 for (int i=0;i<strlength;i++) {
118 if (*(ptr+i)!=' ') {
119 if (inword==false) {
120 inword=true;
121 num_terms++;
122 }
123 }
124 else { // ptr+i is a space
125 inword=false;
126 }
127 }
128
129 // set the field(s) to search on - main ones (that I've found) include:
130 // 1016 => Any
131 // 1 => (Personal) Name
132 // 4 => Title
133 // 21 => Subject Heading
134 // 45 => Subject precis
135 // Note I have no idea how these actually work - I think some servers
136 // only have limited fields, and map all subject-type requests into that
137 // subject field, etc.
138
139 parsed_query="@attr 1=";
140 if (fields==".author")
141 parsed_query+="1 ";
142 else if (fields==".title")
143 parsed_query+="4 ";
144 else // fields==".any"
145 parsed_query+="1016 ";
146
147 // append "@and" for each term after the first
148 for (int i=1;i<num_terms;i++)
149 parsed_query+="@and ";
150 // append the actual query
151 parsed_query+=query;
152
153}
154
155text_tarray *z3950_server::getrecordTitles(const text_t &query,
156 const text_t &fields,
157 int first, int count,
158 int *nummatches, comerror_t &err) {
159 char **c_str_titles;
160 int i;
161 int last;
162 if (titles!=NULL) delete (titles);
163 titles=new text_tarray;
164
165 /* check if connected */
166 if (connected==false)
167 if (connect()==false) {
168 // we could not connect.
169 err=protocolError;
170 return (NULL);
171 }
172
173
174 text_t expanded_query="";
175 parseQuery(query,fields,expanded_query);
176
177 // following functions defined in yaz_zclient.c
178 *nummatches=z_cmd_dosearch(expanded_query.getcstr()); // returns # found, -1 on err.
179 if (*nummatches<=0) {
180 if (*nummatches==0) {
181 // no matches
182 return (NULL);
183 } else if (*nummatches==-1) {
184 // prefix query error
185 err=protocolError;
186 return (NULL);
187 } else if (*nummatches==-2) {
188 // sendsearchRequest not answered by searchResponse
189 err=protocolError;
190 return (NULL);
191 }
192 }
193 // could do a sort eventually, eg on date, title, etc.
194 // (non-existent function) z_sort(field, asc|desc);
195 /* min of (count, first + (*nummatches) ) */
196 // z_getrecordTitles ( first, howmany )
197 c_str_titles=z_getrecordTitles(first,min(count,*nummatches-first+1));
198 if (c_str_titles==NULL) {
199 // an error occurred. we need a logout/err as an arg
200 return (NULL);
201 }
202 if (c_str_titles[0]==0) {
203 // no matches.
204 return (NULL);
205 }
206 last=(int)c_str_titles[0];
207 for (i=1;i<=last;i++) {
208 titles->push_back(c_str_titles[i]);
209 free(c_str_titles[i]);
210 }
211 free(c_str_titles);
212 return (titles);
213
214}
215
216bool z3950_server::getfullrecord(const text_t &query, const text_t &fields,
217 const int ID,
218 text_t &rettitle,
219 text_t &rettext, comerror_t &err) {
220 if (connected==false) {
221 if (connect()==false) {
222 // error connecting...
223 err=protocolError;
224 return (false);
225 }
226 // since we have just re-connected, we need to do the
227 // query again.
228
229 text_t expanded_query="";
230 parseQuery(query,fields,expanded_query);
231
232 int returned=z_cmd_dosearch(expanded_query.getcstr());
233 if (returned<=0) {
234 // 0 => none.
235 // <0 => error
236 err=protocolError;
237 return (false);
238 }
239 }
240
241 if (rettitle!="unneeded") {
242 char **c_str_titles;
243 //int dummy;
244 c_str_titles=z_getrecordTitles(ID,1); // check this return value.
245 if (c_str_titles!=NULL && (int)c_str_titles[0]==1) {
246 rettitle.setcstr(c_str_titles[1]); // and check this
247 free (c_str_titles);
248 // rettitle="Dummy Title";
249 } else {
250 // we didn't get something....
251 rettitle="Nothing Returned...";
252 }
253 }
254
255 if (rettext!="unneeded") {
256 // get the text
257 rettext.setcstr(z_getfullRecord(ID));
258 }
259 return (true);
260 }
261
262text_t &z3950_server::getzAbout() {
263 text_t zserverresp;
264
265 // Assume we have not yet connected, so that must be done here.
266 if (connected==true)
267 return (z_initstr);
268
269 // we need to create the tcp connection to the target (server)
270 // z_initstr=new text_t;
271
272 if (connect()==false) {
273 z_initstr.setcstr("<H2>Server offline</H2>Error - could not connect to server <B>");
274 z_initstr += info->host.getcstr();
275 z_initstr += "</B> on port ";
276 z_initstr += info->port;
277 z_initstr += "\n";
278 return (z_initstr);
279 }
280
281 // z_initstr currently contains the target's response. We want to
282 // PREPEND the following information.
283 zserverresp=z_initstr;
284 z_initstr="Internet server: <b>";
285 z_initstr+=info->host;
286 z_initstr+="</b> on port ";
287 z_initstr+=info->port;
288 z_initstr+=".<br>\n";
289 z_initstr+=zserverresp;
290
291 // should close /******* WHAT IF DOING A QUERY!??!?!? ********/
292 // z_cmd_close(0);
293 // connected=false;
294 return (z_initstr);
295}
296
Note: See TracBrowser for help on using the repository browser.