Work on smarter zlint OO model
[yazpp-moved-to-github.git] / zlint / zlint.cpp
1 /*
2  * Copyright (c) 2004, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: zlint.cpp,v 1.3 2004-02-26 23:41:58 adam Exp $
6  */
7
8 #include <yaz/pquery.h>
9 #include <yaz/options.h>
10 #include <yaz/otherinfo.h>
11 #include <yaz/charneg.h>
12 #include <yaz/sortspec.h>
13 #include <yaz/log.h>
14 #include <yaz++/pdu-assoc.h>
15 #include <yaz++/socket-manager.h>
16 #include <yaz++/z-assoc.h>
17
18 #define REFID_BUF1 "zlint\000check1"
19 #define REFID_LEN1 12
20 #define REFID_BUF2 "zlint\000check2"
21 #define REFID_LEN2 12
22
23 enum Zlint_code {
24     TEST_FINISHED,
25     TEST_CONTINUE,
26 };
27
28 class Zlint;
29     
30 class Zlint_test {
31 public:
32     virtual Zlint_code init(Zlint *z) = 0;
33     virtual Zlint_code recv_gdu(Zlint *z, Z_GDU *gdu) = 0;
34     virtual Zlint_code recv_fail(Zlint *z, int reason) = 0;
35 };
36
37 const char *try_syntax [] = {
38     "usmarc",
39     "unimarc",
40     "danmarc",
41     "sutrs",
42     "grs1",
43     "xml",
44     "normarc",
45     0
46 };
47 const char *try_query[] = {
48     "@attr 1=4 petersson",
49     "@attr 1=1016 petersson",
50     "@attr 1=4 kingdom",
51     "@attr 1=1016 kingdom",
52     "@attr 1=62 sword",
53     "sword"
54     "seven",
55     "@attr 1=4 water",
56     "@attr 1=1016 water",
57     "computer",
58     "@attr 1=4 computer",
59     "@attr 1=1016 computer",
60     "water",
61     "join",
62     "about",
63     "map",
64     0,
65 };
66
67 const char *try_sort [] = {
68     "1=4 <",
69     "1=4 >",
70     "1=62 >",
71     "1=62 <",
72     0
73 };
74 const char *try_scan [] = {
75     "@attr 1=4 ab",
76     "@attr 1=1003 ab",
77     "@attr 1=1016 ab",
78     0
79 };
80
81 #define TEST_STATE_FAIL 0
82 #define TEST_STATE_UNKNOWN 1
83 #define TEST_STATE_OK   2
84 #define TEST_STATE_NOT_APPLIC 3
85
86 class Zlint : public Yaz_Z_Assoc {
87     int m_tst_no;
88
89     int m_subtst_no;
90
91     int m_test_state;
92     int m_query_no;
93     int m_scan_no;
94     int m_sort_no;
95     int m_record_syntax_no;
96     int m_got_result_set;
97     IYaz_PDU_Observable *m_PDU_Observable;
98     char *m_host;
99     char *m_database;
100     int m_timeout_init;
101     int m_timeout_connect;
102     int m_protocol_version;
103     char m_session_str[20];
104 public:
105     int initResponseGetVersion(Z_InitResponse *init);
106     void prepare();
107     void recv_GDU(Z_GDU *apdu, int len);
108     Zlint(IYaz_PDU_Observable *the_PDU_Observable);
109     void args(int argc, char **argv);
110     void connectNotify();
111     void failNotify();
112     void closeNextTest();
113     void sendTest();
114     int nextTest();
115     void testContinue();
116     void timeoutNotify();
117     IYaz_PDU_Observer *sessionNotify(
118         IYaz_PDU_Observable *the_PDU_Observable, int fd);
119     void connect();
120     Z_ReferenceId *mk_refid(const char *buf, int len);
121 };
122
123 int Zlint::initResponseGetVersion(Z_InitResponse *init)
124 {
125     int no = 0;
126     int off = 0;
127     int i;
128     for (i = 0; i<12; i++)
129         if (ODR_MASK_GET(init->protocolVersion, no))
130         {
131             no = i+1;
132             if (off)
133                 yaz_log(LOG_WARN, "%sbad formatted version");
134         }
135         else
136             off = 1;
137     return no;
138 }
139
140 Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
141 {
142     Z_ReferenceId *id = 
143         (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
144     id->size = id->len = len;
145     id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
146     memcpy(id->buf, buf, len);
147     return id;
148 }
149
150 void Zlint::recv_GDU(Z_GDU *gdu, int len)
151 {
152     if (gdu->which != Z_GDU_Z3950)
153     {
154         yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
155         closeNextTest();
156     }
157     if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
158     {
159         int i;
160         Z_InitResponse *init = gdu->u.z3950->u.initResponse;
161         int ver = initResponseGetVersion(init);
162         int result = init->result ? *init->result : 0;
163         if (!result)
164             yaz_log(LOG_WARN, "%sinit rejected");
165         switch(m_tst_no)
166         {
167         case 0:
168             if (ver > 3 || ver < 2)
169                 yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
170                         m_session_str, ver);
171             else
172                 m_test_state = TEST_STATE_OK;
173             m_protocol_version = ver;
174             if (!result)
175                 closeNextTest();
176             else
177             {
178                 close();
179                 nextTest();
180             }
181             break;
182         case 1:
183             if (ver != 2)
184                 yaz_log(LOG_WARN, "%sgot version %d, expected 2",
185                         m_session_str, ver);
186             else
187                 m_test_state = TEST_STATE_OK;
188             closeNextTest();
189             break;
190         case 2:
191             if (ver < 2 || ver > 5)
192                 yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
193                         m_session_str,ver);
194             else
195                 m_test_state = TEST_STATE_OK;
196             closeNextTest();
197             break;
198         case 3:
199             if (!init->referenceId)
200                 yaz_log(LOG_WARN, "%smissing referenceID from init response",
201                         m_session_str);
202             else if (init->referenceId->len != REFID_LEN1
203                      || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
204                 yaz_log(LOG_WARN, "%sreference ID does not match");
205             else
206                 m_test_state = TEST_STATE_OK;
207             closeNextTest();
208             break;
209         case 4:
210             if (m_subtst_no == 0)
211             {
212                 if (!init->referenceId)
213                     yaz_log(LOG_WARN, "%smissing referenceID from first init response",
214                             m_session_str);
215                 else if (init->referenceId->len != REFID_LEN1
216                          || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
217                     yaz_log(LOG_WARN, "%sreference ID does not match");
218                 m_subtst_no++;
219             }
220             else
221             {
222                 if (!init->referenceId)
223                     yaz_log(LOG_WARN, "%smissing referenceID from second init response",
224                             m_session_str);
225                 else if (init->referenceId->len != REFID_LEN2
226                          || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
227                     yaz_log(LOG_WARN, "%sreference ID does not match");
228                 else
229                     m_test_state = TEST_STATE_OK;
230                 closeNextTest();
231             }       
232             break;
233         case 5:
234             if (init->options)
235             {
236                 int i;
237                 int no_set = 0;
238                 int no_reset = 0;
239                 for (i = 0; i <= 24; i++)
240                     if (ODR_MASK_GET(init->options, i))
241                         no_set++;
242                     else
243                         no_reset++;
244                 if (no_set < 2)
245                     yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
246                             m_session_str);
247                 if (no_reset == 0)
248                     yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
249                             m_session_str);
250                 if (no_set >= 2 && no_reset)
251                     m_test_state = TEST_STATE_OK;
252             }
253             closeNextTest();
254             break;
255         case 6:
256             if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
257             {
258                 Z_CharSetandLanguageNegotiation *p =
259                     yaz_get_charneg_record(init->otherInfo);
260                 
261                 if (p) {
262                     
263                     char *charset=NULL, *lang=NULL;
264                     int selected;
265                     NMEM m = nmem_create();
266                     
267                     yaz_get_response_charneg(m, p, &charset, &lang,
268                                              &selected);
269                     yaz_log(LOG_DEBUG, "%sAccepted character set : %s",
270                             m_session_str, charset);
271                     yaz_log(LOG_DEBUG, "%sAccepted code language : %s",
272                             m_session_str, lang ? lang : "none");
273                     yaz_log(LOG_DEBUG, "%sAccepted records in ...: %d",
274                             m_session_str, selected );
275                     nmem_destroy(m);
276                     m_test_state = TEST_STATE_OK;
277                 }
278             }
279             else
280                 m_test_state = TEST_STATE_NOT_APPLIC;
281             closeNextTest();
282             break;
283         case 7:
284             if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
285             {
286                 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
287                 closeNextTest();
288             }
289             else if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
290             {
291                 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
292                 closeNextTest();
293             }
294             else if (m_subtst_no < 3)
295             {
296                 close();
297                 m_subtst_no++;
298                 connect();
299             }
300             else
301             {
302                 m_test_state = TEST_STATE_OK;
303                 closeNextTest();
304             }
305             break;
306         case 9:
307             if (result && ODR_MASK_GET(init->options, Z_Options_scan))
308                 sendTest();
309             else
310             {
311                 m_test_state = TEST_STATE_NOT_APPLIC;
312                 closeNextTest();
313             }
314             break;
315         case 10:
316             if (result && ODR_MASK_GET(init->options, Z_Options_sort))
317                 sendTest();
318             else
319             {
320                 m_test_state = TEST_STATE_NOT_APPLIC;
321                 closeNextTest();
322             }
323             break;
324         default:
325             if (result)
326                 sendTest();
327             else
328             {
329                 m_test_state = TEST_STATE_NOT_APPLIC;
330                 closeNextTest();
331             }
332         }
333     }
334     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
335     {
336         Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
337         switch(m_tst_no)
338         {
339         case 8:
340             if (sr->records && (sr->records->which == Z_Records_NSD 
341                                 || 
342                                 sr->records->which == Z_Records_multipleNSD))
343             {
344                 yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
345                 m_query_no++;
346                 sendTest();
347             }
348             else if (!sr->resultCount || *sr->resultCount == 0)
349             {
350                 m_query_no++;
351                 sendTest();
352             }
353             else
354             {
355                 yaz_log(LOG_DEBUG, "%sgot %d result count with %s",
356                         m_session_str, *sr->resultCount,
357                         try_query[m_query_no]);
358                 m_got_result_set = 1;
359                 sendTest();
360             }
361             break;
362         case 10:
363             if (sr->resultCount && *sr->resultCount > 0)
364             {
365                 m_got_result_set = 1;
366                 sendTest();
367             }
368             else
369             {
370                 closeNextTest();
371             }
372             break;
373         default:
374             closeNextTest();
375         }
376     }
377     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
378     {
379         Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
380         switch(m_tst_no)
381         {
382         case 8:
383             if (sr->records && (sr->records->which == Z_Records_NSD 
384                                 || 
385                                 sr->records->which == Z_Records_multipleNSD))
386             {
387                 yaz_log(LOG_LOG, "%spresent returned NSD for %s",
388                         m_session_str, try_syntax[m_record_syntax_no]);
389             }
390             else if (sr->records && sr->records->which == Z_Records_DBOSD
391                      && sr->records->u.databaseOrSurDiagnostics->num_records>0
392                      && sr->records->u.databaseOrSurDiagnostics->records[0])
393             {
394                 if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
395                 {
396                     Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
397                     Odr_oid *expectRecordSyntax =
398                         yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
399                                             try_syntax[m_record_syntax_no]);
400                     if (oid_oidcmp(expectRecordSyntax,
401                                ext->direct_reference))
402                         yaz_log(LOG_WARN, "%sGot Record in different syntax from that required %s",
403                                 m_session_str,
404                                 try_syntax[m_record_syntax_no]);
405                     else
406                     {
407                         yaz_log(LOG_DEBUG, "%spresent OK for %s", m_session_str,
408                                 try_syntax[m_record_syntax_no]);
409                         m_test_state = TEST_STATE_OK;
410                     }
411                 }
412                 else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
413                     yaz_log(LOG_DEBUG, "%spresent returned SD %s", m_session_str,
414                             try_syntax[m_record_syntax_no]);
415                 else
416                     yaz_log(LOG_WARN, "%spresent returned fragment %s",
417                             m_session_str,
418                             try_syntax[m_record_syntax_no]);
419             }
420             else
421             {
422                 yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
423                 
424             }
425             m_record_syntax_no++;
426             sendTest();
427         }
428     }
429     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
430     {
431         Z_ScanResponse *sr =  gdu->u.z3950->u.scanResponse;
432         switch(m_tst_no)
433         {
434         case 9:
435             if (sr->entries->nonsurrogateDiagnostics)
436             {
437                 yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
438                         try_scan[m_scan_no]);
439                 m_scan_no++;
440                 sendTest();
441             }
442             else if (sr->entries->entries && sr->entries->num_entries > 0)
443             {
444                 yaz_log(LOG_DEBUG, "%sscan OK for %s", m_session_str,
445                         try_scan[m_scan_no]);
446                 m_test_state = TEST_STATE_OK;
447                 closeNextTest();
448             }
449             else
450             {
451                 yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
452                         m_session_str,
453                         try_scan[m_scan_no]);
454                 m_scan_no++;
455                 sendTest();
456             }
457             break;
458         default:
459             closeNextTest();
460         }
461     }
462     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
463     {
464         Z_SortResponse *sr =  gdu->u.z3950->u.sortResponse;
465         switch(m_tst_no)
466         {
467         case 10:
468             if (sr->diagnostics)
469             {
470                 yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
471                         try_sort[m_sort_no]);
472                 m_sort_no++;
473                 sendTest();
474             }
475             else
476             {
477                 yaz_log(LOG_DEBUG, "%ssort OK for %s", m_session_str,
478                         try_sort[m_sort_no]);
479                 m_test_state = TEST_STATE_OK;
480                 closeNextTest();
481             }
482             break;
483         default:
484             closeNextTest();
485         }
486     }
487     else
488         closeNextTest();
489 }
490
491 Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) : 
492     Yaz_Z_Assoc(the_PDU_Observable)
493 {
494     m_PDU_Observable = the_PDU_Observable;
495     m_host = 0;
496     m_database = 0;
497     m_timeout_connect = 30;
498     m_timeout_init = 30;
499     m_tst_no = -1;
500     m_subtst_no = 0;
501     m_protocol_version = 0;
502     sprintf(m_session_str, "%d ", m_tst_no);
503 }
504
505 void Zlint::connectNotify()
506 {
507     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
508     Z_InitRequest *init = apdu->u.initRequest;
509     int len;
510     Z_OtherInformation **oi;
511
512     timeout(m_timeout_init);
513
514     switch(m_tst_no)
515     {
516     case 0:
517         /* check if target properly negotiates to v3 .. */
518         ODR_MASK_ZERO(init->protocolVersion);
519         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
520         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
521         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
522         break;
523     case 1:
524         /* check if target properly negotiates to v2 .. */
525         ODR_MASK_ZERO(init->protocolVersion);
526         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
527         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
528         break;
529     case 2:
530         /* check latest version of target  - up to v9 */
531         ODR_MASK_ZERO(init->protocolVersion);
532         int i;
533         for (i = 0; i< 9; i++)
534             ODR_MASK_SET(init->protocolVersion, i);
535         break;
536     case 3:
537         /* send refID in init request */
538         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
539         init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
540         break;
541     case 4:
542         /* send double init with differnet refID's */
543         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
544         ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
545         init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
546         send_Z_PDU(apdu, &len);
547         
548         apdu = create_Z_PDU(Z_APDU_initRequest);
549         init = apdu->u.initRequest;
550
551         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
552         ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
553
554         init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
555         break;
556     case 5:
557         /* set all options.. see what target really supports .. */
558         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
559         ODR_MASK_ZERO(init->options);
560         for (i = 0; i <= 24; i++)
561             ODR_MASK_SET(init->options, i);
562         break;
563     case 6:
564         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
565         yaz_oi_APDU(apdu, &oi);
566         if (oi)
567         {
568             Z_OtherInformationUnit *p0;
569             const char *negotiationCharset[] = {
570                 "UTF-8",
571                 "UTF-16",
572                 "UCS-2",
573                 "UCS-4",
574                 "ISO-8859-1"
575             };
576             char *yazLang = 0;
577
578             if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
579                 ODR_MASK_SET(init->options, Z_Options_negotiationModel);
580
581                 p0->which = Z_OtherInfo_externallyDefinedInfo;
582                 p0->information.externallyDefinedInfo =
583
584                     yaz_set_proposal_charneg(
585                         odr_encode(),
586                         negotiationCharset, 5,
587                         (const char**)&yazLang, yazLang ? 1 : 0, 1);
588             }
589         }
590         break;
591     case 7:
592         *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
593         *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
594         break;
595     case 8:
596         /* search */
597         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
598         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
599         break;
600     case 9:
601         /* scan */
602         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
603         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
604         ODR_MASK_SET(init->options, Z_Options_scan);
605         break;
606     case 10:
607         /* sort */
608         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
609         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
610         ODR_MASK_SET(init->options, Z_Options_sort);
611         break;
612     }
613     int r = send_Z_PDU(apdu, &len);
614 }
615
616 int Zlint::nextTest()
617 {
618     if (m_tst_no >= 0)
619     {
620         switch(m_test_state)
621         {
622         case TEST_STATE_FAIL:
623             yaz_log(LOG_LOG, "%sTest Failed", m_session_str);
624             break;
625         case TEST_STATE_OK:
626             yaz_log(LOG_LOG, "%sTest Passed", m_session_str);
627             break;
628         case TEST_STATE_NOT_APPLIC:
629             yaz_log(LOG_LOG, "%sTest Not Applicable", m_session_str);
630             break;
631         case TEST_STATE_UNKNOWN:
632             yaz_log(LOG_LOG, "%sTest Could not be performed", m_session_str);
633         }
634     }
635     m_test_state = TEST_STATE_FAIL;
636     m_subtst_no = 0;
637     connect();
638     while(1)
639     {
640         m_tst_no++;
641         sprintf(m_session_str, "%d ", m_tst_no);
642         switch(m_tst_no)
643         {
644         case 0:
645             yaz_log(LOG_LOG, "%sCheck for init v3",
646                     m_session_str);
647             return 1;
648         case 1:
649             yaz_log(LOG_LOG, "%sCheck for init v2",
650                     m_session_str);
651             return 1;
652         case 2:
653             yaz_log(LOG_LOG, "%sCheck for init protocol version negotiation",
654                     m_session_str);
655             return 1;
656         case 3:
657             yaz_log(LOG_LOG, "%sCheck for init reference ID",
658                     m_session_str);
659             return 1;
660         case 4:
661             yaz_log(LOG_LOG, "%sCheck for double init request",
662                     m_session_str);
663             return 1;
664         case 5:
665             yaz_log(LOG_LOG, "%sCheck for init options",
666                     m_session_str);
667             return 1;
668         case 6:
669             yaz_log(LOG_LOG, "%sCheck for character set negotiation",
670                     m_session_str);
671             return 1;
672         case 7:
673             yaz_log(LOG_LOG, "%sCheck for messages size negotiation",
674                     m_session_str);
675             return 1;
676         case 8:
677             yaz_log(LOG_LOG, "%sCheck for basic search and retrieve",
678                     m_session_str);
679             m_query_no = 0;
680             m_record_syntax_no = 0;
681             m_got_result_set = 0;
682             return 1;
683         case 9:
684             yaz_log(LOG_LOG, "%sCheck for scan", m_session_str);
685             m_scan_no = 0;
686             return 1;
687         case 10:
688             yaz_log(LOG_LOG, "%sCheck for sort", m_session_str);
689             m_got_result_set = 0;
690             m_sort_no = 0;
691             return 1;
692         default:
693             close();
694             return 0;
695         }
696     }
697     return 0;
698 }
699
700 // current test failed badly - goto next or stop..
701 void Zlint::closeNextTest()
702 {
703     close();
704     if (m_tst_no != 0)
705     {
706         nextTest();
707     }
708 }
709
710 void Zlint::failNotify()
711 {
712     yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
713     testContinue();
714 }
715
716 void Zlint::timeoutNotify()
717 {
718     yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
719     testContinue();
720 }
721
722 void Zlint::testContinue()
723 {
724     close();
725     switch(m_tst_no)
726     {
727     case 8:
728         if (m_got_result_set)
729         {
730             // must search again to establish.. keep query
731             m_got_result_set = 0;
732             m_record_syntax_no++;
733         }
734         else
735         {
736             // try new search ..
737             m_query_no++;
738         }
739         connect();
740         return;
741     case 9:
742         m_scan_no++;
743         connect();
744         return;
745     case 10:
746         if (m_got_result_set)
747         {
748             // if sort test fails during sort, we'll continue to next
749             m_got_result_set = 0;
750             m_sort_no++;
751             connect();
752             return;
753         }
754     }
755     nextTest();
756 }
757
758 void Zlint::sendTest()
759 {
760     Z_APDU *apdu;
761     switch(m_tst_no)
762     {
763     case 8:
764         if (!m_got_result_set)
765         {
766             apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
767             Z_SearchRequest *sr;
768             sr = apdu->u.searchRequest;
769             sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
770             if (try_query[m_query_no] && sr)
771             {
772                 sr->query->which = Z_Query_type_1;
773                 Z_RPNQuery *rpn;
774                 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
775                 
776                 sr->databaseNames = &m_database;
777                 sr->num_databaseNames = 1;
778                 
779                 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
780
781                 yaz_pqf_destroy (pqf_parser);
782
783                 if (rpn)
784                 {
785                     int len;
786                     yaz_log(LOG_DEBUG, "%spqf: %s",
787                             m_session_str, try_query[m_query_no]);
788
789                     sr->query->u.type_1 = rpn;
790                     send_Z_PDU(apdu, &len);
791                 }
792                 else
793                     closeNextTest();
794             }
795             else
796             {
797                 yaz_log(LOG_WARN, "%sunable to get any hit count", 
798                         m_session_str);
799                 closeNextTest();
800             }
801         }
802         else if (m_got_result_set && try_syntax[m_record_syntax_no])
803         {
804             int len;
805             apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
806             Z_PresentRequest *pr = apdu->u.presentRequest;
807             *pr->numberOfRecordsRequested = 1;
808             *pr->resultSetStartPoint = 1;
809             
810             pr->preferredRecordSyntax =
811                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
812                                     try_syntax[m_record_syntax_no]);
813             send_Z_PDU(apdu, &len);
814         }
815         else
816             closeNextTest();
817         break;
818     case 9:
819         apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
820         if (apdu && try_scan[m_scan_no])
821         {
822             int len;
823             YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
824             Z_ScanRequest *sr = apdu->u.scanRequest;
825             sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
826                                                      odr_encode(),
827                                                      &sr->attributeSet,
828                                                      try_scan[m_scan_no]);
829
830             sr->databaseNames = &m_database;
831             sr->num_databaseNames = 1;
832
833             yaz_pqf_destroy (pqf_parser);
834             send_Z_PDU(apdu, &len);
835         }
836         else
837             closeNextTest();
838         break;
839     case 10:
840         if (!m_got_result_set)
841         {
842             apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
843             Z_SearchRequest *sr;
844             sr = apdu->u.searchRequest;
845             sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
846             if (try_query[m_query_no] && sr)
847             {
848                 sr->query->which = Z_Query_type_1;
849                 Z_RPNQuery *rpn;
850                 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
851                 
852                 sr->databaseNames = &m_database;
853                 sr->num_databaseNames = 1;
854                 
855                 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
856
857                 yaz_pqf_destroy (pqf_parser);
858
859                 if (rpn)
860                 {
861                     int len;
862                     yaz_log(LOG_DEBUG, "%spqf: %s",
863                             m_session_str, try_query[m_query_no]);
864
865                     sr->query->u.type_1 = rpn;
866                     send_Z_PDU(apdu, &len);
867                 }
868                 else
869                     closeNextTest();
870             }
871             else
872             {
873                 yaz_log(LOG_WARN, "%sunable to get any hit count", 
874                         m_session_str);
875                 closeNextTest();
876             }
877         }
878         else 
879         {
880             apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
881             if (apdu && try_sort[m_sort_no])
882             {
883                 char *setstring = "default";
884                 int len;
885                 Z_SortRequest *sr = apdu->u.sortRequest;
886                 
887                 sr->num_inputResultSetNames = 1;
888                 sr->num_inputResultSetNames = 1;
889                 sr->inputResultSetNames = (Z_InternationalString **)
890                     odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
891                 sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
892                 sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
893                 sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
894                 send_Z_PDU(apdu, &len);
895             }
896             else
897                 closeNextTest();
898         }
899         break;
900     default:
901         closeNextTest();
902     }
903 }
904
905 IYaz_PDU_Observer *Zlint::sessionNotify(
906     IYaz_PDU_Observable *the_PDU_Observable, int fd)
907 {
908     return 0;
909 }
910
911 void Zlint::connect()
912 {
913     if (m_host)
914     {
915         yaz_log(LOG_DEBUG, "%sconnecting to %s", m_session_str, m_host);
916         timeout(m_timeout_connect);
917         client(m_host);
918     }
919 }
920
921 void Zlint::args(int argc, char **argv)
922 {
923     char *arg;
924     int ret;
925     while ((ret = options("a:v:", argv, argc, &arg)) != -2)
926     {
927         switch (ret)
928         {
929         case 'v':
930             yaz_log_init(yaz_log_mask_str(arg), "", 0); 
931             break;
932         case 'a':
933             set_APDU_log(arg);
934             break;
935         case 0:
936             if (arg)
937             {
938                 const char *basep;
939                 m_host = xstrdup(arg);
940                 cs_get_host_args(m_host, &basep);
941                 if (!basep || !*basep)
942                     basep = "Default";
943                 m_database = xstrdup(basep);
944             }
945             break;
946         }
947     }
948 }
949
950 int main(int argc, char **argv)
951 {
952     Yaz_SocketManager mySocketManager;
953     Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
954     
955     z.args(argc, argv);
956
957     z.nextTest();
958
959     while (mySocketManager.processEvent() > 0)
960         ;
961     exit (0);
962 }
963
964 class Zlint_test_01 : public Zlint_test {
965 public:
966     Zlint_test_01();
967     ~Zlint_test_01();
968     Zlint_code init(Zlint *z);
969     Zlint_code recv_gdu(Zlint *z, Z_GDU *gdu);
970     Zlint_code recv_fail(Zlint *z, int reason);
971 };
972     
973 Zlint_test_01::Zlint_test_01()
974 {
975 }
976
977 Zlint_test_01::~Zlint_test_01()
978 {
979 }
980
981 Zlint_code Zlint_test_01::init(Zlint *z)
982 {
983     int len;
984     Z_APDU *apdu = z->create_Z_PDU(Z_APDU_initRequest);
985     Z_InitRequest *init = apdu->u.initRequest;
986
987     ODR_MASK_ZERO(init->protocolVersion);
988     ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
989     ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
990     ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
991     
992     int r = z->send_Z_PDU(apdu, &len);
993     if (r < 0)
994         return TEST_FINISHED;
995     return TEST_CONTINUE;
996 }
997
998 Zlint_code Zlint_test_01::recv_gdu(Zlint *z, Z_GDU *gdu)
999 {
1000     if (gdu->which == Z_GDU_Z3950 &&
1001         gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
1002     {
1003         Z_InitResponse *init = gdu->u.z3950->u.initResponse;
1004         int ver = z->initResponseGetVersion(init);
1005         int result = init->result ? *init->result : 0;
1006         if (ver > 3 || ver < 2)
1007             yaz_log(LOG_WARN, "got version %d, expected 2 or 3", ver);
1008         if (!result)
1009             return TEST_FINISHED;
1010     }
1011     return TEST_FINISHED;
1012 }
1013
1014 Zlint_code Zlint_test_01::recv_fail(Zlint *z, int reason)
1015 {
1016     return TEST_FINISHED;
1017 }