0ebe02b9d2f9788e48f38d587199286de06ff8e6
[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.3 2007-04-18 07:17:19 adam 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     char *auth_userid;
64     char *auth_passwd;
65 } ;
66
67
68 /* Call-back to be called for every field in the ill-request */
69 /* It can set values to any field, perhaps from the prog_args */
70 const char *get_ill_element(void *clientData, const char *element) {
71     struct prog_args *args = clientData;
72     char *ret=0;
73     if (!strcmp(element,"foo")) {
74         ret=args->host;
75     } else if (!strcmp(element,"ill,protocol-version-num")) {
76         ret="2";
77     } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) {
78         ret="IndexData";
79     } 
80     yaz_log(YLOG_DEBUG,"get_ill_element: '%s' -> '%s' ", element, ret );
81     return ret;
82 }
83
84
85 /* * * * * * * * * * * * * * * * * */
86 /** \brief  Parse program arguments */
87 void parseargs( int argc, char * argv[],  struct prog_args *args) {
88     int ret;
89     char *arg;
90     char *prog=*argv;
91     char *version="$Id: yaz-illclient.c,v 1.3 2007-04-18 07:17:19 adam Exp $"; /* from cvs */
92
93     /* default values */
94     args->host = 0; /* not known (yet) */
95 #if 0    
96     args->auth_userid = "100-228-301" ; /* FIXME - get from cmd line */
97     args->auth_passwd = "dxg5magxc" ;   /* FIXME - get from cmd line */
98 #else
99     args->auth_userid = "100070049" ; /* FIXME - get from cmd line */
100     args->auth_passwd = "cowgirl" ;   /* FIXME - get from cmd line */
101 #endif
102
103
104     while ((ret = options("k:c:q:a:b:m:v:p:u:t:Vxd:", argv, argc, &arg)) != -2)
105     {
106         switch (ret)
107         {
108         case 0:
109             if (!args->host)
110             {
111                 args->host = xstrdup (arg);
112             }
113             else
114             {
115                 fprintf(stderr, "%s: Specify most one server address\n",
116                         prog);
117                 exit(1);
118             }
119             break;
120         case 'v':
121             yaz_log_init(yaz_log_mask_str(arg), "", 0);
122             break;
123         case 'V':
124             printf("%s %s",prog, version );
125             break;
126         default:
127             fprintf (stderr, "Usage: %s "
128                      " [-v loglevel...]"
129                      " [-V]"
130                      " <server-addr>\n",
131                      prog);
132             exit (1);
133         }
134     }
135 } /* parseargs */
136
137 /* * * * * * * * * * * */
138 /** \brief  Validate the arguments make sense */
139 void validateargs( struct prog_args *args) {
140     if (!args->host) {
141         fprintf(stderr, "Specify a connection address, "
142                         "as in 'bagel.indexdata.dk:210' \n");
143         exit(1);
144     }
145 } /* validateargs */
146
147
148 /* * * * * * * * * * * * * * * */
149 /** \brief  Connect to the target */
150 COMSTACK connect_to( char *hostaddr ){
151     COMSTACK stack;
152     void *server_address_ip;
153     int status;
154
155     yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr);
156     stack = cs_create_host(hostaddr, 1, &server_address_ip );
157     if (!stack) {
158         yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ",
159                  hostaddr );
160         exit(2);
161     }
162     
163     yaz_log(YLOG_DEBUG,"Created stack ok ");
164
165     status = cs_connect(stack, server_address_ip);
166     if (status != 0) {
167         yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ",
168                  hostaddr );
169         exit(3);
170     }
171     yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr);
172     return stack;
173 }
174
175
176 /* * * * * * * * * * * * * * * */
177 /* Makes a Z39.50-like prompt package with username and password */
178 Z_PromptObject1 *makeprompt(struct prog_args *args, ODR odr) {
179     Z_PromptObject1 *p = odr_malloc(odr, sizeof(*p) );
180     Z_ResponseUnit1 *ru;
181     
182     p->which=Z_PromptObject1_response;
183     p->u.response = odr_malloc(odr, sizeof(*(p->u.response)) );
184     p->u.response->num=2;
185     p->u.response->elements=odr_malloc(odr, 
186              p->u.response->num*sizeof(*(p->u.response->elements)) );
187     /* user id, aka "oclc authorization number" */
188     ru = odr_malloc(odr, sizeof(*ru) );
189     p->u.response->elements[0] = ru;
190     ru->promptId = odr_malloc(odr, sizeof(*(ru->promptId) ));
191     ru->promptId->which = Z_PromptId_enumeratedPrompt;
192     ru->promptId->u.enumeratedPrompt =  
193         odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
194     ru->promptId->u.enumeratedPrompt->type = 
195          odr_intdup(odr,Z_PromptIdEnumeratedPrompt_userId);
196     ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
197     ru->which = Z_ResponseUnit1_string ;
198     ru->u.string = odr_strdup(odr, args->auth_userid);
199     /* password */
200     ru = odr_malloc(odr, sizeof(*ru) );
201     p->u.response->elements[1] = ru;
202     ru->promptId = odr_malloc(odr, sizeof(*(ru->promptId) ));
203     ru->promptId->which = Z_PromptId_enumeratedPrompt;
204     ru->promptId->u.enumeratedPrompt =  
205         odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
206     ru->promptId->u.enumeratedPrompt->type = 
207          odr_intdup(odr,Z_PromptIdEnumeratedPrompt_password);
208     ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
209     ru->which = Z_ResponseUnit1_string ;
210     ru->u.string = odr_strdup(odr, args->auth_passwd);
211     return p;
212 } /* makeprompt */
213
214 ILL_Extension *makepromptextension(struct prog_args *args, ODR odr) {
215     ODR odr_ext = odr_createmem(ODR_ENCODE);
216     ODR odr_prt = odr_createmem(ODR_PRINT);
217     ILL_Extension *e = odr_malloc(odr, sizeof(*e));
218     Z_PromptObject1 *p = makeprompt(args,odr_ext);
219     char * buf;
220     int siz;
221     Z_External *ext = odr_malloc(odr, sizeof(*ext));
222     ext->direct_reference = odr_getoidbystr(odr,"1.2.840.10003.8.1");
223     ext->indirect_reference=0;
224     ext->descriptor=0;
225     ext->which=Z_External_single;
226     if ( ! z_PromptObject1(odr_ext, &p, 0,0 ) ) {
227         yaz_log(YLOG_FATAL,"Encoding of z_PromptObject1 failed ");
228         exit (6);
229     }
230     
231     z_PromptObject1(odr_prt, &p, 0,0 ); /*!*/
232
233     buf= odr_getbuf(odr_ext,&siz,0);
234     ext->u.single_ASN1_type=odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
235     ext->u.single_ASN1_type->buf= odr_malloc(odr, siz);
236     memcpy(ext->u.single_ASN1_type->buf,buf, siz );
237     ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
238     odr_reset(odr_ext);
239
240     e->identifier = odr_intdup(odr,1);
241     e->critical = odr_intdup(odr,0);
242     e->item=odr_malloc(odr,sizeof(*e->item));
243     if ( ! z_External(odr_ext, &ext,0,0) ) {
244         yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
245         exit (6);
246     }
247     buf= odr_getbuf(odr_ext,&siz,0); 
248     e->item->buf= odr_malloc(odr, siz);
249     memcpy(e->item->buf,buf, siz );
250     e->item->len = e->item->size = siz;
251
252     return e;
253 } /* makepromptextension */
254
255 ILL_APDU *createrequest( struct prog_args *args, ODR odr) {
256     struct ill_get_ctl ctl;
257     ILL_APDU *apdu;
258     ILL_Request *req;
259
260     ctl.odr = odr;
261     ctl.clientData = & args;
262     ctl.f = get_ill_element;
263     apdu = odr_malloc( odr, sizeof(*apdu) );
264     apdu->which=ILL_APDU_ILL_Request;
265     req = ill_get_ILLRequest(&ctl, "ill", 0);
266     apdu->u.illRequest=req;
267     req->num_iLL_request_extensions=1;
268     req->iLL_request_extensions=
269         odr_malloc(odr, req->num_iLL_request_extensions*sizeof(*req->iLL_request_extensions));
270     req->iLL_request_extensions[0]=makepromptextension(args,odr);
271     if (!req) {
272         yaz_log(YLOG_FATAL,"Could not create ill request");
273         exit(2);
274     }
275     return apdu;
276 } /* createrequest */
277
278
279 /* * * * * * * * * * * * * * * */
280 /** \brief Send the request */
281 void sendrequest(ILL_APDU *apdu, ODR odr, COMSTACK stack ) {
282     char *buf_out;
283     int len_out;
284     int res;
285     if (!ill_APDU  (odr, &apdu, 0, 0)) { 
286         yaz_log(YLOG_FATAL,"ill_Apdu failed");
287         exit(2);
288     }
289     buf_out = odr_getbuf(odr, &len_out, 0);
290     if (0) {
291         yaz_log(YLOG_DEBUG,"Request PDU Dump");
292         odr_dumpBER(yaz_log_file(), buf_out, len_out);
293     }
294     if (!buf_out) {
295         yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out);
296         odr_perror(odr, "encoding failed");
297         exit(2);
298     }
299     yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out);
300     res = cs_put(stack, buf_out, len_out);
301     if ( res<0 ) {
302         yaz_log(YLOG_FATAL,"Could not send packet. code %d",res );
303         exit (4);
304     }
305 } /* sendrequest */
306
307 /* * * * * * * * * * * * * * * */
308 /** \brief  Get a response */
309 ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){
310     ILL_APDU *resp;
311     int res;
312     char *buf_in=0;
313     int len_in=0;
314     yaz_log(YLOG_DEBUG,"About to wait for a response");
315     res = cs_get(stack, &buf_in, &len_in);
316     yaz_log(YLOG_DEBUG,"Got a response of %d bytes at %p. res=%d", len_in,buf_in, res);
317     if (res<0) {
318         yaz_log(YLOG_FATAL,"Could not receive packet. code %d",res );
319         yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", 
320                 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
321                 buf_in[4], buf_in[5], buf_in[6], buf_in[7]  );
322         yaz_log(YLOG_DEBUG,"PDU Dump:");
323         odr_dumpBER(yaz_log_file(), buf_in, len_in);
324         exit (5);
325     }
326     odr_setbuf(in_odr, buf_in, res, 0);
327     if (!ill_APDU (in_odr, &resp, 0, 0))
328     {
329         int x;
330         int err = odr_geterrorx(in_odr, &x);
331         char msg[60];
332         const char *element = odr_getelement(in_odr);
333         sprintf(msg, "ODR code %d:%d element=%-20s",
334                 err, x, element ? element : "<unknown>");
335         yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
336         yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...", 
337                 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
338                 buf_in[4], buf_in[5], buf_in[6], buf_in[7]  );
339         yaz_log(YLOG_DEBUG,"PDU Dump:");
340         odr_dumpBER(yaz_log_file(), buf_in, len_in);
341         yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
342         exit(6);
343     }
344     return resp;
345 } /* getresponse */
346
347
348 /** \brief Dump a apdu */
349 void dumpapdu( ILL_APDU *apdu) {
350     ODR print_odr = odr_createmem(ODR_PRINT);
351     ill_APDU (print_odr, &apdu, 0, 0);
352     odr_destroy(print_odr);
353 } /* dumpapdu */
354
355 /** \brief  Check apdu type and extract the status_or_error */
356 ILL_Status_Or_Error_Report *getstaterr( ILL_APDU *resp, ODR in_odr ) {
357     if (resp->which != ILL_APDU_Status_Or_Error_Report ) {
358         const char *element = odr_getelement(in_odr);
359         if (!element) 
360             element="unknown";
361         printf("Server returned wrong packet type: %d\n", resp->which);
362         yaz_log(YLOG_FATAL,"Server returned a (%d) and "
363                  "not a 'Status_Or_Error_Report' (%d) ",
364                  resp->which, ILL_APDU_Status_Or_Error_Report);
365         exit(6);
366     }
367     return resp->u.Status_Or_Error_Report;
368 } /* getstaterr */
369
370 /** \brief  Return a printable string from an ILL_String */
371 char *getillstring( ILL_String *s) {
372     if (s->which == ILL_String_GeneralString ) 
373         return s->u.GeneralString;
374     else if (s->which == ILL_String_EDIFACTString ) 
375         return s->u.EDIFACTString;
376     else {
377         yaz_log(YLOG_FATAL,"Invalid ILL_String ");
378         exit (6);
379     }
380 } /* getillstring */
381
382 /** \brief Check if the status was an error packet */
383 /* The presence of an error_report indicates it was an error */
384 /* Then the problem is to find the right message. We dig around */
385 /* until we find the first message, print that, and exit the program */
386 void checkerr( ILL_Status_Or_Error_Report *staterr ) {
387     yaz_log(YLOG_DEBUG, "err= %p ",staterr->error_report );
388     if (staterr->error_report) {
389         ILL_Error_Report *err= staterr->error_report;
390         if ( err->user_error_report) {
391             ILL_User_Error_Report *uerr= err->user_error_report;
392             switch( uerr->which ) {
393                 case ILL_User_Error_Report_already_forwarded:
394                     printf("Already forwarded: \n");
395                     break;
396                 case ILL_User_Error_Report_intermediary_problem:
397                     printf("Intermediary problem: %p\n", 
398                         uerr->u.intermediary_problem);
399                     break;
400                 case ILL_User_Error_Report_security_problem:
401                     printf("Security problem: %s\n", 
402                         getillstring(uerr->u.security_problem));
403                     break;
404                 case ILL_User_Error_Report_unable_to_perform:
405                     printf("Unable to perform: %p\n", 
406                           uerr->u.unable_to_perform);
407                     break;
408                 default:
409                     printf("Unknown problem");
410             }
411             exit(7);
412         }
413         if ( err->provider_error_report) {
414             ILL_Provider_Error_Report *perr= err->provider_error_report;
415             switch( perr->which ) {
416                 case ILL_Provider_Error_Report_general_problem:
417                     printf("General Problem: %p\n", 
418                           perr->u.general_problem);
419                     break;
420                 case ILL_Provider_Error_Report_transaction_id_problem:
421                     printf("Transaction Id Problem: %p\n", 
422                           perr->u.general_problem);
423                     break;
424                 case ILL_Provider_Error_Report_state_transition_prohibited:
425                     printf("State Transition prohibited \n");
426                     break;
427             }
428             exit(7);
429         } 
430         /* fallbacks */
431         if ( staterr->note ) 
432             printf("%s", getillstring(staterr->note));
433         else 
434             printf("Unknown error type");
435         exit(7);
436     }
437 } /* checkerr */
438
439
440
441 /* * * * * * * * * * * * * * * */
442
443 /** \brief Main program 
444  *
445  * Parse arguments
446  * Validate arguments
447  * Establish connection
448  * Build a request
449  * Send a request
450  * Get a reply
451  * Parse reply
452  * Produce output
453  */
454
455 int main (int argc, char * argv[]) {
456     struct prog_args args;
457     COMSTACK stack;
458     ODR out_odr = odr_createmem(ODR_ENCODE);
459     ODR in_odr = odr_createmem(ODR_DECODE);
460     ILL_APDU *apdu;
461     ILL_APDU *resp;
462     ILL_Status_Or_Error_Report *staterr;
463
464     parseargs( argc, argv,  &args);
465     validateargs(&args);
466     stack = connect_to(args.host);
467     apdu = createrequest(&args, out_odr);
468     if (1) 
469         dumpapdu(apdu);
470     sendrequest(apdu, out_odr, stack ); 
471     resp = getresponse(stack, in_odr );
472     if (1) 
473         dumpapdu(resp);
474     staterr=getstaterr(resp, in_odr);
475     checkerr(staterr);
476
477
478     printf ("Ok\n"); /* while debugging */
479     exit (0);
480 }
481
482 /*
483  * Local variables:
484  * c-basic-offset: 4
485  * indent-tabs-mode: nil
486  * End:
487  * vim: shiftwidth=4 tabstop=8 expandtab
488  */
489