source: protocols/jabber/xmlnode.c @ ab4afba

Last change on this file since ab4afba was b7d3cc34, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-06T18:23:18Z

Initial repository (0.99 release tree)

  • Property mode set to 100644
File size: 17.6 KB
Line 
1/* --------------------------------------------------------------------------
2 *
3 * License
4 *
5 * The contents of this file are subject to the Jabber Open Source License
6 * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
7 * source code or executable form, except in compliance with the JOSL. You
8 * may obtain a copy of the JOSL at http://www.jabber.org/ or at
9 * http://www.opensource.org/. 
10 *
11 * Software distributed under the JOSL is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
13 * for the specific language governing rights and limitations under the
14 * JOSL.
15 *
16 * Copyrights
17 *
18 * Portions created by or assigned to Jabber.com, Inc. are
19 * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
20 * information for Jabber.com, Inc. is available at http://www.jabber.com/.
21 *
22 * Portions Copyright (c) 1998-1999 Jeremie Miller.
23 *
24 * Acknowledgements
25 *
26 * Special thanks to the Jabber Open Source Contributors for their
27 * suggestions and support of Jabber.
28 *
29 * Alternatively, the contents of this file may be used under the terms of the
30 * GNU General Public License Version 2 or later (the "GPL"), in which case
31 * the provisions of the GPL are applicable instead of those above.  If you
32 * wish to allow use of your version of this file only under the terms of the
33 * GPL and not to allow others to use your version of this file under the JOSL,
34 * indicate your decision by deleting the provisions above and replace them
35 * with the notice and other provisions required by the GPL.  If you do not
36 * delete the provisions above, a recipient may use your version of this file
37 * under either the JOSL or the GPL.
38 *
39 *
40 * --------------------------------------------------------------------------*/
41
42#include "jabber.h"
43#include <glib.h>
44
45static xmlnode xmlnode_get_firstattrib(xmlnode parent);
46static int xmlnode_get_type(xmlnode node);
47static void xmlnode_insert_node(xmlnode parent, xmlnode node);
48
49/* Internal routines */
50static xmlnode _xmlnode_new(pool p, const char* name, unsigned int type)
51{
52    xmlnode result = NULL;
53    if (type > NTYPE_LAST)
54        return NULL;
55
56    if (type != NTYPE_CDATA && name == NULL)
57        return NULL;
58
59    if (p == NULL)
60    {
61        p = pool_heap(1*1024);
62    }
63
64    /* Allocate & zero memory */
65    result = (xmlnode)pmalloco(p, sizeof(_xmlnode));
66
67    /* Initialize fields */
68    if (type != NTYPE_CDATA)
69        result->name = pstrdup(p,name);
70    result->type = type;
71    result->p = p;
72    return result;
73}
74
75static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type)
76{
77    xmlnode result;
78
79    result = _xmlnode_new(xmlnode_pool(lastsibling), name, type);
80    if (result != NULL)
81    {
82        /* Setup sibling pointers */
83        result->prev = lastsibling;
84        lastsibling->next = result;
85    }
86    return result;
87}
88
89static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type)
90{
91    xmlnode result;
92
93    if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL;
94
95    /* If parent->firstchild is NULL, simply create a new node for the first child */
96    if (parent->firstchild == NULL)
97    {
98        result = _xmlnode_new(parent->p, name, type);
99        parent->firstchild = result;
100    }
101    /* Otherwise, append this to the lastchild */
102    else
103    {
104        result= _xmlnode_append_sibling(parent->lastchild, name, type);
105    }
106    result->parent = parent;
107    parent->lastchild = result;
108    return result;
109
110}
111
112static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type)
113{
114    xmlnode current;
115
116    /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
117    the specified name */
118    current = firstsibling;
119    while (current != NULL)
120    {
121        if ((current->type == type) && (j_strcmp(current->name, name) == 0))
122            return current;
123        else
124            current = current->next;
125    }
126    return NULL;
127}
128
129static void _xmlnode_merge(xmlnode data)
130{
131    xmlnode cur;
132    char *merge, *scur;
133    int imerge;
134
135    /* get total size of all merged cdata */
136    imerge = 0;
137    for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
138        imerge += cur->data_sz;
139
140    /* copy in current data and then spin through all of them and merge */
141    scur = merge = pmalloc(data->p,imerge + 1);
142    for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
143    {
144        memcpy(scur,cur->data,cur->data_sz);
145        scur += cur->data_sz;
146    }
147    *scur = '\0';
148
149    /* this effectively hides all of the merged-in chunks */
150    data->next = cur;
151    if(cur == NULL)
152        data->parent->lastchild = data;
153    else
154        cur->prev = data;
155
156    /* reset data */
157    data->data = merge;
158    data->data_sz = imerge;
159   
160}
161
162static void _xmlnode_hide_sibling(xmlnode child)
163{
164    if(child == NULL)
165        return;
166
167    if(child->prev != NULL)
168        child->prev->next = child->next;
169    if(child->next != NULL)
170        child->next->prev = child->prev;
171}
172
173static void _xmlnode_tag2str(spool s, xmlnode node, int flag)
174{
175    xmlnode tmp;
176
177    if(flag==0 || flag==1)
178    {
179            spooler(s,"<",xmlnode_get_name(node),s);
180            tmp = xmlnode_get_firstattrib(node);
181            while(tmp) {
182                spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s);
183                tmp = xmlnode_get_nextsibling(tmp);
184            }
185            if(flag==0)
186                spool_add(s,"/>");
187            else
188                spool_add(s,">");
189    }
190    else
191    {
192            spooler(s,"</",xmlnode_get_name(node),">",s);
193    }
194}
195
196static spool _xmlnode2spool(xmlnode node)
197{
198    spool s;
199    int level=0,dir=0;
200    xmlnode tmp;
201
202    if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
203        return NULL;
204
205    s = spool_new(xmlnode_pool(node));
206    if(!s) return(NULL);
207
208    while(1)
209    {
210        if(dir==0)
211        {
212            if(xmlnode_get_type(node) == NTYPE_TAG)
213            {
214                if(xmlnode_has_children(node))
215                {
216                    _xmlnode_tag2str(s,node,1);
217                    node = xmlnode_get_firstchild(node);
218                    level++;
219                    continue;
220                }else{
221                    _xmlnode_tag2str(s,node,0);
222                }
223            }else{
224                spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
225            }
226        }
227
228        tmp = xmlnode_get_nextsibling(node);
229        if(!tmp)
230        {
231            node = xmlnode_get_parent(node);
232            level--;
233            if(level>=0) _xmlnode_tag2str(s,node,2);
234            if(level<1) break;
235            dir = 1;
236        }else{
237            node = tmp;
238            dir = 0;
239        }
240    }
241
242    return s;
243}
244
245
246/* External routines */
247
248
249/*
250 *  xmlnode_new_tag -- create a tag node
251 *  Automatically creates a memory pool for the node.
252 *
253 *  parameters
254 *      name -- name of the tag
255 *
256 *  returns
257 *      a pointer to the tag node
258 *      or NULL if it was unsuccessfull
259 */
260xmlnode xmlnode_new_tag(const char* name)
261{
262    return _xmlnode_new(NULL, name, NTYPE_TAG);
263}
264
265
266/*
267 *  xmlnode_insert_tag -- append a child tag to a tag
268 *
269 *  parameters
270 *      parent -- pointer to the parent tag
271 *      name -- name of the child tag
272 *
273 *  returns
274 *      a pointer to the child tag node
275 *      or NULL if it was unsuccessfull
276 */
277xmlnode xmlnode_insert_tag(xmlnode parent, const char* name)
278{
279    return _xmlnode_insert(parent, name, NTYPE_TAG);
280}
281
282
283/*
284 *  xmlnode_insert_cdata -- append character data to a tag
285 *
286 *  parameters
287 *      parent -- parent tag
288 *      CDATA -- character data
289 *      size -- size of CDATA
290 *              or -1 for null-terminated CDATA strings
291 *
292 *  returns
293 *      a pointer to the child CDATA node
294 *      or NULL if it was unsuccessfull
295 */
296xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size)
297{
298    xmlnode result;
299
300    if(CDATA == NULL || parent == NULL)
301        return NULL;
302
303    if(size == -1)
304        size = strlen(CDATA);
305
306    result = _xmlnode_insert(parent, NULL, NTYPE_CDATA);
307    if (result != NULL)
308    {
309        result->data = (char*)pmalloc(result->p, size + 1);
310        memcpy(result->data, CDATA, size);
311        result->data[size] = '\0';
312        result->data_sz = size;
313    }
314
315    return result;
316}
317
318
319/*
320 *  xmlnode_get_tag -- find given tag in an xmlnode tree
321 *
322 *  parameters
323 *      parent -- pointer to the parent tag
324 *      name -- "name" for the child tag of that name
325 *              "name/name" for a sub child (recurses)
326 *              "?attrib" to match the first tag with that attrib defined
327 *              "?attrib=value" to match the first tag with that attrib and value
328 *              "=cdata" to match the cdata contents of the child
329 *              or any combination: "name/name/?attrib", "name=cdata", etc
330 *
331 *  results
332 *      a pointer to the tag matching search criteria
333 *      or NULL if search was unsuccessfull
334 */
335xmlnode xmlnode_get_tag(xmlnode parent, const char* name)
336{
337    char *str, *slash, *qmark, *equals;
338    xmlnode step, ret;
339
340
341    if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
342
343    if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL)
344        return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
345
346    str = g_strdup(name);
347    slash = strstr(str, "/");
348    qmark = strstr(str, "?");
349    equals = strstr(str, "=");
350
351    if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark))
352    { /* of type =cdata */
353
354        *equals = '\0';
355        equals++;
356
357        for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
358        {
359            if(xmlnode_get_type(step) != NTYPE_TAG)
360                continue;
361
362            if(*str != '\0')
363                if(j_strcmp(xmlnode_get_name(step),str) != 0)
364                    continue;
365
366            if(j_strcmp(xmlnode_get_data(step),equals) != 0)
367                continue;
368
369            break;
370        }
371
372        g_free(str);
373        return step;
374    }
375
376
377    if(qmark != NULL && (slash == NULL || qmark < slash))
378    { /* of type ?attrib */
379
380        *qmark = '\0';
381        qmark++;
382        if(equals != NULL)
383        {
384            *equals = '\0';
385            equals++;
386        }
387
388        for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
389        {
390            if(xmlnode_get_type(step) != NTYPE_TAG)
391                continue;
392
393            if(*str != '\0')
394                if(j_strcmp(xmlnode_get_name(step),str) != 0)
395                    continue;
396
397            if(xmlnode_get_attrib(step,qmark) == NULL)
398                continue;
399
400            if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0)
401                continue;
402
403            break;
404        }
405
406        g_free(str);
407        return step;
408    }
409
410
411    *slash = '\0';
412    ++slash;
413
414    for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
415    {
416        if(xmlnode_get_type(step) != NTYPE_TAG) continue;
417
418        if(j_strcmp(xmlnode_get_name(step),str) != 0)
419            continue;
420
421        ret = xmlnode_get_tag(step, slash);
422        if(ret != NULL)
423        {
424            g_free(str);
425            return ret;
426        }
427    }
428
429    g_free(str);
430    return NULL;
431}
432
433
434/* return the cdata from any tag */
435char *xmlnode_get_tag_data(xmlnode parent, const char *name)
436{
437    xmlnode tag;
438
439    tag = xmlnode_get_tag(parent, name);
440    if(tag == NULL) return NULL;
441
442    return xmlnode_get_data(tag);
443}
444
445
446void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value)
447{
448    xmlnode attrib;
449
450    if(owner == NULL || name == NULL || value == NULL) return;
451
452    /* If there are no existing attributs, allocate a new one to start
453    the list */
454    if (owner->firstattrib == NULL)
455    {
456        attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB);
457        owner->firstattrib = attrib;
458        owner->lastattrib  = attrib;
459    }
460    else
461    {
462        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
463        if(attrib == NULL)
464        {
465            attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB);
466            owner->lastattrib = attrib;
467        }
468    }
469    /* Update the value of the attribute */
470    attrib->data_sz = strlen(value);
471    attrib->data    = pstrdup(owner->p, value);
472
473}
474
475char* xmlnode_get_attrib(xmlnode owner, const char* name)
476{
477    xmlnode attrib;
478
479    if (owner != NULL && owner->firstattrib != NULL)
480    {
481        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
482        if (attrib != NULL)
483            return (char*)attrib->data;
484    }
485    return NULL;
486}
487
488static xmlnode xmlnode_get_firstattrib(xmlnode parent)
489{
490    if (parent != NULL)
491        return parent->firstattrib;
492    return NULL;
493}
494
495xmlnode xmlnode_get_firstchild(xmlnode parent)
496{
497    if (parent != NULL)
498        return parent->firstchild;
499    return NULL;
500}
501
502xmlnode xmlnode_get_nextsibling(xmlnode sibling)
503{
504    if (sibling != NULL)
505        return sibling->next;
506    return NULL;
507}
508
509xmlnode xmlnode_get_parent(xmlnode node)
510{
511    if (node != NULL)
512        return node->parent;
513    return NULL;
514}
515
516char* xmlnode_get_name(xmlnode node)
517{
518    if (node != NULL)
519        return node->name;
520    return NULL;
521}
522
523char* xmlnode_get_data(xmlnode node)
524{
525    if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */
526        for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node))
527            if(xmlnode_get_type(node) == NTYPE_CDATA) break;
528
529    if(node == NULL) return NULL;
530
531    /* check for a dirty node w/ unassembled cdata chunks */
532    if(xmlnode_get_type(node->next) == NTYPE_CDATA)
533        _xmlnode_merge(node);
534
535    return node->data;
536}
537
538static int xmlnode_get_datasz(xmlnode node)
539{
540    if(xmlnode_get_type(node) != NTYPE_CDATA) return 0;
541
542    /* check for a dirty node w/ unassembled cdata chunks */
543    if(xmlnode_get_type(node->next) == NTYPE_CDATA)
544        _xmlnode_merge(node);
545    return node->data_sz;
546}
547
548static int xmlnode_get_type(xmlnode node)
549{
550    if (node != NULL)
551        return node->type;
552    return NTYPE_UNDEF;
553}
554
555int xmlnode_has_children(xmlnode node)
556{
557    if ((node != NULL) && (node->firstchild != NULL))
558        return 1;
559    return 0;
560}
561
562static int xmlnode_has_attribs(xmlnode node)
563{
564    if ((node != NULL) && (node->firstattrib != NULL))
565        return 1;
566    return 0;
567}
568
569pool xmlnode_pool(xmlnode node)
570{
571    if (node != NULL)
572        return node->p;
573    return (pool)NULL;
574}
575
576void xmlnode_hide_attrib(xmlnode parent, const char *name)
577{
578    xmlnode attrib;
579
580    if(parent == NULL || parent->firstattrib == NULL || name == NULL)
581        return;
582
583    attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB);
584    if(attrib == NULL)
585        return;
586
587    /* first fix up at the child level */
588    _xmlnode_hide_sibling(attrib);
589
590    /* next fix up at the parent level */
591    if(parent->firstattrib == attrib)
592        parent->firstattrib = attrib->next;
593    if(parent->lastattrib == attrib)
594        parent->lastattrib = attrib->prev;
595}
596
597
598
599/*
600 *  xmlnode2str -- convert given xmlnode tree into a string
601 *
602 *  parameters
603 *      node -- pointer to the xmlnode structure
604 *
605 *  results
606 *      a pointer to the created string
607 *      or NULL if it was unsuccessfull
608 */
609char *xmlnode2str(xmlnode node)
610{
611     return spool_print(_xmlnode2spool(node));
612}
613
614/* loop through both a and b comparing everything, attribs, cdata, children, etc */
615static int xmlnode_cmp(xmlnode a, xmlnode b)
616{
617    int ret = 0;
618
619    while(1)
620    {
621        if(a == NULL && b == NULL)
622            return 0;
623
624        if(a == NULL || b == NULL)
625            return -1;
626
627        if(xmlnode_get_type(a) != xmlnode_get_type(b))
628            return -1;
629
630        switch(xmlnode_get_type(a))
631        {
632        case NTYPE_ATTRIB:
633            ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
634            if(ret != 0)
635                return -1;
636            ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
637            if(ret != 0)
638                return -1;
639            break;
640        case NTYPE_TAG:
641            ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
642            if(ret != 0)
643                return -1;
644            ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b));
645            if(ret != 0)
646                return -1;
647            ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b));
648            if(ret != 0)
649                return -1;
650            break;
651        case NTYPE_CDATA:
652            ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
653            if(ret != 0)
654                return -1;
655        }
656        a = xmlnode_get_nextsibling(a);
657        b = xmlnode_get_nextsibling(b);
658    }
659}
660
661
662xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node)
663{
664    xmlnode child;
665
666    child = xmlnode_insert_tag(parent, xmlnode_get_name(node));
667    if (xmlnode_has_attribs(node))
668        xmlnode_insert_node(child, xmlnode_get_firstattrib(node));
669    if (xmlnode_has_children(node))
670        xmlnode_insert_node(child, xmlnode_get_firstchild(node));
671
672    return child;
673}
674
675/* places copy of node and node's siblings in parent */
676static void xmlnode_insert_node(xmlnode parent, xmlnode node)
677{
678    if(node == NULL || parent == NULL)
679        return;
680
681    while(node != NULL)
682    {
683        switch(xmlnode_get_type(node))
684        {
685        case NTYPE_ATTRIB:
686            xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node));
687            break;
688        case NTYPE_TAG:
689            xmlnode_insert_tag_node(parent, node);
690            break;
691        case NTYPE_CDATA:
692            xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node));
693        }
694        node = xmlnode_get_nextsibling(node);
695    }
696}
697
698
699void xmlnode_free(xmlnode node)
700{
701    if(node == NULL)
702        return;
703
704    pool_free(node->p);
705}
Note: See TracBrowser for help on using the repository browser.