2 * Copyright (C) 1995-2006, Index Data ApS
3 * See the file LICENSE for details.
5 * $Id: yaz-illclient.c,v 1.5 2007-04-25 16:51:47 heikki Exp $
8 /* WARNING - This is work in progress - not at all ready */
10 /** \file yaz-illclient.c
11 * \brief client for ILL requests (ISO 10161-1)
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.
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
21 * Exit codes (note, the program exits as soon as it finds a good reason)
23 * 1 errors in arguments
24 * 2 Internal errors in creating objects (comstack, odr...)
25 * mostly programming errors.
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)
51 #include <yaz/yaz-util.h>
52 #include <yaz/proto.h>
53 #include <yaz/comstack.h>
54 #include <yaz/tcpip.h>
59 #include <yaz/oclc-ill-req-ext.h>
62 /* A structure for holding name-value pairs in a linked list */
69 /* A structure for storing all the arguments */
74 struct nameval* namevals; /* circular list, points to last */
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;
89 /* printf("comparing '%s' with '%s' \n",element, nv->name ); */
90 if ( strcmp(element, nv->name) == 0 )
92 } while ( ( !ret) && ( nv != args->namevals) );
94 if (!strcmp(element,"foo")) {
96 } else if (!strcmp(element,"ill,protocol-version-num")) {
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")) {
109 } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) {
112 yaz_log(YLOG_DEBUG,"get_ill_element:'%s'->'%s'", element, ret );
117 /* * * * * * * * * * * * * * * * * */
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));
126 return 0; /* yeah, leaks a bit of memory. so what */
127 while ( *p && ( *p != '=' ) )
132 nv->name = xmalloc(len+1);
133 strncpy(nv->name, arg, len);
136 p++; /* skip the '=' */
138 return 0; /* oops, no '=' */
140 return 0; /* no value */
143 yaz_log(YLOG_DEBUG,"parse_nameval: n='%s' v='%s'", nv->name, nv->val );
147 /** \brief append nv to the list of namevals */
148 void append_nameval (struct prog_args *args, struct nameval *nv) {
151 if (args->namevals) {
152 nv->next=args->namevals->next; /* first */
153 args->namevals->next=nv;
159 } /* append_nameval */
161 /** \brief parse a parameter file */
162 void parse_paramfile(char *arg, struct prog_args *args) {
163 FILE *f=fopen(arg,"r");
169 yaz_log(YLOG_FATAL,"Could not open param file '%s' ", arg);
170 printf("Could not open file '%s' \n",arg);
173 yaz_log(YLOG_DEBUG,"Opened input file '%s' ",arg );
174 while (fgets(buf,BUFSIZE,f)) {
175 if (buf[0] != '#' ) {
177 if (buf[len] == '\n')
179 nv=parse_nameval(buf);
180 append_nameval(args, nv);
181 } /* not a comment */
187 printf("nv chain: ================ \n");
188 printf("(last:) %p: '%s' = '%s' (%p) \n",nv, nv->name, nv->val, nv->next );
191 printf("%p: '%s' = '%s' (%p)\n",nv, nv->name, nv->val, nv->next );
192 } while (nv != args->namevals );
196 } /* parse_paramfile */
199 /** \brief Parse program arguments */
200 void parseargs( int argc, char * argv[], struct prog_args *args) {
204 char *version="$Id: yaz-illclient.c,v 1.5 2007-04-25 16:51:47 heikki Exp $"; /* from cvs */
208 args->host = 0; /* not known (yet) */
209 args->namevals=0; /* none set up */
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 */
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 */
223 while ((ret = options("v:D:f:V", argv, argc, &arg)) != -2)
230 args->host = xstrdup (arg);
234 fprintf(stderr, "%s: Specify at most one server address\n",
240 yaz_log_init(yaz_log_mask_str(arg), "", 0);
243 printf("%s %s",prog, version );
246 nv=parse_nameval(arg);
247 append_nameval(args,nv);
250 parse_paramfile(arg,args);
253 fprintf (stderr, "Usage: %s "
264 /* * * * * * * * * * * */
265 /** \brief Validate the arguments make sense */
266 void validateargs( struct prog_args *args) {
268 fprintf(stderr, "Specify a connection address, "
269 "as in 'bagel.indexdata.dk:210' \n");
275 /* * * * * * * * * * * * * * * */
276 /** \brief Connect to the target */
277 COMSTACK connect_to( char *hostaddr ){
279 void *server_address_ip;
282 yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr);
283 stack = cs_create_host(hostaddr, 1, &server_address_ip );
285 yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ",
290 yaz_log(YLOG_DEBUG,"Created stack ok ");
292 status = cs_connect(stack, server_address_ip);
294 yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ",
298 yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr);
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) );
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);
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);
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);
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;
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 ");
357 printf ("Prompt: \n"); /*!*/
358 z_PromptObject1(odr_prt, &p, 0,0 ); /*!*/
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;
366 odr_reset(odr_prt); /*!*/
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 ");
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;
382 odr_destroy(odr_prt);
383 odr_destroy(odr_ext);
385 } /* makepromptextension */
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));
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;
403 oc->affiliations = 0;
405 ext->direct_reference = odr_getoidbystr(odr,"1.0.10161.13.2");
406 ext->indirect_reference=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 ");
414 printf ("OCLC: \n"); /*!*/
415 ill_OCLCILLRequestExtension(odr_prt, &oc, 0,0 ); /*!*/
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;
423 odr_reset(odr_prt); /*!*/
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 ");
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;
439 odr_destroy(odr_prt);
440 odr_destroy(odr_ext);
443 } /* makeoclcextension */
445 ILL_APDU *createrequest( struct prog_args *args, ODR odr) {
446 struct ill_get_ctl ctl;
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);
463 yaz_log(YLOG_FATAL,"Could not create ill request");
467 } /* createrequest */
470 /* * * * * * * * * * * * * * * */
471 /** \brief Send the request */
472 void sendrequest(ILL_APDU *apdu, ODR odr, COMSTACK stack ) {
476 if (!ill_APDU (odr, &apdu, 0, 0)) {
477 yaz_log(YLOG_FATAL,"ill_Apdu failed");
480 buf_out = odr_getbuf(odr, &len_out, 0);
482 yaz_log(YLOG_DEBUG,"Request PDU Dump");
483 odr_dumpBER(yaz_log_file(), buf_out, len_out);
486 yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out);
487 odr_perror(odr, "encoding failed");
490 yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out);
491 res = cs_put(stack, buf_out, len_out);
493 yaz_log(YLOG_FATAL,"Could not send packet. code %d",res );
497 FILE *F = fopen("req.apdu","w");
498 fwrite ( buf_out, 1, len_out, F);
504 /* * * * * * * * * * * * * * * */
505 /** \brief Get a response */
506 ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){
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);
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);
523 odr_setbuf(in_odr, buf_in, res, 0);
524 if (!ill_APDU (in_odr, &resp, 0, 0))
527 int err = odr_geterrorx(in_odr, &x);
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);
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);
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);
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);
564 return resp->u.Status_Or_Error_Report;
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;
574 yaz_log(YLOG_FATAL,"Invalid ILL_String ");
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");
593 case ILL_User_Error_Report_intermediary_problem:
594 printf("Intermediary problem: %d\n",
595 *uerr->u.intermediary_problem);
597 case ILL_User_Error_Report_security_problem:
598 printf("Security problem: %s\n",
599 getillstring(uerr->u.security_problem));
601 case ILL_User_Error_Report_unable_to_perform:
602 printf("Unable to perform: %d\n",
603 *uerr->u.unable_to_perform);
606 printf("Unknown problem");
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);
617 case ILL_Provider_Error_Report_transaction_id_problem:
618 printf("Transaction Id Problem: %d\n",
619 *perr->u.general_problem);
621 case ILL_Provider_Error_Report_state_transition_prohibited:
622 printf("State Transition prohibited \n");
629 printf("%s", getillstring(staterr->note));
631 printf("Unknown error type");
638 /* * * * * * * * * * * * * * * */
640 /** \brief Main program
644 * Establish connection
652 int main (int argc, char * argv[]) {
653 struct prog_args args;
655 ODR out_odr = odr_createmem(ODR_ENCODE);
656 ODR in_odr = odr_createmem(ODR_DECODE);
659 ILL_Status_Or_Error_Report *staterr;
661 parseargs( argc, argv, &args);
663 stack = connect_to(args.host);
664 apdu = createrequest(&args, out_odr);
667 sendrequest(apdu, out_odr, stack );
668 resp = getresponse(stack, in_odr );
671 staterr=getstaterr(resp, in_odr);
675 printf ("Ok\n"); /* while debugging */
682 * indent-tabs-mode: nil
684 * vim: shiftwidth=4 tabstop=8 expandtab