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