php-src/ext/yaz/php_yaz.c
2000-06-16 18:24:02 +00:00

1599 lines
37 KiB
C

/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Adam Dickmeiss <adam@indexdata.dk> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "php.h"
#if HAVE_YAZ
#include "php_yaz.h"
#include <yaz/proto.h>
#include <yaz/tcpip.h>
#include <yaz/pquery.h>
#include <yaz/diagbib1.h>
#include <yaz/otherinfo.h>
#include <yaz/marcdisp.h>
#include <yaz/yaz-util.h>
#define MAX_ASSOC 100
#define PHP_YAZ_DEBUG 1
typedef struct Yaz_ResultSetInfo *Yaz_ResultSet;
typedef struct Yaz_AssociationInfo *Yaz_Association;
typedef struct Yaz_RecordCacheInfo *Yaz_RecordCache;
struct Yaz_RecordCacheInfo {
Z_NamePlusRecordList recordList;
Yaz_RecordCache next;
};
struct Yaz_ResultSetInfo {
Z_Query *query;
Z_ReferenceId *referenceId;
ODR odr;
int resultCount;
Yaz_ResultSet next;
Z_NamePlusRecordList *recordList;
};
#define PHP_YAZ_SELECT_READ 1
#define PHP_YAZ_SELECT_WRITE 2
#define PHP_YAZ_STATE_CONNECTING 1
#define PHP_YAZ_STATE_ESTABLISHED 2
#define PHP_YAZ_STATE_CLOSED 3
#define PHP_YAZ_ERROR_CONNECTION_LOST (-1)
#define PHP_YAZ_ERROR_DECODE (-2)
#define PHP_YAZ_ERROR_ENCODE (-3)
#define PHP_YAZ_ERROR_CONNECT (-4)
#define PHP_YAZ_ERROR_INIT (-5)
#define PHP_YAZ_ERROR_TIMEOUT (-6)
struct Yaz_AssociationInfo {
char *host_port;
int num_databaseNames;
char **databaseNames;
COMSTACK cs;
char *cookie;
char *proxy;
char *auth_open;
char *user;
char *group;
char *pass;
int error;
char *addinfo;
Yaz_ResultSet resultSets;
int order;
int state;
int mask_select;
int reconnect_flag;
ODR odr_in;
ODR odr_out;
char *buf_out;
int len_out;
char *buf_in;
int len_in;
int (*action)(Yaz_Association t);
int resultSetStartPoint;
int numberOfRecordsRequested;
char *elementSetNames;
char *preferredRecordSyntax;
};
static Yaz_Association yaz_association_mk ()
{
Yaz_Association p = xmalloc (sizeof(*p));
p->host_port = 0;
p->num_databaseNames = 0;
p->databaseNames = 0;
p->cs = 0;
p->cookie = 0;
p->proxy = 0;
p->auth_open = 0;
p->user = 0;
p->group = 0;
p->pass = 0;
p->error = 0;
p->addinfo = 0;
p->resultSets = 0;
p->order = 0;
p->state = PHP_YAZ_STATE_CLOSED;
p->mask_select = 0;
p->reconnect_flag = 0;
p->odr_in = odr_createmem (ODR_DECODE);
p->odr_out = odr_createmem (ODR_ENCODE);
p->buf_out = 0;
p->len_out = 0;
p->buf_in = 0;
p->len_in = 0;
p->action = 0;
p->resultSetStartPoint = 1;
p->numberOfRecordsRequested = 10;
p->elementSetNames = 0;
p->preferredRecordSyntax = 0;
return p;
}
static void yaz_association_destroy (Yaz_Association p)
{
int i;
if (!p)
return ;
xfree (p->host_port);
for (i = 0; i<p->num_databaseNames; i++)
xfree (p->databaseNames[i]);
xfree (p->databaseNames);
if (p->cs)
cs_close (p->cs);
xfree (p->cookie);
xfree (p->proxy);
xfree (p->auth_open);
xfree (p->user);
xfree (p->group);
xfree (p->pass);
xfree (p->addinfo);
odr_destroy (p->odr_in);
odr_destroy (p->odr_out);
/* buf_out */
/* buf_in */
/* action */
xfree (p->elementSetNames);
xfree (p->preferredRecordSyntax);
}
static Yaz_ResultSet yaz_resultset_mk()
{
ODR odr = odr_createmem (ODR_ENCODE);
Yaz_ResultSet p = odr_malloc (odr, sizeof(*p));
p->query = 0;
p->referenceId = 0;
p->odr = odr;
p->resultCount = 0;
p->next = 0;
p->recordList = 0;
return p;
}
static void yaz_resultset_destroy (Yaz_ResultSet p)
{
if (!p)
return;
if (p->odr)
odr_destroy (p->odr);
}
static Yaz_Association *shared_associations;
static int order_associations;
/*
when id = 0, it means all targes...
id = yaz_connect(zurl, user,group, pass);
yaz_set_db(id, db)
yaz_error(id)
yaz_errno(id)
yaz_addinfo(id)
yaz_wait()
yaz_range(id, from, to, syntax, elementset)
yaz_search(id, type, query)
yaz_hits(id)
yaz_record(id, pos)
yaz_scan(id)
yaz_scan_point(id, scanterm, before, after)
yaz_scan_result(id, pos)
yaz_close(id)
*/
function_entry yaz_functions [] = {
PHP_FE(yaz_connect, NULL)
PHP_FE(yaz_close, NULL)
PHP_FE(yaz_search, NULL)
PHP_FE(yaz_wait, NULL)
PHP_FE(yaz_errno, NULL)
PHP_FE(yaz_error, NULL)
PHP_FE(yaz_addinfo, NULL)
PHP_FE(yaz_hits, NULL)
PHP_FE(yaz_record, NULL)
PHP_FE(yaz_syntax, NULL)
PHP_FE(yaz_element, NULL)
PHP_FE(yaz_range, NULL)
{NULL, NULL, NULL}
};
static Yaz_Association get_assoc (pval **id)
{
int i;
convert_to_long_ex(id);
i = (*id)->value.lval;
if (i < 1 || i > MAX_ASSOC)
return 0;
return shared_associations[i-1];
}
static void do_close (Yaz_Association p)
{
p->mask_select = 0;
p->state = PHP_YAZ_STATE_CLOSED;
if (p->cs)
{
cs_close (p->cs);
p->cs = 0;
}
}
static void do_connect (Yaz_Association p)
{
void *addr;
p->reconnect_flag = 0;
p->cs = cs_create (tcpip_type, 0, PROTO_Z3950);
addr = tcpip_strtoaddr (p->host_port);
if (!addr)
{
do_close(p);
p->error = PHP_YAZ_ERROR_CONNECT;
return;
}
cs_connect (p->cs, addr);
p->state = PHP_YAZ_STATE_CONNECTING;
p->mask_select = PHP_YAZ_SELECT_READ | PHP_YAZ_SELECT_WRITE;
}
static void response_diag (Yaz_Association t, Z_DiagRec *p)
{
Z_DefaultDiagFormat *r;
char *addinfo = 0;
xfree (t->addinfo);
t->addinfo = 0;
if (p->which != Z_DiagRec_defaultFormat)
{
t->error = PHP_YAZ_ERROR_DECODE;
return;
}
r = p->u.defaultFormat;
#ifdef ASN_COMPILED
switch (r->which)
{
case Z_DefaultDiagFormat_v2Addinfo:
addinfo = r->u.v2Addinfo;
break;
case Z_DefaultDiagFormat_v3Addinfo:
addinfo = r->u.v3Addinfo;
break;
}
#else
addinfo = r->addinfo;
#endif
if (addinfo)
t->addinfo = xstrdup (addinfo);
t->error = *r->condition;
}
static int send_present (Yaz_Association t);
void handle_records (Yaz_Association t, Z_Records *sr)
{
if (sr && sr->which == Z_Records_NSD)
{
#ifdef ASN_COMPILED
Z_DiagRec dr, *dr_p = &dr;
dr.which = Z_DiagRec_defaultFormat;
dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
response_diag (t, dr_p);
#else
response_diag (t, sr->u.nonSurrogateDiagnostic);
#endif
}
else if (sr && sr->which == Z_Records_multipleNSD)
{
if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
response_diag(t, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
else
t->error = PHP_YAZ_ERROR_DECODE;
}
else
{
if (t->numberOfRecordsRequested > 0 && !t->resultSets->recordList)
{
int i;
t->resultSets->recordList =
odr_malloc (t->resultSets->odr,
sizeof(*t->resultSets->recordList));
t->resultSets->recordList->records =
odr_malloc (t->resultSets->odr, t->numberOfRecordsRequested
* sizeof(*t->resultSets->recordList->records));
for (i = 0; i < t->numberOfRecordsRequested; i++)
t->resultSets->recordList->records[i] = 0;
if (t->numberOfRecordsRequested + t->resultSetStartPoint-1 >
t->resultSets->resultCount)
t->numberOfRecordsRequested = t->resultSets->resultCount -
t->resultSetStartPoint + 1;
t->resultSets->recordList->num_records =
t->numberOfRecordsRequested;
}
if (sr && sr->which == Z_Records_DBOSD)
{
int j, i;
Z_NamePlusRecordList *p =
sr->u.databaseOrSurDiagnostics;
for (j = 0; j < t->resultSets->recordList->num_records; j++)
if (!t->resultSets->recordList->records[j])
break;
for (i = 0; i<p->num_records; i++)
t->resultSets->recordList->records[i+j] = p->records[i];
/* transfer our response to search_nmem .. we need it later */
nmem_transfer (t->resultSets->odr->mem, odr_extract_mem (t->odr_in));
}
}
}
static void search_response (Yaz_Association t, Z_SearchResponse *sr)
{
t->resultSets->resultCount = *sr->resultCount;
handle_records (t, sr->records);
}
static void present_response (Yaz_Association t, Z_PresentResponse *pr)
{
handle_records (t, pr->records);
}
static void handle_apdu (Yaz_Association t, Z_APDU *apdu)
{
Z_InitResponse *initrs;
t->mask_select = 0;
switch(apdu->which)
{
case Z_APDU_initResponse:
initrs = apdu->u.initResponse;
if (!*initrs->result)
{
t->error = PHP_YAZ_ERROR_INIT;
}
else
{
char *cookie =
yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
VAL_COOKIE, 1, 0);
if (cookie)
{
xfree(t->cookie);
t->cookie = xstrdup(cookie);
}
(*t->action) (t);
}
break;
case Z_APDU_searchResponse:
search_response (t, apdu->u.searchResponse);
send_present (t);
break;
case Z_APDU_presentResponse:
present_response (t, apdu->u.presentResponse);
send_present (t);
break;
#if 0
case Z_APDU_scanResponse:
zlog (req, t->name, " scan response");
scanResponse (req, t, apdu->u.scanResponse);
break;
#endif
#if USE_ES
case Z_APDU_extendedServicesResponse:
zlog (req, t->name, " ES response");
esResponse (req, t, apdu->u.extendedServicesResponse);
break;
#endif
case Z_APDU_close:
do_close(t);
if (t->reconnect_flag)
do_connect (t);
else
t->error = PHP_YAZ_ERROR_CONNECTION_LOST;
break;
default:
do_close (t);
t->error = PHP_YAZ_ERROR_DECODE;
break;
}
}
static int do_read (Yaz_Association t)
{
int r;
Z_APDU *apdu;
r = cs_get (t->cs, &t->buf_in, &t->len_in);
if (r == 1)
return 0;
if (r <= 0)
{
do_close (t);
if (t->reconnect_flag)
{
do_connect (t);
}
else
{
do_close (t);
t->error = PHP_YAZ_ERROR_CONNECTION_LOST;
}
}
else
{
odr_reset (t->odr_in);
odr_setbuf (t->odr_in, t->buf_in, r, 0);
if (!z_APDU (t->odr_in, &apdu, 0, 0))
{
do_close (t);
t->error = PHP_YAZ_ERROR_DECODE;
}
else
{
/* apdu_log(req, t->odr_print, apdu); */
handle_apdu (t, apdu);
}
}
return 1;
}
static int do_write (Yaz_Association t)
{
int r;
if ((r=cs_put (t->cs, t->buf_out, t->len_out)) < 0)
{
if (t->reconnect_flag)
{
do_close (t);
do_connect (t);
}
else
{
if (t->state == PHP_YAZ_STATE_CONNECTING)
t->error = PHP_YAZ_ERROR_CONNECT;
else
t->error = PHP_YAZ_ERROR_CONNECTION_LOST;
do_close (t);
return 1;
}
}
else if (r == 1)
{
t->state = PHP_YAZ_STATE_ESTABLISHED;
t->mask_select = PHP_YAZ_SELECT_READ|PHP_YAZ_SELECT_WRITE;
}
else
{
t->state = PHP_YAZ_STATE_ESTABLISHED;
t->mask_select = PHP_YAZ_SELECT_READ;
}
return 0;
}
static int send_APDU (Yaz_Association t, Z_APDU *a)
{
if (t->cookie)
{
Z_OtherInformation **oi;
yaz_oi_APDU(a, &oi);
yaz_oi_set_string_oidval(oi, t->odr_out, VAL_COOKIE, 1, t->cookie);
}
/* from ZAP */
#if 0
if (req->request->connection)
{
Z_OtherInformation **oi;
zlog (req, t->name, " encoding client_ip");
yaz_oi_APDU(a, &oi);
yaz_oi_set_string_oidval(oi, t->odr_out, VAL_CLIENT_IP, 1,
req->request->connection->remote_ip);
}
#endif
if (!z_APDU(t->odr_out, &a, 0, 0))
{
do_close (t);
t->error = PHP_YAZ_ERROR_ENCODE;
return -1;
}
/* apdu_log(req, t->odr_print, a); */
t->buf_out = odr_getbuf(t->odr_out, &t->len_out, 0);
odr_reset(t->odr_out);
do_write (t);
return 0;
}
static char **set_DatabaseNames (Yaz_Association t, int *num)
{
char **databaseNames;
char *c, *cp = strchr (t->host_port, '/');
int no = 2;
if (cp)
{
c = cp;
while ((c = strchr(c, '+')))
{
c++;
no++;
}
}
else
cp = "/Default";
databaseNames = odr_malloc (t->odr_out, no * sizeof(*databaseNames));
no = 0;
while (*cp)
{
c = ++cp;
c = strchr (c, '+');
if (!c)
c = cp + strlen(cp);
else if (c == cp)
continue;
/* cp ptr to first char of db name, c is char following db name */
databaseNames[no] = odr_malloc (t->odr_out, 1+c-cp);
memcpy (databaseNames[no], cp, c-cp);
databaseNames[no][c-cp] = '\0';
no++;
cp = c;
}
databaseNames[no] = NULL;
*num = no;
return databaseNames;
}
static int send_search (Yaz_Association t)
{
Yaz_ResultSet r = t->resultSets;
Z_APDU *apdu = zget_APDU(t->odr_out, Z_APDU_searchRequest);
Z_SearchRequest *sreq = apdu->u.searchRequest;
/* resultSetPrepare (req, t, req->cur_pa); */
if (t->resultSetStartPoint == 1)
{
sreq->largeSetLowerBound = odr_malloc (t->odr_out, sizeof(int));
*sreq->largeSetLowerBound = 999999;
sreq->smallSetUpperBound = &t->numberOfRecordsRequested;
sreq->mediumSetPresentNumber = &t->numberOfRecordsRequested;
if (t->elementSetNames && *t->elementSetNames)
{
Z_ElementSetNames *esn = odr_malloc (t->odr_out, sizeof(*esn));
esn->which = Z_ElementSetNames_generic;
esn->u.generic = t->elementSetNames;
sreq->mediumSetElementSetNames = esn;
sreq->smallSetElementSetNames = esn;
}
}
else
{
sreq->smallSetUpperBound = odr_malloc (t->odr_out, sizeof(int));
*sreq->smallSetUpperBound = 0;
sreq->largeSetLowerBound = odr_malloc (t->odr_out, sizeof(int));
*sreq->largeSetLowerBound = 1;
sreq->mediumSetPresentNumber = odr_malloc (t->odr_out, sizeof(int));
*sreq->mediumSetPresentNumber = 0;
}
sreq->query = r->query;
if (t->preferredRecordSyntax && *t->preferredRecordSyntax)
{
struct oident ident;
ident.proto = PROTO_Z3950;
ident.oclass = CLASS_RECSYN;
ident.value = oid_getvalbyname (t->preferredRecordSyntax);
sreq->preferredRecordSyntax =
odr_oiddup (t->odr_out, oid_getoidbyent (&ident));
}
sreq->databaseNames = set_DatabaseNames (t, &sreq->num_databaseNames);
send_APDU (t, apdu);
return 1;
}
static int send_present (Yaz_Association t)
{
Z_APDU *apdu = zget_APDU(t->odr_out, Z_APDU_presentRequest);
Z_PresentRequest *req = apdu->u.presentRequest;
int i = 0;
if (!t->resultSets->recordList) /* no records to retrieve at all .. */
{
#if PHP_YAZ_DEBUG
php_log_err("No records to retrieve...");
#endif
return 0;
}
while (1)
{
if (i >= t->resultSets->recordList->num_records)
{ /* got all records ... */
/* searchHits (zreq, t); */
return 0;
}
if (!t->resultSets->recordList->records[i])
break;
i++;
}
/* got record(s) to retrieve */
req->resultSetStartPoint = odr_malloc (t->odr_out, sizeof(int));
*req->resultSetStartPoint = t->resultSetStartPoint + i;
req->numberOfRecordsRequested = odr_malloc (t->odr_out, sizeof(int));
*req->numberOfRecordsRequested = t->resultSets->recordList->num_records - i;
if (t->preferredRecordSyntax && *t->preferredRecordSyntax)
{
struct oident ident;
ident.proto = PROTO_Z3950;
ident.oclass = CLASS_RECSYN;
ident.value = oid_getvalbyname (t->preferredRecordSyntax);
req->preferredRecordSyntax =
odr_oiddup (t->odr_out, oid_getoidbyent (&ident));
}
if (t->elementSetNames && *t->elementSetNames)
{
Z_ElementSetNames *esn = odr_malloc (t->odr_out, sizeof(*esn));
Z_RecordComposition *compo = odr_malloc (t->odr_out, sizeof(*compo));
esn->which = Z_ElementSetNames_generic;
esn->u.generic = t->elementSetNames;
compo->which = Z_RecordComp_simple;
compo->u.simple = esn;
req->recordComposition = compo;
}
send_APDU (t, apdu);
return 1;
}
static void send_init(Yaz_Association t)
{
int i = 0;
Z_APDU *apdu = zget_APDU(t->odr_out, Z_APDU_initRequest);
Z_InitRequest *ireq = apdu->u.initRequest;
Z_IdPass *pass = odr_malloc(t->odr_out, sizeof(*pass));
Z_IdAuthentication *auth = odr_malloc(t->odr_out, sizeof(*auth));
const char *auth_open = t->auth_open;
const char *auth_groupId = t->group;
const char *auth_userId = t->user;
const char *auth_password = t->pass;
ODR_MASK_SET(ireq->options, Z_Options_search);
ODR_MASK_SET(ireq->options, Z_Options_present);
ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
ODR_MASK_SET(ireq->options, Z_Options_scan);
ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
ireq->implementationName = "PHP/YAZ";
*ireq->maximumRecordSize = 1024*1024;
*ireq->preferredMessageSize = 1024*1024;
if (auth_open && *auth_open)
{
auth->which = Z_IdAuthentication_open;
auth->u.open = odr_malloc(t->odr_out, strlen(auth_open)+1);
strcpy(auth->u.open, auth_open);
ireq->idAuthentication = auth;
}
pass->groupId = 0;
if (auth_groupId && *auth_groupId)
{
pass->groupId = odr_malloc(t->odr_out, strlen(auth_groupId)+1);
strcpy(pass->groupId, auth_groupId);
i++;
}
pass->userId = 0;
if (auth_userId && *auth_userId)
{
pass->userId = odr_malloc(t->odr_out, strlen(auth_userId)+1);
strcpy(pass->userId, auth_userId);
i++;
}
pass->password = 0;
if (auth_password && *auth_password)
{
pass->password = odr_malloc(t->odr_out, strlen(auth_password)+1);
strcpy(pass->password, auth_password);
i++;
}
if(i)
{
auth->which = Z_IdAuthentication_idPass;
auth->u.idPass = pass;
ireq->idAuthentication = auth;
}
if (t->proxy)
yaz_oi_set_string_oidval(&ireq->otherInfo, t->odr_out,
VAL_PROXY, 1, t->host_port);
send_APDU (t, apdu);
}
static int do_event (int *id)
{
fd_set input, output;
int i;
int no = 0;
int max_fd = 0;
struct timeval tv;
tv.tv_sec = 15;
tv.tv_usec = 0;
FD_ZERO (&input);
FD_ZERO (&output);
for (i = 0; i < MAX_ASSOC; i++)
{
Yaz_Association p = shared_associations[i];
int fd;
if (!p || p->order != order_associations || !p->cs)
continue;
fd = cs_fileno (p->cs);
if (max_fd < fd)
max_fd = fd;
if (p->mask_select & PHP_YAZ_SELECT_READ)
{
FD_SET (fd, &input);
no++;
}
if (p->mask_select & PHP_YAZ_SELECT_WRITE)
{
FD_SET (fd, &output);
no++;
}
}
if (!no)
return 0;
no = select (max_fd+1, &input, &output, 0, &tv);
for (i = 0; i<MAX_ASSOC; i++)
{
int fd;
Yaz_Association p = shared_associations[i];
if (!p || p->order != order_associations || !p->cs)
continue;
*id = i+1;
fd =cs_fileno(p->cs);
if (no <= 0)
{
if (p->mask_select) /* only mark for those still pending */
{
if (p->state == PHP_YAZ_STATE_CONNECTING)
p->error = PHP_YAZ_ERROR_CONNECT;
else
p->error = PHP_YAZ_ERROR_TIMEOUT;
do_close (p);
}
}
else if (p->state == PHP_YAZ_STATE_CONNECTING)
{
if (FD_ISSET (fd, &input))
{
do_close(p);
p->error = PHP_YAZ_ERROR_CONNECT;
return 2;
}
else if (FD_ISSET (fd, &output))
{
send_init(p);
}
}
else if (p->state == PHP_YAZ_STATE_ESTABLISHED)
{
if (FD_ISSET (fd, &input))
do_read (p);
if (p->cs && FD_ISSET (fd, &output))
do_write (p);
}
else
{
do_close (p);
p->error = PHP_YAZ_ERROR_CONNECTION_LOST;
}
}
return no;
}
/* {{{ proto int yaz_connect(string zurl)
Create target with given zurl. Returns positive id if succesful. */
PHP_FUNCTION(yaz_connect)
{
int i;
char *cp;
char *zurl_str;
pval **zurl;
if (ZEND_NUM_ARGS() < 1 || zend_get_parameters_ex (1, &zurl) == FAILURE)
{
WRONG_PARAM_COUNT;
}
convert_to_string_ex (zurl);
zurl_str = (*zurl)->value.str.val;
for (cp = zurl_str; *cp && strchr("\t\n ", *cp); cp++)
;
if (!*cp)
RETURN_LONG(0);
/* see if we have it already ... */
for (i = 0; i<MAX_ASSOC; i++)
if (shared_associations[i] && shared_associations[i]->host_port &&
shared_associations[i]->order != order_associations &&
!strcmp (shared_associations[i]->host_port, zurl_str))
break;
if (i == MAX_ASSOC)
{
/* we didn't have it (or already in use) */
int i0 = -1;
int min_order = 2000000000;
/* find completely free slot or the oldest one */
for (i = 0; i<MAX_ASSOC && shared_associations[i]; i++)
if (shared_associations[i]->order < min_order
&& shared_associations[i]->order != order_associations)
{
min_order = shared_associations[i]->order;
i0 = i;
}
if (i == MAX_ASSOC)
{
i = i0;
if (i == -1)
{
RETURN_LONG(0); /* no free slot */
}
else /* "best" free slot */
yaz_association_destroy(shared_associations[i]);
}
shared_associations[i] = yaz_association_mk ();
shared_associations[i]->host_port = xstrdup (zurl_str);
}
shared_associations[i]->order = order_associations;
shared_associations[i]->error = 0;
shared_associations[i]->numberOfRecordsRequested = 10;
shared_associations[i]->resultSetStartPoint = 1;
RETURN_LONG(i+1);
}
/* }}} */
/* {{{ proto int yaz_close(int id)
Destory and close target */
PHP_FUNCTION(yaz_close)
{
pval **id;
int i;
if (ZEND_NUM_ARGS() != 1)
WRONG_PARAM_COUNT;
if (zend_get_parameters_ex (1, &id) == FAILURE)
RETURN_FALSE;
convert_to_long_ex (id);
i = (*id)->value.lval -1;
if (i < 0 || i >= MAX_ASSOC || !shared_associations[i])
RETURN_FALSE;
yaz_association_destroy (shared_associations[i]);
shared_associations[i] = 0;
RETURN_TRUE;
}
/* }}} */
/* {{{ proto int yaz_search(int id, string type, string query)
Specify query of type for search - returns true if successful */
PHP_FUNCTION(yaz_search)
{
char *query_str, *type_str;
pval **id, **type, **query;
Yaz_Association p;
Yaz_ResultSet r;
if (ZEND_NUM_ARGS() != 3)
WRONG_PARAM_COUNT;
if (zend_get_parameters_ex(3, &id, &type, &query) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (id);
if (!p)
{
#if PHP_YAZ_DEBUG
php_log_err ("get_assoc failed");
#endif
RETURN_FALSE;
}
convert_to_string_ex (type);
type_str = (*type)->value.str.val;
convert_to_string_ex (query);
query_str = (*query)->value.str.val;
yaz_resultset_destroy (p->resultSets);
r = p->resultSets = yaz_resultset_mk();
r->query = odr_malloc (r->odr, sizeof(*r->query));
if (!strcmp (type_str, "rpn"))
{
r->query->which = Z_Query_type_1;
r->query->u.type_1 = p_query_rpn (r->odr, PROTO_Z3950, query_str);
if (!r->query->u.type_1)
{
yaz_resultset_destroy(r);
p->resultSets = 0;
#if PHP_YAZ_DEBUG
php_log_err ("bad rpn");
#endif
RETURN_FALSE;
}
}
else
{
yaz_resultset_destroy(r);
p->resultSets = 0;
#if PHP_YAZ_DEBUG
php_log_err ("bad query type");
#endif
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto int yaz_wait()
Process all outstanding events. */
PHP_FUNCTION(yaz_wait)
{
int i;
int id;
for (i = 0; i<MAX_ASSOC; i++)
{
Yaz_Association p = shared_associations[i];
if (!p || p->order != order_associations || !p->resultSets)
continue;
p->action = send_search;
if (!p->cs)
{
do_connect (p);
}
else
{
p->reconnect_flag = 1;
send_search (p);
}
}
while (do_event(&id))
;
RETURN_TRUE;
}
/* }}} */
/* {{{ proto int yaz_errno(int id)
Return last error number (>0 for bib-1 diagnostic, <0 for other error, 0 for no error */
PHP_FUNCTION(yaz_errno)
{
pval **id;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &id) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (id);
if (!p)
{
RETURN_LONG(0);
}
RETURN_LONG(p->error);
}
/* }}} */
/* {{{ proto string yaz_error(int id)
Return last error message */
PHP_FUNCTION(yaz_error)
{
pval **id;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &id) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (id);
if (p && p->error)
{
const char *msg = 0;
if (p->error < 0)
{
switch (p->error)
{
case PHP_YAZ_ERROR_CONNECTION_LOST:
msg = "connection lost";
break;
case PHP_YAZ_ERROR_DECODE:
msg = "decoding failure";
break;
case PHP_YAZ_ERROR_ENCODE:
msg = "encoding failure";
break;
case PHP_YAZ_ERROR_CONNECT:
msg = "connect failed";
break;
case PHP_YAZ_ERROR_INIT:
msg = "initialization failed";
break;
case PHP_YAZ_ERROR_TIMEOUT:
msg = "timeout failure";
break;
default:
msg = "unknown failure";
break;
}
}
else
{
msg = diagbib1_str (p->error);
if (!msg)
msg = "unknown diagnostic";
}
/* Not macro using because RETURN_STRING throws away const */
return_value->value.str.len = strlen(msg);
return_value->value.str.val =
estrndup(msg, return_value->value.str.len);
return_value->type = IS_STRING;
}
}
/* }}} */
/* {{{ proto string yaz_addinfo(int id)
Return additional info for last error (empty string if none) */
PHP_FUNCTION(yaz_addinfo)
{
pval **id;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &id) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (id);
if (p && p->error > 0 && p->addinfo && *p->addinfo)
{
RETURN_STRING(p->addinfo, 1);
}
}
/* }}} */
/* {{{ proto int yaz_hits(int id)
Return number of hits (result count) for last search */
PHP_FUNCTION(yaz_hits)
{
pval **id;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &id) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (id);
if (!p || !p->resultSets)
{
RETURN_LONG(0);
}
RETURN_LONG(p->resultSets->resultCount);
}
/* }}} */
Z_GenericRecord *marc_to_grs1(const char *buf, ODR o, Odr_oid *oid)
{
int entry_p;
int record_length;
int indicator_length;
int identifier_length;
int base_address;
int length_data_entry;
int length_starting;
int length_implementation;
int max_elements = 256;
Z_GenericRecord *r = odr_malloc (o, sizeof(*r));
r->elements = odr_malloc (o, sizeof(*r->elements) * max_elements);
r->num_elements = 0;
record_length = atoi_n (buf, 5);
if (record_length < 25)
return 0;
indicator_length = atoi_n (buf+10, 1);
identifier_length = atoi_n (buf+11, 1);
base_address = atoi_n (buf+12, 4);
length_data_entry = atoi_n (buf+20, 1);
length_starting = atoi_n (buf+21, 1);
length_implementation = atoi_n (buf+22, 1);
for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
{
entry_p += 3+length_data_entry+length_starting;
if (entry_p >= record_length)
return 0;
}
base_address = entry_p+1;
for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
{
Z_TaggedElement *tag;
int data_length;
int data_offset;
int end_offset;
int i;
char tag_str[4];
memcpy (tag_str, buf+entry_p, 3);
entry_p += 3;
tag_str[3] = '\0';
if ((r->num_elements + 1) >= max_elements)
{
Z_TaggedElement **tmp = r->elements;
/* double array space, throw away old buffer (nibble memory) */
r->elements = odr_malloc(o, sizeof(*r->elements) *
(max_elements *= 2));
memcpy(r->elements, tmp, r->num_elements * sizeof(*tmp));
}
tag = r->elements[r->num_elements++] = odr_malloc (o, sizeof(*tag));
tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
*tag->tagType = 3;
tag->tagOccurrence = 0;
tag->metaData = 0;
tag->appliedVariant = 0;
tag->tagValue = odr_malloc (o, sizeof(*tag->tagValue));
tag->tagValue->which = Z_StringOrNumeric_string;
tag->tagValue->u.string = odr_strdup(o, tag_str);
tag->content = odr_malloc(o, sizeof(*tag->content));
tag->content->which = Z_ElementData_subtree;
tag->content->u.subtree =
odr_malloc (o, sizeof(*tag->content->u.subtree));
tag->content->u.subtree->elements = odr_malloc (o, sizeof(*r->elements));
tag->content->u.subtree->num_elements = 1;
tag = tag->content->u.subtree->elements[0] =
odr_malloc (o, sizeof(**tag->content->u.subtree->elements));
tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
*tag->tagType = 3;
tag->tagOccurrence = 0;
tag->metaData = 0;
tag->appliedVariant = 0;
tag->tagValue = odr_malloc (o, sizeof(*tag->tagValue));
tag->tagValue->which = Z_StringOrNumeric_string;
tag->content = odr_malloc(o, sizeof(*tag->content));
data_length = atoi_n (buf+entry_p, length_data_entry);
entry_p += length_data_entry;
data_offset = atoi_n (buf+entry_p, length_starting);
entry_p += length_starting;
i = data_offset + base_address;
end_offset = i+data_length-1;
if (memcmp (tag_str, "00", 2) && indicator_length)
{
/* indicator */
tag->tagValue->u.string = odr_malloc(o, indicator_length+1);
memcpy (tag->tagValue->u.string, buf + i, indicator_length);
tag->tagValue->u.string[indicator_length] = '\0';
i += indicator_length;
tag->content->which = Z_ElementData_subtree;
tag->content->u.subtree =
odr_malloc (o, sizeof(*tag->content->u.subtree));
tag->content->u.subtree->elements =
odr_malloc (o, 256 * sizeof(*r->elements));
tag->content->u.subtree->num_elements = 0;
while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS
&& i < end_offset)
{
int i0;
/* prepare tag */
Z_TaggedElement *parent_tag = tag;
Z_TaggedElement *tag = odr_malloc (o, sizeof(*tag));
if (parent_tag->content->u.subtree->num_elements < 256)
parent_tag->content->u.subtree->elements[
parent_tag->content->u.subtree->num_elements++] = tag;
tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
*tag->tagType = 3;
tag->tagOccurrence = 0;
tag->metaData = 0;
tag->appliedVariant = 0;
tag->tagValue = odr_malloc (o, sizeof(*tag->tagValue));
tag->tagValue->which = Z_StringOrNumeric_string;
/* sub field */
tag->tagValue->u.string = odr_malloc (o, identifier_length);
memcpy (tag->tagValue->u.string, buf+i+1, identifier_length-1);
tag->tagValue->u.string[identifier_length-1] = '\0';
i += identifier_length;
/* data ... */
tag->content = odr_malloc(o, sizeof(*tag->content));
tag->content->which = Z_ElementData_string;
i0 = i;
while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
buf[i] != ISO2709_FS && i < end_offset)
i++;
tag->content->u.string = odr_malloc (o, i - i0 + 1);
memcpy (tag->content->u.string, buf + i0, i - i0);
tag->content->u.string[i - i0] = '\0';
}
}
else
{
int i0 = i;
tag->tagValue->u.string = "@";
tag->content->which = Z_ElementData_string;
while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS &&
i < end_offset)
i++;
tag->content->u.string = odr_malloc (o, i - i0 +1);
memcpy (tag->content->u.string, buf+i0, i - i0);
tag->content->u.string[i-i0] = '\0';
}
}
return r;
}
static void retval_grs1 (zval *return_value, Z_GenericRecord *p)
{
Z_GenericRecord *grs[20];
int eno[20];
int level = 0;
if (array_init(return_value) == FAILURE)
{
RETURN_FALSE;
}
eno[level] = 0;
grs[level] = p;
while (level >= 0)
{
zval *my_zval;
Z_TaggedElement *e = 0;
Z_GenericRecord *p = grs[level];
int i;
char tag[256];
int taglen = 0;
if (eno[level] >= p->num_elements)
{
--level;
if (level >= 0)
eno[level]++;
continue;
}
/* eno[level]++; */
*tag = '\0';
for (i = 0; i<=level; i++)
{
int tag_type = 3;
e = grs[i]->elements[eno[i]];
if (e->tagType)
tag_type = *e->tagType;
taglen = strlen(tag);
sprintf (tag+taglen, "(%d,", tag_type);
taglen = strlen(tag);
if (e->tagValue->which == Z_StringOrNumeric_string)
{
int len = strlen(e->tagValue->u.string);
memcpy (tag + taglen, e->tagValue->u.string, len);
tag[taglen+len] = '\0';
}
else if (e->tagValue->which == Z_StringOrNumeric_numeric)
{
sprintf (tag + taglen, "%d", *e->tagValue->u.numeric);
}
taglen = strlen(tag);
strcpy (tag + taglen, ")");
}
ALLOC_ZVAL(my_zval);
array_init(my_zval);
INIT_PZVAL(my_zval);
add_next_index_string(my_zval, tag, 1);
switch (e->content->which)
{
case Z_ElementData_string:
add_next_index_string (my_zval, e->content->u.string, 1);
break;
case Z_ElementData_numeric:
add_next_index_long (my_zval, *e->content->u.numeric);
break;
case Z_ElementData_trueOrFalse:
add_next_index_long (my_zval, *e->content->u.trueOrFalse);
break;
case Z_ElementData_subtree:
level++;
grs[level] = e->content->u.subtree;
eno[level] = -1;
default:
}
zend_hash_next_index_insert (return_value->value.ht,
(void *) &my_zval, sizeof(zval *), NULL);
eno[level]++;
}
}
/* {{{ proto string yaz_record(int id, int pos, string type)
Return record information at given result set position */
PHP_FUNCTION(yaz_record)
{
pval **pval_id, **pval_pos, **pval_type;
Yaz_Association p;
int pos;
char *type;
if (ZEND_NUM_ARGS() != 3)
WRONG_PARAM_COUNT;
if (zend_get_parameters_ex(3, &pval_id, &pval_pos, &pval_type) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (pval_id);
convert_to_long_ex(pval_pos);
pos = (*pval_pos)->value.lval;
convert_to_string_ex(pval_type);
type = (*pval_type)->value.str.val;
if (p && p->resultSets && p->resultSets->recordList &&
pos >= p->resultSetStartPoint &&
pos < p->resultSetStartPoint + p->resultSets->recordList->num_records)
{
Z_NamePlusRecord *npr =
p->resultSets->recordList->records[pos - p->resultSetStartPoint];
if (npr->which == Z_NamePlusRecord_databaseRecord)
{
Z_External *r = (Z_External *) npr->u.databaseRecord;
oident *ent = oid_getentbyoid(r->direct_reference);
if (!strcmp (type, "syntax"))
{
if (ent && ent->desc)
RETVAL_STRING(ent->desc, 1);
}
else if (!strcmp (type, "string"))
{
if (r->which == Z_External_sutrs && ent->value == VAL_SUTRS)
{
RETVAL_STRINGL(r->u.sutrs->buf, r->u.sutrs->len, 1);
}
else if (r->which == Z_External_octet)
{
char *buf = (char *) (r->u.octet_aligned->buf);
int len = r->u.octet_aligned->len;
switch (ent->value)
{
case VAL_SOIF:
case VAL_HTML:
break;
case VAL_TEXT_XML:
case VAL_APPLICATION_XML:
break;
default:
}
RETVAL_STRINGL(buf, len, 1);
}
}
else if (!strcmp (type, "array"))
{
if (r->which == Z_External_grs1 && ent->value == VAL_GRS1)
{
retval_grs1 (return_value, r->u.grs1);
}
else if (r->which == Z_External_octet)
{
char *buf = (char *) (r->u.octet_aligned->buf);
ODR odr = odr_createmem (ODR_DECODE);
Z_GenericRecord *rec = 0;
switch (ent->value)
{
case VAL_SOIF:
case VAL_HTML:
break;
case VAL_TEXT_XML:
case VAL_APPLICATION_XML:
/* text2grs1 (&buf, &len, t->odr_in, 0, 0); */
break;
default:
rec = marc_to_grs1 (buf, odr, r->direct_reference);
}
if (rec)
retval_grs1 (return_value, rec);
odr_destroy (odr);
}
}
}
}
}
/* }}} */
/* {{{ proto int yaz_syntax(int id, string syntax)
Set record syntax for retrieval */
PHP_FUNCTION(yaz_syntax)
{
pval **pval_id, **pval_syntax;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 2 ||
zend_get_parameters_ex(2, &pval_id, &pval_syntax) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (pval_id);
if (p)
{
convert_to_string_ex (pval_syntax);
xfree (p->preferredRecordSyntax);
p->preferredRecordSyntax = xstrdup ((*pval_syntax)->value.str.val);
}
}
/* }}} */
/* {{{ proto int yaz_element(int id, string elementsetname)
Set Element-Set-Name for retrieval */
PHP_FUNCTION(yaz_element)
{
pval **pval_id, **pval_element;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 2 ||
zend_get_parameters_ex(2, &pval_id, &pval_element) == FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (pval_id);
if (p)
{
convert_to_string_ex (pval_element);
xfree (p->elementSetNames);
p->elementSetNames = xstrdup ((*pval_element)->value.str.val);
}
}
/* }}} */
/* {{{ proto int yaz_range(int id, int start, int number)
Set result set start point and number of records to request */
PHP_FUNCTION(yaz_range)
{
pval **pval_id, **pval_start, **pval_number;
Yaz_Association p;
if (ZEND_NUM_ARGS() != 3 ||
zend_get_parameters_ex(3, &pval_id, &pval_start, &pval_number) ==
FAILURE)
{
WRONG_PARAM_COUNT;
}
p = get_assoc (pval_id);
if (p)
{
convert_to_long_ex (pval_start);
p->resultSetStartPoint = (*pval_start)->value.lval;
convert_to_long_ex (pval_number);
p->numberOfRecordsRequested = (*pval_number)->value.lval;
}
}
/* }}} */
PHP_MINIT_FUNCTION(yaz)
{
int i;
#if PHP_YAZ_DEBUG
php_log_err ("PHP_MINIT_FUNCTION yaz");
#endif
nmem_init();
order_associations = 1;
shared_associations = xmalloc (sizeof(*shared_associations) * MAX_ASSOC);
for (i = 0; i<MAX_ASSOC; i++)
shared_associations[i] = 0;
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(yaz)
{
int i;
#if PHP_YAZ_DEBUG
php_log_err ("PHP_MSHUTDOWN_FUNCTION yaz");
#endif
if (!shared_associations)
return SUCCESS;
for (i = 0; i<MAX_ASSOC; i++)
yaz_association_destroy (shared_associations[i]);
xfree (shared_associations);
shared_associations = 0;
nmem_exit();
return SUCCESS;
}
PHP_MINFO_FUNCTION(yaz)
{
#if PHP_YAZ_DEBUG
php_log_err ("PHP_MINFO_FUNCTION yaz");
#endif
}
PHP_RSHUTDOWN_FUNCTION(yaz)
{
#if PHP_YAZ_DEBUG
php_log_err ("PHP_RSHUTDOWN yaz");
#endif
return SUCCESS;
}
PHP_RINIT_FUNCTION(yaz)
{
order_associations++;
#if PHP_YAZ_DEBUG
php_log_err ("PHP_RINIT yaz");
#endif
return SUCCESS;
}
zend_module_entry yaz_module_entry = {
"YAZ",
yaz_functions,
PHP_MINIT(yaz),
PHP_MSHUTDOWN(yaz),
PHP_RINIT(yaz),
PHP_RSHUTDOWN(yaz),
PHP_MINFO(yaz),
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_YAZ
ZEND_GET_MODULE(yaz)
#endif
#endif
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/