<?php
/*******************************************************************************
 * Copyright (C) 2007 Easter-eggs
 * https://ldapsaisie.org
 *
 * Author: See AUTHORS file in top-level directory.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

******************************************************************************/

// Error messages

// Support
LSerror :: defineError('FTP_SUPPORT_01',
  ___("FTP support: PHP ftp extension is missing.")
);

// Other errors
LSerror :: defineError('FTP_00',
  ___("FTP error: %{msg}")
);

LSerror :: defineError('FTP_01',
  ___("FTP: Unable to connect to FTP Server (Step : %{step}).")
);
LSerror :: defineError('FTP_02',
  ___("FTP: Unable to make directory %{dir} on the remote server.")
);
LSerror :: defineError('FTP_03',
  ___("FTP: Unable to list directory %{dir} content on the remote server.")
);
LSerror :: defineError('FTP_04',
  ___("FTP: Unable to delete directory %{dir} on the remote server.")
);
LSerror :: defineError('FTP_05',
  ___("FTP: Unable to delete file %{file} on the remote server.")
);
LSerror :: defineError('FTP_06',
  ___("FTP: Unable to modify rights on the directory %{dir} on the remote server.")
);
LSerror :: defineError('FTP_07',
  ___("FTP: Unable to rename folder from %{old} to %{new} on the remote server.")
);

/**
 * Check support of FTP addon
 *
 * @author Benjamin Renard <brenard@easter-eggs.com>
 *
 * @return boolean true if FTP addon is fully supported, false in other case
 */
function LSaddon_ftp_support() {
  $retval = true;

  // PHP ftp extension dependency
  if (!function_exists('ftp_connect')) {
    LSerror :: addErrorCode('FTP_SUPPORT_01');
    $retval = false;
  }

  return $retval;
}

/**
 * Log last FTP error
 * @return void
 */
function _logLastFtpError() {
  $error = error_get_last();
  if ($error) LSerror :: addErrorCode('FTP_00', $error['message']);
}

/**
 * Connect to FTP server
 *
 * @author Benjamin Renard <brenard@easter-eggs.com>
 *
 * @param string $host FTP server FQDN or IP address
 * @param string $port The TCP port of the FTP server
 * @param string $user The username
 * @param string $pwd The password
 *
 * @return FTP\Connection|resource|false FTP\Connection (or resource with PHP <= 8.1.0) in case of
 *                                       success, false otherwise
 */
function connectToFTP($host, $port, $user, $pwd) {
  $cnx = @ftp_connect($host, $port);
  if ($cnx === false) {
    _logLastFtpError();
    LSerror :: addErrorCode('FTP_01', "1");
    return false;
  }
  if (@ftp_login($cnx, $user, $pwd))
    return $cnx;
  _logLastFtpError();
  LSerror :: addErrorCode('FTP_01', "2");
  return false;
}

/**
 * Create one or more directories throught FTP
 *
 * @author Benjamin Renard <brenard@easter-eggs.com>
 *
 * @param string $host FTP server FQDN or IP address
 * @param string $port The TCP port of the FTP server
 * @param string $user The username
 * @param string $pwd The password
 * @param array|string $dirs The directory/ies to add
 * @param int $chmod The directory/ies mode as an octal number (do not forget leading zero,
 *                   example: 0755 or 02755, default : default umask on the SSH server)
 *
 * @return boolean
 */
function createDirsByFTP($host, $port, $user, $pwd, $dirs, $chmod=NULL) {
  $cnx = connectToFTP($host, $port, $user, $pwd);
  if (!$cnx) return false;
  foreach(ensureIsArray($dirs) as $dir) {
    if (@ftp_mkdir($cnx, $dir) === false) {
      _logLastFtpError();
      LSerror :: addErrorCode('FTP_02', $dir);
      return false;
    }
    if ($chmod && !@ftp_chmod($cnx, $chmod, $dir)) {
      _logLastFtpError();
      LSerror :: addErrorCode('FTP_06', $dir);
    }
  }
  return true;
}

/**
 * Internal function call recursively to remove a directory and its content
 * @param FTP\Connection|resource $cnx  The FTP\Connection object (or resource with PHP < 8.1.0)
 * @param string $dir  The directory path to remove
 * @return boolean
 */
function _removeDirByFTP(&$cnx, $dir) {
  if ($dir[strlen($dir)-1] == '/')
    $dir = substr($dir, 0, strlen($dir)-1);
  $items = @ftp_nlist($cnx, $dir);
  LSlog :: get_logger('LSaddon_ftp') -> trace(
    "_removeDirByFTP($dir): directory content: ".varDump($items));
  if (!is_array($items)) {
    _logLastFtpError();
    LSerror :: addErrorCode('FTP_03', $dir);
    return false;
  }

  foreach($items as $item) {
    if (@ftp_chdir($cnx, $item)) {
      LSlog :: get_logger('LSaddon_ftp') -> trace(
        "_removeDirByFTP($dir): item '$item' is a directory, delete it recursively");
      if (!_removeDirByFTP($cnx, $item))
        return false;
    }
    else {
      LSlog :: get_logger('LSaddon_ftp') -> trace(
        "_removeDirByFTP($dir): item '$item' is a file, delete it");
      if (!@ftp_delete($cnx, $item)) {
        _logLastFtpError();
        LSerror :: addErrorCode('FTP_05', $item);
        return false;
      }
      else {
        LSlog :: get_logger('LSaddon_ftp') -> trace(
          "_removeDirByFTP($dir): file '$item' removed");
      }
    }
  }

  if (@!ftp_rmdir($cnx, $dir)) {
    _logLastFtpError();
    LSerror :: addErrorCode('FTP_04', $dir);
    return false;
  }
  else {
    LSlog :: get_logger('LSaddon_ftp') -> trace(
      "_removeDirByFTP($dir): directory '$dir' removed");
  }
  return true;
}

/**
 * Delete one or more directories throught FTP
 *
 * Caution : recursive deletion ! The content of the directory will be listed and deleted before the
 * directory. If your FTP server is configured to mask some files or directories (for instance,
 * starting by '.'), they will not be listed and deleted and this may cause the deletion to fail.
 * With VsFTPd, you have to add force_dot_files=1 in configuration.
 *
 * @author Benjamin Renard <brenard@easter-eggs.com>
 *
 * @param string $host FTP server FQDN or IP address
 * @param string $port The TCP port of the FTP server
 * @param string $user The username
 * @param string $pwd The password
 * @param array|string $dirs The directory/ies to remove
 *
 * @return boolean
 */
function removeDirsByFTP($host, $port, $user, $pwd, $dirs) {
  $cnx = connectToFTP($host, $port, $user, $pwd);
  if (!$cnx) return false;
  foreach(ensureIsArray($dirs) as $dir)
    if (!_removeDirByFTP($cnx, $dir))
      return false;
  return true;
}

/**
 * Rename a directory throught FTP
 *
 * @author Benjamin Renard <brenard@easter-eggs.com>
 *
 * @param string $host FTP server FQDN or IP address
 * @param string $port The TCP port of the FTP server
 * @param string $user The username
 * @param string $pwd The password
 * @param string $old The actual directory path to rename
 * @param string $new The new directory path
 *
 * @return boolean
 */
function renameDirByFTP($host, $port, $user, $pwd, $old, $new) {
  $cnx = connectToFTP($host, $port, $user, $pwd);
  if (!$cnx) return false;
  if (!@ftp_rename($cnx, $old, $new)) {
    _logLastFtpError();
    LSerror :: addErrorCode('FTP_07', array('old' => $old, 'new' => $new));
    return false;
  }
  return true;
}
