1 /*! \file namevaluetree.c
2 * \brief Implemetation of the NameValueTree helper object
3 * (includes XML parser).
4 *
5 * \author Rainer Gerhards <rgerhards@adiscon.com>
6 * \date 2003-08-04
7 * Initial version as part of liblogging 0.2 begun.
8 *
9 * Copyright 2002-2014
10 * Rainer Gerhards and Adiscon GmbH. All Rights Reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are
14 * met:
15 *
16 * * Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * * Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in
21 * the documentation and/or other materials provided with the
22 * distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include <assert.h>
38 #include <ctype.h>
39 #include "settings.h"
40 #include "liblogging.h"
41 #include "namevaluetree.h"
42 #include "stringbuf.h"
43
44 /* ################################################################# *
45 * private members *
46 * ################################################################# */
47 static srRetVal sbNVTXMLProcessXMLSTREAM(char **ppXML, sbNVTRObj *pRoot);
48
49 /**
50 * We can not use the usual sbNVTSearchKeySZ() method in this
51 * destructor, as we would invalidate the previous pointer while
52 * walking & deleting the list. So we need to walk it by using
53 * the raw pointers.
54 */
sbNVTRDestroy(sbNVTRObj * pThis)55 void sbNVTRDestroy(sbNVTRObj* pThis)
56 {
57 sbNVTEObj* pEntry;
58 sbNVTEObj* pNext;
59
60 sbNVTRCHECKVALIDOBJECT(pThis);
61
62 /* debug: printf("sbNVTRDestroy: %8.8x\n", pThis); */
63
64 pEntry = pThis->pFirst;
65 while(pEntry != NULL)
66 {
67 pNext = pEntry->pNext;
68 sbNVTEDestroy(pEntry);
69 pEntry = pNext;
70 }
71
72 /* unlink from parent! */
73 sbNVTRUnlinkFromParent(pThis);
74
75 SRFREEOBJ(pThis);
76 }
77
78
sbNVTEDestroy(sbNVTEObj * pThis)79 void sbNVTEDestroy(sbNVTEObj* pThis)
80 {
81 sbNVTECHECKVALIDOBJECT(pThis);
82
83 /* debug: printf("sbNVTEDestroy: %8.8x\n", pThis); */
84 if(pThis->pszKey != NULL)
85 free(pThis->pszKey);
86
87 if(pThis->pszValue != NULL)
88 free(pThis->pszValue);
89
90 if(pThis->pCDATA != NULL)
91 free(pThis->pCDATA);
92
93 if(pThis->pXMLProps != NULL)
94 sbNVTRDestroy(pThis->pXMLProps);
95
96 if(pThis->pChild != NULL)
97 sbNVTRDestroy(pThis->pChild);
98
99 if(pThis->pUsr != NULL)
100 (pThis->pUsrDestroy)(pThis->pUsr);
101
102 SRFREEOBJ(pThis);
103 }
104
105
106 /**
107 * Remove WHITESPACE (see ABNF \ref sbNVTRParseXML) from the XML stream.
108 *
109 * \param pp [in/out] pointer to XML stream. Updated to reflect new position.
110 */
sbNVTXMLEatWHITESPACE(char ** ppXML)111 static void sbNVTXMLEatWHITESPACE(char **ppXML)
112 {
113 assert(ppXML != NULL);
114 while(isspace(**ppXML))
115 (*ppXML)++;
116 }
117
118
119 /**
120 * Retrieve an ESCSEQ (see ABNF \ref sbNVTRParseXML) from the XML stream.
121 *
122 * \param pp [in/out] pointer to XML stream. Updated to reflect new position.
123 * \retval pointer to text of ESCSEQ or NULL, if a memory allocation error occured.
124 */
sbNTVXMLReadESCSEQ(char ** ppXML)125 static char* sbNTVXMLReadESCSEQ(char **ppXML)
126 {
127 sbStrBObj *pStrB;
128
129 assert(ppXML != NULL);
130
131 if((pStrB = sbStrBConstruct()) == NULL)
132 return NULL;
133 sbStrBSetAllocIncrement(pStrB, 16);
134
135 while( (**ppXML != ';')
136 && (**ppXML != '\0'))
137 {
138 sbStrBAppendChar(pStrB, **ppXML);
139 (*ppXML)++;
140 }
141
142 if(**ppXML != '\0')
143 (*ppXML)++; /* need to eat the ";" */
144
145 return sbStrBFinish(pStrB);
146 }
147
148
149 /**
150 * Retrieve an XMLVALUE (see ABNF \ref sbNVTRParseXML) from the XML stream.
151 *
152 * \param pp [in/out] pointer to XML stream. Updated to reflect new position.
153 * \param cTerm [in] terminating character. This is the counterpart to the
154 * character the initiated the call to XMLVALUE. With BEEP,
155 * we expect it to be ', but it may also be ".
156 * \retval pointer to XMLVALUE or NULL, if an error occured.
157 */
sbNVTXMLReadXMLVALUE(char ** ppXML,char cTerm)158 static char* sbNVTXMLReadXMLVALUE(char **ppXML, char cTerm)
159 {
160 sbStrBObj *pStrB;
161 char *pszESC;
162
163 assert(ppXML != NULL);
164
165 if((pStrB = sbStrBConstruct()) == NULL)
166 return NULL;
167
168 while( (**ppXML != cTerm)
169 && (**ppXML != '\0'))
170 {
171 if(**ppXML == '&')
172 { /* we have an escape sequence, need to handle this */
173 (*ppXML)++; /* eat the "&" */
174 if((pszESC = sbNTVXMLReadESCSEQ(ppXML)) == NULL)
175 return NULL;
176 if(!strcmp(pszESC, "gt"))
177 sbStrBAppendChar(pStrB, '>');
178 else if(!strcmp(pszESC, "lt"))
179 sbStrBAppendChar(pStrB, '<');
180 else if(!strcmp(pszESC, "amp"))
181 sbStrBAppendChar(pStrB, '&');
182 else if(!strcmp(pszESC, "apos"))
183 sbStrBAppendChar(pStrB, ';');
184 else if(!strcmp(pszESC, "quot"))
185 sbStrBAppendChar(pStrB, '"');
186 else
187 {
188 free(pszESC);
189 return NULL;
190 }
191 free(pszESC);
192 }
193 else
194 { /* just a plain character */
195 sbStrBAppendChar(pStrB, **ppXML);
196 (*ppXML)++;
197 }
198 }
199
200 return sbStrBFinish(pStrB);
201
202 }
203
204
205 /**
206 * Retrieve an XMLNAME (see ABNF \ref sbNVTRParseXML) from the XML stream.
207 *
208 * \param pp [in/out] pointer to XML stream. Updated to reflect new position.
209 * \retval pointer to XMLNAME or NULL, if a memory allocation error occured.
210 */
sbNVTXMLReadXMLNAME(char ** ppXML)211 static char* sbNVTXMLReadXMLNAME(char **ppXML)
212 {
213 sbStrBObj *pStrB;
214
215 assert(ppXML != NULL);
216
217 if((pStrB = sbStrBConstruct()) == NULL)
218 return NULL;
219 sbStrBSetAllocIncrement(pStrB, 64);
220
221 while( !isspace(**ppXML)
222 && (**ppXML != '\0')
223 && (**ppXML != '<')
224 && (**ppXML != '>')
225 && (**ppXML != '=')
226 && (**ppXML != ';')
227 && (**ppXML != '/'))
228 {
229 sbStrBAppendChar(pStrB, **ppXML);
230 (*ppXML)++;
231 }
232
233 return sbStrBFinish(pStrB);
234 }
235
236
237 /**
238 * Retrieve a CDATAVALUE (see ABNF \ref sbNVTRParseXML) from the XML stream.
239 *
240 * \param pp [in/out] pointer to XML stream. Updated to reflect new position.
241 * \retval pointer to CDATA value or NULL, if a memory allocation error occured.
242 */
sbNVTXMLReadCDATAVALUE(char ** ppXML)243 static char* sbNVTXMLReadCDATAVALUE(char **ppXML)
244 {
245 sbStrBObj *pStrB;
246
247 assert(ppXML != NULL);
248
249 if((pStrB = sbStrBConstruct()) == NULL)
250 return NULL;
251
252 while( !isspace(**ppXML)
253 && (**ppXML != '\0')
254 && (**ppXML != ']'))
255 {
256 sbStrBAppendChar(pStrB, **ppXML);
257 (*ppXML)++;
258 }
259
260 return sbStrBFinish(pStrB);
261 }
262
263
264 /**
265 * Retrieve a TAG (see ABNF \ref sbNVTRParseXML) from the XML stream.
266 *
267 * \param pp [in/out] pointer to XML stream. Updated to reflect new position.
268 * \retval pointer to XMLNAME or NULL, if a memory allocation error occured.
269 */
sbNVTXMLReadTAG(char ** ppXML)270 static char* sbNVTXMLReadTAG(char **ppXML)
271 {
272 return(sbNVTXMLReadXMLNAME(ppXML));
273 }
274
275
276 /**
277 * Process a PARAM (see ABNF \ref sbNVTRParseXML) from the XML stream
278 * and add it to the NameValueTree.
279 *
280 * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position.
281 * \param pRoot Pointer to the list the element is to be added to.
282 */
sbNVTXMLProcessPARAM(char ** ppXML,sbNVTRObj * pRoot)283 static srRetVal sbNVTXMLProcessPARAM(char **ppXML, sbNVTRObj *pRoot)
284 {
285 char* pName;
286 char *pValue;
287 char cTerm;
288 sbNVTEObj *pEntry;
289
290 assert(ppXML != NULL);
291 sbNVTRCHECKVALIDOBJECT(pRoot);
292
293 if((pName = sbNVTXMLReadXMLNAME(ppXML)) == NULL)
294 return SR_RET_OUT_OF_MEMORY;
295
296 /* check if we have a value and, if yes, retrieve it */
297 if(**ppXML == '=')
298 {
299 (*ppXML)++; /* eat "=" */
300 if(**ppXML == '\'')
301 cTerm = '\'';
302 else if(**ppXML == '"')
303 cTerm = '"';
304 else
305 return SR_RET_XML_INVALID_PARAMTAG;
306
307 (*ppXML)++; /* eat delimiter */
308 if((pValue = sbNVTXMLReadXMLVALUE(ppXML, cTerm)) == NULL)
309 return SR_RET_OUT_OF_MEMORY;
310
311 /* check terminating character */
312 if(**ppXML != cTerm)
313 return SR_RET_XML_INVALID_TERMINATOR;
314
315 (*ppXML)++; /* eat terminating char */
316 }
317 else
318 pValue = NULL;
319
320 /* OK, we have name and value. Now let's add it to the list. */
321 if((pEntry = sbNVTAddEntry(pRoot)) == NULL)
322 return SR_RET_OUT_OF_MEMORY;
323
324 /* the string buffers are handed over to the list,
325 * so they MUST NOT be freed.
326 */
327 sbNVTESetKeySZ(pEntry, pName, FALSE);
328 sbNVTESetValueSZ(pEntry, pValue, FALSE);
329
330 return SR_RET_OK;
331 }
332
333
334 /**
335 * Process a TAGwPARAMS (see ABNF \ref sbNVTRParseXML) from the XML stream
336 * and add it to the NameValueTree.
337 *
338 * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position.
339 * \param pEntry Pointer to the current list entry.
340 */
sbNVTXMLProcessTAGwPARAMS(char ** ppXML,sbNVTEObj * pEntry)341 static srRetVal sbNVTXMLProcessTAGwPARAMS(char **ppXML, sbNVTEObj *pEntry)
342 {
343 char *pTag;
344 sbNVTRObj *pParamRoot = NULL;
345
346 assert(ppXML != NULL);
347 sbNVTECHECKVALIDOBJECT(pEntry);
348
349 if((pTag = sbNVTXMLReadTAG(ppXML)) == NULL)
350 return SR_RET_OUT_OF_MEMORY;
351 sbNVTESetKeySZ(pEntry, pTag, FALSE);
352
353 sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */
354
355 /* now check if we have params */
356 while((**ppXML != '\0') && (**ppXML != '/') && (**ppXML != '>'))
357 { /* OK, we have params, so now let's retrieve
358 * and store them. We need to create a new list
359 * root for the parameter tree now if we do not
360 * have one already.
361 */
362 if(pParamRoot == NULL)
363 if((pParamRoot = sbNVTRConstruct()) == NULL)
364 return SR_RET_OUT_OF_MEMORY;
365
366 pEntry->pXMLProps = pParamRoot;
367 sbNVTXMLProcessPARAM(ppXML, pParamRoot);
368 sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */
369 }
370
371 return SR_RET_OK;
372 }
373
374
sbNVTXMLEscapePCDATAintoStrB(char * pszToEscape,sbStrBObj * pStr)375 srRetVal sbNVTXMLEscapePCDATAintoStrB(char *pszToEscape, sbStrBObj *pStr)
376 {
377 srRetVal iRet;
378 sbSTRBCHECKVALIDOBJECT(pStr);
379
380 if(pszToEscape == NULL)
381 return SR_RET_OK;
382
383 while(*pszToEscape)
384 {
385 if(*pszToEscape == '<')
386 {
387 if((iRet = sbStrBAppendStr(pStr, "<")) != SR_RET_OK) return iRet;
388 }
389 else if(*pszToEscape == '&')
390 {
391 if((iRet = sbStrBAppendStr(pStr, """)) != SR_RET_OK) return iRet;
392 }
393 else
394 {
395 if((iRet = sbStrBAppendChar(pStr, *pszToEscape)) != SR_RET_OK) return iRet;
396 }
397 pszToEscape++;
398 }
399
400 return SR_RET_OK;
401 }
402
403
sbNVTXMLEscapePCDATA(char * pszToEscape)404 char* sbNVTXMLEscapePCDATA(char *pszToEscape)
405 {
406 sbStrBObj *pStrBuf;
407
408 if(pszToEscape == NULL)
409 return NULL;
410
411 if((pStrBuf = sbStrBConstruct()) == NULL)
412 return NULL;
413
414 if(sbNVTXMLEscapePCDATAintoStrB(pszToEscape, pStrBuf) != SR_RET_OK)
415 {
416 sbStrBDestruct(pStrBuf);
417 return NULL;
418 }
419
420 return sbStrBFinish(pStrBuf);
421 }
422
423 /**
424 * Process a XMLNODE and a XMLCONTAINER (see ABNF \ref sbNVTRParseXML) from the XML stream
425 * and add it to the NameValueTree.
426 *
427 * \note Due to the syntaxes, we can not distinguish if we have a plain XMLNODE
428 * or a container at this stage. As such, this method checks if we have
429 * a container (no closing tag) and if so, it processes the container.
430 *
431 * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position.
432 * \param pEntry Pointer to the current list entry.
433 */
sbNVTXMLProcessXMLNODE(char ** ppXML,sbNVTEObj * pEntry)434 static srRetVal sbNVTXMLProcessXMLNODE(char **ppXML, sbNVTEObj *pEntry)
435 {
436 int iRet;
437 char *pCloseTag;
438 char *pValue;
439 char **ppXMLSave;
440 sbNVTRObj *pChildRoot = NULL;
441
442 assert(ppXML != NULL);
443 sbNVTECHECKVALIDOBJECT(pEntry);
444
445 if((iRet = sbNVTXMLProcessTAGwPARAMS(ppXML, pEntry)) != SR_RET_OK)
446 return iRet;
447
448 if(*((*ppXML)++) == '/')
449 /* we have a plain node */
450 if(**ppXML == '>')
451 {
452 (*ppXML)++;
453 return SR_RET_OK;
454 }
455 else
456 return SR_RET_MISSING_CLOSE_BRACE;
457
458 /* We now need to check if we have an XMLVALUE or an XMLCONTAINER.
459 * to do so, we look at the first non-whitespace chartert. If it
460 * is '<', then we have an XMLCONTAINER. If it is not, we have an
461 * XMLVALUE. In the later case, we need to go back to the front of
462 * the whitespace, as the whitespaces, too, belongs to the
463 * XMLVALUE. We use a temporary pointer to do this.
464 */
465
466 ppXMLSave = ppXML;
467 sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */
468 if(**ppXML == '<')
469 { /* OK, we have a container */
470 if((pChildRoot = sbNVTRConstruct()) == NULL)
471 return SR_RET_OUT_OF_MEMORY;
472
473 if((iRet = sbNVTESetChild(pEntry, pChildRoot)) != SR_RET_OK)
474 return iRet;
475
476 if((iRet = sbNVTXMLProcessXMLSTREAM(ppXML, pChildRoot)) != SR_RET_OK)
477 return iRet;
478 }
479 else
480 { /* we have an XMLVALUE and need to retrieve that */
481 ppXML = ppXMLSave; /* restore previous char position */
482 if((pValue = sbNVTXMLReadXMLVALUE(ppXML, '<')) == NULL)
483 return SR_RET_OUT_OF_MEMORY;
484
485 if((iRet = sbNVTESetValueSZ(pEntry, pValue, FALSE)) != SR_RET_OK)
486 return iRet;
487 }
488
489 /* now checking the close tag */
490 if(**ppXML != '<')
491 return SR_RET_XML_MISSING_CLOSETAG;
492
493 (*ppXML)++;
494 if(**ppXML != '/')
495 return SR_RET_XML_MISSING_CLOSETAG;
496
497 (*ppXML)++;
498
499 /* OK, now retrieve the closing tag name */
500 sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */
501 if((pCloseTag = sbNVTXMLReadTAG(ppXML)) == NULL)
502 return SR_RET_OUT_OF_MEMORY;
503
504 if(**ppXML != '>')
505 return SR_RET_MISSING_CLOSE_BRACE;
506 (*ppXML)++; /* eat ">" */
507
508 /* check if start and end tag match */
509
510 if(strcmp(pEntry->pszKey, pCloseTag))
511 return SR_RET_XML_TAG_MISMATCH;
512
513 free(pCloseTag); /* no longer needed */
514
515 return SR_RET_OK;
516 }
517
518
519 /**
520 * Process a CDATA (see ABNF \ref sbNVTRParseXML) from the XML stream
521 * and add it to the NameValueTree.
522 *
523 * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position.
524 * Needs to point at the first [ in the stream.
525 * \param pEntry Pointer to the current list entry.
526 */
sbNVTXMLProcessCDATA(char ** ppXML,sbNVTEObj * pEntry)527 static srRetVal sbNVTXMLProcessCDATA(char **ppXML, sbNVTEObj *pEntry)
528 {
529 assert(ppXML != NULL);
530 sbNVTECHECKVALIDOBJECT(pEntry);
531
532 /* check header */
533 if(**ppXML != '[')
534 return SR_RET_XML_INVALID_CDATA_HDR;
535 if(*(++*ppXML) != 'C')
536 return SR_RET_XML_INVALID_CDATA_HDR;
537 if(*(++*ppXML) != 'D')
538 return SR_RET_XML_INVALID_CDATA_HDR;
539 if(*(++*ppXML) != 'A')
540 return SR_RET_XML_INVALID_CDATA_HDR;
541 if(*(++*ppXML) != 'T')
542 return SR_RET_XML_INVALID_CDATA_HDR;
543 if(*(++*ppXML) != 'A')
544 return SR_RET_XML_INVALID_CDATA_HDR;
545 if(*(++*ppXML) != '[')
546 return SR_RET_XML_INVALID_CDATA_HDR;
547 (*ppXML)++; /* eat '[' */
548
549 /* Now retrieve & set CDATA pointer. We should eventually
550 * create a specific method for this, but on the other
551 * hand it is only done in exactly this one case...
552 */
553 if((pEntry->pCDATA = sbNVTXMLReadCDATAVALUE(ppXML)) == NULL)
554 return SR_RET_OUT_OF_MEMORY;
555
556 /* check trailer */
557 if(**ppXML != ']')
558 return SR_RET_XML_INVALID_CDATA_TRAIL;
559 if(*(++*ppXML) != ']')
560 return SR_RET_XML_INVALID_CDATA_TRAIL;
561 if(*(++*ppXML) != '>')
562 return SR_RET_XML_INVALID_CDATA_TRAIL;
563 (*ppXML)++; /* eat '<' */
564
565 return SR_RET_OK;
566 }
567
568 /**
569 * Process a XMLELEMENT (see ABNF \ref sbNVTRParseXML) from the XML stream
570 * and add it to the NameValueTree.
571 *
572 * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position.
573 * \param pEntry Pointer to the current list entry.
574 */
sbNVTXMLProcessXMLELEMENT(char ** ppXML,sbNVTEObj * pEntry)575 static srRetVal sbNVTXMLProcessXMLELEMENT(char **ppXML, sbNVTEObj *pEntry)
576 {
577 srRetVal iRet;
578
579 assert(ppXML != NULL);
580 sbNVTECHECKVALIDOBJECT(pEntry);
581
582 if(**ppXML != '<')
583 return SR_RET_XML_MISSING_OPENTAG;
584 (*ppXML)++; /* eat '<' */
585
586 if(**ppXML == '!')
587 {
588 (*ppXML)++; /* eat '!' */
589 if((iRet = sbNVTXMLProcessCDATA(ppXML, pEntry)) != SR_RET_OK)
590 return iRet;
591 }
592 else
593 if((iRet = sbNVTXMLProcessXMLNODE(ppXML, pEntry)) != SR_RET_OK)
594 return iRet;
595
596 sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */
597
598 return SR_RET_OK;
599 }
600
601 /**
602 * Process a XMLSTREAM (see ABNF \ref sbNVTRParseXML) from the XML stream
603 * and add it to the NameValueTree.
604 *
605 * This creates one new entry element for each entree it finds.
606 *
607 * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position.
608 * \param pRoot Pointer to the current list root.
609 */
sbNVTXMLProcessXMLSTREAM(char ** ppXML,sbNVTRObj * pRoot)610 static srRetVal sbNVTXMLProcessXMLSTREAM(char **ppXML, sbNVTRObj *pRoot)
611 {
612 srRetVal iRet;
613 sbNVTEObj *pEntry;
614
615 assert(ppXML != NULL);
616 sbNVTRCHECKVALIDOBJECT(pRoot);
617
618 while((**ppXML != '\0') && !((**ppXML == '<') && (*(*ppXML + 1)) == '/'))
619 {
620 sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */
621
622 if(**ppXML == '\0')
623 {
624 return SR_RET_OK;
625 }
626 if((pEntry = sbNVTAddEntry(pRoot)) == NULL)
627 return SR_RET_OUT_OF_MEMORY;
628
629 if((iRet = sbNVTXMLProcessXMLELEMENT(ppXML, pEntry)) != SR_RET_OK)
630 return iRet;
631 }
632
633 return SR_RET_OK;
634 }
635
636
637 /* ################################################################# *
638 * public members *
639 * ################################################################# */
640
641
sbNVTRConstruct(void)642 sbNVTRObj* sbNVTRConstruct(void)
643 {
644 sbNVTRObj *pThis;
645
646 if((pThis = calloc(1, sizeof(sbNVTRObj))) == NULL)
647 return NULL;
648
649 pThis->OID = OIDsbNVTR;
650 pThis->pFirst = NULL;
651 pThis->pLast = NULL;
652 pThis->pParent = NULL;
653
654 /* debug: printf("sbNVTRConstuct: %8.8x\n", pThis); */
655 return(pThis);
656 }
657
658
sbNVTEConstruct(void)659 sbNVTEObj* sbNVTEConstruct(void)
660 {
661 sbNVTEObj *pThis;
662
663 if((pThis = calloc(1, sizeof(sbNVTEObj))) == NULL)
664 return NULL;
665
666 pThis->OID = OIDsbNVTE;
667 pThis->pCDATA = NULL;
668 pThis->pChild = NULL;
669 pThis->pNext = NULL;
670 pThis->pszKey = NULL;
671 pThis->pszValue = NULL;
672 pThis->pUsr = NULL;
673 pThis->pXMLProps = NULL;
674 pThis->uKey = 0;
675 pThis->uKeyPresent = FALSE;
676 pThis->uValue = 0;
677 pThis->bIsSetUValue = FALSE;
678
679 /* debug: printf("sbNVTeConstuct: %8.8x\n", pThis); */
680 return(pThis);
681 }
682
683
sbNVTESetChild(sbNVTEObj * pEntry,sbNVTRObj * pChildRoot)684 srRetVal sbNVTESetChild(sbNVTEObj *pEntry, sbNVTRObj *pChildRoot)
685 {
686 sbNVTECHECKVALIDOBJECT(pEntry);
687 sbNVTRCHECKVALIDOBJECT(pChildRoot);
688
689 pEntry->pChild = pChildRoot;
690
691 return SR_RET_OK;
692 }
693
694
sbNVTAddEntry(sbNVTRObj * pRoot)695 sbNVTEObj* sbNVTAddEntry(sbNVTRObj* pRoot)
696 {
697 sbNVTEObj * pThis;
698
699 sbNVTRCHECKVALIDOBJECT(pRoot);
700
701 if((pThis = sbNVTEConstruct()) == NULL)
702 return NULL;
703
704 if(pRoot->pLast == NULL)
705 { /* empty list */
706 pRoot->pLast = pRoot->pFirst = pThis;
707 }
708 else
709 { /* add to list */
710 sbNVTECHECKVALIDOBJECT(pRoot->pLast);
711 assert(pRoot->pLast->pNext == NULL);
712
713 pRoot->pLast->pNext = pThis;
714 pRoot->pLast = pThis;
715 }
716 return(pThis);
717 }
718
719
sbNVTRRemoveFirst(sbNVTRObj * pRoot)720 srRetVal sbNVTRRemoveFirst(sbNVTRObj* pRoot)
721 {
722 sbNVTEObj *pEntry;
723 sbNVTRCHECKVALIDOBJECT(pRoot);
724
725 if((pEntry = sbNVTUnlinkElement(pRoot)) != NULL)
726 sbNVTEDestroy(pEntry);
727
728 return SR_RET_OK;
729 }
730
731
sbNVTUnlinkElement(sbNVTRObj * pRoot)732 sbNVTEObj* sbNVTUnlinkElement(sbNVTRObj* pRoot)
733 {
734 sbNVTEObj *pThis;
735
736 sbNVTRCHECKVALIDOBJECT(pRoot);
737
738 if(pRoot->pFirst == NULL)
739 return NULL;
740
741 pThis = pRoot->pFirst;
742 pRoot->pFirst = pThis->pNext;
743 if(pRoot->pFirst == NULL)
744 pRoot->pLast = NULL;
745
746 return(pThis);
747 }
748
749
750
sbNVTSearchKeySZ(sbNVTRObj * pRoot,sbNVTEObj * pStart,char * pszSearch)751 sbNVTEObj* sbNVTSearchKeySZ(sbNVTRObj* pRoot, sbNVTEObj* pStart, char* pszSearch)
752 {
753 sbNVTEObj *pThis;
754
755 sbNVTRCHECKVALIDOBJECT(pRoot);
756
757 pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext;
758
759 while(pThis != NULL)
760 {
761 if(pszSearch == NULL)
762 break;
763 /* attention: order of conditions is vitally important! */
764 if((pThis->pszKey != NULL) && !strcmp(pThis->pszKey, pszSearch))
765 break; /* found it! */
766 pThis = pThis->pNext;
767 }
768
769 return(pThis);
770 }
771
772
773
sbNVTSearchKeyU(sbNVTRObj * pRoot,sbNVTEObj * pStart,unsigned uSearch)774 sbNVTEObj* sbNVTSearchKeyU(sbNVTRObj* pRoot, sbNVTEObj* pStart, unsigned uSearch)
775 {
776 sbNVTEObj *pThis;
777
778 sbNVTRCHECKVALIDOBJECT(pRoot);
779
780 pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext;
781
782 while(pThis != NULL)
783 {
784 if((pThis->uKeyPresent == TRUE) && (pThis->uKey == uSearch))
785 break; /* found it! */
786 pThis = pThis->pNext;
787 }
788
789 return(pThis);
790 }
791
792
sbNVTSearchKeyUAndPrev(sbNVTRObj * pRoot,sbNVTEObj * pStart,unsigned uSearch,sbNVTEObj ** ppPrevEntry)793 sbNVTEObj* sbNVTSearchKeyUAndPrev(sbNVTRObj* pRoot, sbNVTEObj* pStart, unsigned uSearch, sbNVTEObj** ppPrevEntry)
794 {
795 sbNVTEObj *pThis;
796 sbNVTEObj *pPrev = NULL;
797
798 sbNVTRCHECKVALIDOBJECT(pRoot);
799
800 pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext;
801
802 while(pThis != NULL)
803 {
804 if((pThis->uKeyPresent == TRUE) && (pThis->uKey == uSearch))
805 break; /* found it! */
806 pPrev = pThis;
807 pThis = pThis->pNext;
808 }
809
810 *ppPrevEntry = pPrev;
811 return(pThis);
812 }
813
sbNVTSearchpUsrAndPrev(sbNVTRObj * pRoot,sbNVTEObj * pStart,void * pUsr,sbNVTEObj ** ppPrevEntry)814 sbNVTEObj* sbNVTSearchpUsrAndPrev(sbNVTRObj* pRoot, sbNVTEObj* pStart, void *pUsr, sbNVTEObj** ppPrevEntry)
815 {
816 sbNVTEObj *pThis;
817 sbNVTEObj *pPrev = NULL;
818
819 sbNVTRCHECKVALIDOBJECT(pRoot);
820
821 pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext;
822
823 while(pThis != NULL)
824 {
825 if(pThis->pUsr == pUsr)
826 break; /* found it! */
827 pPrev = pThis;
828 pThis = pThis->pNext;
829 }
830
831 *ppPrevEntry = pPrev;
832 return(pThis);
833 }
834
835
sbNVTEUtilStrDup(char * pszStrToDup)836 char* sbNVTEUtilStrDup(char *pszStrToDup)
837 {
838 size_t iLen;
839 char* pszBuf;
840
841 assert(pszStrToDup != NULL);
842
843 iLen = strlen(pszStrToDup);
844 /* we use malloc() below for performance
845 * reasons - after all, the buffer is immediately
846 * overwritten.
847 */
848 if((pszBuf = malloc((iLen+1) * sizeof(char))) == NULL)
849 return NULL;
850 memcpy(pszBuf, pszStrToDup, (iLen+1) * sizeof(char));
851 return(pszBuf);
852 }
853
854
sbNVTESetKeySZ(sbNVTEObj * pThis,char * pszKey,int bCopy)855 srRetVal sbNVTESetKeySZ(sbNVTEObj* pThis, char* pszKey, int bCopy)
856 {
857 char *pszBuf;
858 sbNVTECHECKVALIDOBJECT(pThis);
859
860 if(bCopy == TRUE)
861 {
862 if((pszBuf = sbNVTEUtilStrDup(pszKey)) == NULL)
863 return SR_RET_ERR;
864 }
865 else
866 pszBuf = pszKey;
867
868 if(pThis->pszKey != NULL)
869 free(pThis->pszKey);
870
871 pThis->pszKey = pszBuf;
872 return SR_RET_OK;
873 }
874
875
sbNVTESetValueSZ(sbNVTEObj * pThis,char * pszVal,int bCopy)876 srRetVal sbNVTESetValueSZ(sbNVTEObj* pThis, char* pszVal, int bCopy)
877 {
878 char *pszBuf;
879 sbNVTECHECKVALIDOBJECT(pThis);
880
881 if(bCopy == TRUE)
882 {
883 if((pszBuf = sbNVTEUtilStrDup(pszVal)) == NULL)
884 return SR_RET_ERR;
885 }
886 else
887 pszBuf = pszVal;
888
889 if(pThis->pszValue != NULL)
890 free(pThis->pszValue);
891
892 pThis->pszValue = pszBuf;
893 return SR_RET_OK;
894 }
895
896
sbNVTESetKeyU(sbNVTEObj * pThis,unsigned uKey)897 srRetVal sbNVTESetKeyU(sbNVTEObj* pThis, unsigned uKey)
898 {
899 sbNVTECHECKVALIDOBJECT(pThis);
900
901 pThis->uKey = uKey;
902 pThis->uKeyPresent = TRUE;
903 return SR_RET_OK;
904 }
905
906
sbNVTEUnsetKeyU(sbNVTEObj * pThis)907 srRetVal sbNVTEUnsetKeyU(sbNVTEObj* pThis)
908 {
909 sbNVTECHECKVALIDOBJECT(pThis);
910
911 pThis->uKeyPresent = FALSE;
912 return SR_RET_OK;
913 }
914
sbNVTESetUsrPtr(sbNVTEObj * pThis,void * pPtr,void (pPtrDestroy)(void *))915 srRetVal sbNVTESetUsrPtr(sbNVTEObj* pThis, void *pPtr, void(pPtrDestroy)(void*))
916 {
917 sbNVTECHECKVALIDOBJECT(pThis);
918
919 if(pPtrDestroy == NULL)
920 return SR_RET_INVALID_DESTRUCTOR;
921
922 pThis->pUsr = pPtr;
923 pThis->pUsrDestroy = pPtrDestroy;
924
925 return SR_RET_OK;
926 }
927
928
sbNVTESetValueU(sbNVTEObj * pThis,unsigned uVal)929 srRetVal sbNVTESetValueU(sbNVTEObj* pThis, unsigned uVal)
930 {
931 sbNVTECHECKVALIDOBJECT(pThis);
932
933 pThis->uValue = uVal;
934 pThis->bIsSetUValue = TRUE;
935 return SR_RET_OK;
936 }
937
938
sbNVTEGetValueU(sbNVTEObj * pThis,unsigned * puValue)939 srRetVal sbNVTEGetValueU(sbNVTEObj* pThis, unsigned* puValue)
940 {
941 char *pBuf;
942 unsigned u;
943
944 sbNVTECHECKVALIDOBJECT(pThis);
945 assert(puValue != NULL);
946
947 if(pThis->bIsSetUValue)
948 {
949 *puValue = pThis->uValue;
950 }
951 else
952 { /* try to convert the szValue */
953 if(pThis->pszValue == NULL)
954 return SR_RET_NO_VALUE;
955 else
956 {
957 pBuf = pThis->pszValue;
958 u = 0;
959 while(*pBuf)
960 {
961 if(!isdigit(*pBuf))
962 return SR_RET_NO_VALUE;
963 u = u * 10 + (*pBuf - '0');
964 pBuf++;
965 }
966 pThis->uValue = u;
967 /* Please note: this can cause an error if the szValue
968 * is being re-set. Things then go out of sync. This is
969 * no issue for the current library, but may be worth
970 * looking into if the lib use is extended.
971 */
972 pThis->bIsSetUValue = TRUE;
973 *puValue = u;
974 }
975 }
976
977 return SR_RET_OK;
978 }
979
980
sbNVTEUnsetUsrPtr(sbNVTEObj * pEntry)981 void sbNVTEUnsetUsrPtr(sbNVTEObj *pEntry)
982 {
983 sbNVTECHECKVALIDOBJECT(pEntry);
984
985 pEntry->pUsr = NULL;
986 pEntry->pUsrDestroy = NULL;
987
988 }
989
990
sbNVTRUnlinkFromParent(sbNVTRObj * pRoot)991 void sbNVTRUnlinkFromParent(sbNVTRObj *pRoot)
992 {
993 sbNVTRCHECKVALIDOBJECT(pRoot);
994
995 if(pRoot->pParent != NULL)
996 pRoot->pParent->pChild = NULL;
997 }
998
999
sbNVTEUnlinkFromList(sbNVTRObj * pRoot,sbNVTEObj * pEntry,sbNVTEObj * pPrev)1000 void sbNVTEUnlinkFromList(sbNVTRObj *pRoot, sbNVTEObj* pEntry, sbNVTEObj* pPrev)
1001 {
1002 sbNVTRCHECKVALIDOBJECT(pRoot);
1003 sbNVTECHECKVALIDOBJECT(pEntry);
1004
1005 if(pPrev == NULL)
1006 {
1007 pRoot->pFirst = pEntry->pNext;
1008 if(pRoot->pLast == pEntry)
1009 pRoot->pLast = NULL;
1010 }
1011 else
1012 {
1013 pPrev->pNext = pEntry->pNext;
1014 if(pRoot->pLast == pEntry)
1015 pRoot->pLast = pPrev;
1016 }
1017 }
1018
1019
sbNVTRRemoveKeyU(sbNVTRObj * pRoot,unsigned uKey)1020 srRetVal sbNVTRRemoveKeyU(sbNVTRObj *pRoot, unsigned uKey)
1021 {
1022 sbNVTEObj *pEntry, *pPrev;
1023
1024 sbNVTRCHECKVALIDOBJECT(pRoot);
1025
1026 if((pEntry = sbNVTSearchKeyUAndPrev(pRoot, NULL, uKey, &pPrev)) == NULL)
1027 return SR_RET_NOT_FOUND;
1028
1029 sbNVTEUnlinkFromList(pRoot, pEntry, pPrev);
1030 sbNVTEDestroy(pEntry);
1031
1032 return SR_RET_OK;
1033 }
1034
1035
sbNVTRRemovEntryWithpUsr(sbNVTRObj * pRoot,void * pUsr)1036 srRetVal sbNVTRRemovEntryWithpUsr(sbNVTRObj *pRoot, void* pUsr)
1037 {
1038 sbNVTEObj *pEntry, *pPrev;
1039
1040 sbNVTRCHECKVALIDOBJECT(pRoot);
1041
1042 if((pEntry = sbNVTSearchpUsrAndPrev(pRoot, NULL, pUsr, &pPrev)) == NULL)
1043 return SR_RET_NOT_FOUND;
1044
1045 sbNVTEUnlinkFromList(pRoot, pEntry, pPrev);
1046 sbNVTEDestroy(pEntry);
1047
1048 return SR_RET_OK;
1049 }
1050
sbNVTRParseXML(sbNVTRObj * pRoot,char * pszXML)1051 srRetVal sbNVTRParseXML(sbNVTRObj *pRoot, char *pszXML)
1052 {
1053 srRetVal iRet;
1054
1055 sbNVTRCHECKVALIDOBJECT(pRoot);
1056
1057 if(pszXML == NULL)
1058 /* this does not make much sense, but is
1059 * a valid case. So let's do it ;)
1060 */
1061 return SR_RET_OK;
1062
1063 if((iRet = sbNVTXMLProcessXMLSTREAM(&pszXML, pRoot)) != SR_RET_OK)
1064 return iRet;
1065
1066 return SR_RET_OK;
1067 }
1068
1069
1070 #if DEBUGLEVEL
1071 /* Helper to srNVTDebugPrintTree - indents a new line
1072 * by iLevel*2 spaces.
1073 */
sbNVTDebugPrintTreeSpacer(int iLevel)1074 static void sbNVTDebugPrintTreeSpacer(int iLevel)
1075 {
1076 int i;
1077 for(i = 0 ; i < iLevel * 2 ; ++i)
1078 {
1079 putchar(' ');
1080 }
1081 }
1082
sbNVTDebugPrintTree(sbNVTRObj * pRoot,int iLevel)1083 void sbNVTDebugPrintTree(sbNVTRObj *pRoot, int iLevel)
1084 {
1085 sbNVTEObj *pEntry = NULL;
1086
1087 while((pEntry = sbNVTSearchKeySZ(pRoot, pEntry, NULL)) != NULL)
1088 {
1089 sbNVTDebugPrintTreeSpacer(iLevel);
1090 printf("KeySZ: '%s', ValueSZ '%s'\n", pEntry->pszKey, pEntry->pszValue);
1091 if(pEntry->uKeyPresent)
1092 {
1093 sbNVTDebugPrintTreeSpacer(iLevel);
1094 printf("KeyU: '%d', ValueU '%d'\n", pEntry->uKey, pEntry->uValue);
1095 }
1096 if(pEntry->pCDATA != NULL)
1097 {
1098 sbNVTDebugPrintTreeSpacer(iLevel);
1099 printf("CDATA: '%s'\n", pEntry->pCDATA);
1100 }
1101 if(pEntry->pXMLProps != NULL)
1102 {
1103 sbNVTDebugPrintTreeSpacer(iLevel);
1104 printf("HAS XML Properties:\n");
1105 sbNVTDebugPrintTree(pEntry->pXMLProps, iLevel + 1);
1106 }
1107 if(pEntry->pChild != NULL)
1108 {
1109 sbNVTDebugPrintTreeSpacer(iLevel);
1110 printf("HAS Child:\n");
1111 sbNVTDebugPrintTree(pEntry->pChild, iLevel + 1);
1112 }
1113 }
1114 }
1115 /* #endif for #if DEBUGLEVEL */
1116 #endif
1117
1118
sbNVTRHasElement(sbNVTRObj * pRoot,char * pEltname,int bMustBeOnly)1119 sbNVTEObj *sbNVTRHasElement(sbNVTRObj* pRoot, char *pEltname, int bMustBeOnly)
1120 {
1121 sbNVTEObj *pEntry;
1122
1123 sbNVTRCHECKVALIDOBJECT(pRoot);
1124 assert(pEltname != NULL);
1125
1126 if((bMustBeOnly == TRUE) && (pRoot->pFirst != pRoot->pLast))
1127 return NULL;
1128
1129 if((pEntry = sbNVTSearchKeySZ(pRoot, NULL, pEltname)) == NULL)
1130 return NULL;
1131
1132 return pEntry;
1133 }
1134