Bootstrap of 'captive-install-acquire' for W32 modules acquiration process.
[captive.git] / src / install / acquire / captivemodid.c
diff --git a/src/install/acquire/captivemodid.c b/src/install/acquire/captivemodid.c
new file mode 100644 (file)
index 0000000..8421cef
--- /dev/null
@@ -0,0 +1,225 @@
+/* $Id$
+ * W32 disk modules identifier for acquiration installation utility
+ * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * 
+ * 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 "captivemodid.h"      /* self */
+#include <glib/gmessages.h>
+#include <libxml/xmlreader.h>
+#include <glib/ghash.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <libgnomevfs/gnome-vfs-file-size.h>
+#include <openssl/md5.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <glib/gstrfuncs.h>
+#include <ctype.h>
+
+#include <captive/macros.h>
+
+
+gchar *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(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 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);
+}
+
+static void captivemodid_load_module(struct captivemodid_module *module)
+{
+struct captivemodid_module *module_md5_conflict;
+
+       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();
+       g_hash_table_insert(module_valid_length_hash,GINT_TO_POINTER(module->length),module_valid_length_hash);
+}
+
+gboolean 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;
+       return !!g_hash_table_lookup(module_valid_length_hash,GINT_TO_POINTER(file_size_gint));
+}
+
+struct captivemodid_module *captivemodid_module_md5_lookup(const gchar *file_md5)
+{
+       g_return_val_if_fail(file_md5!=NULL,NULL);
+
+       return g_hash_table_lookup(module_md5_hash,file_md5);
+}
+
+static xmlChar *captivemodid_load_module_xml_get_attr
+               (const gchar *captivemodid_pathname,xmlTextReader *xml_reader,const gchar *attr_name)
+{
+xmlChar *r;
+
+       if (!(r=xmlTextReaderGetAttribute(xml_reader,attr_name))) {
+               /* FIXME: File line identification? */
+               g_warning(_("%s: Undefined attributes: %s"),captivemodid_pathname,attr_name);
+               return NULL;
+               }
+       return r;
+}
+
+static long captivemodid_load_module_xml_get_attr_l
+               (const gchar *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-1<num_min,-1);
+       g_return_val_if_fail(num_min<=num_max,num_min-1);
+       g_return_val_if_fail(LONG_MIN<num_min,LONG_MIN);
+       g_return_val_if_fail(num_max<LONG_MAX,num_min-1);
+
+       if (!(string=captivemodid_load_module_xml_get_attr(captivemodid_pathname,xml_reader,attr_name)))
+               return num_min-1;
+       r=strtol(string,&ends,0);
+       xmlFree(string);
+       if (r<num_min || r>num_max) {
+               g_warning(_("%s: Numer of out range %ld..%ld: %ld"),captivemodid_pathname,num_min,num_max,r);
+               return num_min-1;
+               }
+       return r;
+}
+
+static void captivemodid_load_module_xml(const gchar *captivemodid_pathname,xmlTextReader *xml_reader)
+{
+struct captivemodid_module *module;
+
+       captive_new0(module);
+       if (!(module->type=captivemodid_load_module_xml_get_attr(captivemodid_pathname,xml_reader,"type")))
+               goto fail_free_module;
+       if (!(module->md5 =captivemodid_load_module_xml_get_attr(captivemodid_pathname,xml_reader,"md5")))
+               goto fail_free_module;
+       if (strlen(module->md5)!=strspn(module->md5,"0123456789abcdef")) {
+               g_warning(_("%s: Attribute 'md5' can be only lower-cased hexstring: %s"),captivemodid_pathname,module->md5);
+               goto fail_free_module;
+               }
+       if (strlen(module->md5)!=32) {
+               g_warning(_("%s: Attribute 'md5' length must be 32: %s"),captivemodid_pathname,module->md5);
+               goto fail_free_module;
+               }
+       if (!(module->id  =captivemodid_load_module_xml_get_attr(captivemodid_pathname,xml_reader,"id")))
+               goto fail_free_module;
+       if (0>=(module->length=captivemodid_load_module_xml_get_attr_l(captivemodid_pathname,xml_reader,"length",1,G_MAXINT-1)))
+               goto fail_free_module;
+       if (G_MININT>=(module->priority=captivemodid_load_module_xml_get_attr_l(captivemodid_pathname,xml_reader,"priority",
+                       G_MININT+1,G_MAXINT-1)))
+               goto fail_free_module;
+       captivemodid_load_module(module);
+       return;
+
+fail_free_module:
+       xmlFree((xmlChar *)module->type);
+       xmlFree((xmlChar *)module->md5);
+       xmlFree((xmlChar *)module->id);
+       g_free(module);
+}
+
+
+void captivemodid_load(const gchar *captivemodid_pathname)
+{
+xmlTextReader *xml_reader;
+
+       xml_reader=xmlNewTextReaderFilename(captivemodid_pathname);
+       g_assert(xml_reader!=NULL);
+       while (1==xmlTextReaderRead(xml_reader)) {
+               switch (xmlTextReaderNodeType(xml_reader)) {
+
+                       case XML_READER_TYPE_COMMENT:
+                               break;
+
+                       case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+                               break;
+
+                       case XML_READER_TYPE_TEXT:      /* Even empty nodes have some '#text'. */
+                               break;
+
+                       case XML_READER_TYPE_END_ELEMENT:       /* We do not track tag ends. */
+                               break;
+
+                       case XML_READER_TYPE_ELEMENT: {
+const xmlChar *xml_name;
+
+                               xml_name=xmlTextReaderName(xml_reader);
+                               /**/ if (!xmlStrcmp(xml_name,"modid")) {        /* root tag */
+                                       }
+                               else if (!xmlStrcmp(xml_name,"module"))
+                                       captivemodid_load_module_xml(captivemodid_pathname,xml_reader);
+                               else g_warning(_("%s: Unknown ELEMENT node: %s"),captivemodid_pathname,xml_name);
+                               xmlFree((xmlChar *)xml_name);
+                               } break;
+
+                       default: g_assert_not_reached();
+                       }
+               }
+       xmlFreeTextReader(xml_reader);
+}