1 /* Copyright (C) 2000-2015 Lavtech.com corp. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17 
18 #include "udm_config.h"
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26 #include <sys/types.h>
27 #ifdef HAVE_SYS_PARAM_H
28 #include <sys/param.h>
29 #endif
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33 #ifdef HAVE_NETDB_H
34 #include <netdb.h>
35 #endif
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
42 #include <errno.h>
43 
44 #include "udm_common.h"
45 #include "udm_utils.h"
46 #include "udm_proto.h"
47 #include "udm_services.h"
48 #include "udm_agent.h"
49 #include "udm_db.h"
50 #include "udm_doc.h"
51 #include "udm_result.h"
52 #include "udm_sdp.h"
53 #include "udm_xmalloc.h"
54 #include "udm_searchtool.h"
55 #include "udm_vars.h"
56 #include "udm_word.h"
57 #include "udm_log.h"
58 #include "udm_host.h"
59 #include "udm_http.h"
60 #include "udm_url.h"
61 #include "udm_store.h"
62 #include "udm_query.h"
63 
64 /*
65 #define DEBUG_SDP
66 */
67 
68 /*
69 #define DEBUG_SEARCH
70 */
71 
72 typedef struct
73 {
74   UDM_QUERY Query;
75   UDM_VARLIST Vars;  /* Connection params: Host, port, trackquery, etc */
76   int errcode;
77   char errstr[UDM_ERRSTR_SIZE];
78 } UDM_SDDB;
79 
80 
81 static void
UdmSDDBInit(UDM_SDDB * db)82 UdmSDDBInit(UDM_SDDB *db)
83 {
84   bzero((void*) db, sizeof(UDM_SDDB));
85 }
86 
87 static void
UdmSDDBFree(UDM_SDDB * db)88 UdmSDDBFree(UDM_SDDB *db)
89 {
90   UdmQueryFree(&db->Query);
91   UdmVarListFree(&db->Vars);
92 }
93 
94 
95 static inline UDM_SDDB *
UdmSDDB(UDM_DB * db)96 UdmSDDB(UDM_DB *db)
97 {
98   return (UDM_SDDB*) db->specific;
99 }
100 
101 
102 static inline UDM_VARLIST *
UdmSDDBVars(UDM_DB * db)103 UdmSDDBVars(UDM_DB *db)
104 {
105   return &UdmSDDB(db)->Vars;
106 }
107 
108 
109 /*
110   Cache document list, so UDM_QUERYCMD_DOCINFO is handled from cache,
111   without having to fetch a new HTTP response from a node.
112 */
113 static void
UdmResultTakeoverDocList(UDM_QUERY * To,UDM_RESULT * From)114 UdmResultTakeoverDocList(UDM_QUERY *To, UDM_RESULT *From)
115 {
116   UdmResultFree(&To->Res);
117   UdmResultInit(&To->Res);
118   To->Res.Doc= From->Doc;
119   To->Res.num_rows= From->num_rows;
120   From->Doc= NULL;
121   From->num_rows= 0;
122 }
123 
124 
125 static udm_rc_t
UdmQueryAddDocInfoSearchd(UDM_AGENT * A,UDM_DB * db,UDM_QUERY * Query)126 UdmQueryAddDocInfoSearchd(UDM_AGENT *A, UDM_DB *db, UDM_QUERY *Query)
127 {
128   size_t i;
129   for (i= 0; i < UdmResultNumRows(&Query->Res); i++)
130   {
131     UDM_URLDATA *Data= &Query->URLData.Item[i + Query->stats.first];
132     size_t Doc_dbnum= UDM_COORD2DBNUM(Data->score);
133 
134     if (&A->Conf->DBList.Item[Doc_dbnum] == db)
135     {
136       size_t id, num= Data->url_id;
137       UDM_RESULT *SrcRes= A->Conf->DBList.nitems == 1 ? &Query->Res : &UdmSDDB(db)->Query.Res;
138       UDM_VARLIST *DstSections= &Query->Res.Doc[i].Sections;
139       id= UdmVarListFindInt(&SrcRes->Doc[num].Sections, "id", 0);
140       Data->url_id= id;
141       if (A->Conf->DBList.nitems > 1)
142       {
143         UdmVarListReplaceLst(DstSections, &SrcRes->Doc[num].Sections, NULL, "*");
144       }
145       else if (A->Conf->DBList.nitems == 1)
146       {
147         if (Query->stats.first)
148         {
149           UdmVarListFree(DstSections);
150           UdmVarListReplaceLst(DstSections, &SrcRes->Doc[num].Sections, NULL, "*");
151         }
152       }
153       UdmVarListReplaceInt(DstSections, "id", id);
154       UdmVarListReplaceInt(DstSections, "DBOrder", num + 1);
155     }
156   }
157   return UDM_OK;
158 }
159 
160 
161 static udm_rc_t
UdmVarListLog2(UDM_AGENT * A,UDM_VARLIST * V,int l,const char * pre)162 UdmVarListLog2(UDM_AGENT *A,UDM_VARLIST *V,int l,const char *pre)
163 {
164   size_t h;
165   if (UdmNeedLog(A->Conf, l))
166   {
167     for(h=0; h < V->nvars; h++)
168     {
169       const UDM_VAR *v= UdmVarListFindConstByIndex(V, h);
170       UDM_CONST_STR strbuf, *str= UdmVarGetConstStr(v, &strbuf);
171       UdmLog(A, l, "%s.%s: %.*s", pre, UdmVarName(v), (int) str->length, str->str);
172     }
173   }
174   return UDM_OK;
175 }
176 
177 
178 /*
179 static char *
180 UdmParseOpenSearchAddr(UDM_VARLIST *Vars, const char *src)
181 {
182   char *res;
183   UDM_DSTR d;
184   UdmDSTRInit(&d, 1024);
185   UdmDSTRParse(&d, src, Vars);
186   res= UdmStrdup(UdmDSTRPtr(&d));
187   UdmDSTRFree(&d);
188   return res;
189 }
190 */
191 
192 static udm_rc_t
UdmMakeNodeQueryString(UDM_AGENT * A,UDM_DSTR * dstr,const char * dbaddr)193 UdmMakeNodeQueryString(UDM_AGENT *A, UDM_DSTR *dstr, const char *dbaddr)
194 {
195   size_t i;
196   const UDM_VARLIST *Vars= &A->Conf->Vars;
197   size_t ps= UdmVarListFindInt(Vars, "ps", 10);
198   size_t np= UdmVarListFindInt(Vars, "np", 0);
199   size_t offs= UdmVarListFindInt(Vars, "offs", 0);
200   int group_by_site= UdmVarListFindBool(Vars, "GroupBySite", UDM_FALSE) &&
201                      (UdmVarListFindStr(Vars, "site", "")[0] == '\0');
202   int group_by_site_factor= group_by_site ? 3 : 1;
203   size_t newps= ((offs + ps * (np + 1)) * group_by_site_factor);
204 
205   UdmDSTRAppendSTR(dstr, dbaddr);
206   if (!strchr(dbaddr, '?'))
207     UdmDSTRAppend(dstr, "?", 1);
208   for (i= 0; i < Vars->nvars; i++)
209   {
210     const UDM_VAR *Var= &Vars->Var[i][0];
211     UDM_CONST_STR tmp, *str;
212     /**
213       Suppress all offset related variables, "ps" is calculated below.
214       Suppress dbnum, it should be only in the fron-end query string
215       and should not be in the node query string.
216     */
217     if (strncasecmp(UdmVarName(Var), UDM_CSTR_WITH_LEN("query.")) ||
218         !strncasecmp(UdmVarName(Var), UDM_CSTR_WITH_LEN("query.ps")) ||
219         !strncasecmp(UdmVarName(Var), UDM_CSTR_WITH_LEN("query.np")) ||
220         !strncasecmp(UdmVarName(Var), UDM_CSTR_WITH_LEN("query.offs")) ||
221         !strncasecmp(UdmVarName(Var), UDM_CSTR_WITH_LEN("dbnum")) ||
222         !(str= UdmVarGetConstStr(Var, &tmp)))
223       continue;
224     UdmDSTRAppend(dstr, "&", 1);
225     UdmDSTRAppendSTR(dstr, UdmVarName(Var) + 6);
226     UdmDSTRAppend(dstr, "=", 1);
227     UdmDSTRAppendURLEncode(dstr, str->str, str->length);
228   }
229   if (newps)
230   {
231     UdmDSTRAppend(dstr, UDM_CSTR_WITH_LEN("&ps="));
232     UdmDSTRAppendf(dstr, "%d", (int) newps);
233   }
234   return UDM_OK;
235 }
236 
237 
238 static udm_rc_t
UdmFindWordsSD(UDM_AGENT * A,UDM_DB * db,UDM_QUERY * Query,udm_bool_t takeover_doclist)239 UdmFindWordsSD(UDM_AGENT *A, UDM_DB *db,
240                UDM_QUERY *Query, udm_bool_t takeover_doclist)
241 {
242   UDM_DOCUMENT Inc;
243   const char *host= UdmVarListFindStr(UdmSDDBVars(db), "DBHost", "localhost");
244   const char *dbaddr= UdmVarListFindStr(UdmSDDBVars(db), "DBAddr", "");
245   int port= UdmVarListFindInt(UdmSDDBVars(db), "DBPort", 80);
246   udm_rc_t rc;
247   udm_timer_t ticks;
248   UDM_CONST_STR content;
249 
250   UdmDocInit(&Inc);
251   if (!UdmHTTPBufPtr(&Inc.Buf) &&
252       UDM_OK != UdmHTTPBufAlloc(&Inc.Buf, UDM_MAXDOCSIZE))
253     return UDM_ERROR;
254   Inc.Spider.read_timeout= UdmVarListFindInt(&A->Conf->Vars, "ReadTimeOut", UDM_READ_TIMEOUT);
255 
256   UDM_GETLOCK(A, UDM_LOCK_CONF);
257   if (strncasecmp(dbaddr, UDM_CSTR_WITH_LEN("file:")))
258   {
259     UDM_DSTR dstr;
260     UdmDSTRInit(&dstr, 256);
261     UdmMakeNodeQueryString(A, &dstr, dbaddr);
262     UdmURLParse(&Inc.CurURL, UdmDSTRPtr(&dstr));
263     UdmLog(A, UDM_LOG_ERROR, "DBAddr: %s", UdmDSTRPtr(&dstr));
264     UdmDSTRFree(&dstr);
265   }
266   else
267   {
268     if (strchr(dbaddr, '?') || strchr(dbaddr, '&'))
269     {
270       char *tmp= UdmStrdup(dbaddr), *param;
271       if (!tmp)
272         return UDM_ERROR;
273       if ((param= strchr(tmp, '?')) ||
274           (param= strchr(tmp, '&')))
275         *param= '\0';
276       UdmURLParse(&Inc.CurURL, tmp);
277       UdmFree(tmp);
278     }
279     else
280     {
281       UdmURLParse(&Inc.CurURL, dbaddr);
282     }
283   }
284   UDM_RELEASELOCK(A, UDM_LOCK_CONF);
285 
286   if (!strcmp(Inc.CurURL.schema, "http"))
287   {
288     UdmVarListReplaceStr(&Inc.RequestHeaders, "Host", host);
289     Inc.connp.hostname= (char*)UdmStrdup(host);
290     Inc.connp.port= port;
291 
292     if (UDM_OK != (rc= UdmHostLookup2(A, &A->Conf->Hosts, &Inc.connp)))
293     {
294       sprintf(A->Conf->errstr, "Host lookup failed: '%s'", host);
295       return rc;
296     }
297   }
298 
299   ticks= UdmStartTimer();
300   rc= UdmGetURL(A, &Inc);
301   UdmLog(A, UDM_LOG_DEBUG, "Received searchd response: %.2f", UdmStopTimer(&ticks));
302 
303   if (UDM_OK != rc)
304     return rc;
305 
306   UdmParseHTTPResponse(A, &Inc);
307   if (UdmHTTPBufContentToConstStr(&Inc.Buf, &content))
308     return UDM_ERROR;
309   UdmVarListLog2(A, &Inc.Sections, UDM_LOG_DEBUG, "Response");
310 
311   UdmLog(A, UDM_LOG_DEBUG, "Start parsing results");
312   ticks= UdmStartTimer();
313 
314   UdmQueryFromXML(A, Query, content.str, content.length, A->Conf->lcs);
315   UdmDocFree(&Inc);
316   UdmLog(A, UDM_LOG_DEBUG, "Stop parsing results: %.2f", UdmStopTimer(&ticks));
317   UdmLog(A, UDM_LOG_DEBUG, "searchd: %d rows, %d totalResults",
318                            (int) UdmResultNumRows(&Query->Res),
319                            (int) Query->stats.total_found);
320   if (A->Conf->DBList.nitems > 1 && takeover_doclist)
321     UdmResultTakeoverDocList(&UdmSDDB(db)->Query, &Query->Res);
322   return UDM_OK;
323 }
324 
325 
326 static udm_rc_t
UdmCachedCopyUnpack(UDM_DOCUMENT * Doc,const char * sval,size_t l)327 UdmCachedCopyUnpack(UDM_DOCUMENT *Doc, const char *sval, size_t l)
328 {
329 #ifdef HAVE_ZLIB
330   return UdmHTTPBufBase64Decode(&Doc->Buf, sval, l);
331 #endif
332   return UDM_ERROR;
333 }
334 
335 
336 static udm_rc_t
UdmDocActionSD(UDM_AGENT * A,UDM_DB * db,UDM_DOCUMENT * D,udm_doccmd_t cmd)337 UdmDocActionSD(UDM_AGENT *A, UDM_DB *db, UDM_DOCUMENT *D, udm_doccmd_t cmd)
338 {
339   udm_rc_t rc= UDM_OK;
340   if (cmd == UDM_DOCCMD_GET_CACHED_COPY)
341   {
342     UDM_QUERY Query;
343     UdmQueryInit(&Query);
344     /*
345        UDM_LOCK_CONF mutex is locked here,
346        UdmFindWordsSearchd expects unlocked mutex.
347        Let's unlock it and then lock again.
348     */
349     UDM_RELEASELOCK(A, UDM_LOCK_CONF);
350     rc= UdmFindWordsSD(A, db, &Query, UDM_FALSE); /*Keep Docs in Query*/
351     UDM_GETLOCK(A, UDM_LOCK_CONF);
352     if (UdmResultNumRows(&Query.Res))
353     {
354       UDM_DOCUMENT *tmpDoc= &Query.Res.Doc[0];
355       const UDM_VAR *cc= UdmVarListFind(&tmpDoc->Sections, "CachedCopyBase64");
356       if (cc && !UdmHTTPBufContent(&D->Buf))
357       {
358         UDM_CONST_STR valuebuf, *value= UdmVarGetConstStr(cc, &valuebuf);
359         if (UDM_OK == (rc= UdmHTTPBufAlloc(&D->Buf, UDM_MAXDOCSIZE)))
360           UdmCachedCopyUnpack(D, value->str, value->length);
361         UdmVarListAddLst(&D->Sections, &tmpDoc->Sections, NULL, "*");
362       }
363     }
364     UdmResultFree(&Query.Res);
365   }
366   return rc;
367 }
368 
369 
370 static udm_rc_t
not_implemented(UDM_DB * db,const char * action,int code)371 not_implemented(UDM_DB *db, const char *action, int code)
372 {
373   udm_snprintf(UdmSDDB(db)->errstr, sizeof(UdmSDDB(db)->errstr),
374                "%s(%d): not implemented", action, code);
375   return UDM_ERROR;
376 }
377 
378 
379 static udm_rc_t
UdmDBActionSD(UDM_AGENT * A,UDM_DB * db,udm_dbcmd_t cmd)380 UdmDBActionSD(UDM_AGENT *A, UDM_DB *db, udm_dbcmd_t cmd)
381 {
382   return not_implemented(db, "DBAction", cmd);
383 }
384 
385 
386 static udm_rc_t
UdmTrackSD(UDM_AGENT * A,UDM_DB * db,UDM_QUERY * Query)387 UdmTrackSD(UDM_AGENT *A, UDM_DB *db, UDM_QUERY *Query)
388 {
389   /*
390    Be silent if trackquery is not set "yes", complain otherwise.
391    TODO34: Make it possible to track into a different (e.g. SQL) database.
392   */
393   udm_bool_t trackquery= UdmVarListFindBool(UdmSDDBVars(db), "trackquery", UDM_FALSE);
394   return trackquery ? not_implemented(db, "ResultAction", UDM_QUERYCMD_TRACK) : UDM_OK;
395 }
396 
397 
398 static udm_rc_t
UdmServerActionSD(UDM_AGENT * A,UDM_DB * db,UDM_SERVERLIST * Srv,udm_srvcmd_t cmd)399 UdmServerActionSD(UDM_AGENT *A, UDM_DB *db, UDM_SERVERLIST *Srv, udm_srvcmd_t cmd)
400 {
401   return not_implemented(db, "ServerAction", cmd);
402 }
403 
404 
405 static udm_rc_t
UdmQueryWordForms(UDM_AGENT * A,UDM_DB * db,UDM_QUERY * Query)406 UdmQueryWordForms(UDM_AGENT *A, UDM_DB *db, UDM_QUERY *Query)
407 {
408   const char *sql= UdmVarListFindStr(&A->Conf->Vars, "SQLWordForms", NULL);
409   if (sql) /* TODO34 */
410   {
411     udm_snprintf(UdmSDDB(db)->errstr, sizeof(UdmSDDB(db)->errstr),
412                  "SQLWordForms is not supported by this DBAddr type");
413     UdmSDDB(db)->errcode= 1;
414     return UDM_ERROR;
415   }
416   return UDM_OK;
417 }
418 
419 
420 static udm_rc_t
UdmHrefActionSD(UDM_AGENT * A,UDM_DB * db,UDM_HREF * Href,udm_hrefcmd_t cmd)421 UdmHrefActionSD(UDM_AGENT *A, UDM_DB *db, UDM_HREF *Href, udm_hrefcmd_t cmd)
422 {
423   return not_implemented(db, "HrefAction", cmd);
424 }
425 
426 
427 static udm_bool_t
UdmSDDBKnownSchema(const char * schema)428 UdmSDDBKnownSchema(const char *schema)
429 {
430   return UDM_TEST(!strcasecmp(schema, "searchd") ||
431                   !strcasecmp(schema, "http") ||
432                   !strcasecmp(schema, "file"));
433 }
434 
435 
436 static udm_rc_t
UdmDBSetAddrSDFromURL(UDM_DB * db,UDM_URL * addr,const char * dbaddr)437 UdmDBSetAddrSDFromURL(UDM_DB *db, UDM_URL *addr, const char *dbaddr)
438 {
439   if (!(db->specific= UdmMalloc(sizeof(UDM_SDDB))))
440     return UDM_ERROR;
441 
442   UdmSDDBInit(UdmSDDB(db));
443   db->dbhandler= &udm_dbhandler_searchd;
444   UdmVarListReplaceStr(UdmSDDBVars(db), "DBAddr", dbaddr);
445   UdmVarListReplaceStr(UdmSDDBVars(db), "DBHost", addr->hostname);
446   if (addr->port)
447     UdmVarListReplaceInt(UdmSDDBVars(db), "DBPort", addr->port);
448   return UDM_OK;
449 }
450 
451 
452 static udm_rc_t
UdmDBSetAddrSD(UDM_DB * db,const char * dbaddr)453 UdmDBSetAddrSD(UDM_DB *db, const char *dbaddr)
454 {
455   udm_rc_t rc= UDM_NOTARGET;
456   UDM_URL addr;
457   UdmURLInit(&addr);
458   if (!UdmURLParse(&addr, dbaddr) && addr.schema &&
459        UdmSDDBKnownSchema(addr.schema))
460     rc= UdmDBSetAddrSDFromURL(db, &addr, dbaddr);
461   UdmURLFree(&addr);
462 
463   return rc;
464 }
465 
466 
467 static udm_rc_t
UdmDBCloseSD(UDM_DB * db)468 UdmDBCloseSD(UDM_DB *db)
469 {
470   if (db->specific)
471   {
472     UdmSDDBFree(UdmSDDB(db));
473     UDM_FREE(db->specific);
474   }
475   return UDM_OK;
476 }
477 
478 
479 static udm_rc_t
UdmDBInfoSD(UDM_DB * db,void * dst,size_t dstlen,size_t * bytes_written,udm_dbinfo_t info)480 UdmDBInfoSD(UDM_DB *db, void *dst, size_t dstlen, size_t *bytes_written,
481             udm_dbinfo_t info)
482 {
483   switch (info)
484   {
485     case UDM_DBINFO_IS_THREAD_SAFE:
486       if (!dstlen)
487         return UDM_ERROR;
488       ((udm_bool_t *) dst)[0]= UDM_TRUE;
489       *bytes_written= 1;
490       return UDM_OK;
491     case UDM_DBINFO_ERRCODE:
492       if (dstlen < sizeof(int))
493         return UDM_ERROR;
494       ((int *)dst)[0]= UdmSDDB(db)->errcode;
495       *bytes_written= sizeof(int);
496       return UDM_OK;
497     case UDM_DBINFO_ADDR:
498     {
499       const char *dbaddr= UdmVarListFindStr(UdmSDDBVars(db), "DBAddr", "<noaddr>");
500       if (dstlen < 1)
501         return UDM_ERROR;
502       *bytes_written= udm_snprintf((char*) dst, dstlen, "%s", dbaddr);
503       return UDM_OK;
504     }
505     case UDM_DBINFO_ERRSTR:
506     {
507       if (dstlen < 1)
508         return UDM_ERROR;
509       *bytes_written= udm_snprintf((char*) dst, dstlen, "%s", UdmSDDB(db)->errstr);
510       return UDM_OK;
511     }
512   }
513   return UDM_ERROR;
514 }
515 
516 
517 static udm_rc_t
UdmQueryActionSD(UDM_AGENT * A,UDM_DB * db,UDM_QUERY * Query,udm_querycmd_t cmd)518 UdmQueryActionSD(UDM_AGENT *A, UDM_DB *db,
519                  UDM_QUERY *Query, udm_querycmd_t cmd)
520 {
521   switch (cmd)
522   {
523     case UDM_QUERYCMD_FINDWORDS: return UdmFindWordsSD(A, db, Query, UDM_TRUE);
524     case UDM_QUERYCMD_DOCINFO:   return UdmQueryAddDocInfoSearchd(A, db, Query);
525     case UDM_QUERYCMD_TRACK:     return UdmTrackSD(A, db, Query);
526     case UDM_QUERYCMD_SUGGEST:   return UDM_OK;
527     case UDM_QUERYCMD_CLONES:    return UDM_OK;
528     case UDM_QUERYCMD_WORDFORMS: return UdmQueryWordForms(A, db, Query);
529     case UDM_QUERYCMD_TARGETS:
530     case UDM_QUERYCMD_CLEAR:
531     case UDM_QUERYCMD_INDEX:
532     case UDM_QUERYCMD_EXPIRE:
533     case UDM_QUERYCMD_REFERERS:
534     case UDM_QUERYCMD_DUMPDATA:
535     case UDM_QUERYCMD_REWRITE_URLDATA:
536     case UDM_QUERYCMD_REWRITE_LIMITS:
537     case UDM_QUERYCMD_REWRITE_POPULARITY:
538     case UDM_QUERYCMD_EXPORT:
539     case UDM_QUERYCMD_WORDSTAT:
540     case UDM_QUERYCMD_STATISTICS:
541       return not_implemented(db, "QueryAction", cmd);
542   }
543   return UDM_ERROR;
544 }
545 
546 
547 const UDM_DBHANDLER udm_dbhandler_searchd=
548 {
549   UdmDBSetAddrSD,      /*udm_rc_t (*Init)          (UDM_DB *db, const char *addr);*/
550   UdmDBCloseSD,        /*udm_rc_t (*Close)         (UDM_DB *db); */
551   UdmDBInfoSD,         /*udm_rc_t (*Info)          (UDM_DB *db, void *dst, size_t dstlen, size_t *bytes_written, udm_dbhandler_info_t info);*/
552   UdmQueryActionSD,    /*udm_rc_t (*QueryAction)   (UDM_AGENT *A, UDM_DB *db, UDM_QUERY *Query, udm_querycmd_t cmd);*/
553   UdmDBActionSD,       /*udm_rc_t (*DBAction)      (UDM_AGENT *A, UDM_DB *db, udm_dbcmd_t cmd);*/
554   UdmDocActionSD,      /*udm_rc_t (*DocumentAction)(UDM_AGENT *A, UDM_DB *db, UDM_DOCUMENT *D, udm_doccmt_t cmd);*/
555   UdmHrefActionSD,     /*udm_rc_t (*HrefAction)    (UDM_AGENT *A, UDM_DB *db, UDM_HREF *Href, udm_hrefcmd_t cmd);*/
556   UdmServerActionSD,   /*udm_rc_t (*ServerAction)  (UDM_AGENT *A, UDM_DB *db, UDM_SERVERLIST *Srv, udm_srvcmd_t cmd);*/
557 };
558