diff options
Diffstat (limited to 'rpmio')
35 files changed, 12972 insertions, 0 deletions
diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am new file mode 100644 index 0000000..b1003c5 --- /dev/null +++ b/rpmio/Makefile.am @@ -0,0 +1,40 @@ +# Makefile for rpm library. + +AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/ +AM_CPPFLAGS += @WITH_NSS_INCLUDE@ +AM_CPPFLAGS += @WITH_POPT_INCLUDE@ +AM_CPPFLAGS += -I$(top_srcdir)/misc +AM_CPPFLAGS += -DRPMCONFIGDIR="\"@RPMCONFIGDIR@\"" +AM_CPPFLAGS += -DLOCALSTATEDIR="\"$(localstatedir)\"" + +usrlibdir = $(libdir) +usrlib_LTLIBRARIES = librpmio.la +librpmio_la_SOURCES = \ + argv.c base64.h base64.c digest.h digest.c macro.c \ + rpmhook.c rpmio.c rpmlog.c rpmmalloc.c \ + rpmpgp.c rpmsq.c rpmsw.c url.c \ + rpmio_internal.h rpmhook.h \ + rpmstring.c rpmfileutil.c \ + rpmkeyring.c + +librpmio_la_LDFLAGS = -version-info 2:1:0 +librpmio_la_LIBADD = \ + ../misc/libmisc.la \ + @WITH_NSS_LIB@ \ + @WITH_BZ2_LIB@ \ + @WITH_ZLIB_LIB@ \ + @WITH_LIBELF_LIB@ \ + @WITH_POPT_LIB@ \ + @WITH_LZMA_LIB@ \ + -lpthread + +if WITH_LUA +AM_CPPFLAGS += -I$(top_srcdir)/luaext/ +AM_CPPFLAGS += @LUA_CFLAGS@ +librpmio_la_SOURCES += rpmlua.c rpmlua.h +librpmio_la_LIBADD += @LUA_LIBS@ +librpmio_la_LIBADD += $(top_builddir)/luaext/libluaext.la +endif + +check_PROGRAMS = + diff --git a/rpmio/Makefile.in b/rpmio/Makefile.in new file mode 100644 index 0000000..68cbd8a --- /dev/null +++ b/rpmio/Makefile.in @@ -0,0 +1,689 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Makefile for rpm library. + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@WITH_LUA_TRUE@am__append_1 = -I$(top_srcdir)/luaext/ @LUA_CFLAGS@ +@WITH_LUA_TRUE@am__append_2 = rpmlua.c rpmlua.h +@WITH_LUA_TRUE@am__append_3 = @LUA_LIBS@ \ +@WITH_LUA_TRUE@ $(top_builddir)/luaext/libluaext.la +check_PROGRAMS = +subdir = rpmio +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(usrlibdir)" +LTLIBRARIES = $(usrlib_LTLIBRARIES) +@WITH_LUA_TRUE@am__DEPENDENCIES_1 = \ +@WITH_LUA_TRUE@ $(top_builddir)/luaext/libluaext.la +librpmio_la_DEPENDENCIES = ../misc/libmisc.la $(am__DEPENDENCIES_1) +am__librpmio_la_SOURCES_DIST = argv.c base64.h base64.c digest.h \ + digest.c macro.c rpmhook.c rpmio.c rpmlog.c rpmmalloc.c \ + rpmpgp.c rpmsq.c rpmsw.c url.c rpmio_internal.h rpmhook.h \ + rpmstring.c rpmfileutil.c rpmkeyring.c rpmlua.c rpmlua.h +@WITH_LUA_TRUE@am__objects_1 = rpmlua.lo +am_librpmio_la_OBJECTS = argv.lo base64.lo digest.lo macro.lo \ + rpmhook.lo rpmio.lo rpmlog.lo rpmmalloc.lo rpmpgp.lo rpmsq.lo \ + rpmsw.lo url.lo rpmstring.lo rpmfileutil.lo rpmkeyring.lo \ + $(am__objects_1) +librpmio_la_OBJECTS = $(am_librpmio_la_OBJECTS) +librpmio_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(librpmio_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(librpmio_la_SOURCES) +DIST_SOURCES = $(am__librpmio_la_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FIXPERMS = @FIXPERMS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RPMCANONARCH = @RPMCANONARCH@ +RPMCANONCOLOR = @RPMCANONCOLOR@ +RPMCANONGNU = @RPMCANONGNU@ +RPMCANONOS = @RPMCANONOS@ +RPMCANONVENDOR = @RPMCANONVENDOR@ +RPMCONFIGDIR = @RPMCONFIGDIR@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WITH_ACL_LIB = @WITH_ACL_LIB@ +WITH_BZ2_LIB = @WITH_BZ2_LIB@ +WITH_CAP_LIB = @WITH_CAP_LIB@ +WITH_DB_LIB = @WITH_DB_LIB@ +WITH_LIBELF_LIB = @WITH_LIBELF_LIB@ +WITH_LZMA_LIB = @WITH_LZMA_LIB@ +WITH_MAGIC_INCLUDE = @WITH_MAGIC_INCLUDE@ +WITH_MAGIC_LIB = @WITH_MAGIC_LIB@ +WITH_NSS_INCLUDE = @WITH_NSS_INCLUDE@ +WITH_NSS_LIB = @WITH_NSS_LIB@ +WITH_POPT_INCLUDE = @WITH_POPT_INCLUDE@ +WITH_POPT_LIB = @WITH_POPT_LIB@ +WITH_PYTHON_INCLUDE = @WITH_PYTHON_INCLUDE@ +WITH_PYTHON_LIB = @WITH_PYTHON_LIB@ +WITH_SELINUX_LIB = @WITH_SELINUX_LIB@ +WITH_SEMANAGE_LIB = @WITH_SEMANAGE_LIB@ +WITH_ZLIB_INCLUDE = @WITH_ZLIB_INCLUDE@ +WITH_ZLIB_LIB = @WITH_ZLIB_LIB@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +__BZIP2 = @__BZIP2@ +__CAT = @__CAT@ +__CC = @__CC@ +__CHGRP = @__CHGRP@ +__CHMOD = @__CHMOD@ +__CHOWN = @__CHOWN@ +__CP = @__CP@ +__CPIO = @__CPIO@ +__CURL = @__CURL@ +__FAKECHROOT = @__FAKECHROOT@ +__FILE = @__FILE@ +__GPG = @__GPG@ +__GREP = @__GREP@ +__GZIP = @__GZIP@ +__ID = @__ID@ +__INSTALL = @__INSTALL@ +__LD = @__LD@ +__LRZIP = @__LRZIP@ +__LZIP = @__LZIP@ +__MAKE = @__MAKE@ +__MKDIR = @__MKDIR@ +__MKDIR_P = @__MKDIR_P@ +__MV = @__MV@ +__NM = @__NM@ +__OBJCOPY = @__OBJCOPY@ +__OBJDUMP = @__OBJDUMP@ +__PATCH = @__PATCH@ +__PERL = @__PERL@ +__PGP = @__PGP@ +__PYTHON = @__PYTHON@ +__RESTORECON = @__RESTORECON@ +__RM = @__RM@ +__RSH = @__RSH@ +__SED = @__SED@ +__SEMODULE = @__SEMODULE@ +__SSH = @__SSH@ +__STRIP = @__STRIP@ +__TAR = @__TAR@ +__UNZIP = @__UNZIP@ +__XZ = @__XZ@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dirstamp = @dirstamp@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) \ + -I$(top_builddir)/include/ @WITH_NSS_INCLUDE@ \ + @WITH_POPT_INCLUDE@ -I$(top_srcdir)/misc \ + -DRPMCONFIGDIR="\"@RPMCONFIGDIR@\"" \ + -DLOCALSTATEDIR="\"$(localstatedir)\"" $(am__append_1) +usrlibdir = $(libdir) +usrlib_LTLIBRARIES = librpmio.la +librpmio_la_SOURCES = argv.c base64.h base64.c digest.h digest.c \ + macro.c rpmhook.c rpmio.c rpmlog.c rpmmalloc.c rpmpgp.c \ + rpmsq.c rpmsw.c url.c rpmio_internal.h rpmhook.h rpmstring.c \ + rpmfileutil.c rpmkeyring.c $(am__append_2) +librpmio_la_LDFLAGS = -version-info 2:1:0 +librpmio_la_LIBADD = ../misc/libmisc.la @WITH_NSS_LIB@ @WITH_BZ2_LIB@ \ + @WITH_ZLIB_LIB@ @WITH_LIBELF_LIB@ @WITH_POPT_LIB@ \ + @WITH_LZMA_LIB@ -lpthread $(am__append_3) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign rpmio/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign rpmio/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-usrlibLTLIBRARIES: $(usrlib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(usrlibdir)" || $(MKDIR_P) "$(DESTDIR)$(usrlibdir)" + @list='$(usrlib_LTLIBRARIES)'; test -n "$(usrlibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(usrlibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(usrlibdir)"; \ + } + +uninstall-usrlibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(usrlib_LTLIBRARIES)'; test -n "$(usrlibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(usrlibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(usrlibdir)/$$f"; \ + done + +clean-usrlibLTLIBRARIES: + -test -z "$(usrlib_LTLIBRARIES)" || rm -f $(usrlib_LTLIBRARIES) + @list='$(usrlib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +librpmio.la: $(librpmio_la_OBJECTS) $(librpmio_la_DEPENDENCIES) + $(librpmio_la_LINK) -rpath $(usrlibdir) $(librpmio_la_OBJECTS) $(librpmio_la_LIBADD) $(LIBS) + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/digest.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macro.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmfileutil.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmhook.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmkeyring.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmlog.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmlua.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmmalloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmpgp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmsq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmstring.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmsw.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(usrlibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + clean-usrlibLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-usrlibLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-usrlibLTLIBRARIES + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool \ + clean-usrlibLTLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + install-usrlibLTLIBRARIES installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-usrlibLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/rpmio/argv.c b/rpmio/argv.c new file mode 100644 index 0000000..cf6e299 --- /dev/null +++ b/rpmio/argv.c @@ -0,0 +1,225 @@ +/** \ingroup rpmio + * \file rpmio/argv.c + */ + +#include "system.h" + +#include <stdlib.h> +#include <rpm/argv.h> +#include <rpm/rpmstring.h> + +#include "debug.h" + +void argvPrint(const char * msg, ARGV_const_t argv, FILE * fp) +{ + ARGV_const_t av; + + if (fp == NULL) fp = stderr; + + if (msg) + fprintf(fp, "===================================== %s\n", msg); + + if (argv) + for (av = argv; *av; av++) + fprintf(fp, "%s\n", *av); + +} + +ARGV_t argvNew(void) +{ + ARGV_t argv = xcalloc(1, sizeof(*argv)); + return argv; +} + +ARGI_t argiFree(ARGI_t argi) +{ + if (argi) { + argi->nvals = 0; + argi->vals = _free(argi->vals); + } + argi = _free(argi); + return NULL; +} + +ARGV_t argvFree(ARGV_t argv) +{ + ARGV_t av; + + if (argv) + for (av = argv; *av; av++) + *av = _free(*av); + argv = _free(argv); + return NULL; +} + +int argiCount(ARGI_const_t argi) +{ + int nvals = 0; + if (argi) + nvals = argi->nvals; + return nvals; +} + +ARGint_t argiData(ARGI_const_t argi) +{ + ARGint_t vals = NULL; + if (argi && argi->nvals > 0) + vals = argi->vals; + return vals; +} + +int argvCount(ARGV_const_t argv) +{ + int argc = 0; + if (argv) + while (argv[argc] != NULL) + argc++; + return argc; +} + +ARGV_t argvData(ARGV_t argv) +{ + return argv; +} + +int argvCmp(const void * a, const void * b) +{ + const char *astr = *(ARGV_t)a; + const char *bstr = *(ARGV_t)b; + return strcmp(astr, bstr); +} + +int argvSort(ARGV_t argv, int (*compar)(const void *, const void *)) +{ + if (compar == NULL) + compar = argvCmp; + qsort(argv, argvCount(argv), sizeof(*argv), compar); + return 0; +} + +ARGV_t argvSearch(ARGV_const_t argv, const char *val, + int (*compar)(const void *, const void *)) +{ + if (argv == NULL) + return NULL; + if (compar == NULL) + compar = argvCmp; + return bsearch(&val, argv, argvCount(argv), sizeof(*argv), compar); +} + +int argiAdd(ARGI_t * argip, int ix, int val) +{ + ARGI_t argi; + + if (argip == NULL) + return -1; + if (*argip == NULL) + *argip = xcalloc(1, sizeof(**argip)); + argi = *argip; + if (ix < 0) + ix = argi->nvals; + if (ix >= argi->nvals) { + argi->vals = xrealloc(argi->vals, (ix + 1) * sizeof(*argi->vals)); + memset(argi->vals + argi->nvals, 0, + (ix - argi->nvals) * sizeof(*argi->vals)); + argi->nvals = ix + 1; + } + argi->vals[ix] = val; + return 0; +} + +int argvAdd(ARGV_t * argvp, const char *val) +{ + ARGV_t argv; + int argc; + + if (argvp == NULL) + return -1; + argc = argvCount(*argvp); + *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp)); + argv = *argvp; + argv[argc++] = xstrdup(val); + argv[argc ] = NULL; + return 0; +} + +int argvAddNum(ARGV_t *argvp, int val) +{ + char *valstr = NULL; + int rc; + rasprintf(&valstr, "%d", val); + rc = argvAdd(argvp, valstr); + free(valstr); + return rc; +} + +int argvAppend(ARGV_t * argvp, ARGV_const_t av) +{ + ARGV_t argv = *argvp; + int argc = argvCount(argv); + int ac = argvCount(av); + int i; + + argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv)); + for (i = 0; i < ac; i++) + argv[argc + i] = xstrdup(av[i]); + argv[argc + ac] = NULL; + *argvp = argv; + return 0; +} + +ARGV_t argvSplitString(const char * str, const char * seps, argvFlags flags) +{ + char *dest = NULL; + ARGV_t argv; + int argc = 1; + const char * s; + char * t; + int c; + + if (str == NULL || seps == NULL) + return NULL; + + dest = xmalloc(strlen(str) + 1); + for (argc = 1, s = str, t = dest; (c = *s); s++, t++) { + if (strchr(seps, c)) { + argc++; + c = '\0'; + } + *t = c; + } + *t = '\0'; + + argv = xmalloc( (argc + 1) * sizeof(*argv)); + + for (c = 0, s = dest; s < t; s+= strlen(s) + 1) { + if (*s == '\0' && (flags & ARGV_SKIPEMPTY)) + continue; + argv[c] = xstrdup(s); + c++; + } + argv[c] = NULL; + free(dest); + return argv; +} + +/* Backwards compatibility */ +int argvSplit(ARGV_t * argvp, const char * str, const char * seps) +{ + if (argvp) { + *argvp = argvSplitString(str, seps, ARGV_SKIPEMPTY); + } + return 0; +} + +char *argvJoin(ARGV_const_t argv, const char *sep) +{ + char *dest = NULL; + char * const *arg; + + for (arg = argv; arg && *arg; arg++) { + rstrscat(&dest, *arg, *(arg+1) ? sep : "", NULL); + } + return dest; +} + diff --git a/rpmio/argv.h b/rpmio/argv.h new file mode 100644 index 0000000..d73a3c3 --- /dev/null +++ b/rpmio/argv.h @@ -0,0 +1,179 @@ +#ifndef _H_ARGV_ +#define _H_ARGV_ + +/** \ingroup rpmargv + * \file rpmio/argv.h + */ + +#include <stdio.h> +#include <rpm/rpmtypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char ** ARGV_t; +typedef char * const *ARGV_const_t; + +typedef int * ARGint_t; +struct ARGI_s { + unsigned nvals; + ARGint_t vals; +}; +typedef struct ARGI_s * ARGI_t; +typedef struct ARGI_s const * const ARGI_const_t; + +/** \ingroup rpmargv + * Print argv array elements. + * @param msg output message prefix (or NULL) + * @param argv argv array + * @param fp output file handle (NULL uses stderr) + */ +void argvPrint(const char * msg, ARGV_const_t argv, FILE * fp); + +/** \ingroup rpmargv + * Destroy an argi array. + * @param argi argi array + * @return NULL always + */ +ARGI_t argiFree(ARGI_t argi); + + +/** \ingroup rpmargv + * Create an empty argv array. + * @return pointer to empty argv + */ +ARGV_t argvNew(void); + +/** \ingroup rpmargv + * Destroy an argv array. + * @param argv argv array + * @return NULL always + */ +ARGV_t argvFree(ARGV_t argv); + +/** \ingroup rpmargv + * Return no. of elements in argi array. + * @param argi argi array + * @return no. of elements + */ +int argiCount(ARGI_const_t argi); + +/** \ingroup rpmargv + * Return data from argi array. + * @param argi argi array + * @return argi array data address + */ +ARGint_t argiData(ARGI_const_t argi); + +/** \ingroup rpmargv + * Return no. of elements in argv array. + * @param argv argv array + * @return no. of elements + */ +int argvCount(ARGV_const_t argv); + +/** \ingroup rpmargv + * Return data from argv array. + * @param argv argv array + * @return argv array data address + */ +ARGV_t argvData(ARGV_t argv); + +/** \ingroup rpmargv + * Compare argv arrays (qsort/bsearch). + * @param a 1st instance address + * @param b 2nd instance address + * @return result of comparison + */ +int argvCmp(const void * a, const void * b); + +/** \ingroup rpmargv + * Sort an argv array. + * @param argv argv array + * @param compar strcmp-like comparison function, or NULL for argvCmp() + * @return 0 always + */ +int argvSort(ARGV_t argv, int (*compar)(const void *, const void *)); + +/** \ingroup rpmargv + * Find an element in an argv array. + * @param argv argv array + * @param val string to find + * @param compar strcmp-like comparison function, or NULL for argvCmp() + * @return found string (NULL on failure) + */ +ARGV_t argvSearch(ARGV_const_t argv, const char *val, + int (*compar)(const void *, const void *)); + +/** \ingroup rpmargv + * Add an int to an argi array. + * @retval *argip argi array + * @param ix argi array index (or -1 to append) + * @param val int arg to add + * @return 0 always + */ +int argiAdd(ARGI_t * argip, int ix, int val); + +/** \ingroup rpmargv + * Add a string to an argv array. + * @retval *argvp argv array + * @param val string arg to append + * @return 0 always + */ +int argvAdd(ARGV_t * argvp, const char *val); + +/** \ingroup rpmargv + * Add a number to an argv array (converting to a string). + * @retval *argvp argv array + * @param val numeric arg to append + * @return 0 always + */ +int argvAddNum(ARGV_t * argvp, int val); + +/** \ingroup rpmargv + * Append one argv array to another. + * @retval *argvp argv array + * @param av argv array to append + * @return 0 always + */ +int argvAppend(ARGV_t * argvp, ARGV_const_t av); + +enum argvFlags_e { + ARGV_NONE = 0, + ARGV_SKIPEMPTY = (1 << 0), /* omit empty strings from result */ +}; + +typedef rpmFlags argvFlags; + +/** \ingroup rpmargv + * Split a string into an argv array. + * @param str string arg to split + * @param seps seperator characters + * @param flags flags to control behavior + * @return argv array + */ +ARGV_t argvSplitString(const char * str, const char * seps, argvFlags flags); + +/** \ingroup rpmargv + * Split a string into an argv array. + * @retval *argvp argv array + * @param str string arg to split + * @param seps seperator characters + * @return 0 always + */ +int argvSplit(ARGV_t * argvp, const char * str, const char * seps); + +/** \ingroup rpmargv + * Join an argv array into a string. + * @param *argv argv array to join + * @param sep seperator string to use + * @return malloc'ed string + */ +char *argvJoin(ARGV_const_t argv, const char *sep); + +#ifdef __cplusplus +} +#endif + +#endif /* _H_ARGV_ */ diff --git a/rpmio/base64.c b/rpmio/base64.c new file mode 100644 index 0000000..6b006e1 --- /dev/null +++ b/rpmio/base64.c @@ -0,0 +1,255 @@ +/* base64 encoder/decoder based on public domain implementation + * by Chris Venter */ + +#include <arpa/inet.h> +#include <stdlib.h> + +#include "rpmio/base64.h" + + +static char base64_encode_value(char value_in) +{ + static const char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +static char *base64_encode_block(const char *plaintext_in, int length_in, char *codechar) +{ + const char *plainchar = plaintext_in; + const char *const plaintextend = plaintext_in + length_in; + char result; + char fragment; + + while (1) { + if (plainchar == plaintextend) { + return codechar; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + if (plainchar == plaintextend) + { + *codechar++ = base64_encode_value(result); + *codechar++ = '='; + *codechar++ = '='; + return codechar; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + if (plainchar == plaintextend) + { + *codechar++ = base64_encode_value(result); + *codechar++ = '='; + return codechar; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + } + /* control should not reach here */ + return codechar; +} + +#define BASE64_DEFAULT_LINE_LENGTH 64 + +char *b64encode(const void *data, size_t len, int linelen) +{ + size_t encodedlen; + const char *dataptr = data; + char *output; + char *outptr; + + if (data == NULL) + return NULL; + + if (linelen < 0) + linelen = BASE64_DEFAULT_LINE_LENGTH; + + linelen /= 4; + encodedlen = ((len + 2) / 3) * 4; + if (linelen > 0) { + encodedlen += encodedlen/(linelen * 4) + 1; + } + ++encodedlen; /* for zero termination */ + + output = malloc(encodedlen); + if (output == NULL) + return NULL; + + outptr = output; + while (len > 0) { + if (linelen > 0 && len > linelen * 3) { + outptr = base64_encode_block(dataptr, linelen * 3, outptr); + len -= linelen * 3; + dataptr += linelen * 3; + } else { + outptr = base64_encode_block(dataptr, len, outptr); + len = 0; + } + if (linelen > 0) { + *outptr++ = '\n'; + } + } + *outptr = '\0'; + return output; +} + +static int base64_decode_value(unsigned char value_in) +{ + static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + value_in -= 43; + if (value_in > sizeof(decoding)/sizeof(int)) + return -1; + return decoding[value_in]; +} + +static size_t base64_decode_block(const char *code_in, const size_t length_in, char *plaintext_out) +{ + const char *codechar = code_in; + char *plainchar = plaintext_out; + int fragment; + + *plainchar = 0; + + while (1) + { + do { + if (codechar == code_in+length_in) + { + return plainchar - plaintext_out; + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (char)((fragment & 0x03f) << 2); + + do { + if (codechar == code_in+length_in) + { + return plainchar - plaintext_out; + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (char)((fragment & 0x030) >> 4); + *plainchar = (char)((fragment & 0x00f) << 4); + + do { + if (codechar == code_in+length_in) + { + return plainchar - plaintext_out; + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (char)((fragment & 0x03c) >> 2); + *plainchar = (char)((fragment & 0x003) << 6); + + do { + if (codechar == code_in+length_in) + { + return plainchar - plaintext_out; + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (char)(fragment & 0x03f); + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + +int b64decode(const char *in, void **out, size_t *outlen) +{ + size_t outcnt = 0; + const char *inptr = in; + + *out = NULL; + + if (in == NULL) { + return 1; + } + + while (*inptr != '\0') { + /* assume all ASCII control chars as whitespace */ + if (*inptr > 32) { + if (base64_decode_value(*inptr) != -1) { + ++outcnt; + } else { + return 3; + } + } + ++inptr; + } + + if (outcnt % 4 != 0) + return 2; + + outcnt = (outcnt / 4) * 3; + + *out = malloc(outcnt + 1); /* base64_decode_block can write one extra character */ + + if (*out == NULL) + return 4; + + *outlen = base64_decode_block(in, inptr - in, *out); + + return 0; +} + +#define CRC24_INIT 0xb704ce +#define CRC24_POLY 0x1864cfb + +char *b64crc(const unsigned char *data, size_t len) +{ + uint32_t crc = CRC24_INIT; + int i; + + while (len--) { + crc ^= (*data++) << 16; + for (i = 0; i < 8; i++) { + crc <<= 1; + if (crc & 0x1000000) + crc ^= CRC24_POLY; + } + } + crc = htonl(crc & 0xffffff); + data = (unsigned char *)&crc; + ++data; + return b64encode(data, 3, 0); +} + +#ifdef BASE64_TEST +#include <stdio.h> +#include <string.h> + +int main(int argc, char *argv[]) +{ + static char tst[]="wtrt8122čLýáj\x20s ~ýhž\t4\x02šjjmBvž^%$RTš#á.íěj\x1hčýčŤc+"; + char *encoded; + void *decoded; + size_t size; + int err; + printf("Original: %lu\n%s\n", sizeof(tst)-1, tst); + encoded = b64encode(tst, sizeof(tst)-1, 64); + printf("Encoded: %lu\n%s\n", strlen(encoded), encoded); + if ((err = b64decode(encoded, &decoded, &size)) != 0) { + fprintf(stderr, "Error in decode: %d\n", err); + return 1; + } + printf("Decoded:\n%.*s\n", (int)size, (char *)decoded); + if (size != sizeof(tst)-1) { + fprintf(stderr, "Size differs orig: %lu new: %lu\n", sizeof(tst)-1, size); + return 1; + } + if (memcmp(tst, decoded, size) != 0) { + fprintf(stderr, "Decoded data differs.\n"); + return 1; + } + fprintf(stderr, "OK\n"); + return 0; +} +#endif + diff --git a/rpmio/base64.h b/rpmio/base64.h new file mode 100644 index 0000000..d572856 --- /dev/null +++ b/rpmio/base64.h @@ -0,0 +1,37 @@ +/* base64 encoder/decoder based on public domain implementation + * by Chris Venter */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* returns malloced base64 encoded string + * lines are split with \n characters to be nearest lower multiple of linelen + * if linelen/4 == 0 lines are not split + * if linelen < 0 default line length (64) is used + * the returned string is empty when len == 0 + * returns NULL on failures + */ +char *b64encode(const void *data, size_t len, int linelen); + +/* decodes from zero terminated base64 encoded string to a newly malloced buffer + * ignores whitespace characters in the input string + * return values: + * 0 - OK + * 1 - input is NULL + * 2 - invalid length + * 3 - invalid characters on input + * 4 - malloc failed + */ +int b64decode(const char *in, void **out, size_t *outlen); + +/* counts CRC24 and base64 encodes it in a malloced string + * returns NULL on failures + */ +char *b64crc(const unsigned char *data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/rpmio/digest.c b/rpmio/digest.c new file mode 100644 index 0000000..66f27b5 --- /dev/null +++ b/rpmio/digest.c @@ -0,0 +1,246 @@ +/** \ingroup signature + * \file rpmio/digest.c + */ + +#include "system.h" + +#include "rpmio/digest.h" + +#include "debug.h" + +#ifdef SHA_DEBUG +#define DPRINTF(_a) fprintf _a +#else +#define DPRINTF(_a) +#endif + + +/** + * MD5/SHA1 digest private data. + */ +struct DIGEST_CTX_s { + rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */ + HASHContext *hashctx; /*!< Internal NSS hash context. */ + int algo; /*!< Used hash algorithm */ +}; + +#define DIGESTS_MAX 11 +struct rpmDigestBundle_s { + int index_min; /*!< Smallest index of active digest */ + int index_max; /*!< Largest index of active digest */ + off_t nbytes; /*!< Length of total input data */ + DIGEST_CTX digests[DIGESTS_MAX]; /*!< Digest contexts indexed by algo */ +}; + +rpmDigestBundle rpmDigestBundleNew(void) +{ + rpmDigestBundle bundle = xcalloc(1, sizeof(*bundle)); + return bundle; +} + +rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle) +{ + if (bundle) { + for (int i = bundle->index_min; i <= bundle->index_max ; i++) { + if (bundle->digests[i] == NULL) + continue; + rpmDigestFinal(bundle->digests[i], NULL, NULL, 0); + bundle->digests[i] = NULL; + } + memset(bundle, 0, sizeof(*bundle)); + free(bundle); + } + return NULL; +} + +int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo, + rpmDigestFlags flags) +{ + DIGEST_CTX ctx = NULL; + if (bundle && algo > 0 && algo < DIGESTS_MAX) { + if (bundle->digests[algo] == NULL) { + ctx = rpmDigestInit(algo, flags); + if (ctx) { + bundle->digests[algo] = ctx; + if (algo < bundle->index_min) { + bundle->index_min = algo; + } + if (algo > bundle->index_max) { + bundle->index_max = algo; + } + } + } + } + return (ctx != NULL); +} + +int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len) +{ + int rc = 0; + if (bundle && data && len > 0) { + for (int i = bundle->index_min; i <= bundle->index_max; i++) { + DIGEST_CTX ctx = bundle->digests[i]; + if (ctx == NULL) + continue; + rc += rpmDigestUpdate(ctx, data, len); + } + bundle->nbytes += len; + } + return rc; +} + +int rpmDigestBundleFinal(rpmDigestBundle bundle, + int algo, void ** datap, size_t * lenp, int asAscii) +{ + int rc = 0; + if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) { + rc = rpmDigestFinal(bundle->digests[algo], datap, lenp, asAscii); + bundle->digests[algo] = NULL; + } + return rc; +} + +DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo) +{ + DIGEST_CTX dup = NULL; + if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) { + dup = rpmDigestDup(bundle->digests[algo]); + } + return dup; +} + +DIGEST_CTX +rpmDigestDup(DIGEST_CTX octx) +{ + DIGEST_CTX nctx = NULL; + if (octx) { + HASHContext *hctx = HASH_Clone(octx->hashctx); + if (hctx) { + nctx = memcpy(xcalloc(1, sizeof(*nctx)), octx, sizeof(*nctx)); + nctx->hashctx = hctx; + } + } + return nctx; +} + +RPM_GNUC_PURE +static HASH_HashType getHashType(int hashalgo) +{ + switch (hashalgo) { + case PGPHASHALGO_MD5: + return HASH_AlgMD5; + break; + case PGPHASHALGO_MD2: + return HASH_AlgMD2; + break; + case PGPHASHALGO_SHA1: + return HASH_AlgSHA1; + break; + case PGPHASHALGO_SHA256: + return HASH_AlgSHA256; + break; + case PGPHASHALGO_SHA384: + return HASH_AlgSHA384; + break; + case PGPHASHALGO_SHA512: + return HASH_AlgSHA512; + break; + case PGPHASHALGO_RIPEMD160: + case PGPHASHALGO_TIGER192: + case PGPHASHALGO_HAVAL_5_160: + default: + return HASH_AlgNULL; + break; + } +} + +size_t +rpmDigestLength(int hashalgo) +{ + return HASH_ResultLen(getHashType(hashalgo)); +} + +DIGEST_CTX +rpmDigestInit(int hashalgo, rpmDigestFlags flags) +{ + HASH_HashType type = getHashType(hashalgo); + HASHContext *hashctx = NULL; + DIGEST_CTX ctx = NULL; + + if (type == HASH_AlgNULL || rpmInitCrypto() < 0) + goto exit; + + if ((hashctx = HASH_Create(type)) != NULL) { + ctx = xcalloc(1, sizeof(*ctx)); + ctx->flags = flags; + ctx->algo = hashalgo; + ctx->hashctx = hashctx; + HASH_Begin(ctx->hashctx); + } + +DPRINTF((stderr, "*** Init(%x) ctx %p hashctx %p\n", flags, ctx, ctx->hashctx)); +exit: + return ctx; +} + +int +rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len) +{ + size_t partlen; + const unsigned char *ptr = data; + + if (ctx == NULL) + return -1; + +DPRINTF((stderr, "*** Update(%p,%p,%zd) hashctx %p \"%s\"\n", ctx, data, len, ctx->hashctx, ((char *)data))); + partlen = ~(unsigned int)0xFF; + while (len > 0) { + if (len < partlen) { + partlen = len; + } + HASH_Update(ctx->hashctx, ptr, partlen); + ptr += partlen; + len -= partlen; + } + return 0; +} + +int +rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii) +{ + unsigned char * digest; + unsigned int digestlen; + + if (ctx == NULL) + return -1; + digestlen = HASH_ResultLenContext(ctx->hashctx); + digest = xmalloc(digestlen); + +DPRINTF((stderr, "*** Final(%p,%p,%p,%zd) hashctx %p digest %p\n", ctx, datap, lenp, asAscii, ctx->hashctx, digest)); +/* FIX: check rc */ + HASH_End(ctx->hashctx, digest, (unsigned int *) &digestlen, digestlen); + + /* Return final digest. */ + if (!asAscii) { + if (lenp) *lenp = digestlen; + if (datap) { + *datap = digest; + digest = NULL; + } + } else { + if (lenp) *lenp = (2*digestlen) + 1; + if (datap) { + const uint8_t * s = (const uint8_t *) digest; + *datap = pgpHexStr(s, digestlen); + } + } + if (digest) { + memset(digest, 0, digestlen); /* In case it's sensitive */ + free(digest); + } + HASH_Destroy(ctx->hashctx); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ + free(ctx); + return 0; +} + diff --git a/rpmio/digest.h b/rpmio/digest.h new file mode 100644 index 0000000..0f44619 --- /dev/null +++ b/rpmio/digest.h @@ -0,0 +1,49 @@ +#ifndef _RPMDIGEST_H +#define _RPMDIGEST_H + +#include <nss.h> +#include <sechash.h> +#include <keyhi.h> +#include <cryptohi.h> + +#include <rpm/rpmpgp.h> +#include "rpmio/base64.h" + + +/** \ingroup rpmio + * Values parsed from OpenPGP signature/pubkey packet(s). + */ +struct pgpDigParams_s { + char * userid; + uint8_t * hash; + char * params[4]; + uint8_t tag; + + uint8_t version; /*!< version number. */ + pgpTime_t time; /*!< time that the key was created. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ + + uint8_t hash_algo; + uint8_t sigtype; + uint8_t hashlen; + uint8_t signhash16[2]; + pgpKeyID_t signid; + uint8_t saved; +#define PGPDIG_SAVED_TIME (1 << 0) +#define PGPDIG_SAVED_ID (1 << 1) + +}; + +/** \ingroup rpmio + * Container for values parsed from an OpenPGP signature and public key. + */ +struct pgpDig_s { + struct pgpDigParams_s signature; + struct pgpDigParams_s pubkey; + + /* DSA/RSA parameters */ + SECKEYPublicKey *keydata; + SECItem *sigdata; +}; + +#endif /* _RPMDIGEST_H */ diff --git a/rpmio/macro.c b/rpmio/macro.c new file mode 100644 index 0000000..70ed88e --- /dev/null +++ b/rpmio/macro.c @@ -0,0 +1,1611 @@ +/** \ingroup rpmrc rpmio + * \file rpmio/macro.c + */ + +#include "system.h" +#include <stdarg.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif + +#if !defined(isblank) +#define isblank(_c) ((_c) == ' ' || (_c) == '\t') +#endif +#define iseol(_c) ((_c) == '\n' || (_c) == '\r') + +#define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn))) + +#define MACROBUFSIZ (BUFSIZ * 2) + +#include <rpm/rpmio.h> +#include <rpm/rpmstring.h> +#include <rpm/rpmfileutil.h> +#include <rpm/rpmurl.h> +#include <rpm/rpmlog.h> +#include <rpm/rpmmacro.h> +#include <rpm/argv.h> + +#ifdef WITH_LUA +#include "rpmio/rpmlua.h" +#endif + +#include "debug.h" + +/*! The structure used to store a macro. */ +struct rpmMacroEntry_s { + struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */ + char *name; /*!< Macro name. */ + char *opts; /*!< Macro parameters (a la getopt) */ + char *body; /*!< Macro body. */ + int used; /*!< No. of expansions. */ + int level; /*!< Scoping level. */ +}; + +/*! The structure used to store the set of macros in a context. */ +struct rpmMacroContext_s { + rpmMacroEntry *macroTable; /*!< Macro entry table for context. */ + int macrosAllocated;/*!< No. of allocated macros. */ + int firstFree; /*!< No. of macros. */ +}; + + +static struct rpmMacroContext_s rpmGlobalMacroContext_s; +rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s; + +static struct rpmMacroContext_s rpmCLIMacroContext_s; +rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s; + +/** + * Macro expansion state. + */ +typedef struct MacroBuf_s { + char * buf; /*!< Expansion buffer. */ + size_t tpos; /*!< Current position in expansion buffer */ + size_t nb; /*!< No. bytes remaining in expansion buffer. */ + int depth; /*!< Current expansion depth. */ + int macro_trace; /*!< Pre-print macro to expand? */ + int expand_trace; /*!< Post-print macro expansion? */ + rpmMacroContext mc; +} * MacroBuf; + +#define _MAX_MACRO_DEPTH 16 +static int max_macro_depth = _MAX_MACRO_DEPTH; + +#define _PRINT_MACRO_TRACE 0 +static int print_macro_trace = _PRINT_MACRO_TRACE; + +#define _PRINT_EXPAND_TRACE 0 +static int print_expand_trace = _PRINT_EXPAND_TRACE; + +#define MACRO_CHUNK_SIZE 16 + +/* forward ref */ +static int expandMacro(MacroBuf mb, const char *src, size_t slen); + +/* =============================================================== */ + +/** + * Compare macro entries by name (qsort/bsearch). + * @param ap 1st macro entry + * @param bp 2nd macro entry + * @return result of comparison + */ +static int +compareMacroName(const void * ap, const void * bp) +{ + rpmMacroEntry ame = *((const rpmMacroEntry *)ap); + rpmMacroEntry bme = *((const rpmMacroEntry *)bp); + + if (ame == NULL && bme == NULL) + return 0; + if (ame == NULL) + return 1; + if (bme == NULL) + return -1; + return strcmp(ame->name, bme->name); +} + +/** + * Enlarge macro table. + * @param mc macro context + */ +static void +expandMacroTable(rpmMacroContext mc) +{ + if (mc->macroTable == NULL) { + mc->macrosAllocated = MACRO_CHUNK_SIZE; + mc->macroTable = (rpmMacroEntry *) + xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated); + mc->firstFree = 0; + } else { + mc->macrosAllocated += MACRO_CHUNK_SIZE; + mc->macroTable = (rpmMacroEntry *) + xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) * + mc->macrosAllocated); + } + memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable))); +} + +/** + * Sort entries in macro table. + * @param mc macro context + */ +static void +sortMacroTable(rpmMacroContext mc) +{ + int i; + + if (mc == NULL || mc->macroTable == NULL) + return; + + qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)), + compareMacroName); + + /* Empty pointers are now at end of table. Reset first free index. */ + for (i = 0; i < mc->firstFree; i++) { + if (mc->macroTable[i] != NULL) + continue; + mc->firstFree = i; + break; + } +} + +void +rpmDumpMacroTable(rpmMacroContext mc, FILE * fp) +{ + int nempty = 0; + int nactive = 0; + + if (mc == NULL) mc = rpmGlobalMacroContext; + if (fp == NULL) fp = stderr; + + fprintf(fp, "========================\n"); + if (mc->macroTable != NULL) { + int i; + for (i = 0; i < mc->firstFree; i++) { + rpmMacroEntry me; + if ((me = mc->macroTable[i]) == NULL) { + /* XXX this should never happen */ + nempty++; + continue; + } + fprintf(fp, "%3d%c %s", me->level, + (me->used > 0 ? '=' : ':'), me->name); + if (me->opts && *me->opts) + fprintf(fp, "(%s)", me->opts); + if (me->body && *me->body) + fprintf(fp, "\t%s", me->body); + fprintf(fp, "\n"); + nactive++; + } + } + fprintf(fp, _("======================== active %d empty %d\n"), + nactive, nempty); +} + +/** + * Find entry in macro table. + * @param mc macro context + * @param name macro name + * @param namelen no. of bytes + * @return address of slot in macro table with name (or NULL) + */ +static rpmMacroEntry * +findEntry(rpmMacroContext mc, const char * name, size_t namelen) +{ + rpmMacroEntry key, *ret; + struct rpmMacroEntry_s keybuf; + char namebuf[namelen+1]; + const char *mname = name; + + if (mc == NULL) mc = rpmGlobalMacroContext; + if (mc->macroTable == NULL || mc->firstFree == 0) + return NULL; + + if (namelen > 0) { + strncpy(namebuf, name, namelen); + namebuf[namelen] = '\0'; + mname = namebuf; + } + + key = &keybuf; + memset(key, 0, sizeof(*key)); + key->name = (char *)mname; + ret = (rpmMacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree, + sizeof(*(mc->macroTable)), compareMacroName); + /* XXX TODO: find 1st empty slot and return that */ + return ret; +} + +/* =============================================================== */ + +/** + * fgets(3) analogue that reads \ continuations. Last newline always trimmed. + * @param buf input buffer + * @param size inbut buffer size (bytes) + * @param fd file handle + * @return buffer, or NULL on end-of-file + */ +static char * +rdcl(char * buf, size_t size, FILE *f) +{ + char *q = buf - 1; /* initialize just before buffer. */ + size_t nb = 0; + size_t nread = 0; + int pc = 0, bc = 0; + char *p = buf; + + if (f != NULL) + do { + *(++q) = '\0'; /* terminate and move forward. */ + if (fgets(q, size, f) == NULL) /* read next line. */ + break; + nb = strlen(q); + nread += nb; /* trim trailing \r and \n */ + for (q += nb - 1; nb > 0 && iseol(*q); q--) + nb--; + for (; p <= q; p++) { + switch (*p) { + case '\\': + switch (*(p+1)) { + case '\0': break; + default: p++; break; + } + break; + case '%': + switch (*(p+1)) { + case '{': p++, bc++; break; + case '(': p++, pc++; break; + case '%': p++; break; + } + break; + case '{': if (bc > 0) bc++; break; + case '}': if (bc > 0) bc--; break; + case '(': if (pc > 0) pc++; break; + case ')': if (pc > 0) pc--; break; + } + } + if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') { + *(++q) = '\0'; /* trim trailing \r, \n */ + break; + } + q++; p++; nb++; /* copy newline too */ + size -= nb; + if (*q == '\r') /* XXX avoid \r madness */ + *q = '\n'; + } while (size > 0); + return (nread > 0 ? buf : NULL); +} + +/** + * Return text between pl and matching pr characters. + * @param p start of text + * @param pl left char, i.e. '[', '(', '{', etc. + * @param pr right char, i.e. ']', ')', '}', etc. + * @return address of last char before pr (or NULL) + */ +static const char * +matchchar(const char * p, char pl, char pr) +{ + int lvl = 0; + char c; + + while ((c = *p++) != '\0') { + if (c == '\\') { /* Ignore escaped chars */ + p++; + continue; + } + if (c == pr) { + if (--lvl <= 0) return --p; + } else if (c == pl) + lvl++; + } + return (const char *)NULL; +} + +/** + * Pre-print macro expression to be expanded. + * @param mb macro expansion state + * @param s current expansion string + * @param se end of string + */ +static void +printMacro(MacroBuf mb, const char * s, const char * se) +{ + const char *senl; + const char *ellipsis; + int choplen; + + if (s >= se) { /* XXX just in case */ + fprintf(stderr, _("%3d>%*s(empty)"), mb->depth, + (2 * mb->depth + 1), ""); + return; + } + + if (s[-1] == '{') + s--; + + /* Print only to first end-of-line (or end-of-string). */ + for (senl = se; *senl && !iseol(*senl); senl++) + {}; + + /* Limit trailing non-trace output */ + choplen = 61 - (2 * mb->depth); + if ((senl - s) > choplen) { + senl = s + choplen; + ellipsis = "..."; + } else + ellipsis = ""; + + /* Substitute caret at end-of-macro position */ + fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth, + (2 * mb->depth + 1), "", (int)(se - s), s); + if (se[1] != '\0' && (senl - (se+1)) > 0) + fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis); + fprintf(stderr, "\n"); +} + +/** + * Post-print expanded macro expression. + * @param mb macro expansion state + * @param t current expansion string result + * @param te end of string + */ +static void +printExpansion(MacroBuf mb, const char * t, const char * te) +{ + const char *ellipsis; + int choplen; + + if (!(te > t)) { + rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), ""); + return; + } + + /* Shorten output which contains newlines */ + while (te > t && iseol(te[-1])) + te--; + ellipsis = ""; + if (mb->depth > 0) { + const char *tenl; + + /* Skip to last line of expansion */ + while ((tenl = strchr(t, '\n')) && tenl < te) + t = ++tenl; + + /* Limit expand output */ + choplen = 61 - (2 * mb->depth); + if ((te - t) > choplen) { + te = t + choplen; + ellipsis = "..."; + } + } + + rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), ""); + if (te > t) + rpmlog(RPMLOG_DEBUG, "%.*s%s", (int)(te - t), t, ellipsis); + rpmlog(RPMLOG_DEBUG, "\n"); +} + +#define SKIPBLANK(_s, _c) \ + while (((_c) = *(_s)) && isblank(_c)) \ + (_s)++; \ + +#define SKIPNONBLANK(_s, _c) \ + while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \ + (_s)++; \ + +#define COPYNAME(_ne, _s, _c) \ + { SKIPBLANK(_s,_c); \ + while(((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \ + *(_ne)++ = *(_s)++; \ + *(_ne) = '\0'; \ + } + +#define COPYOPTS(_oe, _s, _c) \ + { \ + while(((_c) = *(_s)) && (_c) != ')') \ + *(_oe)++ = *(_s)++; \ + *(_oe) = '\0'; \ + } + +/** + * Macro-expand string src, return result in dynamically allocated buffer. + * @param mb macro expansion state + * @param src string to expand + * @param slen input string length (or 0 for strlen()) + * @retval target pointer to expanded string (malloced) + * @return result of expansion + */ +static int +expandThis(MacroBuf mb, const char * src, size_t slen, char **target) +{ + struct MacroBuf_s umb; + int rc; + + /* Copy other state from "parent", but we want a buffer of our own */ + umb = *mb; + umb.buf = NULL; + rc = expandMacro(&umb, src, slen); + *target = umb.buf; + + return rc; +} + +static void mbAppend(MacroBuf mb, char c) +{ + if (mb->nb < 1) { + mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ); + mb->nb += MACROBUFSIZ; + } + mb->buf[mb->tpos++] = c; + mb->nb--; +} + +static void mbAppendStr(MacroBuf mb, const char *str) +{ + size_t len = strlen(str); + if (len > mb->nb) { + mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len); + mb->nb += MACROBUFSIZ + len; + } + memcpy(mb->buf+mb->tpos, str, len); + mb->tpos += len; + mb->nb -= len; +} +/** + * Expand output of shell command into target buffer. + * @param mb macro expansion state + * @param cmd shell command + * @param clen no. bytes in shell command + * @return result of expansion + */ +static int +doShellEscape(MacroBuf mb, const char * cmd, size_t clen) +{ + char *buf = NULL; + FILE *shf; + int rc = 0; + int c; + + rc = expandThis(mb, cmd, clen, &buf); + if (rc) + goto exit; + + if ((shf = popen(buf, "r")) == NULL) { + rc = 1; + goto exit; + } + while((c = fgetc(shf)) != EOF) { + mbAppend(mb, c); + } + (void) pclose(shf); + + /* XXX delete trailing \r \n */ + while (iseol(mb->buf[mb->tpos-1])) { + mb->buf[mb->tpos--] = '\0'; + mb->nb++; + } + +exit: + _free(buf); + return rc; +} + +/** + * Parse (and execute) new macro definition. + * @param mb macro expansion state + * @param se macro definition to parse + * @param level macro recursion level + * @param expandbody should body be expanded? + * @return address to continue parsing + */ +static const char * +doDefine(MacroBuf mb, const char * se, int level, int expandbody) +{ + const char *s = se; + size_t blen = MACROBUFSIZ; + char *buf = xmalloc(blen); + char *n = buf, *ne = n; + char *o = NULL, *oe; + char *b, *be, *ebody = NULL; + int c; + int oc = ')'; + + /* Copy name */ + COPYNAME(ne, s, c); + + /* Copy opts (if present) */ + oe = ne + 1; + if (*s == '(') { + s++; /* skip ( */ + o = oe; + COPYOPTS(oe, s, oc); + s++; /* skip ) */ + } + + /* Copy body, skipping over escaped newlines */ + b = be = oe + 1; + SKIPBLANK(s, c); + if (c == '{') { /* XXX permit silent {...} grouping */ + if ((se = matchchar(s, c, '}')) == NULL) { + rpmlog(RPMLOG_ERR, + _("Macro %%%s has unterminated body\n"), n); + se = s; /* XXX W2DO? */ + goto exit; + } + s++; /* XXX skip { */ + strncpy(b, s, (se - s)); + b[se - s] = '\0'; + be += strlen(b); + se++; /* XXX skip } */ + s = se; /* move scan forward */ + } else { /* otherwise free-field */ + int bc = 0, pc = 0; + while (*s && (bc || pc || !iseol(*s))) { + switch (*s) { + case '\\': + switch (*(s+1)) { + case '\0': break; + default: s++; break; + } + break; + case '%': + switch (*(s+1)) { + case '{': *be++ = *s++; bc++; break; + case '(': *be++ = *s++; pc++; break; + case '%': *be++ = *s++; break; + } + break; + case '{': if (bc > 0) bc++; break; + case '}': if (bc > 0) bc--; break; + case '(': if (pc > 0) pc++; break; + case ')': if (pc > 0) pc--; break; + } + *be++ = *s++; + } + *be = '\0'; + + if (bc || pc) { + rpmlog(RPMLOG_ERR, + _("Macro %%%s has unterminated body\n"), n); + se = s; /* XXX W2DO? */ + goto exit; + } + + /* Trim trailing blanks/newlines */ + while (--be >= b && (c = *be) && (isblank(c) || iseol(c))) + {}; + *(++be) = '\0'; /* one too far */ + } + + /* Move scan over body */ + while (iseol(*s)) + s++; + se = s; + + /* Names must start with alphabetic or _ and be at least 3 chars */ + if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) { + rpmlog(RPMLOG_ERR, + _("Macro %%%s has illegal name (%%define)\n"), n); + goto exit; + } + + /* Options must be terminated with ')' */ + if (o && oc != ')') { + rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n); + goto exit; + } + + if ((be - b) < 1) { + rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n); + goto exit; + } + + if (expandbody) { + if (expandThis(mb, b, 0, &ebody)) { + rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n); + goto exit; + } + b = ebody; + } + + addMacro(mb->mc, n, o, b, (level - 1)); + +exit: + _free(buf); + _free(ebody); + return se; +} + +/** + * Parse (and execute) macro undefinition. + * @param mc macro context + * @param se macro name to undefine + * @return address to continue parsing + */ +static const char * +doUndefine(rpmMacroContext mc, const char * se) +{ + const char *s = se; + char *buf = xmalloc(MACROBUFSIZ); + char *n = buf, *ne = n; + int c; + + COPYNAME(ne, s, c); + + /* Move scan over body */ + while (iseol(*s)) + s++; + se = s; + + /* Names must start with alphabetic or _ and be at least 3 chars */ + if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) { + rpmlog(RPMLOG_ERR, + _("Macro %%%s has illegal name (%%undefine)\n"), n); + goto exit; + } + + delMacro(mc, n); + +exit: + _free(buf); + return se; +} + +/** + * Push new macro definition onto macro entry stack. + * @param mep address of macro entry slot + * @param n macro name + * @param o macro parameters (NULL if none) + * @param b macro body (NULL becomes "") + * @param level macro recursion level + */ +static void +pushMacro(rpmMacroEntry * mep, + const char * n, const char * o, + const char * b, int level) +{ + rpmMacroEntry prev = (mep && *mep ? *mep : NULL); + rpmMacroEntry me = (rpmMacroEntry) xmalloc(sizeof(*me)); + + me->prev = prev; + me->name = (prev ? prev->name : xstrdup(n)); + me->opts = (o ? xstrdup(o) : NULL); + me->body = xstrdup(b ? b : ""); + me->used = 0; + me->level = level; + if (mep) + *mep = me; + else + me = _free(me); +} + +/** + * Pop macro definition from macro entry stack. + * @param mep address of macro entry slot + */ +static void +popMacro(rpmMacroEntry * mep) +{ + rpmMacroEntry me = (*mep ? *mep : NULL); + + if (me) { + /* XXX cast to workaround const */ + if ((*mep = me->prev) == NULL) + me->name = _free(me->name); + me->opts = _free(me->opts); + me->body = _free(me->body); + me = _free(me); + } +} + +/** + * Free parsed arguments for parameterized macro. + * @param mb macro expansion state + */ +static void +freeArgs(MacroBuf mb) +{ + rpmMacroContext mc = mb->mc; + int ndeleted = 0; + int i; + + if (mc == NULL || mc->macroTable == NULL) + return; + + /* Delete dynamic macro definitions */ + for (i = 0; i < mc->firstFree; i++) { + rpmMacroEntry *mep, me; + int skiptest = 0; + mep = &mc->macroTable[i]; + me = *mep; + + if (me == NULL) /* XXX this should never happen */ + continue; + if (me->level < mb->depth) + continue; + if (strlen(me->name) == 1 && strchr("#*0", *me->name)) { + if (*me->name == '*' && me->used > 0) + skiptest = 1; /* XXX skip test for %# %* %0 */ + } else if (!skiptest && me->used <= 0) { +#if NOTYET + rpmlog(RPMLOG_ERR, + _("Macro %%%s (%s) was not used below level %d\n"), + me->name, me->body, me->level); +#endif + } + popMacro(mep); + if (!(mep && *mep)) + ndeleted++; + } + + /* If any deleted macros, sort macro table */ + if (ndeleted) + sortMacroTable(mc); +} + +/** + * Parse arguments (to next new line) for parameterized macro. + * @todo Use popt rather than getopt to parse args. + * @param mb macro expansion state + * @param me macro entry slot + * @param se arguments to parse + * @param lastc stop parsing at lastc + * @return address to continue parsing + */ +static const char * +grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, + const char * lastc) +{ + const char *opts; + char *args = NULL; + ARGV_t argv = NULL; + int argc = 0; + int c; + + /* Copy macro name as argv[0] */ + argvAdd(&argv, me->name); + addMacro(mb->mc, "0", NULL, me->name, mb->depth); + + /* + * Make a copy of se up to lastc string that we can pass to argvSplit(). + * Append the results to main argv. + */ + { ARGV_t av = NULL; + char *s = xcalloc((lastc-se)+1, sizeof(*s)); + memcpy(s, se, (lastc-se)); + + argvSplit(&av, s, " \t"); + argvAppend(&argv, av); + + argvFree(av); + free(s); + } + + /* + * The macro %* analoguous to the shell's $* means "Pass all non-macro + * parameters." Consequently, there needs to be a macro that means "Pass all + * (including macro parameters) options". This is useful for verifying + * parameters during expansion and yet transparently passing all parameters + * through for higher level processing (e.g. %description and/or %setup). + * This is the (potential) justification for %{**} ... + */ + args = argvJoin(argv + 1, " "); + addMacro(mb->mc, "**", NULL, args, mb->depth); + free(args); + + /* + * POSIX states optind must be 1 before any call but glibc uses 0 + * to (re)initialize getopt structures, eww. + */ +#ifdef __GLIBC__ + optind = 0; +#else + optind = 1; +#endif + + opts = me->opts; + argc = argvCount(argv); + + /* Define option macros. */ + while((c = getopt(argc, argv, opts)) != -1) + { + char *name = NULL, *body = NULL; + if (c == '?' || strchr(opts, c) == NULL) { + rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"), + (char)c, me->name, opts); + goto exit; + } + + rasprintf(&name, "-%c", c); + if (optarg) { + rasprintf(&body, "-%c %s", c, optarg); + } else { + rasprintf(&body, "-%c", c); + } + addMacro(mb->mc, name, NULL, body, mb->depth); + free(name); + free(body); + + if (optarg) { + rasprintf(&name, "-%c*", c); + addMacro(mb->mc, name, NULL, optarg, mb->depth); + free(name); + } + } + + /* Add argument count (remaining non-option items) as macro. */ + { char *ac = NULL; + rasprintf(&ac, "%d", (argc - optind)); + addMacro(mb->mc, "#", NULL, ac, mb->depth); + free(ac); + } + + /* Add macro for each argument */ + if (argc - optind) { + for (c = optind; c < argc; c++) { + char *name = NULL; + rasprintf(&name, "%d", (c - optind + 1)); + addMacro(mb->mc, name, NULL, argv[c], mb->depth); + free(name); + } + } + + /* Add concatenated unexpanded arguments as yet another macro. */ + args = argvJoin(argv + optind, " "); + addMacro(mb->mc, "*", NULL, args ? args : "", mb->depth); + free(args); + +exit: + argvFree(argv); + return *lastc ? lastc + 1 : lastc; +} + +/** + * Perform macro message output + * @param mb macro expansion state + * @param waserror use rpmlog()? + * @param msg message to ouput + * @param msglen no. of bytes in message + */ +static void +doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen) +{ + char *buf = NULL; + + (void) expandThis(mb, msg, msglen, &buf); + if (waserror) + rpmlog(RPMLOG_ERR, "%s\n", buf); + else + fprintf(stderr, "%s", buf); + _free(buf); +} + +/** + * Execute macro primitives. + * @param mb macro expansion state + * @param negate should logic be inverted? + * @param f beginning of field f + * @param fn length of field f + * @param g beginning of field g + * @param gn length of field g + */ +static void +doFoo(MacroBuf mb, int negate, const char * f, size_t fn, + const char * g, size_t gn) +{ + char *buf = NULL; + char *b = NULL, *be; + int c; + + if (g != NULL) { + (void) expandThis(mb, g, gn, &buf); + } else { + buf = xmalloc(MACROBUFSIZ + fn + gn); + buf[0] = '\0'; + } + if (STREQ("basename", f, fn)) { + if ((b = strrchr(buf, '/')) == NULL) + b = buf; + else + b++; +#if NOTYET + /* XXX watchout for conflict with %dir */ + } else if (STREQ("dirname", f, fn)) { + if ((b = strrchr(buf, '/')) != NULL) + *b = '\0'; + b = buf; +#endif + } else if (STREQ("suffix", f, fn)) { + if ((b = strrchr(buf, '.')) != NULL) + b++; + } else if (STREQ("expand", f, fn)) { + b = buf; + } else if (STREQ("verbose", f, fn)) { + if (negate) + b = (rpmIsVerbose() ? NULL : buf); + else + b = (rpmIsVerbose() ? buf : NULL); + } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) { + (void)urlPath(buf, (const char **)&b); + if (*b == '\0') b = "/"; + } else if (STREQ("uncompress", f, fn)) { + rpmCompressedMagic compressed = COMPRESSED_OTHER; + for (b = buf; (c = *b) && isblank(c);) + b++; + for (be = b; (c = *be) && !isblank(c);) + be++; + *be++ = '\0'; + (void) rpmFileIsCompressed(b, &compressed); + switch(compressed) { + default: + case COMPRESSED_NOT: + sprintf(be, "%%__cat %s", b); + break; + case COMPRESSED_OTHER: + sprintf(be, "%%__gzip -dc %s", b); + break; + case COMPRESSED_BZIP2: + sprintf(be, "%%__bzip2 -dc %s", b); + break; + case COMPRESSED_ZIP: + sprintf(be, "%%__unzip %s", b); + break; + case COMPRESSED_LZMA: + case COMPRESSED_XZ: + sprintf(be, "%%__xz -dc %s", b); + break; + case COMPRESSED_LZIP: + sprintf(be, "%%__lzip -dc %s", b); + break; + case COMPRESSED_LRZIP: + sprintf(be, "%%__lrzip -dqo- %s", b); + break; + } + b = be; + } else if (STREQ("getenv", f, fn)) { + b = getenv(buf); + } else if (STREQ("getconfdir", f, fn)) { + sprintf(buf, "%s", rpmConfigDir()); + b = buf; + } else if (STREQ("S", f, fn)) { + for (b = buf; (c = *b) && risdigit(c);) + b++; + if (!c) { /* digit index */ + b++; + sprintf(b, "%%SOURCE%s", buf); + } else + b = buf; + } else if (STREQ("P", f, fn)) { + for (b = buf; (c = *b) && risdigit(c);) + b++; + if (!c) { /* digit index */ + b++; + sprintf(b, "%%PATCH%s", buf); + } else + b = buf; + } else if (STREQ("F", f, fn)) { + b = buf + strlen(buf) + 1; + sprintf(b, "file%s.file", buf); + } + + if (b) { + (void) expandMacro(mb, b, 0); + } + free(buf); +} + +/** + * The main macro recursion loop. + * @param mb macro expansion state + * @param src string to expand + * @return 0 on success, 1 on failure + */ +static int +expandMacro(MacroBuf mb, const char *src, size_t slen) +{ + rpmMacroEntry *mep; + rpmMacroEntry me; + const char *s = src, *se; + const char *f, *fe; + const char *g, *ge; + size_t fn, gn, tpos; + int c; + int rc = 0; + int negate; + const char * lastc; + int chkexist; + char *source = NULL; + + /* Handle non-terminated substrings by creating a terminated copy */ + if (!slen) + slen = strlen(src); + source = xmalloc(slen + 1); + strncpy(source, src, slen); + source[slen] = '\0'; + s = source; + + if (mb->buf == NULL) { + size_t blen = MACROBUFSIZ + strlen(s); + mb->buf = xcalloc(blen + 1, sizeof(*mb->buf)); + mb->tpos = 0; + mb->nb = blen; + } + tpos = mb->tpos; /* save expansion pointer for printExpand */ + + if (++mb->depth > max_macro_depth) { + rpmlog(RPMLOG_ERR, + _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n")); + mb->depth--; + mb->expand_trace = 1; + _free(source); + return 1; + } + + while (rc == 0 && (c = *s) != '\0') { + s++; + /* Copy text until next macro */ + switch(c) { + case '%': + if (*s) { /* Ensure not end-of-string. */ + if (*s != '%') + break; + s++; /* skip first % in %% */ + } + default: + mbAppend(mb, c); + continue; + break; + } + + /* Expand next macro */ + f = fe = NULL; + g = ge = NULL; + if (mb->depth > 1) /* XXX full expansion for outermost level */ + tpos = mb->tpos; /* save expansion pointer for printExpand */ + negate = 0; + lastc = NULL; + chkexist = 0; + switch ((c = *s)) { + default: /* %name substitution */ + while (strchr("!?", *s) != NULL) { + switch(*s++) { + case '!': + negate = ((negate + 1) % 2); + break; + case '?': + chkexist++; + break; + } + } + f = se = s; + if (*se == '-') + se++; + while((c = *se) && (risalnum(c) || c == '_')) + se++; + /* Recognize non-alnum macros too */ + switch (*se) { + case '*': + se++; + if (*se == '*') se++; + break; + case '#': + se++; + break; + default: + break; + } + fe = se; + /* For "%name " macros ... */ + if ((c = *fe) && isblank(c)) + if ((lastc = strchr(fe,'\n')) == NULL) + lastc = strchr(fe, '\0'); + break; + case '(': /* %(...) shell escape */ + if ((se = matchchar(s, c, ')')) == NULL) { + rpmlog(RPMLOG_ERR, + _("Unterminated %c: %s\n"), (char)c, s); + rc = 1; + continue; + } + if (mb->macro_trace) + printMacro(mb, s, se+1); + + s++; /* skip ( */ + rc = doShellEscape(mb, s, (se - s)); + se++; /* skip ) */ + + s = se; + continue; + break; + case '{': /* %{...}/%{...:...} substitution */ + if ((se = matchchar(s, c, '}')) == NULL) { + rpmlog(RPMLOG_ERR, + _("Unterminated %c: %s\n"), (char)c, s); + rc = 1; + continue; + } + f = s+1;/* skip { */ + se++; /* skip } */ + while (strchr("!?", *f) != NULL) { + switch(*f++) { + case '!': + negate = ((negate + 1) % 2); + break; + case '?': + chkexist++; + break; + } + } + for (fe = f; (c = *fe) && !strchr(" :}", c);) + fe++; + switch (c) { + case ':': + g = fe + 1; + ge = se - 1; + break; + case ' ': + lastc = se-1; + break; + default: + break; + } + break; + } + + /* XXX Everything below expects fe > f */ + fn = (fe - f); + gn = (ge - g); + if ((fe - f) <= 0) { +/* XXX Process % in unknown context */ + c = '%'; /* XXX only need to save % */ + mbAppend(mb, c); +#if 0 + rpmlog(RPMLOG_ERR, + _("A %% is followed by an unparseable macro\n")); +#endif + s = se; + continue; + } + + if (mb->macro_trace) + printMacro(mb, s, se); + + /* Expand builtin macros */ + if (STREQ("global", f, fn)) { + s = doDefine(mb, se, RMIL_GLOBAL, 1); + continue; + } + if (STREQ("define", f, fn)) { + s = doDefine(mb, se, mb->depth, 0); + continue; + } + if (STREQ("undefine", f, fn)) { + s = doUndefine(mb->mc, se); + continue; + } + + if (STREQ("echo", f, fn) || + STREQ("warn", f, fn) || + STREQ("error", f, fn)) { + int waserror = 0; + if (STREQ("error", f, fn)) + waserror = 1; + if (g != NULL && g < ge) + doOutput(mb, waserror, g, gn); + else + doOutput(mb, waserror, f, fn); + s = se; + continue; + } + + if (STREQ("trace", f, fn)) { + /* XXX TODO restore expand_trace/macro_trace to 0 on return */ + mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); + if (mb->depth == 1) { + print_macro_trace = mb->macro_trace; + print_expand_trace = mb->expand_trace; + } + s = se; + continue; + } + + if (STREQ("dump", f, fn)) { + rpmDumpMacroTable(mb->mc, NULL); + while (iseol(*se)) + se++; + s = se; + continue; + } + +#ifdef WITH_LUA + if (STREQ("lua", f, fn)) { + rpmlua lua = NULL; /* Global state. */ + const char *ls = s+sizeof("{lua:")-1; + const char *lse = se-sizeof("}")+1; + char *scriptbuf = (char *)xmalloc((lse-ls)+1); + char *printbuf; + memcpy(scriptbuf, ls, lse-ls); + scriptbuf[lse-ls] = '\0'; + rpmluaPushPrintBuffer(lua); + if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) + rc = 1; + printbuf = rpmluaPopPrintBuffer(lua); + if (printbuf) { + mbAppendStr(mb, printbuf); + free(printbuf); + } + free(scriptbuf); + s = se; + continue; + } +#endif + + /* XXX necessary but clunky */ + if (STREQ("basename", f, fn) || + STREQ("suffix", f, fn) || + STREQ("expand", f, fn) || + STREQ("verbose", f, fn) || + STREQ("uncompress", f, fn) || + STREQ("url2path", f, fn) || + STREQ("u2p", f, fn) || + STREQ("getenv", f, fn) || + STREQ("getconfdir", f, fn) || + STREQ("S", f, fn) || + STREQ("P", f, fn) || + STREQ("F", f, fn)) { + /* FIX: verbose may be set */ + doFoo(mb, negate, f, fn, g, gn); + s = se; + continue; + } + + /* Expand defined macros */ + mep = findEntry(mb->mc, f, fn); + me = (mep ? *mep : NULL); + + /* XXX Special processing for flags */ + if (*f == '-') { + if (me) + me->used++; /* Mark macro as used */ + if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ + (me != NULL && negate)) { /* With -f, skip %{!-f...} */ + s = se; + continue; + } + + if (g && g < ge) { /* Expand X in %{-f:X} */ + rc = expandMacro(mb, g, gn); + } else + if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */ + rc = expandMacro(mb, me->body, 0); + } + s = se; + continue; + } + + /* XXX Special processing for macro existence */ + if (chkexist) { + if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ + (me != NULL && negate)) { /* With -f, skip %{!?f...} */ + s = se; + continue; + } + if (g && g < ge) { /* Expand X in %{?f:X} */ + rc = expandMacro(mb, g, gn); + } else + if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ + rc = expandMacro(mb, me->body, 0); + } + s = se; + continue; + } + + if (me == NULL) { /* leave unknown %... as is */ + /* XXX hack to permit non-overloaded %foo to be passed */ + c = '%'; /* XXX only need to save % */ + mbAppend(mb, c); + continue; + } + + /* Setup args for "%name " macros with opts */ + if (me && me->opts != NULL) { + if (lastc != NULL) { + se = grabArgs(mb, me, fe, lastc); + } else { + addMacro(mb->mc, "**", NULL, "", mb->depth); + addMacro(mb->mc, "*", NULL, "", mb->depth); + addMacro(mb->mc, "#", NULL, "0", mb->depth); + addMacro(mb->mc, "0", NULL, me->name, mb->depth); + } + } + + /* Recursively expand body of macro */ + if (me->body && *me->body) { + rc = expandMacro(mb, me->body, 0); + if (rc == 0) + me->used++; /* Mark macro as used */ + } + + /* Free args for "%name " macros with opts */ + if (me->opts != NULL) + freeArgs(mb); + + s = se; + } + + mb->buf[mb->tpos] = '\0'; + mb->depth--; + if (rc != 0 || mb->expand_trace) + printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos); + _free(source); + return rc; +} + + +/* =============================================================== */ + +static int doExpandMacros(rpmMacroContext mc, const char *src, char **target) +{ + MacroBuf mb = xcalloc(1, sizeof(*mb)); + int rc = 0; + + if (mc == NULL) mc = rpmGlobalMacroContext; + + mb->buf = NULL; + mb->depth = 0; + mb->macro_trace = print_macro_trace; + mb->expand_trace = print_expand_trace; + mb->mc = mc; + + rc = expandMacro(mb, src, 0); + + mb->buf[mb->tpos] = '\0'; /* XXX just in case */ + /* expanded output is usually much less than alloced buffer, downsize */ + *target = xrealloc(mb->buf, mb->tpos + 1); + + _free(mb); + return rc; +} + +int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen) +{ + char *target = NULL; + int rc = doExpandMacros(mc, sbuf, &target); + rstrlcpy(sbuf, target, slen); + free(target); + return rc; +} + +void +addMacro(rpmMacroContext mc, + const char * n, const char * o, const char * b, int level) +{ + rpmMacroEntry * mep; + + if (mc == NULL) mc = rpmGlobalMacroContext; + + /* If new name, expand macro table */ + if ((mep = findEntry(mc, n, 0)) == NULL) { + if (mc->firstFree == mc->macrosAllocated) + expandMacroTable(mc); + if (mc->macroTable != NULL) + mep = mc->macroTable + mc->firstFree++; + } + + if (mep != NULL) { + /* Push macro over previous definition */ + pushMacro(mep, n, o, b, level); + + /* If new name, sort macro table */ + if ((*mep)->prev == NULL) + sortMacroTable(mc); + } +} + +void +delMacro(rpmMacroContext mc, const char * n) +{ + rpmMacroEntry * mep; + + if (mc == NULL) mc = rpmGlobalMacroContext; + /* If name exists, pop entry */ + if ((mep = findEntry(mc, n, 0)) != NULL) { + popMacro(mep); + /* If deleted name, sort macro table */ + if (!(mep && *mep)) + sortMacroTable(mc); + } +} + +int +rpmDefineMacro(rpmMacroContext mc, const char * macro, int level) +{ + MacroBuf mb = xcalloc(1, sizeof(*mb)); + + /* XXX just enough to get by */ + mb->mc = (mc ? mc : rpmGlobalMacroContext); + (void) doDefine(mb, macro, level, 0); + _free(mb); + return 0; +} + +void +rpmLoadMacros(rpmMacroContext mc, int level) +{ + + if (mc == NULL || mc == rpmGlobalMacroContext) + return; + + if (mc->macroTable != NULL) { + int i; + for (i = 0; i < mc->firstFree; i++) { + rpmMacroEntry *mep, me; + mep = &mc->macroTable[i]; + me = *mep; + + if (me == NULL) /* XXX this should never happen */ + continue; + addMacro(NULL, me->name, me->opts, me->body, (level - 1)); + } + } +} + +int +rpmLoadMacroFile(rpmMacroContext mc, const char * fn) +{ + FILE *fd = fopen(fn, "r"); + size_t blen = MACROBUFSIZ; + char *buf = xmalloc(blen); + int rc = -1; + + if (fd == NULL || ferror(fd)) { + if (fd) (void) fclose(fd); + goto exit; + } + + /* XXX Assume new fangled macro expansion */ + max_macro_depth = 16; + + buf[0] = '\0'; + while(rdcl(buf, blen, fd) != NULL) { + char c, *n; + + n = buf; + SKIPBLANK(n, c); + + if (c != '%') + continue; + n++; /* skip % */ + rc = rpmDefineMacro(mc, n, RMIL_MACROFILES); + } + rc = fclose(fd); + +exit: + _free(buf); + return rc; +} + +void +rpmInitMacros(rpmMacroContext mc, const char * macrofiles) +{ + ARGV_t pattern, globs = NULL; + + if (macrofiles == NULL) + return; + + argvSplit(&globs, macrofiles, ":"); + for (pattern = globs; *pattern; pattern++) { + ARGV_t path, files = NULL; + + /* Glob expand the macro file path element, expanding ~ to $HOME. */ + if (rpmGlob(*pattern, NULL, &files) != 0) { + continue; + } + + /* Read macros from each file. */ + for (path = files; *path; path++) { + if (rpmFileHasSuffix(*path, ".rpmnew") || + rpmFileHasSuffix(*path, ".rpmsave") || + rpmFileHasSuffix(*path, ".rpmorig")) { + continue; + } + (void) rpmLoadMacroFile(mc, *path); + } + argvFree(files); + } + argvFree(globs); + + /* Reload cmdline macros */ + rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE); +} + +void +rpmFreeMacros(rpmMacroContext mc) +{ + + if (mc == NULL) mc = rpmGlobalMacroContext; + + if (mc->macroTable != NULL) { + int i; + for (i = 0; i < mc->firstFree; i++) { + rpmMacroEntry me; + while ((me = mc->macroTable[i]) != NULL) { + /* XXX cast to workaround const */ + if ((mc->macroTable[i] = me->prev) == NULL) + me->name = _free(me->name); + me->opts = _free(me->opts); + me->body = _free(me->body); + me = _free(me); + } + } + mc->macroTable = _free(mc->macroTable); + } + memset(mc, 0, sizeof(*mc)); +} + +char * +rpmExpand(const char *arg, ...) +{ + size_t blen = 0; + char *buf = NULL, *ret = NULL; + char *pe; + const char *s; + va_list ap; + + if (arg == NULL) { + ret = xstrdup(""); + goto exit; + } + + /* precalculate unexpanded size */ + va_start(ap, arg); + for (s = arg; s != NULL; s = va_arg(ap, const char *)) + blen += strlen(s); + va_end(ap); + + buf = xmalloc(blen + 1); + buf[0] = '\0'; + + va_start(ap, arg); + for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *)) + pe = stpcpy(pe, s); + va_end(ap); + + (void) doExpandMacros(NULL, buf, &ret); + + free(buf); +exit: + return ret; +} + +int +rpmExpandNumeric(const char *arg) +{ + char *val; + int rc; + + if (arg == NULL) + return 0; + + val = rpmExpand(arg, NULL); + if (!(val && *val != '%')) + rc = 0; + else if (*val == 'Y' || *val == 'y') + rc = 1; + else if (*val == 'N' || *val == 'n') + rc = 0; + else { + char *end; + rc = strtol(val, &end, 0); + if (!(end && *end == '\0')) + rc = 0; + } + val = _free(val); + + return rc; +} diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c new file mode 100644 index 0000000..3641341 --- /dev/null +++ b/rpmio/rpmfileutil.c @@ -0,0 +1,777 @@ +#include "system.h" + +#if HAVE_GELF_H + +#include <gelf.h> + +#if !defined(DT_GNU_PRELINKED) +#define DT_GNU_PRELINKED 0x6ffffdf5 +#endif +#if !defined(DT_GNU_LIBLIST) +#define DT_GNU_LIBLIST 0x6ffffef9 +#endif + +#endif + +#if defined(HAVE_MMAP) +#include <sys/mman.h> +#endif + +#include <sys/wait.h> +#include <errno.h> +#include <popt.h> +#include <ctype.h> + +#include <rpm/rpmfileutil.h> +#include <rpm/rpmurl.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmlog.h> +#include <rpm/argv.h> + +#include "rpmio/rpmio_internal.h" + +#include "debug.h" + +static const char *rpm_config_dir = NULL; + +static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep) +{ + static const char * cmd = NULL; + static int initted = 0; + int fdno; + + if (!initted) { + cmd = rpmExpand("%{?__prelink_undo_cmd}", NULL); + initted++; + } + + if (pidp) *pidp = 0; + + if (fsizep) { + struct stat sb, * st = &sb; + if (stat(path, st) < 0) + return -1; + *fsizep = st->st_size; + } + + fdno = open(path, O_RDONLY); + if (fdno < 0) + return fdno; + + if (!(cmd && *cmd)) + return fdno; + +#if HAVE_GELF_H && HAVE_LIBELF + { Elf *elf = NULL; + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + GElf_Dyn dyn; + int bingo; + + (void) elf_version(EV_CURRENT); + + if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL + || elf_kind(elf) != ELF_K_ELF + || gelf_getehdr(elf, &ehdr) == NULL + || !(ehdr.e_type == ET_DYN || ehdr.e_type == ET_EXEC)) + goto exit; + + bingo = 0; + while (!bingo && (scn = elf_nextscn(elf, scn)) != NULL) { + (void) gelf_getshdr(scn, &shdr); + if (shdr.sh_type != SHT_DYNAMIC) + continue; + while (!bingo && (data = elf_getdata (scn, data)) != NULL) { + int maxndx = data->d_size / shdr.sh_entsize; + int ndx; + + for (ndx = 0; ndx < maxndx; ++ndx) { + (void) gelf_getdyn (data, ndx, &dyn); + if (!(dyn.d_tag == DT_GNU_PRELINKED || dyn.d_tag == DT_GNU_LIBLIST)) + continue; + bingo = 1; + break; + } + } + } + + if (pidp != NULL && bingo) { + int pipes[2]; + pid_t pid; + int xx; + + xx = close(fdno); + pipes[0] = pipes[1] = -1; + xx = pipe(pipes); + if (!(pid = fork())) { + ARGV_t av, lib; + argvSplit(&av, cmd, " "); + + xx = close(pipes[0]); + xx = dup2(pipes[1], STDOUT_FILENO); + xx = close(pipes[1]); + if ((lib = argvSearch(av, "library", NULL)) != NULL) { + *lib = (char *) path; + unsetenv("MALLOC_CHECK_"); + xx = execve(av[0], av+1, environ); + } + _exit(127); + } + *pidp = pid; + fdno = pipes[0]; + xx = close(pipes[1]); + } + +exit: + if (elf) (void) elf_end(elf); + } +#endif + + return fdno; +} + +int rpmDoDigest(int algo, const char * fn,int asAscii, + unsigned char * digest, rpm_loff_t * fsizep) +{ + const char * path; + urltype ut = urlPath(fn, &path); + unsigned char * dig = NULL; + size_t diglen; + unsigned char buf[32*BUFSIZ]; + FD_t fd; + rpm_loff_t fsize = 0; + pid_t pid = 0; + int rc = 0; + int fdno; + + fdno = open_dso(path, &pid, &fsize); + if (fdno < 0) { + rc = 1; + goto exit; + } + + /* file to large (32 MB), do not mmap file */ + if (fsize > (size_t) 32*1024*1024) + if (ut == URL_IS_PATH || ut == URL_IS_UNKNOWN) + ut = URL_IS_DASH; /* force fd io */ + + switch(ut) { + case URL_IS_PATH: + case URL_IS_UNKNOWN: +#ifdef HAVE_MMAP + if (pid == 0) { + int xx; + DIGEST_CTX ctx; + void * mapped; + + if (fsize) { + mapped = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdno, 0); + if (mapped == MAP_FAILED) { + xx = close(fdno); + rc = 1; + break; + } + +#ifdef MADV_SEQUENTIAL + xx = madvise(mapped, fsize, MADV_SEQUENTIAL); +#endif + } + + ctx = rpmDigestInit(algo, RPMDIGEST_NONE); + if (fsize) + xx = rpmDigestUpdate(ctx, mapped, fsize); + xx = rpmDigestFinal(ctx, (void **)&dig, &diglen, asAscii); + if (fsize) + xx = munmap(mapped, fsize); + xx = close(fdno); + break; + } +#endif + case URL_IS_HTTPS: + case URL_IS_HTTP: + case URL_IS_FTP: + case URL_IS_HKP: + case URL_IS_DASH: + default: + /* Either use the pipe to prelink -y or open the URL. */ + fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio"); + (void) close(fdno); + if (fd == NULL || Ferror(fd)) { + rc = 1; + if (fd != NULL) + (void) Fclose(fd); + break; + } + + fdInitDigest(fd, algo, 0); + fsize = 0; + while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) + fsize += rc; + fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii); + if (Ferror(fd)) + rc = 1; + + (void) Fclose(fd); + break; + } + + /* Reap the prelink -y helper. */ + if (pid) { + int status; + (void) waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) + rc = 1; + } + +exit: + if (fsizep) + *fsizep = fsize; + if (!rc) + memcpy(digest, dig, diglen); + dig = _free(dig); + + return rc; +} + +FD_t rpmMkTemp(char *templ) +{ + int sfd; + FD_t tfd = NULL; + + sfd = mkstemp(templ); + if (sfd < 0) { + goto exit; + } + + tfd = fdDup(sfd); + close(sfd); + +exit: + return tfd; +} + +FD_t rpmMkTempFile(const char * prefix, char **fn) +{ + const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */ + char *tempfn; + static int _initialized = 0; + FD_t tfd = NULL; + + if (!prefix) prefix = ""; + + /* Create the temp directory if it doesn't already exist. */ + if (!_initialized) { + _initialized = 1; + tempfn = rpmGenPath(prefix, tpmacro, NULL); + if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1)) + goto exit; + free(tempfn); + } + + tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL); + tfd = rpmMkTemp(tempfn); + + if (tfd == NULL || Ferror(tfd)) { + rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn); + goto exit; + } + +exit: + if (tfd != NULL && fn) + *fn = tempfn; + else + free(tempfn); + + return tfd; +} + +int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid) +{ + char *d, *de; + int rc; + + if (path == NULL || *path == '\0') + return -1; + d = rstrcat(NULL, path); + if (d[strlen(d)-1] != '/') { + rstrcat(&d,"/"); + } + de = d; + for (;(de=strchr(de+1,'/'));) { + struct stat st; + *de = '\0'; + rc = stat(d, &st); + if (rc) { + if (errno != ENOENT) + goto exit; + rc = mkdir(d, mode); + if (rc) + goto exit; + rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode); + if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) { + rc = chown(d, uid, gid); + if (rc) + goto exit; + } + } else if (!S_ISDIR(st.st_mode)) { + rc = ENOTDIR; + goto exit; + } + *de = '/'; + } + rc = 0; +exit: + free(d); + return rc; +} + +int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed) +{ + FD_t fd; + ssize_t nb; + int rc = -1; + unsigned char magic[13]; + + *compressed = COMPRESSED_NOT; + + fd = Fopen(file, "r.ufdio"); + if (fd == NULL || Ferror(fd)) { + /* XXX Fstrerror */ + rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd)); + if (fd) (void) Fclose(fd); + return 1; + } + nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd); + if (nb < 0) { + rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd)); + rc = 1; + } else if (nb < sizeof(magic)) { + rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"), + file, (unsigned)sizeof(magic)); + rc = 0; + } + (void) Fclose(fd); + if (rc >= 0) + return rc; + + rc = 0; + + if ((magic[0] == 'B') && (magic[1] == 'Z')) { + *compressed = COMPRESSED_BZIP2; + } else if ((magic[0] == 'P') && (magic[1] == 'K') && + (((magic[2] == 3) && (magic[3] == 4)) || + ((magic[2] == '0') && (magic[3] == '0')))) { /* pkzip */ + *compressed = COMPRESSED_ZIP; + } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) && + (magic[2] == 0x7a) && (magic[3] == 0x58) && + (magic[4] == 0x5a) && (magic[5] == 0x00)) { + /* new style xz (lzma) with magic */ + *compressed = COMPRESSED_XZ; + } else if ((magic[0] == 'L') && (magic[1] == 'Z') && + (magic[2] == 'I') && (magic[3] == 'P')) { + *compressed = COMPRESSED_LZIP; + } else if ((magic[0] == 'L') && (magic[1] == 'R') && + (magic[2] == 'Z') && (magic[3] == 'I')) { + *compressed = COMPRESSED_LRZIP; + } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */ + ((magic[0] == 0037) && (magic[1] == 0236)) || /* old gzip */ + ((magic[0] == 0037) && (magic[1] == 0036)) || /* pack */ + ((magic[0] == 0037) && (magic[1] == 0240)) || /* SCO lzh */ + ((magic[0] == 0037) && (magic[1] == 0235)) /* compress */ + ) { + *compressed = COMPRESSED_OTHER; + } else if (rpmFileHasSuffix(file, ".lzma")) { + *compressed = COMPRESSED_LZMA; + } + + return rc; +} + +/* @todo "../sbin/./../bin/" not correct. */ +char *rpmCleanPath(char * path) +{ + const char *s; + char *se, *t, *te; + int begin = 1; + + if (path == NULL) + return NULL; + +/*fprintf(stderr, "*** RCP %s ->\n", path); */ + s = t = te = path; + while (*s != '\0') { +/*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */ + switch(*s) { + case ':': /* handle url's */ + if (s[1] == '/' && s[2] == '/') { + *t++ = *s++; + *t++ = *s++; + break; + } + begin=1; + break; + case '/': + /* Move parent dir forward */ + for (se = te + 1; se < t && *se != '/'; se++) + {}; + if (se < t && *se == '/') { + te = se; +/*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */ + } + while (s[1] == '/') + s++; + while (t > path && t[-1] == '/') + t--; + break; + case '.': + /* Leading .. is special */ + /* Check that it is ../, so that we don't interpret */ + /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */ + /* in the case of "...", this ends up being processed*/ + /* as "../.", and the last '.' is stripped. This */ + /* would not be correct processing. */ + if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) { +/*fprintf(stderr, " leading \"..\"\n"); */ + *t++ = *s++; + break; + } + /* Single . is special */ + if (begin && s[1] == '\0') { + break; + } + /* Handle the ./ cases */ + if (t > path && t[-1] == '/') { + /* Trim embedded ./ */ + if (s[1] == '/') { + s+=2; + continue; + } + /* Trim trailing /. */ + if (s[1] == '\0') { + s++; + continue; + } + } + /* Trim embedded /../ and trailing /.. */ + if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) { + t = te; + /* Move parent dir forward */ + if (te > path) + for (--te; te > path && *te != '/'; te--) + {}; +/*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */ + s++; + s++; + continue; + } + break; + default: + begin = 0; + break; + } + *t++ = *s++; + } + + /* Trim trailing / (but leave single / alone) */ + if (t > &path[1] && t[-1] == '/') + t--; + *t = '\0'; + +/*fprintf(stderr, "\t%s\n", path); */ + return path; +} + +/* Merge 3 args into path, any or all of which may be a url. */ + +char * rpmGenPath(const char * urlroot, const char * urlmdir, + const char *urlfile) +{ + char * xroot = rpmGetPath(urlroot, NULL); + const char * root = xroot; + char * xmdir = rpmGetPath(urlmdir, NULL); + const char * mdir = xmdir; + char * xfile = rpmGetPath(urlfile, NULL); + const char * file = xfile; + char * result; + char * url = NULL; + int nurl = 0; + int ut; + + ut = urlPath(xroot, &root); + if (url == NULL && ut > URL_IS_DASH) { + url = xroot; + nurl = root - xroot; + } + if (root == NULL || *root == '\0') root = "/"; + + ut = urlPath(xmdir, &mdir); + if (url == NULL && ut > URL_IS_DASH) { + url = xmdir; + nurl = mdir - xmdir; + } + if (mdir == NULL || *mdir == '\0') mdir = "/"; + + ut = urlPath(xfile, &file); + if (url == NULL && ut > URL_IS_DASH) { + url = xfile; + nurl = file - xfile; + } + + if (url && nurl > 0) { + char *t = rstrcat(NULL, url); + t[nurl] = '\0'; + url = t; + } else + url = xstrdup(""); + + result = rpmGetPath(url, root, "/", mdir, "/", file, NULL); + + xroot = _free(xroot); + xmdir = _free(xmdir); + xfile = _free(xfile); + free(url); + return result; +} + +/* Return concatenated and expanded canonical path. */ + +char * rpmGetPath(const char *path, ...) +{ + va_list ap; + char *dest = NULL, *res; + const char *s; + + if (path == NULL) + return xstrdup(""); + + va_start(ap, path); + for (s = path; s; s = va_arg(ap, const char *)) { + rstrcat(&dest, s); + } + va_end(ap); + + res = rpmExpand(dest, NULL); + free(dest); + + return rpmCleanPath(res); +} + +int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr) +{ + int ac = 0; + const char ** av = NULL; + int argc = 0; + ARGV_t argv = NULL; + char * globRoot = NULL; + const char *home = getenv("HOME"); + int gflags = 0; +#ifdef ENABLE_NLS + char * old_collate = NULL; + char * old_ctype = NULL; + const char * t; +#endif + size_t maxb, nb; + int i, j; + int rc; + + if (home != NULL && strlen(home) > 0) + gflags |= GLOB_TILDE; + + /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */ + rc = poptParseArgvString(patterns, &ac, &av); + if (rc) + return rc; + +#ifdef ENABLE_NLS + t = setlocale(LC_COLLATE, NULL); + if (t) + old_collate = xstrdup(t); + t = setlocale(LC_CTYPE, NULL); + if (t) + old_ctype = xstrdup(t); + (void) setlocale(LC_COLLATE, "C"); + (void) setlocale(LC_CTYPE, "C"); +#endif + + if (av != NULL) + for (j = 0; j < ac; j++) { + char * globURL; + const char * path; + int ut = urlPath(av[j], &path); + int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN); + size_t plen = strlen(path); + int flags = gflags; + int dir_only = (plen > 0 && path[plen-1] == '/'); + glob_t gl; + + if (!local || (!glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL)) { + argvAdd(&argv, av[j]); + continue; + } + +#ifdef GLOB_ONLYDIR + if (dir_only) + flags |= GLOB_ONLYDIR; +#endif + + gl.gl_pathc = 0; + gl.gl_pathv = NULL; + + rc = glob(av[j], flags, NULL, &gl); + if (rc) + goto exit; + + /* XXX Prepend the URL leader for globs that have stripped it off */ + maxb = 0; + for (i = 0; i < gl.gl_pathc; i++) { + if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb) + maxb = nb; + } + + nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0); + maxb += nb; + maxb += 1; + globURL = globRoot = xmalloc(maxb); + + switch (ut) { + case URL_IS_PATH: + case URL_IS_DASH: + strncpy(globRoot, av[j], nb); + break; + case URL_IS_HTTPS: + case URL_IS_HTTP: + case URL_IS_FTP: + case URL_IS_HKP: + case URL_IS_UNKNOWN: + default: + break; + } + globRoot += nb; + *globRoot = '\0'; + + for (i = 0; i < gl.gl_pathc; i++) { + const char * globFile = &(gl.gl_pathv[i][0]); + + if (dir_only) { + struct stat sb; + if (lstat(gl.gl_pathv[i], &sb) || !S_ISDIR(sb.st_mode)) + continue; + } + + if (globRoot > globURL && globRoot[-1] == '/') + while (*globFile == '/') globFile++; + strcpy(globRoot, globFile); + argvAdd(&argv, globURL); + } + globfree(&gl); + globURL = _free(globURL); + } + + argc = argvCount(argv); + if (argc > 0) { + if (argvPtr) + *argvPtr = argv; + if (argcPtr) + *argcPtr = argc; + rc = 0; + } else + rc = 1; + + +exit: +#ifdef ENABLE_NLS + if (old_collate) { + (void) setlocale(LC_COLLATE, old_collate); + old_collate = _free(old_collate); + } + if (old_ctype) { + (void) setlocale(LC_CTYPE, old_ctype); + old_ctype = _free(old_ctype); + } +#endif + av = _free(av); + if (rc || argvPtr == NULL) { + argvFree(argv); + } + return rc; +} + +char * rpmEscapeSpaces(const char * s) +{ + const char * se; + char * t; + char * te; + size_t nb = 0; + + for (se = s; *se; se++) { + if (isspace(*se)) + nb++; + nb++; + } + nb++; + + t = te = xmalloc(nb); + for (se = s; *se; se++) { + if (isspace(*se)) + *te++ = '\\'; + *te++ = *se; + } + *te = '\0'; + return t; +} + +int rpmFileHasSuffix(const char *path, const char *suffix) +{ + size_t plen = strlen(path); + size_t slen = strlen(suffix); + return (plen >= slen && rstreq(path+plen-slen, suffix)); +} + +char * rpmGetCwd(void) +{ + int currDirLen = 0; + char * currDir = NULL; + + do { + currDirLen += 128; + currDir = xrealloc(currDir, currDirLen); + memset(currDir, 0, currDirLen); + } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE); + + return currDir; +} + +int rpmMkdirs(const char *root, const char *pathstr) +{ + ARGV_t dirs = NULL; + int rc = 0; + argvSplit(&dirs, pathstr, ":"); + + for (char **d = dirs; *d; d++) { + char *path = rpmGetPath(root ? root : "", *d, NULL); + if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) { + const char *msg = _("failed to create directory"); + /* try to be more informative if the failing part was a macro */ + if (**d == '%') { + rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path); + } else { + rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path); + } + } + free(path); + if (rc) break; + } + argvFree(dirs); + return rc; +} + +const char *rpmConfigDir(void) +{ + if (rpm_config_dir == NULL) { + char *rpmenv = getenv("RPM_CONFIGDIR"); + rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR; + } + return rpm_config_dir; +} diff --git a/rpmio/rpmfileutil.h b/rpmio/rpmfileutil.h new file mode 100644 index 0000000..28c6d8a --- /dev/null +++ b/rpmio/rpmfileutil.h @@ -0,0 +1,151 @@ +#ifndef _RPMFILEUTIL_H +#define _RPMFILEUTIL_H + +/** \ingroup rpmfileutil rpmio + * \file rpmio/rpmfileutil.h + * File and path manipulation helper functions. + */ + +#include <rpm/rpmutil.h> +#include <rpm/rpmio.h> +#include <rpm/rpmpgp.h> +#include <rpm/argv.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmfileutil + */ +typedef enum rpmCompressedMagic_e { + COMPRESSED_NOT = 0, /*!< not compressed */ + COMPRESSED_OTHER = 1, /*!< gzip can handle */ + COMPRESSED_BZIP2 = 2, /*!< bzip2 can handle */ + COMPRESSED_ZIP = 3, /*!< unzip can handle */ + COMPRESSED_LZMA = 4, /*!< lzma can handle */ + COMPRESSED_XZ = 5, /*!< xz can handle */ + COMPRESSED_LZIP = 6, /*!< lzip can handle */ + COMPRESSED_LRZIP = 7 /*!< lrzip can handle */ +} rpmCompressedMagic; + +/** \ingroup rpmfileutil + * Calculate a file digest and size. + * @param algo digest algorithm + * @param fn file name + * @param asAscii return digest as ascii string? + * @retval digest address of calculated digest + * @retval *fsizep file size pointer (or NULL) + * @return 0 on success, 1 on error + */ +int rpmDoDigest(int algo, const char * fn,int asAscii, + unsigned char * digest, rpm_loff_t * fsizep); + +/** \ingroup rpmfileutil + * Thin wrapper for mkstemp(3). + * @param templ template for temporary filename + * @return file handle or NULL on error + */ +FD_t rpmMkTemp(char *templ); + +/** \ingroup rpmfileutil + * Return file handle for a temporaray file. + * A unique temporaray file path will be created in + * [prefix/]%{_tmppath} directory. + * The file name and the open file handle are returned. + * + * @param prefix leading part of temp file path + * @retval fn temp file name (or NULL) + * @return fdptr open file handle or NULL on error + */ +FD_t rpmMkTempFile(const char * prefix, char **fn); + +/** \ingroup rpmfileutil + * Insure that directories in path exist, creating as needed. + * @param path directory path + * @param mode directory mode (if created) + * @param uid directory uid (if created), or -1 to skip + * @param gid directory uid (if created), or -1 to skip + * @return 0 on success, errno (or -1) on error + */ +int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid); + +/** \ingroup rpmfileutil + * Create several directories (including parents if needed) in one go. + * Macros in pathstr will be expanded in the process. + * @param root leading root directory (or NULL for none) + * @param pathstr list of directories separated with : + * @return 0 if all directories were successfully created + * (or already existed), non-zero otherwise + */ +int rpmMkdirs(const char *root, const char *pathstr); + +/** \ingroup rpmfileutil + * Canonicalize file path. + * @param path path to canonicalize (in-place) + * @return pointer to path + */ +char * rpmCleanPath (char * path); + +/** \ingroup rpmfileutil + * Merge 3 args into path, any or all of which may be a url. + * The leading part of the first URL encountered is used + * for the result, other URL prefixes are discarded, permitting + * a primitive form of URL inheiritance. + * @param urlroot root URL (often path to chroot, or NULL) + * @param urlmdir directory URL (often a directory, or NULL) + * @param urlfile file URL (often a file, or NULL) + * @return expanded, merged, canonicalized path (malloc'ed) + */ +char * rpmGenPath (const char * urlroot, + const char * urlmdir, + const char * urlfile); + +/** \ingroup rpmfileutil + * Return (malloc'ed) expanded, canonicalized, file path. + * @param path macro(s) to expand (NULL terminates list) + * @return canonicalized path (malloc'ed) + */ +char * rpmGetPath (const char * path, ...) RPM_GNUC_NULL_TERMINATED; + +/** \ingroup rpmfileutil + * Return URL path(s) from a (URL prefixed) pattern glob. + * @param patterns glob pattern + * @retval *argcPtr no. of paths + * @retval *argvPtr ARGV_t array of paths + * @return 0 on success + */ +int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr); + +/** \ingroup rpmfileutil + * Escape isspace(3) characters in string. + * @param s string + * @return escaped string + */ +char * rpmEscapeSpaces(const char * s); + +/** \ingroup rpmfileutil + * Return type of compression used in file. + * @param file name of file + * @retval compressed address of compression type + * @return 0 on success, 1 on I/O error + */ +int rpmFileIsCompressed (const char * file, rpmCompressedMagic * compressed); + +/** \ingroup rpmfileutil + * Check if path (string) ends with given suffix + * @param path (path) string + * @param suffix suffix string to check for + * @return 1 if true, 0 otherwise + */ +int rpmFileHasSuffix(const char *path, const char *suffix); + +/** \ingroup rpmfileutil + * Like getcwd() but the result is malloced. + * @return current working directory (malloc'ed) + */ +char * rpmGetCwd(void); + +#ifdef __cplusplus +} +#endif +#endif /* _RPMFILEUTIL_H */ diff --git a/rpmio/rpmhook.c b/rpmio/rpmhook.c new file mode 100644 index 0000000..9fe2a21 --- /dev/null +++ b/rpmio/rpmhook.c @@ -0,0 +1,262 @@ +#include "system.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#include <rpm/rpmstring.h> +#include "rpmio/rpmhook.h" + +#include "debug.h" + +#define RPMHOOK_TABLE_INITSIZE 256 +#define RPMHOOK_BUCKET_INITSIZE 5 + +typedef struct rpmhookItem_s { + rpmhookFunc func; + void *data; + struct rpmhookItem_s *next; +} * rpmhookItem; + +typedef struct rpmhookBucket_s { + unsigned long hash; + char *name; + rpmhookItem item; +} * rpmhookBucket; + +typedef struct rpmhookTable_s { + int size; + int used; + struct rpmhookBucket_s bucket[1]; +} * rpmhookTable; + + +rpmhookArgs rpmhookArgsNew(int argc) +{ + rpmhookArgs args = (rpmhookArgs) xcalloc(1, + sizeof(*args) + sizeof(args->argv) * (argc-1)); + args->argc = argc; + return args; +} + +rpmhookArgs rpmhookArgsFree(rpmhookArgs args) +{ + if (args != NULL) + free(args); + return NULL; +} + +static rpmhookTable rpmhookTableNew(int size) +{ + rpmhookTable table = (rpmhookTable) xcalloc(1, + sizeof(*table) + sizeof(table->bucket) * (size-1)); + table->size = size; + return table; +} + +#if 0 +static rpmhookTable rpmhookTableFree(rpmhookTable table) +{ + rpmhookItem item, nextItem; + int i; + for (i = 0; i != table->size; i++) { + if (table->bucket[i].name == NULL) + continue; + free(table->bucket[i].name); + item = table->bucket[i].item; + while (item) { + nextItem = item->next; + free(item); + item = nextItem; + } + } + free(table); + return NULL; +} +#endif + +static void rpmhookTableRehash(rpmhookTable *table); + +static int rpmhookTableFindBucket(rpmhookTable *table, const char *name) +{ + /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */ + unsigned long perturb; + unsigned long hash = 0; + unsigned char *bp = (unsigned char *)name; + unsigned char *be = bp + strlen(name); + rpmhookBucket bucket; + int ret; + + if (((*table)->used/2)*3 > (*table)->size) + rpmhookTableRehash(table); + while (bp < be) { + hash ^= (unsigned long)*bp++; + hash *= (unsigned long)0x01000193; + } + perturb = hash; + ret = hash % (*table)->size; + bucket = &(*table)->bucket[ret]; + while (bucket->name && + (bucket->hash != hash || !rstreq(bucket->name, name))) { + /* Collision resolution based on Python's perturb scheme. */ + ret = ((ret << 2) + ret + perturb + 1) % (*table)->size; + perturb >>= 5; + bucket = &(*table)->bucket[ret]; + } + if (!bucket->name) + bucket->hash = hash; + return ret; +} + +static void rpmhookTableRehash(rpmhookTable *table) +{ + rpmhookTable newtable = rpmhookTableNew((*table)->size*2); + int n, i = 0; + + for (; i != (*table)->size; i++) { + if ((*table)->bucket[i].name == NULL) + continue; + n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name); + newtable->bucket[n].name = (*table)->bucket[i].name; + newtable->bucket[n].item = (*table)->bucket[i].item; + } + newtable->used = (*table)->used; + free(*table); + *table = newtable; +} + +static void rpmhookTableAddItem(rpmhookTable *table, const char *name, + rpmhookFunc func, void *data) +{ + int n = rpmhookTableFindBucket(table, name); + rpmhookBucket bucket = &(*table)->bucket[n]; + rpmhookItem *item = &bucket->item; + if (!bucket->name) { + bucket->name = xstrdup(name); + (*table)->used++; + } + while (*item) item = &(*item)->next; + *item = xcalloc(1, sizeof(**item)); + (*item)->func = func; + (*item)->data = data; +} + +static void rpmhookTableDelItem(rpmhookTable *table, const char *name, + rpmhookFunc func, void *data, + int matchfunc, int matchdata) +{ + int n = rpmhookTableFindBucket(table, name); + rpmhookBucket bucket = &(*table)->bucket[n]; + rpmhookItem item = bucket->item; + rpmhookItem lastItem = NULL; + rpmhookItem nextItem; + while (item) { + nextItem = item->next; + if ((!matchfunc || item->func == func) && + (!matchdata || item->data == data)) { + free(item); + if (lastItem) + lastItem->next = nextItem; + else + bucket->item = nextItem; + } else { + lastItem = item; + } + item = nextItem; + } + if (!bucket->item) { + free(bucket->name); + bucket->name = NULL; + (*table)->used--; + } +} + +static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap) +{ + rpmhookArgs args = rpmhookArgsNew(strlen(argt)); + int i; + + args->argt = argt; + for (i = 0; i != args->argc; i++) { + switch (argt[i]) { + case 's': + args->argv[i].s = va_arg(ap, char *); + break; + case 'i': + args->argv[i].i = va_arg(ap, int); + break; + case 'f': + args->argv[i].f = (float)va_arg(ap, double); + break; + case 'p': + args->argv[i].p = va_arg(ap, void *); + break; + default: + fprintf(stderr, "error: unsupported type '%c' as " + "a hook argument\n", argt[i]); + break; + } + } + return args; +} + +static void rpmhookTableCallArgs(rpmhookTable *table, const char *name, + rpmhookArgs args) +{ + int n = rpmhookTableFindBucket(table, name); + rpmhookItem item = (*table)->bucket[n].item; + rpmhookItem next; + while (item) { + next = item->next; + if (item->func(args, item->data) != 0) + break; + item = next; + } +} + +static rpmhookTable globalTable = NULL; + +void rpmhookRegister(const char *name, rpmhookFunc func, void *data) +{ + if (globalTable == NULL) + globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE); + rpmhookTableAddItem(&globalTable, name, func, data); +} + +void rpmhookUnregister(const char *name, rpmhookFunc func, void *data) +{ + if (globalTable != NULL) + rpmhookTableDelItem(&globalTable, name, func, data, 1, 1); +} + +void rpmhookUnregisterAny(const char *name, rpmhookFunc func) +{ + if (globalTable != NULL) + rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0); +} + +void rpmhookUnregisterAll(const char *name) +{ + if (globalTable != NULL) + rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0); +} + +void rpmhookCall(const char *name, const char *argt, ...) +{ + if (globalTable != NULL) { + rpmhookArgs args; + va_list ap; + va_start(ap, argt); + args = rpmhookArgsParse(argt, ap); + rpmhookTableCallArgs(&globalTable, name, args); + (void) rpmhookArgsFree(args); + va_end(ap); + } +} + +void rpmhookCallArgs(const char *name, rpmhookArgs args) +{ + if (globalTable != NULL) + rpmhookTableCallArgs(&globalTable, name, args); +} diff --git a/rpmio/rpmhook.h b/rpmio/rpmhook.h new file mode 100644 index 0000000..52f5634 --- /dev/null +++ b/rpmio/rpmhook.h @@ -0,0 +1,37 @@ +#ifndef RPMHOOK_H +#define RPMHOOK_H + +typedef union { + const char * s; + int i; + float f; + void * p; +} rpmhookArgv; + +typedef struct rpmhookArgs_s { + int argc; + const char * argt; + rpmhookArgv argv[1]; +} * rpmhookArgs; + +typedef int (*rpmhookFunc) (rpmhookArgs args, void *data); + +#ifdef __cplusplus +extern "C" { +#endif + +rpmhookArgs rpmhookArgsNew(int argc); +rpmhookArgs rpmhookArgsFree(rpmhookArgs args); + +void rpmhookRegister(const char *name, rpmhookFunc func, void *data); +void rpmhookUnregister(const char *name, rpmhookFunc func, void *data); +void rpmhookUnregisterAny(const char *name, rpmhookFunc func); +void rpmhookUnregisterAll(const char *name); +void rpmhookCall(const char *name, const char *argt, ...); +void rpmhookCallArgs(const char *name, rpmhookArgs args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rpmio/rpmio.c b/rpmio/rpmio.c new file mode 100644 index 0000000..2fbbf91 --- /dev/null +++ b/rpmio/rpmio.c @@ -0,0 +1,1900 @@ +/** \ingroup rpmio + * \file rpmio/rpmio.c + */ + +#include "system.h" +#include <stdarg.h> +#include <errno.h> + +#include <rpm/rpmlog.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmfileutil.h> +#include <rpm/rpmsw.h> +#include <rpm/rpmurl.h> + +#include "rpmio/rpmio_internal.h" + +#include "debug.h" + +#if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION) +#define _USE_LIBIO 1 +#endif + +typedef struct _FDSTACK_s { + FDIO_t io; + void * fp; + int fdno; +} FDSTACK_t; + +/** \ingroup rpmio + * Cumulative statistics for a descriptor. + */ +typedef struct { + struct rpmop_s ops[FDSTAT_MAX]; /*!< Cumulative statistics. */ +} * FDSTAT_t; + +/** \ingroup rpmio + * The FD_t File Handle data structure. + */ +struct _FD_s { + int nrefs; + int flags; +#define RPMIO_DEBUG_IO 0x40000000 +#define RPMIO_DEBUG_REFS 0x20000000 + int magic; +#define FDMAGIC 0x04463138 + int nfps; + FDSTACK_t fps[8]; + int urlType; /* ufdio: */ + + ssize_t bytesRemain; /* ufdio: */ + + int syserrno; /* last system errno encountered */ + const char *errcookie; /* gzdio/bzdio/ufdio/xzdio: */ + + FDSTAT_t stats; /* I/O statistics */ + + rpmDigestBundle digests; +}; + +#define DBG(_f, _m, _x) \ + \ + if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x \ + +#define DBGIO(_f, _x) DBG((_f), RPMIO_DEBUG_IO, _x) + +static FDIO_t fdGetIo(FD_t fd) +{ + return (fd != NULL) ? fd->fps[fd->nfps].io : NULL; +} + +static void fdSetIo(FD_t fd, FDIO_t io) +{ + if (fd) + fd->fps[fd->nfps].io = io; +} + +static void * fdGetFp(FD_t fd) +{ + return (fd != NULL) ? fd->fps[fd->nfps].fp : NULL; +} + +static void fdSetFp(FD_t fd, void * fp) +{ + if (fd) + fd->fps[fd->nfps].fp = fp; +} + +static void fdSetFdno(FD_t fd, int fdno) +{ + if (fd) + fd->fps[fd->nfps].fdno = fdno; +} + +static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno) +{ + if (fd == NULL || fd->nfps >= (sizeof(fd->fps)/sizeof(fd->fps[0]) - 1)) + return; + fd->nfps++; + fdSetIo(fd, io); + fdSetFp(fd, fp); + fdSetFdno(fd, fdno); +} + +static void fdPop(FD_t fd) +{ + if (fd == NULL || fd->nfps < 0) return; + fdSetIo(fd, NULL); + fdSetFp(fd, NULL); + fdSetFdno(fd, -1); + fd->nfps--; +} + +static FD_t c2f(void * cookie) +{ + FD_t fd = (FD_t) cookie; + return (fd && fd->magic == FDMAGIC) ? fd : NULL; +} + +void fdSetBundle(FD_t fd, rpmDigestBundle bundle) +{ + if (fd) + fd->digests = bundle; +} + +rpmDigestBundle fdGetBundle(FD_t fd) +{ + return (fd != NULL) ? fd->digests : NULL; +} + +static void * iotFileno(FD_t fd, FDIO_t iot) +{ + void * rc = NULL; + + if (fd == NULL) + return NULL; + + for (int i = fd->nfps; i >= 0; i--) { + FDSTACK_t * fps = &fd->fps[i]; + if (fps->io != iot) + continue; + rc = fps->fp; + break; + } + + return rc; +} + +#define FDNREFS(fd) (fd ? ((FD_t)fd)->nrefs : -9) + +#define FDONLY(fd) assert(fdGetIo(fd) == fdio) +#define GZDONLY(fd) assert(fdGetIo(fd) == gzdio) +#define BZDONLY(fd) assert(fdGetIo(fd) == bzdio) +#define LZDONLY(fd) assert(fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) + +#define UFDONLY(fd) /* assert(fdGetIo(fd) == ufdio) */ + +/** + */ +#if _USE_LIBIO +static int noLibio = 0; +#else +static int noLibio = 1; +#endif + +/** \ingroup rpmio + * \name RPMIO Vectors. + */ +typedef ssize_t (*fdio_read_function_t) (void *cookie, void *buf, size_t nbytes); +typedef ssize_t (*fdio_write_function_t) (void *cookie, const void *buf, size_t nbytes); +typedef int (*fdio_seek_function_t) (void *cookie, _libio_pos_t pos, int whence); +typedef int (*fdio_close_function_t) (void *cookie); +typedef FD_t (*fdio_ref_function_t) ( void * cookie); +typedef FD_t (*fdio_deref_function_t) (FD_t fd); +typedef FD_t (*fdio_new_function_t) (void); +typedef int (*fdio_fileno_function_t) (void * cookie); +typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode); +typedef FD_t (*fdio_fopen_function_t) (const char * path, const char * fmode); +typedef void * (*fdio_ffileno_function_t) (FD_t fd); +typedef int (*fdio_fflush_function_t) (FD_t fd); + +/** \ingroup rpmio + */ +struct FDIO_s { + fdio_read_function_t read; + fdio_write_function_t write; + fdio_seek_function_t seek; + fdio_close_function_t close; + + fdio_ref_function_t _fdref; + fdio_deref_function_t _fdderef; + fdio_new_function_t _fdnew; + fdio_fileno_function_t _fileno; + + fdio_open_function_t _open; + fdio_fopen_function_t _fopen; + fdio_ffileno_function_t _ffileno; + fdio_fflush_function_t _fflush; +}; + +/* forward refs */ +static const FDIO_t fdio; +static const FDIO_t fpio; +static const FDIO_t ufdio; +static const FDIO_t gzdio; +static const FDIO_t bzdio; +static const FDIO_t xzdio; +static const FDIO_t lzdio; + +/** \ingroup rpmio + * Update digest(s) attached to fd. + */ +static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen); + +/** + */ +int _rpmio_debug = 0; + +/* =============================================================== */ + +static const char * fdbg(FD_t fd) +{ + static char buf[BUFSIZ]; + char *be = buf; + int i; + + buf[0] = '\0'; + if (fd == NULL) + return buf; + + if (fd->bytesRemain != -1) { + sprintf(be, " clen %d", (int)fd->bytesRemain); + be += strlen(be); + } + *be++ = '\t'; + for (i = fd->nfps; i >= 0; i--) { + FDSTACK_t * fps = &fd->fps[i]; + if (i != fd->nfps) + *be++ = ' '; + *be++ = '|'; + *be++ = ' '; + if (fps->io == fdio) { + sprintf(be, "FD %d fp %p", fps->fdno, fps->fp); + } else if (fps->io == ufdio) { + sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp); + } else if (fps->io == gzdio) { + sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno); +#if HAVE_BZLIB_H + } else if (fps->io == bzdio) { + sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno); +#endif +#if HAVE_LZMA_H + } else if (fps->io == xzdio) { + sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno); + } else if (fps->io == lzdio) { + sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno); +#endif + } else if (fps->io == fpio) { + sprintf(be, "%s %p(%d) fdno %d", + (fps->fdno < 0 ? "LIBIO" : "FP"), + fps->fp, fileno(((FILE *)fps->fp)), fps->fdno); + } else { + sprintf(be, "??? io %p fp %p fdno %d ???", + fps->io, fps->fp, fps->fdno); + } + be += strlen(be); + *be = '\0'; + } + return buf; +} + +static void fdstat_enter(FD_t fd, fdOpX opx) +{ + if (fd == NULL) return; + if (fd->stats != NULL) + (void) rpmswEnter(fdOp(fd, opx), (ssize_t) 0); +} + +static void fdstat_exit(FD_t fd, fdOpX opx, ssize_t rc) +{ + if (fd == NULL) return; + if (rc == -1) + fd->syserrno = errno; + else if (rc > 0 && fd->bytesRemain > 0) + switch (opx) { + case FDSTAT_READ: + case FDSTAT_WRITE: + fd->bytesRemain -= rc; + break; + default: + break; + } + if (fd->stats != NULL) + (void) rpmswExit(fdOp(fd, opx), rc); +} + +static void fdstat_print(FD_t fd, const char * msg, FILE * fp) +{ + static const int usec_scale = (1000*1000); + int opx; + + if (fd == NULL || fd->stats == NULL) return; + for (opx = 0; opx < 4; opx++) { + rpmop op = &fd->stats->ops[opx]; + if (op->count <= 0) continue; + switch (opx) { + case FDSTAT_READ: + if (msg) fprintf(fp, "%s:", msg); + fprintf(fp, "%8d reads, %8ld total bytes in %d.%06d secs\n", + op->count, (long)op->bytes, + (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale)); + break; + case FDSTAT_WRITE: + if (msg) fprintf(fp, "%s:", msg); + fprintf(fp, "%8d writes, %8ld total bytes in %d.%06d secs\n", + op->count, (long)op->bytes, + (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale)); + break; + case FDSTAT_SEEK: + break; + case FDSTAT_CLOSE: + break; + } + } +} + +/* =============================================================== */ +off_t fdSize(FD_t fd) +{ + struct stat sb; + off_t rc = -1; + + if (fd != NULL && fstat(Fileno(fd), &sb) == 0) + rc = sb.st_size; + return rc; +} + +FD_t fdDup(int fdno) +{ + FD_t fd; + int nfdno; + + if ((nfdno = dup(fdno)) < 0) + return NULL; + fd = fdNew(); + fdSetFdno(fd, nfdno); +DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd))); + return fd; +} + +static int fdSeekNot(void * cookie, _libio_pos_t pos, int whence) +{ + return -2; +} + +/** \ingroup rpmio + */ +static int fdFileno(void * cookie) +{ + FD_t fd = c2f(cookie); + return (fd != NULL) ? fd->fps[0].fdno : -2; +} + +FILE * fdGetFILE(FD_t fd) +{ + return ((FILE *)fdGetFp(fd)); +} + +/* =============================================================== */ + +FD_t fdLink(void * cookie) +{ + FD_t fd = c2f(cookie); + if (fd) + fd->nrefs++; + return fd; +} + +/** + */ +FD_t fdFree( FD_t fd) +{ + if (fd) { + if (--fd->nrefs > 0) + return fd; + fd->stats = _free(fd->stats); + if (fd->digests) { + fd->digests = rpmDigestBundleFree(fd->digests); + } + free(fd); + } + return NULL; +} + +/** + */ +FD_t fdNew(void) +{ + FD_t fd = xcalloc(1, sizeof(*fd)); + if (fd == NULL) /* XXX xmalloc never returns NULL */ + return NULL; + fd->nrefs = 0; + fd->flags = 0; + fd->magic = FDMAGIC; + fd->urlType = URL_IS_UNKNOWN; + + fd->nfps = 0; + memset(fd->fps, 0, sizeof(fd->fps)); + + fd->fps[0].io = fdio; + fd->fps[0].fp = NULL; + fd->fps[0].fdno = -1; + + fd->bytesRemain = -1; + fd->syserrno = 0; + fd->errcookie = NULL; + fd->stats = xcalloc(1, sizeof(*fd->stats)); + fd->digests = NULL; + + return fdLink(fd); +} + +/** + */ +static ssize_t fdRead(void * cookie, void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + ssize_t rc; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + + fdstat_enter(fd, FDSTAT_READ); + rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count)); + fdstat_exit(fd, FDSTAT_READ, rc); + + if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc); + +DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd))); + + return rc; +} + +/** + */ +static ssize_t fdWrite(void * cookie, const void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + int fdno = fdFileno(fd); + ssize_t rc; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + + if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count); + + if (count == 0) return 0; + + fdstat_enter(fd, FDSTAT_WRITE); + rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count)); + fdstat_exit(fd, FDSTAT_WRITE, rc); + +DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd))); + + return rc; +} + +static int fdSeek(void * cookie, _libio_pos_t pos, int whence) +{ +#ifdef USE_COOKIE_SEEK_POINTER + _IO_off64_t p = *pos; +#else + off_t p = pos; +#endif + FD_t fd = c2f(cookie); + off_t rc; + + if (fd == NULL) + return -2; + + assert(fd->bytesRemain == -1); /* XXX FIXME fadio only for now */ + fdstat_enter(fd, FDSTAT_SEEK); + rc = lseek(fdFileno(fd), p, whence); + fdstat_exit(fd, FDSTAT_SEEK, rc); + +DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd))); + + return rc; +} + +/** + */ +static int fdClose( void * cookie) +{ + FD_t fd = c2f(cookie); + int fdno; + int rc; + + if (fd == NULL) return -2; + fdno = fdFileno(fd); + + fdSetFdno(fd, -1); + + fdstat_enter(fd, FDSTAT_CLOSE); + rc = ((fdno >= 0) ? close(fdno) : -2); + fdstat_exit(fd, FDSTAT_CLOSE, rc); + +DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd))); + + fd = fdFree(fd); + return rc; +} + +/** + */ +static FD_t fdOpen(const char *path, int flags, mode_t mode) +{ + FD_t fd; + int fdno; + + fdno = open(path, flags, mode); + if (fdno < 0) return NULL; + if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) { + (void) close(fdno); + return NULL; + } + fd = fdNew(); + fdSetFdno(fd, fdno); + fd->flags = flags; +DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd))); + return fd; +} + +static const struct FDIO_s fdio_s = { + fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno, + fdOpen, NULL, fdGetFp, NULL +}; +static const FDIO_t fdio = &fdio_s ; + +int ufdCopy(FD_t sfd, FD_t tfd) +{ + char buf[BUFSIZ]; + int itemsRead; + int itemsCopied = 0; + int rc = 0; + + while (1) { + rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd); + if (rc < 0) + break; + else if (rc == 0) { + rc = itemsCopied; + break; + } + itemsRead = rc; + rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd); + if (rc < 0) + break; + if (rc != itemsRead) { + rc = -1; + break; + } + + itemsCopied += itemsRead; + } + + DBGIO(sfd, (stderr, "++ copied %d bytes\n", itemsCopied)); + + return rc; +} + +/* + * Deal with remote url's by fetching them with a helper application + * and treat as local file afterwards. + * TODO: + * - better error checking + reporting + * - curl & friends don't know about hkp://, transform to http? + */ + +static FD_t urlOpen(const char * url, int flags, mode_t mode) +{ + FD_t fd; + char *dest = NULL; + int rc = 1; /* assume failure */ + + fd = rpmMkTempFile(NULL, &dest); + if (fd == NULL) { + return NULL; + } + Fclose(fd); + + rc = urlGetFile(url, dest); + if (rc == 0) { + fd = fdOpen(dest, flags, mode); + unlink(dest); + } else { + fd = NULL; + } + dest = _free(dest); + + return fd; + +} +static FD_t ufdOpen(const char * url, int flags, mode_t mode) +{ + FD_t fd = NULL; + const char * path; + urltype urlType = urlPath(url, &path); + +if (_rpmio_debug) +fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode); + + switch (urlType) { + case URL_IS_FTP: + case URL_IS_HTTPS: + case URL_IS_HTTP: + case URL_IS_HKP: + fd = urlOpen(url, flags, mode); + /* we're dealing with local file when urlOpen() returns */ + urlType = URL_IS_UNKNOWN; + break; + case URL_IS_DASH: + if ((flags & O_ACCMODE) == O_RDWR) { + fd = NULL; + } else { + fd = fdDup((flags & O_ACCMODE) == O_WRONLY ? + STDOUT_FILENO : STDIN_FILENO); + } + break; + case URL_IS_PATH: + case URL_IS_UNKNOWN: + default: + fd = fdOpen(path, flags, mode); + break; + } + + if (fd == NULL) return NULL; + + fdSetIo(fd, ufdio); + fd->bytesRemain = -1; + fd->urlType = urlType; + + if (Fileno(fd) < 0) { + (void) fdClose(fd); + return NULL; + } +DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd))); + return fd; +} + +static const struct FDIO_s ufdio_s = { + fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno, + ufdOpen, NULL, fdGetFp, NULL +}; +static const FDIO_t ufdio = &ufdio_s ; + +ssize_t timedRead(FD_t fd, void * bufptr, size_t length) +{ + return ufdio->read(fd, bufptr, length); +} + +/* =============================================================== */ +/* Support for GZIP library. + */ +#include <zlib.h> + +static void * gzdFileno(FD_t fd) +{ + return iotFileno(fd, gzdio); +} + +static +FD_t gzdOpen(const char * path, const char * fmode) +{ + FD_t fd; + gzFile gzfile; + if ((gzfile = gzopen(path, fmode)) == NULL) + return NULL; + fd = fdNew(); + fdPop(fd); fdPush(fd, gzdio, gzfile, -1); + +DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd))); + return fdLink(fd); +} + +static FD_t gzdFdopen(void * cookie, const char *fmode) +{ + FD_t fd = c2f(cookie); + int fdno; + gzFile gzfile; + + if (fd == NULL || fmode == NULL) return NULL; + fdno = fdFileno(fd); + fdSetFdno(fd, -1); /* XXX skip the fdio close */ + if (fdno < 0) return NULL; + gzfile = gzdopen(fdno, fmode); + if (gzfile == NULL) return NULL; + + fdPush(fd, gzdio, gzfile, fdno); /* Push gzdio onto stack */ + + return fdLink(fd); +} + +static int gzdFlush(FD_t fd) +{ + gzFile gzfile; + gzfile = gzdFileno(fd); + if (gzfile == NULL) return -2; + return gzflush(gzfile, Z_SYNC_FLUSH); /* XXX W2DO? */ +} + +/* =============================================================== */ +static ssize_t gzdRead(void * cookie, void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + gzFile gzfile; + ssize_t rc; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + + gzfile = gzdFileno(fd); + if (gzfile == NULL) return -2; /* XXX can't happen */ + + fdstat_enter(fd, FDSTAT_READ); + rc = gzread(gzfile, buf, count); +DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd))); + if (rc < 0) { + int zerror = 0; + fd->errcookie = gzerror(gzfile, &zerror); + if (zerror == Z_ERRNO) { + fd->syserrno = errno; + fd->errcookie = strerror(fd->syserrno); + } + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_READ, rc); + if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc); + } + return rc; +} + +static ssize_t gzdWrite(void * cookie, const void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + gzFile gzfile; + ssize_t rc; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + + if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count); + + gzfile = gzdFileno(fd); + if (gzfile == NULL) return -2; /* XXX can't happen */ + + fdstat_enter(fd, FDSTAT_WRITE); + rc = gzwrite(gzfile, (void *)buf, count); +DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd))); + if (rc < 0) { + int zerror = 0; + fd->errcookie = gzerror(gzfile, &zerror); + if (zerror == Z_ERRNO) { + fd->syserrno = errno; + fd->errcookie = strerror(fd->syserrno); + } + } else if (rc > 0) { + fdstat_exit(fd, FDSTAT_WRITE, rc); + } + return rc; +} + +/* XXX zlib-1.0.4 has not */ +static int gzdSeek(void * cookie, _libio_pos_t pos, int whence) +{ +#ifdef USE_COOKIE_SEEK_POINTER + _IO_off64_t p = *pos; +#else + off_t p = pos; +#endif + int rc; +#if HAVE_GZSEEK + FD_t fd = c2f(cookie); + gzFile gzfile; + + if (fd == NULL) return -2; + assert(fd->bytesRemain == -1); /* XXX FIXME */ + + gzfile = gzdFileno(fd); + if (gzfile == NULL) return -2; /* XXX can't happen */ + + fdstat_enter(fd, FDSTAT_SEEK); + rc = gzseek(gzfile, p, whence); +DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd))); + if (rc < 0) { + int zerror = 0; + fd->errcookie = gzerror(gzfile, &zerror); + if (zerror == Z_ERRNO) { + fd->syserrno = errno; + fd->errcookie = strerror(fd->syserrno); + } + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_SEEK, rc); + } +#else + rc = -2; +#endif + return rc; +} + +static int gzdClose( void * cookie) +{ + FD_t fd = c2f(cookie); + gzFile gzfile; + int rc; + + gzfile = gzdFileno(fd); + if (gzfile == NULL) return -2; /* XXX can't happen */ + + fdstat_enter(fd, FDSTAT_CLOSE); + rc = gzclose(gzfile); + + /* XXX TODO: preserve fd if errors */ + + if (fd) { +DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd))); + if (rc < 0) { + fd->errcookie = "gzclose error"; + if (rc == Z_ERRNO) { + fd->syserrno = errno; + fd->errcookie = strerror(fd->syserrno); + } + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_CLOSE, rc); + } + } + +DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd))); + + if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr); + if (rc == 0) + fd = fdFree(fd); + return rc; +} + +static const struct FDIO_s gzdio_s = { + gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno, + NULL, gzdOpen, gzdFileno, gzdFlush +}; +static const FDIO_t gzdio = &gzdio_s ; + +/* =============================================================== */ +/* Support for BZIP2 library. + */ +#if HAVE_BZLIB_H + +#include <bzlib.h> + +#ifdef HAVE_BZ2_1_0 +# define bzopen BZ2_bzopen +# define bzclose BZ2_bzclose +# define bzdopen BZ2_bzdopen +# define bzerror BZ2_bzerror +# define bzflush BZ2_bzflush +# define bzread BZ2_bzread +# define bzwrite BZ2_bzwrite +#endif /* HAVE_BZ2_1_0 */ + +static void * bzdFileno(FD_t fd) +{ + return iotFileno(fd, bzdio); +} + +static FD_t bzdOpen(const char * path, const char * mode) +{ + FD_t fd; + BZFILE *bzfile;; + if ((bzfile = bzopen(path, mode)) == NULL) + return NULL; + fd = fdNew(); + fdPop(fd); fdPush(fd, bzdio, bzfile, -1); + return fdLink(fd); +} + +static FD_t bzdFdopen(void * cookie, const char * fmode) +{ + FD_t fd = c2f(cookie); + int fdno; + BZFILE *bzfile; + + if (fd == NULL || fmode == NULL) return NULL; + fdno = fdFileno(fd); + fdSetFdno(fd, -1); /* XXX skip the fdio close */ + if (fdno < 0) return NULL; + bzfile = bzdopen(fdno, fmode); + if (bzfile == NULL) return NULL; + + fdPush(fd, bzdio, bzfile, fdno); /* Push bzdio onto stack */ + + return fdLink(fd); +} + +static int bzdFlush(FD_t fd) +{ + return bzflush(bzdFileno(fd)); +} + +/* =============================================================== */ +static ssize_t bzdRead(void * cookie, void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + BZFILE *bzfile; + ssize_t rc = 0; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + bzfile = bzdFileno(fd); + fdstat_enter(fd, FDSTAT_READ); + if (bzfile) + rc = bzread(bzfile, buf, count); + if (rc == -1) { + int zerror = 0; + if (bzfile) + fd->errcookie = bzerror(bzfile, &zerror); + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_READ, rc); + if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc); + } + return rc; +} + +static ssize_t bzdWrite(void * cookie, const void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + BZFILE *bzfile; + ssize_t rc; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + + if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count); + + bzfile = bzdFileno(fd); + fdstat_enter(fd, FDSTAT_WRITE); + rc = bzwrite(bzfile, (void *)buf, count); + if (rc == -1) { + int zerror = 0; + fd->errcookie = bzerror(bzfile, &zerror); + } else if (rc > 0) { + fdstat_exit(fd, FDSTAT_WRITE, rc); + } + return rc; +} + +static int bzdClose( void * cookie) +{ + FD_t fd = c2f(cookie); + BZFILE *bzfile; + int rc; + + bzfile = bzdFileno(fd); + + if (bzfile == NULL) return -2; + fdstat_enter(fd, FDSTAT_CLOSE); + /* FIX: check rc */ + bzclose(bzfile); + rc = 0; /* XXX FIXME */ + + /* XXX TODO: preserve fd if errors */ + + if (fd) { + if (rc == -1) { + int zerror = 0; + fd->errcookie = bzerror(bzfile, &zerror); + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_CLOSE, rc); + } + } + +DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd))); + + if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr); + if (rc == 0) + fd = fdFree(fd); + return rc; +} + +static const struct FDIO_s bzdio_s = { + bzdRead, bzdWrite, fdSeekNot, bzdClose, fdLink, fdFree, fdNew, fdFileno, + NULL, bzdOpen, bzdFileno, bzdFlush +}; +static const FDIO_t bzdio = &bzdio_s ; + +#endif /* HAVE_BZLIB_H */ + +/* =============================================================== */ +static const char * getFdErrstr (FD_t fd) +{ + const char *errstr = NULL; + + if (fdGetIo(fd) == gzdio) { + errstr = fd->errcookie; + } else +#ifdef HAVE_BZLIB_H + if (fdGetIo(fd) == bzdio) { + errstr = fd->errcookie; + } else +#endif /* HAVE_BZLIB_H */ +#ifdef HAVE_LZMA_H + if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) { + errstr = fd->errcookie; + } else +#endif /* HAVE_LZMA_H */ + { + errstr = (fd->syserrno ? strerror(fd->syserrno) : ""); + } + + return errstr; +} + +/* =============================================================== */ +/* Support for LZMA library. + */ + +#ifdef HAVE_LZMA_H + +#include <sys/types.h> +#include <inttypes.h> +#include <lzma.h> + +#define kBufferSize (1 << 15) + +typedef struct lzfile { + /* IO buffer */ + uint8_t buf[kBufferSize]; + + lzma_stream strm; + + FILE *file; + + int encoding; + int eof; + +} LZFILE; + +static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz) +{ + int level = 7; /* Use XZ's default compression level if unspecified */ + int encoding = 0; + FILE *fp; + LZFILE *lzfile; + lzma_ret ret; + lzma_stream init_strm = LZMA_STREAM_INIT; + + for (; *mode; mode++) { + if (*mode == 'w') + encoding = 1; + else if (*mode == 'r') + encoding = 0; + else if (*mode >= '1' && *mode <= '9') + level = *mode - '0'; + } + if (fd != -1) + fp = fdopen(fd, encoding ? "w" : "r"); + else + fp = fopen(path, encoding ? "w" : "r"); + if (!fp) + return 0; + lzfile = calloc(1, sizeof(*lzfile)); + if (!lzfile) { + fclose(fp); + return 0; + } + + lzfile->file = fp; + lzfile->encoding = encoding; + lzfile->eof = 0; + lzfile->strm = init_strm; + if (encoding) { + if (xz) { + ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256); + } else { + lzma_options_lzma options; + lzma_lzma_preset(&options, level); + ret = lzma_alone_encoder(&lzfile->strm, &options); + } + } else { /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */ + ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0); + } + if (ret != LZMA_OK) { + fclose(fp); + free(lzfile); + return 0; + } + return lzfile; +} + +static LZFILE *xzopen(const char *path, const char *mode) +{ + return lzopen_internal(path, mode, -1, 1); +} + +static LZFILE *xzdopen(int fd, const char *mode) +{ + if (fd < 0) + return 0; + return lzopen_internal(0, mode, fd, 1); +} + +static LZFILE *lzopen(const char *path, const char *mode) +{ + return lzopen_internal(path, mode, -1, 0); +} + +static LZFILE *lzdopen(int fd, const char *mode) +{ + if (fd < 0) + return 0; + return lzopen_internal(0, mode, fd, 0); +} + +static int lzflush(LZFILE *lzfile) +{ + return fflush(lzfile->file); +} + +static int lzclose(LZFILE *lzfile) +{ + lzma_ret ret; + size_t n; + int rc; + + if (!lzfile) + return -1; + if (lzfile->encoding) { + for (;;) { + lzfile->strm.avail_out = kBufferSize; + lzfile->strm.next_out = lzfile->buf; + ret = lzma_code(&lzfile->strm, LZMA_FINISH); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + return -1; + n = kBufferSize - lzfile->strm.avail_out; + if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n) + return -1; + if (ret == LZMA_STREAM_END) + break; + } + } + lzma_end(&lzfile->strm); + rc = fclose(lzfile->file); + free(lzfile); + return rc; +} + +static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len) +{ + lzma_ret ret; + int eof = 0; + + if (!lzfile || lzfile->encoding) + return -1; + if (lzfile->eof) + return 0; + lzfile->strm.next_out = buf; + lzfile->strm.avail_out = len; + for (;;) { + if (!lzfile->strm.avail_in) { + lzfile->strm.next_in = lzfile->buf; + lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file); + if (!lzfile->strm.avail_in) + eof = 1; + } + ret = lzma_code(&lzfile->strm, LZMA_RUN); + if (ret == LZMA_STREAM_END) { + lzfile->eof = 1; + return len - lzfile->strm.avail_out; + } + if (ret != LZMA_OK) + return -1; + if (!lzfile->strm.avail_out) + return len; + if (eof) + return -1; + } +} + +static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len) +{ + lzma_ret ret; + size_t n; + if (!lzfile || !lzfile->encoding) + return -1; + if (!len) + return 0; + lzfile->strm.next_in = buf; + lzfile->strm.avail_in = len; + for (;;) { + lzfile->strm.next_out = lzfile->buf; + lzfile->strm.avail_out = kBufferSize; + ret = lzma_code(&lzfile->strm, LZMA_RUN); + if (ret != LZMA_OK) + return -1; + n = kBufferSize - lzfile->strm.avail_out; + if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n) + return -1; + if (!lzfile->strm.avail_in) + return len; + } +} + +/* =============================================================== */ + +static void * lzdFileno(FD_t fd) +{ + void * rc = NULL; + + if (fd == NULL) + return NULL; + + for (int i = fd->nfps; i >= 0; i--) { + FDSTACK_t * fps = &fd->fps[i]; + if (fps->io != xzdio && fps->io != lzdio) + continue; + rc = fps->fp; + break; + } + return rc; +} + +static FD_t xzdOpen(const char * path, const char * mode) +{ + FD_t fd; + LZFILE *lzfile; + if ((lzfile = xzopen(path, mode)) == NULL) + return NULL; + fd = fdNew(); + fdPop(fd); fdPush(fd, xzdio, lzfile, -1); + return fdLink(fd); +} + +static FD_t xzdFdopen(void * cookie, const char * fmode) +{ + FD_t fd = c2f(cookie); + int fdno; + LZFILE *lzfile; + + if (fd == NULL || fmode == NULL) return NULL; + fdno = fdFileno(fd); + fdSetFdno(fd, -1); /* XXX skip the fdio close */ + if (fdno < 0) return NULL; + lzfile = xzdopen(fdno, fmode); + if (lzfile == NULL) return NULL; + fdPush(fd, xzdio, lzfile, fdno); + return fdLink(fd); +} + +static FD_t lzdOpen(const char * path, const char * mode) +{ + FD_t fd; + LZFILE *lzfile; + if ((lzfile = lzopen(path, mode)) == NULL) + return NULL; + fd = fdNew(); + fdPop(fd); fdPush(fd, xzdio, lzfile, -1); + return fdLink(fd); +} + +static FD_t lzdFdopen(void * cookie, const char * fmode) +{ + FD_t fd = c2f(cookie); + int fdno; + LZFILE *lzfile; + + if (fd == NULL || fmode == NULL) return NULL; + fdno = fdFileno(fd); + fdSetFdno(fd, -1); /* XXX skip the fdio close */ + if (fdno < 0) return NULL; + lzfile = lzdopen(fdno, fmode); + if (lzfile == NULL) return NULL; + fdPush(fd, xzdio, lzfile, fdno); + return fdLink(fd); +} + +static int lzdFlush(FD_t fd) +{ + return lzflush(lzdFileno(fd)); +} + +/* =============================================================== */ +static ssize_t lzdRead(void * cookie, void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + LZFILE *lzfile; + ssize_t rc = 0; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + lzfile = lzdFileno(fd); + fdstat_enter(fd, FDSTAT_READ); + if (lzfile) + rc = lzread(lzfile, buf, count); + if (rc == -1) { + fd->errcookie = "Lzma: decoding error"; + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_READ, rc); + if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc); + } + return rc; +} + +static ssize_t lzdWrite(void * cookie, const void * buf, size_t count) +{ + FD_t fd = c2f(cookie); + LZFILE *lzfile; + ssize_t rc = 0; + + if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */ + + if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count); + + lzfile = lzdFileno(fd); + + fdstat_enter(fd, FDSTAT_WRITE); + rc = lzwrite(lzfile, (void *)buf, count); + if (rc < 0) { + fd->errcookie = "Lzma: encoding error"; + } else if (rc > 0) { + fdstat_exit(fd, FDSTAT_WRITE, rc); + } + return rc; +} + +static int lzdClose(void * cookie) +{ + FD_t fd = c2f(cookie); + LZFILE *lzfile; + int rc; + + lzfile = lzdFileno(fd); + + if (lzfile == NULL) return -2; + fdstat_enter(fd, FDSTAT_CLOSE); + rc = lzclose(lzfile); + + /* XXX TODO: preserve fd if errors */ + + if (fd) { + if (rc == -1) { + fd->errcookie = strerror(ferror(lzfile->file)); + } else if (rc >= 0) { + fdstat_exit(fd, FDSTAT_CLOSE, rc); + } + } + +DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd))); + + if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr); + if (rc == 0) + fd = fdFree(fd); + return rc; +} + +static struct FDIO_s xzdio_s = { + lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno, + NULL, xzdOpen, lzdFileno, lzdFlush +}; +static const FDIO_t xzdio = &xzdio_s; + +static struct FDIO_s lzdio_s = { + lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno, + NULL, lzdOpen, lzdFileno, lzdFlush +}; +static const FDIO_t lzdio = &lzdio_s; + +#endif /* HAVE_LZMA_H */ + +/* =============================================================== */ + +const char *Fstrerror(FD_t fd) +{ + if (fd == NULL) + return (errno ? strerror(errno) : ""); + return getFdErrstr(fd); +} + +#define FDIOVEC(_fd, _vec) \ + ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL) + +ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) { + fdio_read_function_t _read; + int rc; + + if (fd == NULL) + return 0; + + if (fdGetIo(fd) == fpio) { + rc = fread(buf, size, nmemb, fdGetFILE(fd)); + return rc; + } + + _read = FDIOVEC(fd, read); + + rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2); + return rc; +} + +ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd) +{ + fdio_write_function_t _write; + int rc; + + if (fd == NULL) + return 0; + + if (fdGetIo(fd) == fpio) { + rc = fwrite(buf, size, nmemb, fdGetFILE(fd)); + return rc; + } + + _write = FDIOVEC(fd, write); + + rc = (_write ? _write(fd, buf, size * nmemb) : -2); + return rc; +} + +int Fseek(FD_t fd, _libio_off_t offset, int whence) { + fdio_seek_function_t _seek; +#ifdef USE_COOKIE_SEEK_POINTER + _IO_off64_t o64 = offset; + _libio_pos_t pos = &o64; +#else + _libio_pos_t pos = offset; +#endif + + long int rc; + + if (fd == NULL) + return -1; + + if (fdGetIo(fd) == fpio) { + FILE *fp; + + fp = fdGetFILE(fd); + rc = fseek(fp, offset, whence); + return rc; + } + + _seek = FDIOVEC(fd, seek); + + rc = (_seek ? _seek(fd, pos, whence) : -2); + return rc; +} + +int Fclose(FD_t fd) +{ + int rc = 0, ec = 0; + + if (fd == NULL) + return -1; + + fd = fdLink(fd); + while (fd->nfps >= 0) { + FDSTACK_t * fps = &fd->fps[fd->nfps]; + + if (fps->io == fpio) { + FILE *fp; + int fpno; + + fp = fdGetFILE(fd); + fpno = fileno(fp); + if (fp) + rc = fclose(fp); + if (fpno == -1) { + fd = fdFree(fd); + fdPop(fd); + } + } else { + fdio_close_function_t _close = FDIOVEC(fd, close); + rc = _close ? _close(fd) : -2; + } + if (fd->nfps == 0) + break; + if (ec == 0 && rc) + ec = rc; + fdPop(fd); + } + fd = fdFree(fd); + return ec; +} + +/** + * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags. + * returns stdio[0] = NUL on error. + * + * - gzopen: [0-9] is compression level + * - gzopen: 'f' is filtered (Z_FILTERED) + * - gzopen: 'h' is Huffman encoding (Z_HUFFMAN_ONLY) + * - bzopen: [1-9] is block size (modulo 100K) + * - bzopen: 's' is smallmode + * - HACK: '.' terminates, rest is type of I/O + */ +static void cvtfmode (const char *m, + char *stdio, size_t nstdio, + char *other, size_t nother, + const char **end, int * f) +{ + int flags = 0; + char c; + + switch (*m) { + case 'a': + flags |= O_WRONLY | O_CREAT | O_APPEND; + if (--nstdio > 0) *stdio++ = *m; + break; + case 'w': + flags |= O_WRONLY | O_CREAT | O_TRUNC; + if (--nstdio > 0) *stdio++ = *m; + break; + case 'r': + flags |= O_RDONLY; + if (--nstdio > 0) *stdio++ = *m; + break; + default: + *stdio = '\0'; + return; + break; + } + m++; + + while ((c = *m++) != '\0') { + switch (c) { + case '.': + break; + case '+': + flags &= ~(O_RDONLY|O_WRONLY); + flags |= O_RDWR; + if (--nstdio > 0) *stdio++ = c; + continue; + break; + case 'b': + if (--nstdio > 0) *stdio++ = c; + continue; + break; + case 'x': + flags |= O_EXCL; + if (--nstdio > 0) *stdio++ = c; + continue; + break; + default: + if (--nother > 0) *other++ = c; + continue; + break; + } + break; + } + + *stdio = *other = '\0'; + if (end != NULL) + *end = (*m != '\0' ? m : NULL); + if (f != NULL) + *f = flags; +} + +#if _USE_LIBIO +#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0 +/* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */ +typedef _IO_cookie_io_functions_t cookie_io_functions_t; +#endif +#endif + +FD_t Fdopen(FD_t ofd, const char *fmode) +{ + char stdio[20], other[20], zstdio[20]; + const char *end = NULL; + FDIO_t iof = NULL; + FD_t fd = ofd; + +if (_rpmio_debug) +fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd)); + + if (fd == NULL || fmode == NULL) + return NULL; + + cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL); + if (stdio[0] == '\0') + return NULL; + zstdio[0] = '\0'; + strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio)); + strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio)); + + if (end == NULL && other[0] == '\0') + return fd; + + if (end && *end) { + if (rstreq(end, "fdio")) { + iof = fdio; + } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) { + iof = gzdio; + fd = gzdFdopen(fd, zstdio); +#if HAVE_BZLIB_H + } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) { + iof = bzdio; + fd = bzdFdopen(fd, zstdio); +#endif +#if HAVE_LZMA_H + } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) { + iof = xzdio; + fd = xzdFdopen(fd, zstdio); + } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) { + iof = lzdio; + fd = lzdFdopen(fd, zstdio); +#endif + } else if (rstreq(end, "ufdio")) { + iof = ufdio; + } else if (rstreq(end, "fpio")) { + iof = fpio; + if (noLibio) { + int fdno = Fileno(fd); + FILE * fp = fdopen(fdno, stdio); +if (_rpmio_debug) +fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp); + if (fp == NULL) + return NULL; + /* XXX gzdio/bzdio use fp for private data */ + if (fdGetFp(fd) == NULL) + fdSetFp(fd, fp); + fdPush(fd, fpio, fp, fdno); /* Push fpio onto stack */ + } + } + } else if (other[0] != '\0') { + for (end = other; *end && strchr("0123456789fh", *end); end++) + {}; + if (*end == '\0') { + iof = gzdio; + fd = gzdFdopen(fd, zstdio); + } + } + if (iof == NULL) + return fd; + + if (!noLibio) { + FILE * fp = NULL; + +#if _USE_LIBIO + { cookie_io_functions_t ciof; + ciof.read = (cookie_read_function_t *) iof->read; + ciof.write = (cookie_write_function_t *) iof->write; + ciof.seek = iof->seek; + ciof.close = iof->close; + fp = fopencookie(fd, stdio, ciof); +DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp)); + } +#endif + + if (fp) { + /* XXX gzdio/bzdio use fp for private data */ + if (fdGetFp(fd) == NULL) + fdSetFp(fd, fp); + fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */ + fd = fdLink(fd); + } + } + +DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd))); + return fd; +} + +FD_t Fopen(const char *path, const char *fmode) +{ + char stdio[20], other[20]; + const char *end = NULL; + mode_t perms = 0666; + int flags = 0; + FD_t fd; + + if (path == NULL || fmode == NULL) + return NULL; + + stdio[0] = '\0'; + cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags); + if (stdio[0] == '\0') + return NULL; + + if (end == NULL || rstreq(end, "fdio")) { +if (_rpmio_debug) +fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode); + fd = fdOpen(path, flags, perms); + if (fdFileno(fd) < 0) { + if (fd) (void) fdClose(fd); + return NULL; + } + } else { + /* XXX gzdio and bzdio here too */ + + switch (urlIsURL(path)) { + case URL_IS_HTTPS: + case URL_IS_HTTP: + case URL_IS_HKP: + case URL_IS_PATH: + case URL_IS_DASH: + case URL_IS_FTP: + case URL_IS_UNKNOWN: +if (_rpmio_debug) +fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode); + fd = ufdOpen(path, flags, perms); + if (fd == NULL || !(fdFileno(fd) >= 0)) + return fd; + break; + default: +if (_rpmio_debug) +fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode); + return NULL; + break; + } + + } + + if (fd) + fd = Fdopen(fd, fmode); + return fd; +} + +int Fflush(FD_t fd) +{ + void * vh; + if (fd == NULL) return -1; + if (fdGetIo(fd) == fpio) + return fflush(fdGetFILE(fd)); + + vh = fdGetFp(fd); + if (vh && fdGetIo(fd) == gzdio) + return gzdFlush(vh); +#if HAVE_BZLIB_H + if (vh && fdGetIo(fd) == bzdio) + return bzdFlush(vh); +#endif +#if HAVE_LZMA_H + if (vh && (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio)) + return lzdFlush(vh); +#endif +/* FIXME: If we get here, something went wrong above */ + return 0; +} + +off_t Ftell(FD_t fd) +{ + FDIO_t iot; + off_t pos = -2; /* assume not implemented */ + + if (fd == NULL) return -1; + iot = fdGetIo(fd); + /* this wont work correctly for compressed types */ + if (iot == fpio || iot == fdio || iot == ufdio) { + pos = lseek(Fileno(fd), 0, SEEK_CUR); + } + + return pos; +} + +int Ferror(FD_t fd) +{ + int i, rc = 0; + + if (fd == NULL) return -1; + for (i = fd->nfps; rc == 0 && i >= 0; i--) { + FDSTACK_t * fps = &fd->fps[i]; + int ec; + + if (fps->io == fpio) { + ec = ferror(fdGetFILE(fd)); + } else if (fps->io == gzdio) { + ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0; + i--; /* XXX fdio under gzdio always has fdno == -1 */ +#if HAVE_BZLIB_H + } else if (fps->io == bzdio) { + ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0; + i--; /* XXX fdio under bzdio always has fdno == -1 */ +#endif +#if HAVE_LZMA_H + } else if (fps->io == xzdio || fps->io == lzdio) { + ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0; + i--; /* XXX fdio under xzdio/lzdio always has fdno == -1 */ +#endif + } else { + /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */ + ec = (fdFileno(fd) < 0 ? -1 : 0); + } + + if (rc == 0 && ec) + rc = ec; + } +DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd))); + return rc; +} + +int Fileno(FD_t fd) +{ + int i, rc = -1; + + if (fd == NULL) return -1; + for (i = fd->nfps ; rc == -1 && i >= 0; i--) { + rc = fd->fps[i].fdno; + } + +DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd))); + return rc; +} + +/* XXX this is naive */ +int Fcntl(FD_t fd, int op, void *lip) +{ + return fcntl(Fileno(fd), op, lip); +} + +rpmop fdOp(FD_t fd, fdOpX opx) +{ + rpmop op = NULL; + + if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX) + op = fd->stats->ops + opx; + return op; +} + +int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp) +{ + static const ssize_t blenmax = (32 * BUFSIZ); + ssize_t blen = 0; + uint8_t * b = NULL; + ssize_t size; + FD_t fd; + int rc = 0; + + fd = Fopen(fn, "r.ufdio"); + if (fd == NULL || Ferror(fd)) { + rc = 2; + goto exit; + } + + size = fdSize(fd); + blen = (size >= 0 ? size : blenmax); + if (blen) { + int nb; + b = xmalloc(blen+1); + b[0] = '\0'; + nb = Fread(b, sizeof(*b), blen, fd); + if (Ferror(fd) || (size > 0 && nb != blen)) { + rc = 1; + goto exit; + } + if (blen == blenmax && nb < blen) { + blen = nb; + b = xrealloc(b, blen+1); + } + b[blen] = '\0'; + } + +exit: + if (fd) (void) Fclose(fd); + + if (rc) { + if (b) free(b); + b = NULL; + blen = 0; + } + + if (bp) *bp = b; + else if (b) free(b); + + if (blenp) *blenp = blen; + + return rc; +} + +static const struct FDIO_s fpio_s = { + fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno, + ufdOpen, NULL, fdGetFp, NULL +}; +static const FDIO_t fpio = &fpio_s ; + +void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags) +{ + if (fd->digests == NULL) { + fd->digests = rpmDigestBundleNew(); + } + fdstat_enter(fd, FDSTAT_DIGEST); + rpmDigestBundleAdd(fd->digests, hashalgo, flags); + fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0); +} + +static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen) +{ + if (fd && fd->digests) { + fdstat_enter(fd, FDSTAT_DIGEST); + rpmDigestBundleUpdate(fd->digests, buf, buflen); + fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen); + } +} + +void fdFiniDigest(FD_t fd, int hashalgo, + void ** datap, size_t * lenp, int asAscii) +{ + if (fd && fd->digests) { + fdstat_enter(fd, FDSTAT_DIGEST); + rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii); + fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0); + } +} + + diff --git a/rpmio/rpmio.h b/rpmio/rpmio.h new file mode 100644 index 0000000..93072e0 --- /dev/null +++ b/rpmio/rpmio.h @@ -0,0 +1,169 @@ +#ifndef H_RPMIO +#define H_RPMIO + +/** \ingroup rpmio + * \file rpmio/rpmio.h + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rpm/rpmtypes.h> +#include <rpm/rpmsw.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmio + * Hide libio API lossage. + * The libio interface changed after glibc-2.1.3 to pass the seek offset + * argument as a pointer rather than as an off_t. The snarl below defines + * typedefs to isolate the lossage. + */ +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) +#define USE_COOKIE_SEEK_POINTER 1 +typedef _IO_off64_t _libio_off_t; +typedef _libio_off_t * _libio_pos_t; +#else +typedef off_t _libio_off_t; +typedef off_t _libio_pos_t; +#endif + +/** \ingroup rpmio + */ +typedef const struct FDIO_s * FDIO_t; + + +/** \ingroup rpmio + * \name RPMIO Interface. + */ + +/** \ingroup rpmio + * strerror(3) clone. + */ +const char * Fstrerror(FD_t fd); + +/** \ingroup rpmio + * fread(3) clone. + */ +ssize_t Fread(void * buf, size_t size, size_t nmemb, FD_t fd); + +/** \ingroup rpmio + * fwrite(3) clone. + */ +ssize_t Fwrite(const void * buf, size_t size, size_t nmemb, FD_t fd); + +/** \ingroup rpmio + * fseek(3) clone. + */ +int Fseek(FD_t fd, _libio_off_t offset, int whence); + +/** \ingroup rpmio + * ftell(3) clone. + */ +off_t Ftell(FD_t fd); + +/** \ingroup rpmio + * fclose(3) clone. + */ +int Fclose( FD_t fd); + +/** \ingroup rpmio + */ +FD_t Fdopen(FD_t ofd, const char * fmode); + +/** \ingroup rpmio + * fopen(3) clone. + */ +FD_t Fopen(const char * path, + const char * fmode); + + +/** \ingroup rpmio + * fflush(3) clone. + */ +int Fflush(FD_t fd); + +/** \ingroup rpmio + * ferror(3) clone. + */ +int Ferror(FD_t fd); + +/** \ingroup rpmio + * fileno(3) clone. + */ +int Fileno(FD_t fd); + +/** \ingroup rpmio + * fcntl(2) clone. + */ +int Fcntl(FD_t fd, int op, void *lip); + +/** \ingroup rpmio + * \name RPMIO Utilities. + */ + +/** \ingroup rpmio + */ +off_t fdSize(FD_t fd); + +/** \ingroup rpmio + */ +FD_t fdDup(int fdno); + +/** \ingroup rpmio + * Get associated FILE stream from fd (if any) + */ +FILE * fdGetFILE(FD_t fd); + +/** \ingroup rpmio + */ +FD_t fdLink(void * cookie); + +/** \ingroup rpmio + */ +FD_t fdFree(FD_t fd); + +/** \ingroup rpmio + */ +FD_t fdNew (void); + +/** + */ +int ufdCopy(FD_t sfd, FD_t tfd); + +/** + * XXX the name is misleading, this is a legacy wrapper that ensures + * only S_ISREG() files are read, nothing to do with timed... + * TODO: get this out of the API + */ +ssize_t timedRead(FD_t fd, void * bufptr, size_t length); + +/** \ingroup rpmio + * Identify per-desciptor I/O operation statistics. + */ +typedef enum fdOpX_e { + FDSTAT_READ = 0, /*!< Read statistics index. */ + FDSTAT_WRITE = 1, /*!< Write statistics index. */ + FDSTAT_SEEK = 2, /*!< Seek statistics index. */ + FDSTAT_CLOSE = 3, /*!< Close statistics index */ + FDSTAT_DIGEST = 4, /*!< Digest statistics index. */ + FDSTAT_MAX = 5 +} fdOpX; + +/** \ingroup rpmio + * + */ +rpmop fdOp(FD_t fd, fdOpX opx); + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMIO */ diff --git a/rpmio/rpmio_internal.h b/rpmio/rpmio_internal.h new file mode 100644 index 0000000..8c9f1a8 --- /dev/null +++ b/rpmio/rpmio_internal.h @@ -0,0 +1,44 @@ +#ifndef H_RPMIO_INTERNAL +#define H_RPMIO_INTERNAL + +/** \ingroup rpmio + * \file rpmio/rpmio_internal.h + */ + +#include <rpm/rpmio.h> +#include <rpm/rpmpgp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void fdSetBundle(FD_t fd, rpmDigestBundle bundle); +rpmDigestBundle fdGetBundle(FD_t fd); + +/** \ingroup rpmio + * Attach digest to fd. + */ +void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags); + +/** \ingroup rpmio + */ +void fdFiniDigest(FD_t fd, int hashalgo, + void ** datap, + size_t * lenp, + int asAscii); + +/** + * Read an entire file into a buffer. + * @param fn file name to read + * @retval *bp (malloc'd) buffer address + * @retval *blenp (malloc'd) buffer length + * @return 0 on success + */ +int rpmioSlurp(const char * fn, + uint8_t ** bp, ssize_t * blenp); + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMIO_INTERNAL */ diff --git a/rpmio/rpmkeyring.c b/rpmio/rpmkeyring.c new file mode 100644 index 0000000..784bc3a --- /dev/null +++ b/rpmio/rpmkeyring.c @@ -0,0 +1,231 @@ +#include "system.h" + +#include <rpm/rpmstring.h> +#include <rpm/rpmpgp.h> +#include <rpm/rpmfileutil.h> +#include <rpm/rpmlog.h> +#include <rpm/rpmkeyring.h> + +#include "rpmio/base64.h" +#include "rpmio/digest.h" + +#include "debug.h" + +struct rpmPubkey_s { + uint8_t *pkt; + size_t pktlen; + pgpKeyID_t keyid; + int nrefs; +}; + +struct rpmKeyring_s { + struct rpmPubkey_s **keys; + size_t numkeys; + int nrefs; +}; + +static rpmPubkey rpmPubkeyUnlink(rpmPubkey key); +static rpmKeyring rpmKeyringUnlink(rpmKeyring keyring); + +static int keyidcmp(const void *k1, const void *k2) +{ + const struct rpmPubkey_s *key1 = *(const struct rpmPubkey_s **) k1; + const struct rpmPubkey_s *key2 = *(const struct rpmPubkey_s **) k2; + + return memcmp(key1->keyid, key2->keyid, sizeof(key1->keyid)); +} + +rpmKeyring rpmKeyringNew(void) +{ + rpmKeyring keyring = xcalloc(1, sizeof(*keyring)); + keyring->keys = NULL; + keyring->numkeys = 0; + keyring->nrefs = 0; + return rpmKeyringLink(keyring); +} + +rpmKeyring rpmKeyringFree(rpmKeyring keyring) +{ + if (keyring == NULL) { + return NULL; + } + + if (keyring->nrefs > 1) { + return rpmKeyringUnlink(keyring); + } + + if (keyring->keys) { + for (int i = 0; i < keyring->numkeys; i++) { + keyring->keys[i] = rpmPubkeyFree(keyring->keys[i]); + } + free(keyring->keys); + } + free(keyring); + return NULL; +} + +static rpmPubkey rpmKeyringFindKeyid(rpmKeyring keyring, rpmPubkey key) +{ + rpmPubkey *found = NULL; + found = bsearch(&key, keyring->keys, keyring->numkeys, sizeof(*keyring->keys), keyidcmp); + return found ? *found : NULL; +} + +int rpmKeyringAddKey(rpmKeyring keyring, rpmPubkey key) +{ + if (keyring == NULL || key == NULL) + return -1; + + /* check if we already have this key */ + if (rpmKeyringFindKeyid(keyring, key)) { + return 1; + } + + keyring->keys = xrealloc(keyring->keys, (keyring->numkeys + 1) * sizeof(rpmPubkey)); + keyring->keys[keyring->numkeys] = rpmPubkeyLink(key); + keyring->numkeys++; + qsort(keyring->keys, keyring->numkeys, sizeof(*keyring->keys), keyidcmp); + + return 0; +} + +rpmKeyring rpmKeyringLink(rpmKeyring keyring) +{ + if (keyring) { + keyring->nrefs++; + } + return keyring; +} + +static rpmKeyring rpmKeyringUnlink(rpmKeyring keyring) +{ + if (keyring) { + keyring->nrefs--; + } + return NULL; +} + +rpmPubkey rpmPubkeyRead(const char *filename) +{ + uint8_t *pkt = NULL; + size_t pktlen; + rpmPubkey key = NULL; + + if (pgpReadPkts(filename, &pkt, &pktlen) <= 0) { + goto exit; + } + key = rpmPubkeyNew(pkt, pktlen); + free(pkt); + +exit: + return key; +} + +rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen) +{ + rpmPubkey key = NULL; + + if (pkt == NULL || pktlen == 0) + goto exit; + + key = xcalloc(1, sizeof(*key)); + pgpPubkeyFingerprint(pkt, pktlen, key->keyid); + key->pkt = xmalloc(pktlen); + key->pktlen = pktlen; + key->nrefs = 0; + memcpy(key->pkt, pkt, pktlen); + +exit: + return rpmPubkeyLink(key); +} + +rpmPubkey rpmPubkeyFree(rpmPubkey key) +{ + if (key == NULL) + return NULL; + + if (key->nrefs > 1) + return rpmPubkeyUnlink(key); + + free(key->pkt); + free(key); + return NULL; +} + +rpmPubkey rpmPubkeyLink(rpmPubkey key) +{ + if (key) { + key->nrefs++; + } + return key; +} + +static rpmPubkey rpmPubkeyUnlink(rpmPubkey key) +{ + if (key) { + key->nrefs--; + } + return NULL; +} + +pgpDig rpmPubkeyDig(rpmPubkey key) +{ + pgpDig dig = NULL; + static unsigned char zeros[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int rc; + if (key == NULL) + return NULL; + + dig = pgpNewDig(); + rc = pgpPrtPkts(key->pkt, key->pktlen, dig, 0); + if (rc == 0) { + pgpDigParams pubp = &dig->pubkey; + if (!memcmp(pubp->signid, zeros, sizeof(pubp->signid)) || + !memcmp(pubp->time, zeros, sizeof(pubp->time)) || + pubp->userid == NULL) { + rc = -1; + } + } + + if (rc) + dig = pgpFreeDig(dig); + + return dig; +} + +char * rpmPubkeyBase64(rpmPubkey key) +{ + char *enc = NULL; + + if (key) { + enc = b64encode(key->pkt, key->pktlen, -1); + } + return enc; +} + +rpmRC rpmKeyringLookup(rpmKeyring keyring, pgpDig sig) +{ + rpmRC res = RPMRC_NOKEY; + + if (keyring && sig) { + pgpDigParams sigp = &sig->signature; + pgpDigParams pubp = &sig->pubkey; + struct rpmPubkey_s needle, *key; + needle.pkt = NULL; + needle.pktlen = 0; + memcpy(needle.keyid, sigp->signid, sizeof(needle.keyid)); + + if ((key = rpmKeyringFindKeyid(keyring, &needle))) { + /* Retrieve parameters from pubkey packet(s) */ + int pktrc = pgpPrtPkts(key->pkt, key->pktlen, sig, 0); + /* Do the parameters match the signature? */ + if (pktrc == 0 && sigp->pubkey_algo == pubp->pubkey_algo && + memcmp(sigp->signid, pubp->signid, sizeof(sigp->signid)) == 0) { + res = RPMRC_OK; + } + } + } + + return res; +} diff --git a/rpmio/rpmkeyring.h b/rpmio/rpmkeyring.h new file mode 100644 index 0000000..8b4378b --- /dev/null +++ b/rpmio/rpmkeyring.h @@ -0,0 +1,96 @@ +#ifndef _RPMKEYRING_H +#define _RPMKEYRING_H + +/** \ingroup rpmkeyring + * \file rpmio/rpmkeyring.h + */ + +#include <rpm/rpmtypes.h> +#include <rpm/rpmpgp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmkeyring + * Create a new, empty keyring + * @return new keyring handle + */ +rpmKeyring rpmKeyringNew(void); + +/** \ingroup rpmkeyring + * Free keyring and the keys within it + * @return NULL always + */ +rpmKeyring rpmKeyringFree(rpmKeyring keyring); + +/** \ingroup rpmkeyring + * Add a public key to keyring. + * @param keyring keyring handle + * @param key pubkey handle + * @return 0 on success, -1 on error, 1 if key already present + */ +int rpmKeyringAddKey(rpmKeyring keyring, rpmPubkey key); + +/** \ingroup rpmkeyring + * Perform keyring lookup for a key matching a signature + * @param keyring keyring handle + * @param sig OpenPGP packet container of signature + * @return RPMRC_OK if found, RPMRC_NOKEY otherwise + */ +rpmRC rpmKeyringLookup(rpmKeyring keyring, pgpDig sig); + +/** \ingroup rpmkeyring + * Reference a keyring. + * @param keyring keyring handle + * @return new keyring reference + */ +rpmKeyring rpmKeyringLink(rpmKeyring keyring); + +/** \ingroup rpmkeyring + * Create a new rpmPubkey from OpenPGP packet + * @param pkt OpenPGP packet data + * @param pktlen Data length + * @return new pubkey handle + */ +rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen); + +/** \ingroup rpmkeyring + * Create a new rpmPubkey from ASCII-armored pubkey file + * @param filename Path to pubkey file + * @return new pubkey handle + */ +rpmPubkey rpmPubkeyRead(const char *filename); + +/** \ingroup rpmkeyring + * Free a pubkey. + * @param key Pubkey to free + * @return NULL always + */ +rpmPubkey rpmPubkeyFree(rpmPubkey key); + +/** \ingroup rpmkeyring + * Reference a pubkey. + * @param key Pubkey + * @return new pubkey reference + */ +rpmPubkey rpmPubkeyLink(rpmPubkey key); + +/** \ingroup rpmkeyring + * Parse OpenPGP pubkey parameters. + * @param key Pubkey + * @return parsed output of pubkey packet parameters + */ +pgpDig rpmPubkeyDig(rpmPubkey key); + +/** \ingroup rpmkeyring + * Return base64 encoding of pubkey + * @param key Pubkey + * @return base64 encoded pubkey (malloced), NULL on error + */ +char * rpmPubkeyBase64(rpmPubkey key); + +#ifdef __cplusplus +} +#endif +#endif /* _RPMKEYDB_H */ diff --git a/rpmio/rpmlog.c b/rpmio/rpmlog.c new file mode 100644 index 0000000..8023d5c --- /dev/null +++ b/rpmio/rpmlog.c @@ -0,0 +1,230 @@ +/** \ingroup rpmio + * \file rpmio/rpmlog.c + */ + +#include "system.h" +#include <stdarg.h> +#include <stdlib.h> +#include <rpm/rpmlog.h> +#include "debug.h" + +static int nrecs = 0; +static rpmlogRec recs = NULL; + +struct rpmlogRec_s { + int code; /* unused */ + rpmlogLvl pri; /* priority */ + char * message; /* log message string */ +}; + +int rpmlogGetNrecs(void) +{ + return nrecs; +} + +int rpmlogCode(void) +{ + if (recs != NULL && nrecs > 0) + return recs[nrecs-1].code; + return -1; +} + + +const char * rpmlogMessage(void) +{ + if (recs != NULL && nrecs > 0) + return recs[nrecs-1].message; + return _("(no error)"); +} + +const char * rpmlogRecMessage(rpmlogRec rec) +{ + assert(rec != NULL); + return (rec->message); +} + +rpmlogLvl rpmlogRecPriority(rpmlogRec rec) +{ + assert(rec != NULL); + return (rec->pri); +} + +void rpmlogPrint(FILE *f) +{ + int i; + + if (f == NULL) + f = stderr; + + if (recs) + for (i = 0; i < nrecs; i++) { + rpmlogRec rec = recs + i; + if (rec->message && *rec->message) + fprintf(f, " %s", rec->message); + } +} + +void rpmlogClose (void) +{ + int i; + + if (recs) + for (i = 0; i < nrecs; i++) { + rpmlogRec rec = recs + i; + rec->message = _free(rec->message); + } + recs = _free(recs); + nrecs = 0; +} + +void rpmlogOpen (const char *ident, int option, + int facility) +{ +} + +static unsigned rpmlogMask = RPMLOG_UPTO( RPMLOG_NOTICE ); + +#ifdef NOTYET +static unsigned rpmlogFacility = RPMLOG_USER; +#endif + +int rpmlogSetMask (int mask) +{ + int omask = rpmlogMask; + if (mask) + rpmlogMask = mask; + return omask; +} + +static rpmlogCallback _rpmlogCallback = NULL; +static rpmlogCallbackData _rpmlogCallbackData = NULL; + +rpmlogCallback rpmlogSetCallback(rpmlogCallback cb, rpmlogCallbackData data) +{ + rpmlogCallback ocb = _rpmlogCallback; + _rpmlogCallback = cb; + _rpmlogCallbackData = data; + return ocb; +} + +static FILE * _stdlog = NULL; + +static int rpmlogDefault(rpmlogRec rec) +{ + FILE *msgout = (_stdlog ? _stdlog : stderr); + + switch (rec->pri) { + case RPMLOG_INFO: + case RPMLOG_NOTICE: + msgout = (_stdlog ? _stdlog : stdout); + break; + case RPMLOG_EMERG: + case RPMLOG_ALERT: + case RPMLOG_CRIT: + case RPMLOG_ERR: + case RPMLOG_WARNING: + case RPMLOG_DEBUG: + default: + break; + } + + (void) fputs(rpmlogLevelPrefix(rec->pri), msgout); + + (void) fputs(rec->message, msgout); + (void) fflush(msgout); + + return (rec->pri <= RPMLOG_CRIT ? RPMLOG_EXIT : 0); +} + + +FILE * rpmlogSetFile(FILE * fp) +{ + FILE * ofp = _stdlog; + _stdlog = fp; + return ofp; +} + +static const char * const rpmlogMsgPrefix[] = { + N_("fatal error: "),/*!< RPMLOG_EMERG */ + N_("fatal error: "),/*!< RPMLOG_ALERT */ + N_("fatal error: "),/*!< RPMLOG_CRIT */ + N_("error: "), /*!< RPMLOG_ERR */ + N_("warning: "), /*!< RPMLOG_WARNING */ + "", /*!< RPMLOG_NOTICE */ + "", /*!< RPMLOG_INFO */ + "D: ", /*!< RPMLOG_DEBUG */ +}; + +const char * rpmlogLevelPrefix(rpmlogLvl pri) +{ + const char * prefix = ""; + if (rpmlogMsgPrefix[pri] && *rpmlogMsgPrefix[pri]) + prefix = _(rpmlogMsgPrefix[pri]); + return prefix; +} + +/* FIX: rpmlogMsgPrefix[] dependent, not unqualified */ +/* FIX: rpmlogMsgPrefix[] may be NULL */ +static void dolog (struct rpmlogRec_s *rec) +{ + int cbrc = RPMLOG_DEFAULT; + int needexit = 0; + + /* Save copy of all messages at warning (or below == "more important"). */ + if (rec->pri <= RPMLOG_WARNING) { + recs = xrealloc(recs, (nrecs+2) * sizeof(*recs)); + recs[nrecs].code = rec->code; + recs[nrecs].pri = rec->pri; + recs[nrecs].message = xstrdup(rec->message); + recs[nrecs+1].code = 0; + recs[nrecs+1].message = NULL; + ++nrecs; + } + + if (_rpmlogCallback) { + cbrc = _rpmlogCallback(rec, _rpmlogCallbackData); + needexit += cbrc & RPMLOG_EXIT; + } + + if (cbrc & RPMLOG_DEFAULT) { + cbrc = rpmlogDefault(rec); + needexit += cbrc & RPMLOG_EXIT; + } + + if (needexit) + exit(EXIT_FAILURE); +} + +void rpmlog (int code, const char *fmt, ...) +{ + unsigned pri = RPMLOG_PRI(code); + unsigned mask = RPMLOG_MASK(pri); + va_list ap; + int n; + + if ((mask & rpmlogMask) == 0) + return; + + va_start(ap, fmt); + n = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (n >= -1) { + struct rpmlogRec_s rec; + size_t nb = n + 1; + char *msg = xmalloc(nb); + + va_start(ap, fmt); + n = vsnprintf(msg, nb, fmt, ap); + va_end(ap); + + rec.code = code; + rec.pri = pri; + rec.message = msg; + + dolog(&rec); + + free(msg); + } +} + diff --git a/rpmio/rpmlog.h b/rpmio/rpmlog.h new file mode 100644 index 0000000..0766086 --- /dev/null +++ b/rpmio/rpmlog.h @@ -0,0 +1,281 @@ +#ifndef H_RPMLOG +#define H_RPMLOG 1 + +/** \ingroup rpmio + * \file rpmio/rpmlog.h + * Yet Another syslog(3) API clone. + * Used to unify rpmError() and rpmMessage() interfaces in rpm. + */ + +#include <stdarg.h> +#include <stdio.h> + +#include <rpm/rpmutil.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmlog + * RPM Log levels. + * priorities/facilities are encoded into a single 32-bit quantity, where the + * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility + * (0-big number). Both the priorities and the facilities map roughly + * one-to-one to strings in the syslogd(8) source code. This mapping is + * included in this file. + * + * priorities (these are ordered) + */ +typedef enum rpmlogLvl_e { + RPMLOG_EMERG = 0, /*!< system is unusable */ + RPMLOG_ALERT = 1, /*!< action must be taken immediately */ + RPMLOG_CRIT = 2, /*!< critical conditions */ + RPMLOG_ERR = 3, /*!< error conditions */ + RPMLOG_WARNING = 4, /*!< warning conditions */ + RPMLOG_NOTICE = 5, /*!< normal but significant condition */ + RPMLOG_INFO = 6, /*!< informational */ + RPMLOG_DEBUG = 7 /*!< debug-level messages */ +} rpmlogLvl; + +#define RPMLOG_PRIMASK 0x07 /* mask to extract priority part (internal) */ + /* extract priority */ +#define RPMLOG_PRI(p) ((p) & RPMLOG_PRIMASK) +#define RPMLOG_MAKEPRI(fac, pri) ((((unsigned)(fac)) << 3) | (pri)) + +#ifdef RPMLOG_NAMES +#define _RPMLOG_NOPRI 0x10 /* the "no priority" priority */ + /* mark "facility" */ +#define _RPMLOG_MARK RPMLOG_MAKEPRI(RPMLOG_NFACILITIES, 0) +typedef struct _rpmcode { + const char *c_name; + int c_val; +} RPMCODE; + +RPMCODE rpmprioritynames[] = + { + { "alert", RPMLOG_ALERT }, + { "crit", RPMLOG_CRIT }, + { "debug", RPMLOG_DEBUG }, + { "emerg", RPMLOG_EMERG }, + { "err", RPMLOG_ERR }, + { "error", RPMLOG_ERR }, /* DEPRECATED */ + { "info", RPMLOG_INFO }, + { "none", _RPMLOG_NOPRI }, /* INTERNAL */ + { "notice", RPMLOG_NOTICE }, + { "panic", RPMLOG_EMERG }, /* DEPRECATED */ + { "warn", RPMLOG_WARNING }, /* DEPRECATED */ + { "warning",RPMLOG_WARNING }, + { NULL, -1 } + }; +#endif + +/** \ingroup rpmlog + * facility codes + */ +typedef enum rpmlogFac_e { + RPMLOG_KERN = (0<<3), /*!< kernel messages */ + RPMLOG_USER = (1<<3), /*!< random user-level messages */ + RPMLOG_MAIL = (2<<3), /*!< mail system */ + RPMLOG_DAEMON = (3<<3), /*!< system daemons */ + RPMLOG_AUTH = (4<<3), /*!< security/authorization messages */ + RPMLOG_SYSLOG = (5<<3), /*!< messages generated internally by syslogd */ + RPMLOG_LPR = (6<<3), /*!< line printer subsystem */ + RPMLOG_NEWS = (7<<3), /*!< network news subsystem */ + RPMLOG_UUCP = (8<<3), /*!< UUCP subsystem */ + RPMLOG_CRON = (9<<3), /*!< clock daemon */ + RPMLOG_AUTHPRIV = (10<<3), /*!< security/authorization messages (private) */ + RPMLOG_FTP = (11<<3), /*!< ftp daemon */ + + /* other codes through 15 reserved for system use */ + RPMLOG_LOCAL0 = (16<<3), /*!< reserved for local use */ + RPMLOG_LOCAL1 = (17<<3), /*!< reserved for local use */ + RPMLOG_LOCAL2 = (18<<3), /*!< reserved for local use */ + RPMLOG_LOCAL3 = (19<<3), /*!< reserved for local use */ + RPMLOG_LOCAL4 = (20<<3), /*!< reserved for local use */ + RPMLOG_LOCAL5 = (21<<3), /*!< reserved for local use */ + RPMLOG_LOCAL6 = (22<<3), /*!< reserved for local use */ + RPMLOG_LOCAL7 = (23<<3), /*!< reserved for local use */ + +#define RPMLOG_NFACILITIES 24 /*!< current number of facilities */ + RPMLOG_ERRMSG = (((unsigned)(RPMLOG_NFACILITIES+0))<<3) +} rpmlogFac; + +#define RPMLOG_FACMASK 0x03f8 /*!< mask to extract facility part */ +#define RPMLOG_FAC(p) (((p) & RPMLOG_FACMASK) >> 3) + + +#ifdef RPMLOG_NAMES +RPMCODE facilitynames[] = + { + { "auth", RPMLOG_AUTH }, + { "authpriv",RPMLOG_AUTHPRIV }, + { "cron", RPMLOG_CRON }, + { "daemon", RPMLOG_DAEMON }, + { "ftp", RPMLOG_FTP }, + { "kern", RPMLOG_KERN }, + { "lpr", RPMLOG_LPR }, + { "mail", RPMLOG_MAIL }, + { "mark", _RPMLOG_MARK }, /* INTERNAL */ + { "news", RPMLOG_NEWS }, + { "security",RPMLOG_AUTH }, /* DEPRECATED */ + { "syslog", RPMLOG_SYSLOG }, + { "user", RPMLOG_USER }, + { "uucp", RPMLOG_UUCP }, + { "local0", RPMLOG_LOCAL0 }, + { "local1", RPMLOG_LOCAL1 }, + { "local2", RPMLOG_LOCAL2 }, + { "local3", RPMLOG_LOCAL3 }, + { "local4", RPMLOG_LOCAL4 }, + { "local5", RPMLOG_LOCAL5 }, + { "local6", RPMLOG_LOCAL6 }, + { "local7", RPMLOG_LOCAL7 }, + { NULL, -1 } + }; +#endif + +/* + * arguments to setlogmask. + */ +#define RPMLOG_MASK(pri) (1 << ((unsigned)(pri))) /*!< mask for one priority */ +#define RPMLOG_UPTO(pri) ((1 << (((unsigned)(pri))+1)) - 1) /*!< all priorities through pri */ + +/* + * Option flags for openlog. + * + * RPMLOG_ODELAY no longer does anything. + * RPMLOG_NDELAY is the inverse of what it used to be. + */ +#define RPMLOG_PID 0x01 /*!< log the pid with each message */ +#define RPMLOG_CONS 0x02 /*!< log on the console if errors in sending */ +#define RPMLOG_ODELAY 0x04 /*!< delay open until first syslog() (default) */ +#define RPMLOG_NDELAY 0x08 /*!< don't delay open */ +#define RPMLOG_NOWAIT 0x10 /*!< don't wait for console forks: DEPRECATED */ +#define RPMLOG_PERROR 0x20 /*!< log to stderr as well */ + +/* \ingroup rpmlog + * Option flags for callback return value. + */ +#define RPMLOG_DEFAULT 0x01 /*!< perform default logging */ +#define RPMLOG_EXIT 0x02 /*!< exit after logging */ + +/** \ingroup rpmlog + */ +typedef struct rpmlogRec_s * rpmlogRec; + +/** \ingroup rpmlog + * Retrieve log message string from rpmlog record + * @param rec rpmlog record + * @return log message + */ +const char * rpmlogRecMessage(rpmlogRec rec); + +/** \ingroup rpmlog + * Retrieve log priority from rpmlog record + * @param rec rpmlog record + * @return log priority + */ +rpmlogLvl rpmlogRecPriority(rpmlogRec rec); + +typedef void * rpmlogCallbackData; + +/** \ingroup rpmlog + * @param rec rpmlog record + * @param data private callback data + * @return flags to define further behavior: + * RPMLOG_DEFAULT to perform default logging, + * RPMLOG_EXIT to exit after processing, + * 0 to return after callback + */ +typedef int (*rpmlogCallback) (rpmlogRec rec, rpmlogCallbackData data); + +/** \ingroup rpmlog + * Return number of rpmError() ressages. + * @return number of messages + */ +int rpmlogGetNrecs(void) ; + +/** \ingroup rpmlog + * Print all rpmError() messages. + * @param f file handle (NULL uses stderr) + */ +void rpmlogPrint(FILE *f); + +/** \ingroup rpmlog + * Close desriptor used to write to system logger. + * @todo Implement. + */ +void rpmlogClose (void); + +/** \ingroup rpmlog + * Open connection to system logger. + * @todo Implement. + */ +void rpmlogOpen (const char * ident, int option, int facility); + +/** \ingroup rpmlog + * Set the log mask level. + * @param mask log mask (0 is no operation) + * @return previous log mask + */ +int rpmlogSetMask (int mask); + +/** \ingroup rpmlog + * Generate a log message using FMT string and option arguments. + */ +void rpmlog (int code, const char *fmt, ...) RPM_GNUC_PRINTF(2, 3); + +/** \ingroup rpmlog + * Return text of last rpmError() message. + * @return text of last message + */ +const char * rpmlogMessage(void); + +/** \ingroup rpmlog + * Return error code from last rpmError() message. + * @deprecated Perl-RPM needs, what's really needed is predictable, non-i18n + * encumbered, error text that can be retrieved through rpmlogMessage() + * and parsed IMHO. + * @return code from last message + */ +int rpmlogCode(void); + +/** \ingroup rpmlog + * Return translated prefix string (if any) given log level. + * @param pri log priority + * @return message prefix (or "" for none) + */ +const char * rpmlogLevelPrefix(rpmlogLvl pri); + +/** \ingroup rpmlog + * Set rpmlog callback function. + * @param cb rpmlog callback function + * @param data callback private (user) data + * @return previous rpmlog callback function + */ +rpmlogCallback rpmlogSetCallback(rpmlogCallback cb, rpmlogCallbackData data); + +/** \ingroup rpmlog + * Set rpmlog file handle. + * @param fp rpmlog file handle (NULL uses stdout/stderr) + * @return previous rpmlog file handle + */ +FILE * rpmlogSetFile(FILE * fp); + +#define rpmSetVerbosity(_lvl) \ + ((void)rpmlogSetMask( RPMLOG_UPTO( RPMLOG_PRI(_lvl)))) +#define rpmIncreaseVerbosity() \ + ((void)rpmlogSetMask(((((unsigned)(rpmlogSetMask(0) & 0xff)) << 1) | 1))) +#define rpmDecreaseVerbosity() \ + ((void)rpmlogSetMask((((int)(rpmlogSetMask(0) & 0xff)) >> 1))) +#define rpmIsNormal() \ + (rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_NOTICE )) +#define rpmIsVerbose() \ + (rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) +#define rpmIsDebug() \ + (rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_DEBUG )) + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMLOG */ diff --git a/rpmio/rpmlua.c b/rpmio/rpmlua.c new file mode 100644 index 0000000..fa8eed3 --- /dev/null +++ b/rpmio/rpmlua.c @@ -0,0 +1,844 @@ +#include "system.h" + +#ifdef WITH_LUA +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> +#include <lposix.h> +#include <lrexlib.h> + +#include <unistd.h> +#include <assert.h> + +#include <rpm/rpmio.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmlog.h> +#include <rpm/rpmurl.h> +#include <rpm/rpmfileutil.h> +#include "rpmio/rpmhook.h" +#include "rpmio/base64.h" + +#define _RPMLUA_INTERNAL +#include "rpmio/rpmlua.h" + +#include "debug.h" + +#define INITSTATE(_lua, lua) \ + rpmlua lua = _lua ? _lua : \ + (globalLuaState ? globalLuaState : \ + \ + (globalLuaState = rpmluaNew()) \ + \ + ) + +struct rpmluapb_s { + size_t alloced; + size_t used; + char *buf; + rpmluapb next; +}; + +static rpmlua globalLuaState = NULL; + +static int luaopen_rpm(lua_State *L); +static int rpm_print(lua_State *L); + +rpmlua rpmluaGetGlobalState(void) +{ + INITSTATE(NULL, lua); + return lua; +} + +rpmlua rpmluaNew() +{ + rpmlua lua = (rpmlua) xcalloc(1, sizeof(*lua)); + struct stat st; + const luaL_reg *lib; + char *initlua = rpmGenPath(rpmConfigDir(), "init.lua", NULL); + + static const luaL_reg extlibs[] = { + {"posix", luaopen_posix}, + {"rex", luaopen_rex}, + {"rpm", luaopen_rpm}, + {"os", luaopen_rpm_os}, + {NULL, NULL}, + }; + + lua_State *L = lua_open(); + luaL_openlibs(L); + lua->L = L; + + for (lib = extlibs; lib->name; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + lua_settop(L, 0); + } + lua_pushliteral(L, "LUA_PATH"); + lua_pushfstring(L, "%s/%s", rpmConfigDir(), "/lua/?.lua"); + lua_rawset(L, LUA_GLOBALSINDEX); + lua_pushliteral(L, "print"); + lua_pushcfunction(L, rpm_print); + lua_rawset(L, LUA_GLOBALSINDEX); + rpmluaSetData(lua, "lua", lua); + if (stat(initlua, &st) != -1) + (void)rpmluaRunScriptFile(lua, initlua); + free(initlua); + return lua; +} + +rpmlua rpmluaFree(rpmlua lua) +{ + if (lua) { + if (lua->L) lua_close(lua->L); + free(lua->printbuf); + free(lua); + if (lua == globalLuaState) globalLuaState = NULL; + } + return NULL; +} + +void rpmluaSetData(rpmlua _lua, const char *key, const void *data) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + lua_pushliteral(L, "rpm_"); + lua_pushstring(L, key); + lua_concat(L, 2); + if (data == NULL) + lua_pushnil(L); + else + lua_pushlightuserdata(L, (void *)data); + lua_rawset(L, LUA_REGISTRYINDEX); +} + +static void *getdata(lua_State *L, const char *key) +{ + void *ret = NULL; + lua_pushliteral(L, "rpm_"); + lua_pushstring(L, key); + lua_concat(L, 2); + lua_rawget(L, LUA_REGISTRYINDEX); + if (lua_islightuserdata(L, -1)) + ret = lua_touserdata(L, -1); + lua_pop(L, 1); + return ret; +} + +void *rpmluaGetData(rpmlua _lua, const char *key) +{ + INITSTATE(_lua, lua); + return getdata(lua->L, key); +} + +void rpmluaPushPrintBuffer(rpmlua _lua) +{ + INITSTATE(_lua, lua); + rpmluapb prbuf = xcalloc(1, sizeof(*prbuf)); + prbuf->buf = NULL; + prbuf->alloced = 0; + prbuf->used = 0; + prbuf->next = lua->printbuf; + + lua->printbuf = prbuf; +} + +char *rpmluaPopPrintBuffer(rpmlua _lua) +{ + INITSTATE(_lua, lua); + rpmluapb prbuf = lua->printbuf; + char *ret = NULL; + + if (prbuf) { + ret = prbuf->buf; + lua->printbuf = prbuf->next; + free(prbuf); + } + + return ret; +} + +static int pushvar(lua_State *L, rpmluavType type, void *value) +{ + int ret = 0; + switch (type) { + case RPMLUAV_NIL: + lua_pushnil(L); + break; + case RPMLUAV_STRING: + lua_pushstring(L, *((char **)value)); + break; + case RPMLUAV_NUMBER: + lua_pushnumber(L, *((double *)value)); + break; + default: + ret = -1; + break; + } + return ret; +} + +void rpmluaSetVar(rpmlua _lua, rpmluav var) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + if (var->listmode && lua->pushsize > 0) { + if (var->keyType != RPMLUAV_NUMBER || var->key.num == (double)0) { + var->keyType = RPMLUAV_NUMBER; + var->key.num = (double) luaL_getn(L, -1); + } + var->key.num++; + } + if (!var->listmode || lua->pushsize > 0) { + if (lua->pushsize == 0) + lua_pushvalue(L, LUA_GLOBALSINDEX); + if (pushvar(L, var->keyType, &var->key) != -1) { + if (pushvar(L, var->valueType, &var->value) != -1) + lua_rawset(L, -3); + else + lua_pop(L, 1); + } + if (lua->pushsize == 0) + lua_pop(L, 1); + } +} + +static void popvar(lua_State *L, rpmluavType *type, void *value) +{ + switch (lua_type(L, -1)) { + case LUA_TSTRING: + *type = RPMLUAV_STRING; + *((const char **)value) = lua_tostring(L, -1); + break; + case LUA_TNUMBER: + *type = RPMLUAV_NUMBER; + *((double *)value) = lua_tonumber(L, -1); + break; + default: + *type = RPMLUAV_NIL; + *((void **)value) = NULL; + break; + } + lua_pop(L, 1); +} + +void rpmluaGetVar(rpmlua _lua, rpmluav var) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + if (!var->listmode) { + if (lua->pushsize == 0) + lua_pushvalue(L, LUA_GLOBALSINDEX); + if (pushvar(L, var->keyType, &var->key) != -1) { + lua_rawget(L, -2); + popvar(L, &var->valueType, &var->value); + } + if (lua->pushsize == 0) + lua_pop(L, 1); + } else if (lua->pushsize > 0) { + (void) pushvar(L, var->keyType, &var->key); + if (lua_next(L, -2) != 0) + popvar(L, &var->valueType, &var->value); + } +} + +#define FINDKEY_RETURN 0 +#define FINDKEY_CREATE 1 +#define FINDKEY_REMOVE 2 +static int findkey(lua_State *L, int oper, const char *key, va_list va) +{ + char *buf; + const char *s, *e; + int ret = 0; + int blen; + + blen = vsnprintf(NULL, 0, key, va); + if (blen <= 0) { + return -1; + } + + buf = xmalloc(blen + 1); + vsnprintf(buf, blen + 1, key, va); + + s = e = buf; + lua_pushvalue(L, LUA_GLOBALSINDEX); + for (;;) { + if (*e == '\0' || *e == '.') { + if (e != s) { + lua_pushlstring(L, s, e-s); + switch (oper) { + case FINDKEY_REMOVE: + if (*e == '\0') { + lua_pushnil(L); + lua_rawset(L, -3); + lua_pop(L, 1); + break; + } + case FINDKEY_RETURN: + lua_rawget(L, -2); + lua_remove(L, -2); + break; + case FINDKEY_CREATE: + lua_rawget(L, -2); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushlstring(L, s, e-s); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + lua_remove(L, -2); + break; + } + } + if (*e == '\0') + break; + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + ret = -1; + break; + } + s = e+1; + } + e++; + } + free(buf); + + return ret; +} + +void rpmluaDelVar(rpmlua _lua, const char *key, ...) +{ + INITSTATE(_lua, lua); + va_list va; + va_start(va, key); + (void) findkey(lua->L, FINDKEY_REMOVE, key, va); + va_end(va); +} + +int rpmluaVarExists(rpmlua _lua, const char *key, ...) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + int ret = 0; + va_list va; + va_start(va, key); + if (findkey(L, FINDKEY_RETURN, key, va) == 0) { + if (!lua_isnil(L, -1)) + ret = 1; + lua_pop(L, 1); + } + va_end(va); + return ret; +} + +void rpmluaPushTable(rpmlua _lua, const char *key, ...) +{ + INITSTATE(_lua, lua); + va_list va; + va_start(va, key); + (void) findkey(lua->L, FINDKEY_CREATE, key, va); + lua->pushsize++; + va_end(va); +} + +void rpmluaPop(rpmlua _lua) +{ + INITSTATE(_lua, lua); + assert(lua->pushsize > 0); + lua->pushsize--; + lua_pop(lua->L, 1); +} + +rpmluav rpmluavNew(void) +{ + rpmluav var = (rpmluav) xcalloc(1, sizeof(*var)); + return var; +} + +rpmluav rpmluavFree(rpmluav var) +{ + free(var); + return NULL; +} + +void rpmluavSetListMode(rpmluav var, int flag) +{ + var->listmode = flag; + var->keyType = RPMLUAV_NIL; +} + +void rpmluavSetKey(rpmluav var, rpmluavType type, const void *value) +{ + var->keyType = type; + switch (type) { + case RPMLUAV_NUMBER: + var->key.num = *((double *)value); + break; + case RPMLUAV_STRING: + var->key.str = (char *)value; + break; + default: + break; + } +} + +void rpmluavSetValue(rpmluav var, rpmluavType type, const void *value) +{ + var->valueType = type; + switch (type) { + case RPMLUAV_NUMBER: + var->value.num = *((const double *)value); + break; + case RPMLUAV_STRING: + var->value.str = (const char *)value; + break; + default: + break; + } +} + +void rpmluavGetKey(rpmluav var, rpmluavType *type, void **value) +{ + *type = var->keyType; + switch (var->keyType) { + case RPMLUAV_NUMBER: + *((double **)value) = &var->key.num; + break; + case RPMLUAV_STRING: + *((const char **)value) = var->key.str; + break; + default: + break; + } +} + +void rpmluavGetValue(rpmluav var, rpmluavType *type, void **value) +{ + *type = var->valueType; + switch (var->valueType) { + case RPMLUAV_NUMBER: + *((double **)value) = &var->value.num; + break; + case RPMLUAV_STRING: + *((const char **)value) = var->value.str; + break; + default: + break; + } +} + +void rpmluavSetKeyNum(rpmluav var, double value) +{ + rpmluavSetKey(var, RPMLUAV_NUMBER, &value); +} + +void rpmluavSetValueNum(rpmluav var, double value) +{ + rpmluavSetValue(var, RPMLUAV_NUMBER, &value); +} + +double rpmluavGetKeyNum(rpmluav var) +{ + rpmluavType type; + void *value; + rpmluavGetKey(var, &type, &value); + if (type == RPMLUAV_NUMBER) + return *((double *)value); + return (double) 0; +} + +double rpmluavGetValueNum(rpmluav var) +{ + rpmluavType type; + void *value; + rpmluavGetValue(var, &type, &value); + if (type == RPMLUAV_NUMBER) + return *((double *)value); + return (double) 0; +} + +int rpmluavKeyIsNum(rpmluav var) +{ + return (var->keyType == RPMLUAV_NUMBER) ? 1 : 0; +} + +int rpmluavValueIsNum(rpmluav var) +{ + return (var->valueType == RPMLUAV_NUMBER) ? 1 : 0; +} + +int rpmluaCheckScript(rpmlua _lua, const char *script, const char *name) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + int ret = 0; + if (name == NULL) + name = "<lua>"; + if (luaL_loadbuffer(L, script, strlen(script), name) != 0) { + rpmlog(RPMLOG_ERR, + _("invalid syntax in lua scriptlet: %s\n"), + lua_tostring(L, -1)); + ret = -1; + } + lua_pop(L, 1); /* Error or chunk. */ + return ret; +} + +int rpmluaRunScript(rpmlua _lua, const char *script, const char *name) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + int ret = 0; + if (name == NULL) + name = "<lua>"; + if (luaL_loadbuffer(L, script, strlen(script), name) != 0) { + rpmlog(RPMLOG_ERR, _("invalid syntax in lua script: %s\n"), + lua_tostring(L, -1)); + lua_pop(L, 1); + ret = -1; + } else if (lua_pcall(L, 0, 0, 0) != 0) { + rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"), + lua_tostring(L, -1)); + lua_pop(L, 1); + ret = -1; + } + return ret; +} + +int rpmluaRunScriptFile(rpmlua _lua, const char *filename) +{ + INITSTATE(_lua, lua); + lua_State *L = lua->L; + int ret = 0; + if (luaL_loadfile(L, filename) != 0) { + rpmlog(RPMLOG_ERR, _("invalid syntax in lua file: %s\n"), + lua_tostring(L, -1)); + lua_pop(L, 1); + ret = -1; + } else if (lua_pcall(L, 0, 0, 0) != 0) { + rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"), + lua_tostring(L, -1)); + lua_pop(L, 1); + ret = -1; + } + return ret; +} + +/* From lua.c */ +static int rpmluaReadline(lua_State *L, const char *prompt) +{ + static char buffer[1024]; + if (prompt) { + (void) fputs(prompt, stdout); + (void) fflush(stdout); + } + if (fgets(buffer, sizeof(buffer), stdin) == NULL) { + return 0; /* read fails */ + } else { + lua_pushstring(L, buffer); + return 1; + } +} + +/* Based on lua.c */ +static void _rpmluaInteractive(lua_State *L) +{ + (void) fputs("\n", stdout); + printf("RPM Interactive %s Interpreter\n", LUA_VERSION); + for (;;) { + int rc = 0; + + if (rpmluaReadline(L, "> ") == 0) + break; + if (lua_tostring(L, -1)[0] == '=') { + (void) lua_pushfstring(L, "print(%s)", lua_tostring(L, -1)+1); + lua_remove(L, -2); + } + for (;;) { + rc = luaL_loadbuffer(L, lua_tostring(L, -1), + lua_strlen(L, -1), "<lua>"); + if (rc == LUA_ERRSYNTAX && + strstr(lua_tostring(L, -1), "near `<eof>'") != NULL) { + if (rpmluaReadline(L, ">> ") == 0) + break; + lua_remove(L, -2); /* Remove error */ + lua_concat(L, 2); + continue; + } + break; + } + if (rc == 0) + rc = lua_pcall(L, 0, 0, 0); + if (rc != 0) { + fprintf(stderr, "%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); + } + lua_pop(L, 1); /* Remove line */ + } + (void) fputs("\n", stdout); +} + +void rpmluaInteractive(rpmlua _lua) +{ + INITSTATE(_lua, lua); + _rpmluaInteractive(lua->L); +} + +/* ------------------------------------------------------------------ */ +/* Lua API */ + +static int rpm_b64encode(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + size_t len = lua_strlen(L, 1); + int linelen = -1; + if (lua_gettop(L) == 2) + linelen = luaL_checkinteger(L, 2); + if (str && len) { + char *data = b64encode(str, len, linelen); + lua_pushstring(L, data); + free(data); + } + return 1; +} + +static int rpm_b64decode(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + if (str) { + void *data = NULL; + size_t len = 0; + if (b64decode(str, &data, &len) == 0) { + lua_pushlstring(L, data, len); + } else { + lua_pushnil(L); + } + free(data); + } + return 1; +} + +static int rpm_expand(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + char *val = rpmExpand(str, NULL); + lua_pushstring(L, val); + free(val); + return 1; +} + +static int rpm_define(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + (void) rpmDefineMacro(NULL, str, 0); + return 0; +} + +static int rpm_interactive(lua_State *L) +{ + _rpmluaInteractive(L); + return 0; +} + +typedef struct rpmluaHookData_s { + lua_State *L; + int funcRef; + int dataRef; +} * rpmluaHookData; + +static int rpmluaHookWrapper(rpmhookArgs args, void *data) +{ + rpmluaHookData hookdata = (rpmluaHookData)data; + lua_State *L = hookdata->L; + int ret = 0; + int i; + lua_rawgeti(L, LUA_REGISTRYINDEX, hookdata->funcRef); + lua_newtable(L); + for (i = 0; i != args->argc; i++) { + switch (args->argt[i]) { + case 's': + lua_pushstring(L, args->argv[i].s); + lua_rawseti(L, -2, i+1); + break; + case 'i': + lua_pushnumber(L, (lua_Number)args->argv[i].i); + lua_rawseti(L, -2, i+1); + break; + case 'f': + lua_pushnumber(L, (lua_Number)args->argv[i].f); + lua_rawseti(L, -2, i+1); + break; + case 'p': + lua_pushlightuserdata(L, args->argv[i].p); + lua_rawseti(L, -2, i+1); + break; + default: + (void) luaL_error(L, "unsupported type '%c' as " + "a hook argument\n", args->argt[i]); + break; + } + } + if (lua_pcall(L, 1, 1, 0) != 0) { + rpmlog(RPMLOG_ERR, _("lua hook failed: %s\n"), + lua_tostring(L, -1)); + lua_pop(L, 1); + } else { + if (lua_isnumber(L, -1)) + ret = (int)lua_tonumber(L, -1); + lua_pop(L, 1); + } + return ret; +} + +static int rpm_register(lua_State *L) +{ + if (!lua_isstring(L, 1)) { + (void) luaL_argerror(L, 1, "hook name expected"); + } else if (!lua_isfunction(L, 2)) { + (void) luaL_argerror(L, 2, "function expected"); + } else { + rpmluaHookData hookdata = + lua_newuserdata(L, sizeof(struct rpmluaHookData_s)); + lua_pushvalue(L, -1); + hookdata->dataRef = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, 2); + hookdata->funcRef = luaL_ref(L, LUA_REGISTRYINDEX); + hookdata->L = L; + rpmhookRegister(lua_tostring(L, 1), rpmluaHookWrapper, hookdata); + return 1; + } + return 0; +} + +static int rpm_unregister(lua_State *L) +{ + if (!lua_isstring(L, 1)) { + (void) luaL_argerror(L, 1, "hook name expected"); + } else if (!lua_isuserdata(L, 2)) { + (void) luaL_argerror(L, 2, "hook information expected"); + } else { + rpmluaHookData hookdata = (rpmluaHookData)lua_touserdata(L, 2); + luaL_unref(L, LUA_REGISTRYINDEX, hookdata->funcRef); + luaL_unref(L, LUA_REGISTRYINDEX, hookdata->dataRef); + rpmhookUnregister(lua_tostring(L, 1), rpmluaHookWrapper, hookdata); + } + return 0; +} + +static int rpm_call(lua_State *L) +{ + if (!lua_isstring(L, 1)) { + (void) luaL_argerror(L, 1, "hook name expected"); + } else { + rpmhookArgs args = rpmhookArgsNew(lua_gettop(L)-1); + const char *name = lua_tostring(L, 1); + char *argt = (char *)xmalloc(args->argc+1); + int i; + for (i = 0; i != args->argc; i++) { + switch (lua_type(L, i+1)) { + case LUA_TNIL: + argt[i] = 'p'; + args->argv[i].p = NULL; + break; + case LUA_TNUMBER: { + float f = (float)lua_tonumber(L, i+1); + if (f == (int)f) { + argt[i] = 'i'; + args->argv[i].i = (int)f; + } else { + argt[i] = 'f'; + args->argv[i].f = f; + } + } break; + case LUA_TSTRING: + argt[i] = 's'; + args->argv[i].s = lua_tostring(L, i+1); + break; + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + argt[i] = 'p'; + args->argv[i].p = lua_touserdata(L, i+1); + break; + default: + (void) luaL_error(L, "unsupported Lua type passed to hook"); + argt[i] = 'p'; + args->argv[i].p = NULL; + break; + } + } + args->argt = argt; + rpmhookCallArgs(name, args); + free(argt); + (void) rpmhookArgsFree(args); + } + return 0; +} + +/* Based on luaB_print. */ +static int rpm_print (lua_State *L) +{ + rpmlua lua = (rpmlua)getdata(L, "lua"); + int n = lua_gettop(L); /* number of arguments */ + int i; + if (!lua) return 0; + lua_getglobal(L, "tostring"); + for (i = 1; i <= n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, "`tostring' must return a string to `print'"); + if (lua->printbuf) { + rpmluapb prbuf = lua->printbuf; + int sl = lua_strlen(L, -1); + if (prbuf->used+sl+1 > prbuf->alloced) { + prbuf->alloced += sl+512; + prbuf->buf = xrealloc(prbuf->buf, prbuf->alloced); + } + if (i > 1) + prbuf->buf[prbuf->used++] = '\t'; + memcpy(prbuf->buf+prbuf->used, s, sl+1); + prbuf->used += sl; + } else { + if (i > 1) + (void) fputs("\t", stdout); + (void) fputs(s, stdout); + } + lua_pop(L, 1); /* pop result */ + } + if (!lua->printbuf) { + (void) fputs("\n", stdout); + } else { + rpmluapb prbuf = lua->printbuf; + if (prbuf->used+1 > prbuf->alloced) { + prbuf->alloced += 512; + prbuf->buf = xrealloc(prbuf->buf, prbuf->alloced); + } + prbuf->buf[prbuf->used] = '\0'; + } + return 0; +} + +static const luaL_reg rpmlib[] = { + {"b64encode", rpm_b64encode}, + {"b64decode", rpm_b64decode}, + {"expand", rpm_expand}, + {"define", rpm_define}, + {"register", rpm_register}, + {"unregister", rpm_unregister}, + {"call", rpm_call}, + {"interactive", rpm_interactive}, + {NULL, NULL} +}; + +static int luaopen_rpm(lua_State *L) +{ + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_openlib(L, "rpm", rpmlib, 0); + return 0; +} +#endif /* WITH_LUA */ + diff --git a/rpmio/rpmlua.h b/rpmio/rpmlua.h new file mode 100644 index 0000000..7298ed5 --- /dev/null +++ b/rpmio/rpmlua.h @@ -0,0 +1,92 @@ +#ifndef RPMLUA_H +#define RPMLUA_H + +typedef enum rpmluavType_e { + RPMLUAV_NIL = 0, + RPMLUAV_STRING = 1, + RPMLUAV_NUMBER = 2 +} rpmluavType; + +#if defined(_RPMLUA_INTERNAL) + +#include <stdarg.h> +#include <lua.h> + +typedef struct rpmluapb_s * rpmluapb; + +struct rpmlua_s { + lua_State *L; + size_t pushsize; + rpmluapb printbuf; +}; + +struct rpmluav_s { + rpmluavType keyType; + rpmluavType valueType; + union { + const char *str; + const void *ptr; + double num; + } key; + union { + const char *str; + const void *ptr; + double num; + } value; + int listmode; +}; + +#endif /* _RPMLUA_INTERNAL */ + +typedef struct rpmlua_s * rpmlua; +typedef struct rpmluav_s * rpmluav; + +#ifdef __cplusplus +extern "C" { +#endif + +rpmlua rpmluaNew(void); +rpmlua rpmluaFree(rpmlua lua); +rpmlua rpmluaGetGlobalState(void); + +int rpmluaCheckScript(rpmlua lua, const char *script, + const char *name); +int rpmluaRunScript(rpmlua lua, const char *script, + const char *name); +int rpmluaRunScriptFile(rpmlua lua, const char *filename); +void rpmluaInteractive(rpmlua lua); + +void *rpmluaGetData(rpmlua lua, const char *key); +void rpmluaSetData(rpmlua lua, const char *key, const void *data); + +char *rpmluaPopPrintBuffer(rpmlua lua); +void rpmluaPushPrintBuffer(rpmlua lua); + +void rpmluaGetVar(rpmlua lua, rpmluav var); +void rpmluaSetVar(rpmlua lua, rpmluav var); +void rpmluaDelVar(rpmlua lua, const char *key, ...); +int rpmluaVarExists(rpmlua lua, const char *key, ...); +void rpmluaPushTable(rpmlua lua, const char *key, ...); +void rpmluaPop(rpmlua lua); + +rpmluav rpmluavNew(void); +rpmluav rpmluavFree(rpmluav var); +void rpmluavSetListMode(rpmluav var, int flag); +void rpmluavSetKey(rpmluav var, rpmluavType type, const void *value); +void rpmluavSetValue(rpmluav var, rpmluavType type, const void *value); +void rpmluavGetKey(rpmluav var, rpmluavType *type, void **value); +void rpmluavGetValue(rpmluav var, rpmluavType *type, void **value); + +/* Optional helpers for numbers. */ +void rpmluavSetKeyNum(rpmluav var, double value); +void rpmluavSetValueNum(rpmluav var, double value); +double rpmluavGetKeyNum(rpmluav var); +double rpmluavGetValueNum(rpmluav var); +int rpmluavKeyIsNum(rpmluav var); +int rpmluavValueIsNum(rpmluav var); + +#ifdef __cplusplus +} +#endif + +#endif /* RPMLUA_H */ diff --git a/rpmio/rpmmacro.h b/rpmio/rpmmacro.h new file mode 100644 index 0000000..765c78c --- /dev/null +++ b/rpmio/rpmmacro.h @@ -0,0 +1,156 @@ +#ifndef _H_MACRO_ +#define _H_MACRO_ + +/** \ingroup rpmio + * \file rpmio/rpmmacro.h + */ + +#include <stdio.h> +#include <stddef.h> + +#include <rpm/rpmutil.h> +#include <rpm/rpmfileutil.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct rpmMacroEntry_s * rpmMacroEntry; + +typedef struct rpmMacroContext_s * rpmMacroContext; + +extern rpmMacroContext rpmGlobalMacroContext; + +extern rpmMacroContext rpmCLIMacroContext; + +/** \ingroup rpmrc + * List of macro files to read when configuring rpm. + * This is a colon separated list of files. URI's are permitted as well, + * identified by the token '://', so file paths must not begin with '//'. + */ +extern const char * macrofiles; + +/** + * Markers for sources of macros added throughout rpm. + */ +#define RMIL_DEFAULT -15 +#define RMIL_MACROFILES -13 +#define RMIL_RPMRC -11 + +#define RMIL_CMDLINE -7 +#define RMIL_TARBALL -5 +#define RMIL_SPEC -3 +#define RMIL_OLDSPEC -1 +#define RMIL_GLOBAL 0 + +/** \ingroup rpmmacro + * Print macros to file stream. + * @param mc macro context (NULL uses global context). + * @param fp file stream (NULL uses stderr). + */ +void rpmDumpMacroTable (rpmMacroContext mc, + FILE * fp); + +/** \ingroup rpmmacro + * Expand macro into buffer. + * @deprecated Use rpmExpand(). + * @todo Eliminate from API. + * @param spec cookie (unused) + * @param mc macro context (NULL uses global context). + * @retval sbuf input macro to expand, output expansion + * @param slen size of buffer + * @return 0 on success + */ +int expandMacros (void * spec, rpmMacroContext mc, + char * sbuf, + size_t slen); + +/** \ingroup rpmmacro + * Add macro to context. + * @deprecated Use rpmDefineMacro(). + * @param mc macro context (NULL uses global context). + * @param n macro name + * @param o macro paramaters + * @param b macro body + * @param level macro recursion level (0 is entry API) + */ +void addMacro (rpmMacroContext mc, const char * n, + const char * o, + const char * b, int level); + +/** \ingroup rpmmacro + * Delete macro from context. + * @param mc macro context (NULL uses global context). + * @param n macro name + */ +void delMacro (rpmMacroContext mc, const char * n); + +/** \ingroup rpmmacro + * Define macro in context. + * @param mc macro context (NULL uses global context). + * @param macro macro name, options, body + * @param level macro recursion level (0 is entry API) + * @return 0 on success (always) + */ +int rpmDefineMacro (rpmMacroContext mc, const char * macro, + int level); + +/** \ingroup rpmmacro + * Load macros from specific context into global context. + * @param mc macro context (NULL does nothing). + * @param level macro recursion level (0 is entry API) + */ +void rpmLoadMacros (rpmMacroContext mc, int level); + +/** \ingroup rpmmacro + * Load macro context from a macro file. + * @param mc (unused) + * @param fn macro file name + */ +int rpmLoadMacroFile(rpmMacroContext mc, const char * fn); + +/** \ingroup rpmmacro + * Initialize macro context from set of macrofile(s). + * @param mc macro context + * @param macrofiles colon separated list of macro files (NULL does nothing) + */ +void rpmInitMacros (rpmMacroContext mc, const char * macrofiles); + +/** \ingroup rpmmacro + * Destroy macro context. + * @param mc macro context (NULL uses global context). + */ +void rpmFreeMacros (rpmMacroContext mc); + +/** \ingroup rpmmacro + * Return (malloc'ed) concatenated macro expansion(s). + * @param arg macro(s) to expand (NULL terminates list) + * @return macro expansion (malloc'ed) + */ +char * rpmExpand (const char * arg, ...) RPM_GNUC_NULL_TERMINATED; + +/** \ingroup rpmmacro + * Return macro expansion as a numeric value. + * Boolean values ('Y' or 'y' returns 1, 'N' or 'n' returns 0) + * are permitted as well. An undefined macro returns 0. + * @param arg macro to expand + * @return numeric value + */ +int rpmExpandNumeric (const char * arg); + +/** \ingroup rpmmacro + * Return rpm configuration base directory. + * If RPM_CONFIGDIR environment variable is set, it's value will be used. + * Otherwise the configuration directory is the one set at build time, + * typically /usr/lib/rpm. The value of rpmConfigDir() is determined + * on first call to this function and is guaranteed to remain the same + * on subsequent calls. + * @return rpm configuration directory name + */ +const char *rpmConfigDir(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _H_ MACRO_ */ diff --git a/rpmio/rpmmalloc.c b/rpmio/rpmmalloc.c new file mode 100644 index 0000000..c38ab96 --- /dev/null +++ b/rpmio/rpmmalloc.c @@ -0,0 +1,85 @@ +/** \ingroup rpmio + * \file rpmio/rpmmalloc.c + */ + +#include "system.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "debug.h" + +static rpmMemFailFunc failfunc = NULL; +static void *failfunc_data = NULL; + +/* + * Give memfail callback a chance to try to give us memory or perform + * it's own cleanup. If we dont get memory we die anyway as rpm doesn't + * check for NULL returns from allocations. + */ +static void *vmefail(size_t size) +{ + void *val = failfunc ? (*failfunc)(size, failfunc_data) : NULL; + if (val == NULL) { + fprintf(stderr, _("memory alloc (%u bytes) returned NULL.\n"), + (unsigned)size); + exit(EXIT_FAILURE); + } + return val; +} + +rpmMemFailFunc rpmSetMemFail(rpmMemFailFunc func, void *data) +{ + rpmMemFailFunc ofunc = failfunc; + failfunc = func; + failfunc_data = data; + return ofunc; +} + +void * rmalloc (size_t size) +{ + register void *value; + if (size == 0) size++; + value = malloc (size); + if (value == NULL) + value = vmefail(size); + return value; +} + +void * rcalloc (size_t nmemb, size_t size) +{ + register void *value; + if (size == 0) size++; + if (nmemb == 0) nmemb++; + value = calloc (nmemb, size); + if (value == NULL) + value = vmefail(size); + return value; +} + +void * rrealloc (void *ptr, size_t size) +{ + register void *value; + if (size == 0) size++; + value = realloc (ptr, size); + if (value == NULL) + value = vmefail(size); + return value; +} + +char * rstrdup (const char *str) +{ + size_t size = strlen(str) + 1; + char *newstr = (char *) malloc (size); + if (newstr == NULL) + newstr = (char *) vmefail(size); + strcpy (newstr, str); + return newstr; +} + +void * rfree (void *ptr) +{ + free(ptr); + return NULL; +} diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c new file mode 100644 index 0000000..04472de --- /dev/null +++ b/rpmio/rpmpgp.c @@ -0,0 +1,1669 @@ +/** \ingroup rpmio signature + * \file rpmio/rpmpgp.c + * Routines to handle RFC-2440 detached signatures. + */ + +#include "system.h" + +#include <pthread.h> + +#include <rpm/rpmstring.h> +#include <rpm/rpmlog.h> + +#include "rpmio/digest.h" +#include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */ + +#include "debug.h" + + +static int _debug = 0; + +static int _print = 0; + +static int _crypto_initialized = 0; +static int _new_process = 1; + +typedef const struct pgpValTbl_s { + int val; + char const * const str; +} * pgpValTbl; + +static struct pgpValTbl_s const pgpSigTypeTbl[] = { + { PGPSIGTYPE_BINARY, "Binary document signature" }, + { PGPSIGTYPE_TEXT, "Text document signature" }, + { PGPSIGTYPE_STANDALONE, "Standalone signature" }, + { PGPSIGTYPE_GENERIC_CERT, "Generic certification of a User ID and Public Key" }, + { PGPSIGTYPE_PERSONA_CERT, "Persona certification of a User ID and Public Key" }, + { PGPSIGTYPE_CASUAL_CERT, "Casual certification of a User ID and Public Key" }, + { PGPSIGTYPE_POSITIVE_CERT, "Positive certification of a User ID and Public Key" }, + { PGPSIGTYPE_SUBKEY_BINDING,"Subkey Binding Signature" }, + { PGPSIGTYPE_SIGNED_KEY, "Signature directly on a key" }, + { PGPSIGTYPE_KEY_REVOKE, "Key revocation signature" }, + { PGPSIGTYPE_SUBKEY_REVOKE, "Subkey revocation signature" }, + { PGPSIGTYPE_CERT_REVOKE, "Certification revocation signature" }, + { PGPSIGTYPE_TIMESTAMP, "Timestamp signature" }, + { -1, "Unknown signature type" }, +}; + +static struct pgpValTbl_s const pgpPubkeyTbl[] = { + { PGPPUBKEYALGO_RSA, "RSA" }, + { PGPPUBKEYALGO_RSA_ENCRYPT,"RSA(Encrypt-Only)" }, + { PGPPUBKEYALGO_RSA_SIGN, "RSA(Sign-Only)" }, + { PGPPUBKEYALGO_ELGAMAL_ENCRYPT,"Elgamal(Encrypt-Only)" }, + { PGPPUBKEYALGO_DSA, "DSA" }, + { PGPPUBKEYALGO_EC, "Elliptic Curve" }, + { PGPPUBKEYALGO_ECDSA, "ECDSA" }, + { PGPPUBKEYALGO_ELGAMAL, "Elgamal" }, + { PGPPUBKEYALGO_DH, "Diffie-Hellman (X9.42)" }, + { -1, "Unknown public key algorithm" }, +}; + +static struct pgpValTbl_s const pgpSymkeyTbl[] = { + { PGPSYMKEYALGO_PLAINTEXT, "Plaintext" }, + { PGPSYMKEYALGO_IDEA, "IDEA" }, + { PGPSYMKEYALGO_TRIPLE_DES, "3DES" }, + { PGPSYMKEYALGO_CAST5, "CAST5" }, + { PGPSYMKEYALGO_BLOWFISH, "BLOWFISH" }, + { PGPSYMKEYALGO_SAFER, "SAFER" }, + { PGPSYMKEYALGO_DES_SK, "DES/SK" }, + { PGPSYMKEYALGO_AES_128, "AES(128-bit key)" }, + { PGPSYMKEYALGO_AES_192, "AES(192-bit key)" }, + { PGPSYMKEYALGO_AES_256, "AES(256-bit key)" }, + { PGPSYMKEYALGO_TWOFISH, "TWOFISH(256-bit key)" }, + { PGPSYMKEYALGO_NOENCRYPT, "no encryption" }, + { -1, "Unknown symmetric key algorithm" }, +}; + +static struct pgpValTbl_s const pgpCompressionTbl[] = { + { PGPCOMPRESSALGO_NONE, "Uncompressed" }, + { PGPCOMPRESSALGO_ZIP, "ZIP" }, + { PGPCOMPRESSALGO_ZLIB, "ZLIB" }, + { PGPCOMPRESSALGO_BZIP2, "BZIP2" }, + { -1, "Unknown compression algorithm" }, +}; + +static struct pgpValTbl_s const pgpHashTbl[] = { + { PGPHASHALGO_MD5, "MD5" }, + { PGPHASHALGO_SHA1, "SHA1" }, + { PGPHASHALGO_RIPEMD160, "RIPEMD160" }, + { PGPHASHALGO_MD2, "MD2" }, + { PGPHASHALGO_TIGER192, "TIGER192" }, + { PGPHASHALGO_HAVAL_5_160, "HAVAL-5-160" }, + { PGPHASHALGO_SHA256, "SHA256" }, + { PGPHASHALGO_SHA384, "SHA384" }, + { PGPHASHALGO_SHA512, "SHA512" }, + { PGPHASHALGO_SHA224, "SHA224" }, + { -1, "Unknown hash algorithm" }, +}; + +static struct pgpValTbl_s const pgpKeyServerPrefsTbl[] = { + { 0x80, "No-modify" }, + { -1, "Unknown key server preference" }, +}; + +static struct pgpValTbl_s const pgpSubTypeTbl[] = { + { PGPSUBTYPE_SIG_CREATE_TIME,"signature creation time" }, + { PGPSUBTYPE_SIG_EXPIRE_TIME,"signature expiration time" }, + { PGPSUBTYPE_EXPORTABLE_CERT,"exportable certification" }, + { PGPSUBTYPE_TRUST_SIG, "trust signature" }, + { PGPSUBTYPE_REGEX, "regular expression" }, + { PGPSUBTYPE_REVOCABLE, "revocable" }, + { PGPSUBTYPE_KEY_EXPIRE_TIME,"key expiration time" }, + { PGPSUBTYPE_ARR, "additional recipient request" }, + { PGPSUBTYPE_PREFER_SYMKEY, "preferred symmetric algorithms" }, + { PGPSUBTYPE_REVOKE_KEY, "revocation key" }, + { PGPSUBTYPE_ISSUER_KEYID, "issuer key ID" }, + { PGPSUBTYPE_NOTATION, "notation data" }, + { PGPSUBTYPE_PREFER_HASH, "preferred hash algorithms" }, + { PGPSUBTYPE_PREFER_COMPRESS,"preferred compression algorithms" }, + { PGPSUBTYPE_KEYSERVER_PREFERS,"key server preferences" }, + { PGPSUBTYPE_PREFER_KEYSERVER,"preferred key server" }, + { PGPSUBTYPE_PRIMARY_USERID,"primary user id" }, + { PGPSUBTYPE_POLICY_URL, "policy URL" }, + { PGPSUBTYPE_KEY_FLAGS, "key flags" }, + { PGPSUBTYPE_SIGNER_USERID, "signer's user id" }, + { PGPSUBTYPE_REVOKE_REASON, "reason for revocation" }, + { PGPSUBTYPE_FEATURES, "features" }, + { PGPSUBTYPE_EMBEDDED_SIG, "embedded signature" }, + + { PGPSUBTYPE_INTERNAL_100, "internal subpkt type 100" }, + { PGPSUBTYPE_INTERNAL_101, "internal subpkt type 101" }, + { PGPSUBTYPE_INTERNAL_102, "internal subpkt type 102" }, + { PGPSUBTYPE_INTERNAL_103, "internal subpkt type 103" }, + { PGPSUBTYPE_INTERNAL_104, "internal subpkt type 104" }, + { PGPSUBTYPE_INTERNAL_105, "internal subpkt type 105" }, + { PGPSUBTYPE_INTERNAL_106, "internal subpkt type 106" }, + { PGPSUBTYPE_INTERNAL_107, "internal subpkt type 107" }, + { PGPSUBTYPE_INTERNAL_108, "internal subpkt type 108" }, + { PGPSUBTYPE_INTERNAL_109, "internal subpkt type 109" }, + { PGPSUBTYPE_INTERNAL_110, "internal subpkt type 110" }, + { -1, "Unknown signature subkey type" }, +}; + +static struct pgpValTbl_s const pgpTagTbl[] = { + { PGPTAG_PUBLIC_SESSION_KEY,"Public-Key Encrypted Session Key" }, + { PGPTAG_SIGNATURE, "Signature" }, + { PGPTAG_SYMMETRIC_SESSION_KEY,"Symmetric-Key Encrypted Session Key" }, + { PGPTAG_ONEPASS_SIGNATURE, "One-Pass Signature" }, + { PGPTAG_SECRET_KEY, "Secret Key" }, + { PGPTAG_PUBLIC_KEY, "Public Key" }, + { PGPTAG_SECRET_SUBKEY, "Secret Subkey" }, + { PGPTAG_COMPRESSED_DATA, "Compressed Data" }, + { PGPTAG_SYMMETRIC_DATA, "Symmetrically Encrypted Data" }, + { PGPTAG_MARKER, "Marker" }, + { PGPTAG_LITERAL_DATA, "Literal Data" }, + { PGPTAG_TRUST, "Trust" }, + { PGPTAG_USER_ID, "User ID" }, + { PGPTAG_PUBLIC_SUBKEY, "Public Subkey" }, + { PGPTAG_COMMENT_OLD, "Comment (from OpenPGP draft)" }, + { PGPTAG_PHOTOID, "PGP's photo ID" }, + { PGPTAG_ENCRYPTED_MDC, "Integrity protected encrypted data" }, + { PGPTAG_MDC, "Manipulaion detection code packet" }, + { PGPTAG_PRIVATE_60, "Private #60" }, + { PGPTAG_COMMENT, "Comment" }, + { PGPTAG_PRIVATE_62, "Private #62" }, + { PGPTAG_CONTROL, "Control (GPG)" }, + { -1, "Unknown packet tag" }, +}; + +static struct pgpValTbl_s const pgpArmorTbl[] = { + { PGPARMOR_MESSAGE, "MESSAGE" }, + { PGPARMOR_PUBKEY, "PUBLIC KEY BLOCK" }, + { PGPARMOR_SIGNATURE, "SIGNATURE" }, + { PGPARMOR_SIGNED_MESSAGE, "SIGNED MESSAGE" }, + { PGPARMOR_FILE, "ARMORED FILE" }, + { PGPARMOR_PRIVKEY, "PRIVATE KEY BLOCK" }, + { PGPARMOR_SECKEY, "SECRET KEY BLOCK" }, + { -1, "Unknown armor block" } +}; + +static struct pgpValTbl_s const pgpArmorKeyTbl[] = { + { PGPARMORKEY_VERSION, "Version: " }, + { PGPARMORKEY_COMMENT, "Comment: " }, + { PGPARMORKEY_MESSAGEID, "MessageID: " }, + { PGPARMORKEY_HASH, "Hash: " }, + { PGPARMORKEY_CHARSET, "Charset: " }, + { -1, "Unknown armor key" } +}; + +static void pgpPrtNL(void) +{ + if (!_print) return; + fprintf(stderr, "\n"); +} + +static void pgpPrtInt(const char *pre, int i) +{ + if (!_print) return; + if (pre && *pre) + fprintf(stderr, "%s", pre); + fprintf(stderr, " %d", i); +} + +static void pgpPrtStr(const char *pre, const char *s) +{ + if (!_print) return; + if (pre && *pre) + fprintf(stderr, "%s", pre); + fprintf(stderr, " %s", s); +} + +static const char * pgpValStr(pgpValTbl vs, uint8_t val) +{ + do { + if (vs->val == val) + break; + } while ((++vs)->val != -1); + return vs->str; +} + +static pgpValTbl pgpValTable(pgpValType type) +{ + switch (type) { + case PGPVAL_TAG: return pgpTagTbl; + case PGPVAL_ARMORBLOCK: return pgpArmorTbl; + case PGPVAL_ARMORKEY: return pgpArmorKeyTbl; + case PGPVAL_SIGTYPE: return pgpSigTypeTbl; + case PGPVAL_SUBTYPE: return pgpSubTypeTbl; + case PGPVAL_PUBKEYALGO: return pgpPubkeyTbl; + case PGPVAL_SYMKEYALGO: return pgpSymkeyTbl; + case PGPVAL_COMPRESSALGO: return pgpCompressionTbl; + case PGPVAL_HASHALGO: return pgpHashTbl; + case PGPVAL_SERVERPREFS: return pgpKeyServerPrefsTbl; + default: + break; + } + return NULL; +} + +const char * pgpValString(pgpValType type, uint8_t val) +{ + pgpValTbl tbl = pgpValTable(type); + return (tbl != NULL) ? pgpValStr(tbl, val) : NULL; +} + +static void pgpPrtHex(const char *pre, const uint8_t *p, size_t plen) +{ + char *hex = NULL; + if (!_print) return; + if (pre && *pre) + fprintf(stderr, "%s", pre); + hex = pgpHexStr(p, plen); + fprintf(stderr, " %s", hex); + free(hex); +} + +static void pgpPrtVal(const char * pre, pgpValTbl vs, uint8_t val) +{ + if (!_print) return; + if (pre && *pre) + fprintf(stderr, "%s", pre); + fprintf(stderr, "%s(%u)", pgpValStr(vs, val), (unsigned)val); +} + +/** \ingroup rpmpgp + * Return no. of bits in a multiprecision integer. + * @param p pointer to multiprecision integer + * @return no. of bits + */ +static +unsigned int pgpMpiBits(const uint8_t *p) +{ + return ((p[0] << 8) | p[1]); +} + +/** \ingroup rpmpgp + * Return no. of bytes in a multiprecision integer. + * @param p pointer to multiprecision integer + * @return no. of bytes + */ +static +size_t pgpMpiLen(const uint8_t *p) +{ + return (2 + ((pgpMpiBits(p)+7)>>3)); +} + +/** \ingroup rpmpgp + * Return hex formatted representation of a multiprecision integer. + * @param p bytes + * @return hex formatted string (malloc'ed) + */ +static inline +char * pgpMpiStr(const uint8_t *p) +{ + char *str = NULL; + char *hex = pgpHexStr(p+2, pgpMpiLen(p)-2); + rasprintf(&str, "[%4u]: %s", pgpGrab(p, (size_t) 2), hex); + free(hex); + return str; +} + +/** \ingroup rpmpgp + * Return value of an OpenPGP string. + * @param vs table of (string,value) pairs + * @param s string token to lookup + * @param se end-of-string address + * @return byte value + */ +static inline +int pgpValTok(pgpValTbl vs, const char * s, const char * se) +{ + do { + size_t vlen = strlen(vs->str); + if (vlen <= (se-s) && rstreqn(s, vs->str, vlen)) + break; + } while ((++vs)->val != -1); + return vs->val; +} +/** + * @return 0 on success + */ +static int pgpMpiSet(const char * pre, unsigned int lbits, + uint8_t *dest, const uint8_t * p, const uint8_t * pend) +{ + unsigned int mbits = pgpMpiBits(p); + unsigned int nbits; + size_t nbytes; + uint8_t *t = dest; + unsigned int ix; + + if ((p + ((mbits+7) >> 3)) > pend) + return 1; + + if (mbits > lbits) + return 1; + + nbits = (lbits > mbits ? lbits : mbits); + nbytes = ((nbits + 7) >> 3); + ix = (nbits - mbits) >> 3; + +if (_debug) +fprintf(stderr, "*** mbits %u nbits %u nbytes %zu ix %u\n", mbits, nbits, nbytes, ix); + if (ix > 0) memset(t, '\0', ix); + memcpy(t+ix, p+2, nbytes-ix); +if (_debug) +fprintf(stderr, "*** %s %s\n", pre, pgpHexStr(dest, nbytes)); + + return 0; +} + +/** + * @return NULL on error + */ +static SECItem *pgpMpiItem(PRArenaPool *arena, SECItem *item, const uint8_t *p) +{ + size_t nbytes = pgpMpiLen(p)-2; + + if (item == NULL) { + if ((item=SECITEM_AllocItem(arena, item, nbytes)) == NULL) + return item; + } else { + if (arena != NULL) + item->data = PORT_ArenaGrow(arena, item->data, item->len, nbytes); + else + item->data = PORT_Realloc(item->data, nbytes); + + if (item->data == NULL) { + if (arena == NULL) + SECITEM_FreeItem(item, PR_TRUE); + return NULL; + } + } + + memcpy(item->data, p+2, nbytes); + item->len = nbytes; + return item; +} +/*@=boundswrite@*/ + +static SECKEYPublicKey *pgpNewPublicKey(KeyType type) +{ + PRArenaPool *arena; + SECKEYPublicKey *key; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + key = PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + + if (key == NULL) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + + key->keyType = type; + key->pkcs11ID = CK_INVALID_HANDLE; + key->pkcs11Slot = NULL; + key->arena = arena; + return key; +} + +/** \ingroup rpmpgp + * Is buffer at beginning of an OpenPGP packet? + * @param p buffer + * @return 1 if an OpenPGP packet, 0 otherwise + */ +static inline +int pgpIsPkt(const uint8_t * p) +{ + unsigned int val = *p++; + pgpTag tag; + int rc; + + /* XXX can't deal with these. */ + if (!(val & 0x80)) + return 0; + + if (val & 0x40) + tag = (pgpTag)(val & 0x3f); + else + tag = (pgpTag)((val >> 2) & 0xf); + + switch (tag) { + case PGPTAG_MARKER: + case PGPTAG_SYMMETRIC_SESSION_KEY: + case PGPTAG_ONEPASS_SIGNATURE: + case PGPTAG_PUBLIC_KEY: + case PGPTAG_SECRET_KEY: + case PGPTAG_PUBLIC_SESSION_KEY: + case PGPTAG_SIGNATURE: + case PGPTAG_COMMENT: + case PGPTAG_COMMENT_OLD: + case PGPTAG_LITERAL_DATA: + case PGPTAG_COMPRESSED_DATA: + case PGPTAG_SYMMETRIC_DATA: + rc = 1; + break; + case PGPTAG_PUBLIC_SUBKEY: + case PGPTAG_SECRET_SUBKEY: + case PGPTAG_USER_ID: + case PGPTAG_RESERVED: + case PGPTAG_TRUST: + case PGPTAG_PHOTOID: + case PGPTAG_ENCRYPTED_MDC: + case PGPTAG_MDC: + case PGPTAG_PRIVATE_60: + case PGPTAG_PRIVATE_62: + case PGPTAG_CONTROL: + default: + rc = 0; + break; + } + + return rc; +} + +#define CRC24_INIT 0xb704ce +#define CRC24_POLY 0x1864cfb + +/** \ingroup rpmpgp + * Return CRC of a buffer. + * @param octets bytes + * @param len no. of bytes + * @return crc of buffer + */ +static inline +unsigned int pgpCRC(const uint8_t *octets, size_t len) +{ + unsigned int crc = CRC24_INIT; + size_t i; + + while (len--) { + crc ^= (*octets++) << 16; + for (i = 0; i < 8; i++) { + crc <<= 1; + if (crc & 0x1000000) + crc ^= CRC24_POLY; + } + } + return crc & 0xffffff; +} + +static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype, + pgpDigParams _digp) +{ + const uint8_t *p = h; + size_t plen, i; + + while (hlen > 0) { + i = pgpLen(p, &plen); + p += i; + hlen -= i; + + pgpPrtVal(" ", pgpSubTypeTbl, (p[0]&(~PGPSUBTYPE_CRITICAL))); + if (p[0] & PGPSUBTYPE_CRITICAL) + if (_print) + fprintf(stderr, " *CRITICAL*"); + switch (*p) { + case PGPSUBTYPE_PREFER_SYMKEY: /* preferred symmetric algorithms */ + for (i = 1; i < plen; i++) + pgpPrtVal(" ", pgpSymkeyTbl, p[i]); + break; + case PGPSUBTYPE_PREFER_HASH: /* preferred hash algorithms */ + for (i = 1; i < plen; i++) + pgpPrtVal(" ", pgpHashTbl, p[i]); + break; + case PGPSUBTYPE_PREFER_COMPRESS:/* preferred compression algorithms */ + for (i = 1; i < plen; i++) + pgpPrtVal(" ", pgpCompressionTbl, p[i]); + break; + case PGPSUBTYPE_KEYSERVER_PREFERS:/* key server preferences */ + for (i = 1; i < plen; i++) + pgpPrtVal(" ", pgpKeyServerPrefsTbl, p[i]); + break; + case PGPSUBTYPE_SIG_CREATE_TIME: + if (_digp && !(_digp->saved & PGPDIG_SAVED_TIME) && + (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE)) + { + _digp->saved |= PGPDIG_SAVED_TIME; + memcpy(_digp->time, p+1, sizeof(_digp->time)); + } + case PGPSUBTYPE_SIG_EXPIRE_TIME: + case PGPSUBTYPE_KEY_EXPIRE_TIME: + if ((plen - 1) == 4) { + time_t t = pgpGrab(p+1, plen-1); + if (_print) + fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + } else + pgpPrtHex("", p+1, plen-1); + break; + + case PGPSUBTYPE_ISSUER_KEYID: /* issuer key ID */ + if (_digp && !(_digp->saved & PGPDIG_SAVED_ID) && + (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE)) + { + _digp->saved |= PGPDIG_SAVED_ID; + memcpy(_digp->signid, p+1, sizeof(_digp->signid)); + } + case PGPSUBTYPE_EXPORTABLE_CERT: + case PGPSUBTYPE_TRUST_SIG: + case PGPSUBTYPE_REGEX: + case PGPSUBTYPE_REVOCABLE: + case PGPSUBTYPE_ARR: + case PGPSUBTYPE_REVOKE_KEY: + case PGPSUBTYPE_NOTATION: + case PGPSUBTYPE_PREFER_KEYSERVER: + case PGPSUBTYPE_PRIMARY_USERID: + case PGPSUBTYPE_POLICY_URL: + case PGPSUBTYPE_KEY_FLAGS: + case PGPSUBTYPE_SIGNER_USERID: + case PGPSUBTYPE_REVOKE_REASON: + case PGPSUBTYPE_FEATURES: + case PGPSUBTYPE_EMBEDDED_SIG: + case PGPSUBTYPE_INTERNAL_100: + case PGPSUBTYPE_INTERNAL_101: + case PGPSUBTYPE_INTERNAL_102: + case PGPSUBTYPE_INTERNAL_103: + case PGPSUBTYPE_INTERNAL_104: + case PGPSUBTYPE_INTERNAL_105: + case PGPSUBTYPE_INTERNAL_106: + case PGPSUBTYPE_INTERNAL_107: + case PGPSUBTYPE_INTERNAL_108: + case PGPSUBTYPE_INTERNAL_109: + case PGPSUBTYPE_INTERNAL_110: + default: + pgpPrtHex("", p+1, plen-1); + break; + } + pgpPrtNL(); + p += plen; + hlen -= plen; + } + return 0; +} + +static const char * const pgpSigRSA[] = { + " m**d =", + NULL, +}; + +static const char * const pgpSigDSA[] = { + " r =", + " s =", + NULL, +}; + +#ifndef DSA_SUBPRIME_LEN +#define DSA_SUBPRIME_LEN 20 +#endif + +static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, uint8_t sigtype, + const uint8_t *p, const uint8_t *h, size_t hlen, pgpDig _dig) +{ + const uint8_t * pend = h + hlen; + size_t i; + SECItem dsaraw; + unsigned char dsabuf[2*DSA_SUBPRIME_LEN]; + char *mpi; + + dsaraw.type = 0; + dsaraw.data = dsabuf; + dsaraw.len = sizeof(dsabuf); + + for (i = 0; p < pend; i++, p += pgpMpiLen(p)) { + if (pubkey_algo == PGPPUBKEYALGO_RSA) { + if (i >= 1) break; + if (_dig && + (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT)) + { + switch (i) { + case 0: /* m**d */ + _dig->sigdata = pgpMpiItem(NULL, _dig->sigdata, p); + if (_dig->sigdata == NULL) + return 1; + break; + default: + break; + } + } + pgpPrtStr("", pgpSigRSA[i]); + } else if (pubkey_algo == PGPPUBKEYALGO_DSA) { + if (i >= 2) break; + if (_dig && + (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT)) + { + int xx; + xx = 0; + switch (i) { + case 0: + memset(dsaraw.data, '\0', 2*DSA_SUBPRIME_LEN); + /* r */ + xx = pgpMpiSet(pgpSigDSA[i], DSA_SUBPRIME_LEN*8, dsaraw.data, p, pend); + break; + case 1: /* s */ + xx = pgpMpiSet(pgpSigDSA[i], DSA_SUBPRIME_LEN*8, dsaraw.data + DSA_SUBPRIME_LEN, p, pend); + if (_dig->sigdata != NULL) + SECITEM_FreeItem(_dig->sigdata, PR_FALSE); + else if ((_dig->sigdata=SECITEM_AllocItem(NULL, NULL, 0)) == NULL) { + xx = 1; + break; + } + if (DSAU_EncodeDerSig(_dig->sigdata, &dsaraw) != SECSuccess) + xx = 1; + break; + default: + xx = 1; + break; + } + if (xx) return xx; + } + pgpPrtStr("", pgpSigDSA[i]); + } else { + if (_print) + fprintf(stderr, "%7zd", i); + } + mpi = pgpMpiStr(p); + pgpPrtStr("", mpi); + free(mpi); + pgpPrtNL(); + } + + return 0; +} + +static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, + pgpDig _dig, pgpDigParams _digp) +{ + uint8_t version = h[0]; + uint8_t * p; + size_t plen; + int rc; + + switch (version) { + case 3: + { pgpPktSigV3 v = (pgpPktSigV3)h; + time_t t; + + if (v->hashlen != 5) + return 1; + + pgpPrtVal("V3 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + pgpPrtVal(" ", pgpHashTbl, v->hash_algo); + pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); + pgpPrtNL(); + t = pgpGrab(v->time, sizeof(v->time)); + if (_print) + fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + pgpPrtNL(); + pgpPrtHex(" signer keyid", v->signid, sizeof(v->signid)); + plen = pgpGrab(v->signhash16, sizeof(v->signhash16)); + pgpPrtHex(" signhash16", v->signhash16, sizeof(v->signhash16)); + pgpPrtNL(); + + if (_digp && _digp->pubkey_algo == 0) { + _digp->version = v->version; + _digp->hashlen = v->hashlen; + _digp->sigtype = v->sigtype; + _digp->hash = memcpy(xmalloc(v->hashlen), &v->sigtype, v->hashlen); + memcpy(_digp->time, v->time, sizeof(_digp->time)); + memcpy(_digp->signid, v->signid, sizeof(_digp->signid)); + _digp->pubkey_algo = v->pubkey_algo; + _digp->hash_algo = v->hash_algo; + memcpy(_digp->signhash16, v->signhash16, sizeof(_digp->signhash16)); + } + + p = ((uint8_t *)v) + sizeof(*v); + rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _dig); + } break; + case 4: + { pgpPktSigV4 v = (pgpPktSigV4)h; + + pgpPrtVal("V4 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + pgpPrtVal(" ", pgpHashTbl, v->hash_algo); + pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); + pgpPrtNL(); + + p = &v->hashlen[0]; + plen = pgpGrab(v->hashlen, sizeof(v->hashlen)); + p += sizeof(v->hashlen); + + if ((p + plen) > (h + hlen)) + return 1; + +if (_debug && _print) +fprintf(stderr, " hash[%zu] -- %s\n", plen, pgpHexStr(p, plen)); + if (_digp && _digp->pubkey_algo == 0) { + _digp->hashlen = sizeof(*v) + plen; + _digp->hash = memcpy(xmalloc(_digp->hashlen), v, _digp->hashlen); + } + (void) pgpPrtSubType(p, plen, v->sigtype, _digp); + p += plen; + + plen = pgpGrab(p,2); + p += 2; + + if ((p + plen) > (h + hlen)) + return 1; + +if (_debug && _print) +fprintf(stderr, " unhash[%zu] -- %s\n", plen, pgpHexStr(p, plen)); + (void) pgpPrtSubType(p, plen, v->sigtype, _digp); + p += plen; + + plen = pgpGrab(p,2); + pgpPrtHex(" signhash16", p, 2); + pgpPrtNL(); + + if (_digp && _digp->pubkey_algo == 0) { + _digp->version = v->version; + _digp->sigtype = v->sigtype; + _digp->pubkey_algo = v->pubkey_algo; + _digp->hash_algo = v->hash_algo; + memcpy(_digp->signhash16, p, sizeof(_digp->signhash16)); + } + + p += 2; + if (p > (h + hlen)) + return 1; + + rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _dig); + } break; + default: + rc = 1; + break; + } + return rc; +} + +static const char * const pgpPublicRSA[] = { + " n =", + " e =", + NULL, +}; + +#ifdef NOTYET +static const char * const pgpSecretRSA[] = { + " d =", + " p =", + " q =", + " u =", + NULL, +}; +#endif + +static const char * const pgpPublicDSA[] = { + " p =", + " q =", + " g =", + " y =", + NULL, +}; + +#ifdef NOTYET +static const char * const pgpSecretDSA[] = { + " x =", + NULL, +}; +#endif + +static const char * const pgpPublicELGAMAL[] = { + " p =", + " g =", + " y =", + NULL, +}; + +#ifdef NOTYET +static const char * const pgpSecretELGAMAL[] = { + " x =", + NULL, +}; +#endif + +char * pgpHexStr(const uint8_t *p, size_t plen) +{ + char *t, *str; + str = t = xmalloc(plen * 2 + 1); + static char const hex[] = "0123456789abcdef"; + while (plen-- > 0) { + size_t i; + i = *p++; + *t++ = hex[ (i >> 4) & 0xf ]; + *t++ = hex[ (i ) & 0xf ]; + } + *t = '\0'; + return str; +} + +static const uint8_t * pgpPrtPubkeyParams(uint8_t pubkey_algo, + const uint8_t *p, const uint8_t *h, size_t hlen, + pgpDig _dig) +{ + size_t i; + + /* XXX we can't handle more than one key in a packet, error out */ + if (_dig && _dig->keydata) + return NULL; + + for (i = 0; p < &h[hlen]; i++, p += pgpMpiLen(p)) { + char * mpi; + if (pubkey_algo == PGPPUBKEYALGO_RSA) { + if (i >= 2) break; + if (_dig) { + if (_dig->keydata == NULL) { + _dig->keydata = pgpNewPublicKey(rsaKey); + if (_dig->keydata == NULL) + return NULL; + } + switch (i) { + case 0: /* n */ + pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.rsa.modulus, p); + break; + case 1: /* e */ + pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.rsa.publicExponent, p); + break; + default: + break; + } + } + pgpPrtStr("", pgpPublicRSA[i]); + } else if (pubkey_algo == PGPPUBKEYALGO_DSA) { + if (i >= 4) break; + if (_dig) { + if (_dig->keydata == NULL) { + _dig->keydata = pgpNewPublicKey(dsaKey); + if (_dig->keydata == NULL) + return NULL; + } + switch (i) { + case 0: /* p */ + pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.prime, p); + break; + case 1: /* q */ + pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.subPrime, p); + break; + case 2: /* g */ + pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.base, p); + break; + case 3: /* y */ + pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.publicValue, p); + break; + default: + break; + } + } + pgpPrtStr("", pgpPublicDSA[i]); + } else if (pubkey_algo == PGPPUBKEYALGO_ELGAMAL_ENCRYPT) { + if (i >= 3) break; + pgpPrtStr("", pgpPublicELGAMAL[i]); + } else { + if (_print) + fprintf(stderr, "%7zd", i); + } + mpi = pgpMpiStr(p); + pgpPrtStr("", mpi); + free(mpi); + pgpPrtNL(); + } + + return p; +} + +static const uint8_t * pgpPrtSeckeyParams(uint8_t pubkey_algo, + const uint8_t *p, const uint8_t *h, size_t hlen) +{ + size_t i; + + switch (*p) { + case 0: + pgpPrtVal(" ", pgpSymkeyTbl, *p); + break; + case 255: + p++; + pgpPrtVal(" ", pgpSymkeyTbl, *p); + switch (p[1]) { + case 0x00: + pgpPrtVal(" simple ", pgpHashTbl, p[2]); + p += 2; + break; + case 0x01: + pgpPrtVal(" salted ", pgpHashTbl, p[2]); + pgpPrtHex("", p+3, 8); + p += 10; + break; + case 0x03: + pgpPrtVal(" iterated/salted ", pgpHashTbl, p[2]); + /* FIX: unsigned cast */ + i = (16 + (p[11] & 0xf)) << ((p[11] >> 4) + 6); + pgpPrtHex("", p+3, 8); + pgpPrtInt(" iter", i); + p += 11; + break; + } + break; + default: + pgpPrtVal(" ", pgpSymkeyTbl, *p); + pgpPrtHex(" IV", p+1, 8); + p += 8; + break; + } + pgpPrtNL(); + + p++; + +#ifdef NOTYET /* XXX encrypted MPI's need to be handled. */ + for (i = 0; p < &h[hlen]; i++, p += pgpMpiLen(p)) { + char *mpi; + if (pubkey_algo == PGPPUBKEYALGO_RSA) { + if (pgpSecretRSA[i] == NULL) break; + pgpPrtStr("", pgpSecretRSA[i]); + } else if (pubkey_algo == PGPPUBKEYALGO_DSA) { + if (pgpSecretDSA[i] == NULL) break; + pgpPrtStr("", pgpSecretDSA[i]); + } else if (pubkey_algo == PGPPUBKEYALGO_ELGAMAL_ENCRYPT) { + if (pgpSecretELGAMAL[i] == NULL) break; + pgpPrtStr("", pgpSecretELGAMAL[i]); + } else { + if (_print) + fprintf(stderr, "%7d", i); + } + mpi = pgpMpiStr(p); + pgpPrtStr("", mpi); + free(mpi); + pgpPrtNL(); + } +#else + pgpPrtHex(" secret", p, (hlen - (p - h) - 2)); + pgpPrtNL(); + p += (hlen - (p - h) - 2); +#endif + pgpPrtHex(" checksum", p, 2); + pgpPrtNL(); + + return p; +} + +static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, + pgpDig _dig, pgpDigParams _digp) +{ + uint8_t version = *h; + const uint8_t * p; + size_t plen; + time_t t; + int rc; + + switch (version) { + case 3: + { pgpPktKeyV3 v = (pgpPktKeyV3)h; + pgpPrtVal("V3 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + t = pgpGrab(v->time, sizeof(v->time)); + if (_print) + fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + plen = pgpGrab(v->valid, sizeof(v->valid)); + if (plen != 0) + fprintf(stderr, " valid %zu days", plen); + pgpPrtNL(); + + if (_digp && _digp->tag == tag) { + _digp->version = v->version; + memcpy(_digp->time, v->time, sizeof(_digp->time)); + _digp->pubkey_algo = v->pubkey_algo; + } + + p = ((uint8_t *)v) + sizeof(*v); + p = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _dig); + rc = (p == NULL); + } break; + case 4: + { pgpPktKeyV4 v = (pgpPktKeyV4)h; + pgpPrtVal("V4 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + t = pgpGrab(v->time, sizeof(v->time)); + if (_print) + fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + pgpPrtNL(); + + if (_digp && _digp->tag == tag) { + _digp->version = v->version; + memcpy(_digp->time, v->time, sizeof(_digp->time)); + _digp->pubkey_algo = v->pubkey_algo; + } + + p = ((uint8_t *)v) + sizeof(*v); + p = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _dig); + if (!(tag == PGPTAG_PUBLIC_KEY || tag == PGPTAG_PUBLIC_SUBKEY)) + p = pgpPrtSeckeyParams(v->pubkey_algo, p, h, hlen); + rc = (p == NULL); + } break; + default: + rc = 1; + break; + } + return rc; +} + +static int pgpPrtUserID(pgpTag tag, const uint8_t *h, size_t hlen, + pgpDigParams _digp) +{ + pgpPrtVal("", pgpTagTbl, tag); + if (_print) + fprintf(stderr, " \"%.*s\"", (int)hlen, (const char *)h); + pgpPrtNL(); + if (_digp) { + free(_digp->userid); + _digp->userid = memcpy(xmalloc(hlen+1), h, hlen); + _digp->userid[hlen] = '\0'; + } + return 0; +} + +static int pgpPrtComment(pgpTag tag, const uint8_t *h, size_t hlen) +{ + size_t i = hlen; + + pgpPrtVal("", pgpTagTbl, tag); + if (_print) + fprintf(stderr, " "); + while (i > 0) { + size_t j; + if (*h >= ' ' && *h <= 'z') { + if (_print) + fprintf(stderr, "%s", (const char *)h); + j = strlen((const char*)h); + while (h[j] == '\0') + j++; + } else { + pgpPrtHex("", h, i); + j = i; + } + i -= j; + h += j; + } + pgpPrtNL(); + return 0; +} + +int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid) +{ + unsigned int val = *pkt; + size_t plen, hlen; + pgpTag tag; + const uint8_t *se, *h; + DIGEST_CTX ctx; + int rc = -1; /* assume failure. */ + + if (!(val & 0x80)) + return rc; + + if (val & 0x40) { + tag = (val & 0x3f); + plen = pgpLen(pkt+1, &hlen); + } else { + tag = (val >> 2) & 0xf; + plen = (1 << (val & 0x3)); + hlen = pgpGrab(pkt+1, plen); + } + if (pktlen > 0 && 1 + plen + hlen > pktlen) + return rc; + + h = pkt + 1 + plen; + + switch (h[0]) { + case 3: + { pgpPktKeyV3 v = (pgpPktKeyV3) (h); + se = (uint8_t *)(v + 1); + switch (v->pubkey_algo) { + case PGPPUBKEYALGO_RSA: + se += pgpMpiLen(se); + memmove(keyid, (se-8), 8); + rc = 0; + break; + default: /* TODO: md5 of mpi bodies (i.e. no length) */ + break; + } + } break; + case 4: + { pgpPktKeyV4 v = (pgpPktKeyV4) (h); + uint8_t * d = NULL; + uint8_t in[3]; + size_t dlen; + int i; + + se = (uint8_t *)(v + 1); + switch (v->pubkey_algo) { + case PGPPUBKEYALGO_RSA: + for (i = 0; i < 2; i++) + se += pgpMpiLen(se); + break; + case PGPPUBKEYALGO_DSA: + for (i = 0; i < 4; i++) + se += pgpMpiLen(se); + break; + } + + ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE); + i = se - h; + in[0] = 0x99; + in[1] = i >> 8; + in[2] = i; + (void) rpmDigestUpdate(ctx, in, 3); + (void) rpmDigestUpdate(ctx, h, i); + (void) rpmDigestFinal(ctx, (void **)&d, &dlen, 0); + + if (d) { + memmove(keyid, (d + (dlen-8)), 8); + free(d); + rc = 0; + } + + } break; + } + return rc; +} + +int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid) +{ + uint8_t * pkt; + size_t pktlen; + + if (b64decode(b64pkt, (void **)&pkt, &pktlen)) + return -1; /* on error */ + (void) pgpPubkeyFingerprint(pkt, pktlen, keyid); + pkt = _free(pkt); + return sizeof(keyid); /* no. of bytes of pubkey signid */ +} + +static int pgpPrtPkt(const uint8_t *pkt, size_t pleft, + pgpDig _dig, pgpDigParams _digp) +{ + unsigned int val = *pkt; + size_t pktlen; + pgpTag tag; + size_t plen; + const uint8_t *h; + size_t hlen = 0; + int rc = 0; + + /* XXX can't deal with these. */ + if (!(val & 0x80)) + return -1; + + if (val & 0x40) { + tag = (val & 0x3f); + plen = pgpLen(pkt+1, &hlen); + } else { + tag = (val >> 2) & 0xf; + plen = (1 << (val & 0x3)); + hlen = pgpGrab(pkt+1, plen); + } + + pktlen = 1 + plen + hlen; + if (pktlen > pleft) + return -1; + + h = pkt + 1 + plen; + switch (tag) { + case PGPTAG_SIGNATURE: + rc = pgpPrtSig(tag, h, hlen, _dig, _digp); + break; + case PGPTAG_PUBLIC_KEY: + /* Get the public key fingerprint. */ + if (_digp) { + if (!pgpPubkeyFingerprint(pkt, pktlen, _digp->signid)) + _digp->saved |= PGPDIG_SAVED_ID; + else + memset(_digp->signid, 0, sizeof(_digp->signid)); + } + rc = pgpPrtKey(tag, h, hlen, _dig, _digp); + break; + case PGPTAG_USER_ID: + rc = pgpPrtUserID(tag, h, hlen, _digp); + break; + case PGPTAG_COMMENT: + case PGPTAG_COMMENT_OLD: + rc = pgpPrtComment(tag, h, hlen); + break; + + case PGPTAG_PUBLIC_SUBKEY: + case PGPTAG_SECRET_KEY: + case PGPTAG_SECRET_SUBKEY: + case PGPTAG_RESERVED: + case PGPTAG_PUBLIC_SESSION_KEY: + case PGPTAG_SYMMETRIC_SESSION_KEY: + case PGPTAG_COMPRESSED_DATA: + case PGPTAG_SYMMETRIC_DATA: + case PGPTAG_MARKER: + case PGPTAG_LITERAL_DATA: + case PGPTAG_TRUST: + case PGPTAG_PHOTOID: + case PGPTAG_ENCRYPTED_MDC: + case PGPTAG_MDC: + case PGPTAG_PRIVATE_60: + case PGPTAG_PRIVATE_62: + case PGPTAG_CONTROL: + default: + pgpPrtVal("", pgpTagTbl, tag); + pgpPrtHex("", h, hlen); + pgpPrtNL(); + break; + } + + return (rc ? -1 : pktlen); +} + +pgpDig pgpNewDig(void) +{ + pgpDig dig = xcalloc(1, sizeof(*dig)); + + return dig; +} + +void pgpCleanDig(pgpDig dig) +{ + if (dig != NULL) { + int i; + dig->signature.userid = _free(dig->signature.userid); + dig->pubkey.userid = _free(dig->pubkey.userid); + dig->signature.hash = _free(dig->signature.hash); + dig->pubkey.hash = _free(dig->pubkey.hash); + /* FIX: double indirection */ + for (i = 0; i < 4; i++) { + dig->signature.params[i] = _free(dig->signature.params[i]); + dig->pubkey.params[i] = _free(dig->pubkey.params[i]); + } + + memset(&dig->signature, 0, sizeof(dig->signature)); + memset(&dig->pubkey, 0, sizeof(dig->pubkey)); + + if (dig->keydata != NULL) { + SECKEY_DestroyPublicKey(dig->keydata); + dig->keydata = NULL; + } + + if (dig->sigdata != NULL) { + SECITEM_ZfreeItem(dig->sigdata, PR_TRUE); + dig->sigdata = NULL; + } + } + return; +} + +pgpDig pgpFreeDig(pgpDig dig) +{ + if (dig != NULL) { + + /* DUmp the signature/pubkey data. */ + pgpCleanDig(dig); + dig = _free(dig); + } + return dig; +} + +int pgpPrtPkts(const uint8_t * pkts, size_t pktlen, pgpDig dig, int printing) +{ + unsigned int val = *pkts; + const uint8_t *p; + size_t pleft; + int len; + pgpDigParams _digp = NULL; + + _print = printing; + if (dig != NULL && (val & 0x80)) { + pgpTag tag = (val & 0x40) ? (val & 0x3f) : ((val >> 2) & 0xf); + _digp = (tag == PGPTAG_SIGNATURE) ? &dig->signature : &dig->pubkey; + _digp->tag = tag; + } else + _digp = NULL; + + for (p = pkts, pleft = pktlen; p < (pkts + pktlen); p += len, pleft -= len) { + len = pgpPrtPkt(p, pleft, dig, _digp); + if (len <= 0) + return len; + if (len > pleft) /* XXX shouldn't happen */ + break; + } + return 0; +} + +static SECOidTag getSigAlg(pgpDigParams sigp) +{ + SECOidTag sigalg = SEC_OID_UNKNOWN; + if (sigp->pubkey_algo == PGPPUBKEYALGO_DSA) { + /* assume SHA1 for now, NSS doesn't have SECOID's for other types */ + sigalg = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; + } else if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA) { + switch (sigp->hash_algo) { + case PGPHASHALGO_MD5: + sigalg = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; + break; + case PGPHASHALGO_MD2: + sigalg = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; + break; + case PGPHASHALGO_SHA1: + sigalg = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; + break; + case PGPHASHALGO_SHA256: + sigalg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; + break; + case PGPHASHALGO_SHA384: + sigalg = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; + break; + case PGPHASHALGO_SHA512: + sigalg = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; + break; + default: + break; + } + } + return sigalg; +} + +char *pgpIdentItem(pgpDigParams digp) +{ + char *id = NULL; + if (digp) { + + char *signid = pgpHexStr(digp->signid+4, sizeof(digp->signid)-4); + rasprintf(&id, _("V%d %s/%s %s, key ID %s"), + digp->version, + pgpValStr(pgpPubkeyTbl, digp->pubkey_algo), + pgpValStr(pgpHashTbl, digp->hash_algo), + pgpValStr(pgpTagTbl, digp->tag), + signid); + free(signid); + } else { + id = xstrdup(_("(none)")); + } + return id; +} + +rpmRC pgpVerifySig(pgpDig dig, DIGEST_CTX hashctx) +{ + DIGEST_CTX ctx = rpmDigestDup(hashctx); + uint8_t *hash = NULL; + size_t hashlen = 0; + rpmRC res = RPMRC_FAIL; /* assume failure */ + pgpDigParams sigp = dig ? &dig->signature : NULL; + + if (sigp == NULL || ctx == NULL) + goto exit; + + if (sigp->hash != NULL) + rpmDigestUpdate(ctx, sigp->hash, sigp->hashlen); + + if (sigp->version == 4) { + /* V4 trailer is six octets long (rfc4880) */ + uint8_t trailer[6]; + uint32_t nb = sigp->hashlen; + nb = htonl(nb); + trailer[0] = sigp->version; + trailer[1] = 0xff; + memcpy(trailer+2, &nb, 4); + rpmDigestUpdate(ctx, trailer, sizeof(trailer)); + } + + rpmDigestFinal(ctx, (void **)&hash, &hashlen, 0); + + /* Compare leading 16 bits of digest for quick check. */ + if (hash && memcmp(hash, sigp->signhash16, 2) != 0) + goto exit; + + /* + * If we have a key, verify the signature for real. Otherwise we've + * done all we can, return NOKEY to indicate "looks okay but dunno." + */ + if (dig->keydata == NULL) { + res = RPMRC_NOKEY; + } else { + SECItem digest = { .type = siBuffer, .data = hash, .len = hashlen }; + SECItem *sig = dig->sigdata; + + /* Zero-pad RSA signature to expected size if necessary */ + if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA) { + size_t siglen = SECKEY_SignatureLen(dig->keydata); + if (siglen > sig->len) { + size_t pad = siglen - sig->len; + if ((sig = SECITEM_AllocItem(NULL, NULL, siglen)) == NULL) { + goto exit; + } + memset(sig->data, 0, pad); + memcpy(sig->data+pad, dig->sigdata->data, dig->sigdata->len); + } + } + + /* XXX VFY_VerifyDigest() is deprecated in NSS 3.12 */ + if (VFY_VerifyDigest(&digest, dig->keydata, sig, + getSigAlg(sigp), NULL) == SECSuccess) { + res = RPMRC_OK; + } + + if (sig != dig->sigdata) { + SECITEM_ZfreeItem(sig, 1); + } + } + +exit: + free(hash); + return res; + +} + +static pgpArmor decodePkts(uint8_t *b, uint8_t **pkt, size_t *pktlen) +{ + const char * enc = NULL; + const char * crcenc = NULL; + uint8_t * dec; + uint8_t * crcdec; + size_t declen; + size_t crclen; + uint32_t crcpkt, crc; + const char * armortype = NULL; + char * t, * te; + int pstate = 0; + pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */ + if (pgpIsPkt(b)) { +#ifdef NOTYET /* XXX ASCII Pubkeys only, please. */ + ec = 0; /* XXX fish out pkt type. */ +#endif + goto exit; + } + +#define TOKEQ(_s, _tok) (rstreqn((_s), (_tok), sizeof(_tok)-1)) + + for (t = (char *)b; t && *t; t = te) { + int rc; + if ((te = strchr(t, '\n')) == NULL) + te = t + strlen(t); + else + te++; + + switch (pstate) { + case 0: + armortype = NULL; + if (!TOKEQ(t, "-----BEGIN PGP ")) + continue; + t += sizeof("-----BEGIN PGP ")-1; + + rc = pgpValTok(pgpArmorTbl, t, te); + if (rc < 0) { + ec = PGPARMOR_ERR_UNKNOWN_ARMOR_TYPE; + goto exit; + } + if (rc != PGPARMOR_PUBKEY) /* XXX ASCII Pubkeys only, please. */ + continue; + + armortype = pgpValStr(pgpArmorTbl, rc); + t += strlen(armortype); + if (!TOKEQ(t, "-----")) + continue; + t += sizeof("-----")-1; + if (*t != '\n' && *t != '\r') + continue; + *t = '\0'; + pstate++; + break; + case 1: + enc = NULL; + rc = pgpValTok(pgpArmorKeyTbl, t, te); + if (rc >= 0) + continue; + if (*t != '\n' && *t != '\r') { + pstate = 0; + continue; + } + enc = te; /* Start of encoded packets */ + pstate++; + break; + case 2: + crcenc = NULL; + if (*t != '=') + continue; + *t++ = '\0'; /* Terminate encoded packets */ + crcenc = t; /* Start of encoded crc */ + pstate++; + break; + case 3: + pstate = 0; + if (!TOKEQ(t, "-----END PGP ")) { + ec = PGPARMOR_ERR_NO_END_PGP; + goto exit; + } + *t = '\0'; /* Terminate encoded crc */ + t += sizeof("-----END PGP ")-1; + if (t >= te) continue; + + if (armortype == NULL) /* XXX can't happen */ + continue; + if (!rstreqn(t, armortype, strlen(armortype))) + continue; + + t += strlen(armortype); + if (t >= te) continue; + + if (!TOKEQ(t, "-----")) { + ec = PGPARMOR_ERR_NO_END_PGP; + goto exit; + } + t += (sizeof("-----")-1); + if (t >= te) continue; + /* XXX permitting \r here is not RFC-2440 compliant <shrug> */ + if (!(*t == '\n' || *t == '\r')) continue; + + crcdec = NULL; + crclen = 0; + if (b64decode(crcenc, (void **)&crcdec, &crclen) != 0) { + ec = PGPARMOR_ERR_CRC_DECODE; + goto exit; + } + crcpkt = pgpGrab(crcdec, crclen); + crcdec = _free(crcdec); + dec = NULL; + declen = 0; + if (b64decode(enc, (void **)&dec, &declen) != 0) { + ec = PGPARMOR_ERR_BODY_DECODE; + goto exit; + } + crc = pgpCRC(dec, declen); + if (crcpkt != crc) { + ec = PGPARMOR_ERR_CRC_CHECK; + goto exit; + } + if (pkt) *pkt = dec; + if (pktlen) *pktlen = declen; + ec = PGPARMOR_PUBKEY; /* XXX ASCII Pubkeys only, please. */ + goto exit; + break; + } + } + ec = PGPARMOR_NONE; + +exit: + return ec; +} + +pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen) +{ + uint8_t * b = NULL; + ssize_t blen; + pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */ + int rc = rpmioSlurp(fn, &b, &blen); + if (rc == 0 && b != NULL && blen > 0) { + ec = decodePkts(b, pkt, pktlen); + } + free(b); + return ec; +} + +pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen) +{ + pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */ + if (armor && strlen(armor) > 0) { + uint8_t *b = (uint8_t*) xstrdup(armor); + ec = decodePkts(b, pkt, pktlen); + free(b); + } + return ec; +} + +char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns) +{ + char *buf = NULL, *val = NULL; + char *enc = b64encode(s, ns, -1); + char *crc = b64crc(s, ns); + const char *valstr = pgpValStr(pgpArmorTbl, atype); + + if (crc != NULL && enc != NULL) { + rasprintf(&buf, "%s=%s", enc, crc); + } + free(crc); + free(enc); + + rasprintf(&val, "-----BEGIN PGP %s-----\nVersion: rpm-" VERSION " (NSS-3)\n\n" + "%s\n-----END PGP %s-----\n", + valstr, buf != NULL ? buf : "", valstr); + + free(buf); + return val; +} + +/* + * Only flag for re-initialization here, in the common case the child + * exec()'s something else shutting down NSS here would be waste of time. + */ +static void at_forkchild(void) +{ + _new_process = 1; +} + +int rpmInitCrypto(void) { + int rc = 0; + + /* Lazy NSS shutdown for re-initialization after fork() */ + if (_new_process && _crypto_initialized) { + rpmFreeCrypto(); + } + + /* Initialize NSS if not already done */ + if (!_crypto_initialized) { + if (NSS_NoDB_Init(NULL) != SECSuccess) { + rc = -1; + } else { + _crypto_initialized = 1; + } + } + + /* Register one post-fork handler per process */ + if (_new_process) { + if (pthread_atfork(NULL, NULL, at_forkchild) != 0) { + rpmlog(RPMLOG_WARNING, _("Failed to register fork handler: %m\n")); + } + _new_process = 0; + } + return rc; +} + +int rpmFreeCrypto(void) +{ + int rc = 0; + if (_crypto_initialized) { + rc = (NSS_Shutdown() != SECSuccess); + _crypto_initialized = 0; + } + return rc; +} + + diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h new file mode 100644 index 0000000..6d484f3 --- /dev/null +++ b/rpmio/rpmpgp.h @@ -0,0 +1,1201 @@ +#ifndef H_RPMPGP +#define H_RPMPGP + +/** \ingroup rpmpgp + * \file rpmio/rpmpgp.h + * + * OpenPGP constants and structures from RFC-2440. + * + * Text from RFC-2440 in comments is + * Copyright (C) The Internet Society (1998). All Rights Reserved. + */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <rpm/rpmtypes.h> +#include <rpm/rpmstring.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmpgp + */ +typedef struct DIGEST_CTX_s * DIGEST_CTX; +typedef struct rpmDigestBundle_s * rpmDigestBundle; + +/** \ingroup rpmpgp + */ +typedef struct pgpDig_s * pgpDig; + +/** \ingroup rpmpgp + */ +typedef struct pgpDigParams_s * pgpDigParams; + +typedef uint8_t pgpKeyID_t[8]; +typedef uint8_t pgpTime_t[4]; + +/** \ingroup rpmpgp + * 4.3. Packet Tags + * + * The packet tag denotes what type of packet the body holds. Note that + * old format headers can only have tags less than 16, whereas new + * format headers can have tags as great as 63. + */ +typedef enum pgpTag_e { + PGPTAG_RESERVED = 0, /*!< Reserved/Invalid */ + PGPTAG_PUBLIC_SESSION_KEY = 1, /*!< Public-Key Encrypted Session Key */ + PGPTAG_SIGNATURE = 2, /*!< Signature */ + PGPTAG_SYMMETRIC_SESSION_KEY= 3, /*!< Symmetric-Key Encrypted Session Key*/ + PGPTAG_ONEPASS_SIGNATURE = 4, /*!< One-Pass Signature */ + PGPTAG_SECRET_KEY = 5, /*!< Secret Key */ + PGPTAG_PUBLIC_KEY = 6, /*!< Public Key */ + PGPTAG_SECRET_SUBKEY = 7, /*!< Secret Subkey */ + PGPTAG_COMPRESSED_DATA = 8, /*!< Compressed Data */ + PGPTAG_SYMMETRIC_DATA = 9, /*!< Symmetrically Encrypted Data */ + PGPTAG_MARKER = 10, /*!< Marker */ + PGPTAG_LITERAL_DATA = 11, /*!< Literal Data */ + PGPTAG_TRUST = 12, /*!< Trust */ + PGPTAG_USER_ID = 13, /*!< User ID */ + PGPTAG_PUBLIC_SUBKEY = 14, /*!< Public Subkey */ + PGPTAG_COMMENT_OLD = 16, /*!< Comment (from OpenPGP draft) */ + PGPTAG_PHOTOID = 17, /*!< PGP's photo ID */ + PGPTAG_ENCRYPTED_MDC = 18, /*!< Integrity protected encrypted data */ + PGPTAG_MDC = 19, /*!< Manipulaion detection code packet */ + PGPTAG_PRIVATE_60 = 60, /*!< Private or Experimental Values */ + PGPTAG_COMMENT = 61, /*!< Comment */ + PGPTAG_PRIVATE_62 = 62, /*!< Private or Experimental Values */ + PGPTAG_CONTROL = 63 /*!< Control (GPG) */ +} pgpTag; + +/** \ingroup rpmpgp + * 5.1. Public-Key Encrypted Session Key Packets (Tag 1) + * + * A Public-Key Encrypted Session Key packet holds the session key used + * to encrypt a message. Zero or more Encrypted Session Key packets + * (either Public-Key or Symmetric-Key) may precede a Symmetrically + * Encrypted Data Packet, which holds an encrypted message. The message + * is encrypted with the session key, and the session key is itself + * encrypted and stored in the Encrypted Session Key packet(s). The + * Symmetrically Encrypted Data Packet is preceded by one Public-Key + * Encrypted Session Key packet for each OpenPGP key to which the + * message is encrypted. The recipient of the message finds a session + * key that is encrypted to their public key, decrypts the session key, + * and then uses the session key to decrypt the message. + * + * The body of this packet consists of: + * - A one-octet number giving the version number of the packet type. + * The currently defined value for packet version is 3. An + * implementation should accept, but not generate a version of 2, + * which is equivalent to V3 in all other respects. + * - An eight-octet number that gives the key ID of the public key + * that the session key is encrypted to. + * - A one-octet number giving the public key algorithm used. + * - A string of octets that is the encrypted session key. This string + * takes up the remainder of the packet, and its contents are + * dependent on the public key algorithm used. + * + * Algorithm Specific Fields for RSA encryption + * - multiprecision integer (MPI) of RSA encrypted value m**e mod n. + * + * Algorithm Specific Fields for Elgamal encryption: + * - MPI of Elgamal (Diffie-Hellman) value g**k mod p. + * - MPI of Elgamal (Diffie-Hellman) value m * y**k mod p. + */ +typedef struct pgpPktPubkey_s { + uint8_t version; /*!< version number (generate 3, accept 2). */ + pgpKeyID_t keyid; /*!< key ID of the public key for session key. */ + uint8_t algo; /*!< public key algorithm used. */ +} pgpPktPubkey; + + +/** \ingroup rpmpgp + * 5.2.1. Signature Types + * + * There are a number of possible meanings for a signature, which are + * specified in a signature type octet in any given signature. + */ +typedef enum pgpSigType_e { + PGPSIGTYPE_BINARY = 0x00, /*!< Binary document */ + PGPSIGTYPE_TEXT = 0x01, /*!< Canonical text document */ + PGPSIGTYPE_STANDALONE = 0x02, /*!< Standalone */ + PGPSIGTYPE_GENERIC_CERT = 0x10, + /*!< Generic certification of a User ID & Public Key */ + PGPSIGTYPE_PERSONA_CERT = 0x11, + /*!< Persona certification of a User ID & Public Key */ + PGPSIGTYPE_CASUAL_CERT = 0x12, + /*!< Casual certification of a User ID & Public Key */ + PGPSIGTYPE_POSITIVE_CERT = 0x13, + /*!< Positive certification of a User ID & Public Key */ + PGPSIGTYPE_SUBKEY_BINDING = 0x18, /*!< Subkey Binding */ + PGPSIGTYPE_SIGNED_KEY = 0x1F, /*!< Signature directly on a key */ + PGPSIGTYPE_KEY_REVOKE = 0x20, /*!< Key revocation */ + PGPSIGTYPE_SUBKEY_REVOKE = 0x28, /*!< Subkey revocation */ + PGPSIGTYPE_CERT_REVOKE = 0x30, /*!< Certification revocation */ + PGPSIGTYPE_TIMESTAMP = 0x40 /*!< Timestamp */ +} pgpSigType; + +/** \ingroup rpmpgp + * 9.1. Public Key Algorithms + * +\verbatim + ID Algorithm + -- --------- + 1 - RSA (Encrypt or Sign) + 2 - RSA Encrypt-Only + 3 - RSA Sign-Only + 16 - Elgamal (Encrypt-Only), see [ELGAMAL] + 17 - DSA (Digital Signature Standard) + 18 - Reserved for Elliptic Curve + 19 - Reserved for ECDSA + 20 - Elgamal (Encrypt or Sign) + 21 - Reserved for Diffie-Hellman (X9.42, + as defined for IETF-S/MIME) + 100 to 110 - Private/Experimental algorithm. +\endverbatim + * + * Implementations MUST implement DSA for signatures, and Elgamal for + * encryption. Implementations SHOULD implement RSA keys. + * Implementations MAY implement any other algorithm. + */ +typedef enum pgpPubkeyAlgo_e { + PGPPUBKEYALGO_RSA = 1, /*!< RSA */ + PGPPUBKEYALGO_RSA_ENCRYPT = 2, /*!< RSA(Encrypt-Only) */ + PGPPUBKEYALGO_RSA_SIGN = 3, /*!< RSA(Sign-Only) */ + PGPPUBKEYALGO_ELGAMAL_ENCRYPT = 16, /*!< Elgamal(Encrypt-Only) */ + PGPPUBKEYALGO_DSA = 17, /*!< DSA */ + PGPPUBKEYALGO_EC = 18, /*!< Elliptic Curve */ + PGPPUBKEYALGO_ECDSA = 19, /*!< ECDSA */ + PGPPUBKEYALGO_ELGAMAL = 20, /*!< Elgamal */ + PGPPUBKEYALGO_DH = 21 /*!< Diffie-Hellman (X9.42) */ +} pgpPubkeyAlgo; + +/** \ingroup rpmpgp + * 9.2. Symmetric Key Algorithms + * +\verbatim + ID Algorithm + -- --------- + 0 - Plaintext or unencrypted data + 1 - IDEA [IDEA] + 2 - Triple-DES (DES-EDE, as per spec - + 168 bit key derived from 192) + 3 - CAST5 (128 bit key, as per RFC 2144) + 4 - Blowfish (128 bit key, 16 rounds) [BLOWFISH] + 5 - SAFER-SK128 (13 rounds) [SAFER] + 6 - Reserved for DES/SK + 7 - Reserved for AES with 128-bit key + 8 - Reserved for AES with 192-bit key + 9 - Reserved for AES with 256-bit key + 100 to 110 - Private/Experimental algorithm. +\endverbatim + * + * Implementations MUST implement Triple-DES. Implementations SHOULD + * implement IDEA and CAST5. Implementations MAY implement any other + * algorithm. + */ +typedef enum pgpSymkeyAlgo_e { + PGPSYMKEYALGO_PLAINTEXT = 0, /*!< Plaintext */ + PGPSYMKEYALGO_IDEA = 1, /*!< IDEA */ + PGPSYMKEYALGO_TRIPLE_DES = 2, /*!< 3DES */ + PGPSYMKEYALGO_CAST5 = 3, /*!< CAST5 */ + PGPSYMKEYALGO_BLOWFISH = 4, /*!< BLOWFISH */ + PGPSYMKEYALGO_SAFER = 5, /*!< SAFER */ + PGPSYMKEYALGO_DES_SK = 6, /*!< DES/SK */ + PGPSYMKEYALGO_AES_128 = 7, /*!< AES(128-bit key) */ + PGPSYMKEYALGO_AES_192 = 8, /*!< AES(192-bit key) */ + PGPSYMKEYALGO_AES_256 = 9, /*!< AES(256-bit key) */ + PGPSYMKEYALGO_TWOFISH = 10, /*!< TWOFISH(256-bit key) */ + PGPSYMKEYALGO_NOENCRYPT = 110 /*!< no encryption */ +} pgpSymkeyAlgo; + +/** \ingroup rpmpgp + * 9.3. Compression Algorithms + * +\verbatim + ID Algorithm + -- --------- + 0 - Uncompressed + 1 - ZIP (RFC 1951) + 2 - ZLIB (RFC 1950) + 100 to 110 - Private/Experimental algorithm. +\endverbatim + * + * Implementations MUST implement uncompressed data. Implementations + * SHOULD implement ZIP. Implementations MAY implement ZLIB. + */ +typedef enum pgpCompressAlgo_e { + PGPCOMPRESSALGO_NONE = 0, /*!< Uncompressed */ + PGPCOMPRESSALGO_ZIP = 1, /*!< ZIP */ + PGPCOMPRESSALGO_ZLIB = 2, /*!< ZLIB */ + PGPCOMPRESSALGO_BZIP2 = 3 /*!< BZIP2 */ +} pgpCompressAlgo; + +/** \ingroup rpmpgp + * 9.4. Hash Algorithms + * +\verbatim + ID Algorithm Text Name + -- --------- ---- ---- + 1 - MD5 "MD5" + 2 - SHA-1 "SHA1" + 3 - RIPE-MD/160 "RIPEMD160" + 4 - Reserved for double-width SHA (experimental) + 5 - MD2 "MD2" + 6 - Reserved for TIGER/192 "TIGER192" + 7 - Reserved for HAVAL (5 pass, 160-bit) + "HAVAL-5-160" + 100 to 110 - Private/Experimental algorithm. +\endverbatim + * + * Implementations MUST implement SHA-1. Implementations SHOULD + * implement MD5. + * @todo Add SHA256. + */ +typedef enum pgpHashAlgo_e { + PGPHASHALGO_MD5 = 1, /*!< MD5 */ + PGPHASHALGO_SHA1 = 2, /*!< SHA1 */ + PGPHASHALGO_RIPEMD160 = 3, /*!< RIPEMD160 */ + PGPHASHALGO_MD2 = 5, /*!< MD2 */ + PGPHASHALGO_TIGER192 = 6, /*!< TIGER192 */ + PGPHASHALGO_HAVAL_5_160 = 7, /*!< HAVAL-5-160 */ + PGPHASHALGO_SHA256 = 8, /*!< SHA256 */ + PGPHASHALGO_SHA384 = 9, /*!< SHA384 */ + PGPHASHALGO_SHA512 = 10, /*!< SHA512 */ + PGPHASHALGO_SHA224 = 11, /*!< SHA224 */ +} pgpHashAlgo; + +/** \ingroup rpmpgp + * 5.2.2. Version 3 Signature Packet Format + * + * The body of a version 3 Signature Packet contains: + * - One-octet version number (3). + * - One-octet length of following hashed material. MUST be 5. + * - One-octet signature type. + * - Four-octet creation time. + * - Eight-octet key ID of signer. + * - One-octet public key algorithm. + * - One-octet hash algorithm. + * - Two-octet field holding left 16 bits of signed hash value. + * - One or more multi-precision integers comprising the signature. + * + * Algorithm Specific Fields for RSA signatures: + * - multiprecision integer (MPI) of RSA signature value m**d. + * + * Algorithm Specific Fields for DSA signatures: + * - MPI of DSA value r. + * - MPI of DSA value s. + */ +typedef struct pgpPktSigV3_s { + uint8_t version; /*!< version number (3). */ + uint8_t hashlen; /*!< length of following hashed material. MUST be 5. */ + uint8_t sigtype; /*!< signature type. */ + pgpTime_t time; /*!< 4 byte creation time. */ + pgpKeyID_t signid; /*!< key ID of signer. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ + uint8_t hash_algo; /*!< hash algorithm. */ + uint8_t signhash16[2]; /*!< left 16 bits of signed hash value. */ +} * pgpPktSigV3; + +/** \ingroup rpmpgp + * 5.2.3. Version 4 Signature Packet Format + * + * The body of a version 4 Signature Packet contains: + * - One-octet version number (4). + * - One-octet signature type. + * - One-octet public key algorithm. + * - One-octet hash algorithm. + * - Two-octet scalar octet count for following hashed subpacket + * data. Note that this is the length in octets of all of the hashed + * subpackets; a pointer incremented by this number will skip over + * the hashed subpackets. + * - Hashed subpacket data. (zero or more subpackets) + * - Two-octet scalar octet count for following unhashed subpacket + * data. Note that this is the length in octets of all of the + * unhashed subpackets; a pointer incremented by this number will + * skip over the unhashed subpackets. + * - Unhashed subpacket data. (zero or more subpackets) + * - Two-octet field holding left 16 bits of signed hash value. + * - One or more multi-precision integers comprising the signature. + */ +typedef struct pgpPktSigV4_s { + uint8_t version; /*!< version number (4). */ + uint8_t sigtype; /*!< signature type. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ + uint8_t hash_algo; /*!< hash algorithm. */ + uint8_t hashlen[2]; /*!< length of following hashed material. */ +} * pgpPktSigV4; + +/** \ingroup rpmpgp + * 5.2.3.1. Signature Subpacket Specification + * + * The subpacket fields consist of zero or more signature subpackets. + * Each set of subpackets is preceded by a two-octet scalar count of the + * length of the set of subpackets. + * + * Each subpacket consists of a subpacket header and a body. The header + * consists of: + * - the subpacket length (1, 2, or 5 octets) + * - the subpacket type (1 octet) + * and is followed by the subpacket specific data. + * + * The length includes the type octet but not this length. Its format is + * similar to the "new" format packet header lengths, but cannot have + * partial body lengths. That is: +\verbatim + if the 1st octet < 192, then + lengthOfLength = 1 + subpacketLen = 1st_octet + + if the 1st octet >= 192 and < 255, then + lengthOfLength = 2 + subpacketLen = ((1st_octet - 192) << 8) + (2nd_octet) + 192 + + if the 1st octet = 255, then + lengthOfLength = 5 + subpacket length = [four-octet scalar starting at 2nd_octet] +\endverbatim + * + * The value of the subpacket type octet may be: + * +\verbatim + 2 = signature creation time + 3 = signature expiration time + 4 = exportable certification + 5 = trust signature + 6 = regular expression + 7 = revocable + 9 = key expiration time + 10 = placeholder for backward compatibility + 11 = preferred symmetric algorithms + 12 = revocation key + 16 = issuer key ID + 20 = notation data + 21 = preferred hash algorithms + 22 = preferred compression algorithms + 23 = key server preferences + 24 = preferred key server + 25 = primary user id + 26 = policy URL + 27 = key flags + 28 = signer's user id + 29 = reason for revocation + 100 to 110 = internal or user-defined +\endverbatim + * + * An implementation SHOULD ignore any subpacket of a type that it does + * not recognize. + * + * Bit 7 of the subpacket type is the "critical" bit. If set, it + * denotes that the subpacket is one that is critical for the evaluator + * of the signature to recognize. If a subpacket is encountered that is + * marked critical but is unknown to the evaluating software, the + * evaluator SHOULD consider the signature to be in error. + */ +typedef enum pgpSubType_e { + PGPSUBTYPE_NONE = 0, /*!< none */ + PGPSUBTYPE_SIG_CREATE_TIME = 2, /*!< signature creation time */ + PGPSUBTYPE_SIG_EXPIRE_TIME = 3, /*!< signature expiration time */ + PGPSUBTYPE_EXPORTABLE_CERT = 4, /*!< exportable certification */ + PGPSUBTYPE_TRUST_SIG = 5, /*!< trust signature */ + PGPSUBTYPE_REGEX = 6, /*!< regular expression */ + PGPSUBTYPE_REVOCABLE = 7, /*!< revocable */ + PGPSUBTYPE_KEY_EXPIRE_TIME = 9, /*!< key expiration time */ + PGPSUBTYPE_ARR = 10, /*!< additional recipient request */ + PGPSUBTYPE_PREFER_SYMKEY = 11, /*!< preferred symmetric algorithms */ + PGPSUBTYPE_REVOKE_KEY = 12, /*!< revocation key */ + PGPSUBTYPE_ISSUER_KEYID = 16, /*!< issuer key ID */ + PGPSUBTYPE_NOTATION = 20, /*!< notation data */ + PGPSUBTYPE_PREFER_HASH = 21, /*!< preferred hash algorithms */ + PGPSUBTYPE_PREFER_COMPRESS = 22, /*!< preferred compression algorithms */ + PGPSUBTYPE_KEYSERVER_PREFERS= 23, /*!< key server preferences */ + PGPSUBTYPE_PREFER_KEYSERVER = 24, /*!< preferred key server */ + PGPSUBTYPE_PRIMARY_USERID = 25, /*!< primary user id */ + PGPSUBTYPE_POLICY_URL = 26, /*!< policy URL */ + PGPSUBTYPE_KEY_FLAGS = 27, /*!< key flags */ + PGPSUBTYPE_SIGNER_USERID = 28, /*!< signer's user id */ + PGPSUBTYPE_REVOKE_REASON = 29, /*!< reason for revocation */ + PGPSUBTYPE_FEATURES = 30, /*!< feature flags (gpg) */ + PGPSUBTYPE_EMBEDDED_SIG = 32, /*!< embedded signature (gpg) */ + + PGPSUBTYPE_INTERNAL_100 = 100, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_101 = 101, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_102 = 102, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_103 = 103, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_104 = 104, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_105 = 105, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_106 = 106, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_107 = 107, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_108 = 108, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_109 = 109, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_110 = 110, /*!< internal or user-defined */ + + PGPSUBTYPE_CRITICAL = 128 /*!< critical subpacket marker */ +} pgpSubType; + +/** \ingroup rpmpgp + * 5.2. Signature Packet (Tag 2) + * + * A signature packet describes a binding between some public key and + * some data. The most common signatures are a signature of a file or a + * block of text, and a signature that is a certification of a user ID. + * + * Two versions of signature packets are defined. Version 3 provides + * basic signature information, while version 4 provides an expandable + * format with subpackets that can specify more information about the + * signature. PGP 2.6.x only accepts version 3 signatures. + * + * Implementations MUST accept V3 signatures. Implementations SHOULD + * generate V4 signatures. Implementations MAY generate a V3 signature + * that can be verified by PGP 2.6.x. + * + * Note that if an implementation is creating an encrypted and signed + * message that is encrypted to a V3 key, it is reasonable to create a + * V3 signature. + */ +typedef union pgpPktSig_u { + struct pgpPktSigV3_s v3; + struct pgpPktSigV4_s v4; +} * pgpPktSig; + +/** + * 5.3. Symmetric-Key Encrypted Session-Key Packets (Tag 3) + * + * The Symmetric-Key Encrypted Session Key packet holds the symmetric- + * key encryption of a session key used to encrypt a message. Zero or + * more Encrypted Session Key packets and/or Symmetric-Key Encrypted + * Session Key packets may precede a Symmetrically Encrypted Data Packet + * that holds an encrypted message. The message is encrypted with a + * session key, and the session key is itself encrypted and stored in + * the Encrypted Session Key packet or the Symmetric-Key Encrypted + * Session Key packet. + * + * If the Symmetrically Encrypted Data Packet is preceded by one or more + * Symmetric-Key Encrypted Session Key packets, each specifies a + * passphrase that may be used to decrypt the message. This allows a + * message to be encrypted to a number of public keys, and also to one + * or more pass phrases. This packet type is new, and is not generated + * by PGP 2.x or PGP 5.0. + * + * The body of this packet consists of: + * - A one-octet version number. The only currently defined version + * is 4. + * - A one-octet number describing the symmetric algorithm used. + * - A string-to-key (S2K) specifier, length as defined above. + * - Optionally, the encrypted session key itself, which is decrypted + * with the string-to-key object. + * + */ +typedef struct pgpPktSymkey_s { + uint8_t version; /*!< version number (4). */ + uint8_t symkey_algo; + uint8_t s2k[1]; +} pgpPktSymkey; + +/** \ingroup rpmpgp + * 5.4. One-Pass Signature Packets (Tag 4) + * + * The One-Pass Signature packet precedes the signed data and contains + * enough information to allow the receiver to begin calculating any + * hashes needed to verify the signature. It allows the Signature + * Packet to be placed at the end of the message, so that the signer can + * compute the entire signed message in one pass. + * + * A One-Pass Signature does not interoperate with PGP 2.6.x or earlier. + * + * The body of this packet consists of: + * - A one-octet version number. The current version is 3. + * - A one-octet signature type. Signature types are described in + * section 5.2.1. + * - A one-octet number describing the hash algorithm used. + * - A one-octet number describing the public key algorithm used. + * - An eight-octet number holding the key ID of the signing key. + * - A one-octet number holding a flag showing whether the signature + * is nested. A zero value indicates that the next packet is + * another One-Pass Signature packet that describes another + * signature to be applied to the same message data. + * + * Note that if a message contains more than one one-pass signature, + * then the signature packets bracket the message; that is, the first + * signature packet after the message corresponds to the last one-pass + * packet and the final signature packet corresponds to the first one- + * pass packet. + */ +typedef struct pgpPktOnepass_s { + uint8_t version; /*!< version number (3). */ + uint8_t sigtype; /*!< signature type. */ + uint8_t hash_algo; /*!< hash algorithm. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ + pgpKeyID_t signid; /*!< key ID of signer. */ + uint8_t nested; +} * pgpPktOnepass; + +/** \ingroup rpmpgp + * 5.5.1. Key Packet Variants + * + * 5.5.1.1. Public Key Packet (Tag 6) + * + * A Public Key packet starts a series of packets that forms an OpenPGP + * key (sometimes called an OpenPGP certificate). + * + * 5.5.1.2. Public Subkey Packet (Tag 14) + * + * A Public Subkey packet (tag 14) has exactly the same format as a + * Public Key packet, but denotes a subkey. One or more subkeys may be + * associated with a top-level key. By convention, the top-level key + * provides signature services, and the subkeys provide encryption + * services. + * + * Note: in PGP 2.6.x, tag 14 was intended to indicate a comment packet. + * This tag was selected for reuse because no previous version of PGP + * ever emitted comment packets but they did properly ignore them. + * Public Subkey packets are ignored by PGP 2.6.x and do not cause it to + * fail, providing a limited degree of backward compatibility. + * + * 5.5.1.3. Secret Key Packet (Tag 5) + * + * A Secret Key packet contains all the information that is found in a + * Public Key packet, including the public key material, but also + * includes the secret key material after all the public key fields. + * + * 5.5.1.4. Secret Subkey Packet (Tag 7) + * + * A Secret Subkey packet (tag 7) is the subkey analog of the Secret Key + * packet, and has exactly the same format. + * + * 5.5.2. Public Key Packet Formats + * + * There are two versions of key-material packets. Version 3 packets + * were first generated by PGP 2.6. Version 2 packets are identical in + * format to Version 3 packets, but are generated by PGP 2.5 or before. + * V2 packets are deprecated and they MUST NOT be generated. PGP 5.0 + * introduced version 4 packets, with new fields and semantics. PGP + * 2.6.x will not accept key-material packets with versions greater than + * 3. + * + * OpenPGP implementations SHOULD create keys with version 4 format. An + * implementation MAY generate a V3 key to ensure interoperability with + * old software; note, however, that V4 keys correct some security + * deficiencies in V3 keys. These deficiencies are described below. An + * implementation MUST NOT create a V3 key with a public key algorithm + * other than RSA. + * + * A version 3 public key or public subkey packet contains: + * - A one-octet version number (3). + * - A four-octet number denoting the time that the key was created. + * - A two-octet number denoting the time in days that this key is + * valid. If this number is zero, then it does not expire. + * - A one-octet number denoting the public key algorithm of this key + * - A series of multi-precision integers comprising the key + * material: + * - a multiprecision integer (MPI) of RSA public modulus n; + * - an MPI of RSA public encryption exponent e. + * + * V3 keys SHOULD only be used for backward compatibility because of + * three weaknesses in them. First, it is relatively easy to construct a + * V3 key that has the same key ID as any other key because the key ID + * is simply the low 64 bits of the public modulus. Secondly, because + * the fingerprint of a V3 key hashes the key material, but not its + * length, which increases the opportunity for fingerprint collisions. + * Third, there are minor weaknesses in the MD5 hash algorithm that make + * developers prefer other algorithms. See below for a fuller discussion + * of key IDs and fingerprints. + * + */ +typedef struct pgpPktKeyV3_s { + uint8_t version; /*!< version number (3). */ + pgpTime_t time; /*!< time that the key was created. */ + uint8_t valid[2]; /*!< time in days that this key is valid. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ +} * pgpPktKeyV3; + +/** \ingroup rpmpgp + * The version 4 format is similar to the version 3 format except for + * the absence of a validity period. This has been moved to the + * signature packet. In addition, fingerprints of version 4 keys are + * calculated differently from version 3 keys, as described in section + * "Enhanced Key Formats." + * + * A version 4 packet contains: + * - A one-octet version number (4). + * - A four-octet number denoting the time that the key was created. + * - A one-octet number denoting the public key algorithm of this key + * - A series of multi-precision integers comprising the key + * material. This algorithm-specific portion is: + * + * Algorithm Specific Fields for RSA public keys: + * - multiprecision integer (MPI) of RSA public modulus n; + * - MPI of RSA public encryption exponent e. + * + * Algorithm Specific Fields for DSA public keys: + * - MPI of DSA prime p; + * - MPI of DSA group order q (q is a prime divisor of p-1); + * - MPI of DSA group generator g; + * - MPI of DSA public key value y (= g**x where x is secret). + * + * Algorithm Specific Fields for Elgamal public keys: + * - MPI of Elgamal prime p; + * - MPI of Elgamal group generator g; + * - MPI of Elgamal public key value y (= g**x where x is + * secret). + * + */ +typedef struct pgpPktKeyV4_s { + uint8_t version; /*!< version number (4). */ + pgpTime_t time; /*!< time that the key was created. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ +} * pgpPktKeyV4; + +/** \ingroup rpmpgp + * 5.5.3. Secret Key Packet Formats + * + * The Secret Key and Secret Subkey packets contain all the data of the + * Public Key and Public Subkey packets, with additional algorithm- + * specific secret key data appended, in encrypted form. + * + * The packet contains: + * - A Public Key or Public Subkey packet, as described above + * - One octet indicating string-to-key usage conventions. 0 + * indicates that the secret key data is not encrypted. 255 + * indicates that a string-to-key specifier is being given. Any + * other value is a symmetric-key encryption algorithm specifier. + * - [Optional] If string-to-key usage octet was 255, a one-octet + * symmetric encryption algorithm. + * - [Optional] If string-to-key usage octet was 255, a string-to-key + * specifier. The length of the string-to-key specifier is implied + * by its type, as described above. + * - [Optional] If secret data is encrypted, eight-octet Initial + * Vector (IV). + * - Encrypted multi-precision integers comprising the secret key + * data. These algorithm-specific fields are as described below. + * - Two-octet checksum of the plaintext of the algorithm-specific + * portion (sum of all octets, mod 65536). + * + * Algorithm Specific Fields for RSA secret keys: + * - multiprecision integer (MPI) of RSA secret exponent d. + * - MPI of RSA secret prime value p. + * - MPI of RSA secret prime value q (p < q). + * - MPI of u, the multiplicative inverse of p, mod q. + * + * Algorithm Specific Fields for DSA secret keys: + * - MPI of DSA secret exponent x. + * + * Algorithm Specific Fields for Elgamal secret keys: + * - MPI of Elgamal secret exponent x. + * + * Secret MPI values can be encrypted using a passphrase. If a string- + * to-key specifier is given, that describes the algorithm for + * converting the passphrase to a key, else a simple MD5 hash of the + * passphrase is used. Implementations SHOULD use a string-to-key + * specifier; the simple hash is for backward compatibility. The cipher + * for encrypting the MPIs is specified in the secret key packet. + * + * Encryption/decryption of the secret data is done in CFB mode using + * the key created from the passphrase and the Initial Vector from the + * packet. A different mode is used with V3 keys (which are only RSA) + * than with other key formats. With V3 keys, the MPI bit count prefix + * (i.e., the first two octets) is not encrypted. Only the MPI non- + * prefix data is encrypted. Furthermore, the CFB state is + * resynchronized at the beginning of each new MPI value, so that the + * CFB block boundary is aligned with the start of the MPI data. + * + * With V4 keys, a simpler method is used. All secret MPI values are + * encrypted in CFB mode, including the MPI bitcount prefix. + * + * The 16-bit checksum that follows the algorithm-specific portion is + * the algebraic sum, mod 65536, of the plaintext of all the algorithm- + * specific octets (including MPI prefix and data). With V3 keys, the + * checksum is stored in the clear. With V4 keys, the checksum is + * encrypted like the algorithm-specific data. This value is used to + * check that the passphrase was correct. + * + */ +typedef union pgpPktKey_u { + struct pgpPktKeyV3_s v3; + struct pgpPktKeyV4_s v4; +} pgpPktKey; + +/* \ingroup rpmpgp + * 5.6. Compressed Data Packet (Tag 8) + * + * The Compressed Data packet contains compressed data. Typically, this + * packet is found as the contents of an encrypted packet, or following + * a Signature or One-Pass Signature packet, and contains literal data + * packets. + * + * The body of this packet consists of: + * - One octet that gives the algorithm used to compress the packet. + * - The remainder of the packet is compressed data. + * + * A Compressed Data Packet's body contains an block that compresses + * some set of packets. See section "Packet Composition" for details on + * how messages are formed. + * + * ZIP-compressed packets are compressed with raw RFC 1951 DEFLATE + * blocks. Note that PGP V2.6 uses 13 bits of compression. If an + * implementation uses more bits of compression, PGP V2.6 cannot + * decompress it. + * + * ZLIB-compressed packets are compressed with RFC 1950 ZLIB-style + * blocks. + */ +typedef struct pgpPktCdata_s { + uint8_t compressalgo; + uint8_t data[1]; +} pgpPktCdata; + +/* \ingroup rpmpgp + * 5.7. Symmetrically Encrypted Data Packet (Tag 9) + * + * The Symmetrically Encrypted Data packet contains data encrypted with + * a symmetric-key algorithm. When it has been decrypted, it will + * typically contain other packets (often literal data packets or + * compressed data packets). + * + * The body of this packet consists of: + * - Encrypted data, the output of the selected symmetric-key cipher + * operating in PGP's variant of Cipher Feedback (CFB) mode. + * + * The symmetric cipher used may be specified in an Public-Key or + * Symmetric-Key Encrypted Session Key packet that precedes the + * Symmetrically Encrypted Data Packet. In that case, the cipher + * algorithm octet is prefixed to the session key before it is + * encrypted. If no packets of these types precede the encrypted data, + * the IDEA algorithm is used with the session key calculated as the MD5 + * hash of the passphrase. + * + * The data is encrypted in CFB mode, with a CFB shift size equal to the + * cipher's block size. The Initial Vector (IV) is specified as all + * zeros. Instead of using an IV, OpenPGP prefixes a 10-octet string to + * the data before it is encrypted. The first eight octets are random, + * and the 9th and 10th octets are copies of the 7th and 8th octets, + * respectively. After encrypting the first 10 octets, the CFB state is + * resynchronized if the cipher block size is 8 octets or less. The + * last 8 octets of ciphertext are passed through the cipher and the + * block boundary is reset. + * + * The repetition of 16 bits in the 80 bits of random data prefixed to + * the message allows the receiver to immediately check whether the + * session key is incorrect. + */ +typedef struct pgpPktEdata_s { + uint8_t data[1]; +} pgpPktEdata; + +/* \ingroup rpmpgp + * 5.8. Marker Packet (Obsolete Literal Packet) (Tag 10) + * + * An experimental version of PGP used this packet as the Literal + * packet, but no released version of PGP generated Literal packets with + * this tag. With PGP 5.x, this packet has been re-assigned and is + * reserved for use as the Marker packet. + * + * The body of this packet consists of: + * - The three octets 0x50, 0x47, 0x50 (which spell "PGP" in UTF-8). + * + * Such a packet MUST be ignored when received. It may be placed at the + * beginning of a message that uses features not available in PGP 2.6.x + * in order to cause that version to report that newer software is + * necessary to process the message. + */ +/* \ingroup rpmpgp + * 5.9. Literal Data Packet (Tag 11) + * + * A Literal Data packet contains the body of a message; data that is + * not to be further interpreted. + * + * The body of this packet consists of: + * - A one-octet field that describes how the data is formatted. + * + * If it is a 'b' (0x62), then the literal packet contains binary data. + * If it is a 't' (0x74), then it contains text data, and thus may need + * line ends converted to local form, or other text-mode changes. RFC + * 1991 also defined a value of 'l' as a 'local' mode for machine-local + * conversions. This use is now deprecated. + * - File name as a string (one-octet length, followed by file name), + * if the encrypted data should be saved as a file. + * + * If the special name "_CONSOLE" is used, the message is considered to + * be "for your eyes only". This advises that the message data is + * unusually sensitive, and the receiving program should process it more + * carefully, perhaps avoiding storing the received data to disk, for + * example. + * - A four-octet number that indicates the modification date of the + * file, or the creation time of the packet, or a zero that + * indicates the present time. + * - The remainder of the packet is literal data. + * + * Text data is stored with <CR><LF> text endings (i.e. network-normal + * line endings). These should be converted to native line endings by + * the receiving software. + */ +typedef struct pgpPktLdata_s { + uint8_t format; + uint8_t filenamelen; + uint8_t filename[1]; +} pgpPktLdata; + +/* \ingroup rpmpgp + * 5.10. Trust Packet (Tag 12) + * + * The Trust packet is used only within keyrings and is not normally + * exported. Trust packets contain data that record the user's + * specifications of which key holders are trustworthy introducers, + * along with other information that implementing software uses for + * trust information. + * + * Trust packets SHOULD NOT be emitted to output streams that are + * transferred to other users, and they SHOULD be ignored on any input + * other than local keyring files. + */ +typedef struct pgpPktTrust_s { + uint8_t flag; +} pgpPktTrust; + +/* \ingroup rpmpgp + * 5.11. User ID Packet (Tag 13) + * + * A User ID packet consists of data that is intended to represent the + * name and email address of the key holder. By convention, it includes + * an RFC 822 mail name, but there are no restrictions on its content. + * The packet length in the header specifies the length of the user id. + * If it is text, it is encoded in UTF-8. + * + */ +typedef struct pgpPktUid_s { + uint8_t userid[1]; +} pgpPktUid; + +/** \ingroup rpmpgp + */ +union pgpPktPre_u { + pgpPktPubkey pubkey; /*!< 5.1. Public-Key Encrypted Session Key */ + pgpPktSig sig; /*!< 5.2. Signature */ + pgpPktSymkey symkey; /*!< 5.3. Symmetric-Key Encrypted Session-Key */ + pgpPktOnepass onepass; /*!< 5.4. One-Pass Signature */ + pgpPktKey key; /*!< 5.5. Key Material */ + pgpPktCdata cdata; /*!< 5.6. Compressed Data */ + pgpPktEdata edata; /*!< 5.7. Symmetrically Encrypted Data */ + /*!< 5.8. Marker (obsolete) */ + pgpPktLdata ldata; /*!< 5.9. Literal Data */ + pgpPktTrust tdata; /*!< 5.10. Trust */ + pgpPktUid uid; /*!< 5.11. User ID */ +}; + +/** \ingroup rpmpgp + */ +typedef enum pgpArmor_e { + PGPARMOR_ERR_CRC_CHECK = -7, + PGPARMOR_ERR_BODY_DECODE = -6, + PGPARMOR_ERR_CRC_DECODE = -5, + PGPARMOR_ERR_NO_END_PGP = -4, + PGPARMOR_ERR_UNKNOWN_PREAMBLE_TAG = -3, + PGPARMOR_ERR_UNKNOWN_ARMOR_TYPE = -2, + PGPARMOR_ERR_NO_BEGIN_PGP = -1, +#define PGPARMOR_ERROR PGPARMOR_ERR_NO_BEGIN_PGP + PGPARMOR_NONE = 0, + PGPARMOR_MESSAGE = 1, /*!< MESSAGE */ + PGPARMOR_PUBKEY = 2, /*!< PUBLIC KEY BLOCK */ + PGPARMOR_SIGNATURE = 3, /*!< SIGNATURE */ + PGPARMOR_SIGNED_MESSAGE = 4, /*!< SIGNED MESSAGE */ + PGPARMOR_FILE = 5, /*!< ARMORED FILE */ + PGPARMOR_PRIVKEY = 6, /*!< PRIVATE KEY BLOCK */ + PGPARMOR_SECKEY = 7 /*!< SECRET KEY BLOCK */ +} pgpArmor; + +/** \ingroup rpmpgp + */ +typedef enum pgpArmorKey_e { + PGPARMORKEY_VERSION = 1, /*!< Version: */ + PGPARMORKEY_COMMENT = 2, /*!< Comment: */ + PGPARMORKEY_MESSAGEID = 3, /*!< MessageID: */ + PGPARMORKEY_HASH = 4, /*!< Hash: */ + PGPARMORKEY_CHARSET = 5 /*!< Charset: */ +} pgpArmorKey; + +typedef enum pgpValType_e { + PGPVAL_TAG = 1, + PGPVAL_ARMORBLOCK = 2, + PGPVAL_ARMORKEY = 3, + PGPVAL_SIGTYPE = 4, + PGPVAL_SUBTYPE = 5, + PGPVAL_PUBKEYALGO = 6, + PGPVAL_SYMKEYALGO = 7, + PGPVAL_COMPRESSALGO = 8, + PGPVAL_HASHALGO = 9, + PGPVAL_SERVERPREFS = 10, +} pgpValType; + +/** \ingroup rpmpgp + * Bit(s) to control digest operation. + */ +enum rpmDigestFlags_e { + RPMDIGEST_NONE = 0 +}; + +typedef rpmFlags rpmDigestFlags; + +/** \ingroup rpmpgp + * Return string representation of am OpenPGP value. + * @param type type of value + * @param val byte value to lookup + * @return string value of byte + */ +const char * pgpValString(pgpValType type, uint8_t val); + +/** \ingroup rpmpgp + * Return (native-endian) integer from big-endian representation. + * @param s pointer to big-endian integer + * @param nbytes no. of bytes + * @return native-endian integer + */ +static inline +unsigned int pgpGrab(const uint8_t *s, size_t nbytes) +{ + size_t i = 0; + size_t nb = (nbytes <= sizeof(i) ? nbytes : sizeof(i)); + while (nb--) + i = (i << 8) | *s++; + return i; +} + +/** \ingroup rpmpgp + * Return length of an OpenPGP packet. + * @param s pointer to packet + * @retval *lenp no. of bytes in packet + * @return no. of bytes in length prefix + */ +static inline +size_t pgpLen(const uint8_t *s, size_t * lenp) +{ + if (*s < 192) { + (*lenp) = *s++; + return 1; + } else if (*s < 255) { + (*lenp) = ((((unsigned)s[0]) - 192) << 8) + s[1] + 192; + return 2; + } else { + (*lenp) = pgpGrab(s+1, (size_t) 4); + return 5; + } +} + +/** \ingroup rpmpgp + * Return hex formatted representation of bytes. + * @param p bytes + * @param plen no. of bytes + * @return hex formatted string (malloc'ed) + */ +char * pgpHexStr(const uint8_t *p, size_t plen); + +/** \ingroup rpmpgp + * Calculate OpenPGP public key fingerprint. + * @todo V3 non-RSA public keys not implemented. + * @param pkt OpenPGP packet (i.e. PGPTAG_PUBLIC_KEY) + * @param pktlen OpenPGP packet length (no. of bytes) + * @retval keyid public key fingerprint + * @return 0 on sucess, else -1 + */ +int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen, + pgpKeyID_t keyid); + +/** \ingroup rpmpgp +* Extract OpenPGP public key fingerprint from base64 encoded packet. +* @todo V3 non-RSA public keys not implemented. +* @param b64pkt base64 encoded openpgp packet +* @retval keyid public key fingerprint +* @return 8 (no. of bytes) on success, < 0 on error +*/ +int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid); + +/** \ingroup rpmpgp + * Print/parse a OpenPGP packet(s). + * @param pkts OpenPGP packet(s) + * @param pktlen OpenPGP packet(s) length (no. of bytes) + * @retval dig parsed output of signature/pubkey packet parameters + * @param printing should packets be printed? + * @return -1 on error, 0 on success + */ +int pgpPrtPkts(const uint8_t *pkts, size_t pktlen, pgpDig dig, int printing); + +/** \ingroup rpmpgp + * Parse armored OpenPGP packets from a file. + * @param fn file name + * @retval pkt dearmored OpenPGP packet(s) (malloced) + * @retval pktlen dearmored OpenPGP packet(s) length in bytes + * @return type of armor found + */ +pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen); + +/** \ingroup rpmpgp + * Parse armored OpenPGP packets from memory. + * @param armor armored OpenPGP packet string + * @retval pkt dearmored OpenPGP packet(s) (malloced) + * @retval pktlen dearmored OpenPGP packet(s) length in bytes + * @return type of armor found + */ +pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen); + +/** \ingroup rpmpgp + * Wrap a OpenPGP packets in ascii armor for transport. + * @param atype type of armor + * @param s binary pkt data + * @param ns binary pkt data length + * @return formatted string + */ +char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns); + +/** \ingroup rpmpgp + * Create a container for parsed OpenPGP packet(s). + * @return container + */ +pgpDig pgpNewDig(void); + +/** \ingroup rpmpgp + * Release (malloc'd) data from container. + * @param dig container + */ +void pgpCleanDig(pgpDig dig); + +/** \ingroup rpmpgp + * Destroy a container for parsed OpenPGP packet(s). + * @param dig container + * @return NULL always + */ +pgpDig pgpFreeDig(pgpDig dig); + +/** \ingroup rpmpgp + * Verify a PGP signature. + * @param dig container + * @param hashctx digest context + * @return RPMRC_OK on success + */ +rpmRC pgpVerifySig(pgpDig dig, DIGEST_CTX hashctx); + +/** \ingroup rpmpgp + * Return a string identification of a PGP signature/pubkey. + * @param digp signature/pubkey container + * @return string describing the item and parameters + */ +char *pgpIdentItem(pgpDigParams digp); + +/** \ingroup rpmpgp + * Perform cryptography initialization. + * It must be called before any cryptography can be used within rpm. + * It's not normally necessary to call it directly as it's called in + * general rpm initialization routines. + * @return 0 on success, -1 on failure + */ +int rpmInitCrypto(void); + +/** \ingroup rpmpgp + * Shutdown cryptography + */ +int rpmFreeCrypto(void); + +/** \ingroup rpmpgp + * Duplicate a digest context. + * @param octx existing digest context + * @return duplicated digest context + */ +DIGEST_CTX rpmDigestDup(DIGEST_CTX octx); + +/** \ingroup rpmpgp + * Obtain digest length in bytes. + * @param hashalgo type of digest + * @return digest length, zero on invalid algorithm + */ +size_t rpmDigestLength(int hashalgo); + +/** \ingroup rpmpgp + * Initialize digest. + * Set bit count to 0 and buffer to mysterious initialization constants. + * @param hashalgo type of digest + * @param flags bit(s) to control digest operation + * @return digest context + */ +DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags); + +/** \ingroup rpmpgp + * Update context with next plain text buffer. + * @param ctx digest context + * @param data next data buffer + * @param len no. bytes of data + * @return 0 on success + */ +int rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len); + +/** \ingroup rpmpgp + * Return digest and destroy context. + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + * + * @param ctx digest context + * @retval datap address of returned digest + * @retval lenp address of digest length + * @param asAscii return digest as ascii string? + * @return 0 on success + */ +int rpmDigestFinal(DIGEST_CTX ctx, + void ** datap, + size_t * lenp, int asAscii); + +/** \ingroup rpmpgp + * Create a new digest bundle. + * @return New digest bundle + */ +rpmDigestBundle rpmDigestBundleNew(void); + +/** \ingroup rpmpgp + * Free a digest bundle and all contained digest contexts. + * @param bundle digest bundle + * @return NULL always + */ +rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle); + +/** \ingroup rpmpgp + * Add a new type of digest to a bundle. + * @param bundle digest bundle + * @param algo type of digest + * @param flags bit(s) to control digest operation + * @return 0 on success + */ +int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo, + rpmDigestFlags flags); + +/** \ingroup rpmpgp + * Update contexts within bundle with next plain text buffer. + * @param bundle digest bundle + * @param data next data buffer + * @param len no. bytes of data + * @return 0 on success + */ +int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len); + +/** \ingroup rpmpgp + * Return digest from a bundle and destroy context, see rpmDigestFinal(). + * + * @param bundle digest bundle + * @param algo type of digest to return + * @retval datap address of returned digest + * @retval lenp address of digest length + * @param asAscii return digest as ascii string? + * @return 0 on success + */ +int rpmDigestBundleFinal(rpmDigestBundle bundle, + int algo, void ** datap, size_t * lenp, int asAscii); + +/** \ingroup rpmpgp + * Duplicate a digest context from a bundle. + * @param bundle digest bundle + * @param algo type of digest to dup + * @return duplicated digest context + */ +DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo); + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMPGP */ diff --git a/rpmio/rpmsq.c b/rpmio/rpmsq.c new file mode 100644 index 0000000..4e69ad0 --- /dev/null +++ b/rpmio/rpmsq.c @@ -0,0 +1,366 @@ +/** \ingroup rpmio + * \file rpmio/rpmsq.c + */ + +#include "system.h" + +#include <signal.h> +#include <sys/signal.h> +#include <sys/wait.h> +#include <search.h> +#include <errno.h> +#include <stdio.h> + +#include <pthread.h> + +/* XXX suggested in bugzilla #159024 */ +#if PTHREAD_MUTEX_DEFAULT != PTHREAD_MUTEX_NORMAL + #error RPM expects PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL +#endif + +#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +static pthread_mutex_t rpmsigTbl_lock = PTHREAD_MUTEX_INITIALIZER; +#else +static pthread_mutex_t rpmsigTbl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +#endif + +#define DO_LOCK() pthread_mutex_lock(&rpmsigTbl_lock); +#define DO_UNLOCK() pthread_mutex_unlock(&rpmsigTbl_lock); +#define ADD_REF(__tbl) (__tbl)->active++ +#define SUB_REF(__tbl) --(__tbl)->active + +#define ME() ((void *)pthread_self()) + +#define _RPMSQ_INTERNAL +#include <rpm/rpmsq.h> + +#include "debug.h" + +static struct rpmsqElem rpmsqRock; + +static rpmsq rpmsqQueue = &rpmsqRock; + +/** \ingroup rpmsq + * Insert node into from queue. + * @param elem node to link + * @param prev previous node from queue + * @return 0 on success + */ +static int rpmsqInsert(void * elem, void * prev) +{ + rpmsq sq = (rpmsq) elem; + int ret = -1; + + if (sq != NULL) { + ret = sighold(SIGCHLD); + if (ret == 0) { + sq->child = 0; + sq->reaped = 0; + sq->status = 0; + sq->reaper = 1; + sq->pipes[0] = sq->pipes[1] = -1; + + sq->id = ME(); + ret = pthread_mutex_init(&sq->mutex, NULL); + insque(elem, (prev != NULL ? prev : rpmsqQueue)); + ret = sigrelse(SIGCHLD); + } + } + return ret; +} + +/** \ingroup rpmsq + * Remove node from queue. + * @param elem node to link + * @return 0 on success + */ +static int rpmsqRemove(void * elem) +{ + rpmsq sq = (rpmsq) elem; + int ret = -1; + + if (elem != NULL) { + ret = sighold (SIGCHLD); + if (ret == 0) { + remque(elem); + + /* Unlock the mutex and then destroy it */ + if((ret = pthread_mutex_unlock(&sq->mutex)) == 0) + ret = pthread_mutex_destroy(&sq->mutex); + + sq->id = NULL; + if (sq->pipes[1]) ret = close(sq->pipes[1]); + if (sq->pipes[0]) ret = close(sq->pipes[0]); + sq->pipes[0] = sq->pipes[1] = -1; + ret = sigrelse(SIGCHLD); + } + } + return ret; +} + +static sigset_t rpmsqCaught; + +static struct rpmsig_s { + int signum; + rpmsqAction_t handler; + int active; + struct sigaction oact; +} rpmsigTbl[] = { + { SIGINT, rpmsqAction }, +#define rpmsigTbl_sigint (&rpmsigTbl[0]) + { SIGQUIT, rpmsqAction }, +#define rpmsigTbl_sigquit (&rpmsigTbl[1]) + { SIGCHLD, rpmsqAction }, +#define rpmsigTbl_sigchld (&rpmsigTbl[2]) + { SIGHUP, rpmsqAction }, +#define rpmsigTbl_sighup (&rpmsigTbl[3]) + { SIGTERM, rpmsqAction }, +#define rpmsigTbl_sigterm (&rpmsigTbl[4]) + { SIGPIPE, rpmsqAction }, +#define rpmsigTbl_sigpipe (&rpmsigTbl[5]) + { -1, NULL }, +}; + +int rpmsqIsCaught(int signum) +{ + return sigismember(&rpmsqCaught, signum); +} + +#ifdef SA_SIGINFO +void rpmsqAction(int signum, siginfo_t * info, void * context) +#else +void rpmsqAction(int signum) +#endif +{ + int save = errno; + rpmsig tbl; + + for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { + if (tbl->signum != signum) + continue; + + (void) sigaddset(&rpmsqCaught, signum); + + switch (signum) { + case SIGCHLD: + while (1) { + rpmsq sq; + int status = 0; + pid_t reaped = waitpid(0, &status, WNOHANG); + + /* XXX errno set to ECHILD/EINVAL/EINTR. */ + if (reaped <= 0) + break; + + /* XXX insque(3)/remque(3) are dequeue, not ring. */ + for (sq = rpmsqQueue->q_forw; + sq != NULL && sq != rpmsqQueue; + sq = sq->q_forw) + { + int ret; + + if (sq->child != reaped) + continue; + sq->reaped = reaped; + sq->status = status; + + /* Unlock the mutex. The waiter will then be able to + * aquire the lock. + * + * XXX: jbj, wtd, if this fails? + */ + ret = pthread_mutex_unlock(&sq->mutex); + + break; + } + } + break; + default: + break; + } + break; + } + errno = save; +} + +int rpmsqEnable(int signum, rpmsqAction_t handler) +{ + int tblsignum = (signum >= 0 ? signum : -signum); + struct sigaction sa; + rpmsig tbl; + int ret = -1; + + (void) DO_LOCK (); + if (rpmsqQueue->id == NULL) + rpmsqQueue->id = ME(); + for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { + if (tblsignum != tbl->signum) + continue; + + if (signum >= 0) { /* Enable. */ + if (ADD_REF(tbl) <= 0) { + (void) sigdelset(&rpmsqCaught, tbl->signum); + + /* XXX Don't set a signal handler if already SIG_IGN */ + (void) sigaction(tbl->signum, NULL, &tbl->oact); + if (tbl->oact.sa_handler == SIG_IGN) + continue; + + (void) sigemptyset (&sa.sa_mask); +#ifdef SA_SIGINFO + sa.sa_flags = SA_SIGINFO; +#else + sa.sa_flags = 0; +#endif + sa.sa_sigaction = (handler != NULL ? handler : tbl->handler); + if (sigaction(tbl->signum, &sa, &tbl->oact) < 0) { + SUB_REF(tbl); + break; + } + tbl->active = 1; /* XXX just in case */ + if (handler != NULL) + tbl->handler = handler; + } + } else { /* Disable. */ + if (SUB_REF(tbl) <= 0) { + if (sigaction(tbl->signum, &tbl->oact, NULL) < 0) + break; + tbl->active = 0; /* XXX just in case */ + tbl->handler = (handler != NULL ? handler : rpmsqAction); + } + } + ret = tbl->active; + break; + } + (void) DO_UNLOCK (); + return ret; +} + +pid_t rpmsqFork(rpmsq sq) +{ + pid_t pid; + int xx; + int nothreads = 0; /* XXX: Shouldn't this be a global? */ + + if (sq->reaper) { + xx = rpmsqInsert(sq, NULL); + xx = rpmsqEnable(SIGCHLD, NULL); + } + + xx = pipe(sq->pipes); + + xx = sighold(SIGCHLD); + + /* + * Initialize the cond var mutex. We have to aquire the lock we + * use for the condition before we fork. Otherwise it is possible for + * the child to exit, we get sigchild and the sig handler to send + * the condition signal before we are waiting on the condition. + */ + if (!nothreads) { + if(pthread_mutex_lock(&sq->mutex)) { + /* Yack we did not get the lock, lets just give up */ + xx = close(sq->pipes[0]); + xx = close(sq->pipes[1]); + sq->pipes[0] = sq->pipes[1] = -1; + goto out; + } + } + + pid = fork(); + if (pid < (pid_t) 0) { /* fork failed. */ + sq->child = (pid_t)-1; + xx = close(sq->pipes[0]); + xx = close(sq->pipes[1]); + sq->pipes[0] = sq->pipes[1] = -1; + goto out; + } else if (pid == (pid_t) 0) { /* Child. */ + int yy; + + /* Block to permit parent time to wait. */ + xx = close(sq->pipes[1]); + xx = read(sq->pipes[0], &yy, sizeof(yy)); + xx = close(sq->pipes[0]); + sq->pipes[0] = sq->pipes[1] = -1; + } else { /* Parent. */ + sq->child = pid; + } + +out: + xx = sigrelse(SIGCHLD); + return sq->child; +} + +/** + * Wait for child process to be reaped, and unregister SIGCHLD handler. + * @todo Rewrite to use waitpid on helper thread. + * @param sq scriptlet queue element + * @return 0 on success + */ +static int rpmsqWaitUnregister(rpmsq sq) +{ + int nothreads = 0; + int ret = 0; + int xx; + + /* Protect sq->reaped from handler changes. */ + ret = sighold(SIGCHLD); + + /* Start the child, linux often runs child before parent. */ + if (sq->pipes[0] >= 0) + xx = close(sq->pipes[0]); + if (sq->pipes[1] >= 0) + xx = close(sq->pipes[1]); + sq->pipes[0] = sq->pipes[1] = -1; + + /* Put a stopwatch on the time spent waiting to measure performance gain. */ + (void) rpmswEnter(&sq->op, -1); + + /* Wait for handler to receive SIGCHLD. */ + while (ret == 0 && sq->reaped != sq->child) { + if (nothreads) + /* Note that sigpause re-enables SIGCHLD. */ + ret = sigpause(SIGCHLD); + else { + xx = sigrelse(SIGCHLD); + + /* + * We start before the fork with this mutex locked; + * The only one that unlocks this the signal handler. + * So if we get the lock the child has been reaped. + */ + ret = pthread_mutex_lock(&sq->mutex); + xx = sighold(SIGCHLD); + } + } + + /* Accumulate stopwatch time spent waiting, potential performance gain. */ + sq->ms_scriptlets += rpmswExit(&sq->op, -1)/1000; + + xx = sigrelse(SIGCHLD); + + /* Remove processed SIGCHLD item from queue. */ + xx = rpmsqRemove(sq); + + /* Disable SIGCHLD handler on refcount == 0. */ + xx = rpmsqEnable(-SIGCHLD, NULL); + + return ret; +} + +pid_t rpmsqWait(rpmsq sq) +{ + if (sq->reaper) { + (void) rpmsqWaitUnregister(sq); + } else { + pid_t reaped; + int status; + do { + reaped = waitpid(sq->child, &status, 0); + } while (reaped >= 0 && reaped != sq->child); + sq->reaped = reaped; + sq->status = status; + } + + return sq->reaped; +} diff --git a/rpmio/rpmsq.h b/rpmio/rpmsq.h new file mode 100644 index 0000000..90087e0 --- /dev/null +++ b/rpmio/rpmsq.h @@ -0,0 +1,106 @@ +#ifndef H_RPMSQ +#define H_RPMSQ + +/** \ingroup rpmio + * \file rpmio/rpmsq.h + * + */ + +#include <rpm/rpmsw.h> +#include <signal.h> +#if defined(_RPMSQ_INTERNAL) +#include <pthread.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmsq + */ +typedef struct rpmsig_s * rpmsig; + +/** \ingroup rpmsq + */ +typedef struct rpmsqElem * rpmsq; + +/** \ingroup rpmsq + * Default signal handler prototype. + * @param signum signal number + * @param info (siginfo_t) signal info + * @param context signal context + */ +#ifdef SA_SIGINFO +typedef void (*rpmsqAction_t) (int signum, siginfo_t * info, void * context); +#else +typedef void (*rpmsqAction_t) (int signum); +#endif + +/* XXX make this fully opaque? */ +#if defined(_RPMSQ_INTERNAL) +/** + * SIGCHLD queue element. + */ +struct rpmsqElem { + struct rpmsqElem * q_forw; /*!< for use by insque(3)/remque(3). */ + struct rpmsqElem * q_back; + pid_t child; /*!< Currently running child. */ + volatile pid_t reaped; /*!< Reaped waitpid(3) return. */ + volatile int status; /*!< Reaped waitpid(3) status. */ + struct rpmop_s op; /*!< Scriptlet operation timestamp; */ + rpmtime_t ms_scriptlets; /*!< Accumulated script duration (msecs). */ + int reaper; /*!< Register SIGCHLD handler? */ + int pipes[2]; /*!< Parent/child interlock. */ + void * id; /*!< Blocking thread id (pthread_t). */ + pthread_mutex_t mutex; /*!< Signal delivery to thread condvar. */ + pthread_cond_t cond; +}; +#endif /* _RPMSQ_INTERNAL */ + +/** \ingroup rpmsq + * Test if given signal has been caught (while signals blocked). + * Similar to sigismember() but operates on internal signal queue. + * @param signum signal to test for + * @return 1 if caught, 0 if not and -1 on error + */ +int rpmsqIsCaught(int signum); + +/** \ingroup rpmsq + * Default signal handler. + * @param signum signal number + * @param info (siginfo_t) signal info + * @param context signal context + */ +#ifdef SA_SIGINFO +void rpmsqAction(int signum, siginfo_t * info, void * context); +#else +void rpmsqAction(int signum); +#endif + +/** \ingroup rpmsq + * Enable or disable a signal handler. + * @param signum signal to enable (or disable if negative) + * @param handler sa_sigaction handler (or NULL to use rpmsqHandler()) + * @return no. of refs, -1 on error + */ +int rpmsqEnable(int signum, rpmsqAction_t handler); + +/** \ingroup rpmsq + * Fork a child process. + * @param sq scriptlet queue element + * @return fork(2) pid + */ +pid_t rpmsqFork(rpmsq sq); + +/** \ingroup rpmsq + * Wait for child process to be reaped. + * @param sq scriptlet queue element + * @return reaped child pid + */ +pid_t rpmsqWait(rpmsq sq); + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMSQ */ diff --git a/rpmio/rpmstring.c b/rpmio/rpmstring.c new file mode 100644 index 0000000..0022b60 --- /dev/null +++ b/rpmio/rpmstring.c @@ -0,0 +1,192 @@ +/** + * \file rpmio/rpmstring.c + */ + +#include "system.h" + +#include <stdarg.h> +#include <stdio.h> + +#include <rpm/rpmstring.h> +#include "debug.h" + + +int rstrcasecmp(const char * s1, const char * s2) +{ + const char * p1 = s1; + const char * p2 = s2; + char c1, c2; + + if (p1 == p2) + return 0; + + do + { + c1 = rtolower (*p1++); + c2 = rtolower (*p2++); + if (c1 == '\0') + break; + } + while (c1 == c2); + + return (int)(c1 - c2); +} + +int rstrncasecmp(const char *s1, const char *s2, size_t n) +{ + const char * p1 = s1; + const char * p2 = s2; + char c1, c2; + + if (p1 == p2 || n == 0) + return 0; + + do + { + c1 = rtolower (*p1++); + c2 = rtolower (*p2++); + if (c1 == '\0' || c1 != c2) + break; + } while (--n > 0); + + return (int)(c1 - c2); +} + +/* + * Simple and stupid asprintf() clone. + * FIXME: write to work with non-C99 vsnprintf or check for one in configure. + */ +int rasprintf(char **strp, const char *fmt, ...) +{ + int n; + va_list ap; + char * p = NULL; + + if (strp == NULL) + return -1; + + va_start(ap, fmt); + n = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (n >= -1) { + size_t nb = n + 1; + p = xmalloc(nb); + va_start(ap, fmt); + n = vsnprintf(p, nb, fmt, ap); + va_end(ap); + } + *strp = p; + return n; +} + +/* + * Concatenate two strings with dynamically (re)allocated + * memory what prevents static buffer overflows by design. + * *dest is reallocated to the size of strings to concatenate. + * + * Note: + * 1) char *buf = rstrcat(NULL,"string"); is the same like rstrcat(&buf,"string"); + * 2) rstrcat(&buf,NULL) returns buf + * 3) rstrcat(NULL,NULL) returns NULL + * 4) *dest and src can overlap + */ +char *rstrcat(char **dest, const char *src) +{ + if ( src == NULL ) { + return dest != NULL ? *dest : NULL; + } + + if ( dest == NULL ) { + return xstrdup(src); + } + + { + size_t dest_size = *dest != NULL ? strlen(*dest) : 0; + size_t src_size = strlen(src); + + *dest = xrealloc(*dest, dest_size+src_size+1); /* include '\0' */ + memmove(&(*dest)[dest_size], src, src_size+1); + } + + return *dest; +} + +/* + * Concatenate strings with dynamically (re)allocated + * memory what prevents static buffer overflows by design. + * *dest is reallocated to the size of strings to concatenate. + * List of strings has to be NULL terminated. + * + * Note: + * 1) char *buf = rstrscat(NULL,"string",NULL); is the same like rstrscat(&buf,"string",NULL); + * 2) rstrscat(&buf,NULL) returns buf + * 3) rstrscat(NULL,NULL) returns NULL + * 4) *dest and argument strings can overlap + */ +char *rstrscat(char **dest, const char *arg, ...) +{ + va_list ap; + size_t arg_size, dst_size; + const char *s; + char *dst, *p; + + dst = dest ? *dest : NULL; + + if ( arg == NULL ) { + return dst; + } + + va_start(ap, arg); + for (arg_size=0, s=arg; s; s = va_arg(ap, const char *)) + arg_size += strlen(s); + va_end(ap); + + dst_size = dst ? strlen(dst) : 0; + dst = xrealloc(dst, dst_size+arg_size+1); /* include '\0' */ + p = &dst[dst_size]; + + va_start(ap, arg); + for (s = arg; s; s = va_arg(ap, const char *)) { + size_t size = strlen(s); + memmove(p, s, size); + p += size; + } + va_end(ap); + *p = '\0'; + + if ( dest ) { + *dest = dst; + } + + return dst; +} + +/* + * Adapted from OpenBSD, strlcpy() originally developed by + * Todd C. Miller <Todd.Miller@courtesan.com> + */ +size_t rstrlcpy(char *dest, const char *src, size_t n) +{ + char *d = dest; + const char *s = src; + size_t len = n; + + /* Copy as many bytes as will fit */ + if (len != 0) { + while (--len != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (len == 0) { + if (n != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return s - src - 1; /* count does not include NUL */ +} diff --git a/rpmio/rpmstring.h b/rpmio/rpmstring.h new file mode 100644 index 0000000..4e8a5db --- /dev/null +++ b/rpmio/rpmstring.h @@ -0,0 +1,179 @@ +#ifndef _RPMSTRING_H_ +#define _RPMSTRING_H_ + +/** \ingroup rpmstring + * \file rpmio/rpmstring.h + * String manipulation helper functions + */ + +#include <stddef.h> +#include <string.h> + +#include <rpm/rpmutil.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmstring + * Locale insensitive islower(3) + */ +RPM_GNUC_CONST +static inline int rislower(int c) { + return (c >= 'a' && c <= 'z'); +} + +/** \ingroup rpmstring + * Locale insensitive isupper(3) + */ +RPM_GNUC_CONST +static inline int risupper(int c) { + return (c >= 'A' && c <= 'Z'); +} + +/** \ingroup rpmstring + * Locale insensitive isalpha(3) + */ +RPM_GNUC_CONST +static inline int risalpha(int c) { + return (rislower(c) || risupper(c)); +} + +/** \ingroup rpmstring + * Locale insensitive isdigit(3) + */ +RPM_GNUC_CONST +static inline int risdigit(int c) { + return (c >= '0' && c <= '9'); +} + +/** \ingroup rpmstring + * Locale insensitive isalnum(3) + */ +RPM_GNUC_CONST +static inline int risalnum(int c) { + return (risalpha(c) || risdigit(c)); +} + +/** \ingroup rpmstring + * Locale insensitive isblank(3) + */ +RPM_GNUC_CONST +static inline int risblank(int c) { + return (c == ' ' || c == '\t'); +} + +/** \ingroup rpmstring + * Locale insensitive isspace(3) + */ +RPM_GNUC_CONST +static inline int risspace(int c) { + return (risblank(c) || c == '\n' || c == '\r' || c == '\f' || c == '\v'); +} + +/** \ingroup rpmstring + * Locale insensitive tolower(3) + */ +RPM_GNUC_CONST +static inline int rtolower(int c) { + return ((risupper(c)) ? (c | ('a' - 'A')) : c); +} + +/** \ingroup rpmstring + * Locale insensitive toupper(3) + */ +RPM_GNUC_CONST +static inline int rtoupper(int c) { + return ((rislower(c)) ? (c & ~('a' - 'A')) : c); +} + +/** + * Convert hex to binary nibble. + * @param c hex character + * @return binary nibble + */ +RPM_GNUC_CONST +static inline unsigned char rnibble(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'A' && c <= 'F') + return (c - 'A') + 10; + if (c >= 'a' && c <= 'f') + return (c - 'a') + 10; + return 0; +} + +/** + * Test for string equality + * @param s1 string 1 + * @param s2 string 2 + * @return 0 if strings differ, 1 if equal + */ +static inline int rstreq(const char *s1, const char *s2) +{ + return (strcmp(s1, s2) == 0); +} + +/** + * Test for string equality + * @param s1 string 1 + * @param s2 string 2 + * @param n compare at most n characters + * @return 0 if strings differ, 1 if equal + */ +static inline int rstreqn(const char *s1, const char *s2, size_t n) +{ + return (strncmp(s1, s2, n) == 0); +} + +/** \ingroup rpmstring + * Locale insensitive strcasecmp(3). + */ +RPM_GNUC_PURE +int rstrcasecmp(const char * s1, const char * s2) ; + +/** \ingroup rpmstring + * Locale insensitive strncasecmp(3). + */ +RPM_GNUC_PURE +int rstrncasecmp(const char *s1, const char * s2, size_t n) ; + +/** \ingroup rpmstring + * asprintf() clone + */ +int rasprintf(char **strp, const char *fmt, ...) RPM_GNUC_PRINTF(2, 3); + +/** \ingroup rpmstring + * Concatenate two strings with dynamically (re)allocated memory. + * @param dest pointer to destination string + * @param src source string + * @return realloc'd dest with src appended + */ +char *rstrcat(char **dest, const char *src); + +/** \ingroup rpmstring + * Concatenate multiple strings with dynamically (re)allocated memory. + * @param dest pointer to destination string + * @param arg NULL terminated list of strings to concatenate + * @return realloc'd dest with strings appended + */ +char *rstrscat(char **dest, const char *arg, ...) RPM_GNUC_NULL_TERMINATED; + +/** \ingroup rpmstring + * strlcpy() clone: + * Copy src to string dest of size n. At most n-1 characters + * will be copied. Always zero-terminates (unless n == 0). + * Length of src is returned; if retval >= n, truncation occurred. + * @param dest destination buffer + * @param src string to copy + * @param n destination buffer size + * @return length of src string + */ +size_t rstrlcpy(char *dest, const char *src, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPMSTRING_H_ */ diff --git a/rpmio/rpmsw.c b/rpmio/rpmsw.c new file mode 100644 index 0000000..2a6d70a --- /dev/null +++ b/rpmio/rpmsw.c @@ -0,0 +1,131 @@ +/** \ingroup rpmio + * \file rpmio/rpmsw.c + */ + +#include "system.h" +#include <rpm/rpmsw.h> +#include "debug.h" + +static rpmtime_t rpmsw_overhead = 0; + +static rpmtime_t rpmsw_cycles = 1; + +static int rpmsw_initialized = 0; + +rpmsw rpmswNow(rpmsw sw) +{ + if (!rpmsw_initialized) + (void) rpmswInit(); + if (sw == NULL) + return NULL; + if (gettimeofday(&sw->u.tv, NULL)) + return NULL; + return sw; +} + +/** \ingroup rpmio + * Return difference of 2 timeval stamps in micro-seconds. + * @param *etv end timeval + * @param *btv begin timeval + * @return difference in milli-seconds + */ +static inline +rpmtime_t tvsub(const struct timeval * etv, + const struct timeval * btv) +{ + time_t secs, usecs; + if (etv == NULL || btv == NULL) return 0; + secs = etv->tv_sec - btv->tv_sec; + for (usecs = etv->tv_usec - btv->tv_usec; usecs < 0; usecs += 1000000) + secs--; + return ((secs * 1000000) + usecs); +} + +rpmtime_t rpmswDiff(rpmsw end, rpmsw begin) +{ + unsigned long long ticks = 0; + + if (end == NULL || begin == NULL) + return 0; + ticks = tvsub(&end->u.tv, &begin->u.tv); + if (ticks >= rpmsw_overhead) + ticks -= rpmsw_overhead; + if (rpmsw_cycles > 1) + ticks /= rpmsw_cycles; + return ticks; +} + +rpmtime_t rpmswInit(void) +{ + struct rpmsw_s begin, end; + rpmtime_t sum_overhead = 0; + int i; + + rpmsw_initialized = 1; + + rpmsw_overhead = 0; + rpmsw_cycles = 0; + + /* Convergence for simultaneous cycles and overhead is overkill ... */ + for (i = 0; i < 3; i++) { + /* Calculate timing overhead in usecs. */ + (void) rpmswNow(&begin); + sum_overhead += rpmswDiff(rpmswNow(&end), &begin); + + rpmsw_overhead = sum_overhead/(i+1); + } + + return rpmsw_overhead; +} + +int rpmswEnter(rpmop op, ssize_t rc) +{ + if (op == NULL) + return 0; + + op->count++; + if (rc < 0) { + op->bytes = 0; + op->usecs = 0; + } + (void) rpmswNow(&op->begin); + return 0; +} + +rpmtime_t rpmswExit(rpmop op, ssize_t rc) +{ + struct rpmsw_s end; + + if (op == NULL) + return 0; + + op->usecs += rpmswDiff(rpmswNow(&end), &op->begin); + if (rc > 0) + op->bytes += rc; + op->begin = end; /* structure assignment */ + return op->usecs; +} + +rpmtime_t rpmswAdd(rpmop to, rpmop from) +{ + rpmtime_t usecs = 0; + if (to != NULL && from != NULL) { + to->count += from->count; + to->bytes += from->bytes; + to->usecs += from->usecs; + usecs = to->usecs; + } + return usecs; +} + +rpmtime_t rpmswSub(rpmop to, rpmop from) +{ + rpmtime_t usecs = 0; + if (to != NULL && from != NULL) { + to->count -= from->count; + to->bytes -= from->bytes; + to->usecs -= from->usecs; + usecs = to->usecs; + } + return usecs; +} diff --git a/rpmio/rpmsw.h b/rpmio/rpmsw.h new file mode 100644 index 0000000..7c285e7 --- /dev/null +++ b/rpmio/rpmsw.h @@ -0,0 +1,104 @@ +#ifndef H_RPMSW +#define H_RPMSW + +/** \ingroup rpmio + * \file rpmio/rpmsw.h + */ + +#include <unistd.h> +#include <sys/time.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmsw + */ +typedef unsigned long int rpmtime_t; + +/** \ingroup rpmsw + */ +typedef struct rpmsw_s * rpmsw; + +/** \ingroup rpmsw + */ +typedef struct rpmop_s * rpmop; + +/** \ingroup rpmsw + */ +struct rpmsw_s { + union { + struct timeval tv; + unsigned long long int ticks; + unsigned long int tocks[2]; + } u; +}; + +/** \ingroup rpmsw + * Cumulative statistics for an operation. + */ +struct rpmop_s { + struct rpmsw_s begin; /*!< Starting time stamp. */ + int count; /*!< Number of operations. */ + size_t bytes; /*!< Number of bytes transferred. */ + rpmtime_t usecs; /*!< Number of ticks. */ +}; + +/** \ingroup rpmsw + * Return benchmark time stamp. + * @param *sw time stamp + * @return 0 on success + */ +rpmsw rpmswNow(rpmsw sw); + +/** \ingroup rpmsw + * Return benchmark time stamp difference. + * @param *end end time stamp + * @param *begin begin time stamp + * @return difference in micro-seconds + */ +rpmtime_t rpmswDiff(rpmsw end, rpmsw begin); + +/** \ingroup rpmsw + * Return benchmark time stamp overhead. + * @return overhead in micro-seconds + */ +rpmtime_t rpmswInit(void); + +/** \ingroup rpmsw + * Enter timed operation. + * @param op operation statistics + * @param rc -1 clears usec counter + * @return 0 always + */ +int rpmswEnter(rpmop op, ssize_t rc); + +/** \ingroup rpmsw + * Exit timed operation. + * @param op operation statistics + * @param rc per-operation data (e.g. bytes transferred) + * @return cumulative usecs for operation + */ +rpmtime_t rpmswExit(rpmop op, ssize_t rc); + +/** \ingroup rpmsw + * Sum statistic counters. + * @param to result statistics + * @param from operation statistics + * @return cumulative usecs for operation + */ +rpmtime_t rpmswAdd(rpmop to, rpmop from); + +/** \ingroup rpmsw + * Subtract statistic counters. + * @param to result statistics + * @param from operation statistics + * @return cumulative usecs for operation + */ +rpmtime_t rpmswSub(rpmop to, rpmop from); + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMSW */ diff --git a/rpmio/rpmurl.h b/rpmio/rpmurl.h new file mode 100644 index 0000000..3384bc4 --- /dev/null +++ b/rpmio/rpmurl.h @@ -0,0 +1,52 @@ +#ifndef H_RPMURL +#define H_RPMURL + +/** \ingroup rpmio + * \file rpmio/rpmurl.h + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmurl + * Supported URL types. + */ +typedef enum urltype_e { + URL_IS_UNKNOWN = 0, /*!< unknown (aka a file) */ + URL_IS_DASH = 1, /*!< stdin/stdout */ + URL_IS_PATH = 2, /*!< file://... */ + URL_IS_FTP = 3, /*!< ftp://... */ + URL_IS_HTTP = 4, /*!< http://... */ + URL_IS_HTTPS = 5, /*!< https://... */ + URL_IS_HKP = 6 /*!< hkp://... */ +} urltype; + +/** \ingroup rpmurl + * Return type of URL. + * @param url url string + * @return type of url + */ +urltype urlIsURL(const char * url); + +/** \ingroup rpmurl + * Return path component of URL. + * @param url url string + * @retval pathp pointer to path component of url + * @return type of url + */ +urltype urlPath(const char * url, const char ** pathp); + +/** \ingroup rpmurl + * Copy data from URL to local file. + * @param url url string of source + * @param dest file name of destination + * @return 0 on success, otherwise FTPERR_* code + */ +int urlGetFile(const char * url, const char * dest); + +#ifdef __cplusplus +} +#endif + +#endif /* H_RPMURL */ diff --git a/rpmio/rpmutil.h b/rpmio/rpmutil.h new file mode 100644 index 0000000..68a21f9 --- /dev/null +++ b/rpmio/rpmutil.h @@ -0,0 +1,159 @@ +#ifndef _RPMUTIL_H +#define _RPMUTIL_H + +#include <unistd.h> + +/* + * Miscellanous utility macros: + * - portability wrappers for various gcc extensions like __attribute__() + * - ... + * + * Copied from glib, names replaced to avoid clashing with glib. + * + */ + +/* Here we provide RPM_GNUC_EXTENSION as an alias for __extension__, + * where this is valid. This allows for warningless compilation of + * "long long" types even in the presence of '-ansi -pedantic'. + */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +# define RPM_GNUC_EXTENSION __extension__ +#else +# define RPM_GNUC_EXTENSION +#endif + +/* Provide macros to feature the GCC function attribute. + */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) +#define RPM_GNUC_PURE \ + __attribute__((__pure__)) +#define RPM_GNUC_MALLOC \ + __attribute__((__malloc__)) +#else +#define RPM_GNUC_PURE +#define RPM_GNUC_MALLOC +#endif + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) +#define RPM_GNUC_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) +#define RPM_GNUC_ALLOC_SIZE2(x,y) __attribute__((__alloc_size__(x,y))) +#else +#define RPM_GNUC_ALLOC_SIZE(x) +#define RPM_GNUC_ALLOC_SIZE2(x,y) +#endif + +#if __GNUC__ >= 4 +#define RPM_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +#else +#define RPM_GNUC_NULL_TERMINATED +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define RPM_GNUC_PRINTF( format_idx, arg_idx ) \ + __attribute__((__format__ (__printf__, format_idx, arg_idx))) +#define RPM_GNUC_SCANF( format_idx, arg_idx ) \ + __attribute__((__format__ (__scanf__, format_idx, arg_idx))) +#define RPM_GNUC_FORMAT( arg_idx ) \ + __attribute__((__format_arg__ (arg_idx))) +#define RPM_GNUC_NORETURN \ + __attribute__((__noreturn__)) +#define RPM_GNUC_CONST \ + __attribute__((__const__)) +#define RPM_GNUC_UNUSED \ + __attribute__((__unused__)) +#define RPM_GNUC_NO_INSTRUMENT \ + __attribute__((__no_instrument_function__)) +#else /* !__GNUC__ */ +#define RPM_GNUC_PRINTF( format_idx, arg_idx ) +#define RPM_GNUC_SCANF( format_idx, arg_idx ) +#define RPM_GNUC_FORMAT( arg_idx ) +#define RPM_GNUC_NORETURN +#define RPM_GNUC_CONST +#define RPM_GNUC_UNUSED +#define RPM_GNUC_NO_INSTRUMENT +#endif /* !__GNUC__ */ + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +#define RPM_GNUC_DEPRECATED \ + __attribute__((__deprecated__)) +#else +#define RPM_GNUC_DEPRECATED +#endif /* __GNUC__ */ + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +#define RPM_GNUC_MAY_ALIAS __attribute__((may_alias)) +#define RPM_GNUC_NONNULL( ... ) \ + __attribute__((__nonnull__ (__VA_ARGS__))) +#else +#define RPM_GNUC_MAY_ALIAS +#define RPM_GNUC_NONNULL( ... ) +#endif + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define RPM_GNUC_WARN_UNUSED_RESULT \ + __attribute__((warn_unused_result)) +#else +#define RPM_GNUC_WARN_UNUSED_RESULT +#endif /* __GNUC__ */ + +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +# define RPM_GNUC_INTERNAL __attribute__((visibility("hidden"))) +#else +# define RPM_GNUC_INTERNAL +#endif + + +/* Guard C code in headers, while including them from C++ */ +#ifdef __cplusplus +# define RPM_BEGIN_DECLS extern "C" { +# define RPM_END_DECLS } +#else +# define RPM_BEGIN_DECLS +# define RPM_END_DECLS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Rpm specific allocators which never return NULL but terminate on failure */ +RPM_GNUC_MALLOC RPM_GNUC_ALLOC_SIZE(1) +void * rmalloc(size_t size); + +RPM_GNUC_MALLOC RPM_GNUC_ALLOC_SIZE2(1,2) +void * rcalloc(size_t nmemb, size_t size); + +RPM_GNUC_ALLOC_SIZE(2) +void * rrealloc(void *ptr, size_t size); + +char * rstrdup(const char *str); + +/* Rpm specific free() which returns NULL */ +void * rfree(void *ptr); + +/** \ingroup rpmutil + * Memory allocation failure callback prototype. When registered through + * rpmSetMemFail(), this gets called if memory allocation through rmalloc() + * and friends fails. If the application can somehow recover memory here, + * it can return a newly allocated memory block of requested size, otherwise + * it must return NULL after performing it's own shutdown deeds or + * terminate itself. + * @param size Size of allocation request in bytes + * @param data User data (or NULL) + * @return Allocated memory block of requested size or NULL + */ +typedef void * (*rpmMemFailFunc) (size_t size, void *data); + +/** \ingroup rpmutil + * Set memory allocation failure callback. + * @param func Allocation failure callback function + * @param data User data (or NULL) + * @return Previous callback function + */ +rpmMemFailFunc rpmSetMemFail(rpmMemFailFunc func, void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPMUTIL_H */ diff --git a/rpmio/url.c b/rpmio/url.c new file mode 100644 index 0000000..c609fd1 --- /dev/null +++ b/rpmio/url.c @@ -0,0 +1,127 @@ +/** \ingroup rpmio + * \file rpmio/url.c + */ + +#include "system.h" + +#include <sys/wait.h> + +#include <rpm/rpmmacro.h> +#include <rpm/rpmlog.h> +#include <rpm/rpmurl.h> +#include <rpm/rpmio.h> +#include <rpm/argv.h> +#include <rpm/rpmstring.h> + +#include "debug.h" + +/** + */ +static struct urlstring { + const char * leadin; + urltype ret; +} const urlstrings[] = { + { "file://", URL_IS_PATH }, + { "ftp://", URL_IS_FTP }, + { "hkp://", URL_IS_HKP }, + { "http://", URL_IS_HTTP }, + { "https://", URL_IS_HTTPS }, + { NULL, URL_IS_UNKNOWN } +}; + +urltype urlIsURL(const char * url) +{ + const struct urlstring *us; + + if (url && *url) { + for (us = urlstrings; us->leadin != NULL; us++) { + if (!rstreqn(url, us->leadin, strlen(us->leadin))) + continue; + return us->ret; + } + if (rstreq(url, "-")) + return URL_IS_DASH; + } + + return URL_IS_UNKNOWN; +} + +/* Return path portion of url (or pointer to NUL if url == NULL) */ +urltype urlPath(const char * url, const char ** pathp) +{ + const char *path; + urltype type; + + path = url; + type = urlIsURL(url); + switch (type) { + case URL_IS_FTP: + url += sizeof("ftp://") - 1; + path = strchr(url, '/'); + if (path == NULL) path = url + strlen(url); + break; + case URL_IS_PATH: + url += sizeof("file://") - 1; + path = strchr(url, '/'); + if (path == NULL) path = url + strlen(url); + break; + case URL_IS_HKP: + url += sizeof("hkp://") - 1; + path = strchr(url, '/'); + if (path == NULL) path = url + strlen(url); + break; + case URL_IS_HTTP: + url += sizeof("http://") - 1; + path = strchr(url, '/'); + if (path == NULL) path = url + strlen(url); + break; + case URL_IS_HTTPS: + url += sizeof("https://") - 1; + path = strchr(url, '/'); + if (path == NULL) path = url + strlen(url); + break; + case URL_IS_UNKNOWN: + if (path == NULL) path = ""; + break; + case URL_IS_DASH: + path = ""; + break; + } + if (pathp) + *pathp = path; + return type; +} + +int urlGetFile(const char * url, const char * dest) +{ + char *cmd = NULL; + const char *target = NULL; + char *urlhelper = NULL; + int rc; + pid_t pid, wait; + + urlhelper = rpmExpand("%{?_urlhelper}", NULL); + + if (dest == NULL) { + urlPath(url, &target); + } else { + target = dest; + } + + /* XXX TODO: sanity checks like target == dest... */ + + rasprintf(&cmd, "%s %s %s", urlhelper, target, url); + urlhelper = _free(urlhelper); + + if ((pid = fork()) == 0) { + ARGV_t argv = NULL; + argvSplit(&argv, cmd, " "); + execvp(argv[0], argv); + exit(127); /* exit with 127 for compatibility with bash(1) */ + } + wait = waitpid(pid, &rc, 0); + cmd = _free(cmd); + + return rc; + +} |