Server sets OPTIONS search and present.
[yazpp-moved-to-github.git] / src / yaz-z-server.cpp
1 /*
2  * Copyright (c) 2000, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Log: yaz-z-server.cpp,v $
6  * Revision 1.6  2001-01-29 11:18:24  adam
7  * Server sets OPTIONS search and present.
8  *
9  * Revision 1.5  2000/10/24 12:29:57  adam
10  * Fixed bug in proxy where a Yaz_ProxyClient could be owned by
11  * two Yaz_Proxy's (fatal).
12  *
13  * Revision 1.4  2000/10/11 11:58:17  adam
14  * Moved header files to include/yaz++. Switched to libtool and automake.
15  * Configure script creates yaz++-config script.
16  *
17  * Revision 1.3  2000/09/21 21:43:20  adam
18  * Better high-level server API.
19  *
20  * Revision 1.2  2000/09/12 12:09:53  adam
21  * More work on high-level server.
22  *
23  * Revision 1.1  2000/09/08 10:23:42  adam
24  * Added skeleton of yaz-z-server.
25  *
26  */
27
28 #include <yaz/log.h>
29 #include <yaz++/yaz-z-server.h>
30
31
32 Yaz_Z_Server::Yaz_Z_Server(IYaz_PDU_Observable *the_PDU_Observable)
33     : Yaz_Z_Assoc(the_PDU_Observable)
34 {
35 }
36
37 /*
38  * database record.
39  */
40 void Yaz_Z_Server::create_databaseRecord (
41     Z_NamePlusRecord *rec, const char *dbname, int format, const void *buf, int len)
42 {
43     rec->databaseName = dbname ? odr_strdup (odr_encode(), dbname) : 0;
44     rec->which = Z_NamePlusRecord_databaseRecord;
45     rec->u.databaseRecord = z_ext_record (odr_encode(), format,
46                                           (const char *) buf, len);
47 }
48
49 /*
50  * surrogate diagnostic.
51  */
52 void Yaz_Z_Server::create_surrogateDiagnostics(
53     Z_NamePlusRecord *rec, const char *dbname, int error, char *const addinfo)
54 {
55     int oid[OID_SIZE];
56     int *err = (int *)odr_malloc (odr_encode(), sizeof(*err));
57     oident bib1;
58     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (odr_encode(), sizeof(*drec));
59     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
60         odr_malloc (odr_encode(), sizeof(*dr));
61     
62     bib1.proto = PROTO_Z3950;
63     bib1.oclass = CLASS_DIAGSET;
64     bib1.value = VAL_BIB1;
65
66     yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
67     *err = error;
68     rec->databaseName = dbname ? odr_strdup (odr_encode(), dbname) : 0;
69     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
70     rec->u.surrogateDiagnostic = drec;
71     drec->which = Z_DiagRec_defaultFormat;
72     drec->u.defaultFormat = dr;
73     dr->diagnosticSetId = odr_oiddup (odr_encode(),
74                                       oid_ent_to_oid(&bib1, oid));
75     dr->condition = err;
76     dr->which = Z_DefaultDiagFormat_v2Addinfo;
77     dr->u.v2Addinfo = odr_strdup (odr_encode(), addinfo ? addinfo : "");
78 }
79
80 Z_Records *Yaz_Z_Server::create_nonSurrogateDiagnostics (
81     int error, const char *addinfo)
82 {
83     int oid[OID_SIZE];
84     Z_Records *rec = (Z_Records *)
85         odr_malloc (odr_encode(), sizeof(*rec));
86     oident bib1;
87     int *err = (int *)
88         odr_malloc (odr_encode(), sizeof(*err));
89     Z_DiagRec *drec = (Z_DiagRec *)
90         odr_malloc (odr_encode(), sizeof(*drec));
91     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
92         odr_malloc (odr_encode(), sizeof(*dr));
93
94     bib1.proto = PROTO_Z3950;
95     bib1.oclass = CLASS_DIAGSET;
96     bib1.value = VAL_BIB1;
97
98     *err = error;
99     rec->which = Z_Records_NSD;
100     rec->u.nonSurrogateDiagnostic = dr;
101     dr->diagnosticSetId =
102         odr_oiddup (odr_encode(), oid_ent_to_oid(&bib1, oid));
103     dr->condition = err;
104     dr->which = Z_DefaultDiagFormat_v2Addinfo;
105     dr->u.v2Addinfo = odr_strdup (odr_encode(), addinfo ? addinfo : "");
106     return rec;
107 }
108
109 Z_Records *Yaz_Z_Server::pack_records (const char *resultSetName,
110                                        int start, int xnum,
111                                        Z_RecordComposition *comp,
112                                        int *next, int *pres,
113                                        int *format)
114 {
115     int recno, total_length = 0, toget = xnum, dumped_records = 0;
116     Z_Records *records =
117         (Z_Records *) odr_malloc (odr_encode(), sizeof(*records));
118     Z_NamePlusRecordList *reclist =
119         (Z_NamePlusRecordList *) odr_malloc (odr_encode(), sizeof(*reclist));
120     Z_NamePlusRecord **list =
121         (Z_NamePlusRecord **) odr_malloc (odr_encode(), sizeof(*list) * toget);
122
123     records->which = Z_Records_DBOSD;
124     records->u.databaseOrSurDiagnostics = reclist;
125     reclist->num_records = 0;
126     reclist->records = list;
127     *pres = Z_PRES_SUCCESS;
128     *next = 0;
129
130     yaz_log(LOG_LOG, "Request to pack %d+%d", start, toget);
131     yaz_log(LOG_LOG, "pms=%d, mrs=%d", m_preferredMessageSize,
132             m_maximumRecordSize);
133     for (recno = start; reclist->num_records < toget; recno++)
134     {
135         Z_NamePlusRecord *this_rec =
136             (Z_NamePlusRecord *) odr_malloc (odr_encode(), sizeof(*this_rec));
137         this_rec->databaseName = 0;
138         this_rec->which = Z_NamePlusRecord_databaseRecord;
139         this_rec->u.databaseRecord = 0;
140
141         int this_length = 0;
142
143         recv_Z_record (resultSetName, recno, format, comp, this_rec, records);
144
145         if (records->which != Z_Records_DBOSD)
146         {
147             *pres = Z_PRES_FAILURE;
148             return records;
149         }
150
151         if (this_rec->which == Z_NamePlusRecord_databaseRecord &&
152             this_rec->u.databaseRecord == 0)
153         {   // handler did not return a record..
154             create_surrogateDiagnostics(this_rec, 0, 14, 0);
155         }
156         /*
157          * we get the number of bytes allocated on the stream before any
158          * allocation done by the backend - this should give us a reasonable
159          * idea of the total size of the data so far.
160          */
161         total_length = odr_total(odr_encode()) - dumped_records;
162         this_length = odr_total(odr_encode()) - total_length;
163         yaz_log(LOG_LOG, "  fetched record, len=%d, total=%d",
164                 this_length, total_length);
165         if (this_length + total_length > m_preferredMessageSize)
166         {
167             /* record is small enough, really */
168             if (this_length <= m_preferredMessageSize)
169             {
170                 yaz_log(LOG_LOG, "  Dropped last normal-sized record");
171                 *pres = Z_PRES_PARTIAL_2;
172                 break;
173             }
174             if (this_length >= m_maximumRecordSize)
175             {   /* too big entirely */
176                 yaz_log(LOG_LOG, "Record > maxrcdsz");
177                 reclist->records[reclist->num_records] = this_rec;
178                 create_surrogateDiagnostics(this_rec,
179                                             this_rec->databaseName, 17, 0);
180                 reclist->num_records++;
181                 *next = recno + 1;
182                 dumped_records += this_length;
183                 continue;
184             }
185             else /* record can only be fetched by itself */
186             {
187                 yaz_log(LOG_LOG, "  Record > prefmsgsz");
188                 if (toget > 1)
189                 {
190                     yaz_log(LOG_DEBUG, "  Dropped it");
191                     reclist->records[reclist->num_records] = this_rec;
192                     create_surrogateDiagnostics(this_rec,
193                                                 this_rec->databaseName,
194                                                 16, 0);
195                     reclist->num_records++;
196                     // *next = freq.last_in_set ? 0 : recno + 1;
197                     *next = recno + 1;
198                     dumped_records += this_length;
199                     continue;
200                 }
201             }
202         }
203         reclist->records[reclist->num_records] = this_rec;
204         reclist->num_records++;
205         *next = recno + 1;
206     }
207     return records;
208 }
209
210 void Yaz_Z_Server::fetch_via_piggyback (Z_SearchRequest *req,
211                                         Z_SearchResponse *res)
212 {
213     bool_t *sr = (bool_t *)odr_malloc (odr_encode(), sizeof(*sr));
214     *sr = 1;
215     
216     int toget = 0;
217     
218     Z_RecordComposition comp, *compp = 0;
219     int hits = *res->resultCount;
220     
221     int *nulint = (int *)odr_malloc (odr_encode(), sizeof(*nulint));
222     *nulint = 0;
223     
224     comp.which = Z_RecordComp_simple;
225     /* how many records does the user agent want, then? */
226     if (hits <= *req->smallSetUpperBound)
227     {
228         toget = hits;
229         if ((comp.u.simple = req->smallSetElementSetNames))
230             compp = &comp;
231     }
232     else if (hits < *req->largeSetLowerBound)
233     {
234         toget = *req->mediumSetPresentNumber;
235         if (toget > hits)
236             toget = hits;
237         if ((comp.u.simple = req->mediumSetElementSetNames))
238             compp = &comp;
239     }
240     
241     if (toget && !res->records)
242     {
243         res->presentStatus = (int *) odr_malloc (odr_encode(), sizeof(int));
244         *res->presentStatus = Z_PRES_SUCCESS;
245         res->records =
246             pack_records(req->resultSetName, 1, toget, compp, 
247                          res->nextResultSetPosition,
248                          res->presentStatus,
249                          req->preferredRecordSyntax);
250         if (!res->records)
251             return;
252         if (res->records->which == Z_Records_DBOSD)
253             *res->numberOfRecordsReturned =
254                 res->records->u.databaseOrSurDiagnostics->num_records;
255         res->searchStatus = sr;
256         res->resultSetStatus = 0;
257     }
258     else
259     {
260         if (hits)
261             *res->nextResultSetPosition = 1;
262         res->numberOfRecordsReturned = nulint;
263         res->searchStatus = sr;
264         res->resultSetStatus = 0;
265         res->presentStatus = 0;
266     }
267 }
268
269 void Yaz_Z_Server::fetch_via_present (Z_PresentRequest *req,
270                                       Z_PresentResponse *res)
271 {
272     res->records = pack_records (req->resultSetId,*req->resultSetStartPoint, 
273                                  *req->numberOfRecordsRequested,
274                                  req->recordComposition,
275                                  res->nextResultSetPosition,
276                                  res->presentStatus,
277                                  req->preferredRecordSyntax);
278     if (res->records->which == Z_Records_DBOSD)
279         *res->numberOfRecordsReturned =
280             res->records->u.databaseOrSurDiagnostics->num_records;
281 }
282     
283 void Yaz_Z_Server::recv_Z_PDU (Z_APDU *apdu_request)
284 {   
285     Z_Options *req, *res;
286     Z_APDU *apdu_response;
287     switch (apdu_request->which)
288     {
289     case Z_APDU_initRequest:
290         logf (LOG_LOG, "got InitRequest p=%p", this);
291         apdu_response = create_Z_PDU(Z_APDU_initResponse);
292         req = apdu_request->u.initRequest->options;
293         res = apdu_response->u.initResponse->options;
294
295         if (ODR_MASK_GET(req, Z_Options_search))
296             ODR_MASK_SET(res, Z_Options_search);
297         if (ODR_MASK_GET(req, Z_Options_present))
298             ODR_MASK_SET(res, Z_Options_present);
299         recv_Z_init (apdu_request->u.initRequest,
300                      apdu_response->u.initResponse);
301         m_preferredMessageSize =
302             *apdu_request->u.initRequest->preferredMessageSize;
303         m_maximumRecordSize =
304             *apdu_request->u.initRequest->maximumRecordSize;
305         send_Z_PDU(apdu_response);
306         break;
307     case Z_APDU_searchRequest:
308         logf (LOG_LOG, "got SearchRequest p=%p", this);
309         apdu_response = create_Z_PDU(Z_APDU_searchResponse);
310         recv_Z_search (apdu_request->u.searchRequest,
311                        apdu_response->u.searchResponse);
312         if (!apdu_response->u.searchResponse->records)
313         {
314             fetch_via_piggyback(apdu_request->u.searchRequest,
315                                 apdu_response->u.searchResponse);
316         }
317         send_Z_PDU(apdu_response);
318         break;
319     case Z_APDU_presentRequest:
320         logf (LOG_LOG, "got PresentRequest p=%p", this);
321         apdu_response = create_Z_PDU(Z_APDU_presentResponse);
322         recv_Z_present (apdu_request->u.presentRequest,
323                         apdu_response->u.presentResponse);
324         if (!apdu_response->u.presentResponse->records)
325             fetch_via_present(apdu_request->u.presentRequest,
326                               apdu_response->u.presentResponse);
327         send_Z_PDU(apdu_response);
328         break;
329     }
330 }
331