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