1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) 1995-2012 Index Data
3 * See the file LICENSE for details.
6 /* WARNING - This is work in progress - not at all ready */
8 /** \file yaz-illclient.c
9 * \brief client for ILL requests (ISO 10161-1)
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.
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
19 * Exit codes (note, the program exits as soon as it finds a good reason)
21 * 1 errors in arguments
22 * 2 Internal errors in creating objects (comstack, odr...)
23 * mostly programming errors.
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)
52 #include <yaz/yaz-util.h>
53 #include <yaz/proto.h>
54 #include <yaz/comstack.h>
55 #include <yaz/tcpip.h>
60 #include <yaz/oclc-ill-req-ext.h>
63 /* A structure for holding name-value pairs in a linked list */
70 /* A structure for storing all the arguments */
75 char *oclc_recno; /* record number in oclc-mode */
76 int oclc_auth; /* 1=use oclc-type auth */
77 struct nameval* namevals; /* circular list, points to last */
82 /* Call-back to be called for every field in the ill-request */
83 /* It can set values to any field, perhaps from the prog_args */
84 const char *get_ill_element(void *clientData, const char *element) {
85 struct prog_args *args = (struct prog_args *) clientData;
86 struct nameval *nv=args->namevals;
92 /* printf("comparing '%s' with '%s' \n",element, nv->name ); */
93 if ( strcmp(element, nv->name) == 0 )
95 } while ( ( !ret) && ( nv != args->namevals) );
96 yaz_log(YLOG_DEBUG,"get_ill_element:'%s'->'%s'", element, ret );
101 /* * * * * * * * * * * * * * * * * */
103 /** \brief parse a parameter string */
104 /* string is like 'name=value' */
105 struct nameval *parse_nameval( char *arg ) {
106 struct nameval *nv = (struct nameval *) xmalloc(sizeof(*nv));
110 return 0; /* yeah, leaks a bit of memory. so what */
111 while ( *p && ( *p != '=' ) )
116 nv->name = (char *) xmalloc(len+1);
117 strncpy(nv->name, arg, len);
120 p++; /* skip the '=' */
122 return 0; /* oops, no '=' */
124 return 0; /* no value */
127 yaz_log(YLOG_DEBUG,"parse_nameval: n='%s' v='%s'", nv->name, nv->val );
131 /** \brief append nv to the list of namevals */
132 void append_nameval (struct prog_args *args, struct nameval *nv) {
135 if (args->namevals) {
136 nv->next=args->namevals->next; /* first */
137 args->namevals->next=nv;
143 } /* append_nameval */
145 /** \brief parse a parameter file */
146 void parse_paramfile(char *arg, struct prog_args *args) {
147 FILE *f=fopen(arg,"r");
153 yaz_log(YLOG_FATAL,"Could not open param file '%s' ", arg);
154 printf("Could not open file '%s' \n",arg);
157 yaz_log(YLOG_DEBUG,"Opened input file '%s' ",arg );
158 while (fgets(buf,BUFSIZE,f)) {
159 if (buf[0] != '#' ) {
161 if (buf[len] == '\n')
163 nv=parse_nameval(buf);
164 append_nameval(args, nv);
165 } /* not a comment */
171 printf("nv chain: ================ \n");
172 printf("(last:) %p: '%s' = '%s' (%p) \n",nv, nv->name, nv->val, nv->next );
175 printf("%p: '%s' = '%s' (%p)\n",nv, nv->name, nv->val, nv->next );
176 } while (nv != args->namevals );
180 } /* parse_paramfile */
183 /** \brief Parse program arguments */
184 void parseargs( int argc, char * argv[], struct prog_args *args) {
192 args->host = 0; /* not known (yet) */
193 args->namevals=0; /* none set up */
196 args->auth_userid = 0;
197 args->auth_passwd = 0;
199 /* Example 3 - directly from OCLC, supposed to work on their test server*/
200 args->auth_userid = "100-310-658" ; /* FIXME - get from cmd line */
201 args->auth_passwd = "apii2test" ; /* FIXME - get from cmd line */
204 while ((ret = options("Vov:p:u:D:f:r:l:", argv, argc, &arg)) != -2)
206 yaz_log(YLOG_DEBUG,"parsing option '%c' '%s'",ret, arg);
212 args->host = xstrdup (arg);
216 fprintf(stderr, "%s: Specify at most one server address\n",
222 yaz_log_init(yaz_log_mask_str(arg), "", 0);
225 yaz_log_init_file(arg);
228 yaz_version(version, 0);
229 printf("%s %s\n",prog, version);
232 nv=parse_nameval(arg);
233 append_nameval(args,nv);
236 parse_paramfile(arg,args);
242 args->auth_userid=xstrdup(arg);
245 args->auth_passwd=xstrdup(arg);
248 args->oclc_recno=xstrdup(arg);
251 fprintf (stderr, "Usage: %s "
255 " [-o -u user -p passwd]"
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");
272 if (args->oclc_auth && ((!args->auth_userid) || (!args->auth_passwd))){
273 fprintf(stderr, "-o option requires -u <user> and -p <pwd>\n");
279 /* * * * * * * * * * * * * * * */
280 /** \brief Connect to the target */
281 COMSTACK connect_to( char *hostaddr ){
283 void *server_address_ip;
286 yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr);
287 stack = cs_create_host(hostaddr, 1, &server_address_ip );
289 yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ",
294 yaz_log(YLOG_DEBUG,"Created stack ok ");
296 status = cs_connect(stack, server_address_ip);
298 yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ",
302 yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr);
307 /* * * * * * * * * * * * * * * */
308 /* Makes a Z39.50-like prompt package with username and password */
309 Z_PromptObject1 *makeprompt(struct prog_args *args, ODR odr) {
310 Z_PromptObject1 *p = (Z_PromptObject1 *) odr_malloc(odr, sizeof(*p) );
311 Z_ResponseUnit1 *ru = (Z_ResponseUnit1 *) odr_malloc(odr, sizeof(*ru) );
313 p->which=Z_PromptObject1_response;
314 p->u.response = (Z_Response1*) odr_malloc(odr, sizeof(*(p->u.response)) );
315 p->u.response->num=2;
316 p->u.response->elements= (Z_ResponseUnit1 **) odr_malloc(odr,
317 p->u.response->num*sizeof(*(p->u.response->elements)) );
318 /* user id, aka "oclc authorization number" */
319 p->u.response->elements[0] = ru;
320 ru->promptId = (Z_PromptId *) odr_malloc(odr, sizeof(*(ru->promptId) ));
321 ru->promptId->which = Z_PromptId_enumeratedPrompt;
322 ru->promptId->u.enumeratedPrompt = (Z_PromptIdEnumeratedPrompt *)
323 odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
324 ru->promptId->u.enumeratedPrompt->type =
325 odr_intdup(odr,Z_PromptIdEnumeratedPrompt_userId);
326 ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
327 ru->which = Z_ResponseUnit1_string ;
328 ru->u.string = odr_strdup(odr, args->auth_userid);
330 ru = (Z_ResponseUnit1 *) odr_malloc(odr, sizeof(*ru) );
331 p->u.response->elements[1] = ru;
332 ru->promptId = (Z_PromptId *) odr_malloc(odr, sizeof(*(ru->promptId) ));
333 ru->promptId->which = Z_PromptId_enumeratedPrompt;
334 ru->promptId->u.enumeratedPrompt = (Z_PromptIdEnumeratedPrompt *)
335 odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
336 ru->promptId->u.enumeratedPrompt->type =
337 odr_intdup(odr,Z_PromptIdEnumeratedPrompt_password);
338 ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
339 ru->which = Z_ResponseUnit1_string ;
340 ru->u.string = odr_strdup(odr, args->auth_passwd);
344 ILL_Extension *makepromptextension(struct prog_args *args, ODR odr) {
345 ODR odr_ext = odr_createmem(ODR_ENCODE);
346 ODR odr_prt = odr_createmem(ODR_PRINT);
347 ILL_Extension *e = (ILL_Extension *) odr_malloc(odr, sizeof(*e));
348 Z_PromptObject1 *p = makeprompt(args,odr_ext);
351 Z_External *ext = (Z_External *) odr_malloc(odr, sizeof(*ext));
352 ext->direct_reference = odr_getoidbystr(odr,"1.2.840.10003.8.1");
353 ext->indirect_reference=0;
355 ext->which=Z_External_single;
356 if ( ! z_PromptObject1(odr_ext, &p, 0,0 ) ) {
357 yaz_log(YLOG_FATAL,"Encoding of z_PromptObject1 failed ");
361 printf ("Prompt: \n"); /*!*/
362 z_PromptObject1(odr_prt, &p, 0,0 ); /*!*/
364 buf= odr_getbuf(odr_ext,&siz,0);
365 ext->u.single_ASN1_type=(Odr_any *)
366 odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
367 ext->u.single_ASN1_type->buf= (unsigned char *) odr_malloc(odr, siz);
368 memcpy(ext->u.single_ASN1_type->buf,buf, siz );
369 ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
371 odr_reset(odr_prt); /*!*/
373 e->identifier = odr_intdup(odr,1);
374 e->critical = odr_booldup(odr,0);
375 e->item = (Odr_any *) odr_malloc(odr,sizeof(*e->item));
376 if ( ! z_External(odr_ext, &ext,0,0) ) {
377 yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
380 printf("External: \n");
381 z_External(odr_prt, &ext,0,0); /*!*/
382 buf= odr_getbuf(odr_ext,&siz,0);
383 e->item->buf= (unsigned char *) odr_malloc(odr, siz);
384 memcpy(e->item->buf,buf, siz );
385 e->item->len = e->item->size = siz;
387 odr_destroy(odr_prt);
388 odr_destroy(odr_ext);
390 } /* makepromptextension */
392 ILL_Extension *makeoclcextension(struct prog_args *args, ODR odr) {
393 /* The oclc extension is required, but only contains optional */
394 /* fields. Here we just null them all out */
395 ODR odr_ext = odr_createmem(ODR_ENCODE);
396 ODR odr_prt = odr_createmem(ODR_PRINT);
397 ILL_Extension *e = (ILL_Extension *) odr_malloc(odr, sizeof(*e));
398 ILL_OCLCILLRequestExtension *oc = (ILL_OCLCILLRequestExtension *)
399 odr_malloc(odr_ext, sizeof(*oc));
402 Z_External *ext = (Z_External *) odr_malloc(odr, sizeof(*ext));
403 oc->clientDepartment = 0;
404 oc->paymentMethod = 0;
405 oc->uniformTitle = 0;
406 oc->dissertation = 0;
409 oc->affiliations = 0;
411 ext->direct_reference = odr_getoidbystr(odr,"1.0.10161.13.2");
412 ext->indirect_reference=0;
414 ext->which=Z_External_single;
415 if ( ! ill_OCLCILLRequestExtension(odr_ext, &oc, 0,0 ) ) {
416 yaz_log(YLOG_FATAL,"Encoding of ill_OCLCILLRequestExtension failed ");
420 printf ("OCLC: \n"); /*!*/
421 ill_OCLCILLRequestExtension(odr_prt, &oc, 0,0 ); /*!*/
423 buf= odr_getbuf(odr_ext,&siz,0);
424 ext->u.single_ASN1_type = (Odr_any*)
425 odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
426 ext->u.single_ASN1_type->buf = (unsigned char *) odr_malloc(odr, siz);
427 memcpy(ext->u.single_ASN1_type->buf,buf, siz );
428 ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
430 odr_reset(odr_prt); /*!*/
432 e->identifier = odr_intdup(odr,1);
433 e->critical = odr_booldup(odr,0);
434 e->item = (Odr_any *) odr_malloc(odr,sizeof(*e->item));
435 if ( ! z_External(odr_ext, &ext,0,0) ) {
436 yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
439 printf("External: \n");
440 z_External(odr_prt, &ext,0,0); /*!*/
441 buf= odr_getbuf(odr_ext,&siz,0);
442 e->item->buf= (unsigned char *) odr_malloc(odr, siz);
443 memcpy(e->item->buf, buf, siz);
444 e->item->len = e->item->size = siz;
446 odr_destroy(odr_prt);
447 odr_destroy(odr_ext);
450 } /* makeoclcextension */
452 ILL_APDU *createrequest( struct prog_args *args, ODR odr) {
453 struct ill_get_ctl ctl;
458 ctl.clientData = args;
459 ctl.f = get_ill_element;
460 apdu = (ILL_APDU *) odr_malloc( odr, sizeof(*apdu) );
461 apdu->which=ILL_APDU_ILL_Request;
462 req = ill_get_ILLRequest(&ctl, "ill", 0);
463 apdu->u.illRequest=req;
464 if (args->oclc_auth) {
465 req->num_iLL_request_extensions=2;
466 req->iLL_request_extensions=
468 odr_malloc(odr, req->num_iLL_request_extensions*
469 sizeof(*req->iLL_request_extensions));
470 req->iLL_request_extensions[0]=makepromptextension(args,odr);
471 req->iLL_request_extensions[1]=makeoclcextension(args,odr);
474 yaz_log(YLOG_FATAL,"Could not create ill request");
478 } /* createrequest */
481 /* * * * * * * * * * * * * * * */
482 /** \brief Send the request */
483 void sendrequest(ILL_APDU *apdu, ODR odr, COMSTACK stack ) {
487 if (!ill_APDU (odr, &apdu, 0, 0)) {
488 yaz_log(YLOG_FATAL,"ill_Apdu failed");
491 buf_out = odr_getbuf(odr, &len_out, 0);
493 yaz_log(YLOG_DEBUG,"Request PDU Dump");
494 odr_dumpBER(yaz_log_file(), buf_out, len_out);
497 yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out);
498 odr_perror(odr, "encoding failed");
501 yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out);
502 res = cs_put(stack, buf_out, len_out);
504 yaz_log(YLOG_FATAL,"Could not send packet. code %d",res );
508 FILE *F = fopen("req.apdu","w");
511 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open req.apdu failed");
515 if (fwrite ( buf_out, 1, len_out, F) != len_out)
516 yaz_log(YLOG_FATAL|YLOG_ERRNO, "write req.apdu failed");
518 yaz_log(YLOG_FATAL|YLOG_ERRNO, "write req.apdu failed");
524 /* * * * * * * * * * * * * * * */
525 /** \brief Get a response */
526 ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){
531 yaz_log(YLOG_DEBUG,"About to wait for a response");
532 res = cs_get(stack, &buf_in, &len_in);
533 yaz_log(YLOG_DEBUG,"Got a response of %d bytes at %p. res=%d", len_in,buf_in, res);
535 yaz_log(YLOG_FATAL,"Could not receive packet. code %d",res );
536 yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...",
537 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
538 buf_in[4], buf_in[5], buf_in[6], buf_in[7] );
539 yaz_log(YLOG_DEBUG,"PDU Dump:");
540 odr_dumpBER(yaz_log_file(), buf_in, len_in);
543 odr_setbuf(in_odr, buf_in, res, 0);
544 if (!ill_APDU (in_odr, &resp, 0, 0))
547 int err = odr_geterrorx(in_odr, &x);
549 const char *element = odr_getelement(in_odr);
550 sprintf(msg, "ODR code %d:%d element=%-20s",
551 err, x, element ? element : "<unknown>");
552 yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
553 yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...",
554 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
555 buf_in[4], buf_in[5], buf_in[6], buf_in[7] );
556 yaz_log(YLOG_DEBUG,"PDU Dump:");
557 odr_dumpBER(yaz_log_file(), buf_in, len_in);
558 yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
565 /** \brief Dump a apdu */
566 void dumpapdu( ILL_APDU *apdu) {
567 ODR print_odr = odr_createmem(ODR_PRINT);
568 ill_APDU (print_odr, &apdu, 0, 0);
569 odr_destroy(print_odr);
572 /** \brief Check apdu type and extract the status_or_error */
573 ILL_Status_Or_Error_Report *getstaterr( ILL_APDU *resp, ODR in_odr ) {
574 if (resp->which != ILL_APDU_Status_Or_Error_Report ) {
575 const char *element = odr_getelement(in_odr);
578 printf("Server returned wrong packet type: %d\n", resp->which);
579 yaz_log(YLOG_FATAL,"Server returned a (%d) and "
580 "not a 'Status_Or_Error_Report' (%d) ",
581 resp->which, ILL_APDU_Status_Or_Error_Report);
584 return resp->u.Status_Or_Error_Report;
587 /** \brief Return a printable string from an ILL_String */
588 char *getillstring( ILL_String *s) {
589 if (s->which == ILL_String_GeneralString )
590 return s->u.GeneralString;
591 else if (s->which == ILL_String_EDIFACTString )
592 return s->u.EDIFACTString;
594 yaz_log(YLOG_FATAL,"Invalid ILL_String ");
599 /** \brief Check if the status was an error packet */
600 /* The presence of an error_report indicates it was an error */
601 /* Then the problem is to find the right message. We dig around */
602 /* until we find the first message, print that, and exit the program */
603 void checkerr( ILL_Status_Or_Error_Report *staterr ) {
604 yaz_log(YLOG_DEBUG, "err= %p ",staterr->error_report );
605 if (staterr->error_report) {
606 ILL_Error_Report *err= staterr->error_report;
607 if ( err->user_error_report) {
608 ILL_User_Error_Report *uerr= err->user_error_report;
609 switch( uerr->which ) {
610 case ILL_User_Error_Report_already_forwarded:
611 printf("Already forwarded: \n");
613 case ILL_User_Error_Report_intermediary_problem:
614 printf("Intermediary problem: " ODR_INT_PRINTF "\n",
615 *uerr->u.intermediary_problem);
617 case ILL_User_Error_Report_security_problem:
618 printf("Security problem: %s\n",
619 getillstring(uerr->u.security_problem));
621 case ILL_User_Error_Report_unable_to_perform:
622 printf("Unable to perform: " ODR_INT_PRINTF "\n",
623 *uerr->u.unable_to_perform);
626 printf("Unknown problem");
630 if ( err->provider_error_report) {
631 ILL_Provider_Error_Report *perr= err->provider_error_report;
632 switch( perr->which ) {
633 case ILL_Provider_Error_Report_general_problem:
634 printf("General Problem: " ODR_INT_PRINTF ":",
635 *perr->u.general_problem);
637 case ILL_Provider_Error_Report_transaction_id_problem:
638 printf("Transaction Id Problem: " ODR_INT_PRINTF ":",
639 *perr->u.general_problem);
641 case ILL_Provider_Error_Report_state_transition_prohibited:
642 printf("State Transition prohibited:");
649 printf("%s", getillstring(staterr->note));
651 printf("Unknown error type");
659 /* * * * * * * * * * * * * * * */
661 /** \brief Main program
665 * Establish connection
673 int main (int argc, char * argv[]) {
674 struct prog_args args;
676 ODR out_odr = odr_createmem(ODR_ENCODE);
677 ODR in_odr = odr_createmem(ODR_DECODE);
680 ILL_Status_Or_Error_Report *staterr;
682 parseargs( argc, argv, &args);
684 stack = connect_to(args.host);
685 apdu = createrequest(&args, out_odr);
688 sendrequest(apdu, out_odr, stack );
689 resp = getresponse(stack, in_odr );
692 staterr=getstaterr(resp, in_odr);
696 printf ("Ok\n"); /* while debugging */
703 * c-file-style: "Stroustrup"
704 * indent-tabs-mode: nil
706 * vim: shiftwidth=4 tabstop=8 expandtab