#!/bin/sh
#
# greenbone-certdata-sync
# This script synchronizes an OpenVAS installation with the
# CERT data from either the Greenbone Security Feed (in
# case a GSF access key is present) or else from the Greenbone
# Community Feed.
#
# Authors:
# Timo Pollmeier <timo.pollmeier@greenbone.net>
#
# Copyright:
# Copyright (C) 2011-2016 Greenbone Networks GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

VERSION=7.0.2

# SETTINGS
# ========

POSTGRES=0
SCHEMA_CERT=""
SCHEMA_SCAP=""
RECURSIVE_TRIGGERS_OFF="PRAGMA recursive_triggers = OFF;"
ATTACH_SCAP=""
if [ "SQLITE3" = "POSTGRESQL" ]; then
  POSTGRES=1
  SCHEMA_CERT="cert."
  SCHEMA_SCAP="scap."
  RECURSIVE_TRIGGERS_OFF=""
fi

# PRIVATE_SUBDIR defines a subdirectory of the CERT data directory
# where files not part of the feed or database will not be deleted by rsync.
if [ -z "$PRIVATE_SUBDIR" ]
then
  PRIVATE_SUBDIR="private"
fi

# RSYNC_DELETE controls whether files which are not part of the repository will
# be removed from the local directory after synchronization. The default value
# for this setting is
# "--delete --exclude cert.db --exclude $PRIVATE_SUBDIR/",
# which means that files which are not part of the feed, database or private
# directory will be deleted.
RSYNC_DELETE="--delete --exclude cert.db --exclude \"$PRIVATE_SUBDIR/\""

# RSYNC_SSH_OPTS contains options which should be passed to ssh for the rsync
# connection to the repository.
RSYNC_SSH_OPTS="-o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\""

# RSYNC_COMPRESS specifies the compression level to use for the rsync connection.
RSYNC_COMPRESS="--compress-level=9"

# PORT controls the outgoing TCP port for updates. If PAT/Port-Translation is
# not used, this should be "24". For some application layer firewalls or gates
# the value 22 (Standard SSH) is useful. Only change if you know what you are
# doing.
PORT=24

# SCRIPT_NAME is the name the scripts will use to identify itself and to mark
# log messages.
SCRIPT_NAME="greenbone-certdata-sync"

# If ENABLED is set to 0, the sync script will not perform a synchronization.
ENABLED=1

# If REFRESH_ONLY is set to 1, the sync script will only update the OpenVAS
# Manager database. This can be controlled via the --refresh parameter.
REFRESH_ONLY=0

# The call with default parameters of sqlite3.
SQLITE3="sqlite3 -noheader -bail"

# PSQL defines the name of the Postgres binary to call, along with additional
# parameters.
PSQL=""
psql_setup () {
  PSQL="psql -v ON_ERROR_STOP=1 -q --pset pager=off --no-align -d $1 -t"
}

# Maximum number of retries if database is locked
if [ -z "$MAX_SQL_RETRIES" ]; then
  MAX_SQL_RETRIES="1" # 0 : try only once
fi

# Delay between retries
if [ -z "$SQL_RETRY_DELAY" ]; then
  SQL_RETRY_DELAY="10m" # allowed unit suffixes: see sleep command
fi

# LOG_CMD defines the command to use for logging. To have logger log to stderr
# as well as syslog, add "-s" here.
LOG_CMD="logger -t $SCRIPT_NAME"

[ -r \"/etc/openvas/greenbone-certdata-sync.conf\" ] && . \"/etc/openvas/greenbone-certdata-sync.conf\"

CERT_DIR="/var/lib/openvas/cert-data"
SCAP_DIR="/var/lib/openvas/scap-data"

CERT_RES_DIR="/usr/share/openvas/cert"

CERT_DB="$CERT_DIR/cert.db"
SCAP_DB="$SCAP_DIR/scap.db"

if [ -z "$DROP_USER" ]; then
  DROP_USER=""
fi

if [ ! $POSTGRES -eq 1 ] ; then
  ATTACH_SCAP="ATTACH DATABASE '$SCAP_DB' AS scap;"
fi

TIMESTAMP="$CERT_DIR/timestamp"

log_debug () {
  $LOG_CMD -p daemon.debug "$1"
}

log_info () {
  $LOG_CMD -p daemon.info "$1"
}

log_notice () {
  $LOG_CMD -p daemon.notice "$1"
}

log_warning () {
  $LOG_CMD -p daemon.warning "$1"
}

log_err () {
  $LOG_CMD -p daemon.err "$1"
}

ACCESSKEY="/etc/openvas/gsf-access-key"

# Note when running as root or restart as $DROP_USER if defined
if [ $(id -u) -eq 0 ]
then
  if [ -z "$DROP_USER" ]
  then
    log_notice "Running as root"
  else
    log_notice "Started as root, restarting as $DROP_USER"
    su --shell /bin/sh --command "$0 $*" "$DROP_USER"
    exit $?
  fi
fi

# Determine whether a GSF access key is present. If yes,
# then use the Greenbone Security Feed. Else use the
# Greenbone Community Feed.
if [ -e $ACCESSKEY ]
then
  RESTRICTED=1

  if [ -z "$FEED_NAME" ] ; then
    FEED_NAME="Greenbone CERT Feed"
  fi

  if [ -z "$FEED_VENDOR" ] ; then
    FEED_VENDOR="Greenbone Networks GmbH"
  fi

  if [ -z "$FEED_HOME" ] ; then
    FEED_HOME="http://www.greenbone.net/technology/gsf.html"
  fi

else
  RESTRICTED=0

  if [ -z "$FEED_NAME" ] ; then
    FEED_NAME="OpenVAS CERT Feed"
  fi

  if [ -z "$FEED_VENDOR" ] ; then
    FEED_VENDOR="The OpenVAS Project"
  fi

  if [ -z "$FEED_HOME" ] ; then
    FEED_HOME="http://www.openvas.org/"
  fi

  if [ -z "$COMMUNITY_CERT_RSYNC_FEED" ]; then
    COMMUNITY_CERT_RSYNC_FEED=rsync://feed.openvas.org:/cert-data
    # An alternative syntax which might work if the above doesn't:
    # COMMUNITY_CERT_RSYNC_FEED=rsync@feed.openvas.org::cert-data
  fi

fi


RSYNC=`command -v rsync`

create_tmp_key () {
  KEYTEMPDIR=`mktemp -d`
  cp "$ACCESSKEY" "$KEYTEMPDIR"
  TMPACCESSKEY="$KEYTEMPDIR/gsf-access-key"
  chmod 400 "$TMPACCESSKEY"
}

remove_tmp_key () {
  rm -rf "$KEYTEMPDIR"
}

do_describe () {
  echo "This script synchronizes a CERT collection with the '$FEED_NAME'."
  echo "The '$FEED_NAME' is provided by '$FEED_VENDOR'."
  echo "Online information about this feed: '$FEED_HOME'."
}

do_feedversion () {
  if [ -r $TIMESTAMP ] ; then
      echo `cat $TIMESTAMP`
  fi
}

set_interrupt_trap () {
  trap "handle_interrupt $1" 2
}

handle_interrupt () {
  echo "$1:X" >&3
}

# This function uses gos-state-manager to get information about the settings.
# gos-state-manager is only available on a Greenbone OS.
# If gos-state-manager is missing the settings values can not be retrieved.
#
# Input: option
# Output: value as string or empty String if gos-state-manager is missing
#         or option not set
get_value ()
{
  value=""
  key=$1
  if which gos-state-manager >/dev/null 2>&1
  then
    if gos-state-manager get "$key.value" >/dev/null 2>&1
    then
      value="$(gos-state-manager get "$key.value")"
    fi
  fi
  echo "$value"
}

reset_sql_tries () {
  try_sql=1
  sql_retries=0
}

test_exit_codes () {
  try_sql=0
  if [ -n "$exit_codes" ]
  then
    for item in $exit_codes
    do
      command=`echo "$item" | cut -d':' -f1`
      code=`echo "$item" | cut -d':' -f2`

      if [ "X" = "$code" ]
      then
        log_err "$1: Sync script was interrupted"
        exit 1
      elif [ "sqlite3" = "$command" ] \
           && ( [ "5" = "$code" ] || [ "1" = "$code" ] )
      then
        if [ "5" = "$code" ]
        then
          log_warning "CERT database is locked"
        else
          log_warning "Could not access CERT database, file may be locked."
        fi

        if [ "$MAX_SQL_RETRIES" -gt "$sql_retries" ]
        then
          sql_retries=$((sql_retries + 1))
          log_notice "Will try to access database again later in $SQL_RETRY_DELAY. (Retry $sql_retries of $MAX_SQL_RETRIES)"
          try_sql=1
          sleep "$SQL_RETRY_DELAY"
        else
          log_err "$1: Gave up trying to access SCAP database."
          exit 1
        fi
      else
        log_err "$1: $command exited with code $code"
        exit 1
      fi
    done
  fi
}

test_sql_exit () {
  # TODO For Postgres the exit check is done before this, in sql_pg.
  exit_code=$?
  try_sql=0
  if ( [ "5" = "$exit_code" ] || [ "1" = "$exit_code" ] )
  then
    if [ "5" = "$exit_code" ]
    then
      log_warning "CERT database is locked."
    else
      log_warning "Could not access CERT database, file may be locked."
    fi

    if [ "$MAX_SQL_RETRIES" -gt "$sql_retries" ]
    then
      sql_retries=$((sql_retries + 1))
      log_notice "Will try to access database again later in $SQL_RETRY_DELAY. (Retry $sql_retries of $MAX_SQL_RETRIES)"
      try_sql=1
      sleep "$SQL_RETRY_DELAY"
    else
      log_err "$1: Gave up trying to access CERT database."
      exit 1
    fi
  elif [ 0 -ne "$exit_code" ]
  then
    log_err "$1: sqlite3 exited with code $exit_code."
    exit 1
  fi
}

is_feed_current () {
  if [ $REFRESH_ONLY -eq 0 ]
  then
    if [ -r $TIMESTAMP ]
    then
      FEED_VERSION=`cat $TIMESTAMP`
    fi

    if [ -z "$FEED_VERSION" ]
    then
      log_warning "Could not determine feed version."
      FEED_CURRENT=0
      return $FEED_CURRENT
    fi

    FEED_INFO_TEMP_DIR=`mktemp -d`

    if [ -e $ACCESSKEY ]
    then
      read feeduser < $ACCESSKEY
      custid_at_host=`head -1 $ACCESSKEY | cut -d : -f 1`

      if [ -z "$feeduser" ] || [ -z "$custid_at_host" ]
      then
        log_err "CERT synchronization: Could not determine credentials, aborting synchronization."
        rm -rf "$FEED_INFO_TEMP_DIR"
        exit 1
      fi
      echo "custid_at_host = '$custid_at_host'"

      gsmproxy=$(get_value proxy_feed | sed -r -e 's/^.*\/\///' -e 's/:([0-9]+)$/ \1/')
      syncport=$(get_value syncport)
      if [ "$syncport" ]
      then
        PORT="$syncport"
      fi

      if [ -z "$gsmproxy" ] || [ "$gsmproxy" = "proxy_feed" ]
      then
        RSYNC_SSH_PROXY_CMD=""
      else
        if [ -e $OPENVAS_SYSCONF_DIR/proxyauth ] && [ -r $OPENVAS_SYSCONF_DIR/proxyauth ]; then
          RSYNC_SSH_PROXY_CMD="-o \"ProxyCommand corkscrew $gsmproxy %h %p $OPENVAS_SYSCONF_DIR/proxyauth\""
        else
          RSYNC_SSH_PROXY_CMD="-o \"ProxyCommand corkscrew $gsmproxy %h %p\""
        fi
      fi
      create_tmp_key
      rsync -e "ssh $RSYNC_SSH_OPTS $RSYNC_SSH_PROXY_CMD -p $PORT -i $TMPACCESSKEY" -ltvrP --chmod=D+x $RSYNC_DELETE $RSYNC_COMPRESS $custid_at_host:/cert-data/timestamp "$FEED_INFO_TEMP_DIR"
      if [ $? -ne 0 ]
      then
        log_err "rsync failed, aborting synchronization."
        rm -rf "$FEED_INFO_TEMP_DIR"
        remove_tmp_key
        exit 1
      fi
      remove_tmp_key
    else
      log_notice "No Greenbone Security Feed accees key found, falling back to Greenbone Community Feed"
      eval "$RSYNC -ltvrP \"$COMMUNITY_CERT_RSYNC_FEED/timestamp\" \"$FEED_INFO_TEMP_DIR\""
      if [ $? -ne 0 ]
      then
        log_err "rsync failed, aborting synchronization."
        rm -rf "$FEED_INFO_TEMP_DIR"
        exit 1
      fi
    fi

    FEED_VERSION_SERVER=`cat $FEED_INFO_TEMP_DIR/timestamp`

    if [ -z "$FEED_VERSION_SERVER" ]
    then
      log_err "Could not determine server feed version."
      rm -rf "$FEED_INFO_TEMP_DIR"
      exit 1
    fi

    # Check against FEED_VERSION
    if [ $FEED_VERSION -lt $FEED_VERSION_SERVER ] ; then
      FEED_CURRENT=0
    else
      FEED_CURRENT=1
    fi

    # Cleanup
    rm -rf "$FEED_INFO_TEMP_DIR"
  else
    FEED_CURRENT=0
  fi

  return $FEED_CURRENT
}

is_db_broken () {
  DB_BROKEN=0
  if [ $POSTGRES -eq 1 ] ; then
    return
  fi
  if [ ! -e $CERT_DB ]
  then
    return
  fi
  reset_sql_tries
  until [ "$try_sql" -eq 0 ]
  do
    DB_INTEGRITY=`$SQLITE3 $CERT_DB "PRAGMA integrity_check;"`
    test_sql_exit "Could not check CERT database integrity"
  done
  if [ "$DB_INTEGRITY" != "ok" ]
  then
    DB_BROKEN=1
    log_warning "Database integrity check failed."
  fi
}

sql_pg () {
  if [ "$#" -gt 0 ]
  then
    $PSQL -c "$1"
  else
    $PSQL -f -
  fi
  exit_code=$?
  if [ 0 -ne "$exit_code" ]
  then
    log_err "psql exited with code $exit_code for sql: $1."
    exit 1
  fi
}

sql_scap () {
  if [ $POSTGRES -eq 1 ]
  then
    if [ "$#" -gt 0 ]
    then
      sql_pg "$1"
    else
      sql_pg $*
    fi
  else
    if [ "$#" -gt 0 ]
    then
      $SQLITE $SCAP_DB "$1"
    else
      $SQLITE $SCAP_DB $*
    fi
  fi
}

sql () {
  if [ $POSTGRES -eq 1 ]
  then
    if [ "$#" -gt 0 ]
    then
      sql_pg "$1"
    else
      sql_pg $*
    fi
  else
    if [ "$#" -gt 0 ]
    then
      $SQLITE $CERT_DB "$1"
    else
      $SQLITE $CERT_DB $*
    fi
  fi
}

cert_db_remove () {
  if [ $POSTGRES -eq 1 ] ;
  then
    sql_pg "DROP SCHEMA IF EXISTS cert CASCADE;"
  else
    rm -f $CERT_DB
  fi
}

reinit () {
  log_notice "Reinitialization of the database necessary."
  cert_db_remove
  reset_sql_tries
  until [ "$try_sql" -eq 0 ]
  do
    sql < $CERT_RES_DIR/cert_db_init.sql
    test_sql_exit "Could not reinitialize CERT database"
  done
}

check_db_version () {
  DB_VERSION=`sql "select value from ${SCHEMA_CERT}meta where name = 'database_version';" 2>/dev/null | tr -d '\n\r' || echo 0`
  case "$DB_VERSION" in
    0) reinit;;
    1) reinit;;
    2) reinit;;
    3) reinit;;
    4) reinit;;
    5) reinit;;
  esac
}

do_help () {
  echo "$0: Sync CERT data"

  if [ -e $ACCESSKEY ]
  then
    echo "GSF access key found: Using Greenbone Security Feed"
  else
    echo "No GSF access key found: Using Community Feed"
  fi

  if [ $POSTGRES -eq 1 ];
  then
    echo " --database     database name"
  fi

  echo " --describe     display current feed info"
  echo " --feedversion  display version of this feed"
  echo " --help         display this help"
  echo " --identify     display information"
  echo " --migrate      migrate database without downloading new state"
  echo " --refresh      update database without downloading new state"
  echo " --selftest     perform self-test"
  echo " --version      display version"
  echo ""
  exit 0
}

cert_db_exists () {
  if [ $POSTGRES -eq 1 ] ;
  then
    EXISTS=`sql_pg "SELECT exists (SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'cert');" 2>/tmp/ERR | tr -d '\n\r' || echo 0`
    if [ "$EXISTS" = "f" ] ;
    then
      return 1;
    fi
    return 0;
  elif [ -f "$CERT_DB" ]
  then
    return 0;
  fi
  return 1;
}

scap_db_exists () {
  if [ $POSTGRES -eq 1 ] ;
  then
    EXISTS=`sql_pg "SELECT exists (SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scap');" 2>/tmp/ERR | tr -d '\n\r' || echo 0`
    if [ "$EXISTS" = "f" ] ;
    then
      return 1;
    fi
    return 0;
  elif [ -f "$SCAP_DB" ]
  then
    return 0;
  fi
  return 1;
}

update_cvss () {
  if ! scap_db_exists
  then
    log_warning "SCAP database not found. Cannot update max_cvss."
  else
    reset_sql_tries
    until [ "$try_sql" -eq 0 ]
    do
      SCAP_DB_LASTUPDATE=`sql_scap "SELECT value FROM ${SCHEMA_SCAP}meta WHERE name = 'last_update';"`
      test_sql_exit "Could not get last_update timestamp from SCAP database"
    done

    if [ 0 -ne "$updated_cb" ] || [ $SCAP_DB_LASTUPDATE -gt $DB_LASTUPDATE ]
    then
      log_info "Updating Max CVSS for CERT-Bund"

      reset_sql_tries
      until [ "$try_sql" -eq 0 ]
      do
        sql "${ATTACH_SCAP}
             ${RECURSIVE_TRIGGERS_OFF}
             UPDATE ${SCHEMA_CERT}cert_bund_advs
               SET max_cvss =
                   (
                     SELECT max(cvss)
                     FROM scap.cves
                     WHERE name IN
                       (
                         SELECT cve_name
                         FROM ${SCHEMA_CERT}cert_bund_cves
                         WHERE adv_id=cert_bund_advs.id
                       )
                       AND cvss != 0.0
                   );"
        test_sql_exit "Could not update CVSS scores for CERT-Bund Advisories"
      done
    else
      log_info "No CERT-Bund advisories updated and CERT DB newer than SCAP DB. Skipping CVSS recalculation."
    fi

    if [ 0 -ne "$updated_dfn" ] || [ $SCAP_DB_LASTUPDATE -gt $DB_LASTUPDATE ]
    then
      log_info "Updating Max CVSS for DFN-CERT"

      reset_sql_tries
      until [ "$try_sql" -eq 0 ]
      do
        sql "${ATTACH_SCAP}
             ${RECURSIVE_TRIGGERS_OFF}
             UPDATE ${SCHEMA_CERT}dfn_cert_advs
               SET max_cvss =
                   (
                     SELECT max(cvss)
                     FROM scap.cves
                     WHERE name IN
                       (
                         SELECT cve_name
                         FROM ${SCHEMA_CERT}dfn_cert_cves
                         WHERE adv_id=dfn_cert_advs.id
                       )
                       AND cvss != 0.0
                   );"
        test_sql_exit "Could not update CVSS scores for DFN-CERT Advisories"
      done
    else
      log_info "No DFN-CERT advisories updated and CERT DB newer than SCAP DB. Skipping CVSS recalculation."
    fi
  fi
}

ensure_sqlite3_wal_mode () {
  if [ $POSTGRES -eq 1 ] ; then
    return
  fi
  if [ ! -e $CERT_DB ]
  then
    log_err "database missing, aborting synchronization."
    exit 1
  fi
  reset_sql_tries
  until [ "$try_sql" -eq 0 ]
  do
    DUMMY=`$SQLITE3 $CERT_DB "PRAGMA journal_mode=WAL;"`
    test_sql_exit "Could not set journal mode to WAL"
  done
}

update_cert_db() {
  if [ -z "$updated_cb" ]
  then
    updated_cb=0
  fi

  if [ -z "$updated_dfn" ]
  then
    updated_dfn=0
  fi

  if cert_db_exists
  then
    log_info "Database exists already"

    check_db_version

    reset_sql_tries
    until [ "$try_sql" -eq 0 ]
    do
      CB_REFDATE=`sql "SELECT max(modification_time) from ${SCHEMA_CERT}cert_bund_advs;"`
      test_sql_exit "Could not get CERT-Bund reference date from database"
    done

    reset_sql_tries
    until [ "$try_sql" -eq 0 ]
    do
      DFN_REFDATE=`sql "SELECT max(modification_time) from ${SCHEMA_CERT}dfn_cert_advs;"`
      test_sql_exit "Could not get DFN-CERT reference date from database"
    done

    ensure_sqlite3_wal_mode
  else
    log_notice "Initializing CERT database."
    reset_sql_tries
    until [ "$try_sql" -eq 0 ]
    do
      sql < $CERT_RES_DIR/cert_db_init.sql
      test_sql_exit "Could not initialize CERT database"
    done
    DB_LASTUPDATE=0
  fi

  reset_sql_tries
  until [ "$try_sql" -eq 0 ]
  do
    DB_LASTUPDATE=`sql "select value from ${SCHEMA_CERT}meta where name = 'last_update';"`
    test_sql_exit "Could not get last_update timestamp from CERT database"
  done

  if [ -z "$CB_REFDATE" ]
  then
    CB_REFDATE=0
  fi

  if [ -z "$DFN_REFDATE" ]
  then
    DFN_REFDATE=0
  fi

  if [ -z "$DB_LASTUPDATE" ]
  then
    # Happens when initial sync was aborted
    log_warning "Inconsistent data. Resetting CERT database."
    cert_db_remove

    reset_sql_tries
    until [ "$try_sql" -eq 0 ]
    do
      sql < $CERT_RES_DIR/cert_db_init.sql
      test_sql_exit "Could not reinitialize CERT database"
    done
    CB_REFDATE=0
    DFN_REFDATE=0
    DB_LASTUPDATE=0
  fi

  log_notice "Updating data from feed"

  # Update CERT-Bund
  xmlcount=$(ls $CERT_DIR/CB-K*.xml 2> /dev/null | wc -l)
  if [ $xmlcount -ne 0 ]
  then
    for certfile in $CERT_DIR/CB-K*.xml
    do
      [ -e "$certfile" ] || break  # No file found
      filedate=`stat -c "%Y" $certfile | cut -d " " -f 1 | tr -d "-"`
      filedate=$(( $filedate - ( $filedate % 60 ) ))
      if [ $filedate -gt $DB_LASTUPDATE ]
      then
        log_info "Updating $certfile"
        reset_sql_tries
        until [ "$try_sql" -eq 0 ]
        do
          exec 4>&1
          exit_codes=$(
            (
              set_interrupt_trap
              (xsltproc --stringparam refdate "$CB_REFDATE" $CERT_RES_DIR/cert_bund_update.xsl $certfile || echo "xsltproc:$?" >&3) | \
              (sql | sed '/^\s*$/d' || echo "sqlite3:$?" >&3)
            ) 3>&1 >&4
          )
          exec 4>&-
          test_exit_codes "Update of CERT-Bund Advisories failed at file '$certfile'"
        done
        updated_cb=1
      else
        log_info "Skipping $certfile, file is older than last revision"
      fi
    done
  else
    log_warning "No CERT-Bund advisories found in $CERT_DIR"
  fi

  # Update DFN-CERT
  xmlcount=$(ls $CERT_DIR/dfn-cert-*.xml 2> /dev/null | wc -l)
  if [ $xmlcount -ne 0 ]
  then
    for certfile in $CERT_DIR/dfn-cert-*.xml
    do
      [ -e "$certfile" ] || break  # no file found
      filedate=`stat -c "%Y" $certfile | cut -d " " -f 1 | tr -d "-"`
      filedate=$(( $filedate - ( $filedate % 60 ) ))
      if [ $filedate -gt $DB_LASTUPDATE ]
      then
        log_info "Updating $certfile"
        reset_sql_tries
        until [ "$try_sql" -eq 0 ]
        do
          exec 4>&1
          exit_codes=$(
            (
              set_interrupt_trap
              (xsltproc --stringparam refdate "$DFN_REFDATE" $CERT_RES_DIR/dfn_cert_update.xsl $certfile || echo "xsltproc:$?" >&3) | \
              (sql | sed '/^\s*$/d' || echo "sqlite3:$?" >&3)
            ) 3>&1 >&4
          )
          exec 4>&-
          test_exit_codes "Update of DFN-CERT Advisories failed at file '$certfile'"
        done
        updated_dfn=1
      else
        log_info "Skipping $certfile, file is older than last revision"
      fi
    done
  else
    log_warning "No DFN-CERT advisories found in $CERT_DIR"
  fi

  update_cvss

  LAST_UPDATE_TIMESTAMP=`sed 's/^\(.\{8\}\)/\1 /' $TIMESTAMP | env TZ="UTC" date +%s -f -`

  reset_sql_tries
  until [ "$try_sql" -eq 0 ]
  do
    sql "UPDATE ${SCHEMA_CERT}meta SET value ='$LAST_UPDATE_TIMESTAMP' WHERE name = 'last_update';"
    test_sql_exit "Could not update last_update timestamp in CERT database"
  done
}

do_rsync_community_feed () {
  if [ -z "$RSYNC" ]; then
    log_err "rsync not found!"
  else
    log_notice "Using rsync: $RSYNC"
    log_notice "Configured CERT data rsync feed: $COMMUNITY_CERT_RSYNC_FEED"
    mkdir -p "$CERT_DIR"
    eval "$RSYNC -ltvrP $RSYNC_DELETE \"$COMMUNITY_CERT_RSYNC_FEED\" \"$CERT_DIR\""
    if [ $? -ne 0 ] ; then
      log_err "rsync failed. Your CERT data might be broken now."
      exit 1
    fi
  fi
}

do_sync_community_feed () {
  if [ -z "$RSYNC" ] ; then
    log_err "rsync not found!"
    log_err "No utility available in PATH environment variable to download Feed data"
    exit 1
  else
    log_notice "Will use rsync"
    do_rsync_community_feed
  fi
}

sync_certdata(){
  if [ $REFRESH_ONLY -eq 0 ]
  then
    if [ -e $ACCESSKEY ]
    then
      log_notice "Found Greenbone Security Feed subscription file, trying to synchronize with Greenbone CERT data Repository ..."
      notsynced=1

      mkdir -p "$CERT_DIR"
      read feeduser < $ACCESSKEY
      custid_at_host=`head -1 $ACCESSKEY | cut -d : -f 1`
      if [ -z "$feeduser" ] || [ -z "$custid_at_host" ]
      then
        log_err "Could not determine credentials, aborting synchronization."
        exit 1
      fi

      while [ 1 -eq $notsynced ]
      do

        gsmproxy=$(get_value proxy_feed | sed -r -e 's/^.*\/\///' -e 's/:([0-9]+)$/ \1/')
        syncport=$(get_value syncport)
        if [ "$syncport" ]
        then
          PORT="$syncport"
        fi

        if [ -z "$gsmproxy" ] || [ "$gsmproxy" = "proxy_feed" ]
        then
          RSYNC_SSH_PROXY_CMD=""
        else
          if [ -e $OPENVAS_SYSCONF_DIR/proxyauth ] && [ -r $OPENVAS_SYSCONF_DIR/proxyauth ]; then
            RSYNC_SSH_PROXY_CMD="-o \"ProxyCommand corkscrew $gsmproxy %h %p $OPENVAS_SYSCONF_DIR/proxyauth\""
          else
            RSYNC_SSH_PROXY_CMD="-o \"ProxyCommand corkscrew $gsmproxy %h %p\""
          fi
        fi
        create_tmp_key
        rsync -e "ssh $RSYNC_SSH_OPTS $RSYNC_SSH_PROXY_CMD -p $PORT -i $ACCESSKEY" -ltvrP --chmod=D+x $RSYNC_DELETE $RSYNC_COMPRESS $custid_at_host:/cert-data/ $CERT_DIR
        if [ 0 -ne "$?" ] ; then
          log_err "rsync failed, aborting synchronization."
          remove_tmp_key
          exit 1
        fi
        remove_tmp_key
        notsynced=0
      done
      log_notice "Synchronization with the Greenbone CERT data Repository successful."
      echo
    else
      log_notice "No Greenbone Security Feed accees key found, falling back to Greenbone Community Feed"
      do_sync_community_feed
    fi
  fi

  update_cert_db
}

do_self_test () {
  if [ -z "$SELFTEST_STDERR" ]
  then
    SELFTEST_STDERR=0
  fi

  if [ $POSTGRES -eq 1 ] ;
  then
    POSTGRESQL=`command -v psql`
    if [ -z "$POSTGRESQL" ]; then
      if [ 0 -ne $SELFTEST_STDERR ]
      then
        echo "psql not found (required)." 1>&2
      fi
      log_err "psql not found (required)."
      SELFTEST_FAIL=1
    fi
  else
    SQLITE=`command -v sqlite3`
    if [ -z "$SQLITE" ]; then
      if [ 0 -ne $SELFTEST_STDERR ]
      then
        echo "sqlite3 not found (required)." 1>&2
      fi
      log_err "sqlite3 not found (required)."
      SELFTEST_FAIL=1
    fi
  fi

  if [ -z "$RSYNC" ]
  then
    if [ 0 -ne $SELFTEST_STDERR ]
    then
      echo "rsync not found (required)." 1>&2
    fi
    log_err "rsync not found (required)."
    SELFTEST_FAIL=1
  fi
}

check_alerts (){
  log_info "Calling openvasmd to check for alerts"
  if command -v openvasmd
  then
    openvasmd --check-alerts
  elif [ -x "/usr/bin/openvasmd" ]
  then
    /usr/bin/openvasmd --check-alerts
  else
    log_warning "Command openvasmd not found"
  fi
}

psql_setup "tasks"
while test $# -gt 0; do
 case "$1" in
        --database)
          if [ $POSTGRES -eq 1 ] ;
          then
            shift
            if [ $# -gt 0 ]; then
              psql_setup "$1"
            else
              echo "[e] --database argument missing"
              exit 1
            fi
          else
            echo "[e] --database option available only for Postgres"
            exit 1
          fi
          ;;
        --version)
          echo $VERSION
          exit 0
           ;;
        --identify)
          echo "CERTSYNC|$SCRIPT_NAME|$VERSION|$FEED_NAME|$RESTRICTED|CERTSYNC"
          exit 0
          ;;
        --describe)
          do_describe
          exit 0
          ;;
        --feedversion)
          do_feedversion
          exit 0
          ;;
        --help)
          do_help
          exit 0
          ;;
        --refresh)
          REFRESH_ONLY=1
          ;;
        --migrate)
          REFRESH_ONLY=1
          ;;
        --selftest)
          SELFTEST_FAIL=0
          SELFTEST_STDERR=1
          do_self_test
          exit $SELFTEST_FAIL
          ;;
        --feedcurrent)
          is_feed_current
          exit $?
          ;;
      esac
      shift
done

SELFTEST_FAIL=0
do_self_test
if [ $SELFTEST_FAIL -ne 0 ]
then
  exit 1
fi

if [ -f "$CERT_DB" ]
then
  if [ "$(id -u)" -ne "$(stat -c %u $CERT_DB)" ]
  then
    log_err "Not synchronizing or updating the database since the current user is not the owner of the database."
    echo "Not synchronizing or updating the database since the current user is not the owner of the database."
    exit 1
  fi
fi

if [ $ENABLED -ne 1 ]
then
  log_notice "Synchronization of CERT data with network is disabled, will only perform local operations."
  REFRESH_ONLY=1
fi

is_db_broken
if [ $DB_BROKEN -eq 1 ]
then
  log_notice "Purging corrupted database."
  cert_db_remove
else
  is_feed_current
  if [ $FEED_CURRENT -eq 1 ]
  then
    log_notice "Feed is already current, skipping synchronization."
    exit 0
  fi
fi
sync_certdata
check_alerts

exit 0
