Imported Upstream version 0.9.1
[platform/upstream/iotivity.git] / service / protocol-plugin / lib / cpluff / libcpluff / ploader.c
1 /*-------------------------------------------------------------------------
2  * C-Pluff, a plug-in framework for C
3  * Copyright 2007 Johannes Lehtinen
4  * 
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *-----------------------------------------------------------------------*/
23
24 /** @file
25  * Plug-in descriptor loader
26  */
27
28 #ifdef __linux__
29 #ifndef _POSIX_C_SOURCE
30 // Defining _POSIX_C_SOURCE macro with 200809L (or greater) as value
31 // causes header files to expose definitions
32 // corresponding to the POSIX.1-2008 base
33 // specification (excluding the XSI extension).
34 // For POSIX.1-2008 base specification,
35 // Refer http://pubs.opengroup.org/stage7tc1/
36 //
37 // For this specific file, see use of strndup,
38 // Refer http://man7.org/linux/man-pages/man3/strdup.3.html
39 #define _POSIX_C_SOURCE 200809L
40 #endif
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <assert.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <expat.h>
51 #include "cpluff.h"
52 #include "defines.h"
53 #include "util.h"
54 #include "internal.h"
55
56 // Use XMLCALL if available
57 #ifdef XMLCALL
58 #define CP_XMLCALL XMLCALL
59 #else
60 #define CP_XMLCALL
61 #endif
62
63
64 /* ------------------------------------------------------------------------
65  * Constants
66  * ----------------------------------------------------------------------*/
67
68 /// XML parser buffer size (in bytes) 
69 #define CP_XML_PARSER_BUFFER_SIZE 4096
70
71 /// Initial configuration element value size 
72 #define CP_CFG_ELEMENT_VALUE_INITSIZE 64
73
74 /// Plugin descriptor name 
75 #define CP_PLUGIN_DESCRIPTOR "plugin.xml"
76
77
78 /* ------------------------------------------------------------------------
79  * Internal data types
80  * ----------------------------------------------------------------------*/
81
82 typedef struct ploader_context_t ploader_context_t;
83
84 /// Parser states 
85 typedef enum parser_state_t {
86         PARSER_BEGIN,
87         PARSER_PLUGIN,
88         PARSER_REQUIRES,
89         PARSER_EXTENSION,
90         PARSER_END,
91         PARSER_UNKNOWN,
92         PARSER_ERROR
93 } parser_state_t;
94
95 /// Plug-in loader context 
96 struct ploader_context_t {
97
98         /// The plug-in context, or NULL if none
99         cp_context_t *context;
100
101         /// The XML parser being used 
102         XML_Parser parser;
103         
104         /// The file being parsed 
105         char *file;
106         
107         /// The plug-in being constructed 
108         cp_plugin_info_t *plugin;
109         
110         /// The configuration element being constructed 
111         cp_cfg_element_t *configuration;
112         
113         /// The current parser state 
114         parser_state_t state;
115         
116         /// The saved parser state (used in PARSER_UNKNOWN) 
117         parser_state_t saved_state;
118         
119         /**
120          * The current parser depth (used in PARSER_UNKNOWN and PARSER_EXTENSION)
121          */
122         unsigned int depth;
123         
124         /// The number of skipped configuration elements 
125         unsigned int skippedCEs;
126
127         /// Size of allocated imports table 
128         size_t imports_size;
129         
130         /// Size of allocated extension points table 
131         size_t ext_points_size;
132         
133         /// Size of allocated extensions table 
134         size_t extensions_size;
135         
136         /// Buffer for a value being read 
137         char *value;
138         
139         /// Size of allocated value field 
140         size_t value_size;
141         
142         /// Current length of value string 
143         size_t value_length;
144         
145         /// The number of parsing errors that have occurred 
146         unsigned int error_count;
147         
148         /// The number of resource errors that have occurred 
149         unsigned int resource_error_count;
150 };
151
152
153 /* ------------------------------------------------------------------------
154  * Function definitions
155  * ----------------------------------------------------------------------*/
156
157 /**
158  * Reports a descriptor error. Does not set the parser to error state but
159  * increments the error count, unless this is merely a warning.
160  * 
161  * @param context the parsing context
162  * @param warn whether this is only a warning
163  * @param error_msg the error message
164  * @param ... parameters for the error message
165  */
166 static void descriptor_errorf(ploader_context_t *plcontext, int warn,
167         const char *error_msg, ...) {
168         va_list ap;
169         char message[128];
170
171         va_start(ap, error_msg);
172         vsnprintf(message, sizeof(message), error_msg, ap);
173         va_end(ap);
174         message[127] = '\0';
175         if (warn) {
176                 cpi_warnf(plcontext->context,
177                         N_("Suspicious plug-in descriptor content in %s, line %d, column %d (%s)."),
178                 plcontext->file,
179                 XML_GetCurrentLineNumber(plcontext->parser),
180                 XML_GetCurrentColumnNumber(plcontext->parser) + 1,
181                 message);
182         } else {                                
183                 cpi_errorf(plcontext->context,
184                         N_("Invalid plug-in descriptor content in %s, line %d, column %d (%s)."),
185                         plcontext->file,
186                         XML_GetCurrentLineNumber(plcontext->parser),
187                         XML_GetCurrentColumnNumber(plcontext->parser) + 1,
188                         message);
189         }
190         if (!warn) {
191                 plcontext->error_count++;
192         }
193 }
194
195 /**
196  * Reports insufficient system resources while parsing and increments the
197  * resource error count.
198  * 
199  * @param context the parsing context
200  */
201 static void resource_error(ploader_context_t *plcontext) {
202         if (plcontext->resource_error_count == 0) {
203                 cpi_errorf(plcontext->context,
204                         N_("Insufficient system resources to parse plug-in descriptor content in %s, line %d, column %d."),
205                         plcontext->file,
206                         XML_GetCurrentLineNumber(plcontext->parser),
207                         XML_GetCurrentColumnNumber(plcontext->parser) + 1);
208         }
209         plcontext->resource_error_count++;
210 }
211
212 /**
213  * Returns whether the specified NULL-terminated list of strings includes
214  * the specified string.
215  * 
216  * @param list the NULL-terminated list of strings, or NULL if none
217  * @param str the string
218  * @param step the stepping (1 to check every string or 2 to check every
219  *                      other string)
220  * @return pointer to the location of the string or NULL if not found
221  */
222 static const XML_Char * const *contains_str(const XML_Char * const *list,
223         const XML_Char *str, int step) {
224         if (list != NULL) {
225                 while (*list != NULL) {
226                         if (!strcmp(*list, str)) {
227                                 return list;
228                         }
229                         list += step;
230                 }
231         }
232         return NULL;
233 }
234
235 /**
236  * Checks that an element has non-empty values for required attributes.
237  * Increments the error count for each missing attribute.
238  * 
239  * @param context the parsing context
240  * @param elem the element being checked
241  * @param atts the attribute list for the element
242  * @param req_atts the required attributes (NULL terminated list, or NULL)
243  * @return whether the required attributes are present
244  */
245 static int check_req_attributes(ploader_context_t *plcontext,
246         const XML_Char *elem, const XML_Char * const *atts,
247         const XML_Char * const *req_atts) {
248         const XML_Char * const *a;
249         int error = 0;
250         
251         // Check that required attributes have non-empty values 
252         for (a = req_atts; a != NULL && *a != NULL; a++) {
253                 const XML_Char * const *av;
254                 
255                 if ((av = contains_str(atts, *a, 2)) != NULL) {
256                         if ((*(av + 1))[0] == '\0') {
257                                 descriptor_errorf(plcontext, 0,
258                                         _("required attribute %s for element %s has an empty value"),
259                                         *a, elem);
260                                 error = 1;
261                         }
262                 } else {
263                         descriptor_errorf(plcontext, 0,
264                                 _("required attribute %s missing for element %s"),
265                                 *a, elem);
266                         error = 1;
267                 }
268         }
269         
270         return !error;
271 }
272
273 /**
274  * Checks that an element has non-empty values for required attributes and
275  * warns if there are unknown attributes. Increments the error count for
276  * each missing required attribute.
277  * 
278  * @param context the parsing context
279  * @param elem the element being checked
280  * @param atts the attribute list for the element
281  * @param req_atts the required attributes (NULL terminated list, or NULL)
282  * @param opt_atts the optional attributes (NULL terminated list, or NULL)
283  * @return whether the required attributes are present
284  */
285 static int check_attributes(ploader_context_t *plcontext,
286         const XML_Char *elem, const XML_Char * const *atts,
287         const XML_Char * const *req_atts, const XML_Char * const *opt_atts) {
288         int error = 0;
289         
290         // Check required attributes
291         error = !check_req_attributes(plcontext, elem, atts, req_atts);
292         
293         // Warn if there are unknown attributes 
294         for (; *atts != NULL; atts += 2) {
295                 if (contains_str(req_atts, *atts, 1) == NULL
296                         && contains_str(opt_atts, *atts, 1) == NULL) {
297                         descriptor_errorf(plcontext, 1,
298                                 _("ignoring unknown attribute %s for element %s"),
299                                 *atts, elem);
300                 }
301         }
302         
303         return !error;
304 }
305
306 /**
307  * Allocates memory using malloc. Reports a resource error if there is not
308  * enough available memory.
309  * 
310  * @param context the parsing context
311  * @param size the number of bytes to allocate
312  * @return pointer to the allocated memory, or NULL if memory allocation failed
313  */
314 static void *parser_malloc(ploader_context_t *plcontext, size_t size) {
315         void *ptr;
316
317         if ((ptr = malloc(size)) == NULL) {
318                 resource_error(plcontext);
319         }
320         return ptr;
321 }
322
323 /**
324  * Makes a copy of the specified string. The memory is allocated using malloc.
325  * Reports a resource error if there is not enough available memory.
326  * 
327  * @param context the parsing context
328  * @param src the source string to be copied
329  * @return copy of the string, or NULL if memory allocation failed
330  */
331 static char *parser_strdup(ploader_context_t *plcontext, const char *src) {
332         char *dup;
333
334         if ((dup = strdup(src)) == NULL) {
335                 resource_error(plcontext);
336         }
337         return dup;
338 }
339
340 /**
341  * Concatenates the specified strings into a new string. The memory for the concatenated
342  * string is allocated using malloc. Reports a resource error if there is not
343  * enough available memory.
344  * 
345  * @param context the parsing context
346  * @param ... the strings to be concatenated, terminated by NULL
347  * @return the concatenated string, or NULL if memory allocation failed
348  */
349 static char *parser_strscat(ploader_context_t *plcontext, ...) {
350         va_list ap;
351         const char *str;
352         char *dst;
353         size_t len;
354         
355         // Calculate the length of the concatenated string 
356         va_start(ap, plcontext);
357         len = 0;
358         while ((str = va_arg(ap, const char *)) != NULL) {
359                 len += strlen(str);
360         }
361         va_end(ap);
362         
363         // Allocate space for the concatenated string 
364         if ((dst = parser_malloc(plcontext, sizeof(char) * (len + 1))) == NULL) {
365                 return NULL;
366         }
367         
368         // Copy the strings 
369         len = 0;
370         va_start(ap, plcontext);
371         while ((str = va_arg(ap, const char *)) != NULL) {
372                 strcpy(dst + len, str);
373                 len += strlen(str);
374         }
375         va_end(ap);
376         dst[len] = '\0';
377         return dst;
378 }
379
380 /**
381  * Puts the parser to a state in which it skips an unknown element.
382  * Warns error handlers about the unknown element.
383  * 
384  * @param context the parsing context
385  * @param elem the element name
386  */
387 static void unexpected_element(ploader_context_t *plcontext, const XML_Char *elem) {
388         plcontext->saved_state = plcontext->state;
389         plcontext->state = PARSER_UNKNOWN;
390         plcontext->depth = 0;
391         descriptor_errorf(plcontext, 1, _("ignoring unexpected element %s and its contents"), elem);
392 }
393
394 /**
395  * Creates a copy of the specified attributes. Reports failed memory
396  * allocation.
397  * 
398  * @param context the parser context
399  * @param src the source attributes to be copied
400  * @param num pointer to the location where number of attributes is stored,
401  *                      or NULL for none
402  * @return the duplicated attribute array, or NULL if empty or failed
403  */
404 static char **parser_attsdup(ploader_context_t *plcontext, const XML_Char * const *src,
405         unsigned int *num_atts) {
406         char **atts = NULL, *attr_data = NULL;
407         unsigned int i;
408         unsigned int num;
409         size_t attr_size;
410         
411         // Calculate the number of attributes and the amount of space required 
412         for (num = 0, attr_size = 0; src[num] != NULL; num++) {
413                 attr_size += strlen(src[num]) + 1;
414         }
415         assert((num & 1) == 0);
416         
417         // Allocate necessary memory and copy attribute data 
418         if (num > 0) {
419                 if ((atts = parser_malloc(plcontext, num * sizeof(char *))) != NULL) {
420                         if ((attr_data = parser_malloc(plcontext, attr_size * sizeof(char))) != NULL) {
421                                 size_t offset;
422                         
423                                 for (i = 0, offset = 0; i < num; i++) {
424                                         strcpy(attr_data + offset, src[i]);
425                                         atts[i] = attr_data + offset;
426                                         offset += strlen(src[i]) + 1;
427                                 }
428                         }
429                 }
430         }
431         
432         // If successful then return duplicates, otherwise free any allocations 
433         if (num == 0 || (atts != NULL && attr_data != NULL)) {
434                 if (num_atts != NULL) {
435                         *num_atts = num / 2;
436                 }
437                 return atts;
438         } else {
439                 free(attr_data);
440                 free(atts);
441                 return NULL;
442         }
443 }
444
445 /**
446  * Initializes a configuration element. Reports an error if memory allocation fails.
447  * 
448  * @param context the parser context
449  * @param ce the configuration element to be initialized
450  * @param name the element name
451  * @param atts the element attributes
452  * @param parent the parent element
453  */
454 static void init_cfg_element(ploader_context_t *plcontext, cp_cfg_element_t *ce,
455         const XML_Char *name, const XML_Char * const *atts, cp_cfg_element_t *parent) {
456         
457         // Initialize the configuration element 
458         memset(ce, 0, sizeof(cp_cfg_element_t));
459         ce->name = parser_strdup(plcontext, name);
460         ce->atts = parser_attsdup(plcontext, atts, &(ce->num_atts));
461         ce->value = NULL;
462         plcontext->value = NULL;
463         plcontext->value_size = 0;
464         plcontext->value_length = 0;
465         ce->parent = parent;
466         ce->children = NULL;    
467 }
468
469 /**
470  * Processes the character data while parsing.
471  * 
472  * @param userData the parsing context
473  * @param str the string data
474  * @param len the string length
475  */
476 static void CP_XMLCALL character_data_handler(
477         void *userData, const XML_Char *str, int len) {
478         ploader_context_t *plcontext = userData;
479         
480         // Ignore leading whitespace 
481         if (plcontext->value == NULL) {
482                 int i;
483                 
484                 for (i = 0; i < len; i++) {
485                         if (str[i] != ' ' && str[i] != '\n' && str[i] != '\r' && str[i] != '\t') {
486                                 break;
487                         }
488                 }
489                 str += i;
490                 len -= i;
491                 if (len == 0) {
492                         return;
493                 }
494         }
495         
496         // Allocate more memory for the character data if needed 
497         if (plcontext->value_length + len >= plcontext->value_size) {
498                 size_t ns;
499                 char *nv;
500
501                 ns = plcontext->value_size;
502                 while (plcontext->value_length + len >= ns) {           
503                         if (ns == 0) {
504                                 ns = CP_CFG_ELEMENT_VALUE_INITSIZE;
505                         } else {
506                                 ns = 2 * ns;
507                         }
508                 }
509                 if ((nv = realloc(plcontext->value, ns * sizeof(char))) != NULL) {
510                         plcontext->value = nv;
511                         plcontext->value_size = ns;
512                 } else {
513                         resource_error(plcontext);
514                         return;
515                 }
516         }
517         
518         // Copy character data 
519         strncpy(plcontext->value + plcontext->value_length, str, len * sizeof(char));
520         plcontext->value_length += len;
521 }
522
523 /**
524  * Processes the start of element events while parsing.
525  * 
526  * @param userData the parsing context
527  * @param name the element name
528  * @param atts the element attributes
529  */
530 static void CP_XMLCALL start_element_handler(
531         void *userData, const XML_Char *name, const XML_Char **atts) {
532         static const XML_Char * const req_plugin_atts[] = { "id", NULL };
533         static const XML_Char * const opt_plugin_atts[] = { "name", "version", "provider-name", NULL };
534         static const XML_Char * const req_bwcompatibility_atts[] = { NULL };
535         static const XML_Char * const opt_bwcompatibility_atts[] = { "abi", "api", NULL };
536         static const XML_Char * const req_cpluff_atts[] = { "version", NULL };
537         static const XML_Char * const opt_cpluff_atts[] = { NULL };
538         static const XML_Char * const req_import_atts[] = { "plugin", NULL };
539         static const XML_Char * const opt_import_atts[] = { "version", "optional", NULL };
540         static const XML_Char * const req_runtime_atts[] = { "library", NULL };
541         static const XML_Char * const opt_runtime_atts[] = { "funcs", NULL };
542         static const XML_Char * const req_ext_point_atts[] = { "id", NULL };
543         static const XML_Char * const opt_ext_point_atts[] = { "name", "schema", NULL };
544         static const XML_Char * const req_extension_atts[] = { "point", NULL };
545         //static const XML_Char * const opt_extension_atts[] = { "id", "name", NULL };
546         ploader_context_t *plcontext = userData;
547         unsigned int i;
548
549         // Process element start 
550         switch (plcontext->state) {
551
552                 case PARSER_BEGIN:
553                         if (!strcmp(name, "plugin")) {
554                                 plcontext->state = PARSER_PLUGIN;
555                                 if (!check_attributes(plcontext, name, atts,
556                                                 req_plugin_atts, opt_plugin_atts)) {
557                                         break;
558                                 }
559                                 for (i = 0; atts[i] != NULL; i += 2) {
560                                         if (!strcmp(atts[i], "name")) {
561                                                 plcontext->plugin->name
562                                                         = parser_strdup(plcontext, atts[i+1]);
563                                         } else if (!strcmp(atts[i], "id")) {
564                                                 plcontext->plugin->identifier
565                                                         = parser_strdup(plcontext, atts[i+1]);
566                                         } else if (!strcmp(atts[i], "version")) {
567                                                 plcontext->plugin->version
568                                                         = parser_strdup(plcontext, atts[i+1]);
569                                         } else if (!strcmp(atts[i], "provider-name")) {
570                                                 plcontext->plugin->provider_name
571                                                         = parser_strdup(plcontext, atts[i+1]);
572                                         } else if(!strcmp(atts[i],"url")){
573                                                 plcontext->plugin->url = parser_strdup(plcontext, atts[i+1]);
574                                         } else if(!strcmp(atts[i],"resourcetype")){
575                                                 plcontext->plugin->resourcetype = parser_strdup(plcontext, atts[i+1]);
576                                         }
577                                 }
578                         } else {
579                                 unexpected_element(plcontext, name);
580                         }
581                         break;
582
583                 case PARSER_PLUGIN:
584                         if (!strcmp(name, "backwards-compatibility")) {
585                                 if (check_attributes(plcontext, name, atts,
586                                                 req_bwcompatibility_atts, opt_bwcompatibility_atts)) {
587                                         for (i = 0; atts[i] != NULL; i += 2) {
588                                                 if (!strcmp(atts[i], "abi")) {
589                                                         plcontext->plugin->abi_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
590                                                 } else if (!strcmp(atts[i], "api")) {
591                                                         plcontext->plugin->api_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
592                                                 }
593                                         }
594                                 }
595                         } else if (!strcmp(name, "requires")) {
596                                 plcontext->state = PARSER_REQUIRES;
597                         } else if (!strcmp(name, "runtime")) {
598                                 if (check_attributes(plcontext, name, atts,
599                                                 req_runtime_atts, opt_runtime_atts)) {
600                                         for (i = 0; atts[i] != NULL; i += 2) {
601                                                 if (!strcmp(atts[i], "library")) {
602                                                         plcontext->plugin->runtime_lib_name
603                                                                 = parser_strdup(plcontext, atts[i+1]);
604                                                 } else if (!strcmp(atts[i], "funcs")) {
605                                                         plcontext->plugin->runtime_funcs_symbol
606                                                                 = parser_strdup(plcontext, atts[i+1]);
607                                                 }
608                                         }
609                                 }
610                         } else if (!strcmp(name, "extension-point")) {
611                                 if (check_attributes(plcontext, name, atts,
612                                                 req_ext_point_atts, opt_ext_point_atts)) {
613                                         cp_ext_point_t *ext_point;
614                                         
615                                         // Allocate space for extension points, if necessary 
616                                         if (plcontext->plugin->num_ext_points == plcontext->ext_points_size) {
617                                                 cp_ext_point_t *nep;
618                                                 size_t ns;
619                                                 
620                                                 if (plcontext->ext_points_size == 0) {
621                                                         ns = 4;
622                                                 } else {
623                                                         ns = plcontext->ext_points_size * 2;
624                                                 }
625                                                 if ((nep = realloc(plcontext->plugin->ext_points,
626                                                                 ns * sizeof(cp_ext_point_t))) == NULL) {
627                                                         resource_error(plcontext);
628                                                         break;
629                                                 }
630                                                 plcontext->plugin->ext_points = nep;
631                                                 plcontext->ext_points_size = ns;
632                                         }
633                                         
634                                         // Parse extension point specification 
635                                         ext_point = plcontext->plugin->ext_points
636                                                 + plcontext->plugin->num_ext_points;
637                                         memset(ext_point, 0, sizeof(cp_ext_point_t));
638                                         ext_point->plugin = plcontext->plugin;
639                                         ext_point->name = NULL;
640                                         ext_point->local_id = NULL;
641                                         ext_point->identifier = NULL;
642                                         ext_point->schema_path = NULL;
643                                         for (i = 0; atts[i] != NULL; i += 2) {
644                                                 if (!strcmp(atts[i], "name")) {
645                                                         ext_point->name
646                                                                 = parser_strdup(plcontext, atts[i+1]);
647                                                 } else if (!strcmp(atts[i], "id")) {
648                                                         ext_point->local_id
649                                                                 = parser_strdup(plcontext, atts[i+1]);
650                                                         ext_point->identifier
651                                                                 = parser_strscat(plcontext,
652                                                                         plcontext->plugin->identifier, ".", atts[i+1], NULL);
653                                                 } else if (!strcmp(atts[i], "schema")) {
654                                                         ext_point->schema_path
655                                                                 = parser_strdup(plcontext, atts[i+1]);
656                                                 }
657                                         }
658                                         plcontext->plugin->num_ext_points++;
659                                         
660                                 }
661                         } else if (!(strcmp(name, "extension"))) {
662                                 plcontext->state = PARSER_EXTENSION;
663                                 plcontext->depth = 0;
664                                 if (check_req_attributes(
665                                         plcontext, name, atts, req_extension_atts)) {
666                                         cp_extension_t *extension;
667                                 
668                                         // Allocate space for extensions, if necessary 
669                                         if (plcontext->plugin->num_extensions == plcontext->extensions_size) {
670                                                 cp_extension_t *ne;
671                                                 size_t ns;
672                                                 
673                                                 if (plcontext->extensions_size == 0) {
674                                                         ns = 16;
675                                                 } else {
676                                                         ns = plcontext->extensions_size * 2;
677                                                 }
678                                                 if ((ne = realloc(plcontext->plugin->extensions,
679                                                                 ns * sizeof(cp_extension_t))) == NULL) {
680                                                         resource_error(plcontext);
681                                                         break;
682                                                 }
683                                                 plcontext->plugin->extensions = ne;
684                                                 plcontext->extensions_size = ns;
685                                         }
686                                         
687                                         // Parse extension attributes 
688                                         extension = plcontext->plugin->extensions
689                                                 + plcontext->plugin->num_extensions;
690                                         memset(extension, 0, sizeof(cp_extension_t));
691                                         extension->plugin = plcontext->plugin;
692                                         extension->name = NULL;
693                                         extension->local_id = NULL;
694                                         extension->identifier = NULL;
695                                         extension->ext_point_id = NULL;
696                                         extension->configuration = NULL;
697                                         for (i = 0; atts[i] != NULL; i += 2) {
698                                                 if (!strcmp(atts[i], "point")) {
699                                                         extension->ext_point_id
700                                                                 = parser_strdup(plcontext, atts[i+1]);
701                                                 } else if (!strcmp(atts[i], "id")) {
702                                                         extension->local_id
703                                                                 = parser_strdup(plcontext, atts[i+1]);
704                                                         extension->identifier
705                                                                 = parser_strscat(plcontext,
706                                                                         plcontext->plugin->identifier, ".", atts[i+1], NULL);
707                                                 } else if (!strcmp(atts[i], "name")) {
708                                                         extension->name
709                                                                 = parser_strdup(plcontext, atts[i+1]);
710                                                 }
711                                         }
712                                         plcontext->plugin->num_extensions++;
713                                         
714                                         // Initialize configuration parsing 
715                                         if ((extension->configuration = plcontext->configuration
716                                                 = parser_malloc(plcontext, sizeof(cp_cfg_element_t))) != NULL) {
717                                                 init_cfg_element(plcontext, plcontext->configuration, name, atts, NULL);
718                                         }
719                                         XML_SetCharacterDataHandler(plcontext->parser, character_data_handler);
720                                 }
721                         } else {
722                                 unexpected_element(plcontext, name);
723                         }
724                         break;
725
726                 case PARSER_REQUIRES:
727                         if (!strcmp(name, "c-pluff")) {
728                                 if (check_attributes(plcontext, name, atts,
729                                                 req_cpluff_atts, opt_cpluff_atts)) {
730                                         for (i = 0; atts[i] != NULL; i += 2) {
731                                                 if (!strcmp(atts[i], "version")) {
732                                                         plcontext->plugin->req_cpluff_version = parser_strdup(plcontext, atts[i+1]);
733                                                 }
734                                         }
735                                 }
736                         } else if (!strcmp(name, "import")) {
737                                 if (check_attributes(plcontext, name, atts,
738                                                 req_import_atts, opt_import_atts)) {
739                                         cp_plugin_import_t *import = NULL;
740                                 
741                                         // Allocate space for imports, if necessary 
742                                         if (plcontext->plugin->num_imports == plcontext->imports_size) {
743                                                 cp_plugin_import_t *ni;
744                                                 size_t ns;
745                                         
746                                                 if (plcontext->imports_size == 0) {
747                                                         ns = 16;
748                                                 } else {
749                                                         ns = plcontext->imports_size * 2;
750                                                 }
751                                                 if ((ni = realloc(plcontext->plugin->imports,
752                                                                 ns * sizeof(cp_plugin_import_t))) == NULL) {
753                                                         resource_error(plcontext);
754                                                         break;
755                                                 }
756                                                 plcontext->plugin->imports = ni;
757                                                 plcontext->imports_size = ns;
758                                         }
759                                 
760                                         // Parse import specification 
761                                         import = plcontext->plugin->imports
762                                                 + plcontext->plugin->num_imports;
763                                         memset(import, 0, sizeof(cp_plugin_import_t));
764                                         import->plugin_id = NULL;
765                                         import->version = NULL;
766                                         for (i = 0; atts[i] != NULL; i += 2) {
767                                                 if (!strcmp(atts[i], "plugin")) {
768                                                         import->plugin_id
769                                                                 = parser_strdup(plcontext, atts[i+1]);
770                                                 } else if (!strcmp(atts[i], "version")) {
771                                                         import->version = parser_strdup(plcontext, atts[i+1]);
772                                                 } else if (!strcmp(atts[i], "optional")) {
773                                                         if (!strcmp(atts[i+1], "true")
774                                                                 || !strcmp(atts[i+1], "1")) {
775                                                                 import->optional = 1;
776                                                         } else if (strcmp(atts[i+1], "false")
777                                                                 && strcmp(atts[i+1], "0")) {
778                                                                 descriptor_errorf(plcontext, 0, _("unknown boolean value: %s"), atts[i+1]);
779                                                         }
780                                                 }
781                                         }
782                                         plcontext->plugin->num_imports++;
783                                 }
784                         } else {
785                                 unexpected_element(plcontext, name);
786                         }
787                         break;
788
789                 case PARSER_EXTENSION:
790                         plcontext->depth++;
791                         if (plcontext->configuration != NULL && plcontext->skippedCEs == 0) {
792                                 cp_cfg_element_t *ce;
793                                 
794                                 // Allocate more space for children, if necessary 
795                                 if (plcontext->configuration->num_children == plcontext->configuration->index) {
796                                         cp_cfg_element_t *nce;
797                                         size_t ns;
798                                                 
799                                         if (plcontext->configuration->index == 0) {
800                                                 ns = 16;
801                                         } else {
802                                                 ns = plcontext->configuration->index * 2;
803                                         }
804                                         if ((nce = realloc(plcontext->configuration->children,
805                                                         ns * sizeof(cp_cfg_element_t))) == NULL) {
806                                                 plcontext->skippedCEs++;
807                                                 resource_error(plcontext);
808                                                 break;
809                                         }
810                                         plcontext->configuration->children = nce;
811                                         plcontext->configuration->index = ns;
812                                 }
813                                 
814                                 // Save possible value 
815                                 if (plcontext->value != NULL) {
816                                         plcontext->value[plcontext->value_length] = '\0';
817                                         plcontext->configuration->value = plcontext->value;
818                                 }
819                                 
820                                 ce = plcontext->configuration->children + plcontext->configuration->num_children;
821                                 init_cfg_element(plcontext, ce, name, atts, plcontext->configuration);
822                                 plcontext->configuration->num_children++;
823                                 plcontext->configuration = ce;
824                         }
825                         break;
826                         
827                 case PARSER_UNKNOWN:
828                         plcontext->depth++;
829                         break;
830                 default:
831                         unexpected_element(plcontext, name);
832                         break;
833         }
834 }
835
836 /**
837  * Processes the end of element events while parsing.
838  * 
839  * @param context the parsing context
840  * @param name the element name
841  */
842 static void CP_XMLCALL end_element_handler(
843         void *userData, const XML_Char *name) {
844         ploader_context_t *plcontext = userData;
845         
846         // Process element end 
847         switch (plcontext->state) {
848
849                 case PARSER_PLUGIN:
850                         if (!strcmp(name, "plugin")) {
851                                 
852                                 // Readjust memory allocated for extension points, if necessary 
853                                 if (plcontext->ext_points_size != plcontext->plugin->num_ext_points) {
854                                         cp_ext_point_t *nep;
855                                         
856                                         if ((nep = realloc(plcontext->plugin->ext_points,
857                                                         plcontext->plugin->num_ext_points *
858                                                                 sizeof(cp_ext_point_t))) != NULL
859                                                 || plcontext->plugin->num_ext_points == 0) {
860                                                 plcontext->plugin->ext_points = nep;
861                                                 plcontext->ext_points_size = plcontext->plugin->num_ext_points;
862                                         }
863                                 }
864                                 
865                                 // Readjust memory allocated for extensions, if necessary 
866                                 if (plcontext->extensions_size != plcontext->plugin->num_extensions) {
867                                         cp_extension_t *ne;
868                                         
869                                         if ((ne = realloc(plcontext->plugin->extensions,
870                                                         plcontext->plugin->num_extensions *
871                                                                 sizeof(cp_extension_t))) != NULL
872                                                 || plcontext->plugin->num_extensions == 0) {
873                                                 plcontext->plugin->extensions = ne;
874                                                 plcontext->extensions_size = plcontext->plugin->num_extensions;
875                                         }                                       
876                                 }
877                                 
878                                 plcontext->state = PARSER_END;
879                         }
880                         break;
881
882                 case PARSER_REQUIRES:
883                         if (!strcmp(name, "requires")) {
884                                 
885                                 // Readjust memory allocated for imports, if necessary 
886                                 if (plcontext->imports_size != plcontext->plugin->num_imports) {
887                                         cp_plugin_import_t *ni;
888                                         
889                                         if ((ni = realloc(plcontext->plugin->imports,
890                                                         plcontext->plugin->num_imports *
891                                                                 sizeof(cp_plugin_import_t))) != NULL
892                                                 || plcontext->plugin->num_imports == 0) {
893                                                 plcontext->plugin->imports = ni;
894                                                 plcontext->imports_size = plcontext->plugin->num_imports;
895                                         }
896                                 }
897                                 
898                                 plcontext->state = PARSER_PLUGIN;
899                         }
900                         break;
901
902                 case PARSER_UNKNOWN:
903                         if (plcontext->depth-- == 0) {
904                                 plcontext->state = plcontext->saved_state;
905                         }
906                         break;
907
908                 case PARSER_EXTENSION:
909                         if (plcontext->skippedCEs > 0) {
910                                 plcontext->skippedCEs--;
911                         } else if (plcontext->configuration != NULL) {
912                                 
913                                 // Readjust memory allocated for children, if necessary 
914                                 if (plcontext->configuration->index != plcontext->configuration->num_children) {
915                                         cp_cfg_element_t *nce;
916                                         
917                                         if ((nce = realloc(plcontext->configuration->children,
918                                                         plcontext->configuration->num_children *
919                                                                 sizeof(cp_cfg_element_t))) != NULL
920                                                 || plcontext->configuration->num_children == 0) {
921                                                 plcontext->configuration->children = nce;
922                                         }
923                                 }
924                                 
925                                 if (plcontext->configuration->parent != NULL) {
926                                         plcontext->configuration->index = plcontext->configuration->parent->num_children - 1;
927                                 } else {
928                                         plcontext->configuration->index = 0;
929                                 }
930                                 if (plcontext->value != NULL) {
931                                         char *v = plcontext->value;
932                                         int i;
933                                         
934                                         // Ignore trailing whitespace 
935                                         for (i = plcontext->value_length - 1; i >= 0; i--) {
936                                                 if (v[i] != ' ' && v[i] != '\n' && v[i] != '\r' && v[i] != '\t') {
937                                                         break;
938                                                 }
939                                         }
940                                         if (i  < 0) {
941                                                 free(plcontext->value);
942                                                 plcontext->value = NULL;
943                                                 plcontext->value_length = 0;
944                                                 plcontext->value_size = 0;
945                                         } else {
946                                                 plcontext->value_length = i + 1;
947                                         }
948                                 }
949                                 if (plcontext->value != NULL) {
950                                         
951                                         // Readjust memory allocated for value, if necessary 
952                                         if (plcontext->value_size > plcontext->value_length + 1) {
953                                                 char *nv;
954                                                 
955                                                 if ((nv = realloc(plcontext->value, (plcontext->value_length + 1) * sizeof(char))) != NULL) {
956                                                         plcontext->value = nv;
957                                                 }
958                                         }
959                                         
960                                         plcontext->value[plcontext->value_length] = '\0';
961                                         plcontext->configuration->value = plcontext->value;
962                                         plcontext->value = NULL;
963                                         plcontext->value_size = 0;
964                                         plcontext->value_length = 0;
965                                 }
966                                 plcontext->configuration = plcontext->configuration->parent;
967                                 
968                                 // Restore possible value 
969                                 if (plcontext->configuration != NULL
970                                         && plcontext->configuration->value != NULL) {
971                                         plcontext->value = plcontext->configuration->value;
972                                         plcontext->value_length = strlen(plcontext->value);
973                                         plcontext->value_size = CP_CFG_ELEMENT_VALUE_INITSIZE;
974                                         while (plcontext->value_size < plcontext->value_length + 1) {
975                                                 plcontext->value_size *= 2;
976                                         }
977                                 }
978                                 
979                         }                       
980                         if (plcontext->depth-- == 0) {
981                                 assert(!strcmp(name, "extension"));
982                                 plcontext->state = PARSER_PLUGIN;
983                                 XML_SetCharacterDataHandler(plcontext->parser, NULL);
984                         }
985                         break;
986                         
987                 default:
988                         descriptor_errorf(plcontext, 0, _("unexpected closing tag for %s"),
989                                 name);
990                         return;
991         }
992 }
993
994 static void dealloc_plugin_info(cp_context_t *ctx, cp_plugin_info_t *plugin) {
995         cpi_free_plugin(plugin);
996 }
997
998 CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *context, const char *path, cp_status_t *error) {
999         char *file = NULL;
1000         cp_status_t status = CP_OK;
1001         FILE *fh = NULL;
1002         XML_Parser parser = NULL;
1003         ploader_context_t *plcontext = NULL;
1004         cp_plugin_info_t *plugin = NULL;
1005
1006         CHECK_NOT_NULL(context);
1007         CHECK_NOT_NULL(path);
1008         cpi_lock_context(context);
1009         cpi_check_invocation(context, CPI_CF_ANY, __func__);
1010         do {
1011                 int path_len;
1012
1013                 // Construct the file name for the plug-in descriptor 
1014                 path_len = strlen(path);
1015                 if (path_len == 0) {
1016                         status = CP_ERR_IO;
1017                         break;
1018                 }
1019                 if (path[path_len - 1] == CP_FNAMESEP_CHAR) {
1020                         path_len--;
1021                 }
1022                 file = malloc((path_len + strlen(CP_PLUGIN_DESCRIPTOR) + 2) * sizeof(char));
1023                 if (file == NULL) {
1024                         status = CP_ERR_RESOURCE;
1025                         break;
1026                 }
1027                 strcpy(file, path);
1028                 file[path_len] = CP_FNAMESEP_CHAR;
1029                 strcpy(file + path_len + 1, CP_PLUGIN_DESCRIPTOR);
1030
1031                 // Open the file 
1032                 if ((fh = fopen(file, "rb")) == NULL) {
1033                         status = CP_ERR_IO;
1034                         break;
1035                 }
1036
1037                 // Initialize the XML parsing 
1038                 parser = XML_ParserCreate(NULL);
1039                 if (parser == NULL) {
1040                         status = CP_ERR_RESOURCE;
1041                         break;
1042                 }
1043                 XML_SetElementHandler(parser,
1044                         start_element_handler,
1045                         end_element_handler);
1046                 
1047                 // Initialize the parsing context 
1048                 if ((plcontext = malloc(sizeof(ploader_context_t))) == NULL) {
1049                         status = CP_ERR_RESOURCE;
1050                         break;
1051                 }
1052                 memset(plcontext, 0, sizeof(ploader_context_t));
1053                 if ((plcontext->plugin = malloc(sizeof(cp_plugin_info_t))) == NULL) {
1054                         status = CP_ERR_RESOURCE;
1055                         break;
1056                 }
1057                 plcontext->context = context;
1058                 plcontext->configuration = NULL;
1059                 plcontext->value = NULL;
1060                 plcontext->parser = parser;
1061                 plcontext->file = file;
1062                 plcontext->state = PARSER_BEGIN;
1063                 memset(plcontext->plugin, 0, sizeof(cp_plugin_info_t));
1064                 plcontext->plugin->name = NULL;
1065                 plcontext->plugin->identifier = NULL;
1066                 plcontext->plugin->version = NULL;
1067                 plcontext->plugin->provider_name = NULL;
1068                 plcontext->plugin->abi_bw_compatibility = NULL;
1069                 plcontext->plugin->api_bw_compatibility = NULL;
1070                 plcontext->plugin->plugin_path = NULL;
1071                 plcontext->plugin->req_cpluff_version = NULL;
1072                 plcontext->plugin->imports = NULL;
1073                 plcontext->plugin->runtime_lib_name = NULL;
1074                 plcontext->plugin->runtime_funcs_symbol = NULL;
1075                 plcontext->plugin->ext_points = NULL;
1076                 plcontext->plugin->extensions = NULL;
1077                 plcontext->plugin->url = NULL;
1078                 plcontext->plugin->resourcetype = NULL;
1079                 XML_SetUserData(parser, plcontext);
1080
1081                 // Parse the plug-in descriptor 
1082                 while (1) {
1083                         int bytes_read;
1084                         void *xml_buffer;
1085                         int i;
1086                         
1087                         // Get buffer from Expat 
1088                         if ((xml_buffer = XML_GetBuffer(parser, CP_XML_PARSER_BUFFER_SIZE))
1089                                 == NULL) {
1090                                 status = CP_ERR_RESOURCE;
1091                                 break;
1092                         }
1093                         
1094                         // Read data into buffer 
1095                         bytes_read = fread(xml_buffer, 1, CP_XML_PARSER_BUFFER_SIZE, fh);
1096                         if (ferror(fh)) {
1097                                 status = CP_ERR_IO;
1098                                 break;
1099                         }
1100
1101                         // Parse the data 
1102                         if (!(i = XML_ParseBuffer(parser, bytes_read, bytes_read == 0))
1103                                 && context != NULL) {
1104                                 cpi_lock_context(context);
1105                                 cpi_errorf(context,
1106                                         N_("XML parsing error in %s, line %d, column %d (%s)."),
1107                                         file,
1108                                         XML_GetErrorLineNumber(parser),
1109                                         XML_GetErrorColumnNumber(parser) + 1,
1110                                         XML_ErrorString(XML_GetErrorCode(parser)));
1111                                 cpi_unlock_context(context);
1112                         }
1113                         if (!i || plcontext->state == PARSER_ERROR) {
1114                                 status = CP_ERR_MALFORMED;
1115                                 break;
1116                         }
1117                         
1118                         if (bytes_read == 0) {
1119                                 break;
1120                         }
1121                 }
1122                 if (status == CP_OK) {
1123                         if (plcontext->state != PARSER_END || plcontext->error_count > 0) {
1124                                 status = CP_ERR_MALFORMED;
1125                         }
1126                         if (plcontext->resource_error_count > 0) {
1127                                 status = CP_ERR_RESOURCE;
1128                         }
1129                 }
1130                 if (status != CP_OK) {
1131                         break;
1132                 }
1133
1134                 // Initialize the plug-in path 
1135                 *(file + path_len) = '\0';
1136                 plcontext->plugin->plugin_path = file;
1137                 file = NULL;
1138                 
1139                 // Increase plug-in usage count
1140                 if ((status = cpi_register_info(context, plcontext->plugin, (void (*)(cp_context_t *, void *)) dealloc_plugin_info)) != CP_OK) {
1141                         break;
1142                 }
1143                 
1144         } while (0);
1145
1146         // Report possible errors
1147         if (status != CP_OK) {
1148                 switch (status) {
1149                         case CP_ERR_MALFORMED:
1150                                 cpi_errorf(context,
1151                                         N_("Plug-in descriptor in %s is invalid."), path);
1152                                 break;
1153                         case CP_ERR_IO:
1154                                 cpi_errorf(context,
1155                                         N_("An I/O error occurred while loading a plug-in descriptor from %s."), path);
1156                                 break;
1157                         case CP_ERR_RESOURCE:
1158                                 cpi_errorf(context,
1159                                         N_("Insufficient system resources to load a plug-in descriptor from %s."), path);
1160                                 break;
1161                         default:
1162                                 cpi_errorf(context,
1163                                         N_("Failed to load a plug-in descriptor from %s."), path);
1164                                 break;
1165                 }
1166         }
1167         cpi_unlock_context(context);
1168
1169         // Release persistently allocated data on failure 
1170         if (status != CP_OK) {
1171                 if (file != NULL) {
1172                         free(file);
1173                         file = NULL;
1174                 }
1175                 if (plcontext != NULL && plcontext->plugin != NULL) {
1176                         cpi_free_plugin(plcontext->plugin);
1177                         plcontext->plugin = NULL;
1178                 }
1179         }
1180         
1181         // Otherwise copy the plug-in pointer
1182         else {
1183                 plugin = plcontext->plugin;
1184         }
1185
1186         // Release data allocated for parsing 
1187         if (parser != NULL) {
1188                 XML_ParserFree(parser);
1189         }
1190         if (fh != NULL) {
1191                 fclose(fh);
1192         }
1193         if (plcontext != NULL) {
1194                 if (plcontext->value != NULL) {
1195                         free(plcontext->value);
1196                 }
1197                 free(plcontext);
1198                 plcontext = NULL;
1199         }
1200
1201         // Return error code
1202         if (error != NULL) {
1203                 *error = status;
1204         }
1205
1206         return plugin;
1207 }