Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package duply for openSUSE:Factory checked 
in at 2022-07-06 15:42:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/duply (Old)
 and      /work/SRC/openSUSE:Factory/.duply.new.1548 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "duply"

Wed Jul  6 15:42:29 2022 rev:23 rq:987165 version:2.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/duply/duply.changes      2020-09-06 
00:03:28.983286309 +0200
+++ /work/SRC/openSUSE:Factory/.duply.new.1548/duply.changes    2022-07-06 
15:42:41.150577547 +0200
@@ -1,0 +2,22 @@
+Wed Jul  6 06:28:27 UTC 2022 - hs...@mail.de
+
+- Update to 2.4:
+  - bugfix #127: date_from_nsecs ignores format string
+  - bugfix #116: separators print date now too
+  - featreq #48: add purgeAuto command (see man page)
+  - replaced tab indents with 2spaces everywhere
+  - bugfix #129,131,132: duply stumbles over 'python -s' shebang,
+    python interpreter parse failed if duplicity is a snap app
+  - bugfix #130: duplicity version check failed "gpg: WARNING: ..."
+  - version output, always print PYTHONPATH, if interpreter was determined
+  - update python references to python3
+- Changes from 2.3.1:
+  - bugfix 123: symmetric encryption errs out, asks for '' private key
+- Changes from 2.3:
+  - don't import whole key pair anymore if only pub/sec is requested
+  - gpg import routine informs on missing key files in profile now
+  - add check/import needed secret key for decryption
+  - featreq 50: Disable GPG key backups, implemented/added settings
+    GPG_IMPORT/GPG_EXPORT='disabled' to conf template
+
+-------------------------------------------------------------------

Old:
----
  duply_2.2.2.tgz

New:
----
  duply_2.4.tgz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ duply.spec ++++++
--- /var/tmp/diff_new_pack.jCi7D9/_old  2022-07-06 15:42:41.510578064 +0200
+++ /var/tmp/diff_new_pack.jCi7D9/_new  2022-07-06 15:42:41.514578070 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           duply
-Version:        2.2.2
+Version:        2.4
 Release:        0
 Summary:        A frontend for the "duplicity" backup program
 License:        GPL-2.0-only

++++++ duply_2.2.2.tgz -> duply_2.4.tgz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duply_2.2.2/CHANGELOG.txt new/duply_2.4/CHANGELOG.txt
--- old/duply_2.2.2/CHANGELOG.txt       2020-02-24 12:53:51.000000000 +0100
+++ new/duply_2.4/CHANGELOG.txt 2022-04-06 17:16:12.000000000 +0200
@@ -13,10 +13,31 @@
                             --old-filenames
 - add 'exclude_<command>' list usage e.g. exclude_verify
 - featreq 25: a download/install duplicity option
-- hint on install software if a piece is missing
 - import/export profile from/to .tgz function !!!
+- remove url_encode, test for invalid chars n throw error instead
 
 CHANGELOG:
+2.4 (6.4.2022)
+- bugfix #127: date_from_nsecs ignores format string
+- bugfix #116: separators print date now too
+- featreq #48: add purgeAuto command (see man page)
+- replaced tab indents with 2spaces everywhere
+- bugfix #129,131,132: duply stumbles over 'python -s' shebang,
+    python interpreter parse failed if duplicity is a snap app
+- bugfix #130: duplicity version check failed "gpg: WARNING: ..."
+- version output, always print PYTHONPATH, if interpreter was determined
+- update python references to python3
+
+2.3.1 (11.2.2021)
+- bugfix 123: symmetric encryption errs out, asks for '' private key
+
+2.3 (30.12.2020)
+- don't import whole key pair anymore if only pub/sec is requested
+- gpg import routine informs on missing key files in profile now
+- add check/import needed secret key for decryption
+- featreq 50: Disable GPG key backups, implemented/added settings
+    GPG_IMPORT/GPG_EXPORT='disabled' to conf template
+
 2.2.2 (24.02.2020)
 - bugfix 120: Failures in "Autoset trust of key" during restore 
   because of gpg2.2 fingerprint output change
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duply_2.2.2/duply new/duply_2.4/duply
--- old/duply_2.2.2/duply       2020-02-24 12:53:51.000000000 +0100
+++ new/duply_2.4/duply 2022-04-06 17:16:12.000000000 +0200
@@ -9,7 +9,7 @@
 #  changed from ftplicity to duply.                                            
#
 #  See http://duply.net or http://ftplicity.sourceforge.net/ for more info.    
#
 #  (c) 2006 Christiane Ruetten, Heise Zeitschriften Verlag, Germany            
#
-#  (c) 2008-2019 Edgar Soldin (changes since version 1.3)                      
#
+#  (c) 2008-2022 Edgar Soldin (changes since version 1.3)                      
#
 
################################################################################
 #  LICENSE:                                                                    
#
 #  This program is licensed under GPLv2.                                       
#
@@ -29,10 +29,31 @@
 #                              --old-filenames
 #  - add 'exclude_<command>' list usage e.g. exclude_verify
 #  - featreq 25: a download/install duplicity option
-#  - hint on install software if a piece is missing
 #  - import/export profile from/to .tgz function !!!
+#  - remove url_encode, test for invalid chars n throw error instead
 #
 #  CHANGELOG:
+#  2.4 (6.4.2022)
+#  - bugfix #127: date_from_nsecs ignores format string
+#  - bugfix #116: separators print date now too
+#  - featreq #48: add purgeAuto command (see man page)
+#  - replaced tab indents with 2spaces everywhere
+#  - bugfix #129,131,132: duply stumbles over 'python -s' shebang,
+#      python interpreter parse failed if duplicity is a snap app
+#  - bugfix #130: duplicity version check failed "gpg: WARNING: ..."
+#  - version output, always print PYTHONPATH, if interpreter was determined
+#  - update python references to python3
+#
+#  2.3.1 (11.2.2021)
+#  - bugfix 123: symmetric encryption errs out, asks for '' private key
+#
+#  2.3 (30.12.2020)
+#  - don't import whole key pair anymore if only pub/sec is requested
+#  - gpg import routine informs on missing key files in profile now
+#  - add check/import needed secret key for decryption
+#  - featreq 50: Disable GPG key backups, implemented/added settings
+#      GPG_IMPORT/GPG_EXPORT='disabled' to conf template
+#
 #  2.2.2 (24.02.2020)
 #  - bugfix 120: Failures in "Autoset trust of key" during restore 
 #    because of gpg2.2 fingerprint output change
@@ -501,27 +522,13 @@
   ( [ "${bin##*/}" == "$bin" ] && hash "$bin" 2>/dev/null ) || [ -x "$bin" ]
 }
 
-# the python binary to use, exit code 0 when configured, else 1
-function python_binary {
-  # if unset, parse from duplicity shebang
-  if ! var_isset 'PYTHON'; then
-    duplicity_python_binary_parse;
-    echo $DUPL_PYTHON_BIN;
-    return 1;
-  else
-    # tell if PYTHON was configured manually
-    echo $PYTHON;
-    return 0
-  fi
-}
-
 # important definitions #######################################################
 
 ME_LONG="$0"
 ME="$(basename $0)"
 ME_NAME="${ME%%.*}"
-ME_VERSION="2.2.2"
-ME_WEBSITE="http://duply.net";
+ME_VERSION="2.4"
+ME_WEBSITE="https://duply.net";
 
 # default config values
 DEFAULT_SOURCE='/path/of/source'
@@ -531,7 +538,6 @@
 DEFAULT_GPG='gpg'
 DEFAULT_GPG_KEY='_KEY_ID_'
 DEFAULT_GPG_PW='_GPG_PASSWORD_'
-DEFAULT_PYTHON='python2'
 
 # function definitions ##########################
 
@@ -593,19 +599,35 @@
 
 function using_info {
   # init needed vars into global name space
-  lookup duplicity && { duplicity_python_binary_parse; duplicity_version_get; }
+  lookup duplicity && { duplicity_version_get; }
   local NOTFOUND="INVALID"
-  local AWK_VERSION GREP_VERSION PYTHON_RUNNER
+  local AWK_VERSION GREP_VERSION PYTHON_RUNNER \
+    PYTHON_RUNNER_RESOLVED PYTHON_VERSION PYTHON_PATH
   # freebsd awk (--version only), debian mawk (-W version only), deliver '' so 
awk does not wait for input
   AWK_VERSION=$( lookup awk && (awk --version 2>/dev/null || awk -W version 
2>&1) | awk 'NR<=2&&tolower($0)~/(busybox|awk)/{success=1;print;exit} 
END{if(success<1) print "unknown"}' || echo "$NOTFOUND" )
   GREP_VERSION=$( lookup grep && grep --version 2>&1 | awk 
'NR<=2&&tolower($0)~/(busybox|grep.*[0-9]+\.[0-9]+)/{success=1;print;exit} 
END{if(success<1) print "unknown"}' || echo "$NOTFOUND" )
-  PYTHON_RUNNER=$(python_binary)
-  local PYTHON_VERSION=$(lookup "$PYTHON_RUNNER" && "$PYTHON_RUNNER" -V 2>&1| 
awk '{print tolower($0);exit}' || echo "'$PYTHON_RUNNER' $NOTFOUND" )
+
+  if [ -n "$PYTHON" ]; then
+    PYTHON_RUNNER=$PYTHON
+  else
+    PYTHON_RUNNER="$(duplicity_python_binary_parse)"
+  fi
+  # fetch version and resolve python
+  [ -n "$PYTHON_RUNNER" ] && {
+    PYTHON_VERSION=$($PYTHON_RUNNER -V 2>&1| awk '{print tolower($0);exit}' || 
echo "'$PYTHON_RUNNER' $NOTFOUND" )
+    local PYTHON_RUNNER_ARRAY=( $PYTHON_RUNNER )
+    PYTHON_RUNNER_RESOLVED="$(which ${PYTHON_RUNNER_ARRAY[0]})"
+    # readd params if there were
+    [ ${#PYTHON_RUNNER_ARRAY[@]} -gt 1 ] && \
+      PYTHON_RUNNER_RESOLVED="${PYTHON_RUNNER_RESOLVED} 
${PYTHON_RUNNER_ARRAY[@]:1}"
+      PYTHON_PATH="$($PYTHON_RUNNER -c "import 
sys;print(':'.join(sys.path));")"
+  }
+
   local GPG_INFO=$(gpg_avail && gpg --version 2>&1| awk 
'/^gpg.*[0-9\.]+$/&&length(v)<1{v=$1" "$3}/^Home:/{h=" ("$0")"}END{print v""h}' 
|| echo "gpg $NOTFOUND")
   local BASH_VERSION=$(bash --version | awk 'NR==1{IGNORECASE=1;sub(/GNU bash, 
version[ ]+/,"",$0);print $0}')
   # print out
   echo -e "Using installed duplicity version ${DUPL_VERSION:-$NOTFOUND}\
-${PYTHON_VERSION+, $PYTHON_VERSION ${PYTHON_RUNNER:+($(which 
"$PYTHON_RUNNER"))}${PYTHONPATH:+ 'PYTHONPATH=$PYTHONPATH'}}\
+${PYTHON_VERSION+, $PYTHON_VERSION 
${PYTHON_RUNNER:+($PYTHON_RUNNER_RESOLVED)}${PYTHON_PATH:+ 
'PYTHONPATH=$PYTHON_PATH'}}\
 ${GPG_INFO:+, $GPG_INFO}${AWK_VERSION:+, awk 
'${AWK_VERSION}'}${GREP_VERSION:+, grep '${GREP_VERSION}'}\
 ${BASH_VERSION:+, bash '${BASH_VERSION}'}."
 }
@@ -721,6 +743,14 @@
              the number of full backups which associated incrementals will be
              kept, counting in reverse chronological order) 
               [use --force to actually delete these files]
+  purgeAuto [--force]  
+             convenience batch wrapper for all purge commands above.
+             purge, purgeFull, purgeIncr are added if their conf vars were 
set. e.g.
+              MAX_AGE=1Y
+              MAX_FULL_BACKUPS=6
+              MAX_FULLS_WITH_INCR=3
+             in profile conf file would result in
+              [purge_purgeFull_purgeIncr]
   cleanup [--force]  
              list broken backup chain files archives (e.g. after unfinished 
run)
               [use --force to actually delete these files]
@@ -857,6 +887,10 @@
 
 # disable preliminary tests with the following setting
 #GPG_TEST='disabled'
+# disable automatic gpg key importing altogether
+#GPG_IMPORT='disabled'
+# disable automatic gpg key exporting to profile folder
+#GPG_EXPORT='disabled'
 
 # backend, credentials & location of the backup target (URL-Format)
 # generic syntax is
@@ -875,6 +909,7 @@
 #   if you define the credentials as TARGET_USER, TARGET_PASS below $ME
 #   will try to url_encode them for you if the need arises.
 TARGET='${DEFAULT_TARGET}'
+
 # optionally the username/password can be defined as extra variables
 # setting them here _and_ in TARGET results in an error
 # ATTENTION:
@@ -905,9 +940,8 @@
 #  "trickle -s -u 640 -d 5120" # 5Mb up, 40Mb down"
 #DUPL_PRECMD=""
 
-# override the used python interpreter, defaults to
-#  - parsed result of duplicity's shebang or 'python2'
-#   e.g. "python2" or "/usr/bin/python2.7"
+# override the python interpreter to execute duplicity, unset by default
+#  e.g. "python3" or "/usr/bin/python3.8"
 #PYTHON="python"
 
 # exclude folders containing exclusion file (since duplicity 0.5.14)
@@ -1080,8 +1114,8 @@
   Don't forget the used _password_ as you will need it.
   When done enter the 8 digit id & the password in the profile conf file.
 
-  The key id can be found doing a 'gpg --list-keys'. In the  example output 
-  below the key id would be FFFFFFFF for the public key.
+  The key id can be found doing a 'gpg --list-keys'. In the example output 
+  below the key id for the public key would be FFFFFFFF.
 
   pub   1024D/FFFFFFFF 2007-12-17
   uid                  duplicity
@@ -1089,16 +1123,6 @@
 "
 }
 
-function error_gpg_key {
-  local KEY_ID="$1"
-  local KIND="$2"
-  error_gpg "${KIND} gpg key '${KEY_ID}' cannot be found." \
-"Doublecheck if the above key is listed by 'gpg --list-keys' or available 
-  as gpg key file '$(basename "$(gpg_keyfile "${KEY_ID}")")' in the profile 
folder.
-  If not you can put it there and $ME_NAME will autoimport it on the next run.
-  Alternatively import it manually as the user you plan to run $ME_NAME with."
-}
-
 function error_gpg_test {
   [ -n "$2" ] && local hint="\n  $2\n\n  "
 
@@ -1121,20 +1145,20 @@
 }
 
 function error_to_string {
-       [ -n "$1" ] && [ "$1" -eq 0 ] && echo "OK" || echo "FAILED 'code $1'"
+  [ -n "$1" ] && [ "$1" -eq 0 ] && echo "OK" || echo "FAILED 'code $1'"
 }
 
 function duplicity_version_get {
-  # nothing to do, just print
+  # use cached value, just print
   var_isset DUPL_VERSION && return
-  
+
   local DUPL_VERSION_OUT DUPL_VERSION_AWK PYTHON_BIN CMD='duplicity'
   # only run with a user specific python if configured (running by default
   # breaks homebrew as they place a shell wrapper for duplicity in path)
-  PYTHON_BIN="$(python_binary)" &&\
-    CMD="$(qw "$PYTHON_BIN") $(which $CMD)"
-  CMD="$CMD --version 2>&1"
-  DUPL_VERSION_OUT=$(eval "$CMD")
+  [ -n "$PYTHON" ] &&\
+    CMD="$PYTHON $(qw "$(which duplicity)")"
+
+  DUPL_VERSION_OUT=$($CMD --version)
   DUPL_VERSION=`echo $DUPL_VERSION_OUT | awk '/^duplicity /{print $2; exit;}'`
   #DUPL_VERSION='0.7.03' #'0.6.08b' #,0.4.4.RC4,0.6.08b
   DUPL_VERSION_VALUE=0
@@ -1176,23 +1200,40 @@
 
 # parse interpreter from duplicity shebang
 function duplicity_python_binary_parse {
-  # cached result
-  ( var_isset 'PYTHON' || var_isset 'DUPL_PYTHON_BIN' ) && return
+  # reuse cached result
+  var_isset 'DUPL_PYTHON_BIN' && {
+    [ -n "$DUPL_PYTHON_BIN" ] && {
+      echo $DUPL_PYTHON_BIN
+      return
+    } || return 1
+  }
 
-  # parse it or warn
   local DUPL_BIN=$(which duplicity)
+  # test for shebang
+  IFS= LC_ALL=C read -rN2 shebang < "$DUPL_BIN" && [ "$shebang" != '#!' ] && {
+    DUPL_PYTHON_BIN=""
+    return 1
+  }
+
+  # parse it or warn
   DUPL_PYTHON_BIN=$(awk 'NR==1&&/^#!/{sub(/^#!( *\/usr\/bin\/env *)?/,""); 
print}' < "$DUPL_BIN")
   if ! echo "$DUPL_PYTHON_BIN" | grep -q -i 'python'; then
-    warning "Could not parse the python interpreter used from duplicity 
($DUPL_BIN). Result was '$DUPL_PYTHON_BIN'.
-Will assume it is '$DEFAULT_PYTHON'."
-    DUPL_PYTHON_BIN="$DEFAULT_PYTHON"
+    warning "Could not parse the python interpreter used from duplicity 
($DUPL_BIN). Result was 
+'$DUPL_PYTHON_BIN'.
+"
+    DUPL_PYTHON_BIN=""
+    return 1
   fi
+  
+  # success
+  echo $DUPL_PYTHON_BIN
+  return
 }
 
 function run_script { # run pre/post scripts
   local ERR=0
   local SCRIPT="$1"
-  if [ ! -z "$PREVIEW" ] ; then        
+  if [ ! -z "$PREVIEW" ] ; then
     echo "$([ ! -x "$SCRIPT" ] && echo ". ")$SCRIPT"
   elif [ -r "$SCRIPT" ] ; then 
     echo -n "Running '$SCRIPT' "
@@ -1348,94 +1389,93 @@
   # init global duplicity parameters same for all tasks
   duplicity_params_global
 
-  local RUN=eval BIN=duplicity DUPL_BIN PYTHON_BIN
+  local RUN=eval CMD=duplicity
   # run in cmd line preview mode if requested
   var_isset 'PREVIEW' && RUN=echo
-  # try to resolve duplicity path for usage with python interpreter
-  DUPL_BIN=$(which "$BIN") || DUPL_BIN="$BIN"
   # only run with a user specific python if configured (running by default
   # breaks homebrew as they place a shell wrapper for duplicity in path)
-  PYTHON_BIN="$(python_binary)" &&\
-    BIN="$(qw "$PYTHON_BIN") $(qw "$DUPL_BIN")"
+  # resolve duplicity path for usage with python interpreter
+  [ -n "$PYTHON" ] &&\
+    CMD="$PYTHON $(qw "$(which duplicity)")"
 
 $RUN "${DUPL_VARS_GLOBAL} ${BACKEND_PARAMS}\
- ${DUPL_PRECMD} $BIN $DUPL_CMD $DUPL_PARAMS_GLOBAL $(duplicity_params_conf)\
- $GPG_USEAGENT $(gpg_custom_binary) $DUPL_CMD_PARAMS"
+  ${DUPL_PRECMD} $CMD $DUPL_CMD $DUPL_PARAMS_GLOBAL $(duplicity_params_conf)\
+  $GPG_USEAGENT $(gpg_custom_binary) $DUPL_CMD_PARAMS"
 
   local ERR=$?
   return $ERR
 }
 
 function secureconf { # secure the configuration dir
-       #PERMS=$(ls -la $(dirname $CONFDIR) | grep -e " $(basename $CONFDIR)\$" 
| awk '{print $1}')
-       local PERMS="$(ls -la "$CONFDIR/." | awk 'NR==2{print $1}')"
-       if [ "${PERMS/#drwx------*/OK}" != 'OK' ] ; then
-               chmod u+rwX,go= "$CONFDIR"; local ERR=$?
-               warning "The profile's folder 
+  #PERMS=$(ls -la $(dirname $CONFDIR) | grep -e " $(basename $CONFDIR)\$" | 
awk '{print $1}')
+  local PERMS="$(ls -la "$CONFDIR/." | awk 'NR==2{print $1}')"
+  if [ "${PERMS/#drwx------*/OK}" != 'OK' ] ; then
+    chmod u+rwX,go= "$CONFDIR"; local ERR=$?
+    warning "The profile's folder 
 '$CONFDIR'
 permissions are not safe ($PERMS). Secure them now. - ($(error_to_string 
$ERR))"
-       fi
+  fi
 }
 
 # params are $1=timeformatstring (default like date output), $2=epoch seconds 
since 1.1.1970 (default now)
 function date_fix {
-       local DEFAULTFORMAT='%a %b %d %H:%M:%S %Z %Y'
-       local date
-       #[ "$1" == "%N" ] && return #test the no nsec test below
-       # gnu date with -d @epoch
-       date=$(date ${2:+-d @$2} ${1:++"$1"} 2> /dev/null) && \
-               echo $date && return
-       # date bsd,osx with -r epoch
-       date=$(date ${2:+-r $2} ${1:++"$1"} 2> /dev/null) && \
-               echo $date && return    
-       # date busybox with -d epoch -D %s
-       date=$(date ${2:+-d $2 -D %s} ${1:++"$1"} 2> /dev/null) && \
-               echo $date && return
-       ## some date commands do not support giving a time w/o setting it 
systemwide (irix,solaris,others?)
-       # python fallback
-       date=$("$(python_binary)" -c "import time;print 
time.strftime('${1:-$DEFAULTFORMAT}',time.localtime(${2}))" 2> /dev/null) && \
-               echo $date && return
-       # awk fallback
-       date=$(awk "BEGIN{print strftime(\"${1:-$DEFAULTFORMAT}\"${2:+,$2})}" 
2> /dev/null) && \
-               echo $date && return
-       # perl fallback
-       date=$(perl  -e "use POSIX qw(strftime);\$date = 
strftime(\"${1:-$DEFAULTFORMAT}\",localtime(${2}));print \"\$date\n\";" 2> 
/dev/null) && \
-               echo $date && return
-       # error
-       echo "ERROR"
-       return 1
+  local DEFAULTFORMAT='%a %b %d %H:%M:%S %Z %Y'
+  local date
+  #[ "$1" == "%N" ] && return #test the no nsec test below
+  # gnu date with -d @epoch
+  date=$(date ${2:+-d @$2} ${1:++"$1"} 2> /dev/null) && \
+    echo $date && return
+  # date bsd,osx with -r epoch
+  date=$(date ${2:+-r $2} ${1:++"$1"} 2> /dev/null) && \
+    echo $date && return
+  # date busybox with -d epoch -D %s
+  date=$(date ${2:+-d $2 -D %s} ${1:++"$1"} 2> /dev/null) && \
+    echo $date && return
+  ## some date commands do not support giving a time w/o setting it systemwide 
(irix,solaris,others?)
+  # python fallback
+  #date=$("$(python_binary)" -c "import time;print 
time.strftime('${1:-$DEFAULTFORMAT}',time.localtime(${2}))" 2> /dev/null) && \
+  #  echo $date && return
+  # awk fallback
+  date=$(awk "BEGIN{print strftime(\"${1:-$DEFAULTFORMAT}\"${2:+,$2})}" 2> 
/dev/null) && \
+    echo $date && return
+  # perl fallback
+  date=$(perl  -e "use POSIX qw(strftime);\$date = 
strftime(\"${1:-$DEFAULTFORMAT}\",localtime(${2}));print \"\$date\n\";" 2> 
/dev/null) && \
+    echo $date && return
+  # error
+  echo "ERROR"
+  return 1
 }
 
 function nsecs {
-       local NSECS
-       # test if date supports nanosecond output
-       if ! var_isset NSECS_DISABLED; then
-               NSECS=$(date_fix %N 2> /dev/null | head -1 |grep -e 
"^[[:digit:]]\{9\}$")
-               [ -n "$NSECS" ] && NSECS_DISABLED=0 || NSECS_DISABLED=1
-       fi
-
-       # add 9 digits, not all date(s) deliver nsecs e.g. busybox date
-       if [ "$NSECS_DISABLED" == "1" ]; then
-               date_fix %s000000000
-       else
-               date_fix %s%N
-       fi
+  local NSECS
+  # test if date supports nanosecond output
+  if ! var_isset NSECS_DISABLED; then
+    NSECS=$(date_fix %N 2> /dev/null | head -1 |grep -e "^[[:digit:]]\{9\}$")
+    [ -n "$NSECS" ] && NSECS_DISABLED=0 || NSECS_DISABLED=1
+  fi
+
+  # add 9 digits, not all date(s) deliver nsecs e.g. busybox date
+  if [ "$NSECS_DISABLED" == "1" ]; then
+    date_fix %s000000000
+  else
+    date_fix %s%N
+  fi
 }
 
 function nsecs_to_sec {
-       echo $(($1/1000000000)).$(printf "%03d" $(($1/1000000%1000)) )
+  echo $(($1/1000000000)).$(printf "%03d" $(($1/1000000%1000)) )
 }
 
 function datefull_from_nsecs {
-       date_from_nsecs $1 '%F %T'
+  date_from_nsecs $1 '%F %T'
 }
 
 function date_from_nsecs {
-       local FORMAT=${2:-%T}
-       local TIME=$(nsecs_to_sec $1)
-       local SECS=${TIME%.*}
-       local DATE=$(date_fix "%T" ${SECS:-0})
-       echo $DATE.${TIME#*.}
+  local FORMAT=${2:-%T}
+  local TIME=$(nsecs_to_sec $1)
+  local SECS=${TIME%.*}
+  local DATE=$(date_fix "${FORMAT}" ${SECS:-0})
+  echo $DATE.${TIME#*.}
 }
 
 function var_isset {
@@ -1464,7 +1504,10 @@
 
 function url_encode {
   # utilize python, silently do nothing on error - because no python no 
duplicity
-  OUT=$("$(python_binary)" -c "
+  local PYTHON_RUNNER
+  PYTHON_RUNNER="$(duplicity_python_binary_parse)" ||\
+  PYTHON_RUNNER="python" &&\
+    OUT=$($PYTHON_RUNNER -c "
 try: import urllib.request as urllib
 except ImportError: import urllib
 print(urllib.${2}quote('$1'));
@@ -1525,10 +1568,18 @@
   echo $OUT
 }
 
+function gpg_testing {
+  [ "$GPG_TEST" != "disabled" ]
+}
+
 function gpg_signing {
   echo ${GPG_KEY_SIGN} | grep -v -q -e '^disabled$'
 }
 
+function gpg_keytype {
+  echo "$1" | awk '/^PUB$/{print "public"}/^SEC$/{print "secret"}'
+}
+
 # parameter key id, key_type
 function gpg_keyfile {
   local GPG_KEY=$(gpg_key_legalize $1) TYPE="$2"
@@ -1539,10 +1590,15 @@
 # parameter key id
 function gpg_import {
   local i FILE FOUND=0 KEY_ID="$1" KEY_TYPE="$2" KEY_FP="" ERR=0
+  [ "$GPG_IMPORT" = "disabled" ] && {
+    echo "Skipping import of needed $(gpg_keytype "$KEY_TYPE") key '$KEY_ID'. 
(GPG_IMPORT='disabled')"
+    return
+  }
+
   # create a list of legacy key file names and current naming scheme
   # we always import pub and sec if they are avail in conf folder
   local KEYFILES=( "$CONFDIR/gpgkey" $(gpg_keyfile "$KEY_ID") \
-                   $(gpg_keyfile "$KEY_ID" PUB) $(gpg_keyfile "$KEY_ID" SEC))
+                   $(gpg_keyfile "$KEY_ID" "$KEY_TYPE") )
 
   # Try autoimport from existing old gpgkey files 
   # and new gpgkey.XXX.asc files (since v1.4.2)
@@ -1564,7 +1620,8 @@
   done
 
   if [ "$FOUND" -eq 0 ]; then
-    warning "No keyfile for '$KEY_ID' found in profile\n'$CONFDIR'."
+    echo "Notice: No keyfile for '$KEY_ID' found in profile folder."
+    return 1
   fi
 
   # try to set trust automagically
@@ -1592,6 +1649,11 @@
 }
 
 function gpg_export_if_needed {
+  [ "$GPG_EXPORT" = 'disabled' ] && { \
+    echo "Skipping export of gpg keys. (GPG_EXPORT='disabled')"
+    return
+  }
+
   local SUCCESS FILE KEY_TYPE
   local TMPFILE="$TEMP_DIR/${ME_NAME}.$$.$(date_fix %s).gpgexp"
   for KEY_ID in "$@"; do
@@ -1599,8 +1661,9 @@
     for KEY_TYPE in PUB SEC; do
       FILE="$(gpg_keyfile "$KEY_ID" $KEY_TYPE)"
       if [ ! -f "$FILE" ] && eval gpg_$(tolower $KEY_TYPE)_avail \"$KEY_ID\"; 
then
+
         # exporting
-        CMD_MSG="Backup $KEY_TYPE key '$KEY_ID' to profile."
+        CMD_MSG="Backup $(gpg_keytype "$KEY_TYPE") key '$KEY_ID' to profile."
         # gpg2.1 insists on passphrase here, gpg2.0- happily exports w/o it
         # we pipe an empty string when GPG_PW is not set to avoid gpg silently 
waiting for input
         run_cmd $(gpg_pass_pipein GPG_PW_SIGN GPG_PW) gpg $GPG_OPTS 
$GPG_USEAGENT $(gpg_param_passwd GPG_PW_SIGN GPG_PW) --armor --export"$(test 
"SEC" = "$KEY_TYPE" && echo -secret-keys)" $(qw "$KEY_ID") '>>' $(qw "$TMPFILE")
@@ -1695,6 +1758,9 @@
 
 # return success if at least one secret key is available
 function gpg_key_decryptable {
+  # no keys, no problem
+  gpg_symmetric && return 0
+
   local KEY_ID
   for KEY_ID in "${GPG_KEYS_ENC_ARRAY[@]}"; do
     gpg_sec_avail "$KEY_ID" && return 0
@@ -1715,7 +1781,7 @@
   fi
 }
 
-# select the earlist defined and create an "echo <value> |" string
+# select the earliest defined and create an "echo <value> |" string
 function gpg_pass_pipein {
   var_isset GPG_USEAGENT && exit 1
   
@@ -1857,11 +1923,11 @@
 # is duplicity avail
 lookup duplicity || error_path "duplicity missing. installed und available in 
path?"
 # init, exec duplicity version check info
-duplicity_python_binary_parse
 duplicity_version_get
 
 # check for certain important helper programs
-for f in awk grep "$(python_binary)"; do
+# TODO: we should probably check for duplicity and $PYTHON (if set) here too
+for f in awk grep ; do
   lookup "$f" || \
     error_path "$f missing. installed und available in path?"
 done
@@ -2059,18 +2125,50 @@
 Tip2: Use gpg-agent."
 fi
 
-# check gpg encr public keys availability
+# test - GPG KEY AVAILABILITY 
##################################################
+
+# check gpg public keys availability, try import if needed
 for (( i = 0 ; i < ${#GPG_KEYS_ENC_ARRAY[@]} ; i++ )); do
   KEY_ID="${GPG_KEYS_ENC_ARRAY[$i]}"
   # test availability, try to import, retest
   if ! gpg_pub_avail "${KEY_ID}"; then
-    echo "Encryption public key '${KEY_ID}' not found."
+    echo "Encryption public key '${KEY_ID}' not in keychain. Try to import 
from profile."
     gpg_import "${KEY_ID}" PUB
     gpg_key_cache RESET "${KEY_ID}"
-    gpg_pub_avail "${KEY_ID}" || error_gpg_key "${KEY_ID}" "Public"
+    gpg_pub_avail "${KEY_ID}" || { \
+      gpg_testing && error_gpg \
+      "Needed public gpg key '${KEY_ID}' is not available in keychain." \
+      "Doublecheck if the above key is listed by 'gpg --list-keys' or available
+  as gpg key file '$(basename "$(gpg_keyfile "${KEY_ID}")")' in the profile 
folder.
+  If not you can put it there and $ME_NAME will autoimport it on the next run.
+  Alternatively import it manually as the user you plan to run $ME_NAME with."
+    }
+  else
+    echo "Public key '${KEY_ID}' found in keychain."
   fi
 done
 
+# check gpg encr secret encryption keys availability and fail
+# if none is available after a round of importing trials
+gpg_key_decryptable || \
+{
+  echo "Missing secret keys for decryption in keychain."
+  for (( i = 0 ; i < ${#GPG_KEYS_ENC_ARRAY[@]} ; i++ )); do
+    KEY_ID="${GPG_KEYS_ENC_ARRAY[$i]}"
+    # test availability, try to import, retest
+    if ! gpg_sec_avail "${KEY_ID}"; then
+    echo "Try to import secret key '${KEY_ID}' from profile."
+      gpg_import "${KEY_ID}" SEC
+      gpg_key_cache RESET "${KEY_ID}"
+    fi
+  done
+  gpg_key_decryptable || \
+  {
+    gpg_testing && error_gpg_test "None of the configured keys '$(join "','" 
"${GPG_KEYS_ENC_ARRAY[@]}")' \
+has a secret key in the keychain. Decryption will be impossible!"
+  }
+}
+
 # gpg secret sign key availability
 # if none set, autoset first encryption key as sign key
 if ! gpg_signing; then
@@ -2086,6 +2184,7 @@
     if gpg_sec_avail "${KEY_ID}"; then
       GPG_KEY_SIGN="${KEY_ID}"
     else
+      echo "Signing secret key '${KEY_ID}' not found."
       gpg_import "${KEY_ID}" SEC
       gpg_key_cache RESET "${KEY_ID}"
       if gpg_sec_avail "${KEY_ID}"; then
@@ -2157,15 +2256,15 @@
 # test - GPG SANITY 
#####################################################################
 # if encryption is disabled, skip this whole section
 if gpg_disabled; then
-  echo -e "Test - En/Decryption skipped. (GPG disabled)"
-elif [ "$GPG_TEST" = "disabled" ]; then 
-  echo -e "Test - En/Decryption skipped. (Testing disabled)"
+  echo -e "Test - En/Decryption skipped. (GPG='disabled')"
+elif ! gpg_testing; then 
+  echo -e "Test - En/Decryption skipped. (GPG_TEST='disabled')"
 else
 
-GPG_TEST="$TEMP_DIR/${ME_NAME}.$$.$(date_fix %s)"
+GPG_TEST_PREFIX="$TEMP_DIR/${ME_NAME}.$$.$(date_fix %s)"
 function cleanup_gpgtest { 
-  echo -en "Cleanup - Delete '${GPG_TEST}_*'"
-  rm "${GPG_TEST}"_* 2>/dev/null && echo "(OK)" || echo "(FAILED)"
+  echo -en "Cleanup - Delete '${GPG_TEST_PREFIX}_*'"
+  rm "${GPG_TEST_PREFIX}"_* 2>/dev/null && echo "(OK)" || echo "(FAILED)"
 }
 
 # signing enabled?
@@ -2182,7 +2281,7 @@
   done
   # check encrypting
   CMD_MSG="Test - Encrypt to '$(join "','" 
"${GPG_KEYS_ENC_ARRAY[@]}")'${CMD_MSG_SIGN:+ & $CMD_MSG_SIGN}"
-  run_cmd $(gpg_pass_pipein GPG_PW_SIGN GPG_PW) gpg $CMD_PARAM_SIGN 
$(gpg_param_passwd GPG_PW_SIGN GPG_PW) $CMD_PARAMS $GPG_USEAGENT --status-fd 1 
$GPG_OPTS -o $(qw "${GPG_TEST}_ENC") -e $(qw "$ME_LONG")
+  run_cmd $(gpg_pass_pipein GPG_PW_SIGN GPG_PW) gpg $CMD_PARAM_SIGN 
$(gpg_param_passwd GPG_PW_SIGN GPG_PW) $CMD_PARAMS $GPG_USEAGENT --status-fd 1 
$GPG_OPTS -o $(qw "${GPG_TEST_PREFIX}_ENC") -e $(qw "$ME_LONG")
   CMD_ERR=$?
 
   if [ "$CMD_ERR" != "0" ]; then 
@@ -2196,7 +2295,7 @@
   # check decrypting
   CMD_MSG="Test - Decrypt"
   gpg_key_decryptable || CMD_DISABLED="No matching secret key available."
-  run_cmd $(gpg_pass_pipein GPG_PW) gpg $(gpg_param_passwd GPG_PW) $GPG_OPTS 
-o $(qw "${GPG_TEST}_DEC") $GPG_USEAGENT -d $(qw "${GPG_TEST}_ENC")
+  run_cmd $(gpg_pass_pipein GPG_PW) gpg $(gpg_param_passwd GPG_PW) $GPG_OPTS 
-o $(qw "${GPG_TEST_PREFIX}_DEC") $GPG_USEAGENT -d $(qw 
"${GPG_TEST_PREFIX}_ENC")
   CMD_ERR=$?
 
   if [ "$CMD_ERR" != "0" ]; then 
@@ -2207,7 +2306,7 @@
 else
   # check encrypting
   CMD_MSG="Test - Encryption with passphrase${CMD_MSG_SIGN:+ & $CMD_MSG_SIGN}"
-  run_cmd $(gpg_pass_pipein GPG_PW) gpg $GPG_OPTS $CMD_PARAM_SIGN 
--passphrase-fd 0 -o $(qw "${GPG_TEST}_ENC") --batch -c $(qw "$ME_LONG")
+  run_cmd $(gpg_pass_pipein GPG_PW) gpg $GPG_OPTS $CMD_PARAM_SIGN 
--passphrase-fd 0 -o $(qw "${GPG_TEST_PREFIX}_ENC") --batch -c $(qw "$ME_LONG")
   CMD_ERR=$?
   if [ "$CMD_ERR" != "0" ]; then 
     error_gpg_test "Encryption failed.${CMD_OUT:+\n$CMD_OUT}"
@@ -2215,7 +2314,7 @@
 
   # check decrypting
   CMD_MSG="Test - Decryption with passphrase"
-  run_cmd $(gpg_pass_pipein GPG_PW) gpg $GPG_OPTS --passphrase-fd 0 -o $(qw 
"${GPG_TEST}_DEC") --batch -d $(qw "${GPG_TEST}_ENC")
+  run_cmd $(gpg_pass_pipein GPG_PW) gpg $GPG_OPTS --passphrase-fd 0 -o $(qw 
"${GPG_TEST_PREFIX}_DEC") --batch -d $(qw "${GPG_TEST_PREFIX}_ENC")
   CMD_ERR=$?
   if [ "$CMD_ERR" != "0" ]; then 
     error_gpg_test "Decryption failed.${CMD_OUT:+\n$CMD_OUT}"
@@ -2224,8 +2323,8 @@
 
 # compare original w/ decryptginal
 CMD_MSG="Test - Compare"
-[ -r "${GPG_TEST}_DEC" ] || CMD_DISABLED="File not found. Nothing to compare."
-run_cmd "test \"\$(cat '$ME_LONG')\" = \"\$(cat '${GPG_TEST}_DEC')\""
+[ -r "${GPG_TEST_PREFIX}_DEC" ] || CMD_DISABLED="File not found. Nothing to 
compare."
+run_cmd "test \"\$(cat '$ME_LONG')\" = \"\$(cat '${GPG_TEST_PREFIX}_DEC')\""
 CMD_ERR=$?
 if [ "$CMD_ERR" = "0" ]; then 
   cleanup_gpgtest
@@ -2315,9 +2414,6 @@
 # since 0.7.03 --exclude-globbing-filelist is deprecated
 EXCLUDE_PARAM="--exclude$(duplicity_version_lt 703 && echo 
-globbing)-filelist" 
 
-# translate backup to batch command 
-cmds=${cmds//backup/groupIn_pre_bkp_post_groupOut}
-
 # replace magic separators to command equivalents 
(+=and,-=or,[=groupIn,]=groupOut)
 cmds=$(awk -v cmds="$cmds" "BEGIN{ \
   gsub(/\+/,\"_and_\",cmds);\
@@ -2325,14 +2421,37 @@
   gsub(/\[/,\"_groupIn_\",cmds);\
   gsub(/\]/,\"_groupOut_\",cmds);\
   print cmds}")
-# convert cmds to array, lowercase for safety
+
+# split commands by '_', preserve spaces even if not allowed :)
+IFS='_' read -ra CMDS_IN <<< "$(tolower $cmds)"
+
+# convert cmds to array, 
+# post process, translate batch commands
+# ATTENTION: commands are lowercase from here on out
 declare -a CMDS
-CMDS=( $(awk "BEGIN{ cmds=tolower(\"$cmds\"); gsub(/_/,\" \",cmds); print cmds 
}") )
+for cmd in "${CMDS_IN[@]}"; do
+  case "$cmd" in
+    # backup -> [pre_bkp_post]
+    'backup')
+      CMDS=("${CMDS[@]}" groupin pre bkp post groupout)
+    ;;
+    # purgeAuto -> [purge purgeFull purgeIncr] depending on set conf vars 
+    'purgeauto')
+      purgeAuto=${MAX_AGE:+ purge}${MAX_FULL_BACKUPS:+ 
purgefull}${MAX_FULLS_WITH_INCRS:+ purgeincr}
+      [[ -z "$purgeAuto" ]] && error "Command 'fullAuto' was given but neither 
of the purge conf vars configured."
+      CMDS=("${CMDS[@]}" groupin $purgeAuto groupout)
+    ;;
+    *)
+      CMDS=("${CMDS[@]}" "$cmd")
+    ;;
+  esac
+done
+#echo $(IFS=',';echo "${CMDS[*]}")
 
 unset FTPL_ERR
 
-# run cmds
-for cmd in ${CMDS[*]};
+# run CMDS
+for cmd in "${CMDS[@]}";
 do
 
 ## init
@@ -2453,7 +2572,7 @@
        RUN_START
 
 # user info
-echo; separator "Start running command $(toupper $cmd) at $(date_from_nsecs 
$RUN_START)"
+echo; separator "Start running command $(toupper $cmd) at 
$(datefull_from_nsecs $RUN_START)"
 
 case "$(tolower $cmd)" in
   'pre'|'post')
@@ -2560,11 +2679,11 @@
 
 # print message on error; set error code
 if [ "$CMD_ERR" -ne 0 ]; then
-       error_print "$(datefull_from_nsecs $RUN_END) Task '$(echo $cmd|awk 
'$0=toupper($0)')' failed with exit code '$CMD_ERR'."
-       FTPL_ERR=1
+  error_print "$(datefull_from_nsecs $RUN_END) Task '$(echo $cmd|awk 
'$0=toupper($0)')' failed with exit code '$CMD_ERR'."
+  FTPL_ERR=1
 fi
 
-separator "Finished state $(error_to_string $CMD_ERR) at $(date_from_nsecs 
$RUN_END) - \
+separator "Finished state $(error_to_string $CMD_ERR) at $(datefull_from_nsecs 
$RUN_END) - \
 Runtime $(printf "%02d:%02d:%02d.%03d" $((RUNTIME/1000000000/60/60)) 
$((RUNTIME/1000000000/60%60)) $((RUNTIME/1000000000%60)) 
$((RUNTIME/1000000%1000)) )"
 
 done

Reply via email to