From 4ef54cf7f0c56ebb62f1a4c9bfecc50feca18656 Mon Sep 17 00:00:00 2001 From: Heikki Levanto Date: Mon, 16 Apr 2007 15:33:51 +0000 Subject: [PATCH] Committing the raw version into cvs, just to have a backup. It can send an empty request, and parse incoming error messages. Filling actual data to the request still missing, as is extracting useful responses (if any). --- util/Makefile.am | 7 +- util/yaz-illclient.c | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 util/yaz-illclient.c diff --git a/util/Makefile.am b/util/Makefile.am index 8f8e511..0ea546c 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -1,6 +1,6 @@ ## Copyright (C) 1995-2007, Index Data ## All rights reserved. -## $Id: Makefile.am,v 1.30 2007-01-03 08:42:16 adam Exp $ +## $Id: Makefile.am,v 1.31 2007-04-16 15:33:51 heikki Exp $ bin_SCRIPTS = yaz-asncomp yaz-config @@ -11,7 +11,7 @@ DISTCLEANFILES = yaz-config AM_CPPFLAGS=-I$(top_srcdir)/include $(XML2_CFLAGS) bin_PROGRAMS = yaz-marcdump yaz-iconv -noinst_PROGRAMS = cclsh cql2pqf cql2xcql srwtst yaz-benchmark yaz-xmlquery +noinst_PROGRAMS = cclsh cql2pqf cql2xcql srwtst yaz-benchmark yaz-xmlquery yaz-illclient # MARC dumper utility yaz_marcdump_SOURCES = marcdump.c @@ -38,3 +38,6 @@ yaz_benchmark_LDADD = ../src/libyaz.la yaz_xmlquery_SOURCES = yaz-xmlquery.c yaz_xmlquery_LDADD = ../src/libyaz.la + +yaz_illclient_SOURCES = yaz-illclient.c +yaz_illclient_LDADD = ../src/libyaz.la $(READLINE_LIBS) diff --git a/util/yaz-illclient.c b/util/yaz-illclient.c new file mode 100644 index 0000000..3606e82 --- /dev/null +++ b/util/yaz-illclient.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 1995-2006, Index Data ApS + * See the file LICENSE for details. + * + * $Id: yaz-illclient.c,v 1.1 2007-04-16 15:33:51 heikki Exp $ + */ + +/* NOTE - This is work in progress - not at all ready */ + +/** \file yaz-illclient.c + * \brief client for ILL requests (ISO 10161-1) + * + * This is a test client for handling ISO 10161-1 ILL requests. + * Those are not directly Z39.50, but the protocol is quite similar + * and yaz already provides the APDUS for it. + * + * This is not an interactive client like yaz-client, but driven by command- + * line arguments. Its output is a return code, and possibly some text on + * stdout. + * + * Exit codes (note, the program exits as soon as it finds a good reason) + * 0 ok + * 1 errors in arguments + * 2 Internal errors in creating objects (comstack, odr...) + * mostly programming errors. + * 3 could not connect + * 4 could not send request + * 5 No reponse received + * 6 Error decoding response packet + * 7 Server returned an error (see log or stdout) + * + * + * + * + */ + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TIME_H +#include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include + +/* A structure for storing all the arguments */ +struct prog_args { + char *host; +} ; + + +/* Call-back to be called for every field in the ill-request */ +/* It can set values to any field, perhaps from the prog_args */ +const char *get_ill_element(void *clientData, const char *element) { + struct prog_args *args = clientData; + char *ret=0; + if (!strcmp(element,"foo")) { + ret=args->host; + } else if (!strcmp(element,"ill,protocol-version-num")) { + ret="2"; + } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) { + ret="IndexData"; + } + yaz_log(YLOG_DEBUG,"get_ill_element: '%s' -> '%s' ", element, ret ); + return ret; +} + + +/* * * * * * * * * * * * * * * * * */ +/** \brief Parse program arguments */ +void parseargs( int argc, char * argv[], struct prog_args *args) { + int ret; + char *arg; + char *prog=*argv; + char *version="$Id: yaz-illclient.c,v 1.1 2007-04-16 15:33:51 heikki Exp $"; /* from cvs */ + + args->host=0; /* not known (yet) */ + + while ((ret = options("k:c:q:a:b:m:v:p:u:t:Vxd:", argv, argc, &arg)) != -2) + { + switch (ret) + { + case 0: + if (!args->host) + { + args->host = xstrdup (arg); + } + else + { + fprintf(stderr, "%s: Specify most one server address\n", + prog); + exit(1); + } + break; + case 'v': + yaz_log_init(yaz_log_mask_str(arg), "", 0); + break; + case 'V': + printf("%s %s",prog, version ); + break; + default: + fprintf (stderr, "Usage: %s " + " [-v loglevel...]" + " [-V]" + " \n", + prog); + exit (1); + } + } +} /* parseargs */ + +/* * * * * * * * * * * */ +/** \brief Validate the arguments make sense */ +void validateargs( struct prog_args *args) { + if (!args->host) { + fprintf(stderr, "Specify a connection address, " + "as in 'bagel.indexdata.dk:210' \n"); + exit(1); + } +} /* validateargs */ + + +/* * * * * * * * * * * * * * * */ +/** \brief Connect to the target */ +COMSTACK connect_to( char *hostaddr ){ + COMSTACK stack; + void *server_address_ip; + int status; + + yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr); + stack = cs_create_host(hostaddr, 1, &server_address_ip ); + if (!stack) { + yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ", + hostaddr ); + exit(2); + } + + yaz_log(YLOG_DEBUG,"Created stack ok "); + + status = cs_connect(stack, server_address_ip); + if (status != 0) { + yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ", + hostaddr ); + exit(3); + } + yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr); + return stack; +} + +/* * * * * * * * * * * * * * * */ +ILL_APDU *createrequest( struct prog_args *args, ODR out_odr) { + struct ill_get_ctl ctl; + ILL_APDU *apdu; + ILL_Request *req; + + ctl.odr = out_odr; + ctl.clientData = & args; + ctl.f = get_ill_element; + apdu = odr_malloc( out_odr, sizeof(*apdu) ); + apdu->which=ILL_APDU_ILL_Request; + req = ill_get_ILLRequest(&ctl, "ill", 0); + apdu->u.illRequest=req; + if (!req) { + yaz_log(YLOG_FATAL,"Could not create ill request"); + exit(2); + } + + return apdu; +} /* createrequest */ + + +/* * * * * * * * * * * * * * * */ +/** \brief Send the request */ +void sendrequest(ILL_APDU *apdu, ODR out_odr, COMSTACK stack ) { + char *buf_out; + int len_out; + int res; + if (!ill_APDU (out_odr, &apdu, 0, 0)) { + yaz_log(YLOG_FATAL,"ill_Apdu failed"); + exit(2); + } + buf_out = odr_getbuf(out_odr, &len_out, 0); + if (0) { + yaz_log(YLOG_DEBUG,"Request PDU Dump"); + odr_dumpBER(yaz_log_file(), buf_out, len_out); + } + if (!buf_out) { + yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out); + odr_perror(out_odr, "encoding failed"); + exit(2); + } + yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out); + res = cs_put(stack, buf_out, len_out); + if ( res<0 ) { + yaz_log(YLOG_FATAL,"Could not send packet. code %d",res ); + exit (4); + } +} /* sendrequest */ + +/* * * * * * * * * * * * * * * */ +/** \brief Get a response */ +ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){ + ILL_APDU *resp; + int res; + char *buf_in=0; + int len_in=0; + yaz_log(YLOG_DEBUG,"About to wait for a response"); + res = cs_get(stack, &buf_in, &len_in); + yaz_log(YLOG_DEBUG,"Got a response of %d bytes at %x. res=%d", len_in,buf_in, res); + if (res<0) { + yaz_log(YLOG_FATAL,"Could not receive packet. code %d",res ); + yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", + buf_in[0], buf_in[1], buf_in[2], buf_in[3], + buf_in[4], buf_in[5], buf_in[6], buf_in[7] ); + yaz_log(YLOG_DEBUG,"PDU Dump:"); + odr_dumpBER(yaz_log_file(), buf_in, len_in); + exit (5); + } + odr_setbuf(in_odr, buf_in, res, 0); + if (!ill_APDU (in_odr, &resp, 0, 0)) + { + int x; + int err = odr_geterrorx(in_odr, &x); + char msg[60]; + const char *element = odr_getelement(in_odr); + sprintf(msg, "ODR code %d:%d element=%-20s", + err, x, element ? element : ""); + yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg); + yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", + buf_in[0], buf_in[1], buf_in[2], buf_in[3], + buf_in[4], buf_in[5], buf_in[6], buf_in[7] ); + yaz_log(YLOG_DEBUG,"PDU Dump:"); + odr_dumpBER(yaz_log_file(), buf_in, len_in); + yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg); + exit(6); + } + return resp; +} /* getresponse */ + + +/** \brief Dump a apdu */ +void dumpapdu( ILL_APDU *apdu) { + ODR print_odr = odr_createmem(ODR_PRINT); + ill_APDU (print_odr, &apdu, 0, 0); + odr_destroy(print_odr); +} /* dumpapdu */ + +/** \brief Check apdu type and extract the status_or_error */ +ILL_Status_Or_Error_Report *getstaterr( ILL_APDU *resp, ODR in_odr ) { + if (resp->which != ILL_APDU_Status_Or_Error_Report ) { + const char *element = odr_getelement(in_odr); + if (!element) + element="unknown"; + printf("Server returned wrong packet type: %d\n", resp->which); + yaz_log(YLOG_FATAL,"Server returned a (%d) and " + "not a 'Status_Or_Error_Report' (%d) ", + resp->which, ILL_APDU_Status_Or_Error_Report); + exit(6); + } + return resp->u.Status_Or_Error_Report; +} /* getstaterr */ + +/** \brief Return a printable string from an ILL_String */ +char *getillstring( ILL_String *s) { + if (s->which == ILL_String_GeneralString ) + return s->u.GeneralString; + else if (s->which == ILL_String_EDIFACTString ) + return s->u.EDIFACTString; + else { + yaz_log(YLOG_FATAL,"Invalid ILL_String "); + exit (6); + } +} /* getillstring */ + +/** \brief Check if the status was an error packet */ +/* The presence of an error_report indicates it was an error */ +/* Then the problem is to find the right message. We dig around */ +/* until we find the first message, print that, and exit the program */ +void checkerr( ILL_Status_Or_Error_Report *staterr ) { + yaz_log(YLOG_DEBUG, "err= %x ",staterr->error_report ); + if (staterr->error_report) { + ILL_Error_Report *err= staterr->error_report; + if ( err->user_error_report) { + ILL_User_Error_Report *uerr= err->user_error_report; + switch( uerr->which ) { + case ILL_User_Error_Report_already_forwarded: + printf("Already forwarded: \n"); + break; + case ILL_User_Error_Report_intermediary_problem: + printf("Intermediary problem: %d\n", + uerr->u.intermediary_problem); + break; + case ILL_User_Error_Report_security_problem: + printf("Security problem: %s\n", + getillstring(uerr->u.security_problem)); + break; + case ILL_User_Error_Report_unable_to_perform: + printf("Unable to perform: %d\n", + uerr->u.unable_to_perform); + break; + default: + printf("Unknown problem"); + } + exit(7); + } + if ( err->provider_error_report) { + ILL_Provider_Error_Report *perr= err->provider_error_report; + switch( perr->which ) { + case ILL_Provider_Error_Report_general_problem: + printf("General Problem: %d\n", + perr->u.general_problem); + break; + case ILL_Provider_Error_Report_transaction_id_problem: + printf("Transaction Id Problem: %d\n", + perr->u.general_problem); + break; + case ILL_Provider_Error_Report_state_transition_prohibited: + printf("State Transition prohibited \n"); + break; + } + exit(7); + } + /* fallbacks */ + if ( staterr->note ) + printf("%s", getillstring(staterr->note)); + else + printf("Unknown error type"); + exit(7); + } +} /* checkerr */ + + + +/* * * * * * * * * * * * * * * */ + +/** \brief Main program + * + * Parse arguments + * Validate arguments + * Establish connection + * Build a request + * Send a request + * Get a reply + * Parse reply + * Produce output + */ + +int main (int argc, char * argv[]) { + struct prog_args args; + COMSTACK stack; + ODR out_odr = odr_createmem(ODR_ENCODE); + ODR in_odr = odr_createmem(ODR_DECODE); + ILL_APDU *apdu; + ILL_APDU *resp; + ILL_Status_Or_Error_Report *staterr; + + parseargs( argc, argv, &args); + validateargs(&args); + stack = connect_to(args.host); + apdu = createrequest(&args, out_odr); + sendrequest(apdu, out_odr, stack ); + resp = getresponse(stack, in_odr ); + if (1) + dumpapdu(resp); + staterr=getstaterr(resp, in_odr); + checkerr(staterr); + + + printf ("Ok\n"); /* while debugging */ + exit (0); +} + +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ + -- 1.7.10.4