Committing the raw version into cvs, just to have a backup.
[yaz-moved-to-github.git] / util / yaz-illclient.c
1 /*
2  * Copyright (C) 1995-2006, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: yaz-illclient.c,v 1.1 2007-04-16 15:33:51 heikki Exp $
6  */
7
8 /* NOTE - This is work in progress - not at all ready */
9
10 /** \file yaz-illclient.c
11  *  \brief client for ILL requests (ISO 10161-1)
12  *
13  *  This is a test client for handling ISO 10161-1 ILL requests.
14  *  Those are not directly Z39.50, but the protocol is quite similar
15  *  and yaz already provides the APDUS for it.
16  *
17  *  This is not an interactive client like yaz-client, but driven by command-
18  *  line arguments. Its output is a return code, and possibly some text on 
19  *  stdout.
20  *
21  *  Exit codes  (note, the program exits as soon as it finds a good reason)
22  *     0   ok
23  *     1   errors in arguments
24  *     2   Internal errors in creating objects (comstack, odr...)
25  *         mostly programming errors.
26  *     3   could not connect
27  *     4   could not send request
28  *     5   No reponse received
29  *     6   Error decoding response packet
30  *     7   Server returned an error (see log or stdout)
31  *
32  *
33  *
34  *
35  */
36
37 #include <stdlib.h>
38 #include <stdio.h>
39
40 #if HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #if HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #if HAVE_SYS_TIME_H
47 #include <sys/time.h>
48 #endif
49
50
51 #include <yaz/yaz-util.h>
52 #include <yaz/proto.h>
53 #include <yaz/comstack.h>
54 #include <yaz/tcpip.h>
55 #include <yaz/unix.h>
56 #include <yaz/odr.h>
57 #include <yaz/log.h>
58 #include <yaz/ill.h>
59
60 /* A structure for storing all the arguments */
61 struct prog_args {
62     char *host;
63 } ;
64
65
66 /* Call-back to be called for every field in the ill-request */
67 /* It can set values to any field, perhaps from the prog_args */
68 const char *get_ill_element(void *clientData, const char *element) {
69     struct prog_args *args = clientData;
70     char *ret=0;
71     if (!strcmp(element,"foo")) {
72         ret=args->host;
73     } else if (!strcmp(element,"ill,protocol-version-num")) {
74         ret="2";
75     } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) {
76         ret="IndexData";
77     } 
78     yaz_log(YLOG_DEBUG,"get_ill_element: '%s' -> '%s' ", element, ret );
79     return ret;
80 }
81
82
83 /* * * * * * * * * * * * * * * * * */
84 /** \brief  Parse program arguments */
85 void parseargs( int argc, char * argv[],  struct prog_args *args) {
86     int ret;
87     char *arg;
88     char *prog=*argv;
89     char *version="$Id: yaz-illclient.c,v 1.1 2007-04-16 15:33:51 heikki Exp $"; /* from cvs */
90
91     args->host=0; /* not known (yet) */
92
93     while ((ret = options("k:c:q:a:b:m:v:p:u:t:Vxd:", argv, argc, &arg)) != -2)
94     {
95         switch (ret)
96         {
97         case 0:
98             if (!args->host)
99             {
100                 args->host = xstrdup (arg);
101             }
102             else
103             {
104                 fprintf(stderr, "%s: Specify most one server address\n",
105                         prog);
106                 exit(1);
107             }
108             break;
109         case 'v':
110             yaz_log_init(yaz_log_mask_str(arg), "", 0);
111             break;
112         case 'V':
113             printf("%s %s",prog, version );
114             break;
115         default:
116             fprintf (stderr, "Usage: %s "
117                      " [-v loglevel...]"
118                      " [-V]"
119                      " <server-addr>\n",
120                      prog);
121             exit (1);
122         }
123     }
124 } /* parseargs */
125
126 /* * * * * * * * * * * */
127 /** \brief  Validate the arguments make sense */
128 void validateargs( struct prog_args *args) {
129     if (!args->host) {
130         fprintf(stderr, "Specify a connection address, "
131                         "as in 'bagel.indexdata.dk:210' \n");
132         exit(1);
133     }
134 } /* validateargs */
135
136
137 /* * * * * * * * * * * * * * * */
138 /** \brief  Connect to the target */
139 COMSTACK connect_to( char *hostaddr ){
140     COMSTACK stack;
141     void *server_address_ip;
142     int status;
143
144     yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr);
145     stack = cs_create_host(hostaddr, 1, &server_address_ip );
146     if (!stack) {
147         yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ",
148                  hostaddr );
149         exit(2);
150     }
151     
152     yaz_log(YLOG_DEBUG,"Created stack ok ");
153
154     status = cs_connect(stack, server_address_ip);
155     if (status != 0) {
156         yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ",
157                  hostaddr );
158         exit(3);
159     }
160     yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr);
161     return stack;
162 }
163
164 /* * * * * * * * * * * * * * * */
165 ILL_APDU *createrequest( struct prog_args *args, ODR out_odr) {
166     struct ill_get_ctl ctl;
167     ILL_APDU *apdu;
168     ILL_Request *req;
169
170     ctl.odr = out_odr;
171     ctl.clientData = & args;
172     ctl.f = get_ill_element;
173     apdu = odr_malloc( out_odr, sizeof(*apdu) );
174     apdu->which=ILL_APDU_ILL_Request;
175     req = ill_get_ILLRequest(&ctl, "ill", 0);
176     apdu->u.illRequest=req;
177     if (!req) {
178         yaz_log(YLOG_FATAL,"Could not create ill request");
179         exit(2);
180     }
181
182     return apdu;
183 } /* createrequest */
184
185
186 /* * * * * * * * * * * * * * * */
187 /** \brief Send the request */
188 void sendrequest(ILL_APDU *apdu, ODR out_odr, COMSTACK stack ) {
189     char *buf_out;
190     int len_out;
191     int res;
192     if (!ill_APDU  (out_odr, &apdu, 0, 0)) { 
193         yaz_log(YLOG_FATAL,"ill_Apdu failed");
194         exit(2);
195     }
196     buf_out = odr_getbuf(out_odr, &len_out, 0);
197     if (0) {
198         yaz_log(YLOG_DEBUG,"Request PDU Dump");
199         odr_dumpBER(yaz_log_file(), buf_out, len_out);
200     }
201     if (!buf_out) {
202         yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out);
203         odr_perror(out_odr, "encoding failed");
204         exit(2);
205     }
206     yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out);
207     res = cs_put(stack, buf_out, len_out);
208     if ( res<0 ) {
209         yaz_log(YLOG_FATAL,"Could not send packet. code %d",res );
210         exit (4);
211     }
212 } /* sendrequest */
213
214 /* * * * * * * * * * * * * * * */
215 /** \brief  Get a response */
216 ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){
217     ILL_APDU *resp;
218     int res;
219     char *buf_in=0;
220     int len_in=0;
221     yaz_log(YLOG_DEBUG,"About to wait for a response");
222     res = cs_get(stack, &buf_in, &len_in);
223     yaz_log(YLOG_DEBUG,"Got a response of %d bytes at %x. res=%d", len_in,buf_in, res);
224     if (res<0) {
225         yaz_log(YLOG_FATAL,"Could not receive packet. code %d",res );
226         yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", 
227                 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
228                 buf_in[4], buf_in[5], buf_in[6], buf_in[7]  );
229         yaz_log(YLOG_DEBUG,"PDU Dump:");
230         odr_dumpBER(yaz_log_file(), buf_in, len_in);
231         exit (5);
232     }
233     odr_setbuf(in_odr, buf_in, res, 0);
234     if (!ill_APDU (in_odr, &resp, 0, 0))
235     {
236         int x;
237         int err = odr_geterrorx(in_odr, &x);
238         char msg[60];
239         const char *element = odr_getelement(in_odr);
240         sprintf(msg, "ODR code %d:%d element=%-20s",
241                 err, x, element ? element : "<unknown>");
242         yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
243         yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", 
244                 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
245                 buf_in[4], buf_in[5], buf_in[6], buf_in[7]  );
246         yaz_log(YLOG_DEBUG,"PDU Dump:");
247         odr_dumpBER(yaz_log_file(), buf_in, len_in);
248         yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
249         exit(6);
250     }
251     return resp;
252 } /* getresponse */
253
254
255 /** \brief Dump a apdu */
256 void dumpapdu( ILL_APDU *apdu) {
257     ODR print_odr = odr_createmem(ODR_PRINT);
258     ill_APDU (print_odr, &apdu, 0, 0);
259     odr_destroy(print_odr);
260 } /* dumpapdu */
261
262 /** \brief  Check apdu type and extract the status_or_error */
263 ILL_Status_Or_Error_Report *getstaterr( ILL_APDU *resp, ODR in_odr ) {
264     if (resp->which != ILL_APDU_Status_Or_Error_Report ) {
265         const char *element = odr_getelement(in_odr);
266         if (!element) 
267             element="unknown";
268         printf("Server returned wrong packet type: %d\n", resp->which);
269         yaz_log(YLOG_FATAL,"Server returned a (%d) and "
270                  "not a 'Status_Or_Error_Report' (%d) ",
271                  resp->which, ILL_APDU_Status_Or_Error_Report);
272         exit(6);
273     }
274     return resp->u.Status_Or_Error_Report;
275 } /* getstaterr */
276
277 /** \brief  Return a printable string from an ILL_String */
278 char *getillstring( ILL_String *s) {
279     if (s->which == ILL_String_GeneralString ) 
280         return s->u.GeneralString;
281     else if (s->which == ILL_String_EDIFACTString ) 
282         return s->u.EDIFACTString;
283     else {
284         yaz_log(YLOG_FATAL,"Invalid ILL_String ");
285         exit (6);
286     }
287 } /* getillstring */
288
289 /** \brief Check if the status was an error packet */
290 /* The presence of an error_report indicates it was an error */
291 /* Then the problem is to find the right message. We dig around */
292 /* until we find the first message, print that, and exit the program */
293 void checkerr( ILL_Status_Or_Error_Report *staterr ) {
294     yaz_log(YLOG_DEBUG, "err= %x ",staterr->error_report );
295     if (staterr->error_report) {
296         ILL_Error_Report *err= staterr->error_report;
297         if ( err->user_error_report) {
298             ILL_User_Error_Report *uerr= err->user_error_report;
299             switch( uerr->which ) {
300                 case ILL_User_Error_Report_already_forwarded:
301                     printf("Already forwarded: \n");
302                     break;
303                 case ILL_User_Error_Report_intermediary_problem:
304                     printf("Intermediary problem: %d\n", 
305                         uerr->u.intermediary_problem);
306                     break;
307                 case ILL_User_Error_Report_security_problem:
308                     printf("Security problem: %s\n", 
309                         getillstring(uerr->u.security_problem));
310                     break;
311                 case ILL_User_Error_Report_unable_to_perform:
312                     printf("Unable to perform: %d\n", 
313                           uerr->u.unable_to_perform);
314                     break;
315                 default:
316                     printf("Unknown problem");
317             }
318             exit(7);
319         }
320         if ( err->provider_error_report) {
321             ILL_Provider_Error_Report *perr= err->provider_error_report;
322             switch( perr->which ) {
323                 case ILL_Provider_Error_Report_general_problem:
324                     printf("General Problem: %d\n", 
325                           perr->u.general_problem);
326                     break;
327                 case ILL_Provider_Error_Report_transaction_id_problem:
328                     printf("Transaction Id Problem: %d\n", 
329                           perr->u.general_problem);
330                     break;
331                 case ILL_Provider_Error_Report_state_transition_prohibited:
332                     printf("State Transition prohibited \n");
333                     break;
334             }
335             exit(7);
336         } 
337         /* fallbacks */
338         if ( staterr->note ) 
339             printf("%s", getillstring(staterr->note));
340         else 
341             printf("Unknown error type");
342         exit(7);
343     }
344 } /* checkerr */
345
346
347
348 /* * * * * * * * * * * * * * * */
349
350 /** \brief Main program 
351  *
352  * Parse arguments
353  * Validate arguments
354  * Establish connection
355  * Build a request
356  * Send a request
357  * Get a reply
358  * Parse reply
359  * Produce output
360  */
361
362 int main (int argc, char * argv[]) {
363     struct prog_args args;
364     COMSTACK stack;
365     ODR out_odr = odr_createmem(ODR_ENCODE);
366     ODR in_odr = odr_createmem(ODR_DECODE);
367     ILL_APDU *apdu;
368     ILL_APDU *resp;
369     ILL_Status_Or_Error_Report *staterr;
370
371     parseargs( argc, argv,  &args);
372     validateargs(&args);
373     stack = connect_to(args.host);
374     apdu = createrequest(&args, out_odr);
375     sendrequest(apdu, out_odr, stack ); 
376     resp = getresponse(stack, in_odr );
377     if (1) 
378         dumpapdu(resp);
379     staterr=getstaterr(resp, in_odr);
380     checkerr(staterr);
381
382
383     printf ("Ok\n"); /* while debugging */
384     exit (0);
385 }
386
387 /*
388  * Local variables:
389  * c-basic-offset: 4
390  * indent-tabs-mode: nil
391  * End:
392  * vim: shiftwidth=4 tabstop=8 expandtab
393  */
394