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, "&lt;")) != SR_RET_OK) return iRet;
388 		}
389 		else if(*pszToEscape == '&')
390 		{
391 			if((iRet = sbStrBAppendStr(pStr, "&quot;")) != 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