2 * Copyright (c) 2004, Index Data.
3 * See the file LICENSE for details.
5 * $Id: zlint.cpp,v 1.2 2004-02-19 15:25:46 adam Exp $
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>
14 #include <yaz++/pdu-assoc.h>
15 #include <yaz++/socket-manager.h>
16 #include <yaz++/z-assoc.h>
18 #define REFID_BUF1 "zlint\000check1"
20 #define REFID_BUF2 "zlint\000check2"
32 virtual void init_send(Zlint *z) = 0;
33 virtual test_code init_recv(Zlint *z, Z_InitResponse *ir) = 0;
34 virtual test_code other_recv(Zlint *z, Z_APDU *ir, Z_InitResponse *ir) = 0;
38 const char *try_syntax [] = {
48 const char *try_query[] = {
49 "@attr 1=4 petersson",
50 "@attr 1=1016 petersson",
52 "@attr 1=1016 kingdom",
60 "@attr 1=1016 computer",
68 const char *try_sort [] = {
75 const char *try_scan [] = {
82 #define TEST_STATE_FAIL 0
83 #define TEST_STATE_UNKNOWN 1
84 #define TEST_STATE_OK 2
85 #define TEST_STATE_NOT_APPLIC 3
87 class Zlint : public Yaz_Z_Assoc {
96 int m_record_syntax_no;
98 IYaz_PDU_Observable *m_PDU_Observable;
102 int m_timeout_connect;
103 int m_protocol_version;
104 char m_session_str[20];
105 int initResponseGetVersion(Z_InitResponse *init);
108 void recv_GDU(Z_GDU *apdu, int len);
109 Zlint(IYaz_PDU_Observable *the_PDU_Observable);
110 void args(int argc, char **argv);
111 void connectNotify();
113 void closeNextTest();
117 void timeoutNotify();
118 IYaz_PDU_Observer *sessionNotify(
119 IYaz_PDU_Observable *the_PDU_Observable, int fd);
121 Z_ReferenceId *mk_refid(const char *buf, int len);
124 int Zlint::initResponseGetVersion(Z_InitResponse *init)
129 for (i = 0; i<12; i++)
130 if (ODR_MASK_GET(init->protocolVersion, no))
134 yaz_log(LOG_WARN, "%sbad formatted version");
141 Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
144 (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
145 id->size = id->len = len;
146 id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
147 memcpy(id->buf, buf, len);
151 void Zlint::recv_GDU(Z_GDU *gdu, int len)
153 if (gdu->which != Z_GDU_Z3950)
155 yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
158 if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
161 Z_InitResponse *init = gdu->u.z3950->u.initResponse;
162 int ver = initResponseGetVersion(init);
163 int result = init->result ? *init->result : 0;
165 yaz_log(LOG_WARN, "%sinit rejected");
169 if (ver > 3 || ver < 2)
170 yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
173 m_test_state = TEST_STATE_OK;
174 m_protocol_version = ver;
185 yaz_log(LOG_WARN, "%sgot version %d, expected 2",
188 m_test_state = TEST_STATE_OK;
192 if (ver < 2 || ver > 5)
193 yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
196 m_test_state = TEST_STATE_OK;
200 if (!init->referenceId)
201 yaz_log(LOG_WARN, "%smissing referenceID from init response",
203 else if (init->referenceId->len != REFID_LEN1
204 || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
205 yaz_log(LOG_WARN, "%sreference ID does not match");
207 m_test_state = TEST_STATE_OK;
211 if (m_subtst_no == 0)
213 if (!init->referenceId)
214 yaz_log(LOG_WARN, "%smissing referenceID from first init response",
216 else if (init->referenceId->len != REFID_LEN1
217 || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
218 yaz_log(LOG_WARN, "%sreference ID does not match");
223 if (!init->referenceId)
224 yaz_log(LOG_WARN, "%smissing referenceID from second init response",
226 else if (init->referenceId->len != REFID_LEN2
227 || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
228 yaz_log(LOG_WARN, "%sreference ID does not match");
230 m_test_state = TEST_STATE_OK;
240 for (i = 0; i <= 24; i++)
241 if (ODR_MASK_GET(init->options, i))
246 yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
249 yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
251 if (no_set >= 2 && no_reset)
252 m_test_state = TEST_STATE_OK;
257 if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
259 Z_CharSetandLanguageNegotiation *p =
260 yaz_get_charneg_record(init->otherInfo);
264 char *charset=NULL, *lang=NULL;
266 NMEM m = nmem_create();
268 yaz_get_response_charneg(m, p, &charset, &lang,
270 yaz_log(LOG_DEBUG, "%sAccepted character set : %s",
271 m_session_str, charset);
272 yaz_log(LOG_DEBUG, "%sAccepted code language : %s",
273 m_session_str, lang ? lang : "none");
274 yaz_log(LOG_DEBUG, "%sAccepted records in ...: %d",
275 m_session_str, selected );
277 m_test_state = TEST_STATE_OK;
281 m_test_state = TEST_STATE_NOT_APPLIC;
285 if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
287 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
290 else if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
292 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
295 else if (m_subtst_no < 3)
303 m_test_state = TEST_STATE_OK;
308 if (result && ODR_MASK_GET(init->options, Z_Options_scan))
312 m_test_state = TEST_STATE_NOT_APPLIC;
317 if (result && ODR_MASK_GET(init->options, Z_Options_sort))
321 m_test_state = TEST_STATE_NOT_APPLIC;
330 m_test_state = TEST_STATE_NOT_APPLIC;
335 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
337 Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
341 if (sr->records && (sr->records->which == Z_Records_NSD
343 sr->records->which == Z_Records_multipleNSD))
345 yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
349 else if (!sr->resultCount || *sr->resultCount == 0)
356 yaz_log(LOG_DEBUG, "%sgot %d result count with %s",
357 m_session_str, *sr->resultCount,
358 try_query[m_query_no]);
359 m_got_result_set = 1;
364 if (sr->resultCount && *sr->resultCount > 0)
366 m_got_result_set = 1;
378 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
380 Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
384 if (sr->records && (sr->records->which == Z_Records_NSD
386 sr->records->which == Z_Records_multipleNSD))
388 yaz_log(LOG_LOG, "%spresent returned NSD for %s",
389 m_session_str, try_syntax[m_record_syntax_no]);
391 else if (sr->records && sr->records->which == Z_Records_DBOSD
392 && sr->records->u.databaseOrSurDiagnostics->num_records>0
393 && sr->records->u.databaseOrSurDiagnostics->records[0])
395 if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
397 Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
398 Odr_oid *expectRecordSyntax =
399 yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
400 try_syntax[m_record_syntax_no]);
401 if (oid_oidcmp(expectRecordSyntax,
402 ext->direct_reference))
403 yaz_log(LOG_WARN, "%sGot Record in different syntax from that required %s",
405 try_syntax[m_record_syntax_no]);
408 yaz_log(LOG_DEBUG, "%spresent OK for %s", m_session_str,
409 try_syntax[m_record_syntax_no]);
410 m_test_state = TEST_STATE_OK;
413 else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
414 yaz_log(LOG_DEBUG, "%spresent returned SD %s", m_session_str,
415 try_syntax[m_record_syntax_no]);
417 yaz_log(LOG_WARN, "%spresent returned fragment %s",
419 try_syntax[m_record_syntax_no]);
423 yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
426 m_record_syntax_no++;
430 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
432 Z_ScanResponse *sr = gdu->u.z3950->u.scanResponse;
436 if (sr->entries->nonsurrogateDiagnostics)
438 yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
439 try_scan[m_scan_no]);
443 else if (sr->entries->entries && sr->entries->num_entries > 0)
445 yaz_log(LOG_DEBUG, "%sscan OK for %s", m_session_str,
446 try_scan[m_scan_no]);
447 m_test_state = TEST_STATE_OK;
452 yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
454 try_scan[m_scan_no]);
463 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
465 Z_SortResponse *sr = gdu->u.z3950->u.sortResponse;
471 yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
472 try_sort[m_sort_no]);
478 yaz_log(LOG_DEBUG, "%ssort OK for %s", m_session_str,
479 try_sort[m_sort_no]);
480 m_test_state = TEST_STATE_OK;
492 Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) :
493 Yaz_Z_Assoc(the_PDU_Observable)
495 m_PDU_Observable = the_PDU_Observable;
498 m_timeout_connect = 30;
502 m_protocol_version = 0;
503 sprintf(m_session_str, "%d ", m_tst_no);
506 void Zlint::connectNotify()
508 Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
509 Z_InitRequest *init = apdu->u.initRequest;
511 Z_OtherInformation **oi;
513 timeout(m_timeout_init);
518 /* check if target properly negotiates to v3 .. */
519 ODR_MASK_ZERO(init->protocolVersion);
520 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
521 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
522 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
525 /* check if target properly negotiates to v2 .. */
526 ODR_MASK_ZERO(init->protocolVersion);
527 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
528 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
531 /* check latest version of target - up to v9 */
532 ODR_MASK_ZERO(init->protocolVersion);
534 for (i = 0; i< 9; i++)
535 ODR_MASK_SET(init->protocolVersion, i);
538 /* send refID in init request */
539 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
540 init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
543 /* send double init with differnet refID's */
544 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
545 ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
546 init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
547 send_Z_PDU(apdu, &len);
549 apdu = create_Z_PDU(Z_APDU_initRequest);
550 init = apdu->u.initRequest;
552 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
553 ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
555 init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
558 /* set all options.. see what target really supports .. */
559 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
560 ODR_MASK_ZERO(init->options);
561 for (i = 0; i <= 24; i++)
562 ODR_MASK_SET(init->options, i);
565 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
566 yaz_oi_APDU(apdu, &oi);
569 Z_OtherInformationUnit *p0;
570 const char *negotiationCharset[] = {
579 if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
580 ODR_MASK_SET(init->options, Z_Options_negotiationModel);
582 p0->which = Z_OtherInfo_externallyDefinedInfo;
583 p0->information.externallyDefinedInfo =
585 yaz_set_proposal_charneg(
587 negotiationCharset, 5,
588 (const char**)&yazLang, yazLang ? 1 : 0, 1);
593 *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
594 *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
598 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
599 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
603 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
604 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
605 ODR_MASK_SET(init->options, Z_Options_scan);
609 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
610 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
611 ODR_MASK_SET(init->options, Z_Options_sort);
614 int r = send_Z_PDU(apdu, &len);
617 int Zlint::nextTest()
623 case TEST_STATE_FAIL:
624 yaz_log(LOG_LOG, "%sTest Failed", m_session_str);
627 yaz_log(LOG_LOG, "%sTest Passed", m_session_str);
629 case TEST_STATE_NOT_APPLIC:
630 yaz_log(LOG_LOG, "%sTest Not Applicable", m_session_str);
632 case TEST_STATE_UNKNOWN:
633 yaz_log(LOG_LOG, "%sTest Could not be performed", m_session_str);
636 m_test_state = TEST_STATE_FAIL;
642 sprintf(m_session_str, "%d ", m_tst_no);
646 yaz_log(LOG_LOG, "%sCheck for init v3",
650 yaz_log(LOG_LOG, "%sCheck for init v2",
654 yaz_log(LOG_LOG, "%sCheck for init protocol version negotiation",
658 yaz_log(LOG_LOG, "%sCheck for init reference ID",
662 yaz_log(LOG_LOG, "%sCheck for double init request",
666 yaz_log(LOG_LOG, "%sCheck for init options",
670 yaz_log(LOG_LOG, "%sCheck for character set negotiation",
674 yaz_log(LOG_LOG, "%sCheck for messages size negotiation",
678 yaz_log(LOG_LOG, "%sCheck for basic search and retrieve",
681 m_record_syntax_no = 0;
682 m_got_result_set = 0;
685 yaz_log(LOG_LOG, "%sCheck for scan", m_session_str);
689 yaz_log(LOG_LOG, "%sCheck for sort", m_session_str);
690 m_got_result_set = 0;
701 // current test failed badly - goto next or stop..
702 void Zlint::closeNextTest()
711 void Zlint::failNotify()
713 yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
717 void Zlint::timeoutNotify()
719 yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
723 void Zlint::testContinue()
729 if (m_got_result_set)
731 // must search again to establish.. keep query
732 m_got_result_set = 0;
733 m_record_syntax_no++;
747 if (m_got_result_set)
749 // if sort test fails during sort, we'll continue to next
750 m_got_result_set = 0;
759 void Zlint::sendTest()
765 if (!m_got_result_set)
767 apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
769 sr = apdu->u.searchRequest;
770 sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
771 if (try_query[m_query_no] && sr)
773 sr->query->which = Z_Query_type_1;
775 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
777 sr->databaseNames = &m_database;
778 sr->num_databaseNames = 1;
780 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
782 yaz_pqf_destroy (pqf_parser);
787 yaz_log(LOG_DEBUG, "%spqf: %s",
788 m_session_str, try_query[m_query_no]);
790 sr->query->u.type_1 = rpn;
791 send_Z_PDU(apdu, &len);
798 yaz_log(LOG_WARN, "%sunable to get any hit count",
803 else if (m_got_result_set && try_syntax[m_record_syntax_no])
806 apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
807 Z_PresentRequest *pr = apdu->u.presentRequest;
808 *pr->numberOfRecordsRequested = 1;
809 *pr->resultSetStartPoint = 1;
811 pr->preferredRecordSyntax =
812 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
813 try_syntax[m_record_syntax_no]);
814 send_Z_PDU(apdu, &len);
820 apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
821 if (apdu && try_scan[m_scan_no])
824 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
825 Z_ScanRequest *sr = apdu->u.scanRequest;
826 sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
829 try_scan[m_scan_no]);
831 sr->databaseNames = &m_database;
832 sr->num_databaseNames = 1;
834 yaz_pqf_destroy (pqf_parser);
835 send_Z_PDU(apdu, &len);
841 if (!m_got_result_set)
843 apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
845 sr = apdu->u.searchRequest;
846 sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
847 if (try_query[m_query_no] && sr)
849 sr->query->which = Z_Query_type_1;
851 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
853 sr->databaseNames = &m_database;
854 sr->num_databaseNames = 1;
856 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
858 yaz_pqf_destroy (pqf_parser);
863 yaz_log(LOG_DEBUG, "%spqf: %s",
864 m_session_str, try_query[m_query_no]);
866 sr->query->u.type_1 = rpn;
867 send_Z_PDU(apdu, &len);
874 yaz_log(LOG_WARN, "%sunable to get any hit count",
881 apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
882 if (apdu && try_sort[m_sort_no])
884 char *setstring = "default";
886 Z_SortRequest *sr = apdu->u.sortRequest;
888 sr->num_inputResultSetNames = 1;
889 sr->num_inputResultSetNames = 1;
890 sr->inputResultSetNames = (Z_InternationalString **)
891 odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
892 sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
893 sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
894 sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
895 send_Z_PDU(apdu, &len);
906 IYaz_PDU_Observer *Zlint::sessionNotify(
907 IYaz_PDU_Observable *the_PDU_Observable, int fd)
912 void Zlint::connect()
916 yaz_log(LOG_DEBUG, "%sconnecting to %s", m_session_str, m_host);
917 timeout(m_timeout_connect);
922 void Zlint::args(int argc, char **argv)
926 while ((ret = options("v", argv, argc, &arg)) != -2)
936 m_host = xstrdup(arg);
937 cs_get_host_args(m_host, &basep);
938 if (!basep || !*basep)
940 m_database = xstrdup(basep);
947 int main(int argc, char **argv)
949 Yaz_SocketManager mySocketManager;
950 Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
956 while (mySocketManager.processEvent() > 0)