2006-06-08 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / mono-config.c
1 /*
2  * mono-config.c
3  *
4  * Runtime and assembly configuration file support routines.
5  *
6  * Author: Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2002 Ximian, Inc.
9  */
10 #include "config.h"
11 #include <glib.h>
12 #include <string.h>
13
14 #include "mono/metadata/assembly.h"
15 #include "mono/metadata/loader.h"
16 #include "mono/metadata/mono-config.h"
17 #include "mono/metadata/metadata-internals.h"
18 #include "mono/utils/mono-logger.h"
19
20 static void start_element (GMarkupParseContext *context, 
21                            const gchar         *element_name,
22                            const gchar        **attribute_names,
23                            const gchar        **attribute_values,
24                            gpointer             user_data,
25                            GError             **error);
26
27 static void end_element   (GMarkupParseContext *context,
28                            const gchar         *element_name,
29                            gpointer             user_data,
30                            GError             **error);
31
32 static void parse_text    (GMarkupParseContext *context,
33                            const gchar         *text,
34                            gsize                text_len,
35                            gpointer             user_data,
36                            GError             **error);
37
38 static void passthrough   (GMarkupParseContext *context,
39                            const gchar         *text,
40                            gsize                text_len,
41                            gpointer             user_data,
42                            GError             **error);
43
44 static void parse_error   (GMarkupParseContext *context,
45                            GError              *error,
46                            gpointer             user_data);
47
48 static const GMarkupParser 
49 mono_parser = {
50         start_element,
51         end_element,
52         parse_text,
53         passthrough,
54         parse_error
55 };
56
57 static GHashTable *config_handlers;
58
59 /* when this interface is stable, export it. */
60 typedef struct MonoParseHandler MonoParseHandler;
61
62 struct MonoParseHandler {
63         const char *element_name;
64         void*(*init)   (MonoImage *assembly);
65         void (*start)  (gpointer user_data, const gchar *name,
66                         const gchar **attributes,
67                         const gchar **values);
68         void (*text)   (gpointer user_data, const char *text, gsize test_len);
69         void (*end)    (gpointer user_data, const char *name);
70         void (*finish) (gpointer user_data);
71 };
72
73 typedef struct {
74         MonoParseHandler *current;
75         void *user_data;
76         MonoImage *assembly;
77         int inited;
78 } ParseState;
79
80 static void start_element (GMarkupParseContext *context, 
81                            const gchar         *element_name,
82                            const gchar        **attribute_names,
83                            const gchar        **attribute_values,
84                            gpointer             user_data,
85                            GError             **error)
86 {
87         ParseState *state = user_data;
88         if (!state->current) {
89                 state->current = g_hash_table_lookup (config_handlers, element_name);
90                 if (state->current && state->current->init)
91                         state->user_data = state->current->init (state->assembly);
92         }
93         if (state->current && state->current->start)
94                 state->current->start (state->user_data, element_name, attribute_names, attribute_values);
95 }
96
97 static void end_element   (GMarkupParseContext *context,
98                            const gchar         *element_name,
99                            gpointer             user_data,
100                            GError             **error)
101 {
102         ParseState *state = user_data;
103         if (state->current) {
104                 if (state->current->end)
105                         state->current->end (state->user_data, element_name);
106                 if (strcmp (state->current->element_name, element_name) == 0) {
107                         if (state->current->finish)
108                                 state->current->finish (state->user_data);
109                         state->current = NULL;
110                         state->user_data = NULL;
111                 }
112         }
113 }
114
115 static void parse_text    (GMarkupParseContext *context,
116                            const gchar         *text,
117                            gsize                text_len,
118                            gpointer             user_data,
119                            GError             **error)
120 {
121         ParseState *state = user_data;
122         if (state->current && state->current->text)
123                 state->current->text (state->user_data, text, text_len);
124 }
125
126 static void passthrough   (GMarkupParseContext *context,
127                            const gchar         *text,
128                            gsize                text_len,
129                            gpointer             user_data,
130                            GError             **error)
131 {
132         /* do nothing */
133 }
134
135 static void parse_error   (GMarkupParseContext *context,
136                            GError              *error,
137                            gpointer             user_data)
138 {
139 }
140
141 typedef struct {
142         char *dll;
143         char *target;
144         MonoImage *assembly;
145 } DllInfo;
146
147 static void*
148 dllmap_init (MonoImage *assembly) {
149         DllInfo *info = g_new0 (DllInfo, 1);
150         info->assembly = assembly;
151         return info;
152 }
153
154 static void
155 dllmap_start (gpointer user_data, 
156               const gchar         *element_name,
157               const gchar        **attribute_names,
158               const gchar        **attribute_values)
159 {
160         int i;
161         DllInfo *info = user_data;
162         
163         if (strcmp (element_name, "dllmap") == 0) {
164                 g_free (info->dll);
165                 g_free (info->target);
166                 info->dll = info->target = NULL;
167                 for (i = 0; attribute_names [i]; ++i) {
168                         if (strcmp (attribute_names [i], "dll") == 0)
169                                 info->dll = g_strdup (attribute_values [i]);
170                         else if (strcmp (attribute_names [i], "target") == 0)
171                                 info->target = g_strdup (attribute_values [i]);
172                 }
173                 mono_dllmap_insert (info->assembly, info->dll, NULL, info->target, NULL);
174         } else if (strcmp (element_name, "dllentry") == 0) {
175                 const char *name = NULL, *target = NULL, *dll = NULL;
176                 for (i = 0; attribute_names [i]; ++i) {
177                         if (strcmp (attribute_names [i], "dll") == 0)
178                                 dll = attribute_values [i];
179                         else if (strcmp (attribute_names [i], "target") == 0)
180                                 target = attribute_values [i];
181                         else if (strcmp (attribute_names [i], "name") == 0)
182                                 name = attribute_values [i];
183                 }
184                 if (!dll)
185                         dll = info->dll;
186                 mono_dllmap_insert (info->assembly, info->dll, name, dll, target);
187         }
188 }
189
190 static void
191 dllmap_finish (gpointer user_data)
192 {
193         DllInfo *info = user_data;
194
195         g_free (info->dll);
196         g_free (info->target);
197         g_free (info);
198 }
199
200 static const MonoParseHandler
201 dllmap_handler = {
202         "dllmap",
203         dllmap_init,
204         dllmap_start,
205         NULL, /* text */
206         NULL, /* end */
207         dllmap_finish
208 };
209
210 static int inited = 0;
211
212 static void
213 mono_config_init (void)
214 {
215         inited = 1;
216         config_handlers = g_hash_table_new (g_str_hash, g_str_equal);
217         g_hash_table_insert (config_handlers, (gpointer) dllmap_handler.element_name, (gpointer) &dllmap_handler);
218 }
219
220 /* FIXME: error handling */
221
222 static void
223 mono_config_parse_xml_with_context (ParseState *state, const char *text, gsize len)
224 {
225         GMarkupParseContext *context;
226
227         if (!inited)
228                 mono_config_init ();
229
230         context = g_markup_parse_context_new (&mono_parser, 0, state, NULL);
231         if (g_markup_parse_context_parse (context, text, len, NULL)) {
232                 g_markup_parse_context_end_parse (context, NULL);
233         }
234         g_markup_parse_context_free (context);
235 }
236
237 /* If assembly is NULL, parse in the global context */
238 static int
239 mono_config_parse_file_with_context (ParseState *state, const char *filename)
240 {
241         char *text;
242         gsize len;
243
244         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_CONFIG,
245                         "Config attempting to parse: '%s'.", filename);
246
247         if (!g_file_get_contents (filename, &text, &len, NULL))
248                 return 0;
249         mono_config_parse_xml_with_context (state, text, len);
250         g_free (text);
251         return 1;
252 }
253
254 /**
255  * mono_config_parse_memory:
256  * @buffer: a pointer to an string XML representation of the configuration
257  *
258  * Parses the configuration from a buffer
259  */
260 void
261 mono_config_parse_memory (const char *buffer)
262 {
263         ParseState state = {NULL};
264         
265         mono_config_parse_xml_with_context (&state, buffer, strlen (buffer));
266 }
267
268 static void
269 mono_config_parse_file (const char *filename)
270 {
271         ParseState state = {NULL};
272         mono_config_parse_file_with_context (&state, filename);
273 }
274
275 /* 
276  * use the equivalent lookup code from the GAC when available.
277  * Depending on state, this should give something like:
278  *      aname/version-pubtoken/
279  *      aname/version/
280  *      aname
281  */
282 static char*
283 get_assembly_filename (MonoImage *image, int state)
284 {
285         switch (state) {
286         case 0:
287                 return g_strdup (mono_image_get_name (image));
288         default:
289                 return NULL;
290         }
291 }
292
293 typedef struct _BundledConfig BundledConfig;
294
295 struct _BundledConfig {
296         BundledConfig *next;
297         const char* aname;
298         const char* config_xml;
299 };
300
301 static BundledConfig *bundled_configs = NULL;
302
303 void
304 mono_register_config_for_assembly (const char* assembly_name, const char* config_xml)
305 {
306         BundledConfig *bconfig;
307
308         bconfig = g_new0 (BundledConfig, 1);
309         bconfig->aname = assembly_name;
310         bconfig->config_xml = config_xml;
311         bconfig->next = bundled_configs;
312         bundled_configs = bconfig;
313 }
314
315 void 
316 mono_config_for_assembly (MonoImage *assembly)
317 {
318         ParseState state = {NULL};
319         int got_it = 0, i;
320         char *aname, *cfg, *cfg_name;
321         const char *home;
322         BundledConfig *bconfig;
323         
324         state.assembly = assembly;
325
326         for (bconfig = bundled_configs; bconfig; bconfig = bconfig->next) {
327                 if (bconfig->aname && strcmp (bconfig->aname, assembly->module_name) == 0)
328                         mono_config_parse_xml_with_context (&state, bconfig->config_xml, strlen (bconfig->config_xml));
329         }
330
331         cfg_name = g_strdup_printf ("%s.config", mono_image_get_filename (assembly));
332         mono_config_parse_file_with_context (&state, cfg_name);
333         g_free (cfg_name);
334
335         cfg_name = g_strdup_printf ("%s.config", mono_image_get_name (assembly));
336
337         home = g_get_home_dir ();
338
339         for (i = 0; (aname = get_assembly_filename (assembly, i)) != NULL; ++i) {
340                 cfg = g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname, cfg_name, NULL);
341                 got_it += mono_config_parse_file_with_context (&state, cfg);
342                 g_free (cfg);
343
344 #ifndef PLATFORM_WIN32
345                 cfg = g_build_filename (home, ".mono", "assemblies", aname, cfg_name, NULL);
346                 got_it += mono_config_parse_file_with_context (&state, cfg);
347                 g_free (cfg);
348 #endif
349                 g_free (aname);
350                 if (got_it)
351                         break;
352         }
353         g_free (cfg_name);
354 }
355
356 /**
357  * mono_config_parse:
358  * @filename: the filename to load the configuration variables from.
359  *
360  * Pass a NULL filename to parse the default config files
361  * (or the file in the MONO_CONFIG env var).
362  */
363 void
364 mono_config_parse (const char *filename) {
365         const char *home;
366         char *mono_cfg;
367 #ifndef PLATFORM_WIN32
368         char *user_cfg;
369 #endif
370
371         if (filename) {
372                 mono_config_parse_file (filename);
373                 return;
374         }
375
376         home = g_getenv ("MONO_CONFIG");
377         if (home) {
378                 mono_config_parse_file (home);
379                 return;
380         }
381
382         mono_cfg = g_build_filename (mono_get_config_dir (), "mono", "config", NULL);
383         mono_config_parse_file (mono_cfg);
384         g_free (mono_cfg);
385
386 #ifndef PLATFORM_WIN32
387         home = g_get_home_dir ();
388         user_cfg = g_strconcat (home, G_DIR_SEPARATOR_S, ".mono/config", NULL);
389         mono_config_parse_file (user_cfg);
390         g_free (user_cfg);
391 #endif
392 }
393
394 static const char *mono_cfg_dir = NULL;
395
396 /* Invoked during startup */
397 void
398 mono_set_config_dir (const char *dir)
399 {
400         /* If this variable is set, overrides the directory computed */
401         mono_cfg_dir = g_getenv ("MONO_CFG_DIR");
402         if (mono_cfg_dir == NULL)
403                 mono_cfg_dir = g_strdup (dir);
404 }
405
406 const char* 
407 mono_get_config_dir (void)
408 {
409         if (mono_cfg_dir == NULL)
410                 mono_set_dirs (NULL, NULL);
411
412         return mono_cfg_dir;
413 }
414
415 static void
416 publisher_policy_start (gpointer user_data,
417                 const gchar *element_name,
418                 const gchar **attribute_names,
419                 const gchar **attribute_values)
420 {
421         MonoAssemblyBindingInfo *info;
422         int n;
423
424         info = user_data;
425         if (!strcmp (element_name, "assemblyIdentity")) {
426                 for (n = 0; attribute_names [n]; n++) {
427                         const gchar *attribute_name = attribute_names [n];
428                         
429                         if (!strcmp (attribute_name, "name"))
430                                 info->name = g_strdup (attribute_values [n]);
431                         else if (!strcmp (attribute_name, "publicKeyToken")) {
432                                 if (strlen (attribute_values [n]) == MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)
433                                         g_strlcpy ((char *) info->public_key_token, attribute_values [n], MONO_PUBLIC_KEY_TOKEN_LENGTH);
434                         } else if (!strcmp (attribute_name, "culture")) {
435                                 if (!strcmp (attribute_values [n], "neutral"))
436                                         info->culture = g_strdup ("");
437                                 else
438                                         info->culture = g_strdup (attribute_values [n]);
439                         }
440                 }
441         } else if (!strcmp (element_name, "bindingRedirect")) {
442                 for (n = 0; attribute_names [n]; n++) {
443                         const gchar *attribute_name = attribute_names [n];
444
445                         if (!strcmp (attribute_name, "oldVersion")) {
446                                 gchar **numbers, **version, **versions;
447                                 gint major, minor, build, revision;
448
449                                 /* Invalid value */
450                                 if (!strcmp (attribute_values [n], ""))
451                                         return;
452                                 
453                                 versions = g_strsplit (attribute_values [n], "-", 2);
454                                 version = g_strsplit (*versions, ".", 4);
455
456                                 /* We assign the values to gint vars to do the checks */
457                                 numbers = version;
458                                 major = *numbers ? atoi (*numbers++) : -1;
459                                 minor = *numbers ? atoi (*numbers++) : -1;
460                                 build = *numbers ? atoi (*numbers++) : -1;
461                                 revision = *numbers ? atoi (*numbers) : -1;
462                                 g_strfreev (version);
463                                 if (major < 0 || minor < 0 || build < 0 || revision < 0) {
464                                         g_strfreev (versions);
465                                         return;
466                                 }
467
468                                 info->old_version_bottom.major = major;
469                                 info->old_version_bottom.minor = minor;
470                                 info->old_version_bottom.build = build;
471                                 info->old_version_bottom.revision = revision;
472                                 info->has_old_version_bottom = TRUE;
473
474                                 if (!*(versions + 1)) {
475                                         g_strfreev (versions);
476                                         continue;
477                                 }
478                                 
479                                 numbers = version = g_strsplit (*(versions + 1), ".", 4);
480                                 major = *numbers ? atoi (*numbers++) : -1;
481                                 minor = *numbers ? atoi (*numbers++) : -1;
482                                 build = *numbers ? atoi (*numbers++) : -1;
483                                 revision = *numbers ? atoi (*numbers) : 1;
484                                 g_strfreev (version);
485                                 if (major < 0 || minor < 0 || build < 0 || revision < 0) {
486                                         g_strfreev (versions);
487                                         return;
488                                 }
489
490                                 info->old_version_top.major = major;
491                                 info->old_version_top.minor = minor;
492                                 info->old_version_top.build = build;
493                                 info->old_version_top.revision = revision;
494                                 info->has_old_version_top = TRUE;
495
496                                 g_strfreev (versions);
497                         } else if (!strcmp (attribute_name, "newVersion")) {
498                                 gchar **numbers, **version;
499
500                                 /* Invalid value */
501                                 if (!strcmp (attribute_values [n], ""))
502                                         return;
503
504                                 numbers = version = g_strsplit (attribute_values [n], ".", 4);
505                                 info->new_version.major = *numbers ? atoi (*numbers++) : -1;
506                                 info->new_version.minor = *numbers ? atoi (*numbers++) : -1;
507                                 info->new_version.build = *numbers ? atoi (*numbers++) : -1;
508                                 info->new_version.revision = *numbers ? atoi (*numbers) : -1;
509                                 info->has_new_version = TRUE;
510                                 g_strfreev (version);
511                         }
512                 }
513         }
514 }
515
516 static MonoParseHandler
517 publisher_policy_parser = {
518         "", /* We don't need to use declare an xml element */
519         NULL,
520         publisher_policy_start,
521         NULL,
522         NULL,
523         NULL
524 };
525
526 void
527 mono_config_parse_publisher_policy (const gchar *filename, MonoAssemblyBindingInfo *info)
528 {
529         ParseState state = {
530                 &publisher_policy_parser, /* MonoParseHandler */
531                 info, /* user_data */
532                 NULL, /* MonoImage (we don't need it right now)*/
533                 TRUE /* We are already inited */
534         };
535         
536         mono_config_parse_file_with_context (&state, filename);
537 }
538