Ursula stuff, mostly in the test client
[yazpp-moved-to-github.git] / src / yaz-my-client.cpp
1 /*
2  * Copyright (c) 1998-2000, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Log: yaz-my-client.cpp,v $
6  * Revision 1.7  2001-04-26 12:17:49  heikki
7  * Ursula stuff, mostly in the test client
8  *
9  * Revision 1.6  2001/04/17 16:21:21  heikki
10  * Working on UrsulaRenewal, Request, and Update
11  *
12  * Revision 1.5  2001/04/10 10:48:08  adam
13  * Fixed problem where proxy could cash bad result sets.
14  *
15  * Revision 1.4  2001/04/05 15:12:24  adam
16  * WIN32 updates.
17  *
18  * Revision 1.3  2001/04/05 13:09:44  adam
19  * Removed ursula dependancy.
20  *
21  * Revision 1.2  2001/04/04 14:02:49  adam
22  * URSULA / Z-ruth service.
23  *
24  * Revision 1.1  2001/03/27 14:47:45  adam
25  * New server facility scheme.
26  *
27  * Revision 1.17  2001/03/26 14:43:49  adam
28  * New threaded PDU association.
29  *
30  * Revision 1.16  2000/11/01 14:22:59  adam
31  * Added fd parameter for method IYaz_PDU_Observer::clone.
32  *
33  * Revision 1.15  2000/10/11 11:58:16  adam
34  * Moved header files to include/yaz++. Switched to libtool and automake.
35  * Configure script creates yaz++-config script.
36  *
37  * Revision 1.14  2000/09/08 10:23:42  adam
38  * Added skeleton of yaz-z-server.
39  *
40  * Revision 1.13  2000/09/06 14:23:45  adam
41  * WIN32 updates.
42  *
43  * Revision 1.12  2000/09/04 08:59:16  adam
44  * Changed call to logging functions (yaz_ added).
45  *
46  * Revision 1.11  2000/07/04 13:48:49  adam
47  * Implemented upper-limit on proxy-to-target sessions.
48  *
49  * Revision 1.10  2000/05/30 03:12:27  ian
50  * minor change to stop g++ 2.95.2 complaining about taking the address
51  * of a member function.
52  *
53  * Revision 1.9  1999/12/06 13:52:45  adam
54  * Modified for new location of YAZ header files. Experimental threaded
55  * operation.
56  *
57  * Revision 1.8  1999/11/10 10:02:34  adam
58  * Work on proxy.
59  *
60  * Revision 1.7  1999/04/21 12:09:01  adam
61  * Many improvements. Modified to proxy server to work with "sessions"
62  * based on cookies.
63  *
64  * Revision 1.6  1999/04/20 10:30:05  adam
65  * Implemented various stuff for client and proxy. Updated calls
66  * to ODR to reflect new name parameter.
67  *
68  * Revision 1.5  1999/04/09 11:46:57  adam
69  * Added object Yaz_Z_Assoc. Much more functional client.
70  *
71  * Revision 1.4  1999/03/23 14:17:57  adam
72  * More work on timeout handling. Work on yaz-client.
73  *
74  * Revision 1.3  1999/02/02 14:01:18  adam
75  * First WIN32 port of YAZ++.
76  *
77  * Revision 1.2  1999/01/28 13:08:42  adam
78  * Yaz_PDU_Assoc better encapsulated. Memory leak fix in
79  * yaz-socket-manager.cc.
80  *
81  * Revision 1.1.1.1  1999/01/28 09:41:07  adam
82  * First implementation of YAZ++.
83  *
84  */
85
86 #include <yaz/log.h>
87 #include <yaz/options.h>
88 #include <yaz/diagbib1.h>
89 #include <yaz/marcdisp.h>
90 #include <yaz++/yaz-ir-assoc.h>
91 #include <yaz++/yaz-pdu-assoc.h>
92 #include <yaz++/yaz-socket-manager.h>
93
94 #if HAVE_YAZ_URSULA_H
95 #include <yaz/zes-ursula.h>
96 #endif
97
98 extern "C" {
99 #if HAVE_READLINE_READLINE_H
100 #include <readline/readline.h>
101 #endif
102 #if HAVE_READLINE_HISTORY_H
103 #include <readline/history.h>
104 #endif
105 }
106
107 class YAZ_EXPORT MyClient : public Yaz_IR_Assoc {
108 private:
109     int m_interactive_flag;
110     char m_thisCommand[1024];
111     char m_lastCommand[1024];
112     int m_setOffset;
113     Yaz_SocketManager *m_socketManager;
114 public:
115     MyClient(IYaz_PDU_Observable *the_PDU_Observable,
116              Yaz_SocketManager *the_SocketManager);
117     IYaz_PDU_Observer *sessionNotify(
118         IYaz_PDU_Observable *the_PDU_Observable, int fd);
119     int args(Yaz_SocketManager *socketManager, int argc, char **argv);
120     int interactive(Yaz_SocketManager *socketManager);
121     int wait();
122     void recv_initResponse(Z_InitResponse *initResponse);
123     void recv_searchResponse(Z_SearchResponse *searchResponse);
124     void recv_presentResponse(Z_PresentResponse *presentResponse);
125     void recv_records (Z_Records *records);
126     void recv_diagrecs(Z_DiagRec **pp, int num);
127     void recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset);
128     void recv_record(Z_DatabaseRecord *record, int offset,
129                      const char *databaseName);
130     void recv_textRecord(int type, const char *buf, size_t len);
131     void recv_genericRecord(Z_GenericRecord *r);
132 #if HAVE_YAZ_URSULA_H
133     void recv_extendedServicesResponse(Z_ExtendedServicesResponse *extendedServicesResponse);
134 #endif
135     void display_genericRecord(Z_GenericRecord *r, int level);
136     void display_variant(Z_Variant *v, int level);
137     void connectNotify();
138     void failNotify();
139     void timeoutNotify();
140     char *get_cookie (Z_OtherInformation **oi);
141     int processCommand(const char *cmd);
142     const char *MyClient::getCommand();
143     int cmd_open(char *host);
144     int cmd_connect(char *host);
145     int cmd_quit(char *args);
146     int cmd_close(char *args);
147     int cmd_find(char *args);
148     int cmd_show(char *args);
149     int cmd_cookie(char *args);
150     int cmd_init(char *args);
151     int cmd_format(char *args);
152     int cmd_proxy(char *args);
153 #if HAVE_YAZ_URSULA_H
154     int cmd_ursula(char *args);
155     int cmd_ursula_renew(char *args);
156 #endif
157 };
158
159
160 void MyClient::connectNotify()
161 {
162     printf ("Connection accepted by target\n");
163     set_lastReceived(-1);
164 }
165
166 void MyClient::timeoutNotify()
167 {
168     printf ("Connection timeout\n");
169     close();
170 }
171
172 void MyClient::failNotify()
173 {
174     printf ("Connection closed by target\n");
175     set_lastReceived(-1);
176 }
177
178 IYaz_PDU_Observer *MyClient::sessionNotify(
179     IYaz_PDU_Observable *the_PDU_Observable, int fd)
180
181     return new MyClient(the_PDU_Observable, m_socketManager);
182 }
183
184 MyClient::MyClient(IYaz_PDU_Observable *the_PDU_Observable,
185                    Yaz_SocketManager *the_socketManager) :
186     Yaz_IR_Assoc (the_PDU_Observable)
187 {
188     m_setOffset = 1;
189     m_interactive_flag = 1;
190     m_thisCommand[0] = '\0';
191     m_lastCommand[0] = '\0';
192     m_socketManager = the_socketManager;
193 }
194
195 void usage(char *prog)
196 {
197     fprintf (stderr, "%s: [-v log] [-c cookie] [-p proxy] [zurl]\n", prog);
198     exit (1);
199 }
200
201 char *MyClient::get_cookie(Z_OtherInformation **otherInfo)
202 {
203     int oid[OID_SIZE];
204     Z_OtherInformationUnit *oi;
205     struct oident ent;
206     ent.proto = PROTO_Z3950;
207     ent.oclass = CLASS_USERINFO;
208     ent.value = (oid_value) VAL_COOKIE;
209
210     if (oid_ent_to_oid (&ent, oid) && 
211         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
212         oi->which == Z_OtherInfo_characterInfo)
213         return oi->information.characterInfo;
214     return 0;
215 }
216
217 void MyClient::recv_initResponse(Z_InitResponse *initResponse)
218 {
219     printf ("Got InitResponse. Status ");
220     if (*initResponse->result)
221     {
222         printf ("Ok\n");
223
224         const char *p = get_cookie (&initResponse->otherInfo);
225         if (p)
226         {
227             printf ("cookie = %s\n", p);
228             set_cookie(p);
229         }
230     }
231     else
232         printf ("Fail\n");
233 }
234
235 void MyClient::recv_diagrecs(Z_DiagRec **pp, int num)
236 {
237     int i;
238     oident *ent;
239     Z_DefaultDiagFormat *r;
240
241     printf("Diagnostic message(s) from database:\n");
242     for (i = 0; i<num; i++)
243     {
244         Z_DiagRec *p = pp[i];
245         if (p->which != Z_DiagRec_defaultFormat)
246         {
247             printf("Diagnostic record not in default format.\n");
248             return;
249         }
250         else
251             r = p->u.defaultFormat;
252         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
253             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
254             printf("Missing or unknown diagset\n");
255         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
256 #ifdef ASN_COMPILED
257         switch (r->which)
258         {
259         case Z_DefaultDiagFormat_v2Addinfo:
260             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
261             break;
262         case Z_DefaultDiagFormat_v3Addinfo:
263             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
264             break;
265         }
266 #else
267         if (r->addinfo && *r->addinfo)
268             printf(" -- '%s'\n", r->addinfo);
269         else
270             printf("\n");
271 #endif
272     }
273 }
274
275 void MyClient::recv_textRecord(int type, const char *buf, size_t len)
276 {
277     fwrite (buf, 1, len, stdout);
278     fputc ('\n', stdout);
279 }
280
281 void MyClient::display_variant(Z_Variant *v, int level)
282 {
283     int i;
284
285     for (i = 0; i < v->num_triples; i++)
286     {
287         printf("%*sclass=%d,type=%d", level * 4, "", *v->triples[i]->zclass,
288             *v->triples[i]->type);
289         if (v->triples[i]->which == Z_Triple_internationalString)
290             printf(",value=%s\n", v->triples[i]->value.internationalString);
291         else
292             printf("\n");
293     }
294 }
295
296 void MyClient::display_genericRecord(Z_GenericRecord *r, int level)
297 {
298     int i;
299
300     if (!r)
301         return;
302     for (i = 0; i < r->num_elements; i++)
303     {
304         Z_TaggedElement *t;
305
306         printf("%*s", level * 4, "");
307         t = r->elements[i];
308         printf("(");
309         if (t->tagType)
310             printf("%d,", *t->tagType);
311         else
312             printf("?,");
313         if (t->tagValue->which == Z_StringOrNumeric_numeric)
314             printf("%d) ", *t->tagValue->u.numeric);
315         else
316             printf("%s) ", t->tagValue->u.string);
317         if (t->content->which == Z_ElementData_subtree)
318         {
319             printf("\n");
320             display_genericRecord(t->content->u.subtree, level+1);
321         }
322         else if (t->content->which == Z_ElementData_string)
323             printf("%s\n", t->content->u.string);
324         else if (t->content->which == Z_ElementData_numeric)
325             printf("%d\n", *t->content->u.numeric);
326         else if (t->content->which == Z_ElementData_oid)
327         {
328             int *ip = t->content->u.oid;
329             oident *oent;
330
331             if ((oent = oid_getentbyoid(t->content->u.oid)))
332                 printf("OID: %s\n", oent->desc);
333             else
334             {
335                 printf("{");
336                 while (ip && *ip >= 0)
337                     printf(" %d", *(ip++));
338                 printf(" }\n");
339             }
340         }
341         else if (t->content->which == Z_ElementData_noDataRequested)
342             printf("[No data requested]\n");
343         else if (t->content->which == Z_ElementData_elementEmpty)
344             printf("[Element empty]\n");
345         else if (t->content->which == Z_ElementData_elementNotThere)
346             printf("[Element not there]\n");
347         else
348             printf("??????\n");
349         if (t->appliedVariant)
350             display_variant(t->appliedVariant, level+1);
351         if (t->metaData && t->metaData->supportedVariants)
352         {
353             int c;
354
355             printf("%*s---- variant list\n", (level+1)*4, "");
356             for (c = 0; c < t->metaData->num_supportedVariants; c++)
357             {
358                 printf("%*svariant #%d\n", (level+1)*4, "", c);
359                 display_variant(t->metaData->supportedVariants[c], level + 2);
360             }
361         }
362     }
363 }
364
365 void MyClient::recv_genericRecord(Z_GenericRecord *r)
366 {
367     display_genericRecord(r, 0);
368 }
369
370 void MyClient::recv_record(Z_DatabaseRecord *record, int offset,
371                            const char *databaseName)
372 {
373     Z_External *r = (Z_External*) record;
374     oident *ent = oid_getentbyoid(r->direct_reference);
375
376     /*
377      * Tell the user what we got.
378      */
379     if (r->direct_reference)
380     {
381         printf("Record type: ");
382         if (ent)
383             printf("%s\n", ent->desc);
384     }
385     /* Check if this is a known, ASN.1 type tucked away in an octet string */
386     Z_ext_typeent *etype = z_ext_getentbyref(ent->value);
387     if (ent && (r->which == Z_External_octet || r->which == Z_External_single)
388         && (etype = z_ext_getentbyref(ent->value)))
389
390     {
391         void *rr;
392         /*
393          * Call the given decoder to process the record.
394          */
395         odr_setbuf(odr_decode(), (char*)record->u.octet_aligned->buf,
396                    record->u.octet_aligned->len, 0);
397         if (!(*etype->fun)(odr_decode(), (char **)&rr, 0, 0))
398         {
399             odr_perror(odr_decode(), "Decoding constructed record.");
400             fprintf(stderr, "[Near %d]\n", odr_offset(odr_decode()));
401             fprintf(stderr, "Packet dump:\n---------\n");
402             odr_dumpBER(stderr, (char*)record->u.octet_aligned->buf,
403                         record->u.octet_aligned->len);
404             fprintf(stderr, "---------\n");
405         }
406         if (etype->what == Z_External_sutrs)
407         {
408             Z_SUTRS *sutrs = (Z_SUTRS *) rr;
409             recv_textRecord ((int) VAL_SUTRS, (const char *) sutrs->buf,
410                              (size_t) sutrs->len);
411         }
412         return;
413     }
414     if (r->which == Z_External_octet && record->u.octet_aligned->len)
415     {
416         switch (ent->value)
417         {
418         case VAL_ISO2709:
419         case VAL_UNIMARC:
420         case VAL_INTERMARC:
421         case VAL_USMARC:
422         case VAL_UKMARC:
423         case VAL_NORMARC:
424         case VAL_LIBRISMARC:
425         case VAL_DANMARC:
426         case VAL_FINMARC:
427         case VAL_MAB:
428         case VAL_CANMARC:
429         case VAL_SBN:
430         case VAL_PICAMARC:
431         case VAL_AUSMARC:
432         case VAL_IBERMARC:
433         case VAL_CATMARC:
434         case VAL_MALMARC:
435         case VAL_JPMARC:
436         case VAL_SWEMARC:
437         case VAL_SIGLEMARC:
438         case VAL_ISDSMARC:
439         case VAL_RUSMARC:
440             marc_display((char*) record->u.octet_aligned->buf,0);
441             break;
442         default:
443             recv_textRecord((int) ent->value,
444                             (const char *) record->u.octet_aligned->buf,
445                             (size_t) record->u.octet_aligned->len);
446         }
447     }
448     else if (ent && ent->value == VAL_SUTRS && r->which == Z_External_sutrs)
449         recv_textRecord((int) VAL_SUTRS, (const char *) r->u.sutrs->buf,
450                         (size_t) r->u.sutrs->len);
451     else if (ent && ent->value == VAL_GRS1 && r->which == Z_External_grs1)
452         recv_genericRecord(r->u.grs1);
453     else 
454     {
455         printf("Unknown record representation.\n");
456         if (!z_External(odr_print(), &r, 0, 0))
457         {
458             odr_perror(odr_print(), "Printing external");
459             odr_reset(odr_print());
460         }
461     }    
462 }
463
464 void MyClient::recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset)
465 {
466     if (zpr->databaseName)
467         printf("[%s]", zpr->databaseName);
468     if (zpr->which == Z_NamePlusRecord_surrogateDiagnostic)
469         recv_diagrecs(&zpr->u.surrogateDiagnostic, 1);
470     else
471         recv_record(zpr->u.databaseRecord, offset, zpr->databaseName);
472 }
473
474 void MyClient::recv_records (Z_Records *records)
475 {
476 #ifdef ASN_COMPILED
477     Z_DiagRec dr, *dr_p = &dr;
478 #endif
479     if (!records)
480         return;
481     int i;
482     switch (records->which)
483     {
484     case Z_Records_DBOSD:
485         for (i = 0; i < records->u.databaseOrSurDiagnostics->num_records; i++)
486             recv_namePlusRecord(records->u.databaseOrSurDiagnostics->
487                                 records[i], i + m_setOffset);
488         m_setOffset += records->u.databaseOrSurDiagnostics->num_records;
489         break;
490     case Z_Records_NSD:
491 #ifdef ASN_COMPILED
492         dr.which = Z_DiagRec_defaultFormat;
493         dr.u.defaultFormat = records->u.nonSurrogateDiagnostic;
494         recv_diagrecs (&dr_p, 1);
495 #else
496         recv_diagrecs (&records->u.nonSurrogateDiagnostic, 1);
497 #endif
498         break;
499     case Z_Records_multipleNSD:
500         recv_diagrecs (records->u.multipleNonSurDiagnostics->diagRecs,
501                        records->u.multipleNonSurDiagnostics->num_diagRecs);
502         break;
503     }
504 }
505
506 void MyClient::recv_searchResponse(Z_SearchResponse *searchResponse)
507 {
508     printf ("Got SearchResponse. Status ");
509     if (!*searchResponse->searchStatus)
510     {
511         printf ("Fail\n");
512     }
513     else
514     {
515         printf ("Ok\n");
516         printf ("Hits: %d\n", *searchResponse->resultCount);
517     }
518     recv_records (searchResponse->records);
519 }
520
521 void MyClient::recv_presentResponse(Z_PresentResponse *presentResponse)
522 {
523     printf ("Got PresentResponse\n");
524     recv_records (presentResponse->records);
525 }
526
527 #if HAVE_YAZ_URSULA_H
528 void MyClient::recv_extendedServicesResponse(Z_ExtendedServicesResponse *extendedServicesResponse)
529 {
530     printf("Got ESresponse\n");
531     printf(" OperationStatus=%d with %d diagnostics:\n", 
532         *extendedServicesResponse->operationStatus,
533          extendedServicesResponse->num_diagnostics);
534     recv_diagrecs(extendedServicesResponse->diagnostics,
535                   extendedServicesResponse->num_diagnostics);
536     //TODO: Add more info !
537 }
538 #endif
539
540 int MyClient::wait()
541 {
542     set_lastReceived(0);
543     while (m_socketManager->processEvent() > 0)
544     {
545         if (get_lastReceived())
546             return 1;
547     }
548     return 0;
549 }
550
551
552 #define C_PROMPT "Z>"
553
554 int MyClient::cmd_connect(char *host)
555 {
556     client (host);
557     timeout (10);
558     wait ();
559     timeout (0);
560     return 1;
561 }
562
563 int MyClient::cmd_open(char *host)
564 {
565     client (host);
566     timeout (10);
567     wait ();
568     timeout (0);
569     send_initRequest();
570     wait ();
571     return 1;
572 }
573
574 int MyClient::cmd_init(char *args)
575 {
576     if (send_initRequest() >= 0)
577         wait();
578     else
579         close();
580     return 1;
581 }
582
583 int MyClient::cmd_quit(char *args)
584 {
585     return 0;
586 }
587
588 int MyClient::cmd_close(char *args)
589 {
590     close();
591     return 1;
592 }
593
594 int MyClient::cmd_find(char *args)
595 {
596     Yaz_Z_Query query;
597
598     if (query.set_rpn(args) <= 0)
599     {
600         printf ("Bad RPN query\n");
601         return 1;
602     }
603     if (send_searchRequest(&query) >= 0)
604         wait();
605     else
606         printf ("Not connected\n");
607     return 1;
608 }
609
610 int MyClient::cmd_show(char *args)
611 {
612     int start = m_setOffset, number = 1;
613
614     sscanf (args, "%d %d", &start, &number);
615     m_setOffset = start;
616     if (send_presentRequest(start, number) >= 0)
617         wait();
618     else
619         printf ("Not connected\n");
620     return 1;
621 }
622
623 int MyClient::cmd_cookie(char *args)
624 {
625     set_cookie(*args ? args : 0);
626     return 1;
627 }
628
629 int MyClient::cmd_format(char *args)
630 {
631     set_preferredRecordSyntax(args);
632     return 1;
633 }
634
635 int MyClient::cmd_proxy(char *args)
636 {
637     set_proxy(args);
638     return 1;
639 }
640
641 #if HAVE_YAZ_URSULA_H
642 int MyClient::cmd_ursula(char *args)
643 {
644     Z_APDU *apdu = create_Z_PDU(Z_APDU_extendedServicesRequest);
645     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
646
647     req->packageType = odr_getoidbystr(odr_encode(), "1.2.840.10003");
648 //  req->packageType = odr_getoidbystr(odr_encode(), "1.2.840.10003.9.1000.105.3");
649     
650     Z_External *ext = (Z_External *) odr_malloc(odr_encode(), sizeof(*ext));
651     req->taskSpecificParameters = ext;
652     ext->direct_reference = req->packageType;
653     ext->descriptor = 0;
654     ext->indirect_reference = 0;
655     
656     ext->which = Z_External_octet;
657     ext->u.single_ASN1_type = (Odr_oct *)
658         odr_malloc (odr_encode(), sizeof(Odr_oct));
659
660     Z_UrsPDU *pdu = (Z_UrsPDU *) odr_malloc (odr_encode(), sizeof(*pdu));
661     pdu->which = Z_UrsPDU_request;
662     pdu->u.request = (Z_UrsRequest *)
663         odr_malloc (odr_encode(), sizeof(*pdu->u.request));
664     pdu->u.request->libraryNo = odr_strdup(odr_encode(), "000200");
665     pdu->u.request->borrowerTickerNo = 0;
666     pdu->u.request->disposalType = 0;
667     pdu->u.request->lastUseDate = 0;
668     pdu->u.request->num_items = 0;
669     pdu->u.request->items = (Z_UrsRequestItem **) odr_nullval();
670     pdu->u.request->counter = 0;
671     pdu->u.request->priority = 0;
672     pdu->u.request->disposalNote = 0;
673     pdu->u.renewal->overrule=(bool_t*)odr_malloc(odr_encode(),sizeof(bool_t));
674     pdu->u.request->overrule = 0;
675
676     if (!z_UrsPDU (odr_encode(), &pdu, 0, ""))
677     {
678         yaz_log (LOG_LOG, "ursula encoding failed");
679         return 1;
680     }
681     char *buf = 
682         odr_getbuf (odr_encode(), &ext->u.single_ASN1_type->len, 0);
683     
684     ext->u.single_ASN1_type->buf = (unsigned char*)
685         odr_malloc (odr_encode(), ext->u.single_ASN1_type->len);
686     memcpy (ext->u.single_ASN1_type->buf, buf, ext->u.single_ASN1_type->len);
687     ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len;
688     
689     if (send_Z_PDU(apdu) >= 0)
690         wait();
691     return 1;
692 }
693
694 int MyClient::cmd_ursula_renew(char *args)
695 {
696     Z_APDU *apdu = create_Z_PDU(Z_APDU_extendedServicesRequest);
697     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
698
699     req->packageType = odr_getoidbystr(odr_encode(), "1.2.840.10003");
700     
701     Z_External *ext = (Z_External *) odr_malloc(odr_encode(), sizeof(*ext));
702     req->taskSpecificParameters = ext;
703     ext->direct_reference = req->packageType;
704     ext->descriptor = 0;
705     ext->indirect_reference = 0;
706     
707     ext->which = Z_External_octet;
708     ext->u.single_ASN1_type = (Odr_oct *)
709         odr_malloc (odr_encode(), sizeof(Odr_oct));
710
711     Z_UrsPDU *pdu = (Z_UrsPDU *) odr_malloc (odr_encode(), sizeof(*pdu));
712     pdu->which = Z_UrsPDU_renewal;
713     pdu->u.renewal = (Z_UrsRenewal *)
714            odr_malloc (odr_encode(), sizeof(*pdu->u.renewal));
715     pdu->u.renewal->libraryNo = odr_strdup(odr_encode(), "000200");
716     pdu->u.renewal->borrowerTicketNo = odr_strdup(odr_encode(),"123456");
717     pdu->u.renewal->num_copies=1;
718     pdu->u.renewal->copies = (Z_InternationalString **)
719             odr_malloc(odr_encode(),1* sizeof(Z_InternationalString *) );
720     pdu->u.renewal->copies[0]= odr_strdup(odr_encode(), "firstcopy");
721     pdu->u.renewal->newReturnDate=odr_strdup(odr_encode(), "20011224");
722     pdu->u.renewal->overrule=(bool_t*)odr_malloc(odr_encode(),sizeof(bool_t));
723     *pdu->u.renewal->overrule=false;
724
725     if (!z_UrsPDU (odr_encode(), &pdu, 0, ""))
726     {
727         yaz_log (LOG_LOG, "ursula encoding failed");
728         return 1;
729     }
730     char *buf = 
731         odr_getbuf (odr_encode(), &ext->u.single_ASN1_type->len, 0);
732     
733     ext->u.single_ASN1_type->buf = (unsigned char*)
734         odr_malloc (odr_encode(), ext->u.single_ASN1_type->len);
735     memcpy (ext->u.single_ASN1_type->buf, buf, ext->u.single_ASN1_type->len);
736     ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len;
737     
738     if (send_Z_PDU(apdu) >= 0)
739         wait();
740     return 1;
741 }
742
743
744 #endif
745
746 int MyClient::processCommand(const char *commandLine)
747 {
748     char cmdStr[1024], cmdArgs[1024];
749     cmdArgs[0] = '\0';
750     cmdStr[0] = '\0';
751     static struct {
752         char *cmd;
753         int (MyClient::*fun)(char *arg);
754         char *ad;
755     } cmd[] = {
756         {"open", &MyClient::cmd_open, "<host>[':'<port>][/<database>]"},
757         {"connect", &MyClient::cmd_connect, "<host>[':'<port>][/<database>]"},
758         {"quit", &MyClient::cmd_quit, ""},
759         {"close", &MyClient::cmd_close, ""},
760         {"find", &MyClient::cmd_find, "<query>"},
761         {"show", &MyClient::cmd_show, "[<start> [<number>]]"},
762         {"cookie", &MyClient::cmd_cookie, "<cookie>"},
763         {"init", &MyClient::cmd_init, ""},
764         {"format", &MyClient::cmd_format, "<record-syntax>"},
765         {"proxy", &MyClient::cmd_proxy, "<host>:[':'<port>]"},
766 #if HAVE_YAZ_URSULA_H
767         {"ursula", &MyClient::cmd_ursula, ""},
768         {"ursula_request", &MyClient::cmd_ursula, ""},
769         {"ursreq", &MyClient::cmd_ursula, ""},
770         {"ursnew", &MyClient::cmd_ursula_renew, ""},
771         {"ursula_renew", &MyClient::cmd_ursula_renew, ""},
772 #endif
773         {0,0,0}
774     };
775     
776     if (sscanf(commandLine, "%s %[^;]", cmdStr, cmdArgs) < 1)
777         return 1;
778     int i;
779     for (i = 0; cmd[i].cmd; i++)
780         if (!strncmp(cmd[i].cmd, cmdStr, strlen(cmdStr)))
781             break;
782     
783     int res = 1;
784     if (cmd[i].cmd) // Invoke command handler
785         res = (this->*cmd[i].fun)(cmdArgs);
786     else            // Dump help screen
787     {
788         printf("Unknown command: %s.\n", cmdStr);
789         printf("Currently recognized commands:\n");
790         for (i = 0; cmd[i].cmd; i++)
791             printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
792     }
793     return res;
794 }
795
796 const char *MyClient::getCommand()
797 {
798 #if HAVE_READLINE_READLINE_H
799     // Read using GNU readline
800     char *line_in;
801     line_in=readline(C_PROMPT);
802     if (!line_in)
803         return 0;
804 #if HAVE_READLINE_HISTORY_H
805     if (*line_in)
806         add_history(line_in);
807 #endif
808     strncpy(m_thisCommand,line_in, 1023);
809     m_thisCommand[1023] = '\0';
810     free (line_in);
811 #else    
812     // Read using fgets(3)
813     printf (C_PROMPT);
814     fflush(stdout);
815     if (!fgets(m_thisCommand, 1023, stdin))
816         return 0;
817 #endif
818     // Remove trailing whitespace
819     char *cp = m_thisCommand + strlen(m_thisCommand);
820     while (cp != m_thisCommand && strchr("\t \n", cp[-1]))
821         cp--;
822     *cp = '\0';
823     cp = m_thisCommand;
824     // Remove leading spaces...
825     while (*cp && strchr ("\t \n", *cp))
826         cp++;
827     // Save command if non-empty
828     if (*cp != '\0')
829         strcpy (m_lastCommand, cp);
830     return m_lastCommand;
831 }
832
833 int MyClient::interactive(Yaz_SocketManager *socketManager)
834 {
835     const char *cmd;
836     if (!m_interactive_flag)
837         return 0;
838     while ((cmd = getCommand()))
839     {
840         if (!processCommand(cmd))
841             break;
842     }
843     return 0;
844 }
845
846 int MyClient::args(Yaz_SocketManager *socketManager, int argc, char **argv)
847 {
848     char *host = 0;
849     char *proxy = 0;
850     char *arg;
851     char *prog = argv[0];
852     int ret;
853
854     while ((ret = options("c:p:v:q", argv, argc, &arg)) != -2)
855     {
856         switch (ret)
857         {
858         case 0:
859             if (host)
860             {
861                 usage(prog);
862                 return 1;
863             }
864             host = arg;
865             break;
866         case 'p':
867             if (proxy)
868             {
869                 usage(prog);
870                 return 1;
871             }
872             set_proxy(arg);
873             break;
874         case 'c':
875             set_cookie(arg);
876             break;
877         case 'v':
878             yaz_log_init_level (yaz_log_mask_str(arg));
879             break;
880         case 'q':
881             m_interactive_flag = 0;
882             break;
883         default:
884             usage(prog);
885             return 1;
886         }
887     }
888     if (host)
889     {
890         client (host);
891         timeout (10);
892         wait ();
893         timeout (0);
894         send_initRequest();
895         wait ();
896     }
897     return 0;
898 }
899
900 int main(int argc, char **argv)
901 {
902     Yaz_SocketManager mySocketManager;
903     Yaz_PDU_Assoc *some = new Yaz_PDU_Assoc(&mySocketManager);
904
905     MyClient z(some, &mySocketManager);
906
907     if (z.args(&mySocketManager, argc, argv))
908         exit (1);
909     if (z.interactive(&mySocketManager))
910         exit (1);
911     return 0;
912 }