/* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc * Copyright (C) 2009, 2011, 2014, 2016 J. Ali Harlow * * 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; either version 2 of the License, or * (at your option) any later version. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "plover/plover.h" #include "plover/uri-handler.h" #include "plover/inputstream.h" /* Import a yum filelist as a razor package set. */ enum { YUM_STATE_BEGIN, YUM_STATE_PACKAGE_NAME, YUM_STATE_PACKAGE_ARCH, YUM_STATE_SUMMARY, YUM_STATE_DESCRIPTION, YUM_STATE_URL, YUM_STATE_LICENSE, YUM_STATE_CHECKSUM, YUM_STATE_REQUIRES, YUM_STATE_PROVIDES, YUM_STATE_OBSOLETES, YUM_STATE_CONFLICTS, YUM_STATE_SKIPPING_PACKAGE, YUM_STATE_FILE }; struct yum_context { XML_Parser primary_parser; XML_Parser filelists_parser; XML_Parser current_parser; struct razor_importer *importer; struct import_property_context *current_property_context; const char *base_uri; GTree *uris; char name[256], arch[64], summary[512], description[4096]; char url[256], license[64], buffer[512], *p; char pkgid[128], evr[128]; uint32_t property_type; int state; int total, current; }; static uint32_t yum_to_razor_relation (const char *flags) { if (flags[0] == 'L') { if (flags[1] == 'T') return RAZOR_PROPERTY_LESS; else return RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL; } else if (flags[0] == 'G') { if (flags[1] == 'T') return RAZOR_PROPERTY_GREATER; else return RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL; } else return RAZOR_PROPERTY_EQUAL; } static void yum_primary_start_element(void *data, const char *name, const char **atts) { struct yum_context *ctx = data; const char *n, *epoch, *version, *release; char buffer[128]; char *s; gchar *nevra; uint32_t pre, relation, flags; int i; if (ctx->state == YUM_STATE_SKIPPING_PACKAGE) return; if (strcmp(name, "metadata") == 0) { for (i = 0; atts[i]; i += 2) { if (strcmp(atts[i], "packages") == 0) ctx->total = atoi(atts[i + 1]); } } else if (strcmp(name, "package") == 0) { *ctx->name=*ctx->arch=*ctx->summary=*ctx->description='\0'; *ctx->url=*ctx->license='\0'; } else if (strcmp(name, "name") == 0) { ctx->state = YUM_STATE_PACKAGE_NAME; ctx->p = ctx->name; } else if (strcmp(name, "arch") == 0) { ctx->state = YUM_STATE_PACKAGE_ARCH; ctx->p = ctx->arch; } else if (strcmp(name, "version") == 0) { epoch = NULL; version = NULL; release = NULL; for (i = 0; atts[i]; i += 2) { if (strcmp(atts[i], "epoch") == 0) epoch = atts[i + 1]; else if (strcmp(atts[i], "ver") == 0) version = atts[i + 1]; else if (strcmp(atts[i], "rel") == 0) release = atts[i + 1]; } if (version == NULL || release == NULL) { fprintf(stderr, "invalid version tag, " "missing version or release attribute\n"); return; } razor_build_evr(ctx->evr, sizeof ctx->evr, epoch, version, release); if (!strcmp(ctx->arch, "noarch") || !strcmp(ctx->arch, razor_system_arch())) { razor_importer_begin_package(ctx->importer, ctx->name, ctx->evr, ctx->arch); } else ctx->state = YUM_STATE_SKIPPING_PACKAGE; } else if (strcmp(name, "summary") == 0) { ctx->p = ctx->summary; ctx->state = YUM_STATE_SUMMARY; } else if (strcmp(name, "description") == 0) { ctx->p = ctx->description; ctx->state = YUM_STATE_DESCRIPTION; } else if (strcmp(name, "url") == 0) { ctx->p = ctx->url; ctx->state = YUM_STATE_URL; } else if (strcmp(name, "checksum") == 0) { ctx->p = ctx->pkgid; ctx->state = YUM_STATE_CHECKSUM; } else if (strcmp(name, "location") == 0) { if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) { for (i = 0; atts[i]; i += 2) if (strcmp(atts[i], "href") == 0) { nevra=g_strconcat(ctx->name,"-",ctx->evr,".", ctx->arch,NULL); s=razor_path_relative_to_uri(ctx->base_uri, atts[i + 1],NULL); g_tree_insert(ctx->uris,nevra,g_strdup(s)); free(s); break; } } } else if (strcmp(name, "rpm:license") == 0) { ctx->p = ctx->license; ctx->state = YUM_STATE_LICENSE; } else if (strcmp(name, "rpm:requires") == 0) { ctx->state = YUM_STATE_REQUIRES; ctx->property_type = RAZOR_PROPERTY_REQUIRES; } else if (strcmp(name, "rpm:provides") == 0) { ctx->state = YUM_STATE_PROVIDES; ctx->property_type = RAZOR_PROPERTY_PROVIDES; } else if (strcmp(name, "rpm:obsoletes") == 0) { ctx->state = YUM_STATE_OBSOLETES; ctx->property_type = RAZOR_PROPERTY_OBSOLETES; } else if (strcmp(name, "rpm:conflicts") == 0) { ctx->state = YUM_STATE_CONFLICTS; ctx->property_type = RAZOR_PROPERTY_CONFLICTS; } else if (strcmp(name, "rpm:entry") == 0 && ctx->state != YUM_STATE_BEGIN) { n = NULL; epoch = NULL; version = NULL; release = NULL; relation = RAZOR_PROPERTY_EQUAL; pre = 0; for (i = 0; atts[i]; i += 2) { if (strcmp(atts[i], "name") == 0) n = atts[i + 1]; else if (strcmp(atts[i], "epoch") == 0) epoch = atts[i + 1]; else if (strcmp(atts[i], "ver") == 0) version = atts[i + 1]; else if (strcmp(atts[i], "rel") == 0) release = atts[i + 1]; else if (strcmp(atts[i], "flags") == 0) relation = yum_to_razor_relation(atts[i + 1]); else if (strcmp(atts[i], "pre") == 0) pre = RAZOR_PROPERTY_PRE; } if (n == NULL) { fprintf(stderr, "invalid rpm:entry, " "missing name or version attributes\n"); return; } razor_build_evr(buffer, sizeof buffer, epoch, version, release); flags = ctx->property_type | relation | pre; razor_importer_add_property(ctx->importer, n, flags, buffer); } } static void yum_primary_end_element (void *data, const char *name) { struct yum_context *ctx = data; switch (ctx->state) { case YUM_STATE_PACKAGE_NAME: case YUM_STATE_PACKAGE_ARCH: case YUM_STATE_SUMMARY: case YUM_STATE_DESCRIPTION: case YUM_STATE_URL: case YUM_STATE_LICENSE: case YUM_STATE_CHECKSUM: case YUM_STATE_FILE: ctx->state = YUM_STATE_BEGIN; break; } if (strcmp(name, "package") == 0) { if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) razor_importer_add_details(ctx->importer, ctx->summary, ctx->description, ctx->url, ctx->license); XML_StopParser(ctx->current_parser, XML_TRUE); ctx->current_parser = ctx->filelists_parser; } } static void yum_character_data (void *data, const XML_Char *s, int len) { struct yum_context *ctx = data; switch (ctx->state) { case YUM_STATE_PACKAGE_NAME: case YUM_STATE_PACKAGE_ARCH: case YUM_STATE_SUMMARY: case YUM_STATE_DESCRIPTION: case YUM_STATE_URL: case YUM_STATE_LICENSE: case YUM_STATE_CHECKSUM: case YUM_STATE_FILE: memcpy(ctx->p, s, len); ctx->p += len; *ctx->p = '\0'; break; } } static void yum_filelists_start_element(void *data, const char *name, const char **atts) { struct yum_context *ctx = data; const char *pkg, *pkgid; int i; if (strcmp(name, "package") == 0 && ctx->state != YUM_STATE_SKIPPING_PACKAGE) { pkg = NULL; pkgid = NULL; for (i = 0; atts[i]; i += 2) { if (strcmp(atts[i], "name") == 0) pkg = atts[i + 1]; else if (strcmp(atts[i], "pkgid") == 0) pkgid = atts[i + 1]; } if (strcmp(pkgid, ctx->pkgid) != 0) fprintf(stderr, "primary.xml and filelists.xml " "mismatch for %s: %s vs %s", pkg, pkgid, ctx->pkgid); } else if (strcmp(name, "file") == 0) { if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) ctx->state = YUM_STATE_FILE; ctx->p = ctx->buffer; } } static void yum_filelists_end_element (void *data, const char *name) { struct yum_context *ctx = data; if (strcmp(name, "package") == 0) { XML_StopParser(ctx->current_parser, XML_TRUE); ctx->current_parser = ctx->primary_parser; if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) razor_importer_finish_package(ctx->importer); ctx->state = YUM_STATE_BEGIN; } else if (strcmp(name, "file") == 0) { if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) razor_importer_add_file(ctx->importer, ctx->buffer); } if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) ctx->state = YUM_STATE_BEGIN; } static int plover_system_arch_is_x86(void) { const char *arch=razor_system_arch(); if (!arch || arch[0]!='i' || arch[1]<'3' || arch[1]>'6') return 0; else return !strcmp(arch+2,"86"); } #define XML_BUFFER_SIZE 4096 PloverRepository *plover_repository_new_from_yum_uri(const char *base_uri, GError **error) { struct yum_context ctx; gchar *s,**rpm_uris; GPtrArray *uris; char *uri; const char *name,*version,*arch; void *buf; gssize len; GInputStream *stream; GInputStream *primary,*filelists; GZlibDecompressor *decompressor; XML_ParsingStatus status; struct razor_set *razor; struct razor_package_iterator *iter; struct razor_package *pkg; PloverPackageSet *set; PloverRepository *repository; g_return_val_if_fail(plover__uri_validate(base_uri),NULL); plover__uri_handler_init(); ctx.importer=razor_importer_create(); ctx.state=YUM_STATE_BEGIN; ctx.base_uri=base_uri; ctx.primary_parser=XML_ParserCreate(NULL); XML_SetUserData(ctx.primary_parser,&ctx); XML_SetElementHandler(ctx.primary_parser,yum_primary_start_element, yum_primary_end_element); XML_SetCharacterDataHandler(ctx.primary_parser,yum_character_data); ctx.filelists_parser=XML_ParserCreate(NULL); XML_SetUserData(ctx.filelists_parser,&ctx); XML_SetElementHandler(ctx.filelists_parser,yum_filelists_start_element, yum_filelists_end_element); XML_SetCharacterDataHandler(ctx.filelists_parser,yum_character_data); uri=razor_path_relative_to_uri(base_uri,"repodata/primary.xml.gz",NULL); stream=plover_razor_input_stream_new(uri,error); free(uri); if (!stream) { XML_ParserFree(ctx.primary_parser); XML_ParserFree(ctx.filelists_parser); return NULL; } decompressor=g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP); primary=g_converter_input_stream_new(G_INPUT_STREAM(stream), G_CONVERTER(decompressor)); g_object_unref(stream); g_object_unref(decompressor); uri=razor_path_relative_to_uri(base_uri,"repodata/filelists.xml.gz",NULL); stream=plover_razor_input_stream_new(uri,error); free(uri); if (!stream) { g_object_unref(primary); XML_ParserFree(ctx.primary_parser); XML_ParserFree(ctx.filelists_parser); return NULL; } decompressor=g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP); filelists=g_converter_input_stream_new(G_INPUT_STREAM(stream), G_CONVERTER(decompressor)); g_object_unref(stream); g_object_unref(decompressor); ctx.current_parser=ctx.primary_parser; ctx.uris=g_tree_new_full((GCompareDataFunc)strcmp,NULL,g_free,NULL); ctx.current=0; do { XML_GetParsingStatus(ctx.current_parser,&status); switch (status.parsing) { case XML_SUSPENDED: XML_ResumeParser(ctx.current_parser); break; case XML_PARSING: case XML_INITIALIZED: buf=XML_GetBuffer(ctx.current_parser,XML_BUFFER_SIZE); if (ctx.current_parser==ctx.primary_parser) len=g_input_stream_read(G_INPUT_STREAM(primary),buf, XML_BUFFER_SIZE,NULL,error); else len=g_input_stream_read(G_INPUT_STREAM(filelists),buf, XML_BUFFER_SIZE,NULL,error); if (len<0) return NULL; XML_ParseBuffer(ctx.current_parser,len,!len); break; case XML_FINISHED: break; } } while (status.parsing!=XML_FINISHED); XML_ParserFree(ctx.primary_parser); XML_ParserFree(ctx.filelists_parser); g_object_unref(primary); g_object_unref(filelists); razor=razor_importer_finish(ctx.importer); #if RAZOR_HEADER_VERSION_MIN<=1 /* * Header version 1 is supported by plover v0.3 and is used on * 32-bit intel machines which allows the setup and update * applications from v0.3 to work. On other machines, we don't * want these old applications to work (since they would do * the wrong thing) and so we use the current header version * which they don't support. */ if (plover_system_arch_is_x86()) razor_set_set_header_version(razor,1); #endif uris=g_ptr_array_new(); iter=razor_package_iterator_create(razor); while(razor_package_iterator_next(iter,&pkg,RAZOR_DETAIL_NAME,&name, RAZOR_DETAIL_VERSION,&version,RAZOR_DETAIL_ARCH,&arch,RAZOR_DETAIL_LAST)) { s=g_strconcat(name,"-",version,".",arch,NULL); g_ptr_array_add(uris,g_tree_lookup(ctx.uris,s)); g_free(s); } razor_package_iterator_destroy(iter); g_ptr_array_add(uris,NULL); g_tree_unref(ctx.uris); rpm_uris=(gchar **)g_ptr_array_free(uris,FALSE); set=plover_package_set_new_from_razor(razor); razor_set_unref(razor); repository=plover_repository_new_from_package_set(set, (const char **)rpm_uris); g_object_unref(set); g_strfreev(rpm_uris); return repository; } struct razor_set *plover_razor_set_create_from_yum_uri(const char *base_uri, GError **error) { PloverRepository *repository; PloverPackageSet *set; struct razor_set *razor; g_return_val_if_fail(plover__uri_validate(base_uri),NULL); repository=plover_repository_new_from_yum_uri(base_uri,error); if (!repository) return NULL; set=plover_repository_get_package_set(repository); razor=plover_package_set_get_razor(set); razor_set_ref(razor); g_object_unref(repository); return razor; } struct razor_set *plover_razor_set_create_from_yum(const char *base, GError **error) { gchar *base_uri; GFile *file; struct razor_set *set; file=g_file_new_for_path(base); base_uri=g_file_get_uri(file); g_object_unref(file); set=plover_razor_set_create_from_yum_uri(base_uri,error); g_free(base_uri); return set; }