2 * W32 disk modules identifier for libcaptive and its clients
3 * Copyright (C) 2003-2005 Jan Kratochvil <project-captive@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "captive/captivemodid.h" /* self */
23 #include "captive/macros.h"
24 #include "captive/libxml.h"
25 #include <glib/gmessages.h>
26 #include <libxml/xmlreader.h>
27 #include <glib/ghash.h>
30 #include <libgnomevfs/gnome-vfs-file-size.h>
31 #include <openssl/md5.h>
32 #include <openssl/bn.h>
33 #include <openssl/crypto.h>
34 #include <glib/gstrfuncs.h>
38 gchar *captive_calc_md5(gconstpointer base,size_t length)
40 unsigned char md5_bin[1+128/8]; /* 128 bits==16 bytes; '1+' for leading stub to prevent shorter output of BN_bn2hex() */
45 /* already done above */
46 /* Calculate MD5 sum and convert it to hex string: */
47 MD5(base,length,md5_bin+1);
48 md5_bin[0]=0xFF; /* stub to prevent shorter output of BN_bn2hex() */
49 bignum=BN_bin2bn(md5_bin,1+128/8,NULL);
50 hex=BN_bn2hex(bignum);
51 g_assert(strlen(hex)==2*(1+128/8));
56 g_assert(strlen(r)==32);
58 g_assert(isxdigit(*gs));
60 g_assert(isxdigit(*gs));
66 /* map: GINT_TO_POINTER(captive_captivemodid_module.length) -> !=NULL */
67 static GHashTable *module_valid_length_hash;
69 static void module_valid_length_hash_init(void)
71 if (module_valid_length_hash)
73 module_valid_length_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
76 /* map: (const xmlChar *)md5 -> (struct captive_captivemodid_module *) */
77 static GHashTable *module_md5_hash;
79 static void module_md5_hash_init(void)
83 module_md5_hash=g_hash_table_new(g_str_hash,g_str_equal);
86 /* map: (const xmlChar *)type -> (gpointer)GINT_TO_POINTER(priority) */
87 /* We remove entry for module with already the best priority found,
88 * therefore captive_captivemodid_module_type_best_priority_lookup() will return
89 * 'G_MININT' afterwards.
91 static GHashTable *module_type_best_priority_hash;
93 static void module_type_best_priority_hash_init(void)
95 if (module_type_best_priority_hash)
97 module_type_best_priority_hash=g_hash_table_new(g_str_hash,g_str_equal);
100 static void captive_captivemodid_load_module(struct captive_captivemodid_module *module)
102 struct captive_captivemodid_module *module_md5_conflict;
103 gpointer valid_length_value_gpointer;
105 module_md5_hash_init();
106 if ((module_md5_conflict=g_hash_table_lookup(module_md5_hash,module->md5))) {
107 g_warning(_("Ignoring module \"%s\" as it has MD5 conflict with: %s"),
108 module->id,module_md5_conflict->id);
111 g_hash_table_insert(module_md5_hash,(/* de-const */ xmlChar *)module->md5,module);
113 module_valid_length_hash_init();
114 if (!g_hash_table_lookup_extended(module_valid_length_hash,GINT_TO_POINTER(module->length),
116 &valid_length_value_gpointer)) /* value */
117 g_hash_table_insert(module_valid_length_hash,GINT_TO_POINTER(module->length),GINT_TO_POINTER(module->cabinet_used));
119 /* Conflicting 'cabinet_used' values for single 'cabinet size'? */
120 if (valid_length_value_gpointer && GPOINTER_TO_INT(valid_length_value_gpointer)!=module->cabinet_used)
121 g_hash_table_insert(module_valid_length_hash,GINT_TO_POINTER(module->length),NULL);
124 if (strcmp((const char *)module->type,"cabinet")) {
125 if (module->priority>captive_captivemodid_module_type_best_priority_lookup(module->type)) {
126 module_type_best_priority_hash_init();
127 g_hash_table_insert(module_type_best_priority_hash,
128 (/* de-const */ xmlChar *)module->type,GINT_TO_POINTER(module->priority));
133 gboolean captive_captivemodid_module_length_is_valid(GnomeVFSFileSize file_size)
137 if ((GnomeVFSFileSize)(file_size_gint=file_size)!=file_size) /* Size too big to be valid. */
139 module_valid_length_hash_init();
140 return g_hash_table_lookup_extended(module_valid_length_hash,GINT_TO_POINTER(file_size_gint),
145 gint captive_captivemodid_cabinet_length_to_used(gint cabinet_length)
147 gpointer valid_length_value_gpointer;
149 if (!g_hash_table_lookup_extended(module_valid_length_hash,GINT_TO_POINTER(cabinet_length),
151 &valid_length_value_gpointer)) /* value */
153 return GPOINTER_TO_INT(valid_length_value_gpointer);
156 struct captive_captivemodid_module *captive_captivemodid_module_md5_lookup(const gchar *file_md5)
158 g_return_val_if_fail(file_md5!=NULL,NULL);
160 module_md5_hash_init();
161 return g_hash_table_lookup(module_md5_hash,file_md5);
164 gint captive_captivemodid_module_type_best_priority_lookup(const xmlChar *module_type)
169 g_return_val_if_fail(module_type!=NULL,G_MININT);
171 module_type_best_priority_hash_init();
172 errbool=g_hash_table_lookup_extended(module_type_best_priority_hash,
173 module_type, /* lookup_key */
175 &r_gpointer); /* value */
179 return GPOINTER_TO_INT(r_gpointer);
182 /* Returns: TRUE if all modules were found. */
183 gboolean captive_captivemodid_module_type_best_priority_found(const xmlChar *module_type)
187 g_return_val_if_fail(module_type!=NULL,FALSE);
189 module_type_best_priority_hash_init();
190 errbool=g_hash_table_remove(module_type_best_priority_hash,module_type);
191 g_assert(errbool==TRUE);
193 return !g_hash_table_size(module_type_best_priority_hash);
196 static xmlChar *captive_captivemodid_load_module_xml_get_attr
197 (const gchar *captive_captivemodid_pathname,xmlTextReader *xml_reader,const gchar *attr_name)
201 if (!(r=xmlTextReaderGetAttribute(xml_reader,BAD_CAST attr_name))) {
202 /* FIXME: File line identification? */
203 g_warning(_("%s: Undefined attributes: %s"),captive_captivemodid_pathname,attr_name);
209 static long captive_captivemodid_load_module_xml_get_attr_l
210 (const gchar *captive_captivemodid_pathname,xmlTextReader *xml_reader,const gchar *attr_name,long num_min,long num_max)
216 g_return_val_if_fail(num_min-1<num_min,-1);
217 g_return_val_if_fail(num_min<=num_max,num_min-1);
218 g_return_val_if_fail(LONG_MIN<num_min,LONG_MIN);
219 g_return_val_if_fail(num_max<LONG_MAX,num_min-1);
221 if (!(string=captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,attr_name)))
223 r=strtol((const char *)string,&ends,0);
225 if (r<num_min || r>num_max) {
226 g_warning(_("%s: Numer of out range %ld..%ld: %ld"),captive_captivemodid_pathname,num_min,num_max,r);
232 static void captive_captivemodid_load_module_xml(const gchar *captive_captivemodid_pathname,xmlTextReader *xml_reader)
234 struct captive_captivemodid_module *module;
235 xmlChar *cabinet_used_string;
237 captive_new0(module);
238 if (!(module->type=captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,"type")))
239 goto fail_free_module;
240 if (!(module->md5 =captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,"md5")))
241 goto fail_free_module;
242 if (strlen((const char *)module->md5)!=strspn((const char *)module->md5,"0123456789abcdef")) {
243 g_warning(_("%s: Attribute 'md5' can be only lower-cased hexstring: %s"),captive_captivemodid_pathname,module->md5);
244 goto fail_free_module;
246 if (strlen((const char *)module->md5)!=32) {
247 g_warning(_("%s: Attribute 'md5' length must be 32: %s"),captive_captivemodid_pathname,module->md5);
248 goto fail_free_module;
250 if (!(module->id =captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,"id")))
251 goto fail_free_module;
252 if (0>=(module->length=captive_captivemodid_load_module_xml_get_attr_l(
253 captive_captivemodid_pathname,xml_reader,"length",1,G_MAXINT-1)))
254 goto fail_free_module;
255 if (!(cabinet_used_string=xmlTextReaderGetAttribute(xml_reader,BAD_CAST "cabinet_used")))
256 module->cabinet_used=0;
258 xmlFree(cabinet_used_string);
259 if (0>=(module->cabinet_used=captive_captivemodid_load_module_xml_get_attr_l(
260 captive_captivemodid_pathname,xml_reader,"cabinet_used",1,G_MAXINT-1)))
261 goto fail_free_module;
263 if (G_MININT>=(module->priority=captive_captivemodid_load_module_xml_get_attr_l(captive_captivemodid_pathname,xml_reader,"priority",
264 G_MININT+1,G_MAXINT-1)))
265 goto fail_free_module;
266 captive_captivemodid_load_module(module);
270 xmlFree((xmlChar *)module->type);
271 xmlFree((xmlChar *)module->md5);
272 xmlFree((xmlChar *)module->id);
276 static void captive_captivemodid_load_foreach
277 (const xmlChar *type /* key */,gpointer priority_gpointer /* value */,gpointer user_data /* unused */)
279 g_return_if_fail(type!=NULL);
281 g_return_if_fail(captive_captivemodid_module_best_priority_notify!=NULL);
283 (*captive_captivemodid_module_best_priority_notify)((const gchar *)type);
286 void (*captive_captivemodid_module_best_priority_notify)(const gchar *module_type);
288 gboolean captive_captivemodid_load(const gchar *captive_captivemodid_pathname)
290 xmlTextReader *xml_reader;
292 if (!(xml_reader=xmlNewTextReaderFilename(captive_captivemodid_pathname)))
294 while (1==xmlTextReaderRead(xml_reader)) {
295 switch (xmlTextReaderNodeType(xml_reader)) {
297 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
300 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE:
303 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
306 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: /* We do not track tag ends. */
309 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
310 const xmlChar *xml_name;
312 xml_name=xmlTextReaderName(xml_reader);
313 /**/ if (!xmlStrcmp(xml_name,BAD_CAST "modid")) { /* root tag */
315 else if (!xmlStrcmp(xml_name,BAD_CAST "module"))
316 captive_captivemodid_load_module_xml(captive_captivemodid_pathname,xml_reader);
317 else g_warning(_("%s: Unknown ELEMENT node: %s"),captive_captivemodid_pathname,xml_name);
318 xmlFree((xmlChar *)xml_name);
321 default: g_assert_not_reached();
324 xmlFreeTextReader(xml_reader);
326 if (captive_captivemodid_module_best_priority_notify) {
327 g_hash_table_foreach(module_type_best_priority_hash,
328 (GHFunc)captive_captivemodid_load_foreach,NULL);