2 * Copyright (C) 1995-2006, Index Data ApS
3 * See the file LICENSE for details.
5 * $Id: yaz-illclient.c,v 1.4 2007-04-18 13:21:46 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 storing all the arguments */
70 /* Call-back to be called for every field in the ill-request */
71 /* It can set values to any field, perhaps from the prog_args */
72 const char *get_ill_element(void *clientData, const char *element) {
73 struct prog_args *args = clientData;
75 if (!strcmp(element,"foo")) {
77 } else if (!strcmp(element,"ill,protocol-version-num")) {
80 } else if (!strcmp(element,"ill,transaction-id,initial-requester-id,person-or-institution-symbol,institution")) {
84 yaz_log(YLOG_DEBUG,"get_ill_element: '%s' -> '%s' ", element, ret );
89 /* * * * * * * * * * * * * * * * * */
90 /** \brief Parse program arguments */
91 void parseargs( int argc, char * argv[], struct prog_args *args) {
95 char *version="$Id: yaz-illclient.c,v 1.4 2007-04-18 13:21:46 heikki Exp $"; /* from cvs */
98 args->host = 0; /* not known (yet) */
100 args->auth_userid = "100-228-301" ; /* FIXME - get from cmd line */
101 args->auth_passwd = "dxg5magxc" ; /* FIXME - get from cmd line */
103 args->auth_userid = "100070049" ; /* FIXME - get from cmd line */
104 args->auth_passwd = "cowgirl" ; /* FIXME - get from cmd line */
108 while ((ret = options("k:c:q:a:b:m:v:p:u:t:Vxd:", argv, argc, &arg)) != -2)
115 args->host = xstrdup (arg);
119 fprintf(stderr, "%s: Specify most one server address\n",
125 yaz_log_init(yaz_log_mask_str(arg), "", 0);
128 printf("%s %s",prog, version );
131 fprintf (stderr, "Usage: %s "
141 /* * * * * * * * * * * */
142 /** \brief Validate the arguments make sense */
143 void validateargs( struct prog_args *args) {
145 fprintf(stderr, "Specify a connection address, "
146 "as in 'bagel.indexdata.dk:210' \n");
152 /* * * * * * * * * * * * * * * */
153 /** \brief Connect to the target */
154 COMSTACK connect_to( char *hostaddr ){
156 void *server_address_ip;
159 yaz_log(YLOG_DEBUG,"Connecting to '%s'", hostaddr);
160 stack = cs_create_host(hostaddr, 1, &server_address_ip );
162 yaz_log(YLOG_FATAL,"Error in creating the comstack '%s' ",
167 yaz_log(YLOG_DEBUG,"Created stack ok ");
169 status = cs_connect(stack, server_address_ip);
171 yaz_log(YLOG_FATAL|YLOG_ERRNO,"Can not connect '%s' ",
175 yaz_log(YLOG_DEBUG,"Connected OK to '%s'", hostaddr);
180 /* * * * * * * * * * * * * * * */
181 /* Makes a Z39.50-like prompt package with username and password */
182 Z_PromptObject1 *makeprompt(struct prog_args *args, ODR odr) {
183 Z_PromptObject1 *p = odr_malloc(odr, sizeof(*p) );
184 Z_ResponseUnit1 *ru = odr_malloc(odr, sizeof(*ru) );
186 p->which=Z_PromptObject1_response;
187 p->u.response = odr_malloc(odr, sizeof(*(p->u.response)) );
188 p->u.response->num=2;
189 p->u.response->elements=odr_malloc(odr,
190 p->u.response->num*sizeof(*(p->u.response->elements)) );
191 /* user id, aka "oclc authorization number" */
192 p->u.response->elements[0] = ru;
193 ru->promptId = odr_malloc(odr, sizeof(*(ru->promptId) ));
194 ru->promptId->which = Z_PromptId_enumeratedPrompt;
195 ru->promptId->u.enumeratedPrompt =
196 odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
197 ru->promptId->u.enumeratedPrompt->type =
198 odr_intdup(odr,Z_PromptIdEnumeratedPrompt_userId);
199 ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
200 ru->which = Z_ResponseUnit1_string ;
201 ru->u.string = odr_strdup(odr, args->auth_userid);
203 ru = odr_malloc(odr, sizeof(*ru) );
204 p->u.response->elements[1] = ru;
205 ru->promptId = odr_malloc(odr, sizeof(*(ru->promptId) ));
206 ru->promptId->which = Z_PromptId_enumeratedPrompt;
207 ru->promptId->u.enumeratedPrompt =
208 odr_malloc(odr, sizeof(*(ru->promptId->u.enumeratedPrompt) ));
209 ru->promptId->u.enumeratedPrompt->type =
210 odr_intdup(odr,Z_PromptIdEnumeratedPrompt_password);
211 ru->promptId->u.enumeratedPrompt->suggestedString = 0 ;
212 ru->which = Z_ResponseUnit1_string ;
213 ru->u.string = odr_strdup(odr, args->auth_passwd);
217 ILL_Extension *makepromptextension(struct prog_args *args, ODR odr) {
218 ODR odr_ext = odr_createmem(ODR_ENCODE);
219 ODR odr_prt = odr_createmem(ODR_PRINT);
220 ILL_Extension *e = odr_malloc(odr, sizeof(*e));
221 Z_PromptObject1 *p = makeprompt(args,odr_ext);
224 Z_External *ext = odr_malloc(odr, sizeof(*ext));
225 ext->direct_reference = odr_getoidbystr(odr,"1.2.840.10003.8.1");
226 ext->indirect_reference=0;
228 ext->which=Z_External_single;
229 if ( ! z_PromptObject1(odr_ext, &p, 0,0 ) ) {
230 yaz_log(YLOG_FATAL,"Encoding of z_PromptObject1 failed ");
234 printf ("Prompt: \n"); /*!*/
235 z_PromptObject1(odr_prt, &p, 0,0 ); /*!*/
237 buf= odr_getbuf(odr_ext,&siz,0);
238 ext->u.single_ASN1_type=odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
239 ext->u.single_ASN1_type->buf= odr_malloc(odr, siz);
240 memcpy(ext->u.single_ASN1_type->buf,buf, siz );
241 ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
243 odr_reset(odr_prt); /*!*/
245 e->identifier = odr_intdup(odr,1);
246 e->critical = odr_intdup(odr,0);
247 e->item=odr_malloc(odr,sizeof(*e->item));
248 if ( ! z_External(odr_ext, &ext,0,0) ) {
249 yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
252 printf("External: \n");
253 z_External(odr_prt, &ext,0,0); /*!*/
254 buf= odr_getbuf(odr_ext,&siz,0);
255 e->item->buf= odr_malloc(odr, siz);
256 memcpy(e->item->buf,buf, siz );
257 e->item->len = e->item->size = siz;
259 odr_destroy(odr_prt);
260 odr_destroy(odr_ext);
262 } /* makepromptextension */
264 ILL_Extension *makeoclcextension(struct prog_args *args, ODR odr) {
265 /* The oclc extension is required, but only contains optional */
266 /* fields. Here we just null them all out */
267 ODR odr_ext = odr_createmem(ODR_ENCODE);
268 ODR odr_prt = odr_createmem(ODR_PRINT);
269 ILL_Extension *e = odr_malloc(odr, sizeof(*e));
270 ILL_OCLCILLRequestExtension *oc = odr_malloc(odr_ext, sizeof(*oc));
273 Z_External *ext = odr_malloc(odr, sizeof(*ext));
274 oc->clientDepartment = 0;
275 oc->paymentMethod = 0;
276 oc->uniformTitle = 0;
277 oc->dissertation = 0;
280 oc->affiliations = 0;
282 ext->direct_reference = odr_getoidbystr(odr,"1.0.10161.13.2");
283 ext->indirect_reference=0;
285 ext->which=Z_External_single;
286 if ( ! ill_OCLCILLRequestExtension(odr_ext, &oc, 0,0 ) ) {
287 yaz_log(YLOG_FATAL,"Encoding of ill_OCLCILLRequestExtension failed ");
291 printf ("OCLC: \n"); /*!*/
292 ill_OCLCILLRequestExtension(odr_prt, &oc, 0,0 ); /*!*/
294 buf= odr_getbuf(odr_ext,&siz,0);
295 ext->u.single_ASN1_type=odr_malloc(odr,sizeof(*ext->u.single_ASN1_type));
296 ext->u.single_ASN1_type->buf= odr_malloc(odr, siz);
297 memcpy(ext->u.single_ASN1_type->buf,buf, siz );
298 ext->u.single_ASN1_type->len = ext->u.single_ASN1_type->size = siz;
300 odr_reset(odr_prt); /*!*/
302 e->identifier = odr_intdup(odr,1);
303 e->critical = odr_intdup(odr,0);
304 e->item=odr_malloc(odr,sizeof(*e->item));
305 if ( ! z_External(odr_ext, &ext,0,0) ) {
306 yaz_log(YLOG_FATAL,"Encoding of z_External failed ");
309 printf("External: \n");
310 z_External(odr_prt, &ext,0,0); /*!*/
311 buf= odr_getbuf(odr_ext,&siz,0);
312 e->item->buf= odr_malloc(odr, siz);
313 memcpy(e->item->buf,buf, siz );
314 e->item->len = e->item->size = siz;
316 odr_destroy(odr_prt);
317 odr_destroy(odr_ext);
320 } /* makeoclcextension */
322 ILL_APDU *createrequest( struct prog_args *args, ODR odr) {
323 struct ill_get_ctl ctl;
328 ctl.clientData = & args;
329 ctl.f = get_ill_element;
330 apdu = odr_malloc( odr, sizeof(*apdu) );
331 apdu->which=ILL_APDU_ILL_Request;
332 req = ill_get_ILLRequest(&ctl, "ill", 0);
333 apdu->u.illRequest=req;
334 req->num_iLL_request_extensions=2;
335 req->iLL_request_extensions=
336 odr_malloc(odr, req->num_iLL_request_extensions*sizeof(*req->iLL_request_extensions));
337 req->iLL_request_extensions[0]=makepromptextension(args,odr);
338 req->iLL_request_extensions[1]=makeoclcextension(args,odr);
340 yaz_log(YLOG_FATAL,"Could not create ill request");
344 } /* createrequest */
347 /* * * * * * * * * * * * * * * */
348 /** \brief Send the request */
349 void sendrequest(ILL_APDU *apdu, ODR odr, COMSTACK stack ) {
353 if (!ill_APDU (odr, &apdu, 0, 0)) {
354 yaz_log(YLOG_FATAL,"ill_Apdu failed");
357 buf_out = odr_getbuf(odr, &len_out, 0);
359 yaz_log(YLOG_DEBUG,"Request PDU Dump");
360 odr_dumpBER(yaz_log_file(), buf_out, len_out);
363 yaz_log(YLOG_FATAL,"Encoding failed. Len=%d", len_out);
364 odr_perror(odr, "encoding failed");
367 yaz_log(YLOG_DEBUG,"About to send the request. Len=%d", len_out);
368 res = cs_put(stack, buf_out, len_out);
370 yaz_log(YLOG_FATAL,"Could not send packet. code %d",res );
374 FILE *F = fopen("req.apdu","w");
375 fwrite ( buf_out, 1, len_out, F);
381 /* * * * * * * * * * * * * * * */
382 /** \brief Get a response */
383 ILL_APDU *getresponse( COMSTACK stack, ODR in_odr ){
388 yaz_log(YLOG_DEBUG,"About to wait for a response");
389 res = cs_get(stack, &buf_in, &len_in);
390 yaz_log(YLOG_DEBUG,"Got a response of %d bytes at %p. res=%d", len_in,buf_in, res);
392 yaz_log(YLOG_FATAL,"Could not receive packet. code %d",res );
393 yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...",
394 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
395 buf_in[4], buf_in[5], buf_in[6], buf_in[7] );
396 yaz_log(YLOG_DEBUG,"PDU Dump:");
397 odr_dumpBER(yaz_log_file(), buf_in, len_in);
400 odr_setbuf(in_odr, buf_in, res, 0);
401 if (!ill_APDU (in_odr, &resp, 0, 0))
404 int err = odr_geterrorx(in_odr, &x);
406 const char *element = odr_getelement(in_odr);
407 sprintf(msg, "ODR code %d:%d element=%-20s",
408 err, x, element ? element : "<unknown>");
409 yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
410 yaz_log(YLOG_DEBUG,"%02x %02x %02x %02x %02x %02x %02x %02x ...",
411 buf_in[0], buf_in[1], buf_in[2], buf_in[3],
412 buf_in[4], buf_in[5], buf_in[6], buf_in[7] );
413 yaz_log(YLOG_DEBUG,"PDU Dump:");
414 odr_dumpBER(yaz_log_file(), buf_in, len_in);
415 yaz_log(YLOG_FATAL,"Error decoding incoming packet: %s",msg);
422 /** \brief Dump a apdu */
423 void dumpapdu( ILL_APDU *apdu) {
424 ODR print_odr = odr_createmem(ODR_PRINT);
425 ill_APDU (print_odr, &apdu, 0, 0);
426 odr_destroy(print_odr);
429 /** \brief Check apdu type and extract the status_or_error */
430 ILL_Status_Or_Error_Report *getstaterr( ILL_APDU *resp, ODR in_odr ) {
431 if (resp->which != ILL_APDU_Status_Or_Error_Report ) {
432 const char *element = odr_getelement(in_odr);
435 printf("Server returned wrong packet type: %d\n", resp->which);
436 yaz_log(YLOG_FATAL,"Server returned a (%d) and "
437 "not a 'Status_Or_Error_Report' (%d) ",
438 resp->which, ILL_APDU_Status_Or_Error_Report);
441 return resp->u.Status_Or_Error_Report;
444 /** \brief Return a printable string from an ILL_String */
445 char *getillstring( ILL_String *s) {
446 if (s->which == ILL_String_GeneralString )
447 return s->u.GeneralString;
448 else if (s->which == ILL_String_EDIFACTString )
449 return s->u.EDIFACTString;
451 yaz_log(YLOG_FATAL,"Invalid ILL_String ");
456 /** \brief Check if the status was an error packet */
457 /* The presence of an error_report indicates it was an error */
458 /* Then the problem is to find the right message. We dig around */
459 /* until we find the first message, print that, and exit the program */
460 void checkerr( ILL_Status_Or_Error_Report *staterr ) {
461 yaz_log(YLOG_DEBUG, "err= %p ",staterr->error_report );
462 if (staterr->error_report) {
463 ILL_Error_Report *err= staterr->error_report;
464 if ( err->user_error_report) {
465 ILL_User_Error_Report *uerr= err->user_error_report;
466 switch( uerr->which ) {
467 case ILL_User_Error_Report_already_forwarded:
468 printf("Already forwarded: \n");
470 case ILL_User_Error_Report_intermediary_problem:
471 printf("Intermediary problem: %d\n",
472 *uerr->u.intermediary_problem);
474 case ILL_User_Error_Report_security_problem:
475 printf("Security problem: %s\n",
476 getillstring(uerr->u.security_problem));
478 case ILL_User_Error_Report_unable_to_perform:
479 printf("Unable to perform: %d\n",
480 *uerr->u.unable_to_perform);
483 printf("Unknown problem");
487 if ( err->provider_error_report) {
488 ILL_Provider_Error_Report *perr= err->provider_error_report;
489 switch( perr->which ) {
490 case ILL_Provider_Error_Report_general_problem:
491 printf("General Problem: %d\n",
492 *perr->u.general_problem);
494 case ILL_Provider_Error_Report_transaction_id_problem:
495 printf("Transaction Id Problem: %d\n",
496 *perr->u.general_problem);
498 case ILL_Provider_Error_Report_state_transition_prohibited:
499 printf("State Transition prohibited \n");
506 printf("%s", getillstring(staterr->note));
508 printf("Unknown error type");
515 /* * * * * * * * * * * * * * * */
517 /** \brief Main program
521 * Establish connection
529 int main (int argc, char * argv[]) {
530 struct prog_args args;
532 ODR out_odr = odr_createmem(ODR_ENCODE);
533 ODR in_odr = odr_createmem(ODR_DECODE);
536 ILL_Status_Or_Error_Report *staterr;
538 parseargs( argc, argv, &args);
540 stack = connect_to(args.host);
541 apdu = createrequest(&args, out_odr);
544 sendrequest(apdu, out_odr, stack );
545 resp = getresponse(stack, in_odr );
548 staterr=getstaterr(resp, in_odr);
552 printf ("Ok\n"); /* while debugging */
559 * indent-tabs-mode: nil
561 * vim: shiftwidth=4 tabstop=8 expandtab