/********************************************************************** * * z3950server.cpp -- * Copyright (C) 2000 The New Zealand Digital Library Project * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ #include "z3950server.h" #include "comtypes.h" #include // z39.50 yaz stuff extern "C" { #include "yaz/yaz_zclient.h" } /*** each z39.50 server+database pair is a GSDL collection. ***/ z3950_server::z3950_server() { info=NULL; connected=false; titles=NULL; gotsinglerecord=false; // for now, assume that all records will have text associated with them. meta["hastxt"]="1"; // for now, assume we don't want ANY DocumentButtons. format["DocumentButtons"]=""; } z3950_server::~z3950_server() { } void z3950_server::setMeta(const text_t &key, const text_t &value) { meta[key]=value; } void z3950_server::setName(const text_t &newname) { title=newname; meta["collectionname"]=newname; } void z3950_server::addcfgAbout(const text_t &lang, const text_t &abouttext) { about[lang]=abouttext; } bool z3950_server::getcfgAbout(const text_t &lang, text_t &abouttxt) { text_tmap::iterator it; it=about.find(lang); if (it==about.end()) return (false); abouttxt=((*it).second); return (true); } // now functions that actually talk over the tcp connection. // create a tcp connection to the associated target. Currently, this will // re-initialise if we are already connected. bool z3950_server::connect() { text_t server_and_port; char *zserverinfo; server_and_port=info->host+":"+info->port; // remember that info.name is the database name z_initialize(); char* serv_str=server_and_port.getcstr(); char* name_str=info->name.getcstr(); int retval=z_cmd_open(serv_str,name_str); delete serv_str; delete name_str; if (retval==1) // we got a connection error return false; // get initialisation response. z_getnextAPDU(); zserverinfo=z_get_initResponse(); if (zserverinfo!=NULL) { z_initstr.appendcstr(zserverinfo); } free(zserverinfo); connected=true; return true; } void z3950_server::parseQuery(const text_t &query, const int querytype, const text_t &fields, text_t &parsed_query) { /****** FIXME *****/ // We need to format the query string into RPN - // by just passing it like this, it will only work for simple queries. // This will require us to actually come up with a query syntax and // a parser. For now, we'll just do an "AND" query for all terms // But look at Common Command Language (CCL) query syntax (ISO 8777). // need to remove " chars from the query. We should really tell the server // to do a phrase search on the terms that are between the "s, but we // can't (easily) tell if the server can do that or not, // so we'll currently just do a query and then post-process. (not yet // implemented........) // we need to count number of terms separated by a space char *ptr=query.getcstr(); int strlength=strlen(ptr); bool inword=false; int num_terms=0; for (int i=0;i Any // 1 => (Personal) Name // 4 => Title // 21 => Subject Heading // 45 => Subject precis // Note I have no idea how these actually work - I think some servers // only have limited fields, and map all subject-type requests into that // subject field, etc. parsed_query="@attr 1="; if (fields==".author") parsed_query+="1 "; else if (fields==".title") parsed_query+="4 "; else // fields==".any" parsed_query+="1016 "; // querytype=1 => ranked/or, =2 => boolean/and // append "@and" for each term after the first { char and_str[]="@and "; char or_str[]="@or "; char *q_type; if (querytype==1) q_type=or_str; else q_type=and_str; for (int i=1;ipush_back(c_str_titles[i]); free(c_str_titles[i]); } free(c_str_titles); return (titles); } bool z3950_server::getfullrecord(const text_t &query, const int querytype, const text_t &fields, const int ID, text_t &rettitle, text_t &rettext, comerror_t &err) { static char **c_str_titles=NULL; static char *fulltext=NULL; /* NOTE!!!!!! Because this code currently only works in cgi-bin mode, we only ever do one request. Therefore, it is CURRENTLY OK to store (cache) the retrieved titles, because if this function is ever called more than once, the arguments will be the same each time. (I think :) */ gotsinglerecord=true; // well, not yet, but we've been called... if (connected==false) { if (connect()==false) { // error connecting... err=protocolError; return (false); } // since we have just re-connected, we need to do the // query again. text_t expanded_query=""; parseQuery(query,querytype,fields,expanded_query); char* query_str=expanded_query.getcstr(); int returned=z_cmd_dosearch(query_str); delete query_str; if (returned<=0) { // 0 => none. // <0 => error err=protocolError; return (false); } } if (c_str_titles==NULL) c_str_titles=z_getrecordTitles(ID,1); // check this return value. if (rettitle!="unneeded") { //int dummy; if (c_str_titles!=NULL && (int)c_str_titles[0]==1) { rettitle.setcstr(c_str_titles[1]); // and check this ////// free (c_str_titles); - we want to "cache" it } else { // we didn't get something.... rettitle="Nothing Returned..."; } } if (fulltext==NULL) // get the text fulltext=z_getfullRecord(ID); if (rettext!="unneeded") { rettext.setcstr(fulltext); } return (true); } text_t &z3950_server::getzAbout() { text_t zserverresp; // Assume we have not yet connected, so that must be done here. if (connected==true) return (z_initstr); // we need to create the tcp connection to the target (server) // z_initstr=new text_t; if (connect()==false) { z_initstr.setcstr("

Server offline

Error - could not connect to server "); z_initstr += info->host; z_initstr += " on port "; z_initstr += info->port; z_initstr += "\n"; return (z_initstr); } // z_initstr currently contains the target's response. We want to // PREPEND the following information. zserverresp=z_initstr; z_initstr="Internet server: "; z_initstr+=info->host; z_initstr+=" on port "; z_initstr+=info->port; z_initstr+=".
\n"; z_initstr+=zserverresp; // should close /******* WHAT IF DOING A QUERY!??!?!? ********/ // z_cmd_close(0); // connected=false; return (z_initstr); }