Imported Upstream version 17.2.0 upstream/17.2.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 2 Sep 2019 07:15:07 +0000 (16:15 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 2 Sep 2019 07:15:07 +0000 (16:15 +0900)
26 files changed:
.gitignore
.travis.yml
CMakeLists.txt
VERSION.cmake
cmake/modules/FindGpgme.cmake [new file with mode: 0644]
doc/CMakeLists.txt
doc/locks.5 [deleted file]
doc/zypp-CheckAccessDeleted.1 [deleted file]
doc/zypp-CheckAccessDeleted.1.txt
doc/zypp-NameReqPrv.1 [deleted file]
libzypp.spec.cmake
package/libzypp.changes
po/fa.po
tests/zypp/KeyRing_test.cc
tools/zypp-CheckAccessDeleted.cc
zypp/CMakeLists.txt
zypp/Digest.cc
zypp/KeyManager.cc [new file with mode: 0644]
zypp/KeyManager.h [new file with mode: 0644]
zypp/KeyRing.cc
zypp/PublicKey.cc
zypp/PublicKey.h
zypp/base/String.h
zypp/media/MediaCD.cc
zypp/misc/CheckAccessDeleted.cc
zypp/misc/CheckAccessDeleted.h

index b81adcd..d481783 100644 (file)
@@ -9,3 +9,4 @@ build
 .externalToolBuilders
 .settings
 *flymake.h.gch
+*.user
index d8ba8bc..638b5d5 100644 (file)
@@ -12,7 +12,8 @@ before_install:
 - docker pull opensuse:tumbleweed
 - docker run -i -d --name test opensuse:tumbleweed bash
 - docker exec test zypper ref
-- docker exec test zypper in -y --recommends cmake openssl-devel boost-devel dejagnu doxygen gcc-c++ gettext-devel graphviz libxml2-devel libproxy-devel pkg-config libsolv-devel libsolv-tools ruby rpm-devel libcurl-devel libboost_program_options*-devel libboost_test*-devel libudev-devel xorg-x11-fonts-core xorg-x11-fonts texlive-lm-fonts
+- docker exec test zypper in -y --recommends cmake openssl-devel boost-devel dejagnu doxygen gcc-c++ gettext-devel graphviz libxml2-devel libproxy-devel pkg-config libsolv-devel libsolv-tools ruby rpm-devel libcurl-devel libboost_program_options*-devel libboost_test*-devel libudev-devel xorg-x11-fonts-core xorg-x11-fonts texlive-lm-fonts libgpgme-devel gpgme libboost_thread1_*
+- docker exec test zypper in -y --no-recommends asciidoc
 
 script:
 - docker cp ../libzypp/ test:/root
index c042289..43c762c 100644 (file)
@@ -2,6 +2,7 @@ PROJECT(LIBZYPP)
 SET( PACKAGE "libzypp" )
 # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
 SET( CMAKE_MODULE_PATH ${LIBZYPP_SOURCE_DIR}/cmake/modules )
+
 cmake_minimum_required(VERSION 2.6)
 
 OPTION (ENABLE_BUILD_DOCS "Build documentation by default?" OFF)
@@ -119,7 +120,7 @@ ELSE ( NOT RPM_FOUND)
   endif ( RPM_SUSPECT_VERSION STREQUAL "5.x" )
 ENDIF( NOT RPM_FOUND)
 
-FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options unit_test_framework)
+FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options unit_test_framework thread)
 IF (Boost_FOUND)
   MESSAGE( STATUS "boost found: includes in ${Boost_INCLUDE_DIRS}, library in ${Boost_LIBRARY_DIRS}")
   INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
@@ -163,6 +164,14 @@ ELSE()
   INCLUDE_DIRECTORIES( ${LibSolv_INCLUDE_DIRS} )
 ENDIF()
 
+FIND_PACKAGE(Gpgme REQUIRED)
+IF ( NOT GPGME_PTHREAD_FOUND )
+  MESSAGE( FATAL_ERROR " gpgme not found" )
+ELSE()
+  INCLUDE_DIRECTORIES( ${GPGME_INCLUDES} )
+  LINK_DIRECTORIES(${GPGME_LIBRARY_DIR})
+ENDIF()
+
 FIND_PACKAGE(OpenSSL REQUIRED)
 
 FIND_PACKAGE(Udev)
index 3e77998..bf521f1 100644 (file)
 #   See './mkChangelog -h' for help.
 #
 SET(LIBZYPP_MAJOR "17")
-SET(LIBZYPP_COMPATMINOR "0")
-SET(LIBZYPP_MINOR "1")
-SET(LIBZYPP_PATCH "3")
+SET(LIBZYPP_COMPATMINOR "2")
+SET(LIBZYPP_MINOR "2")
+SET(LIBZYPP_PATCH "0")
 #
-# LAST RELEASED: 17.1.3 (0)
+# LAST RELEASED: 17.2.0 (2)
 # (The number in parenthesis is LIBZYPP_COMPATMINOR)
 #=======
diff --git a/cmake/modules/FindGpgme.cmake b/cmake/modules/FindGpgme.cmake
new file mode 100644 (file)
index 0000000..6fec3d5
--- /dev/null
@@ -0,0 +1,413 @@
+# Taken from gpgmepp project
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# - Try to find the gpgme library
+#
+# Algorithm:
+#  - Windows:
+#    On Windows, there's three gpgme variants: gpgme{,-glib,-qt}.
+#    - The variant used determines the event loop integration possible:
+#      - gpgme:      no event loop integration possible, only synchronous operations supported
+#      - gpgme-glib: glib event loop integration possible, only asynchronous operations supported
+#      - gpgme-qt:   qt event loop integration possible, only asynchronous operations supported
+#    - GPGME_{VANILLA,GLIB,QT}_{FOUND,LIBRARIES} will be set for each of the above
+#    - GPGME_INCLUDES is the same for all of the above
+#    - GPGME_FOUND is set if any of the above was found
+#  - *nix:
+#    There's also three variants: gpgme{,-pthread,-pth}.
+#    - The variant used determines the multithreaded use possible:
+#      - gpgme:         no multithreading support available
+#      - gpgme-pthread: multithreading available using POSIX threads
+#      - gpgme-pth:     multithreading available using GNU PTH (cooperative multithreading)
+#    - GPGME_{VANILLA,PTH,PTHREAD}_{FOUND,LIBRARIES} will be set for each of the above
+#    - GPGME_INCLUDES is the same for all of the above
+#    - GPGME_FOUND is set if any of the above was found
+#
+#  GPGME_LIBRARY_DIR - the directory where the libraries are located
+
+#
+# THIS IS ALMOST A 1:1 COPY OF FindAssuan.cmake in kdepim.
+# Any changes here likely apply there, too.
+#
+
+include(FeatureSummary)
+
+# do away with crappy condition repetition on else/endfoo
+set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved ${CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS} )
+set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true )
+
+#if this is built-in, please replace, if it isn't, export into a MacroToBool.cmake of it's own
+macro( macro_bool_to_bool FOUND_VAR )
+  foreach( _current_VAR ${ARGN} )
+    if ( ${FOUND_VAR} )
+      set( ${_current_VAR} TRUE )
+    else()
+      set( ${_current_VAR} FALSE )
+    endif()
+  endforeach()
+endmacro()
+
+#HACK: local copy...
+MACRO(MACRO_BOOL_TO_01 FOUND_VAR )
+   FOREACH (_current_VAR ${ARGN})
+      IF(${FOUND_VAR})
+         SET(${_current_VAR} 1)
+      ELSE(${FOUND_VAR})
+         SET(${_current_VAR} 0)
+      ENDIF(${FOUND_VAR})
+   ENDFOREACH(_current_VAR)
+ENDMACRO(MACRO_BOOL_TO_01)
+
+
+if ( WIN32 )
+
+  # On Windows, we don't have a gpgme-config script, so we need to
+  # look for the stuff ourselves:
+
+  # in cmake, AND and OR have the same precedence, there's no
+  # subexpressions, and expressions are evaluated short-circuit'ed
+  # IOW: CMake if() suxx.
+  # Starting with CMake 2.6.3 you can group if expressions with (), but we
+  # don't require 2.6.3 but 2.6.2, we can't use it. Alex
+  set( _seem_to_have_cached_gpgme false )
+  if ( GPGME_INCLUDES )
+    if ( GPGME_VANILLA_LIBRARIES OR GPGME_QT_LIBRARIES OR GPGME_GLIB_LIBRARIES )
+      set( _seem_to_have_cached_gpgme true )
+    endif()
+  endif()
+
+  if ( _seem_to_have_cached_gpgme )
+
+    macro_bool_to_bool( GPGME_VANILLA_LIBRARIES  GPGME_VANILLA_FOUND )
+    macro_bool_to_bool( GPGME_GLIB_LIBRARIES     GPGME_GLIB_FOUND    )
+    macro_bool_to_bool( GPGME_QT_LIBRARIES       GPGME_QT_FOUND      )
+    # this would have been preferred:
+    #set( GPGME_*_FOUND macro_bool_to_bool(GPGME_*_LIBRARIES) )
+
+    if ( GPGME_VANILLA_FOUND OR GPGME_GLIB_FOUND OR GPGME_QT_FOUND )
+      set( GPGME_FOUND true )
+    else()
+      set( GPGME_FOUND false )
+    endif()
+
+  else()
+
+    set( GPGME_FOUND         false )
+    set( GPGME_VANILLA_FOUND false )
+    set( GPGME_GLIB_FOUND    false )
+    set( GPGME_QT_FOUND      false )
+
+    find_path( GPGME_INCLUDES gpgme.h
+      ${CMAKE_INCLUDE_PATH}
+      ${CMAKE_INSTALL_PREFIX}/include
+    )
+
+    find_library( _gpgme_vanilla_library NAMES gpgme libgpgme gpgme-11 libgpgme-11
+      PATHS
+        ${CMAKE_LIBRARY_PATH}
+        ${CMAKE_INSTALL_PREFIX}/lib
+    )
+
+    find_library( _gpgme_glib_library    NAMES gpgme-glib libgpgme-glib gpgme-glib-11 libgpgme-glib-11
+      PATHS
+        ${CMAKE_LIBRARY_PATH}
+        ${CMAKE_INSTALL_PREFIX}/lib
+    )
+
+    find_library( _gpgme_qt_library      NAMES gpgme-qt libgpgme-qt gpgme-qt-11 libgpgme-qt-11
+      PATHS
+        ${CMAKE_LIBRARY_PATH}
+        ${CMAKE_INSTALL_PREFIX}/lib
+    )
+
+    find_library( _gpg_error_library     NAMES gpg-error libgpg-error gpg-error-0 libgpg-error-0
+       PATHS
+            ${CMAKE_LIBRARY_PATH}
+            ${CMAKE_INSTALL_PREFIX}/lib
+    )
+
+    set( GPGME_INCLUDES ${GPGME_INCLUDES} )
+
+    if ( _gpgme_vanilla_library AND _gpg_error_library )
+      set( GPGME_VANILLA_LIBRARIES ${_gpgme_vanilla_library} ${_gpg_error_library} )
+      set( GPGME_VANILLA_FOUND     true )
+      set( GPGME_FOUND             true )
+    endif()
+
+    if ( _gpgme_glib_library AND _gpg_error_library )
+      set( GPGME_GLIB_LIBRARIES    ${_gpgme_glib_library}    ${_gpg_error_library} )
+      set( GPGME_GLIB_FOUND        true )
+      set( GPGME_FOUND             true )
+    endif()
+
+    if ( _gpgme_qt_library AND _gpg_error_library )
+      set( GPGME_QT_LIBRARIES      ${_gpgme_qt_library}      ${_gpg_error_library} )
+      set( GPGME_QT_FOUND          true )
+      set( GPGME_FOUND             true )
+    endif()
+
+  endif()
+
+  # these are Unix-only:
+  set( GPGME_PTHREAD_FOUND false )
+  set( GPGME_PTH_FOUND     false )
+  set( HAVE_GPGME_PTHREAD  0     )
+  set( HAVE_GPGME_PTH      0     )
+
+  macro_bool_to_01( GPGME_FOUND         HAVE_GPGME         )
+  macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA )
+  macro_bool_to_01( GPGME_GLIB_FOUND    HAVE_GPGME_GLIB    )
+  macro_bool_to_01( GPGME_QT_FOUND      HAVE_GPGME_QT      )
+
+else() # not WIN32
+
+  # On *nix, we have the gpgme-config script which can tell us all we
+  # need to know:
+
+  # see WIN32 case for an explanation of what this does:
+  set( _seem_to_have_cached_gpgme false )
+  if ( GPGME_INCLUDES )
+    if ( GPGME_VANILLA_LIBRARIES OR GPGME_PTHREAD_LIBRARIES OR GPGME_PTH_LIBRARIES )
+      set( _seem_to_have_cached_gpgme true )
+    endif()
+  endif()
+
+  if ( _seem_to_have_cached_gpgme )
+
+    macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND )
+    macro_bool_to_bool( GPGME_PTHREAD_LIBRARIES GPGME_PTHREAD_FOUND )
+    macro_bool_to_bool( GPGME_PTH_LIBRARIES     GPGME_PTH_FOUND     )
+
+    if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND )
+      set( GPGME_FOUND true )
+    else()
+      set( GPGME_FOUND false )
+    endif()
+
+  else()
+
+    set( GPGME_FOUND         false )
+    set( GPGME_VANILLA_FOUND false )
+    set( GPGME_PTHREAD_FOUND false )
+    set( GPGME_PTH_FOUND     false )
+
+    find_program( _GPGMECONFIG_EXECUTABLE NAMES gpgme-config )
+
+    # if gpgme-config has been found
+    if ( _GPGMECONFIG_EXECUTABLE )
+
+      message( STATUS "Found gpgme-config at ${_GPGMECONFIG_EXECUTABLE}" )
+
+      exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --version OUTPUT_VARIABLE GPGME_VERSION )
+
+      set( _GPGME_MIN_VERSION "1.1.7" )
+
+      if ( ${GPGME_VERSION} VERSION_LESS ${_GPGME_MIN_VERSION} )
+
+        message( STATUS "The installed version of gpgme is too old: ${GPGME_VERSION} (required: >= ${_GPGME_MIN_VERSION})" )
+
+      else()
+
+        message( STATUS "Found gpgme v${GPGME_VERSION}, checking for flavours..." )
+
+        exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS                  --libs OUTPUT_VARIABLE _gpgme_config_vanilla_libs RETURN_VALUE _ret )
+       if ( _ret )
+         set( _gpgme_config_vanilla_libs )
+       endif()
+
+        exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pthread --libs OUTPUT_VARIABLE _gpgme_config_pthread_libs RETURN_VALUE _ret )
+       if ( _ret )
+         set( _gpgme_config_pthread_libs )
+       endif()
+
+        exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pth     --libs OUTPUT_VARIABLE _gpgme_config_pth_libs     RETURN_VALUE _ret )
+       if ( _ret )
+         set( _gpgme_config_pth_libs )
+       endif()
+
+        # append -lgpg-error to the list of libraries, if necessary
+        foreach ( _flavour vanilla pthread pth )
+          if ( _gpgme_config_${_flavour}_libs AND NOT _gpgme_config_${_flavour}_libs MATCHES "lgpg-error" )
+            set( _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs} -lgpg-error" )
+          endif()
+        endforeach()
+
+        if ( _gpgme_config_vanilla_libs OR _gpgme_config_pthread_libs OR _gpgme_config_pth_libs )
+
+          exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --cflags OUTPUT_VARIABLE _GPGME_CFLAGS )
+
+          if ( _GPGME_CFLAGS )
+            string( REGEX REPLACE "(\r?\n)+$" " " _GPGME_CFLAGS  "${_GPGME_CFLAGS}" )
+            string( REGEX REPLACE " *-I"      ";" GPGME_INCLUDES "${_GPGME_CFLAGS}" )
+          endif()
+
+          foreach ( _flavour vanilla pthread pth )
+            if ( _gpgme_config_${_flavour}_libs )
+
+              set( _gpgme_library_dirs )
+              set( _gpgme_library_names )
+              string( TOUPPER "${_flavour}" _FLAVOUR )
+
+              string( REGEX REPLACE " +" ";" _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs}" )
+
+              foreach( _flag ${_gpgme_config_${_flavour}_libs} )
+                if ( "${_flag}" MATCHES "^-L" )
+                  string( REGEX REPLACE "^-L" "" _dir "${_flag}" )
+                  file( TO_CMAKE_PATH "${_dir}" _dir )
+                  set( _gpgme_library_dirs ${_gpgme_library_dirs} "${_dir}" )
+                elseif( "${_flag}" MATCHES "^-l" )
+                  string( REGEX REPLACE "^-l" "" _name "${_flag}" )
+                  set( _gpgme_library_names ${_gpgme_library_names} "${_name}" )
+                endif()
+              endforeach()
+
+              set( GPGME_${_FLAVOUR}_FOUND true )
+
+              foreach( _name ${_gpgme_library_names} )
+                set( _gpgme_${_name}_lib )
+
+                # if -L options were given, look only there
+                if ( _gpgme_library_dirs )
+                  find_library( _gpgme_${_name}_lib NAMES ${_name} PATHS ${_gpgme_library_dirs} NO_DEFAULT_PATH )
+                endif()
+
+                # if not found there, look in system directories
+                if ( NOT _gpgme_${_name}_lib )
+                  find_library( _gpgme_${_name}_lib NAMES ${_name} )
+                endif()
+
+                # if still not found, then the whole flavour isn't found
+                if ( NOT _gpgme_${_name}_lib )
+                  if ( GPGME_${_FLAVOUR}_FOUND )
+                    set( GPGME_${_FLAVOUR}_FOUND false )
+                    set( _not_found_reason "dependant library ${_name} wasn't found" )
+                  endif()
+                endif()
+
+                set( GPGME_${_FLAVOUR}_LIBRARIES ${GPGME_${_FLAVOUR}_LIBRARIES} "${_gpgme_${_name}_lib}" )
+              endforeach()
+
+              #check_c_library_exists_explicit( gpgme         gpgme_check_version "${_GPGME_CFLAGS}" "${GPGME_LIBRARIES}"         GPGME_FOUND         )
+              if ( GPGME_${_FLAVOUR}_FOUND )
+                message( STATUS " Found flavour '${_flavour}', checking whether it's usable...yes" )
+              else()
+                message( STATUS " Found flavour '${_flavour}', checking whether it's usable...no" )
+                message( STATUS "  (${_not_found_reason})" )
+              endif()
+            endif()
+
+          endforeach( _flavour )
+
+          # ensure that they are cached
+          # This comment above doesn't make sense, the four following lines seem to do nothing. Alex
+          set( GPGME_INCLUDES          ${GPGME_INCLUDES} )
+          set( GPGME_VANILLA_LIBRARIES ${GPGME_VANILLA_LIBRARIES} )
+          set( GPGME_PTHREAD_LIBRARIES ${GPGME_PTHREAD_LIBRARIES} )
+          set( GPGME_PTH_LIBRARIES     ${GPGME_PTH_LIBRARIES} )
+
+          if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND )
+            set( GPGME_FOUND true )
+          else()
+            set( GPGME_FOUND false )
+          endif()
+
+        endif()
+
+      endif()
+
+    endif()
+
+  endif()
+
+  # these are Windows-only:
+  set( GPGME_GLIB_FOUND false )
+  set( GPGME_QT_FOUND   false )
+  set( HAVE_GPGME_GLIB  0     )
+  set( HAVE_GPGME_QT    0     )
+
+  macro_bool_to_01( GPGME_FOUND         HAVE_GPGME         )
+  macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA )
+  macro_bool_to_01( GPGME_PTHREAD_FOUND HAVE_GPGME_PTHREAD )
+  macro_bool_to_01( GPGME_PTH_FOUND     HAVE_GPGME_PTH     )
+
+endif() # WIN32 | Unix
+
+
+set( _gpgme_flavours "" )
+
+if ( GPGME_VANILLA_FOUND )
+  set( _gpgme_flavours "${_gpgme_flavours} vanilla" )
+endif()
+
+if ( GPGME_GLIB_FOUND )
+  set( _gpgme_flavours "${_gpgme_flavours} Glib" )
+endif()
+
+if ( GPGME_QT_FOUND )
+  set( _gpgme_flavours "${_gpgme_flavours} Qt" )
+endif()
+
+if ( GPGME_PTHREAD_FOUND )
+  set( _gpgme_flavours "${_gpgme_flavours} pthread" )
+endif()
+
+if ( GPGME_PTH_FOUND )
+  set( _gpgme_flavours "${_gpgme_flavours} pth" )
+endif()
+
+# determine the library in one of the found flavours, can be reused e.g. by FindQgpgme.cmake, Alex
+foreach(_currentFlavour vanilla glib qt pth pthread)
+   if(NOT GPGME_LIBRARY_DIR)
+      get_filename_component(GPGME_LIBRARY_DIR "${_gpgme_${_currentFlavour}_lib}" PATH)
+   endif()
+endforeach()
+
+if ( NOT Gpgme_FIND_QUIETLY )
+
+  if ( GPGME_FOUND )
+    message( STATUS "Usable gpgme flavours found: ${_gpgme_flavours}" )
+  else()
+    message( STATUS "No usable gpgme flavours found." )
+  endif()
+
+endif()
+
+if ( Gpgme_FIND_REQUIRED AND NOT GPGME_FOUND )
+  message( FATAL_ERROR "Did not find GPGME" )
+endif()
+
+
+if ( WIN32 )
+  set( _gpgme_homepage "http://www.gpg4win.org" )
+else()
+  set( _gpgme_homepage "http://www.gnupg.org/related_software/gpgme" )
+endif()
+
+set_package_properties(Gpgme PROPERTIES
+  DESCRIPTION "The GnuPG Made Easy (GPGME) library)"
+  URL ${_gpgme_homepage})
+
+set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved )
+
index ed3c0d4..edf42c3 100644 (file)
@@ -4,6 +4,48 @@ ELSE ( ENABLE_BUILD_DOCS )
   ADD_SUBDIRECTORY( autodoc EXCLUDE_FROM_ALL )
 ENDIF ( ENABLE_BUILD_DOCS )
 
+FIND_PROGRAM(A2X a2x)
+IF (NOT A2X)
+MESSAGE(FATAL_ERROR "Could not find a2x, please install asciidoc")
+ELSE (NOT A2X)
+MESSAGE("a2x found: ${A2X}")
+ENDIF (NOT A2X)
+
+function(ADD_A2X_MANPAGES)
+    set(options)
+    set(oneValueArgs MAN_INST_PATH)
+    set(multiValueArgs MAN_PAGES1 MAN_PAGES2 MAN_PAGES3 MAN_PAGES4 MAN_PAGES5 MAN_PAGES6 MAN_PAGES7 MAN_PAGES8)
+    cmake_parse_arguments(A2X "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+    set(ALL_MAN_PAGES)
+
+    FOREACH (MAN_IDX RANGE 1 8)
+        IF (NOT DEFINED A2X_MAN_PAGES${MAN_IDX})
+            CONTINUE()
+        ENDIF()
+
+        FOREACH (curr_PAGE ${A2X_MAN_PAGES${MAN_IDX}})
+            ADD_CUSTOM_COMMAND (
+              OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${curr_PAGE}
+              COMMAND ${A2X} -D ${CMAKE_CURRENT_BINARY_DIR} -f manpage ${CMAKE_CURRENT_SOURCE_DIR}/${curr_PAGE}.txt
+              DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${curr_PAGE}.txt
+              WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+              COMMENT "Building manpage ${curr_PAGE}"
+            )
+
+            ADD_CUSTOM_TARGET(
+              ${curr_PAGE}_Target ALL
+              DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${curr_PAGE}
+            )
+
+            INSTALL (
+              FILES ${CMAKE_CURRENT_BINARY_DIR}/${curr_PAGE}
+              DESTINATION "${A2X_MAN_INST_PATH}/man${MAN_IDX}"
+            )
+
+        ENDFOREACH ()
+    ENDFOREACH()
+endfunction(ADD_A2X_MANPAGES)
 
 SET (libzypp_MAN1
      zypp-CheckAccessDeleted.1 zypp-NameReqPrv.1)
@@ -11,40 +53,8 @@ SET (libzypp_MAN1
 SET (libzypp_MAN5
      locks.5)
 
-SET (libzypp_MAN
-     ${libzypp_MAN1} ${libzypp_MAN5})
-
-ADD_CUSTOM_TARGET( man ALL
-   DEPENDS ${libzypp_MAN}
+ADD_A2X_MANPAGES(
+    MAN_INST_PATH ${MANDIR}
+    MAN_PAGES1 ${libzypp_MAN1}
+    MAN_PAGES5 ${libzypp_MAN5}
 )
-
-FOREACH (libzypp_MANIDX 1 2 3 4 5 6 7 8)
-  INSTALL (FILES
-    ${libzypp_MAN${libzypp_MANIDX}}
-    DESTINATION ${MANDIR}/man${libzypp_MANIDX}
-  )
-ENDFOREACH (libzypp_MANIDX)
-
-#
-# If a2x is installed, auto update manpage from asciidoc manpage.txt:
-#
-FIND_PROGRAM(A2X a2x)
-FOREACH (libzypp_MANTARGET ${libzypp_MAN})
-  IF (A2X)
-    ADD_CUSTOM_COMMAND (
-      OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${libzypp_MANTARGET}
-      COMMAND ${A2X} -f manpage ${libzypp_MANTARGET}.txt
-      DEPENDS ${libzypp_MANTARGET}.txt
-      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-    )
-  ELSE (A2X)
-    ADD_CUSTOM_COMMAND (
-      OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${libzypp_MANTARGET}
-      DEPENDS ${libzypp_MANTARGET}.txt
-      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-      COMMENT "Forgott to fix manpage after changing ascidoc?   Install a2x for automated creation or 'touch ${libzypp_MANTARGET}'"
-    )
-  ENDIF (A2X)
-ENDFOREACH (libzypp_MANTARGET)
-
-
diff --git a/doc/locks.5 b/doc/locks.5
deleted file mode 100644 (file)
index 26ac90e..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-'\" t
-.\"     Title: locks
-.\"    Author: [see the "AUTHORS" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: 06/12/2015
-.\"    Manual: LIBZYPP
-.\"    Source: SUSE Linux
-.\"  Language: English
-.\"
-.TH "LOCKS" "5" "06/12/2015" "SUSE Linux" "LIBZYPP"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-locks \- libzypp locking file
-.SH "DESCRIPTION"
-.sp
-The file \fI/etc/zypp/locks\fR is read by libzypp at start\-up if \fIzypp\&.conf\fR allows it\&. The entries are used for initial locking of packages\&. Locking a package means not allowing to install or uninstall it\&.
-.sp
-Valid entries are of the form:
-.PP
-\fIattribute\fR\fB:\fR \fIvalue\fR
-.RS 4
-Where attributes and their values are described below\&.
-.RE
-.sp
-Locks are separated by an empty lines\&.
-.SH "ATTRIBUTES"
-.sp
-All attributes are lower\-case\&.
-.PP
-\fBrepo\fR
-.RS 4
-specifies repository restriction\&. Only alias is accepted\&.
-
-By default all repositories match\&.
-.RE
-.PP
-\fBtype\fR
-.RS 4
-resolvable type restriction\&. The values can be
-\fBpackage\fR,
-\fBpatch\fR,
-\fBpattern\fR,
-\fBproduct\fR
-and
-\fBsrcpackage\fR\&.
-
-By default all types match\&.
-.RE
-.PP
-\fBcase_sensitive\fR
-.RS 4
-if strings are matched case sensitive\&. The values are
-\fBtrue\fR,
-\fBfalse\fR,
-\fBon\fR,
-\fBoff\fR\&.
-
-The default is case insensitive\&.
-.RE
-.PP
-\fBinstall_status\fR
-.RS 4
-status of object\&. Possible states are
-\fBinstalled\fR,
-\fBnot\-installed\fR
-and
-\fBall\fR\&. If more install statuses are specified then the last one is used\&. The values are
-\fBinstalled\fR
-for all packages which are installed,
-\fBnon\-installed\fR
-for packages which can be installed or reinstalled and
-\fBall\fR
-for both\&.
-
-The default is
-\fBall\fR\&.
-.RE
-.PP
-\fBmatch_type\fR
-.RS 4
-type of string matching in values\&. Does not affect
-\fBtype\fR
-and
-\fBrepo\fR
-which must be specified exactly\&. The values are
-\fBexact\fR,
-\fBsubstring\fR,
-\fBregex\fR
-for regular expressions,
-\fBglob\fR
-for matching as on the command line, and
-\fBword\fR\&.
-
-The default is
-\fBsubstring\fR\&.
-.RE
-.PP
-\fBquery_string\fR
-.RS 4
-String to be matched in multiple attributes\&. Should be restricted by another attribute with empty value (it is recommended, because without restriction expect some performance problems)\&.
-.RE
-.PP
-\fBversion\fR
-.RS 4
-Restrict the lock only to some versions\&. It contains two parts: an optional operator and the version\&.
-
-The operator is
-\fB==\fR,
-\fB!=\fR,
-\fB<\fR,
-\fB>\fR,
-\fB⇐\fR,
-\fB>=\fR\&. If operator is not specified then
-\fB==\fR
-is used\&.
-
-The version has the format [\fIepoch\fR\fB:\fR]\fIversion\fR[\fB\-\fR\fIrelease\fR]\&.
-
-Example:
-\fBversion: < 0:0\&.11\&.4\-2\fR
-.RE
-.PP
-\fBsolvable_name\fR
-.RS 4
-name of object (e\&.g\&. zypper)
-.RE
-.PP
-\fBsolvable_summary\fR
-.RS 4
-summary of object
-.RE
-.PP
-\fBsolvable_arch\fR
-.RS 4
-architecture of object (e\&.g\&. x86_64, i586)
-.RE
-.PP
-\fBsolvable_description\fR
-.RS 4
-description of object
-.RE
-.PP
-\fBsolvable_eula\fR
-.RS 4
-license text of objects which request accepting license by user
-.RE
-.PP
-\fBsolvable_license\fR
-.RS 4
-license of package (only for package) (e\&.g\&. GPL2)
-.RE
-.PP
-\fBsolvable_keywords\fR
-.RS 4
-keywords which specify package (only for package)
-.RE
-.PP
-\fBsolvable_authors\fR
-.RS 4
-authors of package (only for package)
-.RE
-.PP
-\fBsolvable_group\fR
-.RS 4
-package group (only for package) (e\&.g\&. Development/Tools/Version Control )
-.RE
-.PP
-\fBupdate_reference_type\fR
-.RS 4
-reference for update (e\&.g\&. bugzilla,cve) (only for patches)
-.RE
-.SH "EXAMPLES"
-.PP
-\fBExact Package\fR
-.RS 4
-This is the way YaST UI does it\&. Lock k3b (e\&.g\&. you don\(cqt want to update it)\&.
-.RE
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\-\-\-\-\-locks\-\-\-\-\-
-type: package
-solvable_name: k3b
-match_type: exact
-case_sensitive: on
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-\fBPackage Wildcard\fR
-.RS 4
-This is the way "zypper addlock cross\-*\-gcc\-icecream\-backend" does it\&.
-.RE
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\-\-\-\-\-locks\-\-\-\-\-
-type: package
-solvable_name: cross\-*\-gcc\-icecream\-backend
-match_type: glob
-case_sensitive: on
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-\fBVersioned Lock\fR
-.RS 4
-Do not install new GCC\&. This format is used when converting from the openSUSE\-10\&.3 lock format\&.
-.RE
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\-\-\-\-\-locks\-\-\-\-\-
-solvable_name: gcc
-match_type: glob
-version: > 4\&.2
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-\fBAnything named KDE\fR
-.RS 4
-Locks everything which contains kde in the name\&.
-.RE
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\-\-\-\-\-locks\-\-\-\-\-
-solvable_name: kde
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-\fBAnything mentioning KDE\fR
-.RS 4
-Locks everything which contains kde in the name, summary, or description\&.
-.RE
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\-\-\-\-\-locks\-\-\-\-\-
-query_string: kde
-solvable_name:
-solvable_summary:
-solvable_description:
-.fi
-.if n \{\
-.RE
-.\}
-.SH "HOMEPAGE"
-.sp
-This manual page only covers the most important attributes\&. The complete list is available at http://en\&.opensuse\&.org/Libzypp/Locksfile
-.SH "AUTHORS"
-.sp
-Josef Reidinger <jreidinger@suse\&.cz> Manual page contributions by Martin Vidner <mvidner@suse\&.cz>\&.
-.SH "SEE ALSO"
-.sp
-zypper(8)
diff --git a/doc/zypp-CheckAccessDeleted.1 b/doc/zypp-CheckAccessDeleted.1
deleted file mode 100644 (file)
index d8a3be7..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-'\" t
-.\"     Title: zypp-checkaccessdeleted
-.\"    Author: [see the "AUTHORS" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: 06/12/2015
-.\"    Manual: LIBZYPP
-.\"    Source: SUSE Linux
-.\"  Language: English
-.\"
-.TH "ZYPP\-CHECKACCESSDEL" "1" "06/12/2015" "SUSE Linux" "LIBZYPP"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-zypp-CheckAccessDeleted \- List processes which access deleted files
-.SH "SYNOPSIS"
-.sp
-\fBzypp\-CheckAccessDeleted\fR [\fIOPTION\fR]\&...
-.SH "DESCRIPTION"
-.sp
-List running processes which access deleted files\&. This may help to identify services and processes which need to be restarted after an update, e\&.g\&. if they still refer to meanwhile deleted libraries\&. The output is similar to \(oq\fBzypper ps\fR\(cq but does not sort out processes accessing normal files only\&.
-.SH "OPTIONS"
-.PP
-\fB\-\-help\fR
-.RS 4
-display help and exit
-.RE
-.SH "AUTHORS"
-.sp
-Michael Andres <ma@suse\&.de>
-.SH "SEE ALSO"
-.sp
-zypper(8)
index 6e3a17b..a25b0fd 100644 (file)
@@ -23,6 +23,10 @@ OPTIONS
 -------
 *--help*::
        display help and exit
+*--zypper*::
+       use less verbose zypper mode to list processes
+*--debugFile* 'filename'::
+       Reads process and opened files information from a file that was created by zypper previously
 
 
 AUTHORS
diff --git a/doc/zypp-NameReqPrv.1 b/doc/zypp-NameReqPrv.1
deleted file mode 100644 (file)
index 0cf4fa8..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-'\" t
-.\"     Title: zypp-namereqprv
-.\"    Author: [see the "AUTHORS" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: 06/12/2015
-.\"    Manual: LIBZYPP
-.\"    Source: SUSE Linux
-.\"  Language: English
-.\"
-.TH "ZYPP\-NAMEREQPRV" "1" "06/12/2015" "SUSE Linux" "LIBZYPP"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-zypp-NameReqPrv \- Investigate packages and dependencies in solver test\-cases
-.SH "SYNOPSIS"
-.sp
-\fBzypp\-NameReqPrv\fR [\fB\-\-root\fR \fIDIR\fR] [[\fIOPTIONS\fR] \fINAME\fR\&...]\&...
-.SH "DESCRIPTION"
-.sp
-Load all enabled repositories (without refresh) and search for occurrences of regular expression \fINAME\fR in package names or dependencies\&. In case \fIDIR\fR denotes a directory containing a solver test\-case, repositories and settings provided by the test\-case are loaded instead\&.
-.sp
-A solver test\-case is an abstract of repositories, packages, hardware and dependency resolution related settings which can be created by using zyppers \fB\-\-debug\-solver\fR option\&. Attached to a bug report the test\-case helps investigating the reported behavior\&. Options and output of this command are tailored to this use\-case and may change as needed\&.
-.sp
-Reported matches show the packages \fIinternal id\fR, \fIname\fR, \fIversion\fR and \fIarchitecture\fR, \fIpriority\fR and name of the providing \fIrepository\fR, \fIvendor\fR, \fIbuildtime\fR and the locations matching \fINAME\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-2431  zypper\-1\&.9\&.16\-22\&.2\&.x86_64  (99)@System  openSUSE  1400499579
-                                              nam: zypper
-40947  zypper\-1\&.9\&.3\-1\&.1\&.x86_64    (99)repo\-oss (13\&.1)  openSUSE     1383049437
-                                                       nam: zypper
-56140  zypper\-1\&.9\&.16\-22\&.2\&.x86_64  (99)repo\-oss\-update (13\&.1)  openSUSE     1400499579
-                                                              nam: zypper
-.fi
-.if n \{\
-.RE
-.\}
-.SH "OPTIONS"
-.PP
-\fB\-\-root\fR \fIDIR\fR
-.RS 4
-Load repos from the system located below
-\fIDIR\fR\&. If
-\fIDIR\fR
-denotes a solver test\-case, the test\-case is loaded\&.
-.RE
-.PP
-\fB\-\-installed\fR
-.RS 4
-Process installed packages only\&.
-.RE
-.PP
-\fB\-i\fR/\fB\-I\fR
-.RS 4
-Turn on/off case insensitive search (default on)
-.RE
-.PP
-\fB\-n\fR/\fB\-N\fR
-.RS 4
-Turn on/off looking for names (default on)
-.RE
-.PP
-\fB\-p\fR/\fB\-P\fR
-.RS 4
-Turn on/off looking for provides (default off)
-.RE
-.PP
-\fB\-r\fR/\fB\-R\fR
-.RS 4
-Turn on/off looking for requires (default off)
-.RE
-.PP
-\fB\-c\fR/\fB\-C\fR
-.RS 4
-Turn on/off looking for conflicts (default off)
-.RE
-.PP
-\fB\-o\fR/\fB\-O\fR
-.RS 4
-Turn on/off looking for obsoletes (default off)
-.RE
-.PP
-\fB\-m\fR/\fB\-M\fR
-.RS 4
-Turn on/off looking for recommends (default off)
-.RE
-.PP
-\fB\-s\fR/\fB\-S\fR
-.RS 4
-Turn on/off looking for supplements (default off)
-.RE
-.PP
-\fB\-a\fR
-.RS 4
-Short for
-\fB\-n \-p \-r\fR
-.RE
-.PP
-\fB\-A\fR
-.RS 4
-Short for
-\fB\-n \-P \-R\fR
-.RE
-.PP
-\fB\-D\fR \fIPKG\fR
-.RS 4
-Dump dependencies of package
-\fIPKG\fR
-(exact name)\&.
-.RE
-.SH "AUTHORS"
-.sp
-Michael Andres <ma@suse\&.de>
-.SH "SEE ALSO"
-.sp
-zypper(8)
index e05c999..b163d92 100644 (file)
@@ -74,7 +74,7 @@ BuildRequires:  pkgconfig
 BuildRequires:  pkg-config
 %endif
 
-BuildRequires:  libsolv-devel >= 0.6.23
+BuildRequires:  libsolv-devel >= 0.6.27
 %if 0%{?suse_version} >= 1100
 BuildRequires:  libsolv-tools
 %requires_eq    libsolv-tools
@@ -103,11 +103,9 @@ BuildRequires:  librpm-devel > 4.4
 %endif
 
 %if 0%{?suse_version}
-BuildRequires:  gpg2
-Requires:       gpg2
+BuildRequires:  libgpgme-devel
 %else
-BuildRequires:  gnupg2
-Requires:       gnupg2
+BuildRequires:  gpgme-devel
 %endif
 
 %define min_curl_version 7.19.4
@@ -126,6 +124,9 @@ BuildRequires:  libcurl-devel >= %{min_curl_version}
 Requires:       libcurl   >= %{min_curl_version}
 %endif
 
+# required for documentation
+BuildRequires:  asciidoc
+
 %description
 Package, Patch, Pattern, and Product Management
 
index e9df2ef..308d6f3 100644 (file)
@@ -1,4 +1,12 @@
 -------------------------------------------------------------------
+Thu Mar  1 17:46:39 CET 2018 - bzeller@suse.de
+
+- Add /var/lib/gdm to CheckAccessDeleted blacklist (bsc#1079991)
+- Use gpgme instead of calling gpg2 binary directly
+- Fix leak in Digest.cc (bsc#1075978)
+- version 17.2.0 (2)
+
+-------------------------------------------------------------------
 Tue Feb 20 18:03:24 CET 2018 - ma@suse.de
 
 - RpmDb::checkPackage: fix parsing localized rpm output (bsc#1076415)
index b9bce56..e911292 100644 (file)
--- a/po/fa.po
+++ b/po/fa.po
@@ -5,16 +5,16 @@ msgstr ""
 "Project-Id-Version: opensuse-i 18n\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2017-12-01 07:26+0100\n"
-"PO-Revision-Date: 2017-09-14 21:15+0000\n"
-"Last-Translator: hayyan rostami <hayyan71@yahoo.com>\n"
-"Language-Team: Persian <https://l10n.opensuse.org/projects/libzypp/master/fa/"
-">\n"
+"PO-Revision-Date: 2018-02-28 13:09+0000\n"
+"Last-Translator: Mohammad Rezaei Seresht <m_rezaei_seresht@hotmail.com>\n"
+"Language-Team: Persian "
+"<https://l10n.opensuse.org/projects/libzypp/master/fa/>\n"
 "Language: fa\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 2.13.1\n"
+"X-Generator: Weblate 2.18\n"
 
 #: zypp/target/hal/HalException.h:46 zypp/target/hal/HalException.h:55
 #: zypp/target/hal/HalException.h:64
@@ -1352,9 +1352,8 @@ msgid "Tried to import not existent key %s into keyring %s"
 msgstr "برای وارد کردن کلید ناموجود %s در دسته کلید %s تلاش شد"
 
 #: zypp/KeyRing.cc:583
-#, fuzzy
 msgid "Failed to import key."
-msgstr "ناموفق در وارد کردن کلید عمومی از پرونده ی %1%"
+msgstr "ناموفق در وارد کردن کلید."
 
 #: zypp/KeyRing.cc:606
 msgid "Failed to delete key."
@@ -3732,13 +3731,13 @@ msgstr "منقضی نمیشود"
 #: zypp/PublicKey.cc:65
 #, boost-format
 msgid "expired: %1%"
-msgstr ""
+msgstr "منقضی شده: %1%"
 
 #. translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
 #: zypp/PublicKey.cc:70
 #, boost-format
 msgid "expires: %1%"
-msgstr ""
+msgstr "تاریخ انقضا: %1%"
 
 #. translators: an annotation to a gpg keys expiry date
 #: zypp/PublicKey.cc:79
@@ -4702,7 +4701,7 @@ msgstr "ناموفق در حذف کلید عمومی %1%"
 
 #: zypp/target/rpm/RpmDb.cc:1596
 msgid "Package is not signed!"
-msgstr ""
+msgstr "بسته مورد تایید نیست!"
 
 #. Translator: %s = name of an rpm package. A list of diffs follows
 #. this message.
@@ -4787,7 +4786,7 @@ msgstr "پرونده موجود نیست و یا امضاها قابل بررس
 #. translators: possible rpm package signature check result [brief]
 #: zypp/target/rpm/RpmDb.cc:2424
 msgid "File is unsigned"
-msgstr ""
+msgstr "فایل تایید نشده"
 
 #: zypp/thread/Mutex.cc:33
 msgid "Can't initialize mutex attributes"
index 4a5cb93..607f850 100644 (file)
@@ -221,6 +221,9 @@ BOOST_AUTO_TEST_CASE(keyring_import)
     virtual void trustedKeyAdded( const PublicKey & key_r )
     { ++_cbcnt; }
 
+    virtual void trustedKeyRemoved( const PublicKey & key_r )
+    { --_cbcnt; }
+
     unsigned _cbcnt = 0;
   } receiver;
 
@@ -236,3 +239,57 @@ BOOST_AUTO_TEST_CASE(keyring_import)
   BOOST_CHECK_EQUAL( receiver._cbcnt, keyring.trustedPublicKeys().size() );
 }
 
+BOOST_AUTO_TEST_CASE(keyring_delete)
+{
+  PublicKey key( Pathname(DATADIR) + "public.asc" );
+  /** scenario #3
+  * import and delete untrusted key
+  */
+  {
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+
+    keyring.importKey( key, false );
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 1 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+
+    keyring.deleteKey( key.id(), false);
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+  }
+
+  /** scenario #3.1
+  * import and delete trusted key
+  */
+  {
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+
+    keyring.importKey( key, true );
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 1 );
+
+    //try to delete from untrusted keyring
+    keyring.deleteKey( key.id(), false);
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 1 );
+
+    keyring.deleteKey( key.id(), true);
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+  }
+}
+
index ebdcd1e..3fe419d 100644 (file)
@@ -94,33 +94,69 @@ struct ProcInfoTable
   TableCol files;
 };
 
+void usage ( const std::string &appname )
+{
+  std::cout << "Usage: " << appname << " [--help] [--debugFile <file>] [--zypper]" << std::endl;
+  std::cout << "List information about all running processe" << std::endl;
+  std::cout << "which access deleted executables or libraries." << std::endl;
+  std::cout << std::endl;
+  std::cout << "GLOBALOPTS:" << std::endl;
+  std::cout << "  --debugFile <file> Use information from <file> instead" << std::endl;
+  std::cout << "                     of inspecting the host system." << std::endl;
+  std::cout << "  --zypper           Disable verbose checking like zypper does." << std::endl;
+  std::cout << std::endl;
+  std::cout << "TABLEHEADERS:" << std::endl;
+  std::cout << "  PID     " << "process ID" << std::endl;
+  std::cout << "  PPID    " << "parent process ID" << std::endl;
+  std::cout << "  UID     " << "process user ID" << std::endl;
+  std::cout << "  LOGIN   " << "process login name" << std::endl;
+  std::cout << "  COMMAND " << "process command name" << std::endl;
+  std::cout << "  SERVICE " << "/etc/init.d/ script that might be used to restart the command (guessed)" << std::endl;
+  std::cout << "  FILES   " << "list of deleted executables or libraries accessed" << std::endl;
+}
+
 int main( int argc, char * argv[] )
 {
-  if ( argc >= 2 )
-  {
-    std::string progname( zypp::Pathname::basename( argv[0] ) );
-    if ( strcmp( argv[1], "--help" ) == 0 )
+
+  std::string progname( zypp::Pathname::basename( argv[0] ) );
+  std::string debugInputFile;
+  bool verbose = true;
+  argv++;
+  argc--;
+
+  while( argc > 0 ) {
+    if ( strcmp( argv[0], "--help" ) == 0 )
     {
-      std::cout << "Usage: " << progname << " [--help]" << std::endl;
-      std::cout << "List information about all running processe" << std::endl;
-      std::cout << "which access deleted executables or libraries." << std::endl;
-      std::cout << "  PID     " << "process ID" << std::endl;
-      std::cout << "  PPID    " << "parent process ID" << std::endl;
-      std::cout << "  UID     " << "process user ID" << std::endl;
-      std::cout << "  LOGIN   " << "process login name" << std::endl;
-      std::cout << "  COMMAND " << "process command name" << std::endl;
-      std::cout << "  SERVICE " << "/etc/init.d/ script that might be used to restart the command (guessed)" << std::endl;
-      std::cout << "  FILES   " << "list of deleted executables or libraries accessed" << std::endl;
+      usage( progname );
       return 0;
+    } else if ( strcmp( argv[0], "--debugFile" ) == 0 ) {
+      if ( argc < 2 ) {
+        std::cerr << progname << ": debugFile requires a argument" << std::endl;
+        return 1;
+      }
+
+      argv++;
+      argc--;
+      debugInputFile = argv[0];
+
+    } else if ( strcmp( argv[0], "--zypper" ) == 0 ) {
+      verbose = false;
+    } else {
+      std::cerr << progname << ": unexpected argument '" << argv[0] << "'" << std::endl;
+      std::cerr << "Try `" << progname << " --help' for more information." << std::endl;
+      return 1;
     }
-    std::cerr << progname << ": unexpected argument '" << argv[1] << "'" << std::endl;
-    std::cerr << "Try `" << progname << " --help' for more information." << std::endl;
-    return 1;
+
+    argv++;
+    argc--;
   }
 
   zypp::CheckAccessDeleted checker(false); // wait for explicit call to check()
   try {
-    checker.check( /*verbose*/true );
+    if ( debugInputFile.empty() )
+      checker.check( verbose );
+    else
+      checker.check( debugInputFile, verbose );
   }
   catch( const zypp::Exception & err )
   {
index ed72bfd..a30696e 100644 (file)
@@ -36,6 +36,7 @@ SET( zypp_SRCS
   HistoryLogData.cc
   IdString.cc
   InstanceId.cc
+  KeyManager.cc
   KeyRing.cc
   Locks.cc
   MediaSetAccess.cc
@@ -128,6 +129,7 @@ SET( zypp_HEADERS
   IdStringType.h
   InstanceId.h
   KeyContext.h
+  KeyManager.h
   KeyRing.h
   KVMap.h
   LanguageCode.h
@@ -935,7 +937,10 @@ TARGET_LINK_LIBRARIES(zypp ${ZLIB_LIBRARY} )
 TARGET_LINK_LIBRARIES(zypp ${LibSolv_LIBRARIES} )
 TARGET_LINK_LIBRARIES(zypp ${OPENSSL_LIBRARIES} )
 TARGET_LINK_LIBRARIES(zypp ${CRYPTO_LIBRARIES} )
-TARGET_LINK_LIBRARIES(zypp ${SIGNALS_LIBRARY} )
+TARGET_LINK_LIBRARIES(zypp ${SIGNALS_LIBRARY})
+TARGET_LINK_LIBRARIES(zypp ${Boost_THREAD_LIBRARY})
+TARGET_LINK_LIBRARIES(zypp ${GPGME_PTHREAD_LIBRARIES})
+
 
 IF ( UDEV_FOUND )
   TARGET_LINK_LIBRARIES(zypp ${UDEV_LIBRARY} )
index c0e8812..38042d9 100644 (file)
@@ -27,6 +27,7 @@
 #endif
 
 #include "zypp/Digest.h"
+#include "zypp/base/PtrTypes.h"
 
 namespace zypp {
 
@@ -61,26 +62,27 @@ namespace zypp {
     // private data
     class Digest::P
     {
-       P(const P& p);
-       const P& operator=(const P& p);
+      P(const P& p);
+      const P& operator=(const P& p);
+
       public:
-       P();
-       ~P();
+        typedef zypp::shared_ptr<EVP_MD_CTX> EvpDataPtr;
+        P();
+        ~P();
 
-        EVP_MD_CTX *mdctx;
+        EvpDataPtr mdctx;
 
-       const EVP_MD *md;
-       unsigned char md_value[EVP_MAX_MD_SIZE];
-       unsigned md_len;
+        const EVP_MD *md;
+        unsigned char md_value[EVP_MAX_MD_SIZE];
+        unsigned md_len;
 
-       bool initialized : 1;
-       bool finalized : 1;
-       static bool openssl_digests_added;
+        bool finalized : 1;
+        static bool openssl_digests_added;
 
-       std::string name;
+        std::string name;
 
-       inline bool maybeInit();
-       inline void cleanup();
+        inline bool maybeInit();
+        inline void cleanup();
     };
 
 
@@ -90,7 +92,6 @@ namespace zypp {
 
     Digest::P::P() :
       md(NULL),
-      initialized(false),
       finalized(false)
     {
     }
@@ -104,47 +105,43 @@ namespace zypp {
     {
       if(!openssl_digests_added)
       {
-       OPENSSL_config(NULL);
-       ENGINE_load_builtin_engines();
-       ENGINE_register_all_complete();
-       OpenSSL_add_all_digests();
-       openssl_digests_added = true;
+        OPENSSL_config(NULL);
+        ENGINE_load_builtin_engines();
+        ENGINE_register_all_complete();
+        OpenSSL_add_all_digests();
+        openssl_digests_added = true;
       }
 
-      if(!initialized)
+      if(!mdctx)
       {
-       md = EVP_get_digestbyname(name.c_str());
-       if(!md)
-           return false;
+        md = EVP_get_digestbyname(name.c_str());
+        if(!md)
+          return false;
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
-        mdctx = (EVP_MD_CTX*) malloc(sizeof(EVP_MD_CTX));
-        EVP_MD_CTX_init(mdctx);
+        EvpDataPtr tmp_mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
 #else
-        mdctx = EVP_MD_CTX_new();
+        EvpDataPtr tmp_mdctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
 #endif
-        if(!EVP_DigestInit_ex(mdctx, md, NULL))
-           return false;
+        if (!tmp_mdctx)
+          return false;
+
+        if (!EVP_DigestInit_ex(tmp_mdctx.get(), md, NULL)) {
+          return false;
+        }
 
-       md_len = 0;
-       ::memset(md_value, 0, sizeof(md_value));
-       initialized = true;
+        md_len = 0;
+        ::memset(md_value, 0, sizeof(md_value));
+
+        mdctx.swap(tmp_mdctx);
       }
       return true;
     }
 
     void Digest::P::cleanup()
     {
-      if(initialized)
-      {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-        EVP_MD_CTX_cleanup(mdctx);
-#else
-        EVP_MD_CTX_free(mdctx);
-#endif
-       initialized = false;
-       finalized = false;
-      }
+      mdctx.reset();
+      finalized = false;
     }
 
     Digest::Digest() : _dp(new P())
@@ -160,7 +157,7 @@ namespace zypp {
     {
       if(name.empty()) return false;
 
-      if(_dp->initialized)
+      if(_dp->mdctx)
        _dp->cleanup();
 
       _dp->name = name;
@@ -175,15 +172,15 @@ namespace zypp {
 
     bool Digest::reset()
     {
-      if (!_dp->initialized)
-       return false;
+      if (!_dp->mdctx)
+        return false;
       if(!_dp->finalized)
-       {
-         (void)EVP_DigestFinal_ex(_dp->mdctx, _dp->md_value, &_dp->md_len);
-          _dp->finalized = true;
-       }
-      if(!EVP_DigestInit_ex(_dp->mdctx, _dp->md, NULL))
-       return false;
+      {
+        (void)EVP_DigestFinal_ex(_dp->mdctx.get(), _dp->md_value, &_dp->md_len);
+        _dp->finalized = true;
+      }
+      if(!EVP_DigestInit_ex(_dp->mdctx.get(), _dp->md, NULL))
+        return false;
       _dp->finalized = false;
       return true;
     }
@@ -195,7 +192,7 @@ namespace zypp {
 
       if(!_dp->finalized)
       {
-       if(!EVP_DigestFinal_ex(_dp->mdctx, _dp->md_value, &_dp->md_len))
+      if(!EVP_DigestFinal_ex(_dp->mdctx.get(), _dp->md_value, &_dp->md_len))
            return std::string();
 
        _dp->finalized = true;
@@ -220,7 +217,7 @@ namespace zypp {
 
       if(!_dp->finalized)
       {
-        if(!EVP_DigestFinal_ex(_dp->mdctx, _dp->md_value, &_dp->md_len))
+        if(!EVP_DigestFinal_ex(_dp->mdctx.get(), _dp->md_value, &_dp->md_len))
             return r;
         _dp->finalized = true;
       }
@@ -247,7 +244,7 @@ namespace zypp {
            return false;
 
       }
-      if(!EVP_DigestUpdate(_dp->mdctx, reinterpret_cast<const unsigned char*>(bytes), len))
+      if(!EVP_DigestUpdate(_dp->mdctx.get(), reinterpret_cast<const unsigned char*>(bytes), len))
        return false;
 
       return true;
diff --git a/zypp/KeyManager.cc b/zypp/KeyManager.cc
new file mode 100644 (file)
index 0000000..781a40c
--- /dev/null
@@ -0,0 +1,387 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+#include "zypp/KeyManager.h"
+#include "zypp/PublicKey.h"
+#include "zypp/PathInfo.h"
+#include "zypp/base/Logger.h"
+#include "zypp/TmpPath.h"
+#include "zypp/base/String.h"
+
+#include <boost/thread/once.hpp>
+#include <boost/interprocess/smart_ptr/scoped_ptr.hpp>
+#include <gpgme.h>
+
+#include <stdio.h>
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
+
+// @TODO [threading]
+// make sure to call the init code of gpgme only once
+// this might need to be moved to a different location when
+// threads are introduced into libzypp
+boost::once_flag gpgme_init_once = BOOST_ONCE_INIT;
+using std::endl;
+
+//using boost::interprocess pointer because it allows a custom deleter
+typedef boost::interprocess::scoped_ptr<gpgme_data, boost::function<void (gpgme_data_t)>> GpgmeDataPtr;
+typedef boost::interprocess::scoped_ptr<_gpgme_key, boost::function<void (gpgme_key_t)>>  GpgmeKeyPtr;
+typedef boost::interprocess::scoped_ptr<FILE, boost::function<int (FILE *)>> FILEPtr;
+
+struct GpgmeErr
+{
+  GpgmeErr( gpgme_error_t err_r = GPG_ERR_NO_ERROR )
+  : _err( err_r )
+  {}
+  operator gpgme_error_t() const { return _err; }
+private:
+  gpgme_error_t _err;
+};
+std::ostream & operator<<( std::ostream & str, const GpgmeErr & obj )
+{ return str << "<" << gpgme_strsource(obj) << "> " << gpgme_strerror(obj); }
+
+static void initGpgme ()
+{
+  gpgme_check_version(NULL);
+
+}
+
+namespace zypp
+{
+
+class KeyManagerCtx::Impl
+{
+public:
+  Impl();
+  ~Impl();
+
+  std::list<std::string> verifyAndReadSignaturesFprs(const Pathname & file, const Pathname & signature, bool &verifed);
+
+  gpgme_ctx_t _ctx;
+};
+
+KeyManagerCtx::Impl::Impl()
+{ }
+
+
+KeyManagerCtx::KeyManagerCtx()
+  : _pimpl( new Impl )
+{
+
+}
+
+/*
+ * \brief KeyManagerCtx::Impl::verifyAndReadSignaturesFprs
+ * Tries to verify the \a file using \a signature , will return all
+ * signatures and sets \a verified to true if all signatures are good
+ * \internal
+ */
+std::list<std::string> KeyManagerCtx::Impl::verifyAndReadSignaturesFprs(const Pathname &file, const Pathname &signature, bool &verifed)
+{
+  //lets be pessimistic
+  verifed = false;
+
+  if (!PathInfo( signature ).isExist())
+    return std::list<std::string>();
+
+  FILEPtr dataFile(fopen(file.c_str(), "rb"), fclose);
+  if (!dataFile)
+    return std::list<std::string>();
+
+  GpgmeDataPtr fileData(nullptr, gpgme_data_release);
+  GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
+  if (err) {
+    ERR << err << endl;
+    return std::list<std::string>();
+  }
+
+  FILEPtr sigFile(fopen(signature.c_str(), "rb"), fclose);
+  if (!sigFile) {
+    ERR << "Unable to open signature file '" << signature << "'" <<endl;
+    return std::list<std::string>();
+  }
+
+  GpgmeDataPtr sigData(nullptr, gpgme_data_release);
+  err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
+  if (err) {
+    ERR << err << endl;
+    return std::list<std::string>();
+  }
+
+  err = gpgme_op_verify(_ctx, sigData.get(), fileData.get(), NULL);
+  if (err != GPG_ERR_NO_ERROR) {
+    ERR << err << endl;
+    return std::list<std::string>();
+  }
+
+  gpgme_verify_result_t res = gpgme_op_verify_result(_ctx);
+  if (!res || !res->signatures) {
+    ERR << "Unable to read signature fingerprints" <<endl;
+    return std::list<std::string>();
+  }
+
+  bool foundBadSignature = false;
+  std::list<std::string> signatures;
+  gpgme_signature_t sig = res->signatures;
+  while (sig) {
+
+    if (!foundBadSignature)
+      foundBadSignature = (sig->status != GPG_ERR_NO_ERROR);
+
+    if (!sig->fpr)
+      continue;
+
+    signatures.push_back(str::asString(sig->fpr));
+    sig = sig->next;
+  }
+
+  verifed = (!foundBadSignature);
+  return signatures;
+}
+
+KeyManagerCtx::Impl::~Impl()
+{
+  gpgme_release(_ctx);
+}
+
+KeyManagerCtx::Ptr KeyManagerCtx::createForOpenPGP()
+{
+  //make sure gpgpme is initialized
+  boost::call_once(gpgme_init_once, initGpgme);
+
+  gpgme_ctx_t ctx;
+  GpgmeErr err = gpgme_new(&ctx);
+  if (err != GPG_ERR_NO_ERROR) {
+    ERR << err << endl;
+    return shared_ptr<KeyManagerCtx>();
+  }
+
+  //use OpenPGP
+  err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
+  if (err != GPG_ERR_NO_ERROR) {
+    ERR << err << endl;
+    gpgme_release(ctx);
+    return shared_ptr<KeyManagerCtx>();
+  }
+
+  shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
+  me->_pimpl->_ctx = ctx;
+  return me;
+}
+
+bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
+{
+
+  /* get engine information to read current state*/
+  gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
+  if (!enginfo)
+    return false;
+
+  GpgmeErr err = gpgme_ctx_set_engine_info(
+        _pimpl->_ctx,
+        GPGME_PROTOCOL_OpenPGP,
+        enginfo->file_name,
+        keyring_r.c_str());
+
+  if (err != GPG_ERR_NO_ERROR) {
+    ERR << "Unable to set homedir " << err << endl;
+    return false;
+  }
+
+  return true;
+}
+
+Pathname KeyManagerCtx::homedir() const
+{
+  gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
+  if (!enginfo)
+    return Pathname();
+
+  return Pathname(enginfo->home_dir);
+}
+
+std::list<PublicKeyData> KeyManagerCtx::listKeys()
+{
+  std::list<PublicKeyData> keys;
+  gpgme_key_t key;
+  GpgmeErr err = GPG_ERR_NO_ERROR;
+
+  gpgme_keylist_mode_t mode = GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS;
+  gpgme_set_keylist_mode (_pimpl->_ctx, mode);
+  gpgme_op_keylist_start (_pimpl->_ctx, NULL, 0);
+
+  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
+    PublicKeyData data = PublicKeyData::fromGpgmeKey(key);
+    if (data) {
+      keys.push_back(data);
+    }
+    gpgme_key_release(key);
+  }
+  gpgme_op_keylist_end(_pimpl->_ctx);
+  return keys;
+}
+
+std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
+{
+  //seems GPGME does not support reading keys from a keyfile using
+  //gpgme_data_t and gpgme_op_keylist_from_data_start, this always
+  //return unsupported errors. However importing and listing the key works.
+  zypp::Pathname realHomedir = homedir();
+
+  zypp::filesystem::TmpDir tmpKeyring;
+  if (!setHomedir(tmpKeyring.path()))
+    return std::list<PublicKeyData>();
+
+  if (!importKey(file)) {
+    setHomedir(realHomedir);
+    return std::list<PublicKeyData>();
+  }
+
+  std::list<PublicKeyData> keys = listKeys();
+  setHomedir(realHomedir);
+  return keys;
+}
+
+bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
+{
+  if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
+    return false;
+
+  bool verified = false;
+  _pimpl->verifyAndReadSignaturesFprs(file, signature, verified);
+  return verified;
+}
+
+bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
+{
+  GpgmeErr err = GPG_ERR_NO_ERROR;
+
+  GpgmeKeyPtr foundKey;
+
+  //search for requested key id
+  gpgme_key_t key;
+  gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
+  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
+    if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
+      GpgmeKeyPtr(key, gpgme_key_release).swap(foundKey);
+      break;
+    }
+    gpgme_key_release(key);
+  }
+  gpgme_op_keylist_end(_pimpl->_ctx);
+
+  if (!foundKey) {
+    WAR << "Key " << id << "not found" << endl;
+    return false;
+  }
+
+  //function needs a array of keys to export
+  gpgme_key_t keyarray[2];
+  keyarray[0] = foundKey.get();
+  keyarray[1] = NULL;
+
+  GpgmeDataPtr out(nullptr, gpgme_data_release);
+  err = gpgme_data_new (&out.get());
+  if (err) {
+    ERR << err << endl;
+    return false;
+  }
+
+  //format as ascii armored
+  gpgme_set_armor (_pimpl->_ctx, 1);
+  err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
+  if (!err) {
+    int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
+    if (ret) {
+      ERR << "Unable to seek in exported key data" << endl;
+      return false;
+    }
+
+    const int bufsize = 512;
+    char buf[bufsize + 1];
+    while ((ret = gpgme_data_read(out.get(), buf, bufsize)) > 0) {
+      stream.write(buf, ret);
+    }
+
+    //failed to read from buffer
+    if (ret < 0) {
+      ERR << "Unable to read exported key data" << endl;
+      return false;
+    }
+  } else {
+    ERR << "Error exporting key: "<< err << endl;
+    return false;
+  }
+
+  //if we reach this point export was successful
+  return true;
+}
+
+bool KeyManagerCtx::importKey(const Pathname &keyfile)
+{
+  if ( !PathInfo( keyfile ).isExist() ) {
+    ERR << "Keyfile '" << keyfile << "' does not exist.";
+    return false;
+  }
+
+  GpgmeDataPtr data(nullptr, gpgme_data_release);
+  GpgmeErr err;
+
+  err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
+  if (err) {
+    ERR << "Error importing key: "<< err << endl;
+    return false;
+  }
+
+  err = gpgme_op_import(_pimpl->_ctx, data.get());
+  if (err) {
+    ERR << "Error importing key: "<< err << endl;
+  }
+  return (err == GPG_ERR_NO_ERROR);
+}
+
+bool KeyManagerCtx::deleteKey(const std::string &id)
+{
+  gpgme_key_t key;
+  GpgmeErr err = GPG_ERR_NO_ERROR;
+
+  gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
+
+  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
+    if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
+        err = gpgme_op_delete(_pimpl->_ctx, key, 0);
+
+        gpgme_key_release(key);
+        gpgme_op_keylist_end(_pimpl->_ctx);
+
+        if (err) {
+          ERR << "Error deleting key: "<< err << endl;
+          return false;
+        }
+        return true;
+    }
+    gpgme_key_release(key);
+  }
+
+  gpgme_op_keylist_end(_pimpl->_ctx);
+  WAR << "Key: '"<< id << "' not found." << endl;
+  return false;
+}
+
+std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
+{
+  //gpgme needs a dummy file to read signatures from a detached sig file
+  //verification will fail but we get all signatures
+  zypp::filesystem::TmpFile dummyFile;
+
+  bool verified = false;
+  return _pimpl->verifyAndReadSignaturesFprs(dummyFile.path(), signature, verified);
+}
+
+}
diff --git a/zypp/KeyManager.h b/zypp/KeyManager.h
new file mode 100644 (file)
index 0000000..5845d8d
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/KeyManager.h
+ *
+*/
+#ifndef ZYPP_KEYMANAGER_H
+#define ZYPP_KEYMANAGER_H
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Pathname.h"
+#include "zypp/PublicKey.h"
+
+#include <memory>
+
+namespace zypp
+{
+
+ ///////////////////////////////////////////////////////////////////
+ /// \class KeyManagerCtx::KeyManagerCtx
+ /// \brief Wrapper for GPGME
+ ///
+ /// Encapsulates all calls to the gpgme library, each instance
+ /// represents a context of operations on the specified keyring.
+ ///////////////////////////////////////////////////////////////////
+class KeyManagerCtx
+{
+    public:
+        typedef shared_ptr<KeyManagerCtx> Ptr;
+
+        /** Creates a new KeyManagerCtx for PGP */
+        static Ptr createForOpenPGP();
+
+        /** Changes the keyring directory */
+        bool setHomedir (const Pathname & keyring_r);
+        Pathname homedir ()const;
+
+        /**  Returns a list of all public keys found in the current keyring */
+        std::list<PublicKeyData> listKeys();
+
+        /** Returns a list of all \sa PublicKeyData found in \a file */
+        std::list<PublicKeyData> readKeyFromFile(const Pathname & file);
+
+        /** Tries to verify \a file using \a signature, returns true on success */
+        bool verify(const Pathname & file, const Pathname & signature);
+
+        /** Exports the key with \a id into the given \a stream, returns true on success */
+        bool exportKey(const std::string & id, std::ostream & stream);
+
+        /** Tries to import a key from \a keyfile, returns true on success */
+        bool importKey(const Pathname & keyfile);
+
+        /** Tries to delete a key specified by \a id, returns true on success */
+        bool deleteKey (const std::string & id);
+
+        /** Reads all fingerprints from the \a signature file , returns a list of all found fingerprints */
+        std::list<std::string> readSignatureFingerprints(const Pathname & signature);
+
+    private:
+      class Impl;
+
+      KeyManagerCtx();
+
+      RW_pointer<Impl> _pimpl; ///< Pointer to implementation
+
+};
+
+}
+
+
+#endif
index c3e1169..f999239 100644 (file)
 #include "zypp/ExternalProgram.h"
 #include "zypp/TmpPath.h"
 #include "zypp/ZYppCallbacks.h"       // JobReport::instance
+#include "zypp/KeyManager.h"
 
 using std::endl;
 
 #undef  ZYPP_BASE_LOGGER_LOGGROUP
 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
 
-/** \todo Fix duplicate define in PublicKey/KeyRing */
-#define GPG_BINARY "/usr/bin/gpg2"
-
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
@@ -144,38 +142,17 @@ namespace zypp
 
       const std::list<PublicKeyData> & getData( const Pathname & keyring_r, Cache & cache_r ) const
       {
-       if ( cache_r.hasChanged() )
-       {
-         const char* argv[] =
-         {
-           GPG_BINARY,
-           "--list-public-keys",
-           "--homedir", keyring_r.c_str(),
-           "--no-default-keyring",
-           "--quiet",
-           "--with-colons",
-           "--fixed-list-mode",
-           "--with-fingerprint",
-           "--with-sig-list",
-           "--no-tty",
-           "--no-greeting",
-           "--batch",
-           "--status-fd", "1",
-           NULL
-         };
-
-         PublicKeyScanner scanner;
-         ExternalProgram prog( argv ,ExternalProgram::Discard_Stderr, false, -1, true );
-         for( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-         {
-           scanner.scan( line );
-         }
-         prog.close();
-
-         cache_r._data.swap( scanner._keys );
-         MIL << "Found keys: " << cache_r._data  << endl;
-       }
-       return cache_r._data;
+        if ( cache_r.hasChanged() ) {
+          shared_ptr<KeyManagerCtx> ctx = KeyManagerCtx::createForOpenPGP();
+          if (ctx) {
+            if (ctx->setHomedir(keyring_r)) {
+              std::list<PublicKeyData> foundKeys = ctx->listKeys();
+              cache_r._data.swap(foundKeys);
+            }
+          }
+          MIL << "Found keys: " << cache_r._data  << endl;
+        }
+        return cache_r._data;
       }
 
       mutable CacheMap _cacheMap;
@@ -391,27 +368,10 @@ namespace zypp
 
   void KeyRing::Impl::dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream )
   {
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "-a",
-      "--export",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--no-greeting",
-      "--no-permission-warning",
-      "--batch",
-      id.c_str(),
-      NULL
-    };
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
-    for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-    {
-      stream << line;
-    }
-    prog.close();
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if (!ctx || !ctx->setHomedir(keyring))
+      return;
+    ctx->exportKey(id, stream);
   }
 
   filesystem::TmpFile KeyRing::Impl::dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring )
@@ -562,48 +522,31 @@ namespace zypp
                                   % keyfile.asString()
                                   % keyring.asString() ));
 
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--import",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--no-greeting",
-      "--no-permission-warning",
-      "--status-fd", "1",
-      keyfile.asString().c_str(),
-      NULL
-    };
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if(!ctx || !ctx->setHomedir(keyring))
+      ZYPP_THROW(KeyRingException(_("Failed to import key.")));
 
     cachedPublicKeyData.setDirty( keyring );
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
-    if ( prog.close() )
+    if(!ctx->importKey(keyfile))
       ZYPP_THROW(KeyRingException(_("Failed to import key.")));
   }
 
   void KeyRing::Impl::deleteKey( const std::string & id, const Pathname & keyring )
   {
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--delete-keys",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--yes",
-      "--quiet",
-      "--no-tty",
-      "--batch",
-      "--status-fd", "1",
-      id.c_str(),
-      NULL
-    };
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if(!ctx) {
+      ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
+    }
 
-    cachedPublicKeyData.setDirty( keyring );
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
-    if ( prog.close() )
+    if(!ctx->setHomedir(keyring)) {
       ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
+    }
+
+    if(!ctx->deleteKey(id)){
+      ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
+    }
+
+    cachedPublicKeyData.setDirty( keyring );
   }
 
   std::string KeyRing::Impl::readSignatureKeyId( const Pathname & signature )
@@ -612,69 +555,28 @@ namespace zypp
       ZYPP_THROW(KeyRingException( str::Format(_("Signature file %s not found")) % signature.asString() ));
 
     MIL << "Determining key id of signature " << signature << endl;
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--list-packets",
-      signature.asString().c_str(),
-      NULL
-    };
-    ExternalProgram prog( argv ,ExternalProgram::Discard_Stderr, false, -1, true );
-
-    // :signature packet: algo 1, keyid 1397BC53640DB551
-    //         version 4, created 1501094968, md5len 0, sigclass 0x00
-    //         digest algo 8, begin of digest 15 89
-    //         hashed subpkt 2 len 4 (sig created 2017-07-26)
-    //         subpkt 16 len 8 (issuer key ID 1397BC53640DB551)
-    //         data: [4095 bits]
-    std::string id;
-    for( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-    {
-      if ( id.empty() && str::startsWith( line, ":signature packet:" ) )
-      {
-       static const str::regex rxKeyId( " keyid +([0-9A-Z]+)" );
-       str::smatch what;
-       if( str::regex_match( line, what, rxKeyId ) )
-         id = what[1];
-      }
+
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if(!ctx) {
+      return std::string();
     }
 
-    MIL << "Determined key id [" << id << "] for signature " << signature << endl;
-    prog.close();
-    return id;
+    std::list<std::string> fprs = ctx->readSignatureFingerprints(signature);
+    if (fprs.size()) {
+      std::string &id = fprs.back();
+      MIL << "Determined key id [" << id << "] for signature " << signature << endl;
+      return id;
+    }
+    return std::string();
   }
 
   bool KeyRing::Impl::verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring )
   {
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--verify",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--batch",
-      "--no-greeting",
-      "--status-fd", "1",
-      signature.asString().c_str(),
-      file.asString().c_str(),
-      NULL
-    };
-
-    // no need to parse output for now
-    //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
-    //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
-    //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
-    //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
-    //     [GNUPG:] TRUST_UNDEFINED
-
-    //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
-    //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
-
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if (!ctx || !ctx->setHomedir(keyring))
+      return false;
 
-    return ( prog.close() == 0 ) ? true : false;
+    return ctx->verify(file, signature);
   }
 
   ///////////////////////////////////////////////////////////////////
index b350034..41d093d 100644 (file)
 #include "zypp/base/Exception.h"
 #include "zypp/base/LogTools.h"
 #include "zypp/Date.h"
+#include "zypp/KeyManager.h"
 
-/** \todo Fix duplicate define in PublicKey/KeyRing */
-#define GPG_BINARY "/usr/bin/gpg2"
+#include <gpgme.h>
 
 using std::endl;
 
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
+
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 {
@@ -102,10 +105,12 @@ namespace zypp
   } //namespace
   ///////////////////////////////////////////////////////////////////
 
+
   ///////////////////////////////////////////////////////////////////
   /// \class PublicSubkeyData::Impl
   /// \brief  PublicSubkeyData implementation.
   ///////////////////////////////////////////////////////////////////
+
   struct PublicSubkeyData::Impl
   {
     std::string _id;
@@ -114,19 +119,25 @@ namespace zypp
 
   public:
     /** Offer default Impl. */
-    static shared_ptr<Impl> nullimpl()
-    {
-      static shared_ptr<Impl> _nullimpl( new Impl );
-      return _nullimpl;
-    }
+    static shared_ptr<Impl> nullimpl();
 
   private:
     friend Impl * rwcowClone<Impl>( const Impl * rhs );
     /** clone for RWCOW_pointer */
-    Impl * clone() const
-    { return new Impl( *this ); }
+    Impl * clone() const;
   };
 
+  shared_ptr<zypp::PublicSubkeyData::Impl> PublicSubkeyData::Impl::nullimpl()
+  {
+    static shared_ptr<Impl> _nullimpl( new Impl );
+    return _nullimpl;
+  }
+
+  zypp::PublicSubkeyData::Impl *PublicSubkeyData::Impl::clone() const
+  {
+    return new Impl( *this );
+  }
+
   ///////////////////////////////////////////////////////////////////
   /// class PublicSubkeyData
   ///////////////////////////////////////////////////////////////////
@@ -135,6 +146,14 @@ namespace zypp
     : _pimpl( Impl::nullimpl() )
   {}
 
+  PublicSubkeyData::PublicSubkeyData(const _gpgme_subkey *rawSubKeyData)
+    : _pimpl (new Impl)
+  {
+    _pimpl->_created = zypp::Date(rawSubKeyData->timestamp);
+    _pimpl->_expires = zypp::Date(rawSubKeyData->expires);
+    _pimpl->_id = str::asString(rawSubKeyData->keyid);
+  }
+
   PublicSubkeyData::~PublicSubkeyData()
   {}
 
@@ -165,6 +184,7 @@ namespace zypp
   /// \class PublicKeyData::Impl
   /// \brief  PublicKeyData implementation.
   ///////////////////////////////////////////////////////////////////
+  ///
   struct PublicKeyData::Impl
   {
     std::string _id;
@@ -176,35 +196,78 @@ namespace zypp
     std::vector<PublicSubkeyData> _subkeys;
 
   public:
-    bool hasSubkeyId( const std::string & id_r ) const
-    {
-      bool ret = false;
-      for ( const PublicSubkeyData & sub : _subkeys )
-      {
-       if ( sub.id() == id_r )
-       {
-         ret = true;
-         break;
-       }
-      }
-      return ret;
-    }
+    bool hasSubkeyId( const std::string & id_r ) const;
 
   public:
     /** Offer default Impl. */
-    static shared_ptr<Impl> nullimpl()
-    {
-      static shared_ptr<Impl> _nullimpl( new Impl );
-      return _nullimpl;
-    }
+    static shared_ptr<Impl> nullimpl();
+    static shared_ptr<Impl> fromGpgmeKey(gpgme_key_t rawData);
 
   private:
     friend Impl * rwcowClone<Impl>( const Impl * rhs );
     /** clone for RWCOW_pointer */
-    Impl * clone() const
-    { return new Impl( *this ); }
+    Impl * clone() const;
   };
 
+  bool PublicKeyData::Impl::hasSubkeyId(const std::string &id_r) const
+  {
+    bool ret = false;
+    for ( const PublicSubkeyData & sub : _subkeys )
+    {
+      if ( sub.id() == id_r )
+      {
+        ret = true;
+        break;
+      }
+    }
+    return ret;
+  }
+
+  shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::nullimpl()
+  {
+    static shared_ptr<Impl> _nullimpl( new Impl );
+    return _nullimpl;
+  }
+
+  shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::fromGpgmeKey(gpgme_key_t rawData)
+  {
+    //gpgpme stores almost nothing in the top level key
+    //the information we look for is stored in the subkey, where subkey[0]
+    //is always the primary key
+    gpgme_subkey_t sKey = rawData->subkeys;
+    if (sKey) {
+      shared_ptr<PublicKeyData::Impl> data(new Impl);
+      //libzypp expects the date of the first signature on the first uid
+      if(rawData->uids && rawData->uids->signatures)
+        data->_created = zypp::Date(rawData->uids->signatures->timestamp);
+      else
+        data->_created = zypp::Date(sKey->timestamp);
+
+      data->_expires = zypp::Date(sKey->expires);
+      data->_fingerprint = str::asString(sKey->fpr);
+      data->_id = str::asString(sKey->keyid);
+
+      //get the primary user ID
+      if (rawData->uids) {
+        data->_name = str::asString(rawData->uids->uid);
+      }
+
+      //the rest of the keys
+      sKey = sKey->next;
+      while (sKey) {
+        data->_subkeys.push_back( PublicSubkeyData(sKey) );
+        sKey = sKey->next;
+      }
+      return data;
+    }
+    return nullimpl();
+  }
+
+  zypp::PublicKeyData::Impl *PublicKeyData::Impl::clone() const
+  {
+    return new Impl( *this );
+  }
+
   ///////////////////////////////////////////////////////////////////
   /// class PublicKeyData
   ///////////////////////////////////////////////////////////////////
@@ -213,9 +276,16 @@ namespace zypp
     : _pimpl( Impl::nullimpl() )
   {}
 
+  PublicKeyData::PublicKeyData(shared_ptr<Impl> data)
+    : _pimpl( data )
+  {}
+
   PublicKeyData::~PublicKeyData()
   {}
 
+  PublicKeyData PublicKeyData::fromGpgmeKey(_gpgme_key *data)
+  { return PublicKeyData(Impl::fromGpgmeKey(data)); }
+
   PublicKeyData::operator bool() const
   { return !_pimpl->_fingerprint.empty(); }
 
@@ -286,141 +356,6 @@ namespace zypp
 
 
   ///////////////////////////////////////////////////////////////////
-  /// \class PublicKeyScanner::Impl
-  /// \brief  PublicKeyScanner implementation.
-  ///////////////////////////////////////////////////////////////////
-  struct PublicKeyScanner::Impl
-  {
-    enum { pNONE, pPUB, pSIG, pFPR, pUID, pSUB } _parseEntry;
-    std::vector<std::string> _words;
-    PublicKeyData::Impl * _keyDataPtr;
-
-   Impl()
-      : _parseEntry( pNONE )
-      , _keyDataPtr( nullptr )
-    {}
-
-    void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
-    {
-      // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
-      // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
-      // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
-      // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
-      // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
-      // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
-      // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
-      // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
-      if ( line_r.empty() )
-       return;
-
-      // quick check for interesting entries, no parsing in subkeys
-      _parseEntry = pNONE;
-      switch ( line_r[0] )
-      {
-       case 'p':
-         if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
-         {
-           _parseEntry = pPUB;
-           keys_r.push_back( PublicKeyData() );        // reset upon new key
-           _keyDataPtr = keys_r.back()._pimpl.get();
-         }
-         break;
-
-       case 'f':
-         if ( line_r[1] == 'p' && line_r[2] == 'r' && line_r[3] == ':' )
-           _parseEntry = pFPR;
-         break;
-
-       case 'u':
-         if ( line_r[1] == 'i' && line_r[2] == 'd' && line_r[3] == ':' )
-           _parseEntry = pUID;
-         break;
-
-       case 's':
-         if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
-           _parseEntry = pSIG;
-         else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
-           _parseEntry = pSUB;
-         break;
-
-       default:
-         return;
-      }
-      if ( _parseEntry == pNONE )
-       return;
-      if ( ! ( _keyDataPtr->_subkeys.empty() || _parseEntry == pSUB ) )
-       return; // collecting subkeys only
-
-      if ( line_r[line_r.size()-1] == '\n' )
-       line_r.erase( line_r.size()-1 );
-      //DBG << line_r << endl;
-
-      _words.clear();
-      str::splitFields( line_r, std::back_inserter(_words), ":" );
-
-      switch ( _parseEntry )
-      {
-       case pPUB:
-         _keyDataPtr->_id      = _words[4];
-         _keyDataPtr->_name    = str::replaceAll( _words[9], "\\x3a", ":" );
-         _keyDataPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
-         _keyDataPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
-         break;
-
-       case pSIG:
-         // Update creation/modification date from signatures type "13x".
-         if ( ( _words.size() > 10 && _words[10] == "13x" && !_words[9].empty() && _words[9] != "[User ID not found]" )
-           || ( _words.size() > 12 && _words[12] == "13x" /* [selfsig] */) )
-         {
-           Date cdate(str::strtonum<Date::ValueType>(_words[5]));
-           if ( _keyDataPtr->_created < cdate )
-             _keyDataPtr->_created = cdate;
-         }
-         break;
-
-       case pFPR:
-         if ( _keyDataPtr->_fingerprint.empty() )
-           _keyDataPtr->_fingerprint = _words[9];
-         break;
-
-       case pUID:
-         if ( ! _words[9].empty() && _words[9] != "[User ID not found]" )
-           _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
-         break;
-
-       case pSUB:
-         _keyDataPtr->_subkeys.push_back( PublicSubkeyData() );
-         {
-           PublicSubkeyData::Impl * subPtr = _keyDataPtr->_subkeys.back()._pimpl.get();
-           subPtr->_id      = _words[4];
-           subPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
-           subPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
-         }
-         break;
-
-       case pNONE:
-         break;        // intentionally no default:
-      }
-    }
-  };
-  ///////////////////////////////////////////////////////////////////
-
-  ///////////////////////////////////////////////////////////////////
-  // class PublicKeyScanner
-  ///////////////////////////////////////////////////////////////////
-
-  PublicKeyScanner::PublicKeyScanner()
-    : _pimpl( new Impl )
-  {}
-
-  PublicKeyScanner::~PublicKeyScanner()
-  {}
-
-  void PublicKeyScanner::scan( std::string line_r )
-  { _pimpl->scan( line_r, _keys ); }
-
-
-  ///////////////////////////////////////////////////////////////////
   /// \class PublicKey::Impl
   /// \brief  PublicKey implementation.
   ///////////////////////////////////////////////////////////////////
@@ -482,60 +417,36 @@ namespace zypp
         PathInfo info( path() );
         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
 
-       static std::string tmppath( _initHomeDir() );
-       std::string datapath( path().asString() );
+        //@TODO is this still required? KeyManagerCtx creates a homedir on the fly
+        static std::string tmppath( _initHomeDir() );
 
-        const char* argv[] =
-        {
-          GPG_BINARY,
-          "-v",
-          "--no-default-keyring",
-          "--fixed-list-mode",
-          "--with-fingerprint",
-          "--with-colons",
-          "--homedir",
-          tmppath.c_str(),
-          "--quiet",
-          "--no-tty",
-          "--no-greeting",
-          "--batch",
-          "--status-fd", "1",
-          datapath.c_str(),
-          NULL
-        };
-        ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
-
-       PublicKeyScanner scanner;
-        for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-        {
-         scanner.scan( line );
-       }
-        int ret = prog.close();
-
-       switch ( scanner._keys.size() )
-       {
-         case 0:
-           if ( ret == 129 )
-             ZYPP_THROW( Exception( std::string("Can't read public key data: ") + GPG_BINARY + " is not installed!" ) );
-           else
-             ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
-           break;
-
-         case 1:
-           // ok.
-           _keyData = scanner._keys.back();
-           _hiddenKeys.clear();
-           break;
-
-         default:
-           WAR << "File " << path().asString() << " contains multiple keys: " <<  scanner._keys << endl;
-           _keyData = scanner._keys.back();
-           scanner._keys.pop_back();
-           _hiddenKeys.swap( scanner._keys );
-           break;
-       }
+        KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+        if (!ctx || !ctx->setHomedir(tmppath)) {
+          ZYPP_THROW( Exception( std::string("Can't read public key data: Setting the keyring path failed!")) );
+        }
 
-       MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
+        std::list<PublicKeyData> keys = ctx->readKeyFromFile(path());
+        switch ( keys.size() )
+        {
+          case 0:
+            ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
+            break;
+
+          case 1:
+            // ok.
+            _keyData = keys.back();
+            _hiddenKeys.clear();
+            break;
+
+          default:
+            WAR << "File " << path().asString() << " contains multiple keys: " <<  keys << endl;
+            _keyData = keys.back();
+            keys.pop_back();
+            _hiddenKeys.swap( keys );
+            break;
+        }
+
+        MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
       }
 
     private:
@@ -563,7 +474,7 @@ namespace zypp
   // class PublicKey
   ///////////////////////////////////////////////////////////////////
   PublicKey::PublicKey()
-  : _pimpl( Impl::nullimpl() )
+    : _pimpl( Impl::nullimpl() )
   {}
 
   PublicKey::PublicKey( const Pathname & file )
@@ -636,6 +547,9 @@ namespace zypp
   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
   { return dumpOn( str, obj.keyData() ); }
 
+
+
+
   /////////////////////////////////////////////////////////////////
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
index 0ac8cb7..4c93d8b 100644 (file)
@@ -25,6 +25,9 @@
 #include "zypp/Edition.h"
 #include "zypp/Date.h"
 
+struct _gpgme_key;
+struct _gpgme_subkey;
+
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
@@ -34,6 +37,7 @@ namespace zypp
     class TmpFile;
   }
   class PublicKeyData;
+  class KeyManagerCtx;
 
   ///////////////////////////////////////////////////////////////////
   /// \class BadKeyException
@@ -112,8 +116,9 @@ namespace zypp
   private:
     class Impl;
     RWCOW_pointer<Impl> _pimpl;
-    friend class PublicKeyScanner;
+    friend class PublicKeyData;
     friend std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
+    PublicSubkeyData(const _gpgme_subkey *rawSubKeyData);
   };
   ///////////////////////////////////////////////////////////////////
 
@@ -214,7 +219,11 @@ namespace zypp
   private:
     class Impl;
     RWCOW_pointer<Impl> _pimpl;
-    friend class PublicKeyScanner;
+
+    friend class KeyManagerCtx;
+    static PublicKeyData fromGpgmeKey(_gpgme_key *data);
+
+    PublicKeyData(shared_ptr<Impl> data);
     friend std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
   };
   ///////////////////////////////////////////////////////////////////
@@ -234,41 +243,6 @@ namespace zypp
   { return !( lhs == rhs ); }
 
   ///////////////////////////////////////////////////////////////////
-  /// \class PublicKeyScanner
-  /// \brief Scan abstract from 'gpg --with-colons' key listings.
-  /// Feed gpg output line by line into \ref scan. The collected \ref PublicKeyData
-  /// contain the keys data (fingerprint, uid,...) but not the key itself (ASCII
-  /// armored stored in a file).
-  /// \code
-  ///   std::list<PublicKeyData> result;
-  ///   {
-  ///     PublicKeyScanner scanner;
-  ///     for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-  ///       scanner.scan( line );
-  ///     result.swap( scanner._keys );
-  ///   }
-  /// \endcode
-  /// \relates PublicKeyData
-  ///////////////////////////////////////////////////////////////////
-  struct PublicKeyScanner
-  {
-    PublicKeyScanner();
-    ~PublicKeyScanner();
-
-    /** Feed gpg output line by line into \ref scan. */
-    void scan( std::string line_r );
-
-    /** Extracted keys. */
-    std::list<PublicKeyData> _keys;
-
-  private:
-    class Impl;
-    RW_pointer<Impl, rw_pointer::Scoped<Impl> > _pimpl;
-  };
-  ///////////////////////////////////////////////////////////////////
-
-
-  ///////////////////////////////////////////////////////////////////
   /// \class PublicKey
   /// \brief Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
   ///
index 285c7dd..9214550 100644 (file)
@@ -142,10 +142,10 @@ namespace zypp
 #endif
 
     inline std::string asString( const char * t )
-    { return t; }
+    { return t == nullptr ? std::string() : t; }
 
     inline std::string asString( char * t )
-    { return t; }
+    { return t == nullptr ? std::string() : t; }
 
     template<class Tp>
         inline std::string asString( const Tp &t )
index 9328c9b..68a71af 100644 (file)
@@ -421,12 +421,12 @@ namespace zypp
     //TODO: make configurable
     list<string> filesystems;
 
-    // if DVD, try UDF filesystem before iso9660
+    filesystems.push_back("iso9660");
+
+    // if DVD, try UDF filesystem after iso9660
     if ( _url.getScheme() == "dvd" )
       filesystems.push_back("udf");
 
-    filesystems.push_back("iso9660");
-
     // try all devices in sequence
     int count = 0;
     std::string mountpoint( attachPoint().asString() );
index 07819da..6588e85 100644 (file)
@@ -12,6 +12,8 @@
 #include <iostream>
 #include <fstream>
 #include <unordered_set>
+#include <iterator>
+#include <stdio.h>
 #include "zypp/base/LogTools.h"
 #include "zypp/base/String.h"
 #include "zypp/base/Gettext.h"
@@ -49,251 +51,354 @@ namespace zypp
     /** lsof output line + files extracted so far for this PID */
     typedef std::pair<std::string,std::unordered_set<std::string>> CacheEntry;
 
-    /** Add \c cache to \c data if the process is accessing deleted files.
-     * \c pid string in \c cache is the proc line \c (pcuLR), \c files
-     * are already in place. Always clear the \c cache.files!
-    */
-    inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, const CacheEntry & cache_r )
+    /////////////////////////////////////////////////////////////////
+    /// \class FilterRunsInLXC
+    /// \brief Functor guessing whether \a PID is running in a container.
+    ///
+    /// Assumme using different \c pid namespace than \c self.
+    /////////////////////////////////////////////////////////////////
+    struct FilterRunsInLXC
     {
-      const auto & filelist( cache_r.second );
+      bool operator()( pid_t pid_r ) const
+      { return( nsIno( pid_r, "pid" ) != pidNS ); }
 
-      if ( filelist.empty() )
-        return;
+      FilterRunsInLXC()
+      : pidNS( nsIno( "self", "pid" ) )
+      {}
 
-      // at least one file access so keep it:
-      data_r.push_back( CheckAccessDeleted::ProcInfo() );
-      CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
-      pinfo.files.insert( pinfo.files.begin(), filelist.begin(), filelist.end() );
+      static inline ino_t nsIno( const std::string & pid_r, const std::string & ns_r )
+      { return PathInfo("/proc/"+pid_r+"/ns/"+ns_r).ino(); }
 
-      const std::string & pline( cache_r.first );
-      std::string commandname; // pinfo.command if still needed...
-      for_( ch, pline.begin(), pline.end() )
-      {
-        switch ( *ch )
-        {
-          case 'p':
-            pinfo.pid = &*(ch+1);
-            break;
-          case 'R':
-            pinfo.ppid = &*(ch+1);
-            break;
-          case 'u':
-            pinfo.puid = &*(ch+1);
-            break;
-          case 'L':
-            pinfo.login = &*(ch+1);
-            break;
-          case 'c':
-           if ( pinfo.command.empty() )
-             commandname = &*(ch+1);
-           break;
-        }
-        if ( *ch == '\n' ) break;              // end of data
-        do { ++ch; } while ( *ch != '\0' );    // skip to next field
-      }
+      static inline ino_t nsIno( pid_t pid_r, const std::string & ns_r )
+      { return  nsIno( asString(pid_r), ns_r ); }
 
-      if ( pinfo.command.empty() )
-      {
-       // the lsof command name might be truncated, so we prefer /proc/<pid>/exe
-       pinfo.command = filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ).basename();
-       if ( pinfo.command.empty() )
-         pinfo.command = std::move(commandname);
-      }
-    }
+      ino_t pidNS;
+    };
+  } //namespace
+  /////////////////////////////////////////////////////////////////
+
+  class CheckAccessDeleted::Impl
+  {
+  public:
+    CheckAccessDeleted::Impl *clone() const;
 
+    bool addDataIf( const CacheEntry & cache_r, std::vector<std::string> *debMap = nullptr );
+    void addCacheIf( CacheEntry & cache_r, const std::string & line_r, std::vector<std::string> *debMap = nullptr );
 
-    /** Add file to cache if it refers to a deleted executable or library file:
-     * - Either the link count \c(k) is \c 0, or no link cout is present.
-     * - The type \c (t) is set to \c REG or \c DEL
-     * - The filedescriptor \c (f) is set to \c txt, \c mem or \c DEL
-    */
-    inline void addCacheIf( CacheEntry & cache_r, const std::string & line_r, bool verbose_r  )
-    {
-      const char * f = 0;
-      const char * t = 0;
-      const char * n = 0;
+    std::map<pid_t,CacheEntry> filterInput( externalprogram::ExternalDataSource &source );
+    CheckAccessDeleted::size_type createProcInfo( const std::map<pid_t,CacheEntry> &in );
 
-      for_( ch, line_r.c_str(), ch+line_r.size() )
-      {
-        switch ( *ch )
-        {
-          case 'k':
-            if ( *(ch+1) != '0' )      // skip non-zero link counts
-              return;
-            break;
-          case 'f':
-            f = ch+1;
-            break;
-          case 't':
-            t = ch+1;
-            break;
-          case 'n':
-            n = ch+1;
-            break;
-        }
-        if ( *ch == '\n' ) break;              // end of data
-        do { ++ch; } while ( *ch != '\0' );    // skip to next field
-      }
+    std::vector<CheckAccessDeleted::ProcInfo> _data;
+    bool _fromLsofFileMode = false; // Set if we currently process data from a debug file
+    bool _verbose = false;
+
+    std::map<pid_t,std::vector<std::string>> debugMap; //will contain all used lsof files after filtering
+    Pathname _debugFile;
+  };
 
-      if ( !t || !f || !n )
-        return;        // wrong filedescriptor/type/name
+  CheckAccessDeleted::Impl *CheckAccessDeleted::Impl::clone() const
+  {
+    Impl *myClone = new Impl( *this );
+    return myClone;
+  }
 
-      if ( !(    ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
-              || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
-        return;        // wrong type
+  /** Add \c cache to \c data if the process is accessing deleted files.
+   * \c pid string in \c cache is the proc line \c (pcuLR), \c files
+   * are already in place. Always clear the \c cache.files!
+  */
+  inline bool CheckAccessDeleted::Impl::addDataIf( const CacheEntry & cache_r, std::vector<std::string> *debMap )
+  {
+    const auto & filelist( cache_r.second );
 
-      if ( !(    ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
-              || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
-              || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
-              || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
-        return;        // wrong filedescriptor type
+    if ( filelist.empty() )
+      return false;
 
-      if ( str::contains( n, "(stat: Permission denied)" ) )
-        return;        // Avoid reporting false positive due to insufficient permission.
+    // at least one file access so keep it:
+    _data.push_back( CheckAccessDeleted::ProcInfo() );
+    CheckAccessDeleted::ProcInfo & pinfo( _data.back() );
+    pinfo.files.insert( pinfo.files.begin(), filelist.begin(), filelist.end() );
 
-      if ( ! verbose_r )
+    const std::string & pline( cache_r.first );
+    std::string commandname;   // pinfo.command if still needed...
+    std::ostringstream pLineStr; //rewrite the first line in debug cache
+    for_( ch, pline.begin(), pline.end() )
+    {
+      switch ( *ch )
       {
-        if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
-          return; // Try to avoid reporting false positive unless verbose.
+        case 'p':
+          pinfo.pid = &*(ch+1);
+          if ( debMap )
+            pLineStr <<&*(ch)<<'\0';
+          break;
+        case 'R':
+          pinfo.ppid = &*(ch+1);
+          if ( debMap )
+            pLineStr <<&*(ch)<<'\0';
+          break;
+        case 'u':
+          pinfo.puid = &*(ch+1);
+          if ( debMap )
+            pLineStr <<&*(ch)<<'\0';
+          break;
+        case 'L':
+          pinfo.login = &*(ch+1);
+          if ( debMap )
+            pLineStr <<&*(ch)<<'\0';
+          break;
+        case 'c':
+          if ( pinfo.command.empty() ) {
+            commandname = &*(ch+1);
+            // the lsof command name might be truncated, so we prefer /proc/<pid>/exe
+            if (!_fromLsofFileMode)
+              pinfo.command = filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ).basename();
+            if ( pinfo.command.empty() )
+              pinfo.command = std::move(commandname);
+            if ( debMap )
+              pLineStr <<'c'<<pinfo.command<<'\0';
+          }
+          break;
       }
+      if ( *ch == '\n' ) break;                // end of data
+      do { ++ch; } while ( *ch != '\0' );      // skip to next field
+    }
+
+    //replace the data in the debug cache as well
+    if ( debMap ) {
+      pLineStr<<endl;
+      debMap->front() = pLineStr.str();
+    }
+
+    //entry was added
+    return true;
+  }
+
 
-      if ( *f == 'm' || *f == 'D' )    // skip some wellknown nonlibrary memorymapped files
+  /** Add file to cache if it refers to a deleted executable or library file:
+   * - Either the link count \c(k) is \c 0, or no link cout is present.
+   * - The type \c (t) is set to \c REG or \c DEL
+   * - The filedescriptor \c (f) is set to \c txt, \c mem or \c DEL
+  */
+  inline void CheckAccessDeleted::Impl::addCacheIf( CacheEntry & cache_r, const std::string & line_r, std::vector<std::string> *debMap )
+  {
+    const char * f = 0;
+    const char * t = 0;
+    const char * n = 0;
+
+    for_( ch, line_r.c_str(), ch+line_r.size() )
+    {
+      switch ( *ch )
       {
-        static const char * black[] = {
-            "/SYSV"
-          , "/var/run/"
-          , "/var/lib/sss/"
-          , "/dev/"
-        };
-        for_( it, arrayBegin( black ), arrayEnd( black ) )
-        {
-          if ( str::hasPrefix( n, *it ) )
+        case 'k':
+          if ( *(ch+1) != '0' )        // skip non-zero link counts
             return;
-        }
+          break;
+        case 'f':
+          f = ch+1;
+          break;
+        case 't':
+          t = ch+1;
+          break;
+        case 'n':
+          n = ch+1;
+          break;
       }
-      // Add if no duplicate
-      cache_r.second.insert( n );
+      if ( *ch == '\n' ) break;                // end of data
+      do { ++ch; } while ( *ch != '\0' );      // skip to next field
     }
 
-    /////////////////////////////////////////////////////////////////
-    /// \class FilterRunsInLXC
-    /// \brief Functor guessing whether \a PID is running in a container.
-    ///
-    /// Assumme using different \c pid namespace than \c self.
-    /////////////////////////////////////////////////////////////////
-    struct FilterRunsInLXC
-    {
-      bool operator()( pid_t pid_r ) const
-      { return( nsIno( pid_r, "pid" ) != pidNS ); }
+    if ( !t || !f || !n )
+      return;  // wrong filedescriptor/type/name
 
-      FilterRunsInLXC()
-      : pidNS( nsIno( "self", "pid" ) )
-      {}
+    if ( !(    ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
+            || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
+      return;  // wrong type
 
-      static inline ino_t nsIno( const std::string & pid_r, const std::string & ns_r )
-      { return PathInfo("/proc/"+pid_r+"/ns/"+ns_r).ino(); }
+    if ( !(    ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
+            || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
+            || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
+            || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
+      return;  // wrong filedescriptor type
 
-      static inline ino_t nsIno( pid_t pid_r, const std::string & ns_r )
-      { return  nsIno( asString(pid_r), ns_r ); }
+    if ( str::contains( n, "(stat: Permission denied)" ) )
+      return;  // Avoid reporting false positive due to insufficient permission.
 
-      ino_t pidNS;
-    };
+    if ( ! _verbose )
+    {
+      if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
+        return; // Try to avoid reporting false positive unless verbose.
+    }
 
-#if 0
-    void lsofdebug( const Pathname & file_r )
+    if ( *f == 'm' || *f == 'D' )      // skip some wellknown nonlibrary memorymapped files
     {
-      std::ifstream infile( file_r.c_str() );
-      USR << infile << endl;
-      std::vector<std::string> fields;
-      CacheEntry cache;
-      for( iostr::EachLine in( infile ); in; in.next() )
+      static const char * black[] = {
+          "/SYSV"
+        , "/var/run/"
+        , "/var/lib/sss/"
+        , "/dev/"
+        , "/var/lib/gdm"
+      };
+      for_( it, arrayBegin( black ), arrayEnd( black ) )
       {
-       std::string field( *in );
-       if ( field[0] == 'f' || field[0] == 'p' )
-       {
-         if ( !fields.empty() )
-         {
-           // consume
-           std::string line( str::join( fields, "\n" ) );
-           for ( char & c : line )
-           { if ( c == '\n' ) c = '\0'; }
-           line.push_back( '\n' );
-
-           size_t sze = cache.second.size();
-           addCacheIf( cache, line, false );
-           if ( sze != cache.second.size() )
-             USR << fields << endl;
-
-           fields.clear();
-         }
-         if ( field[0] == 'p' )
-           continue;
-         fields.push_back( field );
-       }
-       else if ( !fields.empty() )
-       {
-         fields.push_back( field );
-       }
+        if ( str::hasPrefix( n, *it ) )
+          return;
       }
     }
-#endif
-    /////////////////////////////////////////////////////////////////
-  } // namespace
-  ///////////////////////////////////////////////////////////////////
+    // Add if no duplicate
+    if ( debMap && cache_r.second.find(n) == cache_r.second.end() ) {
+      debMap->push_back(line_r);
+    }
+    cache_r.second.insert( n );
+  }
 
-  CheckAccessDeleted::size_type CheckAccessDeleted::check( bool verbose_r )
+  CheckAccessDeleted::CheckAccessDeleted( bool doCheck_r )
+    : _pimpl(new Impl)
   {
-    _data.clear();
+    if ( doCheck_r ) check();
+  }
 
-    static const char* argv[] =
-    {
-      "lsof", "-n", "-FpcuLRftkn0", NULL
-    };
-    ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
+  CheckAccessDeleted::size_type CheckAccessDeleted::check( const Pathname &lsofOutput_r, bool verbose_r )
+  {
+    _pimpl->_verbose = verbose_r;
+    _pimpl->_fromLsofFileMode = true;
+
+    FILE *inFile = fopen( lsofOutput_r.c_str(), "r" );
+    if ( !inFile ) {
+      ZYPP_THROW( Exception(  str::Format("Opening input file %1% failed.") % lsofOutput_r.c_str() ) );
+    }
 
+    //inFile is closed by ExternalDataSource
+    externalprogram::ExternalDataSource inSource( inFile, nullptr );
+    auto cache = _pimpl->filterInput( inSource );
+    return _pimpl->createProcInfo( cache );
+  }
+
+  std::map<pid_t,CacheEntry> CheckAccessDeleted::Impl::filterInput( externalprogram::ExternalDataSource &source )
+  {
     // cachemap: PID => (deleted files)
     // NOTE: omit PIDs running in a (lxc/docker) container
     std::map<pid_t,CacheEntry> cachemap;
+
+    bool debugEnabled = !_debugFile.empty();
+
     pid_t cachepid = 0;
     FilterRunsInLXC runsInLXC;
-    for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+    for( std::string line = source.receiveLine(); ! line.empty(); line = source.receiveLine() )
     {
       // NOTE: line contains '\0' separeated fields!
       if ( line[0] == 'p' )
       {
-       str::strtonum( line.c_str()+1, cachepid );      // line is "p<PID>\0...."
-       if ( !runsInLXC( cachepid ) )
-         cachemap[cachepid].first.swap( line );
-       else
-         cachepid = 0; // ignore this pid
+        str::strtonum( line.c_str()+1, cachepid );     // line is "p<PID>\0...."
+        if ( _fromLsofFileMode || !runsInLXC( cachepid ) ) {
+          if ( debugEnabled ) {
+            auto &pidMad = debugMap[cachepid];
+            if ( pidMad.empty() )
+              debugMap[cachepid].push_back( line );
+            else
+              debugMap[cachepid].front() = line;
+          }
+          cachemap[cachepid].first.swap( line );
+        } else {
+          cachepid = 0;        // ignore this pid
+        }
       }
       else if ( cachepid )
       {
-       addCacheIf( cachemap[cachepid], line, verbose_r );
+        auto &dbgMap = debugMap[cachepid];
+        addCacheIf( cachemap[cachepid], line, debugEnabled ? &dbgMap : nullptr);
       }
     }
+    return cachemap;
+  }
+
+  CheckAccessDeleted::size_type CheckAccessDeleted::check( bool verbose_r  )
+  {
+    static const char* argv[] =
+    {
+      "lsof", "-n", "-FpcuLRftkn0", NULL
+    };
+
+    _pimpl->_verbose = verbose_r;
+    _pimpl->_fromLsofFileMode = false;
+
+    ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
+    std::map<pid_t,CacheEntry> cachemap = _pimpl->filterInput( prog );
 
     int ret = prog.close();
     if ( ret != 0 )
     {
       if ( ret == 129 )
       {
-       ZYPP_THROW( Exception(_("Please install package 'lsof' first.") ) );
+        ZYPP_THROW( Exception(_("Please install package 'lsof' first.") ) );
       }
-      Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
+      Exception err( str::Format("Executing 'lsof' failed (%1%).") % ret );
       err.remember( prog.execError() );
       ZYPP_THROW( err );
     }
 
-    std::vector<ProcInfo> data;
-    for ( const auto & cached : cachemap )
+    return _pimpl->createProcInfo( cachemap );
+  }
+
+  CheckAccessDeleted::size_type CheckAccessDeleted::Impl::createProcInfo(const std::map<pid_t,CacheEntry> &in)
+  {
+    std::ofstream debugFileOut;
+    bool debugEnabled = false;
+    if ( !_debugFile.empty() ) {
+      debugFileOut.open( _debugFile.c_str() );
+      debugEnabled =  debugFileOut.is_open();
+
+      if ( !debugEnabled ) {
+        ERR<<"Unable to open debug file: "<<_debugFile<<endl;
+      }
+    }
+
+    _data.clear();
+    for ( const auto &cached : in )
     {
-      addDataIf( data, cached.second );
+      if (!debugEnabled)
+        addDataIf( cached.second);
+      else {
+        std::vector<std::string> *mapPtr = nullptr;
+
+        auto dbgInfo = debugMap.find(cached.first);
+        if ( dbgInfo != debugMap.end() )
+          mapPtr = &(dbgInfo->second);
+
+        if( !addDataIf( cached.second, mapPtr ) )
+          continue;
+
+        for ( const std::string &dbgLine: dbgInfo->second ) {
+          debugFileOut.write( dbgLine.c_str(), dbgLine.length() );
+        }
+      }
     }
-    _data.swap( data );
     return _data.size();
   }
 
+  bool CheckAccessDeleted::empty() const
+  {
+    return _pimpl->_data.empty();
+  }
+
+  CheckAccessDeleted::size_type CheckAccessDeleted::size() const
+  {
+    return _pimpl->_data.size();
+  }
+
+  CheckAccessDeleted::const_iterator CheckAccessDeleted::begin() const
+  {
+    return _pimpl->_data.begin();
+  }
+
+  CheckAccessDeleted::const_iterator CheckAccessDeleted::end() const
+  {
+    return _pimpl->_data.end();
+  }
+
+  void CheckAccessDeleted::setDebugOutputFile(const Pathname &filename_r)
+  {
+    _pimpl->_debugFile = filename_r;
+  }
+
   std::string CheckAccessDeleted::findService( pid_t pid_r )
   {
     ProcInfo p;
@@ -301,13 +406,6 @@ namespace zypp
     return p.service();
   }
 
-  ///////////////////////////////////////////////////////////////////
-  namespace
-  { /////////////////////////////////////////////////////////////////
-    /////////////////////////////////////////////////////////////////
-  } // namespace
-  ///////////////////////////////////////////////////////////////////
-
   std::string CheckAccessDeleted::ProcInfo::service() const
   {
     static const str::regex rx( "[0-9]+:name=systemd:/system.slice/(.*/)?(.*).service$" );
index a3ebdcb..41694ad 100644 (file)
@@ -15,6 +15,8 @@
 #include <iosfwd>
 #include <vector>
 #include <string>
+#include <zypp/Pathname.h>
+#include <zypp/base/PtrTypes.h>
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
@@ -30,11 +32,17 @@ namespace zypp
    * information about running processes which access deleted files
    * or libraries is collected and provided as a \ref ProcInfo
    * container.
+   *
+   * Provides support for reproducing check results from a foreign system by
+   * creating a debug output file containing all required information,
+   * enabled by \ref setDebugOutputFile.\n
+   * This data file can be used as datasource when passed to \ref check(const Pathname &, bool).
    */
   class CheckAccessDeleted
   {
 
     public:
+      class Impl;
       /**
        * Data about one running process accessing deleted files.
        */
@@ -64,8 +72,7 @@ namespace zypp
        * \throws Exception if \ref check throws.
        * \see \ref check.
        */
-      CheckAccessDeleted( bool doCheck_r = true )
-      { if ( doCheck_r ) check(); }
+      CheckAccessDeleted( bool doCheck_r = true );
 
     public:
       /** Check for running processes which access deleted executables or libraries.
@@ -82,10 +89,25 @@ namespace zypp
        */
       size_type check( bool verbose_r = false );
 
-      bool empty() const               { return _data.empty(); }
-      size_type size() const           { return _data.size(); }
-      const_iterator begin() const     { return _data.begin(); }
-      const_iterator end() const       { return _data.end(); }
+      /**
+       * \overload
+       * Performs the same checks but instead of investigating the current system it
+       * uses information from \a lsofOutput_r to support debugging.
+       *
+       * \sa setDebugOutputFile
+       */
+      size_type check( const Pathname &lsofOutput_r, bool verbose_r = false );
+
+      bool empty() const;
+      size_type size() const;
+      const_iterator begin() const;
+      const_iterator end() const;
+
+      /**
+       * Writes all filtered process entries that make it into the final set into
+       * a file specified by \a filename_r.
+       */
+      void setDebugOutputFile (const Pathname &filename_r);
 
     public:
       /** Guess if pid was started by a systemd service script.
@@ -93,9 +115,8 @@ namespace zypp
        * \warning This is just a guess.
        */
       static std::string findService( pid_t pid_r );
-
-    private:
-      std::vector<ProcInfo> _data;
+  private:
+      RWCOW_pointer<Impl> _pimpl;
   };
   ///////////////////////////////////////////////////////////////////