Command-line params and params from a param file now go straight into the
[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.5 2007-04-25 16:51:47 heikki Exp $
6  */
7
8 /* WARNING - 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 #include <yaz/oclc-ill-req-ext.h>
60
61
62 /* A structure for holding name-value pairs in a linked list */
63 struct nameval {
64     char *name;
65     char *val;
66     struct nameval *next;
67 };
68
69 /* A structure for storing all the arguments */
70 struct prog_args {
71     char *host;
72     char *auth_userid;
73     char *auth_passwd;
74     struct nameval* namevals; /* circular list, points to last */
75 } ;
76
77
78
79 /* Call-back to be called for every field in the ill-request */
80 /* It can set values to any field, perhaps from the prog_args */
81 const char *get_ill_element(void *clientData, const char *element) {
82     struct prog_args *args = clientData;
83     struct nameval *nv=args->namevals;
84     char *ret=0;
85     if (!nv)
86         return "";
87     do  {
88         nv=nv->next;
89         /* printf("comparing '%s' with '%s' \n",element, nv->name ); */
90         if ( strcmp(element, nv->name) == 0 )
91             ret = nv->val;
92     } while ( ( !ret) && ( nv != args->namevals) );
93 #if 0
94     if (!strcmp(element,"foo")) {
95         ret=args->host;
96     } else if (!strcmp(element,"ill,protocol-version-num")) {
97         ret="2";
98     } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) {
99         ret=args->auth_userid; 
100     } else if (!strcmp(element,"ill,requester-id,person-or-institution-symbol,institution")) {
101         ret=args->auth_userid;
102     } else if (!strcmp(element,"ill,responder-id,person-or-institution-symbol,institution")) {
103         ret=args->auth_userid;
104     } else if (!strcmp(element,"ill,ill-service-type")) {
105         ret="1"; /* Loan */
106     } 
107 #endif
108  /*
109     } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) {
110         ret="IndexData";
111  */
112     yaz_log(YLOG_DEBUG,"get_ill_element:'%s'->'%s'", element, ret );
113     return ret;
114 }
115
116
117 /* * * * * * * * * * * * * * * * * */
118
119 /** \brief parse a parameter string */
120 /* string is like 'name=value' */
121 struct nameval *parse_nameval( char *arg ) {
122     struct nameval *nv = xmalloc(sizeof(*nv));
123     char *p=arg;
124     int len;
125     if (!p || !*p) 
126         return 0; /* yeah, leaks a bit of memory. so what */
127     while ( *p && ( *p != '=' ) ) 
128         p++;
129     len = p - arg;
130     if (!len)
131         return 0;
132     nv->name = xmalloc(len+1);
133     strncpy(nv->name, arg, len);
134     nv->name[len]='\0';
135     if (*p == '=' )
136         p++; /* skip the '=' */
137     else
138         return 0; /* oops, no '=' */
139     if (!*p)
140         return 0; /* no value */
141     nv->val=xstrdup(p);
142     nv->next=0;
143     yaz_log(YLOG_DEBUG,"parse_nameval: n='%s' v='%s'", nv->name, nv->val );
144     return nv;
145 }
146
147 /** \brief append nv to the list of namevals */
148 void append_nameval (struct prog_args *args, struct nameval *nv) {
149     if (!nv)
150         return;
151     if (args->namevals) {
152         nv->next=args->namevals->next; /* first */
153         args->namevals->next=nv; 
154         args->namevals=nv;
155     } else {
156         nv->next=nv;
157         args->namevals=nv;
158     }
159 } /* append_nameval */
160
161 /** \brief parse a parameter file */
162 void parse_paramfile(char *arg, struct prog_args *args) {
163     FILE *f=fopen(arg,"r");
164 #define BUFSIZE 4096    
165     char buf[BUFSIZE];
166     int len;
167     struct nameval *nv;
168     if (!f) {
169         yaz_log(YLOG_FATAL,"Could not open param file '%s' ", arg);
170         printf("Could not open file '%s' \n",arg);
171         exit(1);
172     }
173     yaz_log(YLOG_DEBUG,"Opened input file '%s' ",arg );
174     while (fgets(buf,BUFSIZE,f)) {
175         if (buf[0] != '#' ) {
176             len=strlen(buf)-1;
177             if (buf[len] == '\n')
178                 buf[len] = '\0' ;
179             nv=parse_nameval(buf);
180             append_nameval(args, nv);
181         } /* not a comment */
182     }
183     (void) fclose(f);
184
185     if (0) {
186         nv=args->namevals;
187         printf("nv chain: ================ \n");
188         printf("(last:) %p: '%s' = '%s' (%p) \n",nv, nv->name, nv->val, nv->next );
189         do {
190             nv=nv->next;
191             printf("%p: '%s' = '%s' (%p)\n",nv, nv->name, nv->val, nv->next );
192         } while (nv != args->namevals );
193         exit(1);
194     }
195
196 } /* parse_paramfile */
197
198
199 /** \brief  Parse program arguments */
200 void parseargs( int argc, char * argv[],  struct prog_args *args) {
201     int ret;
202     char *arg;
203     char *prog=*argv;
204     char *version="$Id: yaz-illclient.c,v 1.5 2007-04-25 16:51:47 heikki Exp $"; /* from cvs */
205     struct nameval *nv;
206
207     /* default values */
208     args->host = 0; /* not known (yet) */
209     args->namevals=0; /* none set up */
210 #if 0    
211     /* Example 1, from TSLAC */
212     args->auth_userid = "100-228-301" ; /* FIXME - get from cmd line */
213     args->auth_passwd = "dxg5magxc" ;   /* FIXME - get from cmd line */
214     /* Example 2, from TSLAC */
215     args->auth_userid = "100070049" ; /* FIXME - get from cmd line */
216     args->auth_passwd = "cowgirl" ;   /* FIXME - get from cmd line */
217 #else
218     /* Example 3 - directly from OCLC, supposed to work on their test server*/
219     args->auth_userid = "100-310-658" ; /* FIXME - get from cmd line */
220     args->auth_passwd = "apii2test" ;   /* FIXME - get from cmd line */
221 #endif
222
223     while ((ret = options("v:D:f:V", argv, argc, &arg)) != -2)
224     {
225         switch (ret)
226         {
227         case 0:
228             if (!args->host)
229             {
230                 args->host = xstrdup (arg);
231             }
232             else
233             {
234                 fprintf(stderr, "%s: Specify at most one server address\n",
235                         prog);
236                 exit(1);
237             }
238             break;
239         case 'v':
240             yaz_log_init(yaz_log_mask_str(arg), "", 0);
241             break;
242         case 'V':
243             printf("%s %s",prog, version );
244             break;
245         case 'D':
246             nv=parse_nameval(arg);
247             append_nameval(args,nv);
248             break;
249         case 'f':
250             parse_paramfile(arg,args);
251             break;
252         default:
253             fprintf (stderr, "Usage: %s "
254                      " [-v loglevel...]"
255                      " [-D name=value ]"
256                      " [-V]"
257                      " <server-addr>\n",
258                      prog);
259             exit (1);
260         }
261     }
262 } /* parseargs */
263
264 /* * * * * * * * * * * */
265 /** \brief  Validate the arguments make sense */
266 void validateargs( struct prog_args *args) {
267     if (!args->host) {
268         fprintf(stderr, "Specify a connection address, "
269                         "as in 'bagel.indexdata.dk:210' \n");
270         exit(1);
271     }
272 } /* validateargs */
273
274
275 /* * * * * * * * * * * * * * * */
276 /** \brief  Connect to the target */
277 COMSTACK connect_to( char *hostaddr ){
278     COMSTACK stack;
279     void *server_address_ip;
280     int status;
281
282     yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr);
283     stack = cs_create_host(hostaddr, 1, &server_address_ip );
284     if (!stack) {
285         yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ",
286                  hostaddr );
287         exit(2);
288     }
289     
290     yaz_log(YLOG_DEBUG,"Created stack ok ");
291
292     status = cs_connect(stack, server_address_ip);
293     if (status != 0) {
294         yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ",
295                  hostaddr );
296         exit(3);
297     }
298     yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr);
299     return stack;
300 }
301
302
303 /* * * * * * * * * * * * * * * */
304 /* Makes a Z39.50-like prompt package with username and password */
305 Z_PromptObject1 *makeprompt(struct prog_args *args, ODR odr) {
306     Z_PromptObject1 *p = odr_malloc(odr, sizeof(*p) );
307     Z_ResponseUnit1 *ru = odr_malloc(odr, sizeof(*ru) );
308     
309     p->which=Z_PromptObject1_response;
310     p->u.response = odr_malloc(odr, sizeof(*(p->u.response)) );
311     p->u.response->num=2;
312     p->u.response->elements=odr_malloc(odr, 
313              p->u.response->num*sizeof(*(p->u.response->elements)) );
314     /* user id, aka "oclc authorization number" */
315     p->u.response->elements[0] = ru;
316     ru->promptId = odr_malloc(odr, sizeof(*(ru->promptId) ));
317     ru->promptId->which = Z_PromptId_enumeratedPrompt;
318     ru->promptId->u.enumeratedPrompt =  
319         odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
320     ru->promptId->u.enumeratedPrompt->type = 
321          odr_intdup(odr,Z_PromptIdEnumeratedPrompt_userId);
322     ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
323     ru->which = Z_ResponseUnit1_string ;
324     ru->u.string = odr_strdup(odr, args->auth_userid);
325     /* password */
326     ru = odr_malloc(odr, sizeof(*ru) );
327     p->u.response->elements[1] = ru;
328     ru->promptId = odr_malloc(odr, sizeof(*(ru->promptId) ));
329     ru->promptId->which = Z_PromptId_enumeratedPrompt;
330     ru->promptId->u.enumeratedPrompt =  
331         odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
332     ru->promptId->u.enumeratedPrompt->type = 
333          odr_intdup(odr,Z_PromptIdEnumeratedPrompt_password);
334     ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
335     ru->which = Z_ResponseUnit1_string ;
336     ru->u.string = odr_strdup(odr, args->auth_passwd);
337     return p;
338 } /* makeprompt */
339
340 ILL_Extension *makepromptextension(struct prog_args *args, ODR odr) {
341     ODR odr_ext = odr_createmem(ODR_ENCODE);
342     ODR odr_prt = odr_createmem(ODR_PRINT);
343     ILL_Extension *e = odr_malloc(odr, sizeof(*e));
344     Z_PromptObject1 *p = makeprompt(args,odr_ext);
345     char * buf;
346     int siz;
347     Z_External *ext = odr_malloc(odr, sizeof(*ext));
348     ext->direct_reference = odr_getoidbystr(odr,"1.2.840.10003.8.1");
349     ext->indirect_reference=0;
350     ext->descriptor=0;
351     ext->which=Z_External_single;
352     if ( ! z_PromptObject1(odr_ext, &p, 0,0 ) ) {
353         yaz_log(YLOG_FATAL,"Encoding of z_PromptObject1 failed ");
354         exit (6);
355     }
356     
357     printf ("Prompt: \n"); /*!*/
358     z_PromptObject1(odr_prt, &p, 0,0 ); /*!*/
359
360     buf= odr_getbuf(odr_ext,&siz,0);
361     ext->u.single_ASN1_type=odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
362     ext->u.single_ASN1_type->buf= odr_malloc(odr, siz);
363     memcpy(ext->u.single_ASN1_type->buf,buf, siz );
364     ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
365     odr_reset(odr_ext);
366     odr_reset(odr_prt); /*!*/
367
368     e->identifier = odr_intdup(odr,1);
369     e->critical = odr_intdup(odr,0);
370     e->item=odr_malloc(odr,sizeof(*e->item));
371     if ( ! z_External(odr_ext, &ext,0,0) ) {
372         yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
373         exit (6);
374     }
375     printf("External: \n");
376     z_External(odr_prt, &ext,0,0);  /*!*/
377     buf= odr_getbuf(odr_ext,&siz,0); 
378     e->item->buf= odr_malloc(odr, siz);
379     memcpy(e->item->buf,buf, siz );
380     e->item->len = e->item->size = siz;
381
382     odr_destroy(odr_prt);
383     odr_destroy(odr_ext);
384     return e;
385 } /* makepromptextension */
386
387 ILL_Extension *makeoclcextension(struct prog_args *args, ODR odr) {
388     /* The oclc extension is required, but only contains optional */
389     /* fields. Here we just null them all out */
390     ODR odr_ext = odr_createmem(ODR_ENCODE);
391     ODR odr_prt = odr_createmem(ODR_PRINT);
392     ILL_Extension *e = odr_malloc(odr, sizeof(*e));
393     ILL_OCLCILLRequestExtension *oc = odr_malloc(odr_ext, sizeof(*oc));
394     char * buf;
395     int siz;
396     Z_External *ext = odr_malloc(odr, sizeof(*ext));
397     oc->clientDepartment = 0;
398     oc->paymentMethod = 0;
399     oc->uniformTitle = 0;
400     oc->dissertation = 0;
401     oc->issueNumber = 0;
402     oc->volume = 0;
403     oc->affiliations = 0;
404     oc->source = 0;
405     ext->direct_reference = odr_getoidbystr(odr,"1.0.10161.13.2");
406     ext->indirect_reference=0;
407     ext->descriptor=0;
408     ext->which=Z_External_single;
409     if ( ! ill_OCLCILLRequestExtension(odr_ext, &oc, 0,0 ) ) {
410         yaz_log(YLOG_FATAL,"Encoding of ill_OCLCILLRequestExtension failed ");
411         exit (6);
412     }
413     
414     printf ("OCLC: \n"); /*!*/
415     ill_OCLCILLRequestExtension(odr_prt, &oc, 0,0 ); /*!*/
416
417     buf= odr_getbuf(odr_ext,&siz,0);
418     ext->u.single_ASN1_type=odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
419     ext->u.single_ASN1_type->buf= odr_malloc(odr, siz);
420     memcpy(ext->u.single_ASN1_type->buf,buf, siz );
421     ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
422     odr_reset(odr_ext);
423     odr_reset(odr_prt); /*!*/
424
425     e->identifier = odr_intdup(odr,1);
426     e->critical = odr_intdup(odr,0);
427     e->item=odr_malloc(odr,sizeof(*e->item));
428     if ( ! z_External(odr_ext, &ext,0,0) ) {
429         yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
430         exit (6);
431     }
432     printf("External: \n");
433     z_External(odr_prt, &ext,0,0);  /*!*/
434     buf= odr_getbuf(odr_ext,&siz,0); 
435     e->item->buf= odr_malloc(odr, siz);
436     memcpy(e->item->buf,buf, siz );
437     e->item->len = e->item->size = siz;
438
439     odr_destroy(odr_prt);
440     odr_destroy(odr_ext);
441     return e;
442
443 } /* makeoclcextension */
444
445 ILL_APDU *createrequest( struct prog_args *args, ODR odr) {
446     struct ill_get_ctl ctl;
447     ILL_APDU *apdu;
448     ILL_Request *req;
449
450     ctl.odr = odr;
451     ctl.clientData = args;
452     ctl.f = get_ill_element;
453     apdu = odr_malloc( odr, sizeof(*apdu) );
454     apdu->which=ILL_APDU_ILL_Request;
455     req = ill_get_ILLRequest(&ctl, "ill", 0);
456     apdu->u.illRequest=req;
457     req->num_iLL_request_extensions=2;
458     req->iLL_request_extensions=
459         odr_malloc(odr, req->num_iLL_request_extensions*sizeof(*req->iLL_request_extensions));
460     req->iLL_request_extensions[0]=makepromptextension(args,odr);
461     req->iLL_request_extensions[1]=makeoclcextension(args,odr);
462     if (!req) {
463         yaz_log(YLOG_FATAL,"Could not create ill request");
464         exit(2);
465     }
466     return apdu;
467 } /* createrequest */
468
469
470 /* * * * * * * * * * * * * * * */
471 /** \brief Send the request */
472 void sendrequest(ILL_APDU *apdu, ODR odr, COMSTACK stack ) {
473     char *buf_out;
474     int len_out;
475     int res;
476     if (!ill_APDU  (odr, &apdu, 0, 0)) { 
477         yaz_log(YLOG_FATAL,"ill_Apdu failed");
478         exit(2);
479     }
480     buf_out = odr_getbuf(odr, &len_out, 0);
481     if (0) {
482         yaz_log(YLOG_DEBUG,"Request PDU Dump");
483         odr_dumpBER(yaz_log_file(), buf_out, len_out);
484     }
485     if (!buf_out) {
486         yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out);
487         odr_perror(odr, "encoding failed");
488         exit(2);
489     }
490     yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out);
491     res = cs_put(stack, buf_out, len_out);
492     if ( res<0 ) {
493         yaz_log(YLOG_FATAL,"Could not send packet. code %d",res );
494         exit (4);
495     }
496     if (1) {
497         FILE *F = fopen("req.apdu","w");
498         fwrite ( buf_out, 1, len_out, F);
499         fclose(F);
500     }
501     
502 } /* sendrequest */
503
504 /* * * * * * * * * * * * * * * */
505 /** \brief  Get a response */
506 ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){
507     ILL_APDU *resp;
508     int res;
509     char *buf_in=0;
510     int len_in=0;
511     yaz_log(YLOG_DEBUG,"About to wait for a response");
512     res = cs_get(stack, &buf_in, &len_in);
513     yaz_log(YLOG_DEBUG,"Got a response of %d bytes at %p. res=%d", len_in,buf_in, res);
514     if (res<0) {
515         yaz_log(YLOG_FATAL,"Could not receive packet. code %d",res );
516         yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", 
517                 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
518                 buf_in[4], buf_in[5], buf_in[6], buf_in[7]  );
519         yaz_log(YLOG_DEBUG,"PDU Dump:");
520         odr_dumpBER(yaz_log_file(), buf_in, len_in);
521         exit (5);
522     }
523     odr_setbuf(in_odr, buf_in, res, 0);
524     if (!ill_APDU (in_odr, &resp, 0, 0))
525     {
526         int x;
527         int err = odr_geterrorx(in_odr, &x);
528         char msg[60];
529         const char *element = odr_getelement(in_odr);
530         sprintf(msg, "ODR code %d:%d element=%-20s",
531                 err, x, element ? element : "<unknown>");
532         yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
533         yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", 
534                 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
535                 buf_in[4], buf_in[5], buf_in[6], buf_in[7]  );
536         yaz_log(YLOG_DEBUG,"PDU Dump:");
537         odr_dumpBER(yaz_log_file(), buf_in, len_in);
538         yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
539         exit(6);
540     }
541     return resp;
542 } /* getresponse */
543
544
545 /** \brief Dump a apdu */
546 void dumpapdu( ILL_APDU *apdu) {
547     ODR print_odr = odr_createmem(ODR_PRINT);
548     ill_APDU (print_odr, &apdu, 0, 0);
549     odr_destroy(print_odr);
550 } /* dumpapdu */
551
552 /** \brief  Check apdu type and extract the status_or_error */
553 ILL_Status_Or_Error_Report *getstaterr( ILL_APDU *resp, ODR in_odr ) {
554     if (resp->which != ILL_APDU_Status_Or_Error_Report ) {
555         const char *element = odr_getelement(in_odr);
556         if (!element) 
557             element="unknown";
558         printf("Server returned wrong packet type: %d\n", resp->which);
559         yaz_log(YLOG_FATAL,"Server returned a (%d) and "
560                  "not a 'Status_Or_Error_Report' (%d) ",
561                  resp->which, ILL_APDU_Status_Or_Error_Report);
562         exit(6);
563     }
564     return resp->u.Status_Or_Error_Report;
565 } /* getstaterr */
566
567 /** \brief  Return a printable string from an ILL_String */
568 char *getillstring( ILL_String *s) {
569     if (s->which == ILL_String_GeneralString ) 
570         return s->u.GeneralString;
571     else if (s->which == ILL_String_EDIFACTString ) 
572         return s->u.EDIFACTString;
573     else {
574         yaz_log(YLOG_FATAL,"Invalid ILL_String ");
575         exit (6);
576     }
577 } /* getillstring */
578
579 /** \brief Check if the status was an error packet */
580 /* The presence of an error_report indicates it was an error */
581 /* Then the problem is to find the right message. We dig around */
582 /* until we find the first message, print that, and exit the program */
583 void checkerr( ILL_Status_Or_Error_Report *staterr ) {
584     yaz_log(YLOG_DEBUG, "err= %p ",staterr->error_report );
585     if (staterr->error_report) {
586         ILL_Error_Report *err= staterr->error_report;
587         if ( err->user_error_report) {
588             ILL_User_Error_Report *uerr= err->user_error_report;
589             switch( uerr->which ) {
590                 case ILL_User_Error_Report_already_forwarded:
591                     printf("Already forwarded: \n");
592                     break;
593                 case ILL_User_Error_Report_intermediary_problem:
594                     printf("Intermediary problem: %d\n", 
595                         *uerr->u.intermediary_problem);
596                     break;
597                 case ILL_User_Error_Report_security_problem:
598                     printf("Security problem: %s\n", 
599                         getillstring(uerr->u.security_problem));
600                     break;
601                 case ILL_User_Error_Report_unable_to_perform:
602                     printf("Unable to perform: %d\n", 
603                           *uerr->u.unable_to_perform);
604                     break;
605                 default:
606                     printf("Unknown problem");
607             }
608             exit(7);
609         }
610         if ( err->provider_error_report) {
611             ILL_Provider_Error_Report *perr= err->provider_error_report;
612             switch( perr->which ) {
613                 case ILL_Provider_Error_Report_general_problem:
614                     printf("General Problem: %d\n", 
615                           *perr->u.general_problem);
616                     break;
617                 case ILL_Provider_Error_Report_transaction_id_problem:
618                     printf("Transaction Id Problem: %d\n", 
619                           *perr->u.general_problem);
620                     break;
621                 case ILL_Provider_Error_Report_state_transition_prohibited:
622                     printf("State Transition prohibited \n");
623                     break;
624             }
625             exit(7);
626         } 
627         /* fallbacks */
628         if ( staterr->note ) 
629             printf("%s", getillstring(staterr->note));
630         else 
631             printf("Unknown error type");
632         exit(7);
633     }
634 } /* checkerr */
635
636
637
638 /* * * * * * * * * * * * * * * */
639
640 /** \brief Main program 
641  *
642  * Parse arguments
643  * Validate arguments
644  * Establish connection
645  * Build a request
646  * Send a request
647  * Get a reply
648  * Parse reply
649  * Produce output
650  */
651
652 int main (int argc, char * argv[]) {
653     struct prog_args args;
654     COMSTACK stack;
655     ODR out_odr = odr_createmem(ODR_ENCODE);
656     ODR in_odr = odr_createmem(ODR_DECODE);
657     ILL_APDU *apdu;
658     ILL_APDU *resp;
659     ILL_Status_Or_Error_Report *staterr;
660
661     parseargs( argc, argv,  &args);
662     validateargs(&args);
663     stack = connect_to(args.host);
664     apdu = createrequest(&args, out_odr);
665     if (1) 
666         dumpapdu(apdu);
667     sendrequest(apdu, out_odr, stack ); 
668     resp = getresponse(stack, in_odr );
669     if (1) 
670         dumpapdu(resp);
671     staterr=getstaterr(resp, in_odr);
672     checkerr(staterr);
673
674
675     printf ("Ok\n"); /* while debugging */
676     exit (0);
677 }
678
679 /*
680  * Local variables:
681  * c-basic-offset: 4
682  * indent-tabs-mode: nil
683  * End:
684  * vim: shiftwidth=4 tabstop=8 expandtab
685  */
686