/* $Id$ * W32 disk modules identifier for libcaptive and its clients * Copyright (C) 2003-2005 Jan Kratochvil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "captive/captivemodid.h" /* self */ #include "captive/macros.h" #include "captive/libxml.h" #include #include #include #include #include #include #include #include #include #include #include gchar *captive_calc_md5(gconstpointer base,size_t length) { unsigned char md5_bin[1+128/8]; /* 128 bits==16 bytes; '1+' for leading stub to prevent shorter output of BN_bn2hex() */ BIGNUM *bignum; char *hex; gchar *r,*gs; /* already done above */ /* Calculate MD5 sum and convert it to hex string: */ MD5(base,length,md5_bin+1); md5_bin[0]=0xFF; /* stub to prevent shorter output of BN_bn2hex() */ bignum=BN_bin2bn(md5_bin,1+128/8,NULL); hex=BN_bn2hex(bignum); g_assert(strlen(hex)==2*(1+128/8)); r=g_strdup(hex+2); OPENSSL_free(hex); BN_free(bignum); g_assert(strlen(r)==32); for (gs=r;*gs;gs++) { g_assert(isxdigit(*gs)); *gs=tolower(*gs); g_assert(isxdigit(*gs)); } return r; } /* map: GINT_TO_POINTER(captive_captivemodid_module.length) -> !=NULL */ static GHashTable *module_valid_length_hash; static void module_valid_length_hash_init(void) { if (module_valid_length_hash) return; module_valid_length_hash=g_hash_table_new(g_direct_hash,g_direct_equal); } /* map: (const xmlChar *)md5 -> (struct captive_captivemodid_module *) */ static GHashTable *module_md5_hash; static void module_md5_hash_init(void) { if (module_md5_hash) return; module_md5_hash=g_hash_table_new(g_str_hash,g_str_equal); } /* map: (const xmlChar *)type -> (gpointer)GINT_TO_POINTER(priority) */ /* We remove entry for module with already the best priority found, * therefore captive_captivemodid_module_type_best_priority_lookup() will return * 'G_MININT' afterwards. */ static GHashTable *module_type_best_priority_hash; static void module_type_best_priority_hash_init(void) { if (module_type_best_priority_hash) return; module_type_best_priority_hash=g_hash_table_new(g_str_hash,g_str_equal); } static void captive_captivemodid_load_module(struct captive_captivemodid_module *module) { struct captive_captivemodid_module *module_md5_conflict; gpointer valid_length_value_gpointer; module_md5_hash_init(); if ((module_md5_conflict=g_hash_table_lookup(module_md5_hash,module->md5))) { g_warning(_("Ignoring module \"%s\" as it has MD5 conflict with: %s"), module->id,module_md5_conflict->id); return; } g_hash_table_insert(module_md5_hash,(/* de-const */ xmlChar *)module->md5,module); module_valid_length_hash_init(); if (!g_hash_table_lookup_extended(module_valid_length_hash,GINT_TO_POINTER(module->length), NULL, /* orig_key */ &valid_length_value_gpointer)) /* value */ g_hash_table_insert(module_valid_length_hash,GINT_TO_POINTER(module->length),GINT_TO_POINTER(module->cabinet_used)); else { /* Conflicting 'cabinet_used' values for single 'cabinet size'? */ if (valid_length_value_gpointer && GPOINTER_TO_INT(valid_length_value_gpointer)!=module->cabinet_used) g_hash_table_insert(module_valid_length_hash,GINT_TO_POINTER(module->length),NULL); } if (strcmp((const char *)module->type,"cabinet")) { if (module->priority>captive_captivemodid_module_type_best_priority_lookup(module->type)) { module_type_best_priority_hash_init(); g_hash_table_insert(module_type_best_priority_hash, (/* de-const */ xmlChar *)module->type,GINT_TO_POINTER(module->priority)); } } } gboolean captive_captivemodid_module_length_is_valid(GnomeVFSFileSize file_size) { gint file_size_gint; if ((GnomeVFSFileSize)(file_size_gint=file_size)!=file_size) /* Size too big to be valid. */ return FALSE; module_valid_length_hash_init(); return g_hash_table_lookup_extended(module_valid_length_hash,GINT_TO_POINTER(file_size_gint), NULL, /* orig_key */ NULL); /* value */ } gint captive_captivemodid_cabinet_length_to_used(gint cabinet_length) { gpointer valid_length_value_gpointer; if (!g_hash_table_lookup_extended(module_valid_length_hash,GINT_TO_POINTER(cabinet_length), NULL, /* orig_key */ &valid_length_value_gpointer)) /* value */ return 0; return GPOINTER_TO_INT(valid_length_value_gpointer); } struct captive_captivemodid_module *captive_captivemodid_module_md5_lookup(const gchar *file_md5) { g_return_val_if_fail(file_md5!=NULL,NULL); module_md5_hash_init(); return g_hash_table_lookup(module_md5_hash,file_md5); } gint captive_captivemodid_module_type_best_priority_lookup(const xmlChar *module_type) { gpointer r_gpointer; gboolean errbool; g_return_val_if_fail(module_type!=NULL,G_MININT); module_type_best_priority_hash_init(); errbool=g_hash_table_lookup_extended(module_type_best_priority_hash, module_type, /* lookup_key */ NULL, /* orig_key */ &r_gpointer); /* value */ if (!errbool) return G_MININT; return GPOINTER_TO_INT(r_gpointer); } /* Returns: TRUE if all modules were found. */ gboolean captive_captivemodid_module_type_best_priority_found(const xmlChar *module_type) { gboolean errbool; g_return_val_if_fail(module_type!=NULL,FALSE); module_type_best_priority_hash_init(); errbool=g_hash_table_remove(module_type_best_priority_hash,module_type); g_assert(errbool==TRUE); return !g_hash_table_size(module_type_best_priority_hash); } static xmlChar *captive_captivemodid_load_module_xml_get_attr (const gchar *captive_captivemodid_pathname,xmlTextReader *xml_reader,const gchar *attr_name) { xmlChar *r; if (!(r=xmlTextReaderGetAttribute(xml_reader,BAD_CAST attr_name))) { /* FIXME: File line identification? */ g_warning(_("%s: Undefined attributes: %s"),captive_captivemodid_pathname,attr_name); return NULL; } return r; } static long captive_captivemodid_load_module_xml_get_attr_l (const gchar *captive_captivemodid_pathname,xmlTextReader *xml_reader,const gchar *attr_name,long num_min,long num_max) { xmlChar *string; long r; char *ends; g_return_val_if_fail(num_min-1num_max) { g_warning(_("%s: Numer of out range %ld..%ld: %ld"),captive_captivemodid_pathname,num_min,num_max,r); return num_min-1; } return r; } static void captive_captivemodid_load_module_xml(const gchar *captive_captivemodid_pathname,xmlTextReader *xml_reader) { struct captive_captivemodid_module *module; xmlChar *cabinet_used_string; captive_new0(module); if (!(module->type=captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,"type"))) goto fail_free_module; if (!(module->md5 =captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,"md5"))) goto fail_free_module; if (strlen((const char *)module->md5)!=strspn((const char *)module->md5,"0123456789abcdef")) { g_warning(_("%s: Attribute 'md5' can be only lower-cased hexstring: %s"),captive_captivemodid_pathname,module->md5); goto fail_free_module; } if (strlen((const char *)module->md5)!=32) { g_warning(_("%s: Attribute 'md5' length must be 32: %s"),captive_captivemodid_pathname,module->md5); goto fail_free_module; } if (!(module->id =captive_captivemodid_load_module_xml_get_attr(captive_captivemodid_pathname,xml_reader,"id"))) goto fail_free_module; if (0>=(module->length=captive_captivemodid_load_module_xml_get_attr_l( captive_captivemodid_pathname,xml_reader,"length",1,G_MAXINT-1))) goto fail_free_module; if (!(cabinet_used_string=xmlTextReaderGetAttribute(xml_reader,BAD_CAST "cabinet_used"))) module->cabinet_used=0; else { xmlFree(cabinet_used_string); if (0>=(module->cabinet_used=captive_captivemodid_load_module_xml_get_attr_l( captive_captivemodid_pathname,xml_reader,"cabinet_used",1,G_MAXINT-1))) goto fail_free_module; } if (G_MININT>=(module->priority=captive_captivemodid_load_module_xml_get_attr_l(captive_captivemodid_pathname,xml_reader,"priority", G_MININT+1,G_MAXINT-1))) goto fail_free_module; captive_captivemodid_load_module(module); return; fail_free_module: xmlFree((xmlChar *)module->type); xmlFree((xmlChar *)module->md5); xmlFree((xmlChar *)module->id); g_free(module); } static void captive_captivemodid_load_foreach (const xmlChar *type /* key */,gpointer priority_gpointer /* value */,gpointer user_data /* unused */) { g_return_if_fail(type!=NULL); g_return_if_fail(captive_captivemodid_module_best_priority_notify!=NULL); (*captive_captivemodid_module_best_priority_notify)((const gchar *)type); } void (*captive_captivemodid_module_best_priority_notify)(const gchar *module_type); gboolean captive_captivemodid_load(const gchar *captive_captivemodid_pathname) { xmlTextReader *xml_reader; if (!(xml_reader=xmlNewTextReaderFilename(captive_captivemodid_pathname))) return FALSE; while (1==xmlTextReaderRead(xml_reader)) { switch (xmlTextReaderNodeType(xml_reader)) { case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT: break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE: break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */ break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: /* We do not track tag ends. */ break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: { const xmlChar *xml_name; xml_name=xmlTextReaderName(xml_reader); /**/ if (!xmlStrcmp(xml_name,BAD_CAST "modid")) { /* root tag */ } else if (!xmlStrcmp(xml_name,BAD_CAST "module")) captive_captivemodid_load_module_xml(captive_captivemodid_pathname,xml_reader); else g_warning(_("%s: Unknown ELEMENT node: %s"),captive_captivemodid_pathname,xml_name); xmlFree((xmlChar *)xml_name); } break; default: g_assert_not_reached(); } } xmlFreeTextReader(xml_reader); if (captive_captivemodid_module_best_priority_notify) { g_hash_table_foreach(module_type_best_priority_hash, (GHFunc)captive_captivemodid_load_foreach,NULL); } return TRUE; }