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