Committing the raw version into cvs, just to have a backup.
authorHeikki Levanto <heikki@indexdata.dk>
Mon, 16 Apr 2007 15:33:51 +0000 (15:33 +0000)
committerHeikki Levanto <heikki@indexdata.dk>
Mon, 16 Apr 2007 15:33:51 +0000 (15:33 +0000)
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
util/yaz-illclient.c [new file with mode: 0644]

index 8f8e511..0ea546c 100644 (file)
@@ -1,6 +1,6 @@
 ## Copyright (C) 1995-2007, Index Data
 ## All rights reserved.
 ## 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
 
 
 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
 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
 
 # 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_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 (file)
index 0000000..3606e82
--- /dev/null
@@ -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 <stdlib.h>
+#include <stdio.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+
+#include <yaz/yaz-util.h>
+#include <yaz/proto.h>
+#include <yaz/comstack.h>
+#include <yaz/tcpip.h>
+#include <yaz/unix.h>
+#include <yaz/odr.h>
+#include <yaz/log.h>
+#include <yaz/ill.h>
+
+/* 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]"
+                     " <server-addr>\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 : "<unknown>");
+        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
+ */
+